split off osmo-bsc: remove files, apply build

Change-Id: I64d84c52f6e38e98144eb9be8f0ab82e0e1f6cca
This commit is contained in:
Neels Hofmeyr 2017-07-09 22:09:18 +02:00
parent 31a11aa75b
commit 7b656884cf
226 changed files with 100 additions and 87177 deletions

View File

@ -209,7 +209,7 @@ AC_ARG_ENABLE([external_tests],
[Include the VTY/CTRL tests in make check [default=no]]),
[enable_ext_tests="$enableval"],[enable_ext_tests="no"])
if test "x$enable_ext_tests" = "xyes" ; then
AM_PATH_PYTHON
AM_PATH_PYTHON
AC_CHECK_PROG(OSMOTESTEXT_CHECK,osmotestvty.py,yes)
if test "x$OSMOTESTEXT_CHECK" != "xyes" ; then
AC_MSG_ERROR([Please install git://osmocom.org/python/osmo-python-tests to run the VTY/CTRL tests.])
@ -229,17 +229,13 @@ AC_OUTPUT(
src/Makefile
src/libtrau/Makefile
src/libbsc/Makefile
src/libmsc/Makefile
src/libvlr/Makefile
src/libcommon/Makefile
src/libfilter/Makefile
src/libcommon-cs/Makefile
src/osmo-msc/Makefile
src/osmo-bsc/Makefile
src/osmo-bsc_nat/Makefile
src/ipaccess/Makefile
src/utils/Makefile
src/gprs/Makefile
tests/Makefile
tests/atlocal
tests/gsm0408/Makefile
@ -247,22 +243,10 @@ AC_OUTPUT(
tests/bsc/Makefile
tests/bsc-nat/Makefile
tests/bsc-nat-trie/Makefile
tests/gprs/Makefile
tests/gbproxy/Makefile
tests/abis/Makefile
tests/smpp/Makefile
tests/trau/Makefile
tests/sgsn/Makefile
tests/subscr/Makefile
tests/oap/Makefile
tests/gtphub/Makefile
tests/xid/Makefile
tests/sndcp_xid/Makefile
tests/slhc/Makefile
tests/v42bis/Makefile
tests/nanobts_omlattr/Makefile
tests/sms_queue/Makefile
tests/msc_vlr/Makefile
doc/Makefile
doc/examples/Makefile
contrib/Makefile

View File

@ -1,9 +1,8 @@
noinst_HEADERS = \
a_reset.h \
abis_nm.h \
abis_om2000.h \
abis_rsl.h \
a_iface.h \
a_iface_bssap.h \
arfcn_range_encode.h \
auth.h \
bsc_msc.h \
@ -21,37 +20,16 @@ noinst_HEADERS = \
common_cs.h \
crc24.h \
ctrl.h \
db.h \
debug.h \
e1_config.h \
gb_proxy.h \
gprs_gb_parse.h \
gprs_gmm.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_subscriber.h \
gprs_utils.h \
gsm_04_08.h \
gsm_04_11.h \
gsm_04_14.h \
gsm_04_08_utils.h \
gsm_04_80.h \
gsm_data.h \
gsm_data_shared.h \
gsm_subscriber.h \
gsup_client.h \
gtphub.h \
handover.h \
handover_decision.h \
ipaccess.h \
iucs.h \
iucs_ranap.h \
iu_dummy.h \
meas_feed.h \
meas_rep.h \
misdn.h \
@ -60,11 +38,9 @@ noinst_HEADERS = \
msc_ifaces.h \
nat_rewrite_trie.h \
network_listen.h \
oap_client.h \
openbscdefines.h \
osmo_bsc.h \
osmo_bsc_grace.h \
a_reset.h \
osmo_bsc_rf.h \
osmo_msc.h \
osmo_bsc_sigtran.h \
@ -80,8 +56,6 @@ noinst_HEADERS = \
sgsn.h \
signal.h \
silent_call.h \
slhc.h \
smpp.h \
sms_queue.h \
socket.h \
system_information.h \
@ -89,18 +63,6 @@ noinst_HEADERS = \
trau_mux.h \
trau_upqueue.h \
ussd.h \
vlr.h \
vty.h \
v42bis.h \
v42bis_private.h \
$(NULL)
openbsc_HEADERS = \
bsc_api.h \
gsm_04_08.h \
meas_rep.h \
$(NULL)
# DO NOT add a newline and '$(NULL)' to this line. That would add a trailing
# space to the directory installed: $prefix/include/'openbsc '
openbscdir = $(includedir)/openbsc

View File

@ -1,76 +0,0 @@
/* (C) 2017 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/>.
*
*/
#pragma once
#include <openbsc/a_reset.h>
/* A struct to keep a context information about the BSCs we are associated with */
struct bsc_context {
struct llist_head list;
/* Holds a copy of the sccp address of the BSC,
* this address will become known as soon as
* a remote BSC tries to make a connection or
* sends a RESET request via UNIDATA */
struct osmo_sccp_addr bsc_addr;
/* Holds a copy of the our local MSC address,
* this will be the sccp-address that is associated
* with the A interface */
struct osmo_sccp_addr msc_addr;
/* A pointer to the reset handler FSM, the
* state machine is allocated when the BSC
* is registerd. */
struct a_reset_ctx *reset;
/* A pointer to the sccp_user that is associated
* with the A interface. We need this information
* to send the resets and to send paging requests */
struct osmo_sccp_user *sccp_user;
};
/* Initalize A interface connection between to MSC and BSC */
int a_init(struct osmo_sccp_instance *sccp, struct gsm_network *network);
/* Send DTAP message via A-interface */
int a_iface_tx_dtap(struct msgb *msg);
/* Send Cipher mode command via A-interface */
int a_iface_tx_cipher_mode(const struct gsm_subscriber_connection *conn,
int cipher, const const uint8_t *key, int len, int include_imeisv);
/* Page a subscriber via A-interface */
int a_iface_tx_paging(const char *imsi, uint32_t tmsi, uint16_t lac);
/* Send assignment request via A-interface */
int a_iface_tx_assignment(const struct gsm_trans *trans);
/* Send clear command via A-interface */
int a_iface_tx_clear_cmd(struct gsm_subscriber_connection *conn);
/* Clear all subscriber connections on a specified BSC
* (Helper function for a_iface_bssap.c) */
void a_clear_all(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *bsc_addr);
/* Delete info of a closed connection from the active connection list
* (Helper function for a_iface_bssap.c) */
void a_delete_bsc_con(uint32_t conn_id);

View File

@ -1,41 +0,0 @@
/* (C) 2017 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/>.
*
*/
#pragma once
/* Note: The structs and functions presented in this header file are intended
* to be used only by a_iface.c. */
/* A structure to hold tha most basic information about a sigtran connection
* we use this struct internally here to pass connection data around */
struct a_conn_info {
struct osmo_sccp_addr *msc_addr;
struct osmo_sccp_addr *bsc_addr;
uint32_t conn_id;
struct gsm_network *network;
struct a_reset_ctx *reset;
};
/* Receive incoming connection less data messages via sccp */
void sccp_rx_udt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg);
/* Receive incoming connection oriented data messages via sccp */
int sccp_rx_dt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg);

View File

@ -55,4 +55,5 @@ 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);
int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id);
#endif

View File

@ -1,6 +1,7 @@
#pragma once
#include <stdint.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
struct msgb;
struct gsm_network;
@ -25,3 +26,9 @@ struct gsm_network *gsm_network_init(void *ctx,
int common_cs_vty_init(struct gsm_network *network,
int (* config_write_net )(struct vty *));
struct gsm_network *gsmnet_from_vty(struct vty *v);
struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value);
int gsm48_extract_mi(uint8_t *classmark2_lv, int length, char *mi_string, uint8_t *mi_type);
int gsm48_paging_extract_mi(struct gsm48_pag_resp *resp, int length,
char *mi_string, uint8_t *mi_type);
struct msgb *gsm48_create_loc_upd_rej(uint8_t cause);

View File

@ -1,59 +0,0 @@
/* (C) 2008 by Jan Luebbe <jluebbe@debian.org>
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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/>.
*
*/
#ifndef _DB_H
#define _DB_H
#include <stdbool.h>
#include "gsm_subscriber.h"
struct gsm_equipment;
struct gsm_network;
struct gsm_auth_info;
struct gsm_auth_tuple;
struct gsm_sms;
/* one time initialisation */
int db_init(const char *name);
int db_prepare(void);
int db_fini(void);
/* SMS store-and-forward */
int db_sms_store(struct gsm_sms *sms);
struct gsm_sms *db_sms_get(struct gsm_network *net, unsigned long long id);
struct gsm_sms *db_sms_get_next_unsent(struct gsm_network *net,
unsigned long long min_sms_id,
unsigned int max_failed);
struct gsm_sms *db_sms_get_next_unsent_rr_msisdn(struct gsm_network *net,
const char *last_msisdn,
unsigned int max_failed);
struct gsm_sms *db_sms_get_unsent_for_subscr(struct vlr_subscr *vsub,
unsigned int max_failed);
int db_sms_mark_delivered(struct gsm_sms *sms);
int db_sms_inc_deliver_attempts(struct gsm_sms *sms);
int db_sms_delete_by_msisdn(const char *msisdn);
/* Statistics counter storage */
struct osmo_counter;
int db_store_counter(struct osmo_counter *ctr);
struct rate_ctr_group;
int db_store_rate_ctr_group(struct rate_ctr_group *ctrg);
#endif /* _DB_H */

View File

@ -1,288 +0,0 @@
#ifndef _GB_PROXY_H
#define _GB_PROXY_H
#include <osmocom/core/msgb.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/vty/command.h>
#include <sys/types.h>
#include <regex.h>
#define GBPROXY_INIT_VU_GEN_TX 256
struct rate_ctr_group;
struct gprs_gb_parse_context;
struct tlv_parsed;
enum gbproxy_global_ctr {
GBPROX_GLOB_CTR_INV_BVCI,
GBPROX_GLOB_CTR_INV_LAI,
GBPROX_GLOB_CTR_INV_RAI,
GBPROX_GLOB_CTR_INV_NSEI,
GBPROX_GLOB_CTR_PROTO_ERR_BSS,
GBPROX_GLOB_CTR_PROTO_ERR_SGSN,
GBPROX_GLOB_CTR_NOT_SUPPORTED_BSS,
GBPROX_GLOB_CTR_NOT_SUPPORTED_SGSN,
GBPROX_GLOB_CTR_RESTART_RESET_SGSN,
GBPROX_GLOB_CTR_TX_ERR_SGSN,
GBPROX_GLOB_CTR_OTHER_ERR,
GBPROX_GLOB_CTR_PATCH_PEER_ERR,
};
enum gbproxy_peer_ctr {
GBPROX_PEER_CTR_BLOCKED,
GBPROX_PEER_CTR_UNBLOCKED,
GBPROX_PEER_CTR_DROPPED,
GBPROX_PEER_CTR_INV_NSEI,
GBPROX_PEER_CTR_TX_ERR,
GBPROX_PEER_CTR_RAID_PATCHED_BSS,
GBPROX_PEER_CTR_RAID_PATCHED_SGSN,
GBPROX_PEER_CTR_APN_PATCHED,
GBPROX_PEER_CTR_TLLI_PATCHED_BSS,
GBPROX_PEER_CTR_TLLI_PATCHED_SGSN,
GBPROX_PEER_CTR_PTMSI_PATCHED_BSS,
GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN,
GBPROX_PEER_CTR_PATCH_CRYPT_ERR,
GBPROX_PEER_CTR_PATCH_ERR,
GBPROX_PEER_CTR_ATTACH_REQS,
GBPROX_PEER_CTR_ATTACH_REJS,
GBPROX_PEER_CTR_ATTACH_ACKS,
GBPROX_PEER_CTR_ATTACH_COMPLS,
GBPROX_PEER_CTR_RA_UPD_REQS,
GBPROX_PEER_CTR_RA_UPD_REJS,
GBPROX_PEER_CTR_RA_UPD_ACKS,
GBPROX_PEER_CTR_RA_UPD_COMPLS,
GBPROX_PEER_CTR_GMM_STATUS_BSS,
GBPROX_PEER_CTR_GMM_STATUS_SGSN,
GBPROX_PEER_CTR_DETACH_REQS,
GBPROX_PEER_CTR_DETACH_ACKS,
GBPROX_PEER_CTR_PDP_ACT_REQS,
GBPROX_PEER_CTR_PDP_ACT_REJS,
GBPROX_PEER_CTR_PDP_ACT_ACKS,
GBPROX_PEER_CTR_PDP_DEACT_REQS,
GBPROX_PEER_CTR_PDP_DEACT_ACKS,
GBPROX_PEER_CTR_TLLI_UNKNOWN,
GBPROX_PEER_CTR_TLLI_CACHE_SIZE,
GBPROX_PEER_CTR_LAST,
};
enum gbproxy_keep_mode {
GBPROX_KEEP_NEVER,
GBPROX_KEEP_REATTACH,
GBPROX_KEEP_IDENTIFIED,
GBPROX_KEEP_ALWAYS,
};
enum gbproxy_match_id {
GBPROX_MATCH_PATCHING,
GBPROX_MATCH_ROUTING,
GBPROX_MATCH_LAST
};
struct gbproxy_match {
int enable;
char *re_str;
regex_t re_comp;
};
struct gbproxy_config {
/* parsed from config file */
uint16_t nsip_sgsn_nsei;
/* misc */
struct gprs_ns_inst *nsi;
/* Linked list of all Gb peers (except SGSN) */
struct llist_head bts_peers;
/* Counter */
struct rate_ctr_group *ctrg;
/* force mcc/mnc */
int core_mnc;
int core_mcc;
uint8_t* core_apn;
size_t core_apn_size;
int tlli_max_age;
int tlli_max_len;
/* Experimental config */
int patch_ptmsi;
int acquire_imsi;
int route_to_sgsn2;
uint16_t nsip_sgsn2_nsei;
enum gbproxy_keep_mode keep_link_infos;
/* IMSI checking/matching */
struct gbproxy_match matches[GBPROX_MATCH_LAST];
};
struct gbproxy_patch_state {
int local_mnc;
int local_mcc;
/* List of TLLIs for which patching is enabled */
struct llist_head logical_links;
int logical_link_count;
};
struct gbproxy_peer {
struct llist_head list;
/* point back to the config */
struct gbproxy_config *cfg;
/* NSEI of the peer entity */
uint16_t nsei;
/* BVCI used for Point-to-Point to this peer */
uint16_t bvci;
int blocked;
/* Routeing Area that this peer is part of (raw 04.08 encoding) */
uint8_t ra[6];
/* Counter */
struct rate_ctr_group *ctrg;
struct gbproxy_patch_state patch_state;
};
struct gbproxy_tlli_state {
uint32_t current;
uint32_t assigned;
int bss_validated;
int net_validated;
uint32_t ptmsi;
};
struct gbproxy_link_info {
struct llist_head list;
struct gbproxy_tlli_state tlli;
struct gbproxy_tlli_state sgsn_tlli;
uint32_t sgsn_nsei;
time_t timestamp;
uint8_t *imsi;
size_t imsi_len;
int imsi_acq_pending;
struct llist_head stored_msgs;
unsigned vu_gen_tx_bss;
int is_deregistered;
int is_matching[GBPROX_MATCH_LAST];
};
/* gb_proxy_vty .c */
int gbproxy_vty_init(void);
int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg);
/* gb_proxy.c */
int gbproxy_init_config(struct gbproxy_config *cfg);
/* Main input function for Gb proxy */
int gbprox_rcvmsg(struct gbproxy_config *cfg, struct msgb *msg, uint16_t nsei, uint16_t ns_bvci, uint16_t nsvci);
int gbprox_signal(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data);
/* Reset all persistent NS-VC's */
int gbprox_reset_persistent_nsvcs(struct gprs_ns_inst *nsi);
void gbprox_reset(struct gbproxy_config *cfg);
/* TLLI info handling */
void gbproxy_delete_link_infos(struct gbproxy_peer *peer);
struct gbproxy_link_info *gbproxy_update_link_state_ul(
struct gbproxy_peer *peer, time_t now,
struct gprs_gb_parse_context *parse_ctx);
struct gbproxy_link_info *gbproxy_update_link_state_dl(
struct gbproxy_peer *peer, time_t now,
struct gprs_gb_parse_context *parse_ctx);
int gbproxy_update_link_state_after(
struct gbproxy_peer *peer, struct gbproxy_link_info *link_info,
time_t now, struct gprs_gb_parse_context *parse_ctx);
int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now);
void gbproxy_delete_link_info(struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info);
void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info);
void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now,
struct gbproxy_link_info *link_info);
void gbproxy_update_link_info(struct gbproxy_link_info *link_info,
const uint8_t *imsi, size_t imsi_len);
void gbproxy_detach_link_info(struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info);
struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer);
struct gbproxy_link_info *gbproxy_link_info_by_tlli(
struct gbproxy_peer *peer, uint32_t tlli);
struct gbproxy_link_info *gbproxy_link_info_by_imsi(
struct gbproxy_peer *peer, const uint8_t *imsi, size_t imsi_len);
struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli(
struct gbproxy_peer *peer, uint32_t tlli);
struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli(
struct gbproxy_peer *peer,
uint32_t tlli, uint32_t sgsn_nsei);
struct gbproxy_link_info *gbproxy_link_info_by_ptmsi(
struct gbproxy_peer *peer,
uint32_t ptmsi);
int gbproxy_imsi_matches(
struct gbproxy_config *cfg,
enum gbproxy_match_id match_id,
struct gbproxy_link_info *link_info);
uint32_t gbproxy_map_tlli(
uint32_t other_tlli, struct gbproxy_link_info *link_info, int to_bss);
/* needed by gb_proxy_tlli.h */
uint32_t gbproxy_make_bss_ptmsi(struct gbproxy_peer *peer, uint32_t sgsn_ptmsi);
uint32_t gbproxy_make_sgsn_tlli(
struct gbproxy_peer *peer, struct gbproxy_link_info *link_info,
uint32_t bss_tlli);
void gbproxy_reset_link(struct gbproxy_link_info *link_info);
int gbproxy_check_imsi(
struct gbproxy_match *match, const uint8_t *imsi, size_t imsi_len);
/* Message patching */
void gbproxy_patch_bssgp(
struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
struct gbproxy_peer *peer, struct gbproxy_link_info *link_info,
int *len_change, struct gprs_gb_parse_context *parse_ctx);
int gbproxy_patch_llc(
struct msgb *msg, uint8_t *llc, size_t llc_len,
struct gbproxy_peer *peer, struct gbproxy_link_info *link_info,
int *len_change, struct gprs_gb_parse_context *parse_ctx);
int gbproxy_set_patch_filter(
struct gbproxy_match *match, const char *filter, const char **err_msg);
void gbproxy_clear_patch_filter(struct gbproxy_match *match);
/* Peer handling */
struct gbproxy_peer *gbproxy_peer_by_bvci(
struct gbproxy_config *cfg, uint16_t bvci);
struct gbproxy_peer *gbproxy_peer_by_nsei(
struct gbproxy_config *cfg, uint16_t nsei);
struct gbproxy_peer *gbproxy_peer_by_rai(
struct gbproxy_config *cfg, const uint8_t *ra);
struct gbproxy_peer *gbproxy_peer_by_lai(
struct gbproxy_config *cfg, const uint8_t *la);
struct gbproxy_peer *gbproxy_peer_by_lac(
struct gbproxy_config *cfg, const uint8_t *la);
struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv(
struct gbproxy_config *cfg, struct tlv_parsed *tp);
struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci);
void gbproxy_peer_free(struct gbproxy_peer *peer);
int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci);
#endif

View File

@ -1,59 +0,0 @@
#pragma once
#include <openbsc/gprs_llc.h>
#include <sys/types.h>
struct gprs_gb_parse_context {
/* Pointer to protocol specific parts */
struct gsm48_hdr *g48_hdr;
struct bssgp_normal_hdr *bgp_hdr;
struct bssgp_ud_hdr *bud_hdr;
uint8_t *bssgp_data;
size_t bssgp_data_len;
uint8_t *llc;
size_t llc_len;
/* Extracted information */
struct gprs_llc_hdr_parsed llc_hdr_parsed;
struct tlv_parsed bssgp_tp;
int to_bss;
uint8_t *tlli_enc;
uint8_t *old_tlli_enc;
uint8_t *imsi;
size_t imsi_len;
uint8_t *apn_ie;
size_t apn_ie_len;
uint8_t *ptmsi_enc;
uint8_t *new_ptmsi_enc;
uint8_t *raid_enc;
uint8_t *old_raid_enc;
uint8_t *bssgp_raid_enc;
uint8_t *bssgp_ptmsi_enc;
/* General info */
const char *llc_msg_name;
int invalidate_tlli;
int await_reattach;
int need_decryption;
uint32_t tlli;
int pdu_type;
int old_raid_is_foreign;
int peer_nsei;
};
int gprs_gb_parse_dtap(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx);
int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len,
struct gprs_gb_parse_context *parse_ctx);
int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len,
struct gprs_gb_parse_context *parse_ctx);
const char *gprs_gb_message_name(const struct gprs_gb_parse_context *parse_ctx,
const char *default_msg_name);
void gprs_gb_log_parse_context(int log_level,
struct gprs_gb_parse_context *parse_ctx,
const char *default_msg_name);

View File

@ -1,35 +0,0 @@
#ifndef _GPRS_GMM_H
#define _GPRS_GMM_H
#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_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,
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);
void gsm0408_gprs_authenticate(struct sgsn_mm_ctx *mmctx);
int gprs_gmm_rx_suspend(struct gprs_ra_id *raid, uint32_t tlli);
int gprs_gmm_rx_resume(struct gprs_ra_id *raid, uint32_t tlli,
uint8_t suspend_ref);
time_t gprs_max_time_to_idle(void);
int iu_rab_act_ps(uint8_t rab_id, struct sgsn_pdp_ctx *pdp);
#endif /* _GPRS_GMM_H */

View File

@ -1,284 +0,0 @@
#ifndef _GPRS_LLC_H
#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 {
GPRS_SAPI_GMM = 1,
GPRS_SAPI_TOM2 = 2,
GPRS_SAPI_SNDCP3 = 3,
GPRS_SAPI_SNDCP5 = 5,
GPRS_SAPI_SMS = 7,
GPRS_SAPI_TOM8 = 8,
GPRS_SAPI_SNDCP9 = 9,
GPRS_SAPI_SNDCP11 = 11,
};
/* Section 6.4 Commands and Responses */
enum gprs_llc_u_cmd {
GPRS_LLC_U_DM_RESP = 0x01,
GPRS_LLC_U_DISC_CMD = 0x04,
GPRS_LLC_U_UA_RESP = 0x06,
GPRS_LLC_U_SABM_CMD = 0x07,
GPRS_LLC_U_FRMR_RESP = 0x08,
GPRS_LLC_U_XID = 0x0b,
GPRS_LLC_U_NULL_CMD = 0x00,
};
/* Section 6.4.1.6 / Table 6 */
enum gprs_llc_xid_type {
GPRS_LLC_XID_T_VERSION = 0,
GPRS_LLC_XID_T_IOV_UI = 1,
GPRS_LLC_XID_T_IOV_I = 2,
GPRS_LLC_XID_T_T200 = 3,
GPRS_LLC_XID_T_N200 = 4,
GPRS_LLC_XID_T_N201_U = 5,
GPRS_LLC_XID_T_N201_I = 6,
GPRS_LLC_XID_T_mD = 7,
GPRS_LLC_XID_T_mU = 8,
GPRS_LLC_XID_T_kD = 9,
GPRS_LLC_XID_T_kU = 10,
GPRS_LLC_XID_T_L3_PAR = 11,
GPRS_LLC_XID_T_RESET = 12,
};
extern const struct value_string gprs_llc_xid_type_names[];
/* TS 04.64 Section 7.1.2 Table 7: LLC layer primitives (GMM/SNDCP/SMS/TOM) */
/* TS 04.65 Section 5.1.2 Table 2: Service primitives used by SNDCP */
enum gprs_llc_primitive {
/* GMM <-> LLME */
LLGMM_ASSIGN_REQ, /* GMM tells us new TLLI: TLLI old, TLLI new, Kc, CiphAlg */
LLGMM_RESET_REQ, /* GMM tells us to perform XID negotiation: TLLI */
LLGMM_RESET_CNF, /* LLC informs GMM that XID has completed: TLLI */
LLGMM_SUSPEND_REQ, /* GMM tells us MS has suspended: TLLI, Page */
LLGMM_RESUME_REQ, /* GMM tells us MS has resumed: TLLI */
LLGMM_PAGE_IND, /* LLC asks GMM to page MS: TLLI */
LLGMM_IOV_REQ, /* GMM tells us to perform XID: TLLI */
LLGMM_STATUS_IND, /* LLC informs GMM about error: TLLI, Cause */
/* LLE <-> (GMM/SNDCP/SMS/TOM) */
LL_RESET_IND, /* TLLI */
LL_ESTABLISH_REQ, /* TLLI, XID Req */
LL_ESTABLISH_IND, /* TLLI, XID Req, N201-I, N201-U */
LL_ESTABLISH_RESP, /* TLLI, XID Negotiated */
LL_ESTABLISH_CONF, /* TLLI, XID Neg, N201-i, N201-U */
LL_RELEASE_REQ, /* TLLI, Local */
LL_RELEASE_IND, /* TLLI, Cause */
LL_RELEASE_CONF, /* TLLI */
LL_XID_REQ, /* TLLI, XID Requested */
LL_XID_IND, /* TLLI, XID Req, N201-I, N201-U */
LL_XID_RESP, /* TLLI, XID Negotiated */
LL_XID_CONF, /* TLLI, XID Neg, N201-I, N201-U */
LL_DATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */
LL_DATA_IND, /* TLLI, SN-PDU */
LL_DATA_CONF, /* TLLI, Ref */
LL_UNITDATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */
LL_UNITDATA_IND, /* TLLI, SN-PDU */
LL_STATUS_IND, /* TLLI, Cause */
};
/* Section 4.5.2 Logical Link States + Annex C.2 */
enum gprs_llc_lle_state {
GPRS_LLES_UNASSIGNED = 1, /* No TLLI yet */
GPRS_LLES_ASSIGNED_ADM = 2, /* TLLI assigned */
GPRS_LLES_LOCAL_EST = 3, /* Local Establishment */
GPRS_LLES_REMOTE_EST = 4, /* Remote Establishment */
GPRS_LLES_ABM = 5,
GPRS_LLES_LOCAL_REL = 6, /* Local Release */
GPRS_LLES_TIMER_REC = 7, /* Timer Recovery */
};
enum gprs_llc_llme_state {
GPRS_LLMS_UNASSIGNED = 1, /* No TLLI yet */
GPRS_LLMS_ASSIGNED = 2, /* TLLI assigned */
};
/* Section 8.9.9 LLC layer parameter default values */
struct gprs_llc_params {
uint16_t iov_i_exp;
uint16_t t200_201;
uint16_t n200;
uint16_t n201_u;
uint16_t n201_i;
uint16_t mD;
uint16_t mU;
uint16_t kD;
uint16_t kU;
};
/* Section 4.7.1: Logical Link Entity: One per DLCI (TLLI + SAPI) */
struct gprs_llc_lle {
struct llist_head list;
uint32_t sapi;
struct gprs_llc_llme *llme;
enum gprs_llc_lle_state state;
struct osmo_timer_list t200;
struct osmo_timer_list t201; /* wait for acknowledgement */
uint16_t v_sent;
uint16_t v_ack;
uint16_t v_recv;
uint16_t vu_send;
uint16_t vu_recv;
/* non-standard LLC state */
uint16_t vu_recv_last;
uint16_t vu_recv_duplicates;
/* Overflow Counter for ABM */
uint32_t oc_i_send;
uint32_t oc_i_recv;
/* Overflow Counter for unconfirmed transfer */
uint32_t oc_ui_send;
uint32_t oc_ui_recv;
unsigned int retrans_ctr;
struct gprs_llc_params params;
};
#define NUM_SAPIS 16
struct gprs_llc_llme {
struct llist_head list;
enum gprs_llc_llme_state state;
uint32_t tlli;
uint32_t old_tlli;
/* Crypto parameters */
enum gprs_ciph_algo algo;
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;
};
#define GPRS_LLME_RESET_AGE (0)
extern struct llist_head gprs_llc_llmes;
/* LLC low level types */
enum gprs_llc_cmd {
GPRS_LLC_NULL,
GPRS_LLC_RR,
GPRS_LLC_ACK,
GPRS_LLC_RNR,
GPRS_LLC_SACK,
GPRS_LLC_DM,
GPRS_LLC_DISC,
GPRS_LLC_UA,
GPRS_LLC_SABM,
GPRS_LLC_FRMR,
GPRS_LLC_XID,
GPRS_LLC_UI,
};
struct gprs_llc_hdr_parsed {
uint8_t sapi;
uint8_t is_cmd:1,
ack_req:1,
is_encrypted:1;
uint32_t seq_rx;
uint32_t seq_tx;
uint32_t fcs;
uint32_t fcs_calc;
uint8_t *data;
uint16_t data_len;
uint16_t crc_length;
enum gprs_llc_cmd cmd;
};
/* BSSGP-UL-UNITDATA.ind */
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,
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,
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);
int gprs_llgmm_unassign(struct gprs_llc_llme *llme);
int gprs_llc_init(const char *cipher_plugin_path);
int gprs_llc_vty_init(void);
/**
* \short Check if N(U) should be considered a retransmit
*
* Implements the range check as of GSM 04.64 8.4.2
* Receipt of unacknowledged information.
*
* @returns Returns 1 if (V(UR)-32) <= N(U) < V(UR)
* @param nu N(U) unconfirmed sequence number of the UI frame
* @param vur V(UR) unconfirmend received state variable
*/
static inline int gprs_llc_is_retransmit(uint16_t nu, uint16_t vur)
{
int delta = (vur - nu) & 0x1ff;
return 0 < delta && delta < 32;
}
/* 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,
uint8_t *llc_hdr, int len);
void gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph, struct gprs_llc_lle *lle);
int gprs_llc_fcs(uint8_t *data, unsigned int len);
/* LLME handling routines */
struct llist_head *gprs_llme_list(void);
struct gprs_llc_lle *gprs_lle_get_or_create(const uint32_t tlli, uint8_t sapi);
#endif

View File

@ -1,57 +0,0 @@
/* 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

@ -1,478 +0,0 @@
#ifndef _GPRS_SGSN_H
#define _GPRS_SGSN_H
#include <stdint.h>
#include <netinet/in.h>
#include <osmocom/core/timer.h>
#include <osmocom/gsm/gsm48.h>
#include <osmocom/crypt/gprs_cipher.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <openbsc/gsm_data.h>
#define GSM_EXTENSION_LENGTH 15
#define GSM_APN_LENGTH 102
struct gprs_llc_lle;
struct ctrl_handle;
struct gprs_subscr;
enum gsm48_gsm_cause;
/* TS 04.08 4.1.3.3 GMM mobility management states on the network side */
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 */
GMM_REGISTERED_SUSPENDED, /* 4.1.3.3.2.2 */
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,
MM_READY,
MM_STANDBY,
};
enum gprs_mm_ctr {
GMM_CTR_PKTS_SIG_IN,
GMM_CTR_PKTS_SIG_OUT,
GMM_CTR_PKTS_UDATA_IN,
GMM_CTR_PKTS_UDATA_OUT,
GMM_CTR_BYTES_UDATA_IN,
GMM_CTR_BYTES_UDATA_OUT,
GMM_CTR_PDP_CTX_ACT,
GMM_CTR_SUSPEND,
GMM_CTR_PAGING_PS,
GMM_CTR_PAGING_CS,
GMM_CTR_RA_UPDATE,
};
enum gprs_pdp_ctx {
PDP_CTR_PKTS_UDATA_IN,
PDP_CTR_PKTS_UDATA_OUT,
PDP_CTR_BYTES_UDATA_IN,
PDP_CTR_BYTES_UDATA_OUT,
};
enum gprs_t3350_mode {
GMM_T3350_MODE_NONE,
GMM_T3350_MODE_ATT,
GMM_T3350_MODE_RAU,
GMM_T3350_MODE_PTMSI_REALL,
};
/* Authorization/ACL handling */
enum sgsn_auth_state {
SGSN_AUTH_UNKNOWN,
SGSN_AUTH_AUTHENTICATE,
SGSN_AUTH_UMTS_RESYNC,
SGSN_AUTH_ACCEPTED,
SGSN_AUTH_REJECTED
};
#define MS_RADIO_ACCESS_CAPA
enum sgsn_ggsn_lookup_state {
SGSN_GGSN_2DIGIT,
SGSN_GGSN_3DIGIT,
};
struct sgsn_ggsn_lookup {
int state;
struct sgsn_mm_ctx *mmctx;
/* APN string */
char apn_str[GSM_APN_LENGTH];
/* the original data */
struct msgb *orig_msg;
struct tlv_parsed tp;
/* for dealing with re-transmissions */
uint8_t nsapi;
uint8_t sapi;
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 ranap_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_gmm_state gmm_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;
char imei[GSM23003_IMEISV_NUM_DIGITS+1];
/* Opt: Software Version Numbber / TS 23.195 */
char msisdn[GSM_EXTENSION_LENGTH];
struct gprs_ra_id ra;
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 ranap_ue_conn_ctx *ue_ctx;
struct service_info service;
} iu;
/* VLR number */
uint32_t new_sgsn_addr;
/* Authentication Triplet */
struct gsm_auth_tuple auth_triplet;
/* Kc */
/* 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;
/* Access Restriction */
/* GPRS CSI (CAMEL) */
/* MG-CSI (CAMEL) */
/* Subscribed UE-AMBR */
/* UE-AMBR */
/* APN Subscribed */
struct llist_head pdp_list;
struct rate_ctr_group *ctrg;
struct osmo_timer_list timer;
unsigned int T; /* Txxxx number */
unsigned int num_T_exp; /* number of consecutive T expirations */
enum gprs_t3350_mode t3350_mode;
uint8_t t3370_id_type;
uint8_t pending_req; /* the request's message type */
/* TODO: There isn't much semantic difference between t3350_mode
* (refers to the timer) and pending_req (refers to the procedure),
* where mm->T == 3350 => mm->t3350_mode == f(mm->pending_req). Check
* whether one of them can be dropped. */
enum sgsn_auth_state auth_state;
int is_authenticated;
/* the string representation of the current hlr */
char hlr[GSM_EXTENSION_LENGTH];
/* the current GGSN look-up operation */
struct sgsn_ggsn_lookup *ggsn_lookup;
struct gprs_subscr *subscr;
};
#define LOGMMCTXP(level, mm, fmt, args...) \
LOGP(DMM, level, "MM(%s/%08x) " fmt, (mm) ? (mm)->imsi : "---", \
(mm) ? (mm)->p_tmsi : GSM_RESERVED_TMSI, ## args)
/* 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);
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,
const struct gprs_ra_id *raid);
/* Allocate a new SGSN MM context */
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(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,
struct tlv_parsed *tp,
enum gsm48_gsm_cause *gsm_cause,
char *apn_str);
enum pdp_ctx_state {
PDP_STATE_NONE,
PDP_STATE_CR_REQ,
PDP_STATE_CR_CONF,
/* 04.08 / Figure 6.2 / 6.1.2.2 */
PDP_STATE_INACT_PEND,
PDP_STATE_INACTIVE = PDP_STATE_NONE,
};
enum pdp_type {
PDP_TYPE_NONE,
PDP_TYPE_ETSI_PPP,
PDP_TYPE_IANA_IPv4,
PDP_TYPE_IANA_IPv6,
};
struct sgsn_pdp_ctx {
struct llist_head list; /* list_head for mmctx->pdp_list */
struct llist_head g_list; /* list_head for global list */
struct sgsn_mm_ctx *mm; /* back pointer to MM CTX */
int destroy_ggsn; /* destroy it on destruction */
struct sgsn_ggsn_ctx *ggsn; /* which GGSN serves this PDP */
struct rate_ctr_group *ctrg;
//unsigned int id;
struct pdp_t *lib; /* pointer to libgtp PDP ctx */
enum pdp_ctx_state state;
enum pdp_type type;
uint32_t address;
char *apn_subscribed;
//char *apn_used;
uint16_t nsapi; /* SNDCP */
uint16_t sapi; /* LLC */
uint8_t ti; /* transaction identifier */
int vplmn_allowed;
uint32_t qos_profile_subscr;
//uint32_t qos_profile_req;
//uint32_t qos_profile_neg;
uint8_t radio_prio;
//uint32_t charging_id;
struct osmo_timer_list timer;
unsigned int T; /* Txxxx number */
unsigned int num_T_exp; /* number of consecutive T expirations */
struct osmo_timer_list cdr_timer; /* CDR record wird timer */
struct timespec cdr_start; /* The start of the CDR */
uint64_t cdr_bytes_in;
uint64_t cdr_bytes_out;
uint32_t cdr_charging_id;
};
#define LOGPDPCTXP(level, pdp, fmt, args...) \
LOGP(DGPRS, level, "PDP(%s/%u) " \
fmt, (pdp)->mm ? (pdp)->mm->imsi : "---", (pdp)->ti, ## args)
/* look up PDP context by MM context and NSAPI */
struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
uint8_t nsapi);
/* look up PDP context by MM context and transaction ID */
struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
uint8_t tid);
struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
uint8_t nsapi);
void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp);
void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp);
struct sgsn_ggsn_ctx {
struct llist_head list;
uint32_t id;
unsigned int gtp_version;
struct in_addr remote_addr;
int remote_restart_ctr;
struct gsn_t *gsn;
};
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id);
void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc);
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id);
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr);
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id);
struct apn_ctx {
struct llist_head list;
struct sgsn_ggsn_ctx *ggsn;
char *name;
char *imsi_prefix;
char *description;
};
struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix);
void sgsn_apn_ctx_free(struct apn_ctx *actx);
struct apn_ctx *sgsn_apn_ctx_by_name(const char *name, const char *imsi_prefix);
struct apn_ctx *sgsn_apn_ctx_match(const char *name, const char *imsi_prefix);
extern struct llist_head sgsn_mm_ctxts;
extern struct llist_head sgsn_ggsn_ctxts;
extern struct llist_head sgsn_apn_ctxts;
extern struct llist_head sgsn_pdp_ctxts;
uint32_t sgsn_alloc_ptmsi(void);
void sgsn_inst_init(void);
/* High-level function to be called in case a GGSN has disappeared or
* ottherwise lost state (recovery procedure) */
int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn);
char *gprs_pdpaddr2str(uint8_t *pdpa, uint8_t len);
/*
* ctrl interface related work
*/
struct gsm_network;
struct ctrl_handle *sgsn_controlif_setup(struct gsm_network *,
const char *bind_addr, uint16_t port);
int sgsn_ctrl_cmds_install(void);
/*
* Authorization/ACL handling
*/
struct imsi_acl_entry {
struct llist_head list;
char imsi[16+1];
};
/* see GSM 09.02, 17.7.1, PDP-Context and GPRSSubscriptionData */
/* see GSM 09.02, B.1, gprsSubscriptionData */
struct sgsn_subscriber_pdp_data {
struct llist_head list;
unsigned int context_id;
uint16_t pdp_type;
char apn_str[GSM_APN_LENGTH];
uint8_t qos_subscribed[20];
size_t qos_subscribed_len;
uint8_t pdp_charg[2];
bool has_pdp_charg;
};
struct sgsn_subscriber_data {
struct sgsn_mm_ctx *mm;
struct gsm_auth_tuple auth_triplets[5];
int auth_triplets_updated;
struct llist_head pdp_list;
int error_cause;
uint8_t msisdn[9];
size_t msisdn_len;
uint8_t hlr[9];
size_t hlr_len;
uint8_t pdp_charg[2];
bool has_pdp_charg;
};
#define SGSN_ERROR_CAUSE_NONE (-1)
#define LOGGSUBSCRP(level, subscr, fmt, args...) \
LOGP(DGPRS, level, "SUBSCR(%s) " fmt, \
(subscr) ? (subscr)->imsi : "---", \
## args)
struct sgsn_config;
struct sgsn_instance;
extern const struct value_string *sgsn_auth_state_names;
void sgsn_auth_init(void);
struct imsi_acl_entry *sgsn_acl_lookup(const char *imsi, struct sgsn_config *cfg);
int sgsn_acl_add(const char *imsi, struct sgsn_config *cfg);
int sgsn_acl_del(const char *imsi, struct sgsn_config *cfg);
/* Request authorization */
int sgsn_auth_request(struct sgsn_mm_ctx *mm);
enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mm);
void sgsn_auth_update(struct sgsn_mm_ctx *mm);
struct gsm_auth_tuple *sgsn_auth_get_tuple(struct sgsn_mm_ctx *mmctx,
unsigned key_seq);
/*
* GPRS subscriber data
*/
#define GPRS_SUBSCRIBER_FIRST_CONTACT 0x00000001
#define GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING (1 << 16)
#define GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING (1 << 17)
#define GPRS_SUBSCRIBER_CANCELLED (1 << 18)
#define GPRS_SUBSCRIBER_ENABLE_PURGE (1 << 19)
#define GPRS_SUBSCRIBER_UPDATE_PENDING_MASK ( \
GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING | \
GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING \
)
int gprs_subscr_init(struct sgsn_instance *sgi);
int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx);
int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx,
const uint8_t *auts,
const uint8_t *auts_rand);
int gprs_subscr_auth_sync(struct gprs_subscr *subscr,
const uint8_t *auts, const uint8_t *auts_rand);
void gprs_subscr_cleanup(struct gprs_subscr *subscr);
struct gprs_subscr *gprs_subscr_get_or_create(const char *imsi);
struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx( struct sgsn_mm_ctx *mmctx);
struct gprs_subscr *gprs_subscr_get_by_imsi(const char *imsi);
void gprs_subscr_cancel(struct gprs_subscr *subscr);
void gprs_subscr_update(struct gprs_subscr *subscr);
void gprs_subscr_update_auth_info(struct gprs_subscr *subscr);
int gprs_subscr_rx_gsup_message(struct msgb *msg);
/* Called on subscriber data updates */
void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx);
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

@ -1,79 +0,0 @@
#ifndef _INT_SNDCP_H
#define _INT_SNDCP_H
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
/* A fragment queue header, maintaining list of fragments for one N-PDU */
struct defrag_state {
/* PDU number for which the defragmentation state applies */
uint16_t npdu;
/* highest segment number we have received so far */
uint8_t highest_seg;
/* bitmask of the segments we already have */
uint32_t seg_have;
/* do we still expect more segments? */
unsigned int no_more;
/* total length of all segments together */
unsigned int tot_len;
/* linked list of defrag_queue_entry: one for each fragment */
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 */
enum sndcp_rx_state {
SNDCP_RX_S_FIRST,
SNDCP_RX_S_SUBSEQ,
SNDCP_RX_S_DISCARD,
};
struct gprs_sndcp_entity {
struct llist_head list;
/* FIXME: move this RA_ID up to the LLME or even higher */
struct gprs_ra_id ra_id;
/* reference to the LLC Entity below this SNDCP entity */
struct gprs_llc_lle *lle;
/* The NSAPI we shall use on top of LLC */
uint8_t nsapi;
/* NPDU number for the GTP->SNDCP side */
uint16_t tx_npdu_nr;
/* SNDCP eeceiver state */
enum sndcp_rx_state rx_state;
/* The defragmentation queue */
struct defrag_state defrag;
};
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

@ -1,82 +0,0 @@
/* 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

@ -1,53 +0,0 @@
/* 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

@ -1,46 +0,0 @@
/* 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

@ -1,218 +0,0 @@
/* 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 DEFAULT_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, int version);
/* Transform an SNDCP-XID message (src) into a list of SNDCP-XID fields */
struct llist_head *gprs_sndcp_parse_xid(int *version,
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

@ -1,31 +0,0 @@
/* GPRS subscriber details for use in SGSN land */
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
extern struct llist_head * const gprs_subscribers;
struct gprs_subscr {
struct llist_head entry;
int use_count;
char imsi[GSM23003_IMSI_MAX_DIGITS+1];
uint32_t tmsi;
char imei[GSM23003_IMEISV_NUM_DIGITS+1];
bool authorized;
bool keep_in_ram;
uint32_t flags;
uint16_t lac;
struct sgsn_subscriber_data *sgsn_data;
};
struct gprs_subscr *_gprs_subscr_get(struct gprs_subscr *gsub,
const char *file, int line);
struct gprs_subscr *_gprs_subscr_put(struct gprs_subscr *gsub,
const char *file, int line);
#define gprs_subscr_get(gsub) _gprs_subscr_get(gsub, __BASE_FILE__, __LINE__)
#define gprs_subscr_put(gsub) _gprs_subscr_put(gsub, __BASE_FILE__, __LINE__)

View File

@ -1,44 +0,0 @@
/* GPRS utility functions */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010-2014 by On-Waves
* (C) 2013 by Holger Hans Peter Freyther
* 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/>.
*
*/
#pragma once
#include <stdint.h>
#include <sys/types.h>
struct msgb;
struct gprs_ra_id;
struct msgb *gprs_msgb_copy(const struct msgb *msg, const char *name);
int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area,
size_t old_size, size_t new_size);
int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str);
/* GSM 04.08, 10.5.7.3 GPRS Timer */
int gprs_tmr_to_secs(uint8_t tmr);
uint8_t gprs_secs_to_tmr_floor(int secs);
int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len);
int gprs_is_mi_imsi(const uint8_t *value, size_t value_len);
int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi);
void gprs_parse_tmsi(const uint8_t *value, uint32_t *tmsi);
int gprs_ra_id_equals(const struct gprs_ra_id *id1, const struct gprs_ra_id *id2);

View File

@ -1,85 +0,0 @@
#ifndef _GSM_04_08_H
#define _GSM_04_08_H
#include <osmocom/gsm/gsm48.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <openbsc/meas_rep.h>
struct msgb;
struct gsm_bts;
struct gsm_network;
struct gsm_trans;
struct gsm_subscriber_connection;
struct amr_multirate_conf;
struct amr_mode;
struct bsc_subscr;
#define GSM48_ALLOC_SIZE 2048
#define GSM48_ALLOC_HEADROOM 256
static inline struct msgb *gsm48_msgb_alloc_name(const char *name)
{
return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM,
name);
}
void cm_service_request_concludes(struct gsm_subscriber_connection *conn,
struct msgb *msg);
/* config options controlling the behaviour of the lower leves */
void gsm0408_allow_everyone(int allow);
void gsm0408_clear_all_trans(struct gsm_network *net, int protocol);
int gsm0408_dispatch(struct gsm_subscriber_connection *conn, struct msgb *msg);
int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id);
enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *bts, uint8_t ra);
/* don't use "enum gsm_chreq_reason_t" to avoid circular dependency */
int get_reason_by_chreq(uint8_t ra, int neci);
void gsm_net_update_ctype(struct gsm_network *net);
int gsm48_tx_mm_info(struct gsm_subscriber_connection *conn);
int gsm48_tx_mm_auth_req(struct gsm_subscriber_connection *conn, uint8_t *rand,
uint8_t *autn, int key_seq);
int gsm48_tx_mm_auth_rej(struct gsm_subscriber_connection *conn);
int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn);
int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn,
enum gsm48_reject_value value);
int gsm48_send_rr_release(struct gsm_lchan *lchan);
int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv);
int gsm48_send_rr_app_info(struct gsm_subscriber_connection *conn, uint8_t apdu_id,
uint8_t apdu_len, const uint8_t *apdu);
int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_class);
int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
uint8_t power_command, uint8_t ho_ref);
int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg);
/* convert a ASCII phone number to call-control BCD */
int encode_bcd_number(uint8_t *bcd_lv, uint8_t max_len,
int h_len, const char *input);
int decode_bcd_number(char *output, int output_len, const uint8_t *bcd_lv,
int h_len);
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);
int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t lchan_mode);
int gsm48_rx_rr_modif_ack(struct msgb *msg);
int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg);
struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value);
struct msgb *gsm48_create_loc_upd_rej(uint8_t cause);
void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd,
const struct gsm_lchan *lchan);
void release_security_operation(struct gsm_subscriber_connection *conn);
void allocate_security_operation(struct gsm_subscriber_connection *conn);
int gsm48_multirate_config(uint8_t *lv, const struct amr_multirate_conf *mr, const struct amr_mode *modes);
int gsm48_tch_rtp_create(struct gsm_trans *trans);
#endif

View File

@ -0,0 +1,32 @@
#pragma once
void gsm_net_update_ctype(struct gsm_network *network);
enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *network, uint8_t ra);
int get_reason_by_chreq(uint8_t ra, int neci);
int gsm48_send_rr_release(struct gsm_lchan *lchan);
int send_siemens_mrpci(struct gsm_lchan *lchan,
uint8_t *classmark2_lv);
int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn,
struct msgb *msg, struct bsc_subscr *bsub);
int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv);
void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd,
const struct gsm_lchan *lchan);
int gsm48_multirate_config(uint8_t *lv, const struct amr_multirate_conf *mr, const struct amr_mode *modes);
int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
uint8_t power_command, uint8_t ho_ref);
int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command);
int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t mode);
int gsm48_rx_rr_modif_ack(struct msgb *msg);
int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg);
int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn);
int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn,
enum gsm48_reject_value value);
#define GSM48_ALLOC_SIZE 2048
#define GSM48_ALLOC_HEADROOM 256
static inline struct msgb *gsm48_msgb_alloc_name(const char *name)
{
return msgb_alloc_headroom(GSM48_ALLOC_SIZE, GSM48_ALLOC_HEADROOM,
name);
}

View File

@ -1,53 +0,0 @@
#ifndef _GSM_04_11_H
#define _GSM_04_11_H
#include <osmocom/gsm/protocol/gsm_04_11.h>
struct vlr_subscr;
struct gsm_subscriber_connection;
struct gsm_trans;
#define UM_SAPI_SMS 3 /* See GSM 04.05/04.06 */
/* SMS deliver PDU */
struct sms_deliver {
uint8_t mti:2; /* message type indicator */
uint8_t mms:1; /* more messages to send */
uint8_t rp:1; /* reply path */
uint8_t udhi:1; /* user data header indicator */
uint8_t sri:1; /* status report indication */
uint8_t *orig_addr; /* originating address */
uint8_t pid; /* protocol identifier */
uint8_t dcs; /* data coding scheme */
/* service centre time stamp */
uint8_t ud_len; /* user data length */
uint8_t *user_data; /* user data */
uint8_t msg_ref; /* message reference */
uint8_t *smsc;
};
struct msgb;
int gsm0411_rcv_sms(struct gsm_subscriber_connection *conn, struct msgb *msg);
struct gsm_sms *sms_alloc(void);
void sms_free(struct gsm_sms *sms);
struct gsm_sms *sms_from_text(struct vlr_subscr *receiver,
struct vlr_subscr *sender,
int dcs, const char *text);
void _gsm411_sms_trans_free(struct gsm_trans *trans);
int gsm411_send_sms_subscr(struct vlr_subscr *vsub,
struct gsm_sms *sms);
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(uint8_t *next_rp_ref);
int gsm411_send_rp_ack(struct gsm_trans *trans, uint8_t msg_ref);
int gsm411_send_rp_error(struct gsm_trans *trans, uint8_t msg_ref,
uint8_t cause);
#endif

View File

@ -31,6 +31,7 @@ struct bsc_subscr;
struct vlr_instance;
struct vlr_subscr;
struct ranap_ue_conn_ctx;
struct gprs_ra_id;
#define OBSC_LINKID_CB(__msgb) (__msgb)->cb[3]

View File

@ -25,6 +25,7 @@
#endif
#include <openbsc/common_cs.h>
#include <openbsc/meas_rep.h>
/* 16 is the max. number of SI2quater messages according to 3GPP TS 44.018 Table 10.5.2.33b.1:
4-bit index is used (2#1111 = 10#15) */

View File

@ -1,63 +0,0 @@
/* GPRS Subscriber Update Protocol client */
/* (C) 2014 by Sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* Author: Jacob Erlbeck
*
* 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 <osmocom/core/timer.h>
#include <openbsc/oap_client.h>
#define GSUP_CLIENT_RECONNECT_INTERVAL 10
#define GSUP_CLIENT_PING_INTERVAL 20
struct msgb;
struct ipa_client_conn;
struct gsup_client;
/* Expects message in msg->l2h */
typedef int (*gsup_client_read_cb_t)(struct gsup_client *gsupc,
struct msgb *msg);
struct gsup_client {
const char *unit_name;
struct ipa_client_conn *link;
gsup_client_read_cb_t read_cb;
void *data;
struct oap_client_state oap_state;
struct osmo_timer_list ping_timer;
struct osmo_timer_list connect_timer;
int is_connected;
int got_ipa_pong;
};
struct gsup_client *gsup_client_create(const char *unit_name,
const char *ip_addr,
unsigned int tcp_port,
gsup_client_read_cb_t read_cb,
struct oap_client_config *oapc_config);
void gsup_client_destroy(struct gsup_client *gsupc);
int gsup_client_send(struct gsup_client *gsupc, struct msgb *msg);
struct msgb *gsup_client_msgb_alloc(void);

View File

@ -1,523 +0,0 @@
/* GTP Hub Implementation */
/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr
*
* 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 <sys/socket.h>
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/rate_ctr.h>
#include <openbsc/gprs_sgsn.h>
/* support */
/* TODO move to osmocom/core/socket.c ? */
#include <netdb.h> /* for IPPROTO_* etc */
struct osmo_sockaddr {
struct sockaddr_storage a;
socklen_t l;
};
/* TODO move to osmocom/core/socket.c ? */
/*! \brief Initialize a sockaddr
* \param[out] addr Valid osmo_sockaddr pointer to write result to
* \param[in] family Address Family like AF_INET, AF_INET6, AF_UNSPEC
* \param[in] type Socket type like SOCK_DGRAM, SOCK_STREAM
* \param[in] proto Protocol like IPPROTO_TCP, IPPROTO_UDP
* \param[in] host Remote host name or IP address in string form
* \param[in] port Remote port number in host byte order
* \returns 0 on success, otherwise an error code (from getaddrinfo()).
*
* Copy the first result from a getaddrinfo() call with the given parameters to
* *addr and *addr_len. On error, do not change *addr and return nonzero.
*/
int osmo_sockaddr_init(struct osmo_sockaddr *addr,
uint16_t family, uint16_t type, uint8_t proto,
const char *host, uint16_t port);
/* Conveniently pass AF_UNSPEC, SOCK_DGRAM and IPPROTO_UDP to
* osmo_sockaddr_init(). */
static inline int osmo_sockaddr_init_udp(struct osmo_sockaddr *addr,
const char *host, uint16_t port)
{
return osmo_sockaddr_init(addr, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
host, port);
}
/*! \brief convert sockaddr to human readable string.
* \param[out] addr_str Valid pointer to a buffer of length addr_str_len.
* \param[in] addr_str_len Size of buffer addr_str points at.
* \param[out] port_str Valid pointer to a buffer of length port_str_len.
* \param[in] port_str_len Size of buffer port_str points at.
* \param[in] addr Binary representation as returned by osmo_sockaddr_init().
* \param[in] flags flags as passed to getnameinfo().
* \returns 0 on success, an error code on error.
*
* Return the IPv4 or IPv6 address string and the port (a.k.a. service) string
* representations of the given struct osmo_sockaddr in two caller provided
* char buffers. Flags of (NI_NUMERICHOST | NI_NUMERICSERV) return numeric
* address and port. Either one of addr_str or port_str may be NULL, in which
* case nothing is returned there.
*
* See also osmo_sockaddr_to_str() (less flexible, but much more convenient). */
int osmo_sockaddr_to_strs(char *addr_str, size_t addr_str_len,
char *port_str, size_t port_str_len,
const struct osmo_sockaddr *addr,
int flags);
/*! \brief concatenate the parts returned by osmo_sockaddr_to_strs().
* \param[in] addr Binary representation as returned by osmo_sockaddr_init().
* \param[in] buf A buffer to use for string operations.
* \param[in] buf_len Length of the buffer.
* \returns Address string (in buffer).
*
* Compose a string of the numeric IP-address and port represented by *addr of
* the form "<ip-addr> port <port>". The returned string is valid until the
* next invocation of this function.
*/
const char *osmo_sockaddr_to_strb(const struct osmo_sockaddr *addr,
char *buf, size_t buf_len);
/*! \brief conveniently return osmo_sockaddr_to_strb() in a static buffer.
* \param[in] addr Binary representation as returned by osmo_sockaddr_init().
* \returns Address string in static buffer.
*
* See osmo_sockaddr_to_strb().
*
* Note: only one osmo_sockaddr_to_str() call will work per print/log
* statement. For two or more, use osmo_sockaddr_to_strb() with a separate
* buffer each.
*/
const char *osmo_sockaddr_to_str(const struct osmo_sockaddr *addr);
/*! \brief compare two osmo_sockaddr.
* \param[in] a The first address to compare.
* \param[in] b The other address to compare.
* \returns 0 if equal, otherwise -1 or 1.
*/
int osmo_sockaddr_cmp(const struct osmo_sockaddr *a,
const struct osmo_sockaddr *b);
/*! \brief Overwrite *dst with *src.
* Like memcpy(), but copy only the valid bytes. */
void osmo_sockaddr_copy(struct osmo_sockaddr *dst,
const struct osmo_sockaddr *src);
/* general */
enum gtphub_plane_idx {
GTPH_PLANE_CTRL = 0,
GTPH_PLANE_USER = 1,
GTPH_PLANE_N
};
enum gtphub_side_idx {
GTPH_SIDE_SGSN = 0,
GTPH_SIDE_GGSN = 1,
GTPH_SIDE_N
};
#define for_each_side(I) for (I = 0; I < GTPH_SIDE_N; I++)
#define for_each_plane(I) for (I = 0; I < GTPH_PLANE_N; I++)
#define for_each_side_and_plane(I,J) for_each_side(I) for_each_plane(J)
static inline int other_side_idx(int side_idx)
{
return (side_idx + 1) & 1;
}
extern const char* const gtphub_plane_idx_names[GTPH_PLANE_N];
extern const uint16_t gtphub_plane_idx_default_port[GTPH_PLANE_N];
extern const char* const gtphub_side_idx_names[GTPH_SIDE_N];
/* A host address in the form that is expected in the 7.7.32 GSN Address IE.
* len is either 4 (IPv4) or 16 (IPv6), any other value is invalid. If no
* address is set, len shall be 0. */
struct gsn_addr {
uint16_t len;
uint8_t buf[16];
};
void gsn_addr_copy(struct gsn_addr *gsna, const struct gsn_addr *src);
int gsn_addr_from_str(struct gsn_addr *gsna, const char *numeric_addr_str);
/* Return gsna in numeric string form, in a static buffer. */
const char *gsn_addr_to_str(const struct gsn_addr *gsna);
/* note: strbuf_len doesn't need to be larger than INET6_ADDRSTRLEN + 1. */
const char *gsn_addr_to_strb(const struct gsn_addr *gsna,
char *strbuf, int strbuf_len);
/* Return 1 on match, zero otherwise. */
int gsn_addr_same(const struct gsn_addr *a, const struct gsn_addr *b);
/* Decode sa to gsna. Return 0 on success. If port is non-NULL, the port number
* from sa is also returned. */
int gsn_addr_from_sockaddr(struct gsn_addr *gsna, uint16_t *port,
const struct osmo_sockaddr *sa);
/* expiry */
struct expiring_item;
typedef void (*del_cb_t)(struct expiring_item *);
struct expiring_item {
struct llist_head entry;
time_t expiry;
del_cb_t del_cb;
};
struct expiry {
int expiry_in_seconds;
struct llist_head items;
};
/* Initialize an expiry queue. */
void expiry_init(struct expiry *exq, int expiry_in_seconds);
/* Add a new mapping, or restart the expiry timeout for an already listed
* mapping. */
void expiry_add(struct expiry *exq, struct expiring_item *item, time_t now);
/* Initialize to all-empty; must be called before using the item in any way. */
void expiring_item_init(struct expiring_item *item);
/* Remove the given item from its expiry queue, and call item->del_cb, if set.
* This sets item->del_cb to NULL and is harmless when run a second time on the
* same item, so the del_cb may choose to call this function, too, to allow
* deleting items from several code paths. */
void expiring_item_del(struct expiring_item *item);
/* Carry out due expiry of mappings. Must be invoked regularly.
* 'now' is the current clock count in seconds and must correspond to the clock
* count passed to nr_map_add(). A monotonous clock counter should be used. */
int expiry_tick(struct expiry *exq, time_t now);
/* Expire all items. */
void expiry_clear(struct expiry *exq);
/* number map */
/* A number map assigns a "random" mapped number to each user provided number.
* If the same number is requested multiple times, the same mapped number is
* returned.
*
* Number maps plug into possibly shared pools and expiry queues, for example:
*
* mapA -----------+-> pool1 <-+-- mapB
* {10->1, 11->5} | {1, 2, 3, ...} | {10->2, 11->3}
* | |
* | |
* /-> \-> expiry1 <-/
* | (30 seconds)
* |
* mapC -------+-----> pool2 <-+-- mapD
* {10->1, 11->3} {1, 2, 3, ...} | {10->2, 11->5}
* |
* expiry2 <-/
* (60 seconds)
*
* A map contains mappings ("10->1"). Each map needs a number pool, which can
* be shared with other maps. Each new mapping receives a number from the pool,
* which is then unavailable to any other map using the same pool.
*
* A map may point at an expiry queue, in which case all mappings added to it
* are also appended to the expiry queue (using a separate llist entry in the
* mapping). Any number of maps may submit to the same expiry queue, if they
* desire the same expiry timeout. An expiry queue stores the mappings in
* chronological order, so that expiry checking is needed only from the start
* of the queue; hence only mappings with identical expiry timeout can be added
* to the same expiry queue. Upon expiry, a mapping is dropped from the map it
* was submitted at. expiry_tick() needs to be called regularly for each expiry
* queue.
*
* A nr_mapping can be embedded in a larger struct: each mapping can have a
* distinct destructor (del_cb), and each del_cb can figure out the container
* struct's address and free that upon expiry or manual deletion. So in expiry
* queues (and even maps), mappings of different container types can be mixed.
* This can help to drastically reduce the amount of unnecessary visits during
* expiry checking, for the case that no expiry is pending. An expiry queue
* always knows which mappings to expire next, because they are right at the
* start of its list.
*
* Mapping allocation and a del_cb are provided by the caller. If del_cb is
* NULL, no deallocation will be done (allowing statically allocated entries).
*/
typedef unsigned int nr_t;
/* Generator for unused numbers. So far this counts upwards from zero, but the
* implementation may change in the future. Treat this like an opaque struct.
* If this becomes random, the tests need to be fixed. */
struct nr_pool {
nr_t last_nr;
nr_t nr_min;
nr_t nr_max;
};
struct nr_mapping {
struct llist_head entry;
struct expiring_item expiry_entry;
void *origin;
nr_t orig;
nr_t repl;
};
struct nr_map {
struct nr_pool *pool; /* multiple nr_maps can share a nr_pool. */
struct expiry *add_items_to_expiry;
struct llist_head mappings;
};
void nr_pool_init(struct nr_pool *pool, nr_t nr_min, nr_t nr_max);
/* Return the next unused number from the nr_pool. */
nr_t nr_pool_next(struct nr_pool *pool);
/* Initialize the nr_mapping to zero/empty values. */
void nr_mapping_init(struct nr_mapping *mapping);
/* Remove the given mapping from its parent map and expiry queue, and call
* mapping->del_cb, if set. */
void nr_mapping_del(struct nr_mapping *mapping);
/* Initialize an (already allocated) nr_map, and set the map's number pool.
* Multiple nr_map instances may use the same nr_pool. Set the nr_map's expiry
* queue to exq, so that all added mappings are automatically expired after the
* time configured in exq. exq may be NULL to disable automatic expiry. */
void nr_map_init(struct nr_map *map, struct nr_pool *pool,
struct expiry *exq);
/* Add a new entry to the map. mapping->orig, mapping->origin and
* mapping->del_cb must be set before calling this function. The remaining
* fields of *mapping will be overwritten. mapping->repl is set to the next
* available mapped number from map->pool. 'now' is the current clock count in
* seconds; if no map->expiry is used, just pass 0 for 'now'. */
void nr_map_add(struct nr_map *map, struct nr_mapping *mapping,
time_t now);
/* Restart the timeout for the given mapping. mapping must be a member of map.
*/
void nr_map_refresh(struct nr_map *map, struct nr_mapping *mapping,
time_t now);
/* Return a known mapping from nr_orig and the given origin. If nr_orig is
* unknown, return NULL. */
struct nr_mapping *nr_map_get(const struct nr_map *map,
void *origin, nr_t nr_orig);
/* Return a known mapping to nr_repl. If nr_repl is unknown, return NULL. */
struct nr_mapping *nr_map_get_inv(const struct nr_map *map, nr_t nr_repl);
/* Remove all mappings from map. */
void nr_map_clear(struct nr_map *map);
/* Return 1 if map has no entries, 0 otherwise. */
int nr_map_empty(const struct nr_map *map);
/* config */
static const int GTPH_EXPIRE_QUICKLY_SECS = 30; /* TODO is there a spec for this? */
static const int GTPH_EXPIRE_SLOWLY_MINUTES = 6 * 60; /* TODO is there a spec for this? */
struct gtphub_cfg_addr {
const char *addr_str;
uint16_t port;
};
struct gtphub_cfg_bind {
struct gtphub_cfg_addr bind;
};
struct gtphub_cfg {
struct gtphub_cfg_bind to_gsns[GTPH_SIDE_N][GTPH_PLANE_N];
struct gtphub_cfg_addr proxy[GTPH_SIDE_N][GTPH_PLANE_N];
int sgsn_use_sender; /* Use sender, not GSN addr IE with std ports */
};
/* state */
struct gtphub_peer {
struct llist_head entry;
struct llist_head addresses; /* Alternatives, not load balancing. */
struct nr_pool seq_pool;
struct nr_map seq_map;
};
struct gtphub_peer_addr {
struct llist_head entry;
struct gtphub_peer *peer;
struct gsn_addr addr;
struct llist_head ports;
};
struct gtphub_peer_port {
struct llist_head entry;
struct gtphub_peer_addr *peer_addr;
uint16_t port;
unsigned int ref_count; /* references from other peers' seq_maps */
struct osmo_sockaddr sa; /* a "cache" for (peer_addr->addr, port) */
int last_restart_count; /* 0..255 = valid, all else means unknown */
struct rate_ctr_group *counters_io;
};
struct gtphub_tunnel_endpoint {
struct gtphub_peer_port *peer;
uint32_t tei_orig; /* from/to peer */
struct rate_ctr_group *counters_io;
};
struct gtphub_tunnel {
struct llist_head entry;
struct expiring_item expiry_entry;
uint32_t tei_repl; /* unique TEI to replace peers' TEIs */
struct gtphub_tunnel_endpoint endpoint[GTPH_SIDE_N][GTPH_PLANE_N];
};
struct gtphub_bind {
struct gsn_addr local_addr;
uint16_t local_port;
struct osmo_fd ofd;
/* list of struct gtphub_peer */
struct llist_head peers;
const char *label; /* For logging */
struct rate_ctr_group *counters_io;
};
struct gtphub_resolved_ggsn {
struct llist_head entry;
struct expiring_item expiry_entry;
/* The APN OI, the Operator Identifier, is the combined address,
* including parts of the IMSI and APN NI, and ending with ".gprs". */
char apn_oi_str[GSM_APN_LENGTH];
/* Which address and port we resolved that to. */
struct gtphub_peer_port *peer;
};
struct gtphub {
struct gtphub_bind to_gsns[GTPH_SIDE_N][GTPH_PLANE_N];
/* pointers to an entry of to_gsns[s][p].peers */
struct gtphub_peer_port *proxy[GTPH_SIDE_N][GTPH_PLANE_N];
/* The TEI numbers will simply wrap and be reused, which will work out
* in practice. Problems would arise if one given peer maintained the
* same TEI for a time long enough for the TEI nr map to wrap an entire
* uint32_t; if a new TEI were mapped every second, this would take
* more than 100 years (in which a single given TEI must not time out)
* to cause a problem. */
struct nr_pool tei_pool;
struct llist_head tunnels; /* struct gtphub_tunnel */
struct llist_head pending_deletes; /* opaque (gtphub.c) */
struct llist_head ggsn_lookups; /* opaque (gtphub_ares.c) */
struct llist_head resolved_ggsns; /* struct gtphub_resolved_ggsn */
struct osmo_timer_list gc_timer;
struct expiry expire_quickly;
struct expiry expire_slowly;
uint8_t restart_counter;
int sgsn_use_sender;
};
struct gtp_packet_desc;
/* api */
int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg);
int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file);
/* Initialize and start gtphub: bind to ports, run expiry timers. */
int gtphub_start(struct gtphub *hub, struct gtphub_cfg *cfg,
uint8_t restart_counter);
/* Close all sockets, expire all maps and peers and free all allocations. The
* struct is then unusable, unless gtphub_start() is run on it again. */
void gtphub_stop(struct gtphub *hub);
time_t gtphub_now(void);
/* Remove expired items, empty peers, ... */
void gtphub_gc(struct gtphub *hub, time_t now);
/* Return the string of the first address for this peer. */
const char *gtphub_peer_str(struct gtphub_peer *peer);
/* Return a human readable description of tun in a static buffer. */
const char *gtphub_tunnel_str(struct gtphub_tunnel *tun);
/* Return 1 if all of tun's endpoints are fully established, 0 otherwise. */
int gtphub_tunnel_complete(struct gtphub_tunnel *tun);
int gtphub_handle_buf(struct gtphub *hub,
unsigned int side_idx,
unsigned int port_idx,
const struct osmo_sockaddr *from_addr,
uint8_t *buf,
size_t received,
time_t now,
uint8_t **reply_buf,
struct osmo_fd **to_ofd,
struct osmo_sockaddr *to_addr);
struct gtphub_peer_port *gtphub_port_have(struct gtphub *hub,
struct gtphub_bind *bind,
const struct gsn_addr *addr,
uint16_t port);
struct gtphub_peer_port *gtphub_port_find_sa(const struct gtphub_bind *bind,
const struct osmo_sockaddr *addr);
void gtphub_resolved_ggsn(struct gtphub *hub, const char *apn_oi_str,
struct gsn_addr *resolved_addr,
time_t now);
const char *gtphub_port_str(struct gtphub_peer_port *port);
int gtphub_write(const struct osmo_fd *to,
const struct osmo_sockaddr *to_addr,
const uint8_t *buf, size_t buf_len);

View File

@ -1,7 +0,0 @@
#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 ranap_ue_conn_ctx *ue);

View File

@ -1,7 +0,0 @@
#pragma once
struct gsm_network;
struct ranap_ue_conn_ctx;
int iucs_rx_ranap_event(struct gsm_network *network,
struct ranap_ue_conn_ctx *ue_ctx, int type, void *data);

View File

@ -1,82 +0,0 @@
/* Osmocom Authentication Protocol API */
/* (C) 2015 by Sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* Author: Neels Hofmeyr
*
* 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>
struct msgb;
struct osmo_oap_message;
/* This is the config part for vty. It is essentially copied in
* oap_client_state, where values are copied over once the config is
* considered valid. */
struct oap_client_config {
uint16_t client_id;
int secret_k_present;
uint8_t secret_k[16];
int secret_opc_present;
uint8_t secret_opc[16];
};
/* The runtime state of the OAP client. client_id and the secrets are in fact
* duplicated from oap_client_config, so that a separate validation of the
* config data is possible, and so that only a struct oap_client_state* is
* passed around. */
struct oap_client_state {
enum {
OAP_UNINITIALIZED = 0, /* just allocated. */
OAP_DISABLED, /* disabled by config. */
OAP_INITIALIZED, /* enabled, config is valid. */
OAP_REQUESTED_CHALLENGE,
OAP_SENT_CHALLENGE_RESULT,
OAP_REGISTERED
} state;
uint16_t client_id;
uint8_t secret_k[16];
uint8_t secret_opc[16];
int registration_failures;
};
/* From config, initialize state. Return 0 on success. */
int oap_client_init(struct oap_client_config *config,
struct oap_client_state *state);
/* Construct an OAP registration message and return in *msg_tx. Use
* state->client_id and update state->state.
* Return 0 on success, or a negative value on error.
* If an error is returned, *msg_tx is guaranteed to be NULL. */
int oap_client_register(struct oap_client_state *state, struct msgb **msg_tx);
/* Decode and act on a received OAP message msg_rx. Update state->state. If a
* non-NULL pointer is returned in *msg_tx, that msgb should be sent to the OAP
* server (and freed) by the caller. The received msg_rx is not freed.
* Return 0 on success, or a negative value on error.
* If an error is returned, *msg_tx is guaranteed to be NULL. */
int oap_client_handle(struct oap_client_state *state,
const struct msgb *msg_rx, struct msgb **msg_tx);
/* Allocate a msgb and in it, return the encoded oap_client_msg. Return
* NULL on error. (Like oap_client_encode(), but also allocates a msgb.)
* About the name: the idea is do_something(oap_client_encoded(my_struct))
*/
struct msgb *oap_client_encoded(const struct osmo_oap_message *oap_client_msg);

View File

@ -2,9 +2,10 @@
#define _REST_OCTETS_H
#include <stdbool.h>
#include <openbsc/gsm_04_08.h>
#include <osmocom/gsm/sysinfo.h>
struct gsm_bts;
/* generate SI1 rest octets */
int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net);
int rest_octets_si2quater(uint8_t *data, struct gsm_bts *bts);

View File

@ -1,187 +0,0 @@
#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

@ -4,7 +4,6 @@
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_subscriber.h>
#include <osmocom/core/linuxlist.h>
#include <openbsc/gsm_04_11.h>
#include <openbsc/mncc.h>
#include <osmocom/gsm/gsm0411_smc.h>
#include <osmocom/gsm/gsm0411_smr.h>

View File

@ -1,147 +0,0 @@
/*
* 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

@ -1,126 +0,0 @@
/*
* 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

@ -1,422 +0,0 @@
#pragma once
#include <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/logging.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/gsm23003.h>
#include <openbsc/gsm_data.h>
// for GSM_NAME_LENGTH
#include <openbsc/gsm_subscriber.h>
struct log_target;
/* from 3s to 10s */
#define GSM_29002_TIMER_S 10
/* from 15s to 30s */
#define GSM_29002_TIMER_M 30
/* from 1min to 10min */
#define GSM_29002_TIMER_ML (10*60)
/* from 28h to 38h */
#define GSM_29002_TIMER_L (32*60*60)
/* VLR subscriber authentication state */
enum vlr_subscr_auth_state {
/* subscriber needs to be autenticated */
VLR_SUB_AS_NEEDS_AUTH,
/* waiting for AuthInfo from HLR/AUC */
VLR_SUB_AS_NEEDS_AUTH_WAIT_AI,
/* waiting for response from subscriber */
VLR_SUB_AS_WAIT_RESP,
/* successfully authenticated */
VLR_SUB_AS_AUTHENTICATED,
/* subscriber needs re-sync */
VLR_SUB_AS_NEEDS_RESYNC,
/* waiting for AuthInfo with ReSync */
VLR_SUB_AS_NEEDS_AUTH_WAIT_SAI_RESYNC,
/* waiting for response from subscr, resync case */
VLR_SUB_AS_WAIT_RESP_RESYNC,
/* waiting for IMSI from subscriber */
VLR_SUB_AS_WAIT_ID_IMSI,
/* authentication has failed */
VLR_SUB_AS_AUTH_FAILED,
};
enum vlr_lu_event {
VLR_ULA_E_UPDATE_LA, /* Initial trigger (LU from MS) */
VLR_ULA_E_SEND_ID_ACK, /* Result of Send-ID from PVLR */
VLR_ULA_E_SEND_ID_NACK, /* Result of Send-ID from PVLR */
VLR_ULA_E_AUTH_RES, /* Result of auth procedure */
VLR_ULA_E_CIPH_RES, /* Result of Ciphering Mode Command */
VLR_ULA_E_ID_IMSI, /* IMSI recieved from MS */
VLR_ULA_E_ID_IMEI, /* IMEI received from MS */
VLR_ULA_E_ID_IMEISV, /* IMEISV received from MS */
VLR_ULA_E_HLR_LU_RES, /* HLR UpdateLocation result */
VLR_ULA_E_UPD_HLR_COMPL,/* UpdatE_HLR_VLR result */
VLR_ULA_E_LU_COMPL_SUCCESS,/* Location_Update_Completion_VLR result */
VLR_ULA_E_LU_COMPL_FAILURE,/* Location_Update_Completion_VLR result */
VLR_ULA_E_NEW_TMSI_ACK, /* TMSI Reallocation Complete */
};
enum vlr_ciph_result_cause {
VLR_CIPH_REJECT, /* ? */
VLR_CIPH_COMPL,
};
struct vlr_ciph_result {
enum vlr_ciph_result_cause cause;
const char *imeisv;
};
enum vlr_subscr_security_context {
VLR_SEC_CTX_NONE,
VLR_SEC_CTX_GSM,
VLR_SEC_CTX_UMTS,
};
enum vlr_lu_type {
VLR_LU_TYPE_PERIODIC,
VLR_LU_TYPE_IMSI_ATTACH,
VLR_LU_TYPE_REGULAR,
};
#define OSMO_LBUF_DECL(name, xlen) \
struct { \
uint8_t buf[xlen]; \
size_t len; \
} name
struct sgsn_mm_ctx;
struct vlr_instance;
/* The VLR subscriber is the part of the GSM subscriber state in VLR (CS) or
* SGSN (PS), particularly while interacting with the HLR via GSUP */
struct vlr_subscr {
struct llist_head list;
struct vlr_instance *vlr;
/* TODO either populate from HLR or drop this completely? */
long long unsigned int id;
/* Data from HLR */ /* 3GPP TS 23.008 */
/* Always use vlr_subscr_set_imsi() to write to imsi[] */
char imsi[GSM23003_IMSI_MAX_DIGITS+1]; /* 2.1.1.1 */
char msisdn[GSM_EXTENSION_LENGTH+1]; /* 2.1.2 */
char name[GSM_NAME_LENGTH+1]; /* proprietary */
OSMO_LBUF_DECL(hlr, 16); /* 2.4.7 */
uint32_t periodic_lu_timer; /* 2.4.24 */
uint32_t age_indicator; /* 2.17.1 */
/* Authentication Data */
struct gsm_auth_tuple auth_tuples[5]; /* 2.3.1-2.3.4 */
struct gsm_auth_tuple *last_tuple;
enum vlr_subscr_security_context sec_ctx;
/* Data local to VLR is below */
uint32_t tmsi; /* 2.1.4 */
/* Newly allocated TMSI that was not yet acked by MS */
uint32_t tmsi_new;
/* some redundancy in information below? */
struct osmo_cell_global_id cgi; /* 2.4.16 */
uint16_t lac; /* 2.4.2 */
char imeisv[GSM23003_IMEISV_NUM_DIGITS+1]; /* 2.2.3 */
char imei[GSM23003_IMEISV_NUM_DIGITS+1]; /* 2.1.9 */
bool imsi_detached_flag; /* 2.7.1 */
bool conf_by_radio_contact_ind; /* 2.7.4.1 */
bool sub_dataconf_by_hlr_ind; /* 2.7.4.2 */
bool loc_conf_in_hlr_ind; /* 2.7.4.3 */
bool dormant_ind; /* 2.7.8 */
bool cancel_loc_rx; /* 2.7.8A */
bool ms_not_reachable_flag; /* 2.10.2 (MNRF) */
bool la_allowed;
int use_count;
time_t expire_lu; /* FIXME: overlap with periodic_lu_timer/age_indicator */
struct osmo_fsm_inst *lu_fsm;
struct osmo_fsm_inst *auth_fsm;
struct osmo_fsm_inst *proc_arq_fsm;
bool lu_complete;
void *msc_conn_ref;
/* PS (SGSN) specific parts */
struct {
struct llist_head pdp_list;
uint8_t rac;
uint8_t sac;
struct gprs_mm_ctx *mmctx;
} ps;
/* CS (NITB/CSCN) specific parts */
struct {
/* pending requests */
bool is_paging;
/* list of struct subscr_request */
struct llist_head requests;
uint8_t lac;
enum ran_type attached_via_ran;
} cs;
};
enum vlr_proc_arq_result;
enum vlr_ciph {
VLR_CIPH_NONE, /*< A5/0, no encryption */
VLR_CIPH_A5_1, /*< A5/1, encryption */
VLR_CIPH_A5_2, /*< A5/2, deprecated export-grade encryption */
VLR_CIPH_A5_3, /*< A5/3, 'new secure' encryption */
};
struct vlr_ops {
/* encode + transmit an AUTH REQ towards the MS.
* \param[in] at auth tuple providing rand, key_seq and autn.
* \param[in] send_autn True to send AUTN, for r99 UMTS auth.
*/
int (*tx_auth_req)(void *msc_conn_ref, struct gsm_auth_tuple *at,
bool send_autn);
/* encode + transmit an AUTH REJECT towards the MS */
int (*tx_auth_rej)(void *msc_conn_ref);
/* encode + transmit an IDENTITY REQUEST towards the MS */
int (*tx_id_req)(void *msc_conn_ref, uint8_t mi_type);
int (*tx_lu_acc)(void *msc_conn_ref, uint32_t send_tmsi);
int (*tx_lu_rej)(void *msc_conn_ref, uint8_t cause);
int (*tx_cm_serv_acc)(void *msc_conn_ref);
int (*tx_cm_serv_rej)(void *msc_conn_ref, enum vlr_proc_arq_result result);
int (*set_ciph_mode)(void *msc_conn_ref, enum vlr_ciph ciph_mode,
bool retrieve_imeisv);
/* UTRAN: send Common Id (when auth+ciph are complete) */
int (*tx_common_id)(void *msc_conn_ref);
/* notify MSC/SGSN that the subscriber data in VLR has been updated */
void (*subscr_update)(struct vlr_subscr *vsub);
/* notify MSC/SGSN that the given subscriber has been associated
* with this msc_conn_ref */
void (*subscr_assoc)(void *msc_conn_ref, struct vlr_subscr *vsub);
};
enum vlr_timer {
VLR_T_3250,
VLR_T_3260,
VLR_T_3270,
_NUM_VLR_TIMERS
};
/* An instance of the VLR codebase */
struct vlr_instance {
struct llist_head subscribers;
struct llist_head operations;
struct gsup_client *gsup_client;
struct vlr_ops ops;
struct {
bool retrieve_imeisv_early;
bool retrieve_imeisv_ciphered;
bool assign_tmsi;
bool check_imei_rqd;
int auth_tuple_max_use_count;
bool auth_reuse_old_sets_on_error;
bool parq_retrieve_imsi;
bool is_ps;
uint32_t timer[_NUM_VLR_TIMERS];
} cfg;
/* A free-form pointer for use by the caller */
void *user_ctx;
};
extern const struct value_string vlr_ciph_names[];
static inline const char *vlr_ciph_name(enum vlr_ciph val)
{
return get_value_string(vlr_ciph_names, val);
}
/* Location Updating request */
struct osmo_fsm_inst *
vlr_loc_update(struct osmo_fsm_inst *parent,
uint32_t parent_event_success,
uint32_t parent_event_failure,
void *parent_event_data,
struct vlr_instance *vlr, void *msc_conn_ref,
enum vlr_lu_type type, uint32_t tmsi, const char *imsi,
const struct osmo_location_area_id *old_lai,
const struct osmo_location_area_id *new_lai,
bool authentication_required,
enum vlr_ciph ciphering_required,
bool is_r99, bool is_utran,
bool assign_tmsi);
void vlr_loc_update_conn_timeout(struct osmo_fsm_inst *fi);
/* tell the VLR that the subscriber connection is gone */
int vlr_subscr_disconnected(struct vlr_subscr *vsub);
int vlr_subscr_rx_id_resp(struct vlr_subscr *vsub, const uint8_t *mi, size_t mi_len);
int vlr_subscr_rx_auth_resp(struct vlr_subscr *vsub, bool is_r99, bool is_utran,
const uint8_t *res, uint8_t res_len);
int vlr_subscr_rx_auth_fail(struct vlr_subscr *vsub, const uint8_t *auts);
int vlr_subscr_tx_auth_fail_rep(struct vlr_subscr *vsub);
void vlr_subscr_rx_ciph_res(struct vlr_subscr *vsub, struct vlr_ciph_result *res);
int vlr_subscr_rx_tmsi_reall_compl(struct vlr_subscr *vsub);
int vlr_subscr_rx_imsi_detach(struct vlr_subscr *vsub);
void vlr_subscr_conn_timeout(struct vlr_subscr *vsub);
struct vlr_instance *vlr_alloc(void *ctx, const struct vlr_ops *ops);
int vlr_start(const char *gsup_unit_name, struct vlr_instance *vlr,
const char *gsup_server_addr_str, uint16_t gsup_server_port);
/* internal use only */
struct osmo_fsm_inst *sub_pres_vlr_fsm_start(struct osmo_fsm_inst *parent,
struct vlr_subscr *vsub,
uint32_t term_event);
struct osmo_fsm_inst *
upd_hlr_vlr_proc_start(struct osmo_fsm_inst *parent,
struct vlr_subscr *vsub,
uint32_t parent_event);
struct osmo_fsm_inst *
lu_compl_vlr_proc_start(struct osmo_fsm_inst *parent,
struct vlr_subscr *vsub,
void *msc_conn_ref,
uint32_t parent_event_success,
uint32_t parent_event_failure);
const char *vlr_subscr_name(struct vlr_subscr *vsub);
const char *vlr_subscr_msisdn_or_name(struct vlr_subscr *vsub);
#define vlr_subscr_find_by_imsi(vlr, imsi) \
_vlr_subscr_find_by_imsi(vlr, imsi, __BASE_FILE__, __LINE__)
#define vlr_subscr_find_or_create_by_imsi(vlr, imsi, created) \
_vlr_subscr_find_or_create_by_imsi(vlr, imsi, created, \
__BASE_FILE__, __LINE__)
#define vlr_subscr_find_by_tmsi(vlr, tmsi) \
_vlr_subscr_find_by_tmsi(vlr, tmsi, __BASE_FILE__, __LINE__)
#define vlr_subscr_find_or_create_by_tmsi(vlr, tmsi, created) \
_vlr_subscr_find_or_create_by_tmsi(vlr, tmsi, created, \
__BASE_FILE__, __LINE__)
#define vlr_subscr_find_by_msisdn(vlr, msisdn) \
_vlr_subscr_find_by_msisdn(vlr, msisdn, __BASE_FILE__, __LINE__)
struct vlr_subscr *_vlr_subscr_find_by_imsi(struct vlr_instance *vlr,
const char *imsi,
const char *file, int line);
struct vlr_subscr *_vlr_subscr_find_or_create_by_imsi(struct vlr_instance *vlr,
const char *imsi,
bool *created,
const char *file,
int line);
struct vlr_subscr *_vlr_subscr_find_by_tmsi(struct vlr_instance *vlr,
uint32_t tmsi,
const char *file, int line);
struct vlr_subscr *_vlr_subscr_find_or_create_by_tmsi(struct vlr_instance *vlr,
uint32_t tmsi,
bool *created,
const char *file,
int line);
struct vlr_subscr *_vlr_subscr_find_by_msisdn(struct vlr_instance *vlr,
const char *msisdn,
const char *file, int line);
#define vlr_subscr_get(sub) _vlr_subscr_get(sub, __BASE_FILE__, __LINE__)
#define vlr_subscr_put(sub) _vlr_subscr_put(sub, __BASE_FILE__, __LINE__)
struct vlr_subscr *_vlr_subscr_get(struct vlr_subscr *sub, const char *file, int line);
struct vlr_subscr *_vlr_subscr_put(struct vlr_subscr *sub, const char *file, int line);
struct vlr_subscr *vlr_subscr_alloc(struct vlr_instance *vlr);
void vlr_subscr_free(struct vlr_subscr *vsub);
int vlr_subscr_alloc_tmsi(struct vlr_subscr *vsub);
void vlr_subscr_set_imsi(struct vlr_subscr *vsub, const char *imsi);
void vlr_subscr_set_imei(struct vlr_subscr *vsub, const char *imei);
void vlr_subscr_set_imeisv(struct vlr_subscr *vsub, const char *imeisv);
void vlr_subscr_set_msisdn(struct vlr_subscr *vsub, const char *msisdn);
bool vlr_subscr_matches_imsi(struct vlr_subscr *vsub, const char *imsi);
bool vlr_subscr_matches_tmsi(struct vlr_subscr *vsub, uint32_t tmsi);
bool vlr_subscr_matches_msisdn(struct vlr_subscr *vsub, const char *msisdn);
bool vlr_subscr_matches_imei(struct vlr_subscr *vsub, const char *imei);
uint32_t vlr_timer(struct vlr_instance *vlr, uint32_t timer);
int vlr_subscr_changed(struct vlr_subscr *vsub);
int vlr_subscr_purge(struct vlr_subscr *vsub);
void vlr_subscr_cancel(struct vlr_subscr *vsub, enum gsm48_gmm_cause cause);
/* Process Acccess Request FSM */
enum vlr_proc_arq_result {
VLR_PR_ARQ_RES_NONE,
VLR_PR_ARQ_RES_SYSTEM_FAILURE,
VLR_PR_ARQ_RES_ILLEGAL_SUBSCR,
VLR_PR_ARQ_RES_UNIDENT_SUBSCR,
VLR_PR_ARQ_RES_ROAMING_NOTALLOWED,
VLR_PR_ARQ_RES_ILLEGAL_EQUIP,
VLR_PR_ARQ_RES_UNKNOWN_ERROR,
VLR_PR_ARQ_RES_TIMEOUT,
VLR_PR_ARQ_RES_PASSED,
};
extern const struct value_string vlr_proc_arq_result_names[];
static inline const char *vlr_proc_arq_result_name(enum vlr_proc_arq_result res)
{
return get_value_string(vlr_proc_arq_result_names, res);
}
enum proc_arq_vlr_event {
PR_ARQ_E_START,
PR_ARQ_E_ID_IMSI,
PR_ARQ_E_AUTH_RES,
PR_ARQ_E_CIPH_RES,
PR_ARQ_E_UPD_LOC_RES,
PR_ARQ_E_TRACE_RES,
PR_ARQ_E_IMEI_RES,
PR_ARQ_E_PRES_RES,
PR_ARQ_E_TMSI_ACK,
};
enum vlr_parq_type {
VLR_PR_ARQ_T_INVALID = 0, /* to guard against unset vars */
VLR_PR_ARQ_T_CM_SERV_REQ,
VLR_PR_ARQ_T_PAGING_RESP,
/* FIXME: differentiate between services of 24.008 10.5.3.3 */
};
/* Process Access Request (CM SERV REQ / PAGING RESP) */
void
vlr_proc_acc_req(struct osmo_fsm_inst *parent,
uint32_t parent_event_success,
uint32_t parent_event_failure,
void *parent_event_data,
struct vlr_instance *vlr, void *msc_conn_ref,
enum vlr_parq_type type, const uint8_t *mi_lv,
const struct osmo_location_area_id *lai,
bool authentication_required,
enum vlr_ciph ciphering_required,
bool is_r99, bool is_utran);
void vlr_parq_conn_timeout(struct osmo_fsm_inst *fi);
void vlr_parq_fsm_init(void);
int vlr_set_ciph_mode(struct vlr_instance *vlr,
struct osmo_fsm_inst *fi,
void *msc_conn_ref,
enum vlr_ciph ciph_mode,
bool retrieve_imeisv);
void log_set_filter_vlr_subscr(struct log_target *target,
struct vlr_subscr *vlr_subscr);

View File

@ -30,23 +30,14 @@ nitb_e1_configs = [
app_configs = {
"osmo-bsc": ["doc/examples/osmo-bsc/osmo-bsc.cfg"],
"nat": ["doc/examples/osmo-bsc_nat/osmo-bsc_nat.cfg"],
"gbproxy": ["doc/examples/osmo-gbproxy/osmo-gbproxy.cfg",
"doc/examples/osmo-gbproxy/osmo-gbproxy-legacy.cfg"],
"sgsn": ["doc/examples/osmo-sgsn/osmo-sgsn.cfg"],
"msc": ["doc/examples/osmo-msc/osmo-msc.cfg"],
"gtphub": ["doc/examples/osmo-gtphub/osmo-gtphub-1iface.cfg"]
}
apps = [(4242, "src/osmo-bsc/osmo-bsc", "OsmoBSC", "osmo-bsc"),
(4244, "src/osmo-bsc_nat/osmo-bsc_nat", "OsmoBSCNAT", "nat"),
(4246, "src/gprs/osmo-gbproxy", "OsmoGbProxy", "gbproxy"),
(4245, "src/gprs/osmo-sgsn", "OsmoSGSN", "sgsn"),
(4254, "src/osmo-msc/osmo-msc", "OsmoMSC", "msc"),
(4253, "src/gprs/osmo-gtphub", "OsmoGTPhub", "gtphub")
]
vty_command = ["./src/osmo-msc/osmo-msc", "-c",
"doc/examples/osmo-msc/osmo-msc.cfg"]
vty_command = ["./src/osmo-bsc/osmo-bsc", "-c",
"doc/examples/osmo-bsc/osmo-bsc.cfg"]
vty_app = apps[4] # reference apps[] entry for osmo-msc
vty_app = apps[0]

View File

@ -22,9 +22,7 @@ AM_LDFLAGS = \
# Libraries
SUBDIRS = \
libcommon \
libvlr \
libbsc \
libmsc \
libtrau \
libfilter \
libcommon-cs \
@ -32,10 +30,8 @@ SUBDIRS = \
# Programs
SUBDIRS += \
osmo-msc \
utils \
ipaccess \
gprs \
$(NULL)
# Conditional Programs

2
src/gprs/.gitignore vendored
View File

@ -1,2 +0,0 @@
gsn_restart
osmo_*.cfg*

View File

@ -1,133 +0,0 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
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
OSMO_LIBS = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOABIS_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOGB_LIBS) \
$(LIBGTP_LIBS) \
$(LIBOSMOSIGTRAN_LIBS) \
$(NULL)
bin_PROGRAMS = \
osmo-gbproxy \
$(NULL)
if HAVE_LIBGTP
if HAVE_LIBCARES
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 \
$(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_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 \
sgsn_cdr.c \
sgsn_ares.c \
slhc.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) \
$(LIBGTP_LIBS) \
-lrt \
-lm \
$(NULL)
if BUILD_IU
osmo_sgsn_LDADD += \
$(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 \
$(NULL)
osmo_gtphub_LDADD = \
$(top_builddir)/src/libcommon/libcommon.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBCARES_LIBS) \
$(LIBGTP_LIBS) \
$(LIBOSMOSIGTRAN_LIBS) \
-lrt \
$(NULL)

View File

@ -1,67 +0,0 @@
/* GPRS LLC CRC-24 Implementation */
/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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/crc24.h>
/* CRC24 table - FCS */
static const uint32_t tbl_crc24[256] = {
0x00000000, 0x00d6a776, 0x00f64557, 0x0020e221, 0x00b78115, 0x00612663, 0x0041c442, 0x00976334,
0x00340991, 0x00e2aee7, 0x00c24cc6, 0x0014ebb0, 0x00838884, 0x00552ff2, 0x0075cdd3, 0x00a36aa5,
0x00681322, 0x00beb454, 0x009e5675, 0x0048f103, 0x00df9237, 0x00093541, 0x0029d760, 0x00ff7016,
0x005c1ab3, 0x008abdc5, 0x00aa5fe4, 0x007cf892, 0x00eb9ba6, 0x003d3cd0, 0x001ddef1, 0x00cb7987,
0x00d02644, 0x00068132, 0x00266313, 0x00f0c465, 0x0067a751, 0x00b10027, 0x0091e206, 0x00474570,
0x00e42fd5, 0x003288a3, 0x00126a82, 0x00c4cdf4, 0x0053aec0, 0x008509b6, 0x00a5eb97, 0x00734ce1,
0x00b83566, 0x006e9210, 0x004e7031, 0x0098d747, 0x000fb473, 0x00d91305, 0x00f9f124, 0x002f5652,
0x008c3cf7, 0x005a9b81, 0x007a79a0, 0x00acded6, 0x003bbde2, 0x00ed1a94, 0x00cdf8b5, 0x001b5fc3,
0x00fb4733, 0x002de045, 0x000d0264, 0x00dba512, 0x004cc626, 0x009a6150, 0x00ba8371, 0x006c2407,
0x00cf4ea2, 0x0019e9d4, 0x00390bf5, 0x00efac83, 0x0078cfb7, 0x00ae68c1, 0x008e8ae0, 0x00582d96,
0x00935411, 0x0045f367, 0x00651146, 0x00b3b630, 0x0024d504, 0x00f27272, 0x00d29053, 0x00043725,
0x00a75d80, 0x0071faf6, 0x005118d7, 0x0087bfa1, 0x0010dc95, 0x00c67be3, 0x00e699c2, 0x00303eb4,
0x002b6177, 0x00fdc601, 0x00dd2420, 0x000b8356, 0x009ce062, 0x004a4714, 0x006aa535, 0x00bc0243,
0x001f68e6, 0x00c9cf90, 0x00e92db1, 0x003f8ac7, 0x00a8e9f3, 0x007e4e85, 0x005eaca4, 0x00880bd2,
0x00437255, 0x0095d523, 0x00b53702, 0x00639074, 0x00f4f340, 0x00225436, 0x0002b617, 0x00d41161,
0x00777bc4, 0x00a1dcb2, 0x00813e93, 0x005799e5, 0x00c0fad1, 0x00165da7, 0x0036bf86, 0x00e018f0,
0x00ad85dd, 0x007b22ab, 0x005bc08a, 0x008d67fc, 0x001a04c8, 0x00cca3be, 0x00ec419f, 0x003ae6e9,
0x00998c4c, 0x004f2b3a, 0x006fc91b, 0x00b96e6d, 0x002e0d59, 0x00f8aa2f, 0x00d8480e, 0x000eef78,
0x00c596ff, 0x00133189, 0x0033d3a8, 0x00e574de, 0x007217ea, 0x00a4b09c, 0x008452bd, 0x0052f5cb,
0x00f19f6e, 0x00273818, 0x0007da39, 0x00d17d4f, 0x00461e7b, 0x0090b90d, 0x00b05b2c, 0x0066fc5a,
0x007da399, 0x00ab04ef, 0x008be6ce, 0x005d41b8, 0x00ca228c, 0x001c85fa, 0x003c67db, 0x00eac0ad,
0x0049aa08, 0x009f0d7e, 0x00bfef5f, 0x00694829, 0x00fe2b1d, 0x00288c6b, 0x00086e4a, 0x00dec93c,
0x0015b0bb, 0x00c317cd, 0x00e3f5ec, 0x0035529a, 0x00a231ae, 0x007496d8, 0x005474f9, 0x0082d38f,
0x0021b92a, 0x00f71e5c, 0x00d7fc7d, 0x00015b0b, 0x0096383f, 0x00409f49, 0x00607d68, 0x00b6da1e,
0x0056c2ee, 0x00806598, 0x00a087b9, 0x007620cf, 0x00e143fb, 0x0037e48d, 0x001706ac, 0x00c1a1da,
0x0062cb7f, 0x00b46c09, 0x00948e28, 0x0042295e, 0x00d54a6a, 0x0003ed1c, 0x00230f3d, 0x00f5a84b,
0x003ed1cc, 0x00e876ba, 0x00c8949b, 0x001e33ed, 0x008950d9, 0x005ff7af, 0x007f158e, 0x00a9b2f8,
0x000ad85d, 0x00dc7f2b, 0x00fc9d0a, 0x002a3a7c, 0x00bd5948, 0x006bfe3e, 0x004b1c1f, 0x009dbb69,
0x0086e4aa, 0x005043dc, 0x0070a1fd, 0x00a6068b, 0x003165bf, 0x00e7c2c9, 0x00c720e8, 0x0011879e,
0x00b2ed3b, 0x00644a4d, 0x0044a86c, 0x00920f1a, 0x00056c2e, 0x00d3cb58, 0x00f32979, 0x00258e0f,
0x00eef788, 0x003850fe, 0x0018b2df, 0x00ce15a9, 0x0059769d, 0x008fd1eb, 0x00af33ca, 0x007994bc,
0x00dafe19, 0x000c596f, 0x002cbb4e, 0x00fa1c38, 0x006d7f0c, 0x00bbd87a, 0x009b3a5b, 0x004d9d2d
};
#define INIT_CRC24 0xffffff
uint32_t crc24_calc(uint32_t fcs, uint8_t *cp, unsigned int len)
{
while (len--)
fcs = (fcs >> 8) ^ tbl_crc24[(fcs ^ *cp++) & 0xff];
return fcs;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,317 +0,0 @@
/* NS-over-IP proxy */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <signal.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stats.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <openbsc/signal.h>
#include <openbsc/debug.h>
#include <openbsc/vty.h>
#include <openbsc/gb_proxy.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/ports.h>
#include "../../bscconfig.h"
#define _GNU_SOURCE
#include <getopt.h>
void *tall_bsc_ctx;
const char *openbsc_copyright =
"Copyright (C) 2010 Harald Welte and On-Waves\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
static char *config_file = "osmo_gbproxy.cfg";
struct gbproxy_config gbcfg = {0};
static int daemonize = 0;
/* Pointer to the SGSN peer */
extern struct gbprox_peer *gbprox_peer_sgsn;
/* call-back function for the NS protocol */
static int proxy_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
struct msgb *msg, uint16_t bvci)
{
int rc = 0;
switch (event) {
case GPRS_NS_EVT_UNIT_DATA:
rc = gbprox_rcvmsg(&gbcfg, msg, nsvc->nsei, bvci, nsvc->nsvci);
break;
default:
LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
if (msg)
msgb_free(msg);
rc = -EIO;
break;
}
return rc;
}
static void signal_handler(int signal)
{
fprintf(stdout, "signal %u received\n", signal);
switch (signal) {
case SIGINT:
case SIGTERM:
osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
sleep(1);
exit(0);
break;
case SIGABRT:
/* in case of abort, we want to obtain a talloc report
* and then return to the caller, who will abort the process */
case SIGUSR1:
talloc_report(tall_vty_ctx, stderr);
talloc_report_full(tall_bsc_ctx, stderr);
break;
case SIGUSR2:
talloc_report_full(tall_vty_ctx, stderr);
break;
default:
break;
}
}
static void print_usage()
{
printf("Usage: bsc_hack\n");
}
static void print_help()
{
printf(" Some useful help...\n");
printf(" -h --help this text\n");
printf(" -d option --debug=DNS:DGPRS,0:0 enable debugging\n");
printf(" -D --daemonize Fork the process into a background daemon\n");
printf(" -c --config-file filename The config file to use.\n");
printf(" -s --disable-color\n");
printf(" -T --timestamp Prefix every log line with a timestamp\n");
printf(" -V --version. Print the version of OpenBSC.\n");
printf(" -e --log-level number. Set a global loglevel.\n");
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{ "help", 0, 0, 'h' },
{ "debug", 1, 0, 'd' },
{ "daemonize", 0, 0, 'D' },
{ "config-file", 1, 0, 'c' },
{ "disable-color", 0, 0, 's' },
{ "timestamp", 0, 0, 'T' },
{ "version", 0, 0, 'V' },
{ "log-level", 1, 0, 'e' },
{ 0, 0, 0, 0 }
};
c = getopt_long(argc, argv, "hd:Dc:sTVe:",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
print_usage();
print_help();
exit(0);
case 's':
log_set_use_color(osmo_stderr_target, 0);
break;
case 'd':
log_parse_category_mask(osmo_stderr_target, optarg);
break;
case 'D':
daemonize = 1;
break;
case 'c':
config_file = optarg;
break;
case 'T':
log_set_print_timestamp(osmo_stderr_target, 1);
break;
case 'e':
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
case 'V':
print_version(1);
exit(0);
break;
default:
break;
}
}
}
extern int bsc_vty_go_parent(struct vty *vty);
static struct vty_app_info vty_info = {
.name = "OsmoGbProxy",
.version = PACKAGE_VERSION,
.go_parent_cb = bsc_vty_go_parent,
.is_config_node = bsc_vty_is_config_node,
};
/* default categories */
static struct log_info_cat gprs_categories[] = {
[DGPRS] = {
.name = "DGPRS",
.description = "GPRS Packet Service",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DNS] = {
.name = "DNS",
.description = "GPRS Network Service (NS)",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DBSSGP] = {
.name = "DBSSGP",
.description = "GPRS BSS Gateway Protocol (BSSGP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
};
static const struct log_info gprs_log_info = {
.filter_fn = gprs_log_filter_fn,
.cat = gprs_categories,
.num_cat = ARRAY_SIZE(gprs_categories),
};
int main(int argc, char **argv)
{
struct gsm_network dummy_network;
int rc;
tall_bsc_ctx = talloc_named_const(NULL, 0, "nsip_proxy");
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
signal(SIGINT, &signal_handler);
signal(SIGTERM, &signal_handler);
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
osmo_init_logging(&gprs_log_info);
vty_info.copyright = openbsc_copyright;
vty_init(&vty_info);
logging_vty_add_cmds(NULL);
osmo_stats_vty_add_cmds(&gprs_log_info);
gbproxy_vty_init();
handle_options(argc, argv);
rate_ctr_init(tall_bsc_ctx);
osmo_stats_init(tall_bsc_ctx);
bssgp_nsi = gprs_ns_instantiate(&proxy_ns_cb, tall_bsc_ctx);
if (!bssgp_nsi) {
LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
exit(1);
}
gbproxy_init_config(&gbcfg);
gbcfg.nsi = bssgp_nsi;
gprs_ns_vty_init(bssgp_nsi);
gprs_ns_set_log_ss(DNS);
bssgp_set_log_ss(DBSSGP);
osmo_signal_register_handler(SS_L_NS, &gbprox_signal, &gbcfg);
rc = gbproxy_parse_config(config_file, &gbcfg);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n");
exit(2);
}
/* start telnet after reading config for vty_get_bind_addr() */
rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network,
vty_get_bind_addr(), OSMO_VTY_PORT_GBPROXY);
if (rc < 0)
exit(1);
if (!gprs_nsvc_by_nsei(gbcfg.nsi, gbcfg.nsip_sgsn_nsei)) {
LOGP(DGPRS, LOGL_FATAL, "You cannot proxy to NSEI %u "
"without creating that NSEI before\n",
gbcfg.nsip_sgsn_nsei);
exit(2);
}
rc = gprs_ns_nsip_listen(bssgp_nsi);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n");
exit(2);
}
rc = gprs_ns_frgre_listen(bssgp_nsi);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE "
"socket. Do you have CAP_NET_RAW?\n");
exit(2);
}
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
perror("Error during daemonize");
exit(1);
}
}
/* Reset all the persistent NS-VCs that we've read from the config */
gbprox_reset_persistent_nsvcs(bssgp_nsi);
while (1) {
rc = osmo_select_main(0);
if (rc < 0)
exit(3);
}
exit(0);
}

View File

@ -1,459 +0,0 @@
/* Gb-proxy message patching */
/* (C) 2014 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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/gb_proxy.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/gprs_gb_parse.h>
#include <openbsc/gsm_data.h>
#include <openbsc/debug.h>
#include <osmocom/gprs/protocol/gsm_08_18.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/gsm/apn.h>
/* patch RA identifier in place */
static void gbproxy_patch_raid(uint8_t *raid_enc, struct gbproxy_peer *peer,
int to_bss, const char *log_text)
{
struct gbproxy_patch_state *state = &peer->patch_state;
int old_mcc;
int old_mnc;
struct gprs_ra_id raid;
enum gbproxy_peer_ctr counter =
to_bss ?
GBPROX_PEER_CTR_RAID_PATCHED_SGSN :
GBPROX_PEER_CTR_RAID_PATCHED_BSS;
if (!state->local_mcc || !state->local_mnc)
return;
gsm48_parse_ra(&raid, raid_enc);
old_mcc = raid.mcc;
old_mnc = raid.mnc;
if (!to_bss) {
/* BSS -> SGSN */
if (state->local_mcc)
raid.mcc = peer->cfg->core_mcc;
if (state->local_mnc)
raid.mnc = peer->cfg->core_mnc;
} else {
/* SGSN -> BSS */
if (state->local_mcc)
raid.mcc = state->local_mcc;
if (state->local_mnc)
raid.mnc = state->local_mnc;
}
LOGP(DGPRS, LOGL_DEBUG,
"Patching %s to %s: "
"%d-%d-%d-%d -> %d-%d-%d-%d\n",
log_text,
to_bss ? "BSS" : "SGSN",
old_mcc, old_mnc, raid.lac, raid.rac,
raid.mcc, raid.mnc, raid.lac, raid.rac);
gsm48_construct_ra(raid_enc, &raid);
rate_ctr_inc(&peer->ctrg->ctr[counter]);
}
static void gbproxy_patch_apn_ie(struct msgb *msg,
uint8_t *apn_ie, size_t apn_ie_len,
struct gbproxy_peer *peer,
size_t *new_apn_ie_len, const char *log_text)
{
struct apn_ie_hdr {
uint8_t iei;
uint8_t apn_len;
uint8_t apn[0];
} *hdr = (void *)apn_ie;
size_t apn_len = hdr->apn_len;
uint8_t *apn = hdr->apn;
OSMO_ASSERT(apn_ie_len == apn_len + sizeof(struct apn_ie_hdr));
OSMO_ASSERT(apn_ie_len > 2 && apn_ie_len <= 102);
if (peer->cfg->core_apn_size == 0) {
char str1[110];
/* Remove the IE */
LOGP(DGPRS, LOGL_DEBUG,
"Patching %s to SGSN: Removing APN '%s'\n",
log_text,
osmo_apn_to_str(str1, apn, apn_len));
*new_apn_ie_len = 0;
gprs_msgb_resize_area(msg, apn_ie, apn_ie_len, 0);
} else {
/* Resize the IE */
char str1[110];
char str2[110];
OSMO_ASSERT(peer->cfg->core_apn_size <= 100);
LOGP(DGPRS, LOGL_DEBUG,
"Patching %s to SGSN: "
"Replacing APN '%s' -> '%s'\n",
log_text,
osmo_apn_to_str(str1, apn, apn_len),
osmo_apn_to_str(str2, peer->cfg->core_apn,
peer->cfg->core_apn_size));
*new_apn_ie_len = peer->cfg->core_apn_size + 2;
gprs_msgb_resize_area(msg, apn, apn_len, peer->cfg->core_apn_size);
memcpy(apn, peer->cfg->core_apn, peer->cfg->core_apn_size);
hdr->apn_len = peer->cfg->core_apn_size;
}
rate_ctr_inc(&peer->ctrg->ctr[GBPROX_PEER_CTR_APN_PATCHED]);
}
static int gbproxy_patch_tlli(uint8_t *tlli_enc,
struct gbproxy_peer *peer,
uint32_t new_tlli,
int to_bss, const char *log_text)
{
uint32_t tlli_be;
uint32_t tlli;
enum gbproxy_peer_ctr counter =
to_bss ?
GBPROX_PEER_CTR_TLLI_PATCHED_SGSN :
GBPROX_PEER_CTR_TLLI_PATCHED_BSS;
memcpy(&tlli_be, tlli_enc, sizeof(tlli_be));
tlli = ntohl(tlli_be);
if (tlli == new_tlli)
return 0;
LOGP(DGPRS, LOGL_DEBUG,
"Patching %ss: "
"Replacing %08x -> %08x\n",
log_text, tlli, new_tlli);
tlli_be = htonl(new_tlli);
memcpy(tlli_enc, &tlli_be, sizeof(tlli_be));
rate_ctr_inc(&peer->ctrg->ctr[counter]);
return 1;
}
static int gbproxy_patch_ptmsi(uint8_t *ptmsi_enc,
struct gbproxy_peer *peer,
uint32_t new_ptmsi,
int to_bss, const char *log_text)
{
uint32_t ptmsi_be;
uint32_t ptmsi;
enum gbproxy_peer_ctr counter =
to_bss ?
GBPROX_PEER_CTR_PTMSI_PATCHED_SGSN :
GBPROX_PEER_CTR_PTMSI_PATCHED_BSS;
memcpy(&ptmsi_be, ptmsi_enc, sizeof(ptmsi_be));
ptmsi = ntohl(ptmsi_be);
if (ptmsi == new_ptmsi)
return 0;
LOGP(DGPRS, LOGL_DEBUG,
"Patching %ss: "
"Replacing %08x -> %08x\n",
log_text, ptmsi, new_ptmsi);
ptmsi_be = htonl(new_ptmsi);
memcpy(ptmsi_enc, &ptmsi_be, sizeof(ptmsi_be));
rate_ctr_inc(&peer->ctrg->ctr[counter]);
return 1;
}
int gbproxy_patch_llc(struct msgb *msg, uint8_t *llc, size_t llc_len,
struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info, int *len_change,
struct gprs_gb_parse_context *parse_ctx)
{
struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed;
int have_patched = 0;
int fcs;
struct gbproxy_config *cfg = peer->cfg;
if (parse_ctx->ptmsi_enc && link_info &&
!parse_ctx->old_raid_is_foreign && peer->cfg->patch_ptmsi) {
uint32_t ptmsi;
if (parse_ctx->to_bss)
ptmsi = link_info->tlli.ptmsi;
else
ptmsi = link_info->sgsn_tlli.ptmsi;
if (ptmsi != GSM_RESERVED_TMSI) {
if (gbproxy_patch_ptmsi(parse_ctx->ptmsi_enc, peer,
ptmsi, parse_ctx->to_bss, "P-TMSI"))
have_patched = 1;
} else {
/* TODO: invalidate old RAI if present (see below) */
}
}
if (parse_ctx->new_ptmsi_enc && link_info && cfg->patch_ptmsi) {
uint32_t ptmsi;
if (parse_ctx->to_bss)
ptmsi = link_info->tlli.ptmsi;
else
ptmsi = link_info->sgsn_tlli.ptmsi;
OSMO_ASSERT(ptmsi);
if (gbproxy_patch_ptmsi(parse_ctx->new_ptmsi_enc, peer,
ptmsi, parse_ctx->to_bss, "new P-TMSI"))
have_patched = 1;
}
if (parse_ctx->raid_enc) {
gbproxy_patch_raid(parse_ctx->raid_enc, peer, parse_ctx->to_bss,
parse_ctx->llc_msg_name);
have_patched = 1;
}
if (parse_ctx->old_raid_enc && !parse_ctx->old_raid_is_foreign) {
/* TODO: Patch to invalid if P-TMSI unknown. */
gbproxy_patch_raid(parse_ctx->old_raid_enc, peer, parse_ctx->to_bss,
parse_ctx->llc_msg_name);
have_patched = 1;
}
if (parse_ctx->apn_ie &&
cfg->core_apn &&
!parse_ctx->to_bss &&
gbproxy_imsi_matches(cfg, GBPROX_MATCH_PATCHING, link_info) &&
cfg->core_apn) {
size_t new_len;
gbproxy_patch_apn_ie(msg,
parse_ctx->apn_ie, parse_ctx->apn_ie_len,
peer, &new_len, parse_ctx->llc_msg_name);
*len_change += (int)new_len - (int)parse_ctx->apn_ie_len;
have_patched = 1;
}
if (have_patched) {
llc_len += *len_change;
ghp->crc_length += *len_change;
/* Fix FCS */
fcs = gprs_llc_fcs(llc, ghp->crc_length);
LOGP(DLLC, LOGL_DEBUG, "Updated LLC message, CRC: %06x -> %06x\n",
ghp->fcs, fcs);
llc[llc_len - 3] = fcs & 0xff;
llc[llc_len - 2] = (fcs >> 8) & 0xff;
llc[llc_len - 1] = (fcs >> 16) & 0xff;
}
return have_patched;
}
/* patch BSSGP message to use core_mcc/mnc on the SGSN side */
void gbproxy_patch_bssgp(struct msgb *msg, uint8_t *bssgp, size_t bssgp_len,
struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info, int *len_change,
struct gprs_gb_parse_context *parse_ctx)
{
const char *err_info = NULL;
int err_ctr = -1;
if (parse_ctx->bssgp_raid_enc)
gbproxy_patch_raid(parse_ctx->bssgp_raid_enc, peer,
parse_ctx->to_bss, "BSSGP");
if (parse_ctx->need_decryption &&
(peer->cfg->patch_ptmsi || peer->cfg->core_apn)) {
/* Patching LLC messages has been requested
* explicitly, but the message (including the
* type) is encrypted, so we possibly fail to
* patch the LLC part of the message. */
err_ctr = GBPROX_PEER_CTR_PATCH_CRYPT_ERR;
err_info = "GMM message is encrypted";
goto patch_error;
}
if (!link_info && parse_ctx->tlli_enc && parse_ctx->to_bss) {
/* Happens with unknown (not cached) TLLI coming from
* the SGSN */
/* TODO: What shall be done with the message in this case? */
err_ctr = GBPROX_PEER_CTR_TLLI_UNKNOWN;
err_info = "TLLI sent by the SGSN is unknown";
goto patch_error;
}
if (!link_info)
return;
if (parse_ctx->tlli_enc && peer->cfg->patch_ptmsi) {
uint32_t tlli = gbproxy_map_tlli(parse_ctx->tlli,
link_info, parse_ctx->to_bss);
if (tlli) {
gbproxy_patch_tlli(parse_ctx->tlli_enc, peer, tlli,
parse_ctx->to_bss, "TLLI");
parse_ctx->tlli = tlli;
} else {
/* Internal error */
err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
err_info = "Replacement TLLI is 0";
goto patch_error;
}
}
if (parse_ctx->bssgp_ptmsi_enc && peer->cfg->patch_ptmsi) {
uint32_t ptmsi;
if (parse_ctx->to_bss)
ptmsi = link_info->tlli.ptmsi;
else
ptmsi = link_info->sgsn_tlli.ptmsi;
if (ptmsi != GSM_RESERVED_TMSI)
gbproxy_patch_ptmsi(
parse_ctx->bssgp_ptmsi_enc, peer,
ptmsi, parse_ctx->to_bss, "BSSGP P-TMSI");
}
if (parse_ctx->llc) {
uint8_t *llc = parse_ctx->llc;
size_t llc_len = parse_ctx->llc_len;
int llc_len_change = 0;
gbproxy_patch_llc(msg, llc, llc_len, peer, link_info,
&llc_len_change, parse_ctx);
/* Note that the APN might have been resized here, but no
* pointer int the parse_ctx will refer to an adress after the
* APN. So it's possible to patch first and do the TLLI
* handling afterwards. */
if (llc_len_change) {
llc_len += llc_len_change;
/* Fix LLC IE len */
/* TODO: This is a kludge, but the a pointer to the
* start of the IE is not available here */
if (llc[-2] == BSSGP_IE_LLC_PDU && llc[-1] & 0x80) {
/* most probably a one byte length */
if (llc_len > 127) {
err_info = "Cannot increase size";
err_ctr = GBPROX_PEER_CTR_PATCH_ERR;
goto patch_error;
}
llc[-1] = llc_len | 0x80;
} else {
llc[-2] = (llc_len >> 8) & 0x7f;
llc[-1] = llc_len & 0xff;
}
*len_change += llc_len_change;
}
/* Note that the tp struct might contain invalid pointers here
* if the LLC field has changed its size */
parse_ctx->llc_len = llc_len;
}
return;
patch_error:
OSMO_ASSERT(err_ctr >= 0);
rate_ctr_inc(&peer->ctrg->ctr[err_ctr]);
LOGP(DGPRS, LOGL_ERROR,
"NSEI=%u(%s) failed to patch BSSGP message as requested: %s.\n",
msgb_nsei(msg), parse_ctx->to_bss ? "SGSN" : "BSS",
err_info);
}
void gbproxy_clear_patch_filter(struct gbproxy_match *match)
{
if (match->enable) {
regfree(&match->re_comp);
match->enable = 0;
}
talloc_free(match->re_str);
match->re_str = NULL;
}
int gbproxy_set_patch_filter(struct gbproxy_match *match, const char *filter,
const char **err_msg)
{
static char err_buf[300];
int rc;
gbproxy_clear_patch_filter(match);
if (!filter)
return 0;
rc = regcomp(&match->re_comp, filter,
REG_EXTENDED | REG_NOSUB | REG_ICASE);
if (rc == 0) {
match->enable = 1;
match->re_str = talloc_strdup(tall_bsc_ctx, filter);
return 0;
}
if (err_msg) {
regerror(rc, &match->re_comp,
err_buf, sizeof(err_buf));
*err_msg = err_buf;
}
return -1;
}
int gbproxy_check_imsi(struct gbproxy_match *match,
const uint8_t *imsi, size_t imsi_len)
{
char mi_buf[200];
int rc;
if (!match->enable)
return 1;
rc = gprs_is_mi_imsi(imsi, imsi_len);
if (rc > 0)
rc = gsm48_mi_to_string(mi_buf, sizeof(mi_buf), imsi, imsi_len);
if (rc <= 0) {
LOGP(DGPRS, LOGL_NOTICE, "Invalid IMSI %s\n",
osmo_hexdump(imsi, imsi_len));
return -1;
}
LOGP(DGPRS, LOGL_DEBUG, "Checking IMSI '%s' (%d)\n", mi_buf, rc);
rc = regexec(&match->re_comp, mi_buf, 0, NULL, 0);
if (rc == REG_NOMATCH) {
LOGP(DGPRS, LOGL_INFO,
"IMSI '%s' doesn't match pattern '%s'\n",
mi_buf, match->re_str);
return 0;
}
return 1;
}

View File

@ -1,222 +0,0 @@
/* Gb proxy peer handling */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010-2013 by On-Waves
* (C) 2013 by Holger Hans Peter Freyther
* 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/gb_proxy.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_data_shared.h>
#include <openbsc/debug.h>
#include <osmocom/gprs/protocol/gsm_08_18.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/talloc.h>
#include <string.h>
static const struct rate_ctr_desc peer_ctr_description[] = {
{ "blocked", "BVC Block " },
{ "unblocked", "BVC Unblock " },
{ "dropped", "BVC blocked, dropped packet " },
{ "inv-nsei", "NSEI mismatch " },
{ "tx-err", "NS Transmission error " },
{ "raid-mod.bss", "RAID patched (BSS )" },
{ "raid-mod.sgsn", "RAID patched (SGSN)" },
{ "apn-mod.sgsn", "APN patched " },
{ "tlli-mod.bss", "TLLI patched (BSS )" },
{ "tlli-mod.sgsn", "TLLI patched (SGSN)" },
{ "ptmsi-mod.bss", "P-TMSI patched (BSS )" },
{ "ptmsi-mod.sgsn","P-TMSI patched (SGSN)" },
{ "mod-crypt-err", "Patch error: encrypted " },
{ "mod-err", "Patch error: other " },
{ "attach-reqs", "Attach Request count " },
{ "attach-rejs", "Attach Reject count " },
{ "attach-acks", "Attach Accept count " },
{ "attach-cpls", "Attach Completed count " },
{ "ra-upd-reqs", "RoutingArea Update Request count" },
{ "ra-upd-rejs", "RoutingArea Update Reject count " },
{ "ra-upd-acks", "RoutingArea Update Accept count " },
{ "ra-upd-cpls", "RoutingArea Update Compltd count" },
{ "gmm-status", "GMM Status count (BSS)" },
{ "gmm-status", "GMM Status count (SGSN)" },
{ "detach-reqs", "Detach Request count " },
{ "detach-acks", "Detach Accept count " },
{ "pdp-act-reqs", "PDP Activation Request count " },
{ "pdp-act-rejs", "PDP Activation Reject count " },
{ "pdp-act-acks", "PDP Activation Accept count " },
{ "pdp-deact-reqs","PDP Deactivation Request count " },
{ "pdp-deact-acks","PDP Deactivation Accept count " },
{ "tlli-unknown", "TLLI from SGSN unknown " },
{ "tlli-cache", "TLLI cache size " },
};
osmo_static_assert(ARRAY_SIZE(peer_ctr_description) == GBPROX_PEER_CTR_LAST, everything_described);
static const struct rate_ctr_group_desc peer_ctrg_desc = {
.group_name_prefix = "gbproxy.peer",
.group_description = "GBProxy Peer Statistics",
.num_ctr = ARRAY_SIZE(peer_ctr_description),
.ctr_desc = peer_ctr_description,
.class_id = OSMO_STATS_CLASS_PEER,
};
/* Find the gbprox_peer by its BVCI */
struct gbproxy_peer *gbproxy_peer_by_bvci(struct gbproxy_config *cfg, uint16_t bvci)
{
struct gbproxy_peer *peer;
llist_for_each_entry(peer, &cfg->bts_peers, list) {
if (peer->bvci == bvci)
return peer;
}
return NULL;
}
/* Find the gbprox_peer by its NSEI */
struct gbproxy_peer *gbproxy_peer_by_nsei(struct gbproxy_config *cfg,
uint16_t nsei)
{
struct gbproxy_peer *peer;
llist_for_each_entry(peer, &cfg->bts_peers, list) {
if (peer->nsei == nsei)
return peer;
}
return NULL;
}
/* look-up a peer by its Routeing Area Identification (RAI) */
struct gbproxy_peer *gbproxy_peer_by_rai(struct gbproxy_config *cfg,
const uint8_t *ra)
{
struct gbproxy_peer *peer;
llist_for_each_entry(peer, &cfg->bts_peers, list) {
if (!memcmp(peer->ra, ra, 6))
return peer;
}
return NULL;
}
/* look-up a peer by its Location Area Identification (LAI) */
struct gbproxy_peer *gbproxy_peer_by_lai(struct gbproxy_config *cfg,
const uint8_t *la)
{
struct gbproxy_peer *peer;
llist_for_each_entry(peer, &cfg->bts_peers, list) {
if (!memcmp(peer->ra, la, 5))
return peer;
}
return NULL;
}
/* look-up a peer by its Location Area Code (LAC) */
struct gbproxy_peer *gbproxy_peer_by_lac(struct gbproxy_config *cfg,
const uint8_t *la)
{
struct gbproxy_peer *peer;
llist_for_each_entry(peer, &cfg->bts_peers, list) {
if (!memcmp(peer->ra + 3, la + 3, 2))
return peer;
}
return NULL;
}
struct gbproxy_peer *gbproxy_peer_by_bssgp_tlv(struct gbproxy_config *cfg,
struct tlv_parsed *tp)
{
if (TLVP_PRESENT(tp, BSSGP_IE_BVCI)) {
uint16_t bvci;
bvci = ntohs(tlvp_val16_unal(tp, BSSGP_IE_BVCI));
if (bvci >= 2)
return gbproxy_peer_by_bvci(cfg, bvci);
}
if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA)) {
uint8_t *rai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA);
/* Only compare LAC part, since MCC/MNC are possibly patched.
* Since the LAC of different BSS must be different when
* MCC/MNC are patched, collisions shouldn't happen. */
return gbproxy_peer_by_lac(cfg, rai);
}
if (TLVP_PRESENT(tp, BSSGP_IE_LOCATION_AREA)) {
uint8_t *lai = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LOCATION_AREA);
return gbproxy_peer_by_lac(cfg, lai);
}
return NULL;
}
struct gbproxy_peer *gbproxy_peer_alloc(struct gbproxy_config *cfg, uint16_t bvci)
{
struct gbproxy_peer *peer;
peer = talloc_zero(tall_bsc_ctx, struct gbproxy_peer);
if (!peer)
return NULL;
peer->bvci = bvci;
peer->ctrg = rate_ctr_group_alloc(peer, &peer_ctrg_desc, bvci);
if (!peer->ctrg) {
talloc_free(peer);
return NULL;
}
peer->cfg = cfg;
llist_add(&peer->list, &cfg->bts_peers);
INIT_LLIST_HEAD(&peer->patch_state.logical_links);
return peer;
}
void gbproxy_peer_free(struct gbproxy_peer *peer)
{
llist_del(&peer->list);
gbproxy_delete_link_infos(peer);
rate_ctr_group_free(peer->ctrg);
peer->ctrg = NULL;
talloc_free(peer);
}
int gbproxy_cleanup_peers(struct gbproxy_config *cfg, uint16_t nsei, uint16_t bvci)
{
int counter = 0;
struct gbproxy_peer *peer, *tmp;
llist_for_each_entry_safe(peer, tmp, &cfg->bts_peers, list) {
if (peer->nsei != nsei)
continue;
if (bvci && peer->bvci != bvci)
continue;
gbproxy_peer_free(peer);
counter += 1;
}
return counter;
}

View File

@ -1,723 +0,0 @@
/* Gb-proxy TLLI state handling */
/* (C) 2014 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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/gsm/gsm48.h>
#include <openbsc/gb_proxy.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/gprs_gb_parse.h>
#include <openbsc/debug.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/talloc.h>
struct gbproxy_link_info *gbproxy_link_info_by_tlli(struct gbproxy_peer *peer,
uint32_t tlli)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &peer->patch_state;
if (!tlli)
return NULL;
llist_for_each_entry(link_info, &state->logical_links, list)
if (link_info->tlli.current == tlli ||
link_info->tlli.assigned == tlli)
return link_info;
return NULL;
}
struct gbproxy_link_info *gbproxy_link_info_by_ptmsi(
struct gbproxy_peer *peer,
uint32_t ptmsi)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &peer->patch_state;
if (ptmsi == GSM_RESERVED_TMSI)
return NULL;
llist_for_each_entry(link_info, &state->logical_links, list)
if (link_info->tlli.ptmsi == ptmsi)
return link_info;
return NULL;
}
struct gbproxy_link_info *gbproxy_link_info_by_any_sgsn_tlli(
struct gbproxy_peer *peer,
uint32_t tlli)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &peer->patch_state;
if (!tlli)
return NULL;
/* Don't care about the NSEI */
llist_for_each_entry(link_info, &state->logical_links, list)
if (link_info->sgsn_tlli.current == tlli ||
link_info->sgsn_tlli.assigned == tlli)
return link_info;
return NULL;
}
struct gbproxy_link_info *gbproxy_link_info_by_sgsn_tlli(
struct gbproxy_peer *peer,
uint32_t tlli, uint32_t sgsn_nsei)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &peer->patch_state;
if (!tlli)
return NULL;
llist_for_each_entry(link_info, &state->logical_links, list)
if ((link_info->sgsn_tlli.current == tlli ||
link_info->sgsn_tlli.assigned == tlli) &&
link_info->sgsn_nsei == sgsn_nsei)
return link_info;
return NULL;
}
struct gbproxy_link_info *gbproxy_link_info_by_imsi(
struct gbproxy_peer *peer,
const uint8_t *imsi,
size_t imsi_len)
{
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &peer->patch_state;
if (!gprs_is_mi_imsi(imsi, imsi_len))
return NULL;
llist_for_each_entry(link_info, &state->logical_links, list) {
if (link_info->imsi_len != imsi_len)
continue;
if (memcmp(link_info->imsi, imsi, imsi_len) != 0)
continue;
return link_info;
}
return NULL;
}
void gbproxy_link_info_discard_messages(struct gbproxy_link_info *link_info)
{
struct msgb *msg, *nxt;
llist_for_each_entry_safe(msg, nxt, &link_info->stored_msgs, list) {
llist_del(&msg->list);
msgb_free(msg);
}
}
void gbproxy_delete_link_info(struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info)
{
struct gbproxy_patch_state *state = &peer->patch_state;
gbproxy_link_info_discard_messages(link_info);
llist_del(&link_info->list);
talloc_free(link_info);
state->logical_link_count -= 1;
peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
state->logical_link_count;
}
void gbproxy_delete_link_infos(struct gbproxy_peer *peer)
{
struct gbproxy_link_info *link_info, *nxt;
struct gbproxy_patch_state *state = &peer->patch_state;
llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list)
gbproxy_delete_link_info(peer, link_info);
OSMO_ASSERT(state->logical_link_count == 0);
OSMO_ASSERT(llist_empty(&state->logical_links));
}
void gbproxy_attach_link_info(struct gbproxy_peer *peer, time_t now,
struct gbproxy_link_info *link_info)
{
struct gbproxy_patch_state *state = &peer->patch_state;
link_info->timestamp = now;
llist_add(&link_info->list, &state->logical_links);
state->logical_link_count += 1;
peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
state->logical_link_count;
}
int gbproxy_remove_stale_link_infos(struct gbproxy_peer *peer, time_t now)
{
struct gbproxy_patch_state *state = &peer->patch_state;
int exceeded_max_len = 0;
int deleted_count = 0;
int check_for_age;
if (peer->cfg->tlli_max_len > 0)
exceeded_max_len =
state->logical_link_count - peer->cfg->tlli_max_len;
check_for_age = peer->cfg->tlli_max_age > 0;
for (; exceeded_max_len > 0; exceeded_max_len--) {
struct gbproxy_link_info *link_info;
OSMO_ASSERT(!llist_empty(&state->logical_links));
link_info = llist_entry(state->logical_links.prev,
struct gbproxy_link_info,
list);
LOGP(DGPRS, LOGL_INFO,
"Removing TLLI %08x from list "
"(stale, length %d, max_len exceeded)\n",
link_info->tlli.current, state->logical_link_count);
gbproxy_delete_link_info(peer, link_info);
deleted_count += 1;
}
while (check_for_age && !llist_empty(&state->logical_links)) {
time_t age;
struct gbproxy_link_info *link_info;
link_info = llist_entry(state->logical_links.prev,
struct gbproxy_link_info,
list);
age = now - link_info->timestamp;
/* age < 0 only happens after system time jumps, discard entry */
if (age <= peer->cfg->tlli_max_age && age >= 0) {
check_for_age = 0;
continue;
}
LOGP(DGPRS, LOGL_INFO,
"Removing TLLI %08x from list "
"(stale, age %d, max_age exceeded)\n",
link_info->tlli.current, (int)age);
gbproxy_delete_link_info(peer, link_info);
deleted_count += 1;
}
return deleted_count;
}
struct gbproxy_link_info *gbproxy_link_info_alloc( struct gbproxy_peer *peer)
{
struct gbproxy_link_info *link_info;
link_info = talloc_zero(peer, struct gbproxy_link_info);
link_info->tlli.ptmsi = GSM_RESERVED_TMSI;
link_info->sgsn_tlli.ptmsi = GSM_RESERVED_TMSI;
link_info->vu_gen_tx_bss = GBPROXY_INIT_VU_GEN_TX;
INIT_LLIST_HEAD(&link_info->stored_msgs);
return link_info;
}
void gbproxy_detach_link_info(
struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info)
{
struct gbproxy_patch_state *state = &peer->patch_state;
llist_del(&link_info->list);
OSMO_ASSERT(state->logical_link_count > 0);
state->logical_link_count -= 1;
peer->ctrg->ctr[GBPROX_PEER_CTR_TLLI_CACHE_SIZE].current =
state->logical_link_count;
}
void gbproxy_update_link_info(struct gbproxy_link_info *link_info,
const uint8_t *imsi, size_t imsi_len)
{
if (!gprs_is_mi_imsi(imsi, imsi_len))
return;
link_info->imsi_len = imsi_len;
link_info->imsi =
talloc_realloc_size(link_info, link_info->imsi, imsi_len);
OSMO_ASSERT(link_info->imsi != NULL);
memcpy(link_info->imsi, imsi, imsi_len);
}
void gbproxy_reassign_tlli(struct gbproxy_tlli_state *tlli_state,
struct gbproxy_peer *peer, uint32_t new_tlli)
{
if (new_tlli == tlli_state->current)
return;
LOGP(DGPRS, LOGL_INFO,
"The TLLI has been reassigned from %08x to %08x\n",
tlli_state->current, new_tlli);
/* Remember assigned TLLI */
tlli_state->assigned = new_tlli;
tlli_state->bss_validated = 0;
tlli_state->net_validated = 0;
}
uint32_t gbproxy_map_tlli(uint32_t other_tlli,
struct gbproxy_link_info *link_info, int to_bss)
{
uint32_t tlli = 0;
struct gbproxy_tlli_state *src, *dst;
if (to_bss) {
src = &link_info->sgsn_tlli;
dst = &link_info->tlli;
} else {
src = &link_info->tlli;
dst = &link_info->sgsn_tlli;
}
if (src->current == other_tlli)
tlli = dst->current;
else if (src->assigned == other_tlli)
tlli = dst->assigned;
return tlli;
}
static void gbproxy_validate_tlli(struct gbproxy_tlli_state *tlli_state,
uint32_t tlli, int to_bss)
{
LOGP(DGPRS, LOGL_DEBUG,
"%s({current = %08x, assigned = %08x, net_vld = %d, bss_vld = %d}, %08x)\n",
__func__, tlli_state->current, tlli_state->assigned,
tlli_state->net_validated, tlli_state->bss_validated, tlli);
if (!tlli_state->assigned || tlli_state->assigned != tlli)
return;
/* TODO: Is this ok? Check spec */
if (gprs_tlli_type(tlli) != TLLI_LOCAL)
return;
/* See GSM 04.08, 4.7.1.5 */
if (to_bss)
tlli_state->net_validated = 1;
else
tlli_state->bss_validated = 1;
if (!tlli_state->bss_validated || !tlli_state->net_validated)
return;
LOGP(DGPRS, LOGL_INFO,
"The TLLI %08x has been validated (was %08x)\n",
tlli_state->assigned, tlli_state->current);
tlli_state->current = tlli;
tlli_state->assigned = 0;
}
static void gbproxy_touch_link_info(struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info,
time_t now)
{
gbproxy_detach_link_info(peer, link_info);
gbproxy_attach_link_info(peer, now, link_info);
}
static int gbproxy_unregister_link_info(struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info)
{
if (!link_info)
return 1;
if (link_info->tlli.ptmsi == GSM_RESERVED_TMSI && !link_info->imsi_len) {
LOGP(DGPRS, LOGL_INFO,
"Removing TLLI %08x from list (P-TMSI or IMSI are not set)\n",
link_info->tlli.current);
gbproxy_delete_link_info(peer, link_info);
return 1;
}
link_info->tlli.current = 0;
link_info->tlli.assigned = 0;
link_info->sgsn_tlli.current = 0;
link_info->sgsn_tlli.assigned = 0;
link_info->is_deregistered = 1;
gbproxy_reset_link(link_info);
return 0;
}
int gbproxy_imsi_matches(struct gbproxy_config *cfg,
enum gbproxy_match_id match_id,
struct gbproxy_link_info *link_info)
{
struct gbproxy_match *match;
OSMO_ASSERT(match_id >= 0 && match_id < ARRAY_SIZE(cfg->matches));
match = &cfg->matches[match_id];
if (!match->enable)
return 1;
return link_info != NULL && link_info->is_matching[match_id];
}
void gbproxy_assign_imsi(struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info,
struct gprs_gb_parse_context *parse_ctx)
{
int imsi_matches;
struct gbproxy_link_info *other_link_info;
enum gbproxy_match_id match_id;
/* Make sure that there is a second entry with the same IMSI */
other_link_info = gbproxy_link_info_by_imsi(
peer, parse_ctx->imsi, parse_ctx->imsi_len);
if (other_link_info && other_link_info != link_info) {
char mi_buf[200];
mi_buf[0] = '\0';
gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
parse_ctx->imsi, parse_ctx->imsi_len);
LOGP(DGPRS, LOGL_INFO,
"Removing TLLI %08x from list (IMSI %s re-used)\n",
other_link_info->tlli.current, mi_buf);
gbproxy_delete_link_info(peer, other_link_info);
}
/* Update the IMSI field */
gbproxy_update_link_info(link_info,
parse_ctx->imsi, parse_ctx->imsi_len);
/* Check, whether the IMSI matches */
OSMO_ASSERT(ARRAY_SIZE(link_info->is_matching) ==
ARRAY_SIZE(peer->cfg->matches));
for (match_id = 0; match_id < ARRAY_SIZE(link_info->is_matching);
++match_id) {
imsi_matches = gbproxy_check_imsi(
&peer->cfg->matches[match_id],
parse_ctx->imsi, parse_ctx->imsi_len);
if (imsi_matches >= 0)
link_info->is_matching[match_id] = imsi_matches;
}
}
static int gbproxy_tlli_match(const struct gbproxy_tlli_state *a,
const struct gbproxy_tlli_state *b)
{
if (a->current && a->current == b->current)
return 1;
if (a->assigned && a->assigned == b->assigned)
return 1;
if (a->ptmsi != GSM_RESERVED_TMSI && a->ptmsi == b->ptmsi)
return 1;
return 0;
}
static void gbproxy_remove_matching_link_infos(
struct gbproxy_peer *peer, struct gbproxy_link_info *link_info)
{
struct gbproxy_link_info *info, *nxt;
struct gbproxy_patch_state *state = &peer->patch_state;
/* Make sure that there is no second entry with the same P-TMSI or TLLI */
llist_for_each_entry_safe(info, nxt, &state->logical_links, list) {
if (info == link_info)
continue;
if (!gbproxy_tlli_match(&link_info->tlli, &info->tlli) &&
(link_info->sgsn_nsei != info->sgsn_nsei ||
!gbproxy_tlli_match(&link_info->sgsn_tlli, &info->sgsn_tlli)))
continue;
LOGP(DGPRS, LOGL_INFO,
"Removing TLLI %08x from list (P-TMSI/TLLI re-used)\n",
info->tlli.current);
gbproxy_delete_link_info(peer, info);
}
}
static struct gbproxy_link_info *gbproxy_get_link_info_ul(
struct gbproxy_peer *peer,
int *tlli_is_valid,
struct gprs_gb_parse_context *parse_ctx)
{
struct gbproxy_link_info *link_info = NULL;
if (parse_ctx->tlli_enc) {
link_info = gbproxy_link_info_by_tlli(peer, parse_ctx->tlli);
if (link_info) {
*tlli_is_valid = 1;
return link_info;
}
}
*tlli_is_valid = 0;
if (!link_info && parse_ctx->imsi) {
link_info = gbproxy_link_info_by_imsi(
peer, parse_ctx->imsi, parse_ctx->imsi_len);
}
if (!link_info && parse_ctx->ptmsi_enc && !parse_ctx->old_raid_is_foreign) {
uint32_t bss_ptmsi;
gprs_parse_tmsi(parse_ctx->ptmsi_enc, &bss_ptmsi);
link_info = gbproxy_link_info_by_ptmsi(peer, bss_ptmsi);
}
if (!link_info)
return NULL;
link_info->is_deregistered = 0;
return link_info;
}
struct gbproxy_link_info *gbproxy_update_link_state_ul(
struct gbproxy_peer *peer,
time_t now,
struct gprs_gb_parse_context *parse_ctx)
{
struct gbproxy_link_info *link_info;
int tlli_is_valid;
link_info = gbproxy_get_link_info_ul(peer, &tlli_is_valid, parse_ctx);
if (parse_ctx->tlli_enc && parse_ctx->llc) {
uint32_t sgsn_tlli;
if (!link_info) {
LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list\n",
parse_ctx->tlli);
link_info = gbproxy_link_info_alloc(peer);
gbproxy_attach_link_info(peer, now, link_info);
/* Setup TLLIs */
sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info,
parse_ctx->tlli);
link_info->sgsn_tlli.current = sgsn_tlli;
link_info->tlli.current = parse_ctx->tlli;
} else if (!tlli_is_valid) {
/* New TLLI (info found by IMSI or P-TMSI) */
link_info->tlli.current = parse_ctx->tlli;
link_info->tlli.assigned = 0;
link_info->sgsn_tlli.current =
gbproxy_make_sgsn_tlli(peer, link_info,
parse_ctx->tlli);
link_info->sgsn_tlli.assigned = 0;
gbproxy_touch_link_info(peer, link_info, now);
} else {
sgsn_tlli = gbproxy_map_tlli(parse_ctx->tlli, link_info, 0);
if (!sgsn_tlli)
sgsn_tlli = gbproxy_make_sgsn_tlli(peer, link_info,
parse_ctx->tlli);
gbproxy_validate_tlli(&link_info->tlli,
parse_ctx->tlli, 0);
gbproxy_validate_tlli(&link_info->sgsn_tlli,
sgsn_tlli, 0);
gbproxy_touch_link_info(peer, link_info, now);
}
} else if (link_info) {
gbproxy_touch_link_info(peer, link_info, now);
}
if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
gbproxy_assign_imsi(peer, link_info, parse_ctx);
return link_info;
}
static struct gbproxy_link_info *gbproxy_get_link_info_dl(
struct gbproxy_peer *peer,
struct gprs_gb_parse_context *parse_ctx)
{
struct gbproxy_link_info *link_info = NULL;
/* Which key to use depends on its availability only, if that fails, do
* not retry it with another key (e.g. IMSI). */
if (parse_ctx->tlli_enc)
link_info = gbproxy_link_info_by_sgsn_tlli(peer, parse_ctx->tlli,
parse_ctx->peer_nsei);
/* TODO: Get link_info by (SGSN) P-TMSI if that is available (see
* GSM 08.18, 7.2) instead of using the IMSI as key. */
else if (parse_ctx->imsi)
link_info = gbproxy_link_info_by_imsi(
peer, parse_ctx->imsi, parse_ctx->imsi_len);
if (link_info)
link_info->is_deregistered = 0;
return link_info;
}
struct gbproxy_link_info *gbproxy_update_link_state_dl(
struct gbproxy_peer *peer,
time_t now,
struct gprs_gb_parse_context *parse_ctx)
{
struct gbproxy_link_info *link_info = NULL;
link_info = gbproxy_get_link_info_dl(peer, parse_ctx);
if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && link_info) {
/* A new P-TMSI has been signalled in the message,
* register new TLLI */
uint32_t new_sgsn_ptmsi;
uint32_t new_bss_ptmsi = GSM_RESERVED_TMSI;
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_sgsn_ptmsi);
if (link_info->sgsn_tlli.ptmsi == new_sgsn_ptmsi)
new_bss_ptmsi = link_info->tlli.ptmsi;
if (new_bss_ptmsi == GSM_RESERVED_TMSI)
new_bss_ptmsi = gbproxy_make_bss_ptmsi(peer, new_sgsn_ptmsi);
LOGP(DGPRS, LOGL_INFO,
"Got new PTMSI %08x from SGSN, using %08x for BSS\n",
new_sgsn_ptmsi, new_bss_ptmsi);
/* Setup PTMSIs */
link_info->sgsn_tlli.ptmsi = new_sgsn_ptmsi;
link_info->tlli.ptmsi = new_bss_ptmsi;
} else if (parse_ctx->tlli_enc && parse_ctx->new_ptmsi_enc && !link_info &&
!peer->cfg->patch_ptmsi) {
/* A new P-TMSI has been signalled in the message with an unknown
* TLLI, create a new link_info */
/* TODO: Add a test case for this branch */
uint32_t new_ptmsi;
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
LOGP(DGPRS, LOGL_INFO,
"Adding TLLI %08x to list (SGSN, new P-TMSI is %08x)\n",
parse_ctx->tlli, new_ptmsi);
link_info = gbproxy_link_info_alloc(peer);
link_info->sgsn_tlli.current = parse_ctx->tlli;
link_info->tlli.current = parse_ctx->tlli;
link_info->sgsn_tlli.ptmsi = new_ptmsi;
link_info->tlli.ptmsi = new_ptmsi;
gbproxy_attach_link_info(peer, now, link_info);
} else if (parse_ctx->tlli_enc && parse_ctx->llc && !link_info &&
!peer->cfg->patch_ptmsi) {
/* Unknown SGSN TLLI, create a new link_info */
uint32_t new_ptmsi;
link_info = gbproxy_link_info_alloc(peer);
LOGP(DGPRS, LOGL_INFO, "Adding TLLI %08x to list (SGSN)\n",
parse_ctx->tlli);
gbproxy_attach_link_info(peer, now, link_info);
/* Setup TLLIs */
link_info->sgsn_tlli.current = parse_ctx->tlli;
link_info->tlli.current = parse_ctx->tlli;
if (!parse_ctx->new_ptmsi_enc)
return link_info;
/* A new P-TMSI has been signalled in the message */
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
LOGP(DGPRS, LOGL_INFO,
"Assigning new P-TMSI %08x\n", new_ptmsi);
/* Setup P-TMSIs */
link_info->sgsn_tlli.ptmsi = new_ptmsi;
link_info->tlli.ptmsi = new_ptmsi;
} else if (parse_ctx->tlli_enc && parse_ctx->llc && link_info) {
uint32_t bss_tlli = gbproxy_map_tlli(parse_ctx->tlli,
link_info, 1);
gbproxy_validate_tlli(&link_info->sgsn_tlli, parse_ctx->tlli, 1);
gbproxy_validate_tlli(&link_info->tlli, bss_tlli, 1);
gbproxy_touch_link_info(peer, link_info, now);
} else if (link_info) {
gbproxy_touch_link_info(peer, link_info, now);
}
if (parse_ctx->imsi && link_info && link_info->imsi_len == 0)
gbproxy_assign_imsi(peer, link_info, parse_ctx);
return link_info;
}
int gbproxy_update_link_state_after(
struct gbproxy_peer *peer,
struct gbproxy_link_info *link_info,
time_t now,
struct gprs_gb_parse_context *parse_ctx)
{
int rc = 0;
if (parse_ctx->invalidate_tlli && link_info) {
int keep_info =
peer->cfg->keep_link_infos == GBPROX_KEEP_ALWAYS ||
(peer->cfg->keep_link_infos == GBPROX_KEEP_REATTACH &&
parse_ctx->await_reattach) ||
(peer->cfg->keep_link_infos == GBPROX_KEEP_IDENTIFIED &&
link_info->imsi_len > 0);
if (keep_info) {
LOGP(DGPRS, LOGL_INFO, "Unregistering TLLI %08x\n",
link_info->tlli.current);
rc = gbproxy_unregister_link_info(peer, link_info);
} else {
LOGP(DGPRS, LOGL_INFO, "Removing TLLI %08x from list\n",
link_info->tlli.current);
gbproxy_delete_link_info(peer, link_info);
rc = 1;
}
} else if (parse_ctx->to_bss && parse_ctx->tlli_enc &&
parse_ctx->new_ptmsi_enc && link_info) {
/* A new PTMSI has been signaled in the message,
* register new TLLI */
uint32_t new_sgsn_ptmsi = link_info->sgsn_tlli.ptmsi;
uint32_t new_bss_ptmsi = link_info->tlli.ptmsi;
uint32_t new_sgsn_tlli;
uint32_t new_bss_tlli = 0;
new_sgsn_tlli = gprs_tmsi2tlli(new_sgsn_ptmsi, TLLI_LOCAL);
if (new_bss_ptmsi != GSM_RESERVED_TMSI)
new_bss_tlli = gprs_tmsi2tlli(new_bss_ptmsi, TLLI_LOCAL);
LOGP(DGPRS, LOGL_INFO,
"Assigning new TLLI %08x to SGSN, %08x to BSS\n",
new_sgsn_tlli, new_bss_tlli);
gbproxy_reassign_tlli(&link_info->sgsn_tlli,
peer, new_sgsn_tlli);
gbproxy_reassign_tlli(&link_info->tlli,
peer, new_bss_tlli);
gbproxy_remove_matching_link_infos(peer, link_info);
}
gbproxy_remove_stale_link_infos(peer, now);
return rc;
}

View File

@ -1,853 +0,0 @@
/*
* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <time.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/rate_ctr.h>
#include <openbsc/gsm_04_08.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gsm/apn.h>
#include <openbsc/debug.h>
#include <openbsc/gb_proxy.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/vty.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/misc.h>
static struct gbproxy_config *g_cfg = NULL;
/*
* vty code for mgcp below
*/
static struct cmd_node gbproxy_node = {
GBPROXY_NODE,
"%s(config-gbproxy)# ",
1,
};
static const struct value_string keep_modes[] = {
{GBPROX_KEEP_NEVER, "never"},
{GBPROX_KEEP_REATTACH, "re-attach"},
{GBPROX_KEEP_IDENTIFIED, "identified"},
{GBPROX_KEEP_ALWAYS, "always"},
{0, NULL}
};
static const struct value_string match_ids[] = {
{GBPROX_MATCH_PATCHING, "patching"},
{GBPROX_MATCH_ROUTING, "routing"},
{0, NULL}
};
static void gbprox_vty_print_peer(struct vty *vty, struct gbproxy_peer *peer)
{
struct gprs_ra_id raid;
gsm48_parse_ra(&raid, peer->ra);
vty_out(vty, "NSEI %5u, PTP-BVCI %5u, "
"RAI %u-%u-%u-%u",
peer->nsei, peer->bvci,
raid.mcc, raid.mnc, raid.lac, raid.rac);
if (peer->blocked)
vty_out(vty, " [BVC-BLOCKED]");
vty_out(vty, "%s", VTY_NEWLINE);
}
static int config_write_gbproxy(struct vty *vty)
{
enum gbproxy_match_id match_id;
vty_out(vty, "gbproxy%s", VTY_NEWLINE);
vty_out(vty, " sgsn nsei %u%s", g_cfg->nsip_sgsn_nsei,
VTY_NEWLINE);
if (g_cfg->core_mcc > 0)
vty_out(vty, " core-mobile-country-code %d%s",
g_cfg->core_mcc, VTY_NEWLINE);
if (g_cfg->core_mnc > 0)
vty_out(vty, " core-mobile-network-code %d%s",
g_cfg->core_mnc, VTY_NEWLINE);
for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id) {
struct gbproxy_match *match = &g_cfg->matches[match_id];
if (match->re_str)
vty_out(vty, " match-imsi %s %s%s",
get_value_string(match_ids, match_id),
match->re_str, VTY_NEWLINE);
}
if (g_cfg->core_apn != NULL) {
if (g_cfg->core_apn_size > 0) {
char str[500] = {0};
vty_out(vty, " core-access-point-name %s%s",
osmo_apn_to_str(str, g_cfg->core_apn,
g_cfg->core_apn_size),
VTY_NEWLINE);
} else {
vty_out(vty, " core-access-point-name none%s",
VTY_NEWLINE);
}
}
if (g_cfg->route_to_sgsn2)
vty_out(vty, " secondary-sgsn nsei %u%s", g_cfg->nsip_sgsn2_nsei,
VTY_NEWLINE);
if (g_cfg->tlli_max_age > 0)
vty_out(vty, " link-list max-age %d%s",
g_cfg->tlli_max_age, VTY_NEWLINE);
if (g_cfg->tlli_max_len > 0)
vty_out(vty, " link-list max-length %d%s",
g_cfg->tlli_max_len, VTY_NEWLINE);
vty_out(vty, " link-list keep-mode %s%s",
get_value_string(keep_modes, g_cfg->keep_link_infos),
VTY_NEWLINE);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy,
cfg_gbproxy_cmd,
"gbproxy",
"Configure the Gb proxy")
{
vty->node = GBPROXY_NODE;
return CMD_SUCCESS;
}
DEFUN(cfg_nsip_sgsn_nsei,
cfg_nsip_sgsn_nsei_cmd,
"sgsn nsei <0-65534>",
"SGSN information\n"
"NSEI to be used in the connection with the SGSN\n"
"The NSEI\n")
{
unsigned int nsei = atoi(argv[0]);
if (g_cfg->route_to_sgsn2 && g_cfg->nsip_sgsn2_nsei == nsei) {
vty_out(vty, "SGSN NSEI %d conflicts with secondary SGSN NSEI%s",
nsei, VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->nsip_sgsn_nsei = nsei;
return CMD_SUCCESS;
}
#define GBPROXY_CORE_MNC_STR "Use this network code for the core network\n"
DEFUN(cfg_gbproxy_core_mnc,
cfg_gbproxy_core_mnc_cmd,
"core-mobile-network-code <1-999>",
GBPROXY_CORE_MNC_STR "NCC value\n")
{
g_cfg->core_mnc = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_core_mnc,
cfg_gbproxy_no_core_mnc_cmd,
"no core-mobile-network-code",
NO_STR GBPROXY_CORE_MNC_STR)
{
g_cfg->core_mnc = 0;
return CMD_SUCCESS;
}
#define GBPROXY_CORE_MCC_STR "Use this country code for the core network\n"
DEFUN(cfg_gbproxy_core_mcc,
cfg_gbproxy_core_mcc_cmd,
"core-mobile-country-code <1-999>",
GBPROXY_CORE_MCC_STR "MCC value\n")
{
g_cfg->core_mcc = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_core_mcc,
cfg_gbproxy_no_core_mcc_cmd,
"no core-mobile-country-code",
NO_STR GBPROXY_CORE_MCC_STR)
{
g_cfg->core_mcc = 0;
return CMD_SUCCESS;
}
#define GBPROXY_MATCH_IMSI_STR "Restrict actions to certain IMSIs\n"
DEFUN(cfg_gbproxy_match_imsi,
cfg_gbproxy_match_imsi_cmd,
"match-imsi (patching|routing) .REGEXP",
GBPROXY_MATCH_IMSI_STR
"Patch MS related information elements on match only\n"
"Route to the secondary SGSN on match only\n"
"Regular expression for the IMSI match\n")
{
const char *filter = argv[1];
const char *err_msg = NULL;
struct gbproxy_match *match;
enum gbproxy_match_id match_id = get_string_value(match_ids, argv[0]);
OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
match_id < GBPROX_MATCH_LAST);
match = &g_cfg->matches[match_id];
if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
vty_out(vty, "Match expression invalid: %s%s",
err_msg, VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->acquire_imsi = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_match_imsi,
cfg_gbproxy_no_match_imsi_cmd,
"no match-imsi",
NO_STR GBPROXY_MATCH_IMSI_STR)
{
enum gbproxy_match_id match_id;
for (match_id = 0; match_id < ARRAY_SIZE(g_cfg->matches); ++match_id)
gbproxy_clear_patch_filter(&g_cfg->matches[match_id]);
g_cfg->acquire_imsi = 0;
return CMD_SUCCESS;
}
#define GBPROXY_CORE_APN_STR "Use this access point name (APN) for the backbone\n"
#define GBPROXY_CORE_APN_ARG_STR "Replace APN by this string\n" "Remove APN\n"
static int set_core_apn(struct vty *vty, const char *apn)
{
int apn_len;
if (!apn) {
talloc_free(g_cfg->core_apn);
g_cfg->core_apn = NULL;
g_cfg->core_apn_size = 0;
return CMD_SUCCESS;
}
apn_len = strlen(apn);
if (apn_len >= 100) {
vty_out(vty, "APN string too long (max 99 chars)%s",
VTY_NEWLINE);
return CMD_WARNING;
}
if (apn_len == 0) {
talloc_free(g_cfg->core_apn);
/* TODO: replace NULL */
g_cfg->core_apn = talloc_zero_size(NULL, 2);
g_cfg->core_apn_size = 0;
} else {
/* TODO: replace NULL */
g_cfg->core_apn =
talloc_realloc_size(NULL, g_cfg->core_apn, apn_len + 1);
g_cfg->core_apn_size =
gprs_str_to_apn(g_cfg->core_apn, apn_len + 1, apn);
}
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_core_apn,
cfg_gbproxy_core_apn_cmd,
"core-access-point-name (APN|none)",
GBPROXY_CORE_APN_STR GBPROXY_CORE_APN_ARG_STR)
{
if (strcmp(argv[0], "none") == 0)
return set_core_apn(vty, "");
else
return set_core_apn(vty, argv[0]);
}
DEFUN(cfg_gbproxy_no_core_apn,
cfg_gbproxy_no_core_apn_cmd,
"no core-access-point-name",
NO_STR GBPROXY_CORE_APN_STR)
{
return set_core_apn(vty, NULL);
}
/* TODO: Remove the patch-ptmsi command, since P-TMSI patching is enabled
* automatically when needed. This command is only left for manual testing
* (e.g. doing P-TMSI patching without using a secondary SGSN)
*/
#define GBPROXY_PATCH_PTMSI_STR "Patch P-TMSI/TLLI\n"
DEFUN(cfg_gbproxy_patch_ptmsi,
cfg_gbproxy_patch_ptmsi_cmd,
"patch-ptmsi",
GBPROXY_PATCH_PTMSI_STR)
{
g_cfg->patch_ptmsi = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_patch_ptmsi,
cfg_gbproxy_no_patch_ptmsi_cmd,
"no patch-ptmsi",
NO_STR GBPROXY_PATCH_PTMSI_STR)
{
g_cfg->patch_ptmsi = 0;
return CMD_SUCCESS;
}
/* TODO: Remove the acquire-imsi command, since that feature is enabled
* automatically when IMSI matching is enabled. This command is only left for
* manual testing (e.g. doing IMSI acquisition without IMSI based patching)
*/
#define GBPROXY_ACQUIRE_IMSI_STR "Acquire the IMSI before establishing a LLC connection (Experimental)\n"
DEFUN(cfg_gbproxy_acquire_imsi,
cfg_gbproxy_acquire_imsi_cmd,
"acquire-imsi",
GBPROXY_ACQUIRE_IMSI_STR)
{
g_cfg->acquire_imsi = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_acquire_imsi,
cfg_gbproxy_no_acquire_imsi_cmd,
"no acquire-imsi",
NO_STR GBPROXY_ACQUIRE_IMSI_STR)
{
g_cfg->acquire_imsi = 0;
return CMD_SUCCESS;
}
#define GBPROXY_SECOND_SGSN_STR "Route matching LLC connections to a second SGSN (Experimental)\n"
DEFUN(cfg_gbproxy_secondary_sgsn,
cfg_gbproxy_secondary_sgsn_cmd,
"secondary-sgsn nsei <0-65534>",
GBPROXY_SECOND_SGSN_STR
"NSEI to be used in the connection with the SGSN\n"
"The NSEI\n")
{
unsigned int nsei = atoi(argv[0]);
if (g_cfg->nsip_sgsn_nsei == nsei) {
vty_out(vty, "Secondary SGSN NSEI %d conflicts with primary SGSN NSEI%s",
nsei, VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->route_to_sgsn2 = 1;
g_cfg->nsip_sgsn2_nsei = nsei;
g_cfg->patch_ptmsi = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_no_secondary_sgsn,
cfg_gbproxy_no_secondary_sgsn_cmd,
"no secondary-sgsn",
NO_STR GBPROXY_SECOND_SGSN_STR)
{
g_cfg->route_to_sgsn2 = 0;
g_cfg->nsip_sgsn2_nsei = 0xFFFF;
g_cfg->patch_ptmsi = 0;
return CMD_SUCCESS;
}
#define GBPROXY_LINK_LIST_STR "Set TLLI list parameters\n"
#define GBPROXY_MAX_AGE_STR "Limit maximum age\n"
DEFUN(cfg_gbproxy_link_list_max_age,
cfg_gbproxy_link_list_max_age_cmd,
"link-list max-age <1-999999>",
GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR
"Maximum age in seconds\n")
{
g_cfg->tlli_max_age = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_link_list_no_max_age,
cfg_gbproxy_link_list_no_max_age_cmd,
"no link-list max-age",
NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_AGE_STR)
{
g_cfg->tlli_max_age = 0;
return CMD_SUCCESS;
}
#define GBPROXY_MAX_LEN_STR "Limit list length\n"
DEFUN(cfg_gbproxy_link_list_max_len,
cfg_gbproxy_link_list_max_len_cmd,
"link-list max-length <1-99999>",
GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR
"Maximum number of logical links in the list\n")
{
g_cfg->tlli_max_len = atoi(argv[0]);
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_link_list_no_max_len,
cfg_gbproxy_link_list_no_max_len_cmd,
"no link-list max-length",
NO_STR GBPROXY_LINK_LIST_STR GBPROXY_MAX_LEN_STR)
{
g_cfg->tlli_max_len = 0;
return CMD_SUCCESS;
}
DEFUN(cfg_gbproxy_link_list_keep_mode,
cfg_gbproxy_link_list_keep_mode_cmd,
"link-list keep-mode (never|re-attach|identified|always)",
GBPROXY_LINK_LIST_STR "How to keep entries for detached logical links\n"
"Discard entry immediately after detachment\n"
"Keep entry if a re-attachment has be requested\n"
"Keep entry if it associated with an IMSI\n"
"Don't discard entries after detachment\n")
{
int val = get_string_value(keep_modes, argv[0]);
OSMO_ASSERT(val >= GBPROX_KEEP_NEVER && val <= GBPROX_KEEP_ALWAYS);
g_cfg->keep_link_infos = val;
return CMD_SUCCESS;
}
DEFUN(show_gbproxy, show_gbproxy_cmd, "show gbproxy [stats]",
SHOW_STR "Display information about the Gb proxy\n" "Show statistics\n")
{
struct gbproxy_peer *peer;
int show_stats = argc >= 1;
if (show_stats)
vty_out_rate_ctr_group(vty, "", g_cfg->ctrg);
llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
gbprox_vty_print_peer(vty, peer);
if (show_stats)
vty_out_rate_ctr_group(vty, " ", peer->ctrg);
}
return CMD_SUCCESS;
}
DEFUN(show_gbproxy_links, show_gbproxy_links_cmd, "show gbproxy links",
SHOW_STR "Display information about the Gb proxy\n" "Show logical links\n")
{
struct gbproxy_peer *peer;
char mi_buf[200];
time_t now;
struct timespec ts = {0,};
clock_gettime(CLOCK_MONOTONIC, &ts);
now = ts.tv_sec;
llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
struct gbproxy_link_info *link_info;
struct gbproxy_patch_state *state = &peer->patch_state;
gbprox_vty_print_peer(vty, peer);
llist_for_each_entry(link_info, &state->logical_links, list) {
time_t age = now - link_info->timestamp;
int stored_msgs = 0;
struct llist_head *iter;
llist_for_each(iter, &link_info->stored_msgs)
stored_msgs++;
if (link_info->imsi > 0) {
snprintf(mi_buf, sizeof(mi_buf), "(invalid)");
gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
link_info->imsi,
link_info->imsi_len);
} else {
snprintf(mi_buf, sizeof(mi_buf), "(none)");
}
vty_out(vty, " TLLI %08x, IMSI %s, AGE %d",
link_info->tlli.current, mi_buf, (int)age);
if (stored_msgs)
vty_out(vty, ", STORED %d", stored_msgs);
if (g_cfg->route_to_sgsn2)
vty_out(vty, ", SGSN NSEI %d",
link_info->sgsn_nsei);
if (link_info->is_deregistered)
vty_out(vty, ", DE-REGISTERED");
vty_out(vty, "%s", VTY_NEWLINE);
}
}
return CMD_SUCCESS;
}
DEFUN(delete_gb_bvci, delete_gb_bvci_cmd,
"delete-gbproxy-peer <0-65534> bvci <2-65534>",
"Delete a GBProxy peer by NSEI and optionally BVCI\n"
"NSEI number\n"
"Only delete peer with a matching BVCI\n"
"BVCI number\n")
{
const uint16_t nsei = atoi(argv[0]);
const uint16_t bvci = atoi(argv[1]);
int counter;
counter = gbproxy_cleanup_peers(g_cfg, nsei, bvci);
if (counter == 0) {
vty_out(vty, "BVC not found%s", VTY_NEWLINE);
return CMD_WARNING;
}
return CMD_SUCCESS;
}
DEFUN(delete_gb_nsei, delete_gb_nsei_cmd,
"delete-gbproxy-peer <0-65534> (only-bvc|only-nsvc|all) [dry-run]",
"Delete a GBProxy peer by NSEI and optionally BVCI\n"
"NSEI number\n"
"Only delete BSSGP connections (BVC)\n"
"Only delete dynamic NS connections (NS-VC)\n"
"Delete BVC and dynamic NS connections\n"
"Show what would be deleted instead of actually deleting\n"
)
{
const uint16_t nsei = atoi(argv[0]);
const char *mode = argv[1];
int dry_run = argc > 2;
int delete_bvc = 0;
int delete_nsvc = 0;
int counter;
if (strcmp(mode, "only-bvc") == 0)
delete_bvc = 1;
else if (strcmp(mode, "only-nsvc") == 0)
delete_nsvc = 1;
else
delete_bvc = delete_nsvc = 1;
if (delete_bvc) {
if (!dry_run)
counter = gbproxy_cleanup_peers(g_cfg, nsei, 0);
else {
struct gbproxy_peer *peer;
counter = 0;
llist_for_each_entry(peer, &g_cfg->bts_peers, list) {
if (peer->nsei != nsei)
continue;
vty_out(vty, "BVC: ");
gbprox_vty_print_peer(vty, peer);
counter += 1;
}
}
vty_out(vty, "%sDeleted %d BVC%s",
dry_run ? "Not " : "", counter, VTY_NEWLINE);
}
if (delete_nsvc) {
struct gprs_ns_inst *nsi = g_cfg->nsi;
struct gprs_nsvc *nsvc, *nsvc2;
counter = 0;
llist_for_each_entry_safe(nsvc, nsvc2, &nsi->gprs_nsvcs, list) {
if (nsvc->nsei != nsei)
continue;
if (nsvc->persistent)
continue;
if (!dry_run)
gprs_nsvc_delete(nsvc);
else
vty_out(vty, "NS-VC: NSEI %5u, NS-VCI %5u, "
"remote %s%s",
nsvc->nsei, nsvc->nsvci,
gprs_ns_ll_str(nsvc), VTY_NEWLINE);
counter += 1;
}
vty_out(vty, "%sDeleted %d NS-VC%s",
dry_run ? "Not " : "", counter, VTY_NEWLINE);
}
return CMD_SUCCESS;
}
#define GBPROXY_DELETE_LINK_STR \
"Delete a GBProxy logical link entry by NSEI and identification\nNSEI number\n"
DEFUN(delete_gb_link_by_id, delete_gb_link_by_id_cmd,
"delete-gbproxy-link <0-65534> (tlli|imsi|sgsn-nsei) IDENT",
GBPROXY_DELETE_LINK_STR
"Delete entries with a matching TLLI (hex)\n"
"Delete entries with a matching IMSI\n"
"Delete entries with a matching SGSN NSEI\n"
"Identification to match\n")
{
const uint16_t nsei = atoi(argv[0]);
enum {MATCH_TLLI = 't', MATCH_IMSI = 'i', MATCH_SGSN = 's'} match;
uint32_t ident = 0;
const char *imsi = NULL;
struct gbproxy_peer *peer = 0;
struct gbproxy_link_info *link_info, *nxt;
struct gbproxy_patch_state *state;
char mi_buf[200];
int found = 0;
match = argv[1][0];
switch (match) {
case MATCH_TLLI: ident = strtoll(argv[2], NULL, 16); break;
case MATCH_IMSI: imsi = argv[2]; break;
case MATCH_SGSN: ident = strtoll(argv[2], NULL, 0); break;
};
peer = gbproxy_peer_by_nsei(g_cfg, nsei);
if (!peer) {
vty_out(vty, "Didn't find peer with NSEI %d%s",
nsei, VTY_NEWLINE);
return CMD_WARNING;
}
state = &peer->patch_state;
llist_for_each_entry_safe(link_info, nxt, &state->logical_links, list) {
switch (match) {
case MATCH_TLLI:
if (link_info->tlli.current != ident)
continue;
break;
case MATCH_SGSN:
if (link_info->sgsn_nsei != ident)
continue;
break;
case MATCH_IMSI:
if (!link_info->imsi)
continue;
mi_buf[0] = '\0';
gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
link_info->imsi,
link_info->imsi_len);
if (strcmp(mi_buf, imsi) != 0)
continue;
break;
}
vty_out(vty, "Deleting link with TLLI %08x%s", link_info->tlli.current,
VTY_NEWLINE);
gbproxy_delete_link_info(peer, link_info);
found += 1;
}
if (!found && argc >= 2) {
vty_out(vty, "Didn't find link entry with %s %s%s",
argv[1], argv[2], VTY_NEWLINE);
}
return CMD_SUCCESS;
}
DEFUN(delete_gb_link, delete_gb_link_cmd,
"delete-gbproxy-link <0-65534> (stale|de-registered)",
GBPROXY_DELETE_LINK_STR
"Delete stale entries\n"
"Delete de-registered entries\n")
{
const uint16_t nsei = atoi(argv[0]);
enum {MATCH_STALE = 's', MATCH_DEREGISTERED = 'd'} match;
struct gbproxy_peer *peer = 0;
struct gbproxy_link_info *link_info, *nxt;
struct gbproxy_patch_state *state;
time_t now;
struct timespec ts = {0,};
int found = 0;
match = argv[1][0];
peer = gbproxy_peer_by_nsei(g_cfg, nsei);
if (!peer) {
vty_out(vty, "Didn't find peer with NSEI %d%s",
nsei, VTY_NEWLINE);
return CMD_WARNING;
}
state = &peer->patch_state;
clock_gettime(CLOCK_MONOTONIC, &ts);
now = ts.tv_sec;
if (match == MATCH_STALE) {
found = gbproxy_remove_stale_link_infos(peer, now);
if (found)
vty_out(vty, "Deleted %d stale logical link%s%s",
found, found == 1 ? "" : "s", VTY_NEWLINE);
} else {
llist_for_each_entry_safe(link_info, nxt,
&state->logical_links, list) {
if (!link_info->is_deregistered)
continue;
gbproxy_delete_link_info(peer, link_info);
found += 1;
}
}
if (found)
vty_out(vty, "Deleted %d %s logical link%s%s",
found, argv[1], found == 1 ? "" : "s", VTY_NEWLINE);
return CMD_SUCCESS;
}
/*
* legacy commands to provide an upgrade path from "broken" releases
* or pre-releases
*/
DEFUN_DEPRECATED(cfg_gbproxy_broken_apn_match,
cfg_gbproxy_broken_apn_match_cmd,
"core-access-point-name none match-imsi .REGEXP",
GBPROXY_CORE_APN_STR GBPROXY_MATCH_IMSI_STR "Remove APN\n"
"Patch MS related information elements on match only\n"
"Route to the secondary SGSN on match only\n"
"Regular expression for the IMSI match\n")
{
const char *filter = argv[0];
const char *err_msg = NULL;
struct gbproxy_match *match;
enum gbproxy_match_id match_id = get_string_value(match_ids, "patching");
/* apply APN none */
set_core_apn(vty, "");
/* do the matching... with copy and paste */
OSMO_ASSERT(match_id >= GBPROX_MATCH_PATCHING &&
match_id < GBPROX_MATCH_LAST);
match = &g_cfg->matches[match_id];
if (gbproxy_set_patch_filter(match, filter, &err_msg) != 0) {
vty_out(vty, "Match expression invalid: %s%s",
err_msg, VTY_NEWLINE);
return CMD_WARNING;
}
g_cfg->acquire_imsi = 1;
return CMD_SUCCESS;
}
#define GBPROXY_TLLI_LIST_STR "Set TLLI list parameters\n"
#define GBPROXY_MAX_LEN_STR "Limit list length\n"
DEFUN_DEPRECATED(cfg_gbproxy_depr_tlli_list_max_len,
cfg_gbproxy_depr_tlli_list_max_len_cmd,
"tlli-list max-length <1-99999>",
GBPROXY_TLLI_LIST_STR GBPROXY_MAX_LEN_STR
"Maximum number of TLLIs in the list\n")
{
g_cfg->tlli_max_len = atoi(argv[0]);
return CMD_SUCCESS;
}
int gbproxy_vty_init(void)
{
install_element_ve(&show_gbproxy_cmd);
install_element_ve(&show_gbproxy_links_cmd);
install_element(ENABLE_NODE, &delete_gb_bvci_cmd);
install_element(ENABLE_NODE, &delete_gb_nsei_cmd);
install_element(ENABLE_NODE, &delete_gb_link_by_id_cmd);
install_element(ENABLE_NODE, &delete_gb_link_cmd);
install_element(CONFIG_NODE, &cfg_gbproxy_cmd);
install_node(&gbproxy_node, config_write_gbproxy);
vty_install_default(GBPROXY_NODE);
install_element(GBPROXY_NODE, &cfg_nsip_sgsn_nsei_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_mcc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_mnc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_match_imsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_core_apn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_secondary_sgsn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_patch_ptmsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_acquire_imsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_age_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_max_len_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_keep_mode_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mcc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_mnc_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_match_imsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_core_apn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_secondary_sgsn_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_patch_ptmsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_no_acquire_imsi_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_age_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_link_list_no_max_len_cmd);
/* broken or deprecated to allow an upgrade path */
install_element(GBPROXY_NODE, &cfg_gbproxy_broken_apn_match_cmd);
install_element(GBPROXY_NODE, &cfg_gbproxy_depr_tlli_list_max_len_cmd);
return 0;
}
int gbproxy_parse_config(const char *config_file, struct gbproxy_config *cfg)
{
int rc;
g_cfg = cfg;
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
return rc;
}
return 0;
}

View File

@ -1,636 +0,0 @@
/* GPRS Gb message parser */
/* (C) 2014 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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/gsm/gsm48.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <openbsc/gprs_gb_parse.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/debug.h>
#include <osmocom/gprs/gprs_bssgp.h>
static int gprs_gb_parse_gmm_attach_req(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
size_t value_len;
parse_ctx->llc_msg_name = "ATTACH_REQ";
/* Skip MS network capability */
if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 ||
value_len < 1 || value_len > 8)
/* invalid */
return 0;
/* Skip Attach type */
/* Skip Ciphering key sequence number */
/* Skip DRX parameter */
osmo_shift_v_fixed(&data, &data_len, 3, NULL);
/* Get Mobile identity */
if (osmo_shift_lv(&data, &data_len, &value, &value_len) <= 0 ||
value_len < 5 || value_len > 8)
/* invalid */
return 0;
if (gprs_is_mi_tmsi(value, value_len)) {
parse_ctx->ptmsi_enc = value + 1;
} else if (gprs_is_mi_imsi(value, value_len)) {
parse_ctx->imsi = value;
parse_ctx->imsi_len = value_len;
}
if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
return 0;
parse_ctx->old_raid_enc = value;
return 1;
}
static int gprs_gb_parse_gmm_attach_ack(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
size_t value_len;
parse_ctx->llc_msg_name = "ATTACH_ACK";
/* Skip Attach result */
/* Skip Force to standby */
/* Skip Periodic RA update timer */
/* Skip Radio priority for SMS */
/* Skip Spare half octet */
osmo_shift_v_fixed(&data, &data_len, 3, NULL);
if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
return 0;
parse_ctx->raid_enc = value;
/* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */
osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL);
/* Skip Negotiated READY timer value (GPRS timer, opt, TV, length 2) */
osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_TIMER_READY, 1, NULL);
/* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */
if (osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI,
&value, &value_len) > 0 &&
gprs_is_mi_tmsi(value, value_len))
parse_ctx->new_ptmsi_enc = value + 1;
return 1;
}
static int gprs_gb_parse_gmm_attach_rej(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
parse_ctx->llc_msg_name = "ATTACH_REJ";
/* GMM cause */
if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0)
return 0;
parse_ctx->invalidate_tlli = 1;
return 1;
}
static int gprs_gb_parse_gmm_detach_req(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
size_t value_len;
int detach_type;
int power_off;
parse_ctx->llc_msg_name = "DETACH_REQ";
/* Skip spare half octet */
/* Get Detach type */
if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0)
/* invalid */
return 0;
detach_type = *value & 0x07;
power_off = *value & 0x08 ? 1 : 0;
if (parse_ctx->to_bss) {
/* Network originated */
if (detach_type == GPRS_DET_T_MT_REATT_REQ)
parse_ctx->await_reattach = 1;
} else {
/* Mobile originated */
if (power_off)
parse_ctx->invalidate_tlli = 1;
/* Get P-TMSI (Mobile identity), see GSM 24.008, 9.4.5.2 */
if (osmo_match_shift_tlv(&data, &data_len,
GSM48_IE_GMM_ALLOC_PTMSI, &value, &value_len) > 0)
{
if (gprs_is_mi_tmsi(value, value_len))
parse_ctx->ptmsi_enc = value + 1;
}
}
return 1;
}
static int gprs_gb_parse_gmm_ra_upd_req(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
parse_ctx->llc_msg_name = "RA_UPD_REQ";
/* Skip Update type */
/* Skip GPRS ciphering key sequence number */
osmo_shift_v_fixed(&data, &data_len, 1, NULL);
if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
return 0;
parse_ctx->old_raid_enc = value;
return 1;
}
static int gprs_gb_parse_gmm_ra_upd_rej(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
uint8_t cause;
int force_standby;
parse_ctx->llc_msg_name = "RA_UPD_REJ";
/* GMM cause */
if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0)
return 0;
cause = value[0];
/* Force to standby, 1/2 */
/* spare bits, 1/2 */
if (osmo_shift_v_fixed(&data, &data_len, 1, &value) <= 0)
return 0;
force_standby = (value[0] & 0x07) == 0x01;
if (cause == GMM_CAUSE_IMPL_DETACHED && !force_standby)
parse_ctx->await_reattach = 1;
parse_ctx->invalidate_tlli = 1;
return 1;
}
static int gprs_gb_parse_gmm_ra_upd_ack(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
size_t value_len;
parse_ctx->llc_msg_name = "RA_UPD_ACK";
/* Skip Force to standby */
/* Skip Update result */
/* Skip Periodic RA update timer */
osmo_shift_v_fixed(&data, &data_len, 2, NULL);
if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
return 0;
parse_ctx->raid_enc = value;
/* Skip P-TMSI signature (P-TMSI signature, opt, TV, length 4) */
osmo_match_shift_tv_fixed(&data, &data_len, GSM48_IE_GMM_PTMSI_SIG, 3, NULL);
/* Allocated P-TMSI (Mobile identity, opt, TLV, length 7) */
if (osmo_match_shift_tlv(&data, &data_len, GSM48_IE_GMM_ALLOC_PTMSI,
&value, &value_len) > 0 &&
gprs_is_mi_tmsi(value, value_len))
parse_ctx->new_ptmsi_enc = value + 1;
return 1;
}
static int gprs_gb_parse_gmm_ptmsi_reall_cmd(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
size_t value_len;
parse_ctx->llc_msg_name = "PTMSI_REALL_CMD";
LOGP(DLLC, LOGL_NOTICE,
"Got P-TMSI Reallocation Command which is not covered by unit tests yet.\n");
/* Allocated P-TMSI */
if (osmo_shift_lv(&data, &data_len, &value, &value_len) > 0 &&
gprs_is_mi_tmsi(value, value_len))
parse_ctx->new_ptmsi_enc = value + 1;
if (osmo_shift_v_fixed(&data, &data_len, 6, &value) <= 0)
return 0;
parse_ctx->raid_enc = value;
return 1;
}
static int gprs_gb_parse_gmm_id_resp(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
uint8_t *value;
size_t value_len;
parse_ctx->llc_msg_name = "ID_RESP";
/* Mobile identity, Mobile identity 10.5.1.4, M LV 2-10 */
if (osmo_shift_lv(&data, &data_len, &value, &value_len) <= 0 ||
value_len < 1 || value_len > 9)
/* invalid */
return 0;
if (gprs_is_mi_tmsi(value, value_len)) {
parse_ctx->ptmsi_enc = value + 1;
} else if (gprs_is_mi_imsi(value, value_len)) {
parse_ctx->imsi = value;
parse_ctx->imsi_len = value_len;
}
return 1;
}
static int gprs_gb_parse_gsm_act_pdp_req(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
ssize_t old_len;
uint8_t *value;
size_t value_len;
parse_ctx->llc_msg_name = "ACT_PDP_REQ";
/* Skip Requested NSAPI */
/* Skip Requested LLC SAPI */
osmo_shift_v_fixed(&data, &data_len, 2, NULL);
/* Skip Requested QoS (support 04.08 and 24.008) */
if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 ||
value_len < 4 || value_len > 14)
/* invalid */
return 0;
/* Skip Requested PDP address */
if (osmo_shift_lv(&data, &data_len, NULL, &value_len) <= 0 ||
value_len < 2 || value_len > 18)
/* invalid */
return 0;
/* Access point name */
old_len = osmo_match_shift_tlv(&data, &data_len,
GSM48_IE_GSM_APN, &value, &value_len);
if (old_len > 0 && value_len >=1 && value_len <= 100) {
parse_ctx->apn_ie = data - old_len;
parse_ctx->apn_ie_len = old_len;
}
return 1;
}
int gprs_gb_parse_dtap(uint8_t *data, size_t data_len,
struct gprs_gb_parse_context *parse_ctx)
{
struct gsm48_hdr *g48h;
uint8_t pdisc;
uint8_t msg_type;
if (osmo_shift_v_fixed(&data, &data_len, sizeof(*g48h), (uint8_t **)&g48h) <= 0)
return 0;
parse_ctx->g48_hdr = g48h;
pdisc = gsm48_hdr_pdisc(g48h);
if (pdisc != GSM48_PDISC_MM_GPRS && pdisc != GSM48_PDISC_SM_GPRS)
return 1;
msg_type = gsm48_hdr_msg_type(g48h);
switch (msg_type) {
case GSM48_MT_GMM_ATTACH_REQ:
return gprs_gb_parse_gmm_attach_req(data, data_len, parse_ctx);
case GSM48_MT_GMM_ATTACH_REJ:
return gprs_gb_parse_gmm_attach_rej(data, data_len, parse_ctx);
case GSM48_MT_GMM_ATTACH_ACK:
return gprs_gb_parse_gmm_attach_ack(data, data_len, parse_ctx);
case GSM48_MT_GMM_RA_UPD_REQ:
return gprs_gb_parse_gmm_ra_upd_req(data, data_len, parse_ctx);
case GSM48_MT_GMM_RA_UPD_REJ:
return gprs_gb_parse_gmm_ra_upd_rej(data, data_len, parse_ctx);
case GSM48_MT_GMM_RA_UPD_ACK:
return gprs_gb_parse_gmm_ra_upd_ack(data, data_len, parse_ctx);
case GSM48_MT_GMM_PTMSI_REALL_CMD:
return gprs_gb_parse_gmm_ptmsi_reall_cmd(data, data_len, parse_ctx);
case GSM48_MT_GSM_ACT_PDP_REQ:
return gprs_gb_parse_gsm_act_pdp_req(data, data_len, parse_ctx);
case GSM48_MT_GMM_ID_RESP:
return gprs_gb_parse_gmm_id_resp(data, data_len, parse_ctx);
case GSM48_MT_GMM_DETACH_REQ:
return gprs_gb_parse_gmm_detach_req(data, data_len, parse_ctx);
case GSM48_MT_GMM_DETACH_ACK:
parse_ctx->llc_msg_name = "DETACH_ACK";
parse_ctx->invalidate_tlli = 1;
break;
default:
LOGP(DLLC, LOGL_NOTICE,
"Unhandled GSM 04.08 message type %s for protocol discriminator %s.\n",
get_value_string(gprs_msgt_gmm_names, msg_type), get_value_string(gsm48_pdisc_names, pdisc));
break;
};
return 1;
}
int gprs_gb_parse_llc(uint8_t *llc, size_t llc_len,
struct gprs_gb_parse_context *parse_ctx)
{
struct gprs_llc_hdr_parsed *ghp = &parse_ctx->llc_hdr_parsed;
int rc;
int fcs;
/* parse LLC */
rc = gprs_llc_hdr_parse(ghp, llc, llc_len);
gprs_llc_hdr_dump(ghp, NULL);
if (rc != 0) {
LOGP(DLLC, LOGL_NOTICE, "Error during LLC header parsing\n");
return 0;
}
fcs = gprs_llc_fcs(llc, ghp->crc_length);
LOGP(DLLC, LOGL_DEBUG, "Got LLC message, CRC: %06x (computed %06x)\n",
ghp->fcs, fcs);
if (!ghp->data)
return 0;
if (ghp->sapi != GPRS_SAPI_GMM)
return 1;
if (ghp->cmd != GPRS_LLC_UI)
return 1;
if (ghp->is_encrypted) {
parse_ctx->need_decryption = 1;
return 0;
}
return gprs_gb_parse_dtap(ghp->data, ghp->data_len, parse_ctx);
}
int gprs_gb_parse_bssgp(uint8_t *bssgp, size_t bssgp_len,
struct gprs_gb_parse_context *parse_ctx)
{
struct bssgp_normal_hdr *bgph;
struct bssgp_ud_hdr *budh = NULL;
struct tlv_parsed *tp = &parse_ctx->bssgp_tp;
uint8_t pdu_type;
uint8_t *data;
size_t data_len;
int rc;
if (bssgp_len < sizeof(struct bssgp_normal_hdr))
return 0;
bgph = (struct bssgp_normal_hdr *)bssgp;
pdu_type = bgph->pdu_type;
if (pdu_type == BSSGP_PDUT_UL_UNITDATA ||
pdu_type == BSSGP_PDUT_DL_UNITDATA) {
if (bssgp_len < sizeof(struct bssgp_ud_hdr))
return 0;
budh = (struct bssgp_ud_hdr *)bssgp;
bgph = NULL;
data = budh->data;
data_len = bssgp_len - sizeof(*budh);
} else {
data = bgph->data;
data_len = bssgp_len - sizeof(*bgph);
}
parse_ctx->pdu_type = pdu_type;
parse_ctx->bud_hdr = budh;
parse_ctx->bgp_hdr = bgph;
parse_ctx->bssgp_data = data;
parse_ctx->bssgp_data_len = data_len;
if (bssgp_tlv_parse(tp, data, data_len) < 0)
return 0;
if (budh)
parse_ctx->tlli_enc = (uint8_t *)&budh->tlli;
if (TLVP_PRESENT(tp, BSSGP_IE_ROUTEING_AREA))
parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_ROUTEING_AREA);
if (TLVP_PRESENT(tp, BSSGP_IE_CELL_ID))
parse_ctx->bssgp_raid_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_CELL_ID);
if (TLVP_PRESENT(tp, BSSGP_IE_IMSI)) {
parse_ctx->imsi = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_IMSI);
parse_ctx->imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI);
}
if (TLVP_PRESENT(tp, BSSGP_IE_TLLI)) {
if (parse_ctx->tlli_enc)
/* This is TLLI old, don't confuse it with TLLI current */
parse_ctx->old_tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI);
else
parse_ctx->tlli_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TLLI);
}
if (TLVP_PRESENT(tp, BSSGP_IE_TMSI) && pdu_type == BSSGP_PDUT_PAGING_PS)
parse_ctx->bssgp_ptmsi_enc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_TMSI);
if (TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU)) {
uint8_t *llc = (uint8_t *)TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
size_t llc_len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU);
rc = gprs_gb_parse_llc(llc, llc_len, parse_ctx);
if (!rc)
return 0;
parse_ctx->llc = llc;
parse_ctx->llc_len = llc_len;
}
if (parse_ctx->tlli_enc) {
uint32_t tmp_tlli;
memcpy(&tmp_tlli, parse_ctx->tlli_enc, sizeof(tmp_tlli));
parse_ctx->tlli = ntohl(tmp_tlli);
}
if (parse_ctx->bssgp_raid_enc && parse_ctx->old_raid_enc &&
memcmp(parse_ctx->bssgp_raid_enc, parse_ctx->old_raid_enc, 6) != 0)
parse_ctx->old_raid_is_foreign = 1;
return 1;
}
void gprs_gb_log_parse_context(int log_level,
struct gprs_gb_parse_context *parse_ctx,
const char *default_msg_name)
{
const char *msg_name;
const char *sep = "";
if (!parse_ctx->tlli_enc &&
!parse_ctx->ptmsi_enc &&
!parse_ctx->new_ptmsi_enc &&
!parse_ctx->bssgp_ptmsi_enc &&
!parse_ctx->imsi)
return;
msg_name = gprs_gb_message_name(parse_ctx, default_msg_name);
if (parse_ctx->llc_msg_name)
msg_name = parse_ctx->llc_msg_name;
LOGP(DGPRS, log_level, "%s: Got", msg_name);
if (parse_ctx->tlli_enc) {
LOGPC(DGPRS, log_level, "%s TLLI %08x", sep, parse_ctx->tlli);
sep = ",";
}
if (parse_ctx->old_tlli_enc) {
LOGPC(DGPRS, log_level, "%s old TLLI %02x%02x%02x%02x", sep,
parse_ctx->old_tlli_enc[0],
parse_ctx->old_tlli_enc[1],
parse_ctx->old_tlli_enc[2],
parse_ctx->old_tlli_enc[3]);
sep = ",";
}
if (parse_ctx->bssgp_raid_enc) {
struct gprs_ra_id raid;
gsm48_parse_ra(&raid, parse_ctx->bssgp_raid_enc);
LOGPC(DGPRS, log_level, "%s BSSGP RAID %u-%u-%u-%u", sep,
raid.mcc, raid.mnc, raid.lac, raid.rac);
sep = ",";
}
if (parse_ctx->raid_enc) {
struct gprs_ra_id raid;
gsm48_parse_ra(&raid, parse_ctx->raid_enc);
LOGPC(DGPRS, log_level, "%s RAID %u-%u-%u-%u", sep,
raid.mcc, raid.mnc, raid.lac, raid.rac);
sep = ",";
}
if (parse_ctx->old_raid_enc) {
struct gprs_ra_id raid;
gsm48_parse_ra(&raid, parse_ctx->old_raid_enc);
LOGPC(DGPRS, log_level, "%s old RAID %u-%u-%u-%u", sep,
raid.mcc, raid.mnc, raid.lac, raid.rac);
sep = ",";
}
if (parse_ctx->bssgp_ptmsi_enc) {
uint32_t ptmsi = GSM_RESERVED_TMSI;
gprs_parse_tmsi(parse_ctx->bssgp_ptmsi_enc, &ptmsi);
LOGPC(DGPRS, log_level, "%s BSSGP PTMSI %08x", sep, ptmsi);
sep = ",";
}
if (parse_ctx->ptmsi_enc) {
uint32_t ptmsi = GSM_RESERVED_TMSI;
gprs_parse_tmsi(parse_ctx->ptmsi_enc, &ptmsi);
LOGPC(DGPRS, log_level, "%s PTMSI %08x", sep, ptmsi);
sep = ",";
}
if (parse_ctx->new_ptmsi_enc) {
uint32_t new_ptmsi = GSM_RESERVED_TMSI;
gprs_parse_tmsi(parse_ctx->new_ptmsi_enc, &new_ptmsi);
LOGPC(DGPRS, log_level, "%s new PTMSI %08x", sep, new_ptmsi);
sep = ",";
}
if (parse_ctx->imsi) {
char mi_buf[200];
mi_buf[0] = '\0';
gsm48_mi_to_string(mi_buf, sizeof(mi_buf),
parse_ctx->imsi, parse_ctx->imsi_len);
LOGPC(DGPRS, log_level, "%s IMSI %s",
sep, mi_buf);
sep = ",";
}
if (parse_ctx->invalidate_tlli) {
LOGPC(DGPRS, log_level, "%s invalidate", sep);
sep = ",";
}
if (parse_ctx->await_reattach) {
LOGPC(DGPRS, log_level, "%s re-attach", sep);
sep = ",";
}
LOGPC(DGPRS, log_level, "\n");
}
const char *gprs_gb_message_name(const struct gprs_gb_parse_context *parse_ctx,
const char *default_msg_name)
{
if (parse_ctx->llc_msg_name)
return parse_ctx->llc_msg_name;
if (parse_ctx->g48_hdr)
return "GMM";
if (parse_ctx->llc)
return "LLC";
if (parse_ctx->bud_hdr)
return "BSSGP-UNITDATA";
if (parse_ctx->bgp_hdr)
return "BSSGP";
return "unknown";
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,251 +0,0 @@
/* GPRS LLC protocol implementation as per 3GPP TS 04.64 */
/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 <errno.h>
#include <stdint.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <openbsc/gsm_data.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/crc24.h>
static const struct value_string llc_cmd_strs[] = {
{ GPRS_LLC_NULL, "NULL" },
{ GPRS_LLC_RR, "RR" },
{ GPRS_LLC_ACK, "ACK" },
{ GPRS_LLC_RNR, "RNR" },
{ GPRS_LLC_SACK, "SACK" },
{ GPRS_LLC_DM, "DM" },
{ GPRS_LLC_DISC, "DISC" },
{ GPRS_LLC_UA, "UA" },
{ GPRS_LLC_SABM, "SABM" },
{ GPRS_LLC_FRMR, "FRMR" },
{ GPRS_LLC_XID, "XID" },
{ GPRS_LLC_UI, "UI" },
{ 0, NULL }
};
#define LLC_ALLOC_SIZE 16384
#define UI_HDR_LEN 3
#define N202 4
#define CRC24_LENGTH 3
int gprs_llc_fcs(uint8_t *data, unsigned int len)
{
uint32_t fcs_calc;
fcs_calc = crc24_calc(INIT_CRC24, data, len);
fcs_calc = ~fcs_calc;
fcs_calc &= 0xffffff;
return fcs_calc;
}
void gprs_llc_hdr_dump(struct gprs_llc_hdr_parsed *gph, struct gprs_llc_lle *lle)
{
const char *gea;
uint32_t iov_ui = 0;
if (lle) {
gea = get_value_string(gprs_cipher_names, lle->llme->algo);
iov_ui = lle->llme->iov_ui;
} else
gea = "GEA?";
DEBUGP(DLLC, "LLC SAPI=%u %c %c %c %s IOV-UI=0x%06x FCS=0x%06x ",
gph->sapi, gph->is_cmd ? 'C' : 'R', gph->ack_req ? 'A' : ' ',
gph->is_encrypted ? 'E' : 'U',
gea, iov_ui, gph->fcs);
if (gph->cmd)
DEBUGPC(DLLC, "CMD=%s ", get_value_string(llc_cmd_strs, gph->cmd));
if (gph->data)
DEBUGPC(DLLC, "DATA ");
DEBUGPC(DLLC, "\n");
}
/* parse a GPRS LLC header, also check for invalid frames */
int gprs_llc_hdr_parse(struct gprs_llc_hdr_parsed *ghp,
uint8_t *llc_hdr, int len)
{
uint8_t *ctrl = llc_hdr+1;
if (len <= CRC24_LENGTH)
return -EIO;
ghp->crc_length = len - CRC24_LENGTH;
ghp->ack_req = 0;
/* Section 5.5: FCS */
ghp->fcs = *(llc_hdr + len - 3);
ghp->fcs |= *(llc_hdr + len - 2) << 8;
ghp->fcs |= *(llc_hdr + len - 1) << 16;
/* Section 6.2.1: invalid PD field */
if (llc_hdr[0] & 0x80)
return -EIO;
/* This only works for the MS->SGSN direction */
if (llc_hdr[0] & 0x40)
ghp->is_cmd = 0;
else
ghp->is_cmd = 1;
ghp->sapi = llc_hdr[0] & 0xf;
/* Section 6.2.3: check for reserved SAPI */
switch (ghp->sapi) {
case 0:
case 4:
case 6:
case 0xa:
case 0xc:
case 0xd:
case 0xf:
return -EINVAL;
}
if ((ctrl[0] & 0x80) == 0) {
/* I (Information transfer + Supervisory) format */
uint8_t k;
ghp->data = ctrl + 3;
if (ctrl[0] & 0x40)
ghp->ack_req = 1;
ghp->seq_tx = (ctrl[0] & 0x1f) << 4;
ghp->seq_tx |= (ctrl[1] >> 4);
ghp->seq_rx = (ctrl[1] & 0x7) << 6;
ghp->seq_rx |= (ctrl[2] >> 2);
switch (ctrl[2] & 0x03) {
case 0:
ghp->cmd = GPRS_LLC_RR;
break;
case 1:
ghp->cmd = GPRS_LLC_ACK;
break;
case 2:
ghp->cmd = GPRS_LLC_RNR;
break;
case 3:
ghp->cmd = GPRS_LLC_SACK;
k = ctrl[3] & 0x1f;
ghp->data += 1 + k;
break;
}
ghp->data_len = (llc_hdr + len - 3) - ghp->data;
} else if ((ctrl[0] & 0xc0) == 0x80) {
/* S (Supervisory) format */
ghp->data = NULL;
ghp->data_len = 0;
if (ctrl[0] & 0x20)
ghp->ack_req = 1;
ghp->seq_rx = (ctrl[0] & 0x7) << 6;
ghp->seq_rx |= (ctrl[1] >> 2);
switch (ctrl[1] & 0x03) {
case 0:
ghp->cmd = GPRS_LLC_RR;
break;
case 1:
ghp->cmd = GPRS_LLC_ACK;
break;
case 2:
ghp->cmd = GPRS_LLC_RNR;
break;
case 3:
ghp->cmd = GPRS_LLC_SACK;
break;
}
} else if ((ctrl[0] & 0xe0) == 0xc0) {
/* UI (Unconfirmed Inforamtion) format */
ghp->cmd = GPRS_LLC_UI;
ghp->data = ctrl + 2;
ghp->data_len = (llc_hdr + len - 3) - ghp->data;
ghp->seq_tx = (ctrl[0] & 0x7) << 6;
ghp->seq_tx |= (ctrl[1] >> 2);
if (ctrl[1] & 0x02) {
ghp->is_encrypted = 1;
/* FIXME: encryption */
}
if (ctrl[1] & 0x01) {
/* FCS over hdr + all inf fields */
} else {
/* FCS over hdr + N202 octets (4) */
if (ghp->crc_length > UI_HDR_LEN + N202)
ghp->crc_length = UI_HDR_LEN + N202;
}
} else {
/* U (Unnumbered) format: 1 1 1 P/F M4 M3 M2 M1 */
ghp->data = NULL;
ghp->data_len = 0;
switch (ctrl[0] & 0xf) {
case GPRS_LLC_U_NULL_CMD:
ghp->cmd = GPRS_LLC_NULL;
break;
case GPRS_LLC_U_DM_RESP:
ghp->cmd = GPRS_LLC_DM;
break;
case GPRS_LLC_U_DISC_CMD:
ghp->cmd = GPRS_LLC_DISC;
break;
case GPRS_LLC_U_UA_RESP:
ghp->cmd = GPRS_LLC_UA;
break;
case GPRS_LLC_U_SABM_CMD:
ghp->cmd = GPRS_LLC_SABM;
break;
case GPRS_LLC_U_FRMR_RESP:
ghp->cmd = GPRS_LLC_FRMR;
break;
case GPRS_LLC_U_XID:
ghp->cmd = GPRS_LLC_XID;
ghp->data = ctrl + 1;
ghp->data_len = (llc_hdr + len - 3) - ghp->data;
break;
default:
return -EIO;
}
}
/* FIXME: parse sack frame */
if (ghp->cmd == GPRS_LLC_SACK) {
LOGP(DLLC, LOGL_NOTICE, "Unsupported SACK frame\n");
return -EIO;
}
return 0;
}

View File

@ -1,116 +0,0 @@
/* VTY interface for our GPRS LLC implementation */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <time.h>
#include <arpa/inet.h>
#include <openbsc/gsm_data.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <openbsc/debug.h>
#include <openbsc/signal.h>
#include <openbsc/gprs_llc.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
struct value_string gprs_llc_state_strs[] = {
{ GPRS_LLES_UNASSIGNED, "TLLI Unassigned" },
{ GPRS_LLES_ASSIGNED_ADM, "TLLI Assigned" },
{ GPRS_LLES_LOCAL_EST, "Local Establishment" },
{ GPRS_LLES_REMOTE_EST, "Remote Establishment" },
{ GPRS_LLES_ABM, "Asynchronous Balanced Mode" },
{ GPRS_LLES_LOCAL_REL, "Local Release" },
{ GPRS_LLES_TIMER_REC, "Timer Recovery" },
{ 0, NULL }
};
static void vty_dump_lle(struct vty *vty, struct gprs_llc_lle *lle)
{
struct gprs_llc_params *par = &lle->params;
vty_out(vty, " SAPI %2u State %s VUsend=%u, VUrecv=%u", lle->sapi,
get_value_string(gprs_llc_state_strs, lle->state),
lle->vu_send, lle->vu_recv);
vty_out(vty, " Vsent=%u Vack=%u Vrecv=%u, RetransCtr=%u%s",
lle->v_sent, lle->v_ack, lle->v_recv,
lle->retrans_ctr, VTY_NEWLINE);
vty_out(vty, " T200=%u, N200=%u, N201-U=%u, N201-I=%u, mD=%u, "
"mU=%u, kD=%u, kU=%u%s", par->t200_201, par->n200,
par->n201_u, par->n201_i, par->mD, par->mU, par->kD,
par->kU, VTY_NEWLINE);
}
static uint8_t valid_sapis[] = { 1, 2, 3, 5, 7, 8, 9, 11 };
static void vty_dump_llme(struct vty *vty, struct gprs_llc_llme *llme)
{
unsigned int i;
struct timespec now_tp = {0};
clock_gettime(CLOCK_MONOTONIC, &now_tp);
vty_out(vty, "TLLI %08x (Old TLLI %08x) BVCI=%u NSEI=%u %s: "
"IOV-UI=0x%06x CKSN=%d Age=%d: State %s%s", llme->tlli,
llme->old_tlli, llme->bvci, llme->nsei,
get_value_string(gprs_cipher_names, llme->algo), llme->iov_ui,
llme->cksn, llme->age_timestamp == GPRS_LLME_RESET_AGE ? 0 :
(int)(now_tp.tv_sec - (time_t)llme->age_timestamp),
get_value_string(gprs_llc_state_strs, llme->state), VTY_NEWLINE);
for (i = 0; i < ARRAY_SIZE(valid_sapis); i++) {
struct gprs_llc_lle *lle;
uint8_t sapi = valid_sapis[i];
if (sapi >= ARRAY_SIZE(llme->lle))
continue;
lle = &llme->lle[sapi];
vty_dump_lle(vty, lle);
}
}
DEFUN(show_llc, show_llc_cmd,
"show llc",
SHOW_STR "Display information about the LLC protocol")
{
struct gprs_llc_llme *llme;
vty_out(vty, "State of LLC Entities%s", VTY_NEWLINE);
llist_for_each_entry(llme, &gprs_llc_llmes, list) {
vty_dump_llme(vty, llme);
}
return CMD_SUCCESS;
}
int gprs_llc_vty_init(void)
{
install_element_ve(&show_llc_cmd);
return 0;
}

View File

@ -1,281 +0,0 @@
/* 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>
const struct value_string gprs_llc_xid_type_names[] = {
{ GPRS_LLC_XID_T_VERSION, "VERSION"},
{ GPRS_LLC_XID_T_IOV_UI, "IOV_UI"},
{ GPRS_LLC_XID_T_IOV_I, "IOV_I"},
{ GPRS_LLC_XID_T_T200, "T200"},
{ GPRS_LLC_XID_T_N200, "N200"},
{ GPRS_LLC_XID_T_N201_U, "N201_"},
{ GPRS_LLC_XID_T_N201_I, "N201_I"},
{ GPRS_LLC_XID_T_mD, "mD"},
{ GPRS_LLC_XID_T_mU, "mU"},
{ GPRS_LLC_XID_T_kD, "kD"},
{ GPRS_LLC_XID_T_kU, "kU"},
{ GPRS_LLC_XID_T_L3_PAR, "L3_PAR"},
{ GPRS_LLC_XID_T_RESET, "RESET"},
{ 0, NULL },
};
/* 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 %s, data_len=%d, data=%s\n",
get_value_string(gprs_llc_xid_type_names,
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

@ -1,933 +0,0 @@
/* GPRS SGSN functionality */
/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 <stdint.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/stats.h>
#include <osmocom/core/backtrace.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/apn.h>
#include <openbsc/gprs_subscriber.h>
#include <openbsc/debug.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/signal.h>
#include "openbsc/gprs_llc.h"
#include <pdp.h>
#include <time.h>
#include <openssl/rand.h>
#include "../../bscconfig.h"
#if BUILD_IU
#include <osmocom/ranap/iu_client.h>
#endif
#define GPRS_LLME_CHECK_TICK 30
extern struct sgsn_instance *sgsn;
LLIST_HEAD(sgsn_mm_ctxts);
LLIST_HEAD(sgsn_ggsn_ctxts);
LLIST_HEAD(sgsn_apn_ctxts);
LLIST_HEAD(sgsn_pdp_ctxts);
static const struct rate_ctr_desc mmctx_ctr_description[] = {
{ "sign.packets.in", "Signalling Messages ( In)" },
{ "sign.packets.out", "Signalling Messages (Out)" },
{ "udata.packets.in", "User Data Messages ( In)" },
{ "udata.packets.out", "User Data Messages (Out)" },
{ "udata.bytes.in", "User Data Bytes ( In)" },
{ "udata.bytes.out", "User Data Bytes (Out)" },
{ "pdp_ctx_act", "PDP Context Activations " },
{ "suspend", "SUSPEND Count " },
{ "paging.ps", "Paging Packet Switched " },
{ "paging.cs", "Paging Circuit Switched " },
{ "ra_update", "Routing Area Update " },
};
static const struct rate_ctr_group_desc mmctx_ctrg_desc = {
.group_name_prefix = "sgsn.mmctx",
.group_description = "SGSN MM Context Statistics",
.num_ctr = ARRAY_SIZE(mmctx_ctr_description),
.ctr_desc = mmctx_ctr_description,
.class_id = OSMO_STATS_CLASS_SUBSCRIBER,
};
static const struct rate_ctr_desc pdpctx_ctr_description[] = {
{ "udata.packets.in", "User Data Messages ( In)" },
{ "udata.packets.out", "User Data Messages (Out)" },
{ "udata.bytes.in", "User Data Bytes ( In)" },
{ "udata.bytes.out", "User Data Bytes (Out)" },
};
static const struct rate_ctr_group_desc pdpctx_ctrg_desc = {
.group_name_prefix = "sgsn.pdpctx",
.group_description = "SGSN PDP Context Statistics",
.num_ctr = ARRAY_SIZE(pdpctx_ctr_description),
.ctr_desc = pdpctx_ctr_description,
.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);
OSMO_ASSERT(sgsn->rate_ctrs);
}
/* 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)
{
struct sgsn_mm_ctx *ctx;
llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
if ((tlli == ctx->gb.tlli || tlli == ctx->gb.tlli_new) &&
gprs_ra_id_equals(raid, &ctx->ra))
return ctx;
}
return NULL;
}
struct sgsn_mm_ctx *sgsn_mm_ctx_by_tlli_and_ptmsi(uint32_t tlli,
const struct gprs_ra_id *raid)
{
struct sgsn_mm_ctx *ctx;
int tlli_type;
/* TODO: Also check the P_TMSI signature to be safe. That signature
* should be different (at least with a sufficiently high probability)
* after SGSN restarts and for multiple SGSN instances.
*/
tlli_type = gprs_tlli_type(tlli);
if (tlli_type != TLLI_FOREIGN && tlli_type != TLLI_LOCAL)
return NULL;
llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
if ((gprs_tmsi2tlli(ctx->p_tmsi, tlli_type) == tlli ||
gprs_tmsi2tlli(ctx->p_tmsi_old, tlli_type) == tlli) &&
gprs_ra_id_equals(raid, &ctx->ra))
return ctx;
}
return NULL;
}
struct sgsn_mm_ctx *sgsn_mm_ctx_by_ptmsi(uint32_t p_tmsi)
{
struct sgsn_mm_ctx *ctx;
llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
if (p_tmsi == ctx->p_tmsi ||
(ctx->p_tmsi_old && ctx->p_tmsi_old == p_tmsi))
return ctx;
}
return NULL;
}
struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi)
{
struct sgsn_mm_ctx *ctx;
llist_for_each_entry(ctx, &sgsn_mm_ctxts, list) {
if (!strcmp(imsi, ctx->imsi))
return ctx;
}
return NULL;
}
/* Allocate a new SGSN MM context for GERAN_Gb */
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_gb(uint32_t tlli,
const struct gprs_ra_id *raid)
{
struct sgsn_mm_ctx *ctx;
ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx);
if (!ctx)
return NULL;
memcpy(&ctx->ra, raid, sizeof(ctx->ra));
ctx->ran_type = MM_CTX_T_GERAN_Gb;
ctx->gb.tlli = tlli;
ctx->gmm_state = GMM_DEREGISTERED;
ctx->pmm_state = MM_IDLE;
ctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
ctx->ciph_algo = sgsn->cfg.cipher;
LOGMMCTXP(LOGL_DEBUG, ctx, "Allocated with %s cipher.\n",
get_value_string(gprs_cipher_names, ctx->ciph_algo));
ctx->ctrg = rate_ctr_group_alloc(ctx, &mmctx_ctrg_desc, tlli);
if (!ctx->ctrg) {
LOGMMCTXP(LOGL_ERROR, ctx, "Cannot allocate counter group\n");
talloc_free(ctx);
return NULL;
}
INIT_LLIST_HEAD(&ctx->pdp_list);
llist_add(&ctx->list, &sgsn_mm_ctxts);
return ctx;
}
/* Allocate a new SGSN MM context */
struct sgsn_mm_ctx *sgsn_mm_ctx_alloc_iu(void *uectx)
{
#if BUILD_IU
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.ue_ctx->rab_assign_addr_enc = sgsn->cfg.iu.rab_assign_addr_enc;
ctx->iu.new_key = 1;
ctx->gmm_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);
if (!ctx->ctrg) {
LOGMMCTXP(LOGL_ERROR, ctx, "Cannot allocate counter group\n");
talloc_free(ctx);
return NULL;
}
/* 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;
#else
return NULL;
#endif
}
/* 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)
{
struct sgsn_pdp_ctx *pdp, *pdp2;
/* Unlink from global list of MM contexts */
llist_del(&mm->list);
/* Free all PDP contexts */
llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list)
sgsn_pdp_ctx_free(pdp);
rate_ctr_group_free(mm->ctrg);
talloc_free(mm);
}
void sgsn_mm_ctx_cleanup_free(struct sgsn_mm_ctx *mm)
{
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,
"Cleaning mmctx with on-going query.\n");
mm->ggsn_lookup->mmctx = NULL;
mm->ggsn_lookup = NULL;
}
/* delete all existing PDP contexts for this MS */
llist_for_each_entry_safe(pdp, pdp2, &mm->pdp_list, list) {
LOGMMCTXP(LOGL_NOTICE, mm,
"Dropping PDP context for NSAPI=%u\n", pdp->nsapi);
sgsn_pdp_ctx_terminate(pdp);
}
if (osmo_timer_pending(&mm->timer)) {
LOGMMCTXP(LOGL_INFO, mm, "Cancelling MM timer %u\n", mm->T);
osmo_timer_del(&mm->timer);
}
memset(&sig_data, 0, sizeof(sig_data));
sig_data.mm = mm;
osmo_signal_dispatch(SS_SGSN, S_SGSN_MM_FREE, &sig_data);
/* Detach from subscriber which is possibly freed then */
if (mm->subscr) {
struct gprs_subscr *subscr = gprs_subscr_get(mm->subscr);
gprs_subscr_cleanup(subscr);
gprs_subscr_put(subscr);
}
sgsn_mm_ctx_free(mm);
mm = NULL;
if (llme) {
/* TLLI unassignment, must be called after sgsn_mm_ctx_free */
gprs_llgmm_assign(llme, tlli, 0xffffffff);
}
}
/* look up PDP context by MM context and NSAPI */
struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_nsapi(const struct sgsn_mm_ctx *mm,
uint8_t nsapi)
{
struct sgsn_pdp_ctx *pdp;
llist_for_each_entry(pdp, &mm->pdp_list, list) {
if (pdp->nsapi == nsapi)
return pdp;
}
return NULL;
}
/* look up PDP context by MM context and transaction ID */
struct sgsn_pdp_ctx *sgsn_pdp_ctx_by_tid(const struct sgsn_mm_ctx *mm,
uint8_t tid)
{
struct sgsn_pdp_ctx *pdp;
llist_for_each_entry(pdp, &mm->pdp_list, list) {
if (pdp->ti == tid)
return pdp;
}
return NULL;
}
/* you don't want to use this directly, call sgsn_create_pdp_ctx() */
struct sgsn_pdp_ctx *sgsn_pdp_ctx_alloc(struct sgsn_mm_ctx *mm,
uint8_t nsapi)
{
struct sgsn_pdp_ctx *pdp;
pdp = sgsn_pdp_ctx_by_nsapi(mm, nsapi);
if (pdp)
return NULL;
pdp = talloc_zero(tall_bsc_ctx, struct sgsn_pdp_ctx);
if (!pdp)
return NULL;
pdp->mm = mm;
pdp->nsapi = nsapi;
pdp->ctrg = rate_ctr_group_alloc(pdp, &pdpctx_ctrg_desc, nsapi);
if (!pdp->ctrg) {
LOGPDPCTXP(LOGL_ERROR, pdp, "Error allocation counter group\n");
talloc_free(pdp);
return NULL;
}
llist_add(&pdp->list, &mm->pdp_list);
llist_add(&pdp->g_list, &sgsn_pdp_ctxts);
return pdp;
}
/*
* 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
* isn't detached already.
*/
void sgsn_pdp_ctx_terminate(struct sgsn_pdp_ctx *pdp)
{
struct sgsn_signal_data sig_data;
OSMO_ASSERT(pdp->mm != NULL);
/* There might still be pending callbacks in libgtp. So the parts of
* this object relevant to GTP need to remain intact in this case. */
LOGPDPCTXP(LOGL_INFO, pdp, "Forcing release of PDP context\n");
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;
osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_TERMINATE, &sig_data);
/* Detach from MM context */
llist_del(&pdp->list);
pdp->mm = NULL;
sgsn_delete_pdp_ctx(pdp);
}
/*
* Don't call this function directly unless you know what you are doing.
* In normal conditions use sgsn_delete_pdp_ctx and in unspecified or
* implementation dependent abnormal ones sgsn_pdp_ctx_terminate.
*/
void sgsn_pdp_ctx_free(struct sgsn_pdp_ctx *pdp)
{
struct sgsn_signal_data sig_data;
memset(&sig_data, 0, sizeof(sig_data));
sig_data.pdp = pdp;
osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_FREE, &sig_data);
rate_ctr_group_free(pdp->ctrg);
if (pdp->mm)
llist_del(&pdp->list);
llist_del(&pdp->g_list);
/* _if_ we still have a library handle, at least set it to NULL
* to avoid any dereferences of the now-deleted PDP context from
* sgsn_libgtp:cb_data_ind() */
if (pdp->lib) {
struct pdp_t *lib = pdp->lib;
LOGPDPCTXP(LOGL_NOTICE, pdp, "freeing PDP context that still "
"has a libgtp handle attached to it, this shouldn't "
"happen!\n");
osmo_generate_backtrace();
lib->priv = NULL;
}
if (pdp->destroy_ggsn)
sgsn_ggsn_ctx_free(pdp->ggsn);
talloc_free(pdp);
}
/* GGSN contexts */
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_alloc(uint32_t id)
{
struct sgsn_ggsn_ctx *ggc;
ggc = talloc_zero(tall_bsc_ctx, struct sgsn_ggsn_ctx);
if (!ggc)
return NULL;
ggc->id = id;
ggc->gtp_version = 1;
ggc->remote_restart_ctr = -1;
/* if we are called from config file parse, this gsn doesn't exist yet */
ggc->gsn = sgsn->gsn;
llist_add(&ggc->list, &sgsn_ggsn_ctxts);
return ggc;
}
void sgsn_ggsn_ctx_free(struct sgsn_ggsn_ctx *ggc)
{
llist_del(&ggc->list);
talloc_free(ggc);
}
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_id(uint32_t id)
{
struct sgsn_ggsn_ctx *ggc;
llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
if (id == ggc->id)
return ggc;
}
return NULL;
}
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_by_addr(struct in_addr *addr)
{
struct sgsn_ggsn_ctx *ggc;
llist_for_each_entry(ggc, &sgsn_ggsn_ctxts, list) {
if (!memcmp(addr, &ggc->remote_addr, sizeof(*addr)))
return ggc;
}
return NULL;
}
struct sgsn_ggsn_ctx *sgsn_ggsn_ctx_find_alloc(uint32_t id)
{
struct sgsn_ggsn_ctx *ggc;
ggc = sgsn_ggsn_ctx_by_id(id);
if (!ggc)
ggc = sgsn_ggsn_ctx_alloc(id);
return ggc;
}
/* APN contexts */
static struct apn_ctx *sgsn_apn_ctx_alloc(const char *ap_name, const char *imsi_prefix)
{
struct apn_ctx *actx;
actx = talloc_zero(tall_bsc_ctx, struct apn_ctx);
if (!actx)
return NULL;
actx->name = talloc_strdup(actx, ap_name);
actx->imsi_prefix = talloc_strdup(actx, imsi_prefix);
llist_add_tail(&actx->list, &sgsn_apn_ctxts);
return actx;
}
void sgsn_apn_ctx_free(struct apn_ctx *actx)
{
llist_del(&actx->list);
talloc_free(actx);
}
struct apn_ctx *sgsn_apn_ctx_match(const char *name, const char *imsi)
{
struct apn_ctx *actx;
struct apn_ctx *found_actx = NULL;
size_t imsi_prio = 0;
size_t name_prio = 0;
size_t name_req_len = strlen(name);
llist_for_each_entry(actx, &sgsn_apn_ctxts, list) {
size_t name_ref_len, imsi_ref_len;
const char *name_ref_start, *name_match_start;
imsi_ref_len = strlen(actx->imsi_prefix);
if (strncmp(actx->imsi_prefix, imsi, imsi_ref_len) != 0)
continue;
if (imsi_ref_len < imsi_prio)
continue;
/* IMSI matches */
name_ref_start = &actx->name[0];
if (name_ref_start[0] == '*') {
/* Suffix match */
name_ref_start += 1;
name_ref_len = strlen(name_ref_start);
if (name_ref_len > name_req_len)
continue;
} else {
name_ref_len = strlen(name_ref_start);
if (name_ref_len != name_req_len)
continue;
}
name_match_start = name + (name_req_len - name_ref_len);
if (strcasecmp(name_match_start, name_ref_start) != 0)
continue;
/* IMSI and name match */
if (imsi_ref_len == imsi_prio && name_ref_len < name_prio)
/* Lower priority, skip */
continue;
imsi_prio = imsi_ref_len;
name_prio = name_ref_len;
found_actx = actx;
}
return found_actx;
}
struct apn_ctx *sgsn_apn_ctx_by_name(const char *name, const char *imsi_prefix)
{
struct apn_ctx *actx;
llist_for_each_entry(actx, &sgsn_apn_ctxts, list) {
if (strcasecmp(name, actx->name) == 0 &&
strcasecmp(imsi_prefix, actx->imsi_prefix) == 0)
return actx;
}
return NULL;
}
struct apn_ctx *sgsn_apn_ctx_find_alloc(const char *name, const char *imsi_prefix)
{
struct apn_ctx *actx;
actx = sgsn_apn_ctx_by_name(name, imsi_prefix);
if (!actx)
actx = sgsn_apn_ctx_alloc(name, imsi_prefix);
return actx;
}
uint32_t sgsn_alloc_ptmsi(void)
{
struct sgsn_mm_ctx *mm;
uint32_t ptmsi = 0xdeadbeef;
int max_retries = 100;
restart:
if (RAND_bytes((uint8_t *) &ptmsi, sizeof(ptmsi)) != 1)
goto failed;
/* Enforce that the 2 MSB are set without loosing the distance between
* identical values. Since rand() has no duplicate values within a
* period (because the size of the state is the same like the size of
* the random value), this leads to a distance of period/4 when the
* distribution of the 2 MSB is uniform. This approach fails with a
* probability of (3/4)^max_retries, only 1% of the approaches will
* need more than 16 numbers (even distribution assumed).
*
* Alternatively, a freeze list could be used if another PRNG is used
* or when this approach proves to be not sufficient.
*/
if (ptmsi >= 0xC0000000) {
if (!max_retries--)
goto failed;
goto restart;
}
ptmsi |= 0xC0000000;
if (ptmsi == GSM_RESERVED_TMSI) {
if (!max_retries--)
goto failed;
goto restart;
}
llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
if (mm->p_tmsi == ptmsi) {
if (!max_retries--)
goto failed;
goto restart;
}
}
return ptmsi;
failed:
LOGP(DGPRS, LOGL_ERROR, "Failed to allocate a P-TMSI\n");
return GSM_RESERVED_TMSI;
}
static void drop_one_pdp(struct sgsn_pdp_ctx *pdp)
{
if (pdp->mm->gmm_state == GMM_REGISTERED_NORMAL)
gsm48_tx_gsm_deact_pdp_req(pdp, GSM_CAUSE_NET_FAIL);
else {
/* FIXME: GPRS paging in case MS is SUSPENDED */
LOGPDPCTXP(LOGL_NOTICE, pdp, "Hard-dropping PDP ctx due to GGSN "
"recovery\n");
/* FIXME: how to tell this to libgtp? */
sgsn_pdp_ctx_free(pdp);
}
}
/* High-level function to be called in case a GGSN has disappeared or
* otherwise lost state (recovery procedure) */
int drop_all_pdp_for_ggsn(struct sgsn_ggsn_ctx *ggsn)
{
struct sgsn_mm_ctx *mm;
int num = 0;
llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
struct sgsn_pdp_ctx *pdp;
llist_for_each_entry(pdp, &mm->pdp_list, list) {
if (pdp->ggsn == ggsn) {
drop_one_pdp(pdp);
num++;
}
}
}
return num;
}
void sgsn_update_subscriber_data(struct sgsn_mm_ctx *mmctx)
{
OSMO_ASSERT(mmctx != NULL);
LOGMMCTXP(LOGL_INFO, mmctx, "Subscriber data update\n");
sgsn_auth_update(mmctx);
}
static void insert_extra(struct tlv_parsed *tp,
struct sgsn_subscriber_data *data,
struct sgsn_subscriber_pdp_data *pdp)
{
tp->lv[OSMO_IE_GSM_SUB_QOS].len = pdp->qos_subscribed_len;
tp->lv[OSMO_IE_GSM_SUB_QOS].val = pdp->qos_subscribed;
/* Prefer PDP charging characteristics of per subscriber one */
if (pdp->has_pdp_charg) {
tp->lv[OSMO_IE_GSM_CHARG_CHAR].len = sizeof(pdp->pdp_charg);
tp->lv[OSMO_IE_GSM_CHARG_CHAR].val = &pdp->pdp_charg[0];
} else if (data->has_pdp_charg) {
tp->lv[OSMO_IE_GSM_CHARG_CHAR].len = sizeof(data->pdp_charg);
tp->lv[OSMO_IE_GSM_CHARG_CHAR].val = &data->pdp_charg[0];
}
}
/**
* The tlv_parsed tp parameter will be modified to insert a
* OSMO_IE_GSM_SUB_QOS in case the data is available in the
* PDP context handling.
*/
struct sgsn_ggsn_ctx *sgsn_mm_ctx_find_ggsn_ctx(struct sgsn_mm_ctx *mmctx,
struct tlv_parsed *tp,
enum gsm48_gsm_cause *gsm_cause,
char *out_apn_str)
{
char req_apn_str[GSM_APN_LENGTH] = {0};
const struct apn_ctx *apn_ctx = NULL;
const char *selected_apn_str = NULL;
struct sgsn_subscriber_pdp_data *pdp;
struct sgsn_ggsn_ctx *ggsn = NULL;
int allow_any_apn = 0;
out_apn_str[0] = '\0';
if (TLVP_PRESENT(tp, GSM48_IE_GSM_APN)) {
if (TLVP_LEN(tp, GSM48_IE_GSM_APN) >= GSM_APN_LENGTH - 1) {
LOGMMCTXP(LOGL_ERROR, mmctx, "APN IE too long\n");
*gsm_cause = GSM_CAUSE_INV_MAND_INFO;
return NULL;
}
osmo_apn_to_str(req_apn_str,
TLVP_VAL(tp, GSM48_IE_GSM_APN),
TLVP_LEN(tp, GSM48_IE_GSM_APN));
if (strcmp(req_apn_str, "*") == 0)
req_apn_str[0] = 0;
}
if (mmctx->subscr == NULL)
allow_any_apn = 1;
if (strlen(req_apn_str) == 0 && !allow_any_apn) {
/* No specific APN requested, check for an APN that is both
* granted and configured */
llist_for_each_entry(pdp, &mmctx->subscr->sgsn_data->pdp_list, list) {
if (strcmp(pdp->apn_str, "*") == 0)
{
allow_any_apn = 1;
selected_apn_str = "";
insert_extra(tp, mmctx->subscr->sgsn_data, pdp);
continue;
}
if (!llist_empty(&sgsn_apn_ctxts)) {
apn_ctx = sgsn_apn_ctx_match(req_apn_str, mmctx->imsi);
/* Not configured */
if (apn_ctx == NULL)
continue;
}
insert_extra(tp, mmctx->subscr->sgsn_data, pdp);
selected_apn_str = pdp->apn_str;
break;
}
} else if (!allow_any_apn) {
/* Check whether the given APN is granted */
llist_for_each_entry(pdp, &mmctx->subscr->sgsn_data->pdp_list, list) {
if (strcmp(pdp->apn_str, "*") == 0) {
insert_extra(tp, mmctx->subscr->sgsn_data, pdp);
selected_apn_str = req_apn_str;
allow_any_apn = 1;
continue;
}
if (strcasecmp(pdp->apn_str, req_apn_str) == 0) {
insert_extra(tp, mmctx->subscr->sgsn_data, pdp);
selected_apn_str = req_apn_str;
break;
}
}
} else if (strlen(req_apn_str) != 0) {
/* Any APN is allowed */
selected_apn_str = req_apn_str;
} else {
/* Prefer the GGSN associated with the wildcard APN */
selected_apn_str = "";
}
if (!allow_any_apn && selected_apn_str == NULL) {
/* Access not granted */
LOGMMCTXP(LOGL_NOTICE, mmctx,
"The requested APN '%s' is not allowed\n",
req_apn_str);
*gsm_cause = GSM_CAUSE_REQ_SERV_OPT_NOTSUB;
return NULL;
}
/* copy the selected apn_str */
if (selected_apn_str)
strcpy(out_apn_str, selected_apn_str);
else
out_apn_str[0] = '\0';
if (apn_ctx == NULL && selected_apn_str)
apn_ctx = sgsn_apn_ctx_match(selected_apn_str, mmctx->imsi);
if (apn_ctx != NULL) {
ggsn = apn_ctx->ggsn;
} else if (llist_empty(&sgsn_apn_ctxts)) {
/* No configuration -> use GGSN 0 */
ggsn = sgsn_ggsn_ctx_by_id(0);
} else if (allow_any_apn &&
(selected_apn_str == NULL || strlen(selected_apn_str) == 0)) {
/* No APN given and no default configuration -> Use GGSN 0 */
ggsn = sgsn_ggsn_ctx_by_id(0);
} else {
/* No matching configuration found */
LOGMMCTXP(LOGL_NOTICE, mmctx,
"The selected APN '%s' has not been configured\n",
selected_apn_str);
*gsm_cause = GSM_CAUSE_MISSING_APN;
return NULL;
}
if (!ggsn) {
LOGMMCTXP(LOGL_NOTICE, mmctx,
"No static GGSN configured. Selected APN '%s'\n",
selected_apn_str);
return NULL;
}
LOGMMCTXP(LOGL_INFO, mmctx,
"Found GGSN %d for APN '%s' (requested '%s')\n",
ggsn->id, selected_apn_str ? selected_apn_str : "---",
req_apn_str);
return ggsn;
}
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->gb.llme) {
gsm0408_gprs_access_cancelled(mmctx, SGSN_ERROR_CAUSE_NONE);
return;
}
}
/* No MM context found */
LOGP(DGPRS, LOGL_INFO, "Deleting orphaned LLME, TLLI 0x%08x\n",
llme->tlli);
gprs_llgmm_unassign(llme);
}
static void sgsn_llme_check_cb(void *data_)
{
struct gprs_llc_llme *llme, *llme_tmp;
struct timespec now_tp;
time_t now, age;
time_t max_age = gprs_max_time_to_idle();
int rc;
rc = clock_gettime(CLOCK_MONOTONIC, &now_tp);
OSMO_ASSERT(rc >= 0);
now = now_tp.tv_sec;
LOGP(DGPRS, LOGL_DEBUG,
"Checking for inactive LLMEs, time = %u\n", (unsigned)now);
llist_for_each_entry_safe(llme, llme_tmp, &gprs_llc_llmes, list) {
if (llme->age_timestamp == GPRS_LLME_RESET_AGE)
llme->age_timestamp = now;
age = now - llme->age_timestamp;
if (age > max_age || age < 0) {
LOGP(DGPRS, LOGL_INFO,
"Inactivity timeout for TLLI 0x%08x, age %d\n",
llme->tlli, (int)age);
sgsn_llme_cleanup_free(llme);
}
}
osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0);
}
void sgsn_inst_init()
{
osmo_timer_setup(&sgsn->llme_timer, sgsn_llme_check_cb, NULL);
osmo_timer_schedule(&sgsn->llme_timer, GPRS_LLME_CHECK_TICK, 0);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,323 +0,0 @@
/* 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->v44_params->nsapi_len;
memcpy(comp_entity->nsapi,
comp_field->v44_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;
}
}
/* Bail on failure */
if (comp_entity == NULL) {
LOGP(DSNDCP, LOGL_ERROR,
"Compression entity creation failed!\n");
return NULL;
}
/* Display info message */
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

@ -1,358 +0,0 @@
/* 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) {
OSMO_ASSERT(comp_field->v42bis_params);
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

@ -1,282 +0,0 @@
/* 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) {
OSMO_ASSERT(comp_field->rfc1144_params);
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;
break;
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

@ -1,71 +0,0 @@
/* VTY interface for our GPRS SNDCP implementation */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdint.h>
#include <arpa/inet.h>
#include <openbsc/gsm_data.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <openbsc/debug.h>
#include <openbsc/signal.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/gprs_sndcp.h>
#include <osmocom/vty/vty.h>
#include <osmocom/vty/command.h>
static void vty_dump_sne(struct vty *vty, struct gprs_sndcp_entity *sne)
{
vty_out(vty, " TLLI %08x SAPI=%u NSAPI=%u:%s",
sne->lle->llme->tlli, sne->lle->sapi, sne->nsapi, VTY_NEWLINE);
vty_out(vty, " Defrag: npdu=%u highest_seg=%u seg_have=0x%08x tot_len=%u%s",
sne->defrag.npdu, sne->defrag.highest_seg, sne->defrag.seg_have,
sne->defrag.tot_len, VTY_NEWLINE);
}
DEFUN(show_sndcp, show_sndcp_cmd,
"show sndcp",
SHOW_STR "Display information about the SNDCP protocol")
{
struct gprs_sndcp_entity *sne;
vty_out(vty, "State of SNDCP Entities%s", VTY_NEWLINE);
llist_for_each_entry(sne, &gprs_sndcp_entities, list)
vty_dump_sne(vty, sne);
return CMD_SUCCESS;
}
int gprs_sndcp_vty_init(void)
{
install_element_ve(&show_sndcp_cmd);
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,937 +0,0 @@
/* MS subscriber data handling */
/* (C) 2014 by sysmocom s.f.m.c. GmbH
* (C) 2015 by Holger Hans Peter Freyther
*
* 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/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gsm/apn.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/logging.h>
#include <openbsc/gprs_subscriber.h>
#include <openbsc/gsup_client.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/debug.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <limits.h>
#define SGSN_SUBSCR_MAX_RETRIES 3
#define SGSN_SUBSCR_RETRY_INTERVAL 10
#define LOGGSUPP(level, gsup, fmt, args...) \
LOGP(DGPRS, level, "GSUP(%s) " fmt, \
(gsup)->imsi, \
## args)
extern void *tall_bsc_ctx;
LLIST_HEAD(_gprs_subscribers);
struct llist_head * const gprs_subscribers = &_gprs_subscribers;
static int gsup_read_cb(struct gsup_client *gsupc, struct msgb *msg);
/* TODO: Some functions are specific to the SGSN, but this file is more general
* (it has gprs_* name). Either move these functions elsewhere, split them and
* move a part, or replace the gprs_ prefix by sgsn_. The applies to
* gprs_subscr_init, gsup_read_cb, and gprs_subscr_tx_gsup_message.
*/
int gprs_subscr_init(struct sgsn_instance *sgi)
{
const char *addr_str;
if (!sgi->cfg.gsup_server_addr.sin_addr.s_addr)
return 0;
addr_str = inet_ntoa(sgi->cfg.gsup_server_addr.sin_addr);
sgi->gsup_client = gsup_client_create(
"SGSN",
addr_str, sgi->cfg.gsup_server_port,
&gsup_read_cb,
&sgi->cfg.oap);
if (!sgi->gsup_client)
return -1;
return 1;
}
static int gsup_read_cb(struct gsup_client *gsupc, struct msgb *msg)
{
int rc;
rc = gprs_subscr_rx_gsup_message(msg);
msgb_free(msg);
if (rc < 0)
return -1;
return rc;
}
int gprs_subscr_purge(struct gprs_subscr *subscr);
static struct sgsn_subscriber_data *sgsn_subscriber_data_alloc(void *ctx)
{
struct sgsn_subscriber_data *sdata;
int idx;
sdata = talloc_zero(ctx, struct sgsn_subscriber_data);
sdata->error_cause = SGSN_ERROR_CAUSE_NONE;
for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++)
sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL;
INIT_LLIST_HEAD(&sdata->pdp_list);
return sdata;
}
struct sgsn_subscriber_pdp_data* sgsn_subscriber_pdp_data_alloc(
struct sgsn_subscriber_data *sdata)
{
struct sgsn_subscriber_pdp_data* pdata;
pdata = talloc_zero(sdata, struct sgsn_subscriber_pdp_data);
llist_add_tail(&pdata->list, &sdata->pdp_list);
return pdata;
}
struct gprs_subscr *gprs_subscr_get_by_imsi(const char *imsi)
{
struct gprs_subscr *gsub;
if (!imsi || !*imsi)
return NULL;
llist_for_each_entry(gsub, gprs_subscribers, entry) {
if (!strcmp(gsub->imsi, imsi))
return gprs_subscr_get(gsub);
}
return NULL;
}
static struct gprs_subscr *gprs_subscr_alloc(void)
{
struct gprs_subscr *gsub;
gsub = talloc_zero(tall_bsc_ctx, struct gprs_subscr);
if (!gsub)
return NULL;
llist_add_tail(&gsub->entry, gprs_subscribers);
gsub->use_count = 1;
gsub->tmsi = GSM_RESERVED_TMSI;
return gsub;
}
struct gprs_subscr *gprs_subscr_get_or_create(const char *imsi)
{
struct gprs_subscr *gsub;
gsub = gprs_subscr_get_by_imsi(imsi);
if (!gsub) {
gsub = gprs_subscr_alloc();
if (!gsub)
return NULL;
osmo_strlcpy(gsub->imsi, imsi, sizeof(gsub->imsi));
}
if (!gsub->sgsn_data)
gsub->sgsn_data = sgsn_subscriber_data_alloc(gsub);
return gsub;
}
void gprs_subscr_cleanup(struct gprs_subscr *subscr)
{
if (subscr->sgsn_data->mm) {
gprs_subscr_put(subscr->sgsn_data->mm->subscr);
subscr->sgsn_data->mm->subscr = NULL;
subscr->sgsn_data->mm = NULL;
}
if (subscr->flags & GPRS_SUBSCRIBER_ENABLE_PURGE) {
gprs_subscr_purge(subscr);
subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE;
}
}
void gprs_subscr_cancel(struct gprs_subscr *subscr)
{
subscr->authorized = 0;
subscr->flags |= GPRS_SUBSCRIBER_CANCELLED;
subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE;
gprs_subscr_update(subscr);
gprs_subscr_cleanup(subscr);
}
static int gprs_subscr_tx_gsup_message(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
struct msgb *msg = gsup_client_msgb_alloc();
if (strlen(gsup_msg->imsi) == 0 && subscr)
osmo_strlcpy(gsup_msg->imsi, subscr->imsi,
sizeof(gsup_msg->imsi));
gsup_msg->cn_domain = OSMO_GSUP_CN_DOMAIN_PS;
osmo_gsup_encode(msg, gsup_msg);
LOGGSUBSCRP(LOGL_INFO, subscr,
"Sending GSUP, will send: %s\n", msgb_hexdump(msg));
if (!sgsn->gsup_client) {
msgb_free(msg);
return -ENOTSUP;
}
return gsup_client_send(sgsn->gsup_client, msg);
}
static int gprs_subscr_tx_gsup_error_reply(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_orig,
enum gsm48_gmm_cause cause)
{
struct osmo_gsup_message gsup_reply = {0};
osmo_strlcpy(gsup_reply.imsi, gsup_orig->imsi,
sizeof(gsup_reply.imsi));
gsup_reply.cause = cause;
gsup_reply.message_type =
OSMO_GSUP_TO_MSGT_ERROR(gsup_orig->message_type);
return gprs_subscr_tx_gsup_message(subscr, &gsup_reply);
}
static int gprs_subscr_handle_gsup_auth_res(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
unsigned idx;
struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
LOGGSUBSCRP(LOGL_INFO, subscr,
"Got SendAuthenticationInfoResult, num_auth_vectors = %zu\n",
gsup_msg->num_auth_vectors);
if (gsup_msg->num_auth_vectors > 0) {
memset(sdata->auth_triplets, 0, sizeof(sdata->auth_triplets));
for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++)
sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL;
}
for (idx = 0; idx < gsup_msg->num_auth_vectors; idx++) {
size_t key_seq = idx;
LOGGSUBSCRP(LOGL_DEBUG, subscr,
"Adding auth tuple, cksn = %zu\n", key_seq);
if (key_seq >= ARRAY_SIZE(sdata->auth_triplets)) {
LOGGSUBSCRP(LOGL_NOTICE, subscr,
"Skipping auth triplet with invalid cksn %zu\n",
key_seq);
continue;
}
sdata->auth_triplets[key_seq].vec = gsup_msg->auth_vectors[idx];
sdata->auth_triplets[key_seq].key_seq = key_seq;
}
sdata->auth_triplets_updated = 1;
sdata->error_cause = SGSN_ERROR_CAUSE_NONE;
gprs_subscr_update_auth_info(subscr);
return 0;
}
static int gprs_subscr_pdp_data_clear(struct gprs_subscr *subscr)
{
struct sgsn_subscriber_pdp_data *pdp, *pdp2;
int count = 0;
llist_for_each_entry_safe(pdp, pdp2, &subscr->sgsn_data->pdp_list, list) {
llist_del(&pdp->list);
talloc_free(pdp);
count += 1;
}
return count;
}
static struct sgsn_subscriber_pdp_data *gprs_subscr_pdp_data_get_by_id(
struct gprs_subscr *subscr, unsigned context_id)
{
struct sgsn_subscriber_pdp_data *pdp;
llist_for_each_entry(pdp, &subscr->sgsn_data->pdp_list, list) {
if (pdp->context_id == context_id)
return pdp;
}
return NULL;
}
static void gprs_subscr_gsup_insert_data(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
unsigned idx;
int rc;
if (gsup_msg->msisdn_enc) {
if (gsup_msg->msisdn_enc_len > sizeof(sdata->msisdn)) {
LOGP(DGPRS, LOGL_ERROR, "MSISDN too long (%zu)\n",
gsup_msg->msisdn_enc_len);
sdata->msisdn_len = 0;
} else {
memcpy(sdata->msisdn, gsup_msg->msisdn_enc,
gsup_msg->msisdn_enc_len);
sdata->msisdn_len = gsup_msg->msisdn_enc_len;
}
}
if (gsup_msg->hlr_enc) {
if (gsup_msg->hlr_enc_len > sizeof(sdata->hlr)) {
LOGP(DGPRS, LOGL_ERROR, "HLR-Number too long (%zu)\n",
gsup_msg->hlr_enc_len);
sdata->hlr_len = 0;
} else {
memcpy(sdata->hlr, gsup_msg->hlr_enc,
gsup_msg->hlr_enc_len);
sdata->hlr_len = gsup_msg->hlr_enc_len;
}
}
if (gsup_msg->pdp_charg_enc && gsup_msg->pdp_charg_enc_len >= sizeof(sdata->pdp_charg)) {
memcpy(&sdata->pdp_charg, gsup_msg->pdp_charg_enc, sizeof(sdata->pdp_charg));
sdata->has_pdp_charg = 1;
} else {
sdata->has_pdp_charg = 0;
}
if (gsup_msg->pdp_info_compl) {
rc = gprs_subscr_pdp_data_clear(subscr);
if (rc > 0)
LOGP(DGPRS, LOGL_INFO, "Cleared existing PDP info\n");
}
for (idx = 0; idx < gsup_msg->num_pdp_infos; idx++) {
struct osmo_gsup_pdp_info *pdp_info = &gsup_msg->pdp_infos[idx];
size_t ctx_id = pdp_info->context_id;
struct sgsn_subscriber_pdp_data *pdp_data;
if (pdp_info->apn_enc_len >= sizeof(pdp_data->apn_str)-1) {
LOGGSUBSCRP(LOGL_ERROR, subscr,
"APN too long, context id = %zu, APN = %s\n",
ctx_id, osmo_hexdump(pdp_info->apn_enc,
pdp_info->apn_enc_len));
continue;
}
if (pdp_info->qos_enc_len > sizeof(pdp_data->qos_subscribed)) {
LOGGSUBSCRP(LOGL_ERROR, subscr,
"QoS info too long (%zu)\n",
pdp_info->qos_enc_len);
continue;
}
LOGGSUBSCRP(LOGL_INFO, subscr,
"Will set PDP info, context id = %zu, APN = %s\n",
ctx_id, osmo_hexdump(pdp_info->apn_enc, pdp_info->apn_enc_len));
/* Set PDP info [ctx_id] */
pdp_data = gprs_subscr_pdp_data_get_by_id(subscr, ctx_id);
if (!pdp_data) {
pdp_data = sgsn_subscriber_pdp_data_alloc(subscr->sgsn_data);
pdp_data->context_id = ctx_id;
}
OSMO_ASSERT(pdp_data != NULL);
pdp_data->pdp_type = pdp_info->pdp_type;
osmo_apn_to_str(pdp_data->apn_str,
pdp_info->apn_enc, pdp_info->apn_enc_len);
memcpy(pdp_data->qos_subscribed, pdp_info->qos_enc, pdp_info->qos_enc_len);
pdp_data->qos_subscribed_len = pdp_info->qos_enc_len;
if (pdp_info->pdp_charg_enc && pdp_info->pdp_charg_enc_len >= sizeof(pdp_data->pdp_charg)) {
memcpy(&pdp_data->pdp_charg, pdp_info->pdp_charg_enc, sizeof(pdp_data->pdp_charg));
pdp_data->has_pdp_charg = 1;
} else {
pdp_data->has_pdp_charg = 0;
}
}
}
static int gprs_subscr_handle_gsup_upd_loc_res(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
/* contrary to MAP, we allow piggy-backing subscriber data onto
* the UPDATE LOCATION RESULT, and don't mandate the use of a
* separate nested INSERT SUBSCRIBER DATA transaction */
gprs_subscr_gsup_insert_data(subscr, gsup_msg);
subscr->authorized = 1;
subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
subscr->flags |= GPRS_SUBSCRIBER_ENABLE_PURGE;
gprs_subscr_update(subscr);
return 0;
}
static int gprs_subscr_handle_gsup_dsd_req(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
struct osmo_gsup_message gsup_reply = {0};
if (gsup_msg->cn_domain != OSMO_GSUP_CN_DOMAIN_PS) {
LOGGSUBSCRP(LOGL_ERROR, subscr,
"Rx GSUP message %s not supported for CS\n",
osmo_gsup_message_type_name(gsup_msg->message_type));
gsup_reply.cause = GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL;
gsup_reply.message_type = OSMO_GSUP_MSGT_DELETE_DATA_ERROR;
} else {
gsm0408_gprs_access_cancelled(subscr->sgsn_data->mm,
GMM_CAUSE_GPRS_NOTALLOWED);
gsup_reply.message_type = OSMO_GSUP_MSGT_DELETE_DATA_RESULT;
}
return gprs_subscr_tx_gsup_message(subscr, &gsup_reply);
}
static int gprs_subscr_handle_gsup_isd_req(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
struct osmo_gsup_message gsup_reply = {0};
gprs_subscr_gsup_insert_data(subscr, gsup_msg);
subscr->authorized = 1;
subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
subscr->flags |= GPRS_SUBSCRIBER_ENABLE_PURGE;
gprs_subscr_update(subscr);
gsup_reply.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT;
return gprs_subscr_tx_gsup_message(subscr, &gsup_reply);
}
static int check_cause(int cause)
{
switch (cause) {
case GMM_CAUSE_IMSI_UNKNOWN ... GMM_CAUSE_ILLEGAL_ME:
case GMM_CAUSE_GPRS_NOTALLOWED ... GMM_CAUSE_NO_GPRS_PLMN:
return EACCES;
case GMM_CAUSE_MSC_TEMP_NOTREACH ... GMM_CAUSE_CONGESTION:
return EHOSTUNREACH;
case GMM_CAUSE_SEM_INCORR_MSG ... GMM_CAUSE_PROTO_ERR_UNSPEC:
default:
return EINVAL;
}
}
static int gprs_subscr_handle_gsup_auth_err(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
unsigned idx;
struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
int cause_err;
cause_err = check_cause(gsup_msg->cause);
LOGGSUBSCRP(LOGL_DEBUG, subscr,
"Send authentication info has failed with cause %d, "
"handled as: %s\n",
gsup_msg->cause, strerror(cause_err));
switch (cause_err) {
case EACCES:
LOGGSUBSCRP(LOGL_NOTICE, subscr,
"GPRS send auth info req failed, access denied, "
"GMM cause = '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
/* Clear auth tuples */
memset(sdata->auth_triplets, 0, sizeof(sdata->auth_triplets));
for (idx = 0; idx < ARRAY_SIZE(sdata->auth_triplets); idx++)
sdata->auth_triplets[idx].key_seq = GSM_KEY_SEQ_INVAL;
subscr->authorized = 0;
sdata->error_cause = gsup_msg->cause;
gprs_subscr_update_auth_info(subscr);
break;
case EHOSTUNREACH:
LOGGSUBSCRP(LOGL_NOTICE, subscr,
"GPRS send auth info req failed, GMM cause = '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
sdata->error_cause = gsup_msg->cause;
gprs_subscr_update_auth_info(subscr);
break;
default:
case EINVAL:
LOGGSUBSCRP(LOGL_ERROR, subscr,
"GSUP protocol remote error, GMM cause = '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
break;
}
return -gsup_msg->cause;
}
static int gprs_subscr_handle_gsup_upd_loc_err(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
int cause_err;
cause_err = check_cause(gsup_msg->cause);
LOGGSUBSCRP(LOGL_DEBUG, subscr,
"Update location has failed with cause %d, handled as: %s\n",
gsup_msg->cause, strerror(cause_err));
switch (cause_err) {
case EACCES:
LOGGSUBSCRP(LOGL_NOTICE, subscr,
"GPRS update location failed, access denied, "
"GMM cause = '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
subscr->authorized = 0;
subscr->sgsn_data->error_cause = gsup_msg->cause;
gprs_subscr_update_auth_info(subscr);
break;
case EHOSTUNREACH:
LOGGSUBSCRP(LOGL_NOTICE, subscr,
"GPRS update location failed, GMM cause = '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
subscr->sgsn_data->error_cause = gsup_msg->cause;
gprs_subscr_update_auth_info(subscr);
break;
default:
case EINVAL:
LOGGSUBSCRP(LOGL_ERROR, subscr,
"GSUP protocol remote error, GMM cause = '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
break;
}
return -gsup_msg->cause;
}
static int gprs_subscr_handle_gsup_purge_no_subscr(
struct osmo_gsup_message *gsup_msg)
{
if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) {
LOGGSUPP(LOGL_NOTICE, gsup_msg,
"Purge MS has failed with cause '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
return -gsup_msg->cause;
}
LOGGSUPP(LOGL_INFO, gsup_msg, "Completing purge MS\n");
return 0;
}
static int gprs_subscr_handle_gsup_purge_res(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
LOGGSUBSCRP(LOGL_INFO, subscr, "Completing purge MS\n");
/* Force silent cancellation */
subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
gprs_subscr_cancel(subscr);
return 0;
}
static int gprs_subscr_handle_gsup_purge_err(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
LOGGSUBSCRP(LOGL_NOTICE, subscr,
"Purge MS has failed with cause '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
/* In GSM 09.02, 19.1.4.4, the text and the SDL diagram imply that
* the subscriber data is not removed if the request has failed. On the
* other hand, keeping the subscriber data in either error case
* (subscriber unknown, syntactical message error, connection error)
* doesn't seem to give any advantage, since the data will be restored
* on the next Attach Request anyway.
* This approach ensures, that the subscriber record will not stick if
* an error happens.
*/
/* TODO: Check whether this behaviour is acceptable and either just
* remove this TODO-notice or change the implementation to not delete
* the subscriber data (eventually resetting the ENABLE_PURGE flag and
* restarting the expiry timer based on the cause).
*
* Subscriber Unknown: cancel subscr
* Temporary network problems: do nothing (handled by timer based retry)
* Message problems (syntax, nyi, ...): cancel subscr (retry won't help)
*/
gprs_subscr_handle_gsup_purge_res(subscr, gsup_msg);
return -gsup_msg->cause;
}
static int gprs_subscr_handle_loc_cancel_req(struct gprs_subscr *subscr,
struct osmo_gsup_message *gsup_msg)
{
struct osmo_gsup_message gsup_reply = {0};
int is_update_procedure = !gsup_msg->cancel_type ||
gsup_msg->cancel_type == OSMO_GSUP_CANCEL_TYPE_UPDATE;
LOGGSUBSCRP(LOGL_INFO, subscr, "Cancelling MS subscriber (%s)\n",
is_update_procedure ?
"update procedure" : "subscription withdraw");
gsup_reply.message_type = OSMO_GSUP_MSGT_LOCATION_CANCEL_RESULT;
gprs_subscr_tx_gsup_message(subscr, &gsup_reply);
if (is_update_procedure)
subscr->sgsn_data->error_cause = SGSN_ERROR_CAUSE_NONE;
else
/* Since a withdraw cause is not specified, just abort the
* current attachment. The following re-attachment should then
* be rejected with a proper cause value.
*/
subscr->sgsn_data->error_cause = GMM_CAUSE_IMPL_DETACHED;
gprs_subscr_cancel(subscr);
return 0;
}
static int gprs_subscr_handle_unknown_imsi(struct osmo_gsup_message *gsup_msg)
{
if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg->message_type)) {
gprs_subscr_tx_gsup_error_reply(NULL, gsup_msg,
GMM_CAUSE_IMSI_UNKNOWN);
LOGP(DGPRS, LOGL_NOTICE,
"Unknown IMSI %s, discarding GSUP request "
"of type 0x%02x\n",
gsup_msg->imsi, gsup_msg->message_type);
} else if (OSMO_GSUP_IS_MSGT_ERROR(gsup_msg->message_type)) {
LOGP(DGPRS, LOGL_NOTICE,
"Unknown IMSI %s, discarding GSUP error "
"of type 0x%02x, cause '%s' (%d)\n",
gsup_msg->imsi, gsup_msg->message_type,
get_value_string(gsm48_gmm_cause_names, gsup_msg->cause),
gsup_msg->cause);
} else {
LOGP(DGPRS, LOGL_NOTICE,
"Unknown IMSI %s, discarding GSUP response "
"of type 0x%02x\n",
gsup_msg->imsi, gsup_msg->message_type);
}
return -GMM_CAUSE_IMSI_UNKNOWN;
}
int gprs_subscr_rx_gsup_message(struct msgb *msg)
{
uint8_t *data = msgb_l2(msg);
size_t data_len = msgb_l2len(msg);
int rc = 0;
struct osmo_gsup_message gsup_msg = {0};
struct gprs_subscr *subscr;
rc = osmo_gsup_decode(data, data_len, &gsup_msg);
if (rc < 0) {
LOGP(DGPRS, LOGL_ERROR,
"decoding GSUP message fails with error '%s' (%d)\n",
get_value_string(gsm48_gmm_cause_names, -rc), -rc);
return rc;
}
if (!gsup_msg.imsi[0]) {
LOGP(DGPRS, LOGL_ERROR, "Missing IMSI in GSUP message\n");
if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg.message_type))
gprs_subscr_tx_gsup_error_reply(NULL, &gsup_msg,
GMM_CAUSE_INV_MAND_INFO);
return -GMM_CAUSE_INV_MAND_INFO;
}
if (!gsup_msg.cause && OSMO_GSUP_IS_MSGT_ERROR(gsup_msg.message_type))
gsup_msg.cause = GMM_CAUSE_NET_FAIL;
subscr = gprs_subscr_get_by_imsi(gsup_msg.imsi);
if (!subscr) {
switch (gsup_msg.message_type) {
case OSMO_GSUP_MSGT_PURGE_MS_RESULT:
case OSMO_GSUP_MSGT_PURGE_MS_ERROR:
return gprs_subscr_handle_gsup_purge_no_subscr(&gsup_msg);
default:
return gprs_subscr_handle_unknown_imsi(&gsup_msg);
}
}
LOGGSUBSCRP(LOGL_INFO, subscr,
"Received GSUP message %s\n",
osmo_gsup_message_type_name(gsup_msg.message_type));
switch (gsup_msg.message_type) {
case OSMO_GSUP_MSGT_LOCATION_CANCEL_REQUEST:
rc = gprs_subscr_handle_loc_cancel_req(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT:
rc = gprs_subscr_handle_gsup_auth_res(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR:
rc = gprs_subscr_handle_gsup_auth_err(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT:
rc = gprs_subscr_handle_gsup_upd_loc_res(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR:
rc = gprs_subscr_handle_gsup_upd_loc_err(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_PURGE_MS_ERROR:
rc = gprs_subscr_handle_gsup_purge_err(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_PURGE_MS_RESULT:
rc = gprs_subscr_handle_gsup_purge_res(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST:
rc = gprs_subscr_handle_gsup_isd_req(subscr, &gsup_msg);
break;
case OSMO_GSUP_MSGT_DELETE_DATA_REQUEST:
rc = gprs_subscr_handle_gsup_dsd_req(subscr, &gsup_msg);
break;
default:
LOGGSUBSCRP(LOGL_ERROR, subscr,
"Rx GSUP message %s not valid at SGSN\n",
osmo_gsup_message_type_name(gsup_msg.message_type));
if (OSMO_GSUP_IS_MSGT_REQUEST(gsup_msg.message_type))
gprs_subscr_tx_gsup_error_reply(
subscr, &gsup_msg, GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL);
rc = -GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL;
break;
};
gprs_subscr_put(subscr);
return rc;
}
int gprs_subscr_purge(struct gprs_subscr *subscr)
{
struct sgsn_subscriber_data *sdata = subscr->sgsn_data;
struct osmo_gsup_message gsup_msg = {0};
LOGGSUBSCRP(LOGL_INFO, subscr, "purging MS subscriber\n");
gsup_msg.message_type = OSMO_GSUP_MSGT_PURGE_MS_REQUEST;
/* Provide the HLR number in case it is known */
gsup_msg.hlr_enc_len = sdata->hlr_len;
gsup_msg.hlr_enc = sdata->hlr;
return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
}
static int gprs_subscr_query_auth_info(struct gprs_subscr *subscr,
const uint8_t *auts,
const uint8_t *auts_rand)
{
struct osmo_gsup_message gsup_msg = {0};
/* Make sure we have a complete resync or clearly no resync. */
OSMO_ASSERT((auts != NULL) == (auts_rand != NULL));
LOGGSUBSCRP(LOGL_INFO, subscr, "requesting auth info%s\n",
auts ? " with AUTS (UMTS Resynch)" : "");
gsup_msg.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST;
gsup_msg.auts = auts;
gsup_msg.rand = auts_rand;
return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
}
int gprs_subscr_location_update(struct gprs_subscr *subscr)
{
struct osmo_gsup_message gsup_msg = {0};
LOGGSUBSCRP(LOGL_INFO, subscr,
"subscriber data is not available\n");
gsup_msg.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST;
return gprs_subscr_tx_gsup_message(subscr, &gsup_msg);
}
void gprs_subscr_update(struct gprs_subscr *subscr)
{
LOGGSUBSCRP(LOGL_DEBUG, subscr, "Updating subscriber data\n");
subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING;
subscr->flags &= ~GPRS_SUBSCRIBER_FIRST_CONTACT;
if (subscr->sgsn_data->mm)
sgsn_update_subscriber_data(subscr->sgsn_data->mm);
}
void gprs_subscr_update_auth_info(struct gprs_subscr *subscr)
{
LOGGSUBSCRP(LOGL_DEBUG, subscr,
"Updating subscriber authentication info\n");
subscr->flags &= ~GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING;
subscr->flags &= ~GPRS_SUBSCRIBER_FIRST_CONTACT;
if (subscr->sgsn_data->mm)
sgsn_update_subscriber_data(subscr->sgsn_data->mm);
}
struct gprs_subscr *gprs_subscr_get_or_create_by_mmctx(struct sgsn_mm_ctx *mmctx)
{
struct gprs_subscr *subscr = NULL;
if (mmctx->subscr)
return gprs_subscr_get(mmctx->subscr);
if (mmctx->imsi[0])
subscr = gprs_subscr_get_by_imsi(mmctx->imsi);
if (!subscr) {
subscr = gprs_subscr_get_or_create(mmctx->imsi);
subscr->flags |= GPRS_SUBSCRIBER_FIRST_CONTACT;
subscr->flags &= ~GPRS_SUBSCRIBER_ENABLE_PURGE;
}
osmo_strlcpy(subscr->imei, mmctx->imei, sizeof(subscr->imei));
if (subscr->lac != mmctx->ra.lac)
subscr->lac = mmctx->ra.lac;
subscr->sgsn_data->mm = mmctx;
mmctx->subscr = gprs_subscr_get(subscr);
return subscr;
}
int gprs_subscr_request_update_location(struct sgsn_mm_ctx *mmctx)
{
struct gprs_subscr *subscr = NULL;
int rc;
LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber data update\n");
subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
subscr->flags |= GPRS_SUBSCRIBER_UPDATE_LOCATION_PENDING;
rc = gprs_subscr_location_update(subscr);
gprs_subscr_put(subscr);
return rc;
}
/*! \brief Send Update Auth Info request via GSUP, with or without resync.
* \param[in] mmctx MM context to request authentication tuples for.
* \param[in] auts 14 octet AUTS token for UMTS resync, or NULL.
* \param[in] auts_rand 16 octet Random token for UMTS resync, or NULL.
* In case of normal Authentication Info request, both \a auts and \a auts_rand
* must be NULL. For resync, both must be non-NULL.
*/
int gprs_subscr_request_auth_info(struct sgsn_mm_ctx *mmctx,
const uint8_t *auts,
const uint8_t *auts_rand)
{
struct gprs_subscr *subscr = NULL;
int rc;
LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting subscriber authentication info\n");
subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
subscr->flags |= GPRS_SUBSCRIBER_UPDATE_AUTH_INFO_PENDING;
rc = gprs_subscr_query_auth_info(subscr, auts, auts_rand);
gprs_subscr_put(subscr);
return rc;
}
static void gprs_subscr_free(struct gprs_subscr *gsub)
{
llist_del(&gsub->entry);
talloc_free(gsub);
}
struct gprs_subscr *_gprs_subscr_get(struct gprs_subscr *gsub,
const char *file, int line)
{
OSMO_ASSERT(gsub->use_count < INT_MAX);
gsub->use_count++;
LOGPSRC(DREF, LOGL_DEBUG, file, line,
"subscr %s usage increases to: %d\n",
gsub->imsi, gsub->use_count);
return gsub;
}
struct gprs_subscr *_gprs_subscr_put(struct gprs_subscr *gsub,
const char *file, int line)
{
gsub->use_count--;
LOGPSRC(DREF, gsub->use_count >= 0? LOGL_DEBUG : LOGL_ERROR,
file, line,
"subscr %s usage decreases to: %d%s\n",
gsub->imsi, gsub->use_count,
gsub->keep_in_ram? ", keep-in-ram flag is set" : "");
if (gsub->use_count > 0)
return gsub;
if (gsub->keep_in_ram)
return gsub;
gprs_subscr_free(gsub);
return NULL;
}

View File

@ -1,246 +0,0 @@
/* GPRS utility functions */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010-2014 by On-Waves
* (C) 2013 by Holger Hans Peter Freyther
* 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/gprs_utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm48.h>
#include <string.h>
/* FIXME: this needs to go to libosmocore/msgb.c */
struct msgb *gprs_msgb_copy(const struct msgb *msg, const char *name)
{
struct libgb_msgb_cb *old_cb, *new_cb;
struct msgb *new_msg;
new_msg = msgb_alloc(msg->data_len, name);
if (!new_msg)
return NULL;
/* copy data */
memcpy(new_msg->_data, msg->_data, new_msg->data_len);
/* copy header */
new_msg->len = msg->len;
new_msg->data += msg->data - msg->_data;
new_msg->head += msg->head - msg->_data;
new_msg->tail += msg->tail - msg->_data;
if (msg->l1h)
new_msg->l1h = new_msg->_data + (msg->l1h - msg->_data);
if (msg->l2h)
new_msg->l2h = new_msg->_data + (msg->l2h - msg->_data);
if (msg->l3h)
new_msg->l3h = new_msg->_data + (msg->l3h - msg->_data);
if (msg->l4h)
new_msg->l4h = new_msg->_data + (msg->l4h - msg->_data);
/* copy GB specific data */
old_cb = LIBGB_MSGB_CB(msg);
new_cb = LIBGB_MSGB_CB(new_msg);
if (old_cb->bssgph)
new_cb->bssgph = new_msg->_data + (old_cb->bssgph - msg->_data);
if (old_cb->llch)
new_cb->llch = new_msg->_data + (old_cb->llch - msg->_data);
/* bssgp_cell_id is a pointer into the old msgb, so we need to make
* it a pointer into the new msgb */
if (old_cb->bssgp_cell_id)
new_cb->bssgp_cell_id = new_msg->_data +
(old_cb->bssgp_cell_id - msg->_data);
new_cb->nsei = old_cb->nsei;
new_cb->bvci = old_cb->bvci;
new_cb->tlli = old_cb->tlli;
return new_msg;
}
/* TODO: Move this to libosmocore/msgb.c */
int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area,
size_t old_size, size_t new_size)
{
int rc;
uint8_t *rest = area + old_size;
int rest_len = msg->len - old_size - (area - msg->data);
int delta_size = (int)new_size - (int)old_size;
if (delta_size == 0)
return 0;
if (delta_size > 0) {
rc = msgb_trim(msg, msg->len + delta_size);
if (rc < 0)
return rc;
}
memmove(area + new_size, area + old_size, rest_len);
if (msg->l1h >= rest)
msg->l1h += delta_size;
if (msg->l2h >= rest)
msg->l2h += delta_size;
if (msg->l3h >= rest)
msg->l3h += delta_size;
if (msg->l4h >= rest)
msg->l4h += delta_size;
if (delta_size < 0)
msgb_trim(msg, msg->len + delta_size);
return 0;
}
int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str)
{
uint8_t *last_len_field;
int len;
/* Can we even write the length field to the output? */
if (max_len == 0)
return -1;
/* Remember where we need to put the length once we know it */
last_len_field = apn_enc;
len = 1;
apn_enc += 1;
while (str[0]) {
if (len >= max_len)
return -1;
if (str[0] == '.') {
*last_len_field = (apn_enc - last_len_field) - 1;
last_len_field = apn_enc;
} else {
*apn_enc = str[0];
}
apn_enc += 1;
str += 1;
len += 1;
}
*last_len_field = (apn_enc - last_len_field) - 1;
return len;
}
/* GSM 04.08, 10.5.7.3 GPRS Timer */
int gprs_tmr_to_secs(uint8_t tmr)
{
switch (tmr & GPRS_TMR_UNIT_MASK) {
case GPRS_TMR_2SECONDS:
return 2 * (tmr & GPRS_TMR_FACT_MASK);
default:
case GPRS_TMR_MINUTE:
return 60 * (tmr & GPRS_TMR_FACT_MASK);
case GPRS_TMR_6MINUTE:
return 360 * (tmr & GPRS_TMR_FACT_MASK);
case GPRS_TMR_DEACTIVATED:
return -1;
}
}
/* This functions returns a tmr value such that
* - f is monotonic
* - f(s) <= s
* - f(s) == s if a tmr exists with s = gprs_tmr_to_secs(tmr)
* - the best possible resolution is used
* where
* f(s) = gprs_tmr_to_secs(gprs_secs_to_tmr_floor(s))
*/
uint8_t gprs_secs_to_tmr_floor(int secs)
{
if (secs < 0)
return GPRS_TMR_DEACTIVATED;
if (secs < 2 * 32)
return GPRS_TMR_2SECONDS | (secs / 2);
if (secs < 60 * 2)
/* Ensure monotonicity */
return GPRS_TMR_2SECONDS | GPRS_TMR_FACT_MASK;
if (secs < 60 * 32)
return GPRS_TMR_MINUTE | (secs / 60);
if (secs < 360 * 6)
/* Ensure monotonicity */
return GPRS_TMR_MINUTE | GPRS_TMR_FACT_MASK;
if (secs < 360 * 32)
return GPRS_TMR_6MINUTE | (secs / 360);
return GPRS_TMR_6MINUTE | GPRS_TMR_FACT_MASK;
}
/* GSM 04.08, 10.5.1.4 */
int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len)
{
if (value_len != GSM48_TMSI_LEN)
return 0;
if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_TMSI)
return 0;
return 1;
}
/* GSM 04.08, 10.5.1.4 */
int gprs_is_mi_imsi(const uint8_t *value, size_t value_len)
{
if (value_len == 0)
return 0;
if (!value || (value[0] & GSM_MI_TYPE_MASK) != GSM_MI_TYPE_IMSI)
return 0;
return 1;
}
int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi)
{
uint32_t tmsi_be;
if (!gprs_is_mi_tmsi(value, value_len))
return 0;
memcpy(&tmsi_be, value + 1, sizeof(tmsi_be));
*tmsi = ntohl(tmsi_be);
return 1;
}
void gprs_parse_tmsi(const uint8_t *value, uint32_t *tmsi)
{
uint32_t tmsi_be;
memcpy(&tmsi_be, value, sizeof(tmsi_be));
*tmsi = ntohl(tmsi_be);
}
int gprs_ra_id_equals(const struct gprs_ra_id *id1,
const struct gprs_ra_id *id2)
{
return (id1->mcc == id2->mcc && id1->mnc == id2->mnc &&
id1->lac == id2->lac && id1->rac == id2->rac);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,220 +0,0 @@
/* GTP Hub Implementation */
/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* gtphub_ares.c.
*
* This file is kept separate so that these functions can be wrapped for
* gtphub_test.c. When a function and its callers are in the same compilational
* unit, the wrappability may be optimized away.
*
* Author: Neels Hofmeyr
*
* 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 <string.h>
#include <unistd.h>
#include <openbsc/gtphub.h>
#include <openbsc/debug.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/apn.h>
/* TODO split GRX ares from sgsn into a separate struct and allow use without
* globals. */
#include <openbsc/sgsn.h>
extern struct sgsn_instance *sgsn;
struct sgsn_instance sgsn_inst = { 0 };
struct sgsn_instance *sgsn = &sgsn_inst;
extern void *osmo_gtphub_ctx;
int gtphub_ares_init(struct gtphub *hub)
{
return sgsn_ares_init(sgsn);
}
struct ggsn_lookup {
struct llist_head entry;
struct expiring_item expiry_entry;
struct gtphub *hub;
char imsi_str[GSM23003_IMSI_MAX_DIGITS+1];
char apn_ni_str[GSM_APN_LENGTH];
char apn_oi_str[GSM_APN_LENGTH];
int have_3dig_mnc;
};
static int start_ares_query(struct ggsn_lookup *lookup);
static void ggsn_lookup_cb(void *arg, int status, int timeouts,
struct hostent *hostent)
{
struct ggsn_lookup *lookup = arg;
LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_cb(%p / %p)", lookup,
&lookup->expiry_entry);
if (status != ARES_SUCCESS) {
LOGP(DGTPHUB, LOGL_ERROR, "DNS query failed.\n");
/* Need to try with three digits now */
if (!lookup->have_3dig_mnc) {
lookup->have_3dig_mnc = 1;
if (start_ares_query(lookup) == 0)
return;
}
LOGP(DGTPHUB, LOGL_ERROR, "Failed to resolve GGSN. (%p)\n",
lookup);
goto remove_from_queue;
}
struct gsn_addr resolved_addr;
if (hostent->h_length > sizeof(resolved_addr.buf)) {
LOGP(DGTPHUB, LOGL_ERROR, "Addr size too large: %d > %d\n",
(int)hostent->h_length, (int)sizeof(resolved_addr.buf));
goto remove_from_queue;
}
/* Get the first addr from the list */
char *addr0 = hostent->h_addr_list[0];
if (!addr0) {
LOGP(DGTPHUB, LOGL_ERROR, "No host address.\n");
goto remove_from_queue;
}
memcpy(resolved_addr.buf, addr0, hostent->h_length);
resolved_addr.len = hostent->h_length;
LOGP(DGTPHUB, LOGL_NOTICE, "resolved addr %s\n",
osmo_hexdump((unsigned char*)&resolved_addr,
sizeof(resolved_addr)));
gtphub_resolved_ggsn(lookup->hub, lookup->apn_oi_str, &resolved_addr,
gtphub_now());
remove_from_queue:
LOGP(DGTPHUB, LOGL_ERROR, "Removing GGSN lookup. (%p / %p)\n", lookup,
&lookup->expiry_entry);
expiring_item_del(&lookup->expiry_entry);
}
static void make_addr_str(struct ggsn_lookup *lookup)
{
char *apn_oi_str;
apn_oi_str = osmo_apn_qualify_from_imsi(lookup->imsi_str,
lookup->apn_ni_str,
lookup->have_3dig_mnc);
osmo_strlcpy(lookup->apn_oi_str, apn_oi_str,
sizeof(lookup->apn_oi_str));
}
static int start_ares_query(struct ggsn_lookup *lookup)
{
LOGP(DGTPHUB, LOGL_DEBUG, "Going to query %s (%p / %p)\n",
lookup->apn_oi_str, lookup, &lookup->expiry_entry);
int rc = sgsn_ares_query(sgsn, lookup->apn_oi_str, ggsn_lookup_cb,
lookup);
if (rc != 0)
LOGP(DGTPHUB, LOGL_ERROR, "Failed to start ares query.\n");
return rc;
}
static void ggsn_lookup_del_cb(struct expiring_item *expi)
{
struct ggsn_lookup *lookup;
lookup = container_of(expi, struct ggsn_lookup, expiry_entry);
LOGP(DGTPHUB, LOGL_NOTICE, "ggsn_lookup_del_cb(%p / %p)\n", lookup,
expi);
lookup->expiry_entry.del_cb = 0;
expiring_item_del(expi);
llist_del(&lookup->entry);
talloc_free(lookup);
}
struct gtphub_peer_port *gtphub_resolve_ggsn_addr(struct gtphub *hub,
const char *imsi_str,
const char *apn_ni_str)
{
OSMO_ASSERT(imsi_str);
OSMO_ASSERT(apn_ni_str);
struct ggsn_lookup *lookup = talloc_zero(osmo_gtphub_ctx,
struct ggsn_lookup);
OSMO_ASSERT(lookup);
LOGP(DGTPHUB, LOGL_DEBUG, "Request to resolve IMSI"
" '%s' with APN-NI '%s' (%p / %p)\n",
imsi_str, apn_ni_str, lookup, &lookup->expiry_entry);
expiring_item_init(&lookup->expiry_entry);
lookup->hub = hub;
osmo_strlcpy(lookup->imsi_str, imsi_str, sizeof(lookup->imsi_str));
osmo_strlcpy(lookup->apn_ni_str, apn_ni_str,
sizeof(lookup->apn_ni_str));
make_addr_str(lookup);
struct ggsn_lookup *active;
llist_for_each_entry(active, &hub->ggsn_lookups, entry) {
if (strncmp(active->apn_oi_str, lookup->apn_oi_str,
sizeof(lookup->apn_oi_str)) == 0) {
LOGP(DGTPHUB, LOGL_DEBUG,
"Query already pending for %s\n",
lookup->apn_oi_str);
/* A query already pending. Just tip our hat. */
return NULL;
}
}
struct gtphub_resolved_ggsn *resolved;
llist_for_each_entry(resolved, &hub->resolved_ggsns, entry) {
if (strncmp(resolved->apn_oi_str, lookup->apn_oi_str,
sizeof(lookup->apn_oi_str)) == 0) {
LOGP(DGTPHUB, LOGL_DEBUG,
"GGSN resolved from cache: %s -> %s\n",
lookup->apn_oi_str,
gtphub_port_str(resolved->peer));
return resolved->peer;
}
}
/* Kick off a resolution, but so far return nothing. The hope is that
* the peer will resend the request (a couple of times), and by then
* the GGSN will be resolved. */
LOGP(DGTPHUB, LOGL_DEBUG,
"Sending out DNS query for %s..."
" (Returning failure, hoping for a retry once resolution"
" has concluded)\n",
lookup->apn_oi_str);
llist_add(&lookup->entry, &hub->ggsn_lookups);
lookup->expiry_entry.del_cb = ggsn_lookup_del_cb;
expiry_add(&hub->expire_quickly, &lookup->expiry_entry, gtphub_now());
start_ares_query(lookup);
return NULL;
}

View File

@ -1,359 +0,0 @@
/* GTP Hub main program */
/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr
*
* 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 <unistd.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <inttypes.h>
#include <sys/stat.h>
#define _GNU_SOURCE
#include <getopt.h>
#include <osmocom/core/signal.h>
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/ports.h>
#include <openbsc/debug.h>
#include <openbsc/gtphub.h>
#include <openbsc/vty.h>
#include "../../bscconfig.h"
extern void *osmo_gtphub_ctx;
const char *gtphub_copyright =
"Copyright (C) 2015 sysmocom s.f.m.c GmbH <info@sysmocom.de>\r\n"
"License AGPLv3+: GNU AGPL version 2 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
static struct log_info_cat gtphub_categories[] = {
[DGTPHUB] = {
.name = "DGTPHUB",
.description = "GTP Hub",
.color = "\033[1;33m",
.enabled = 1,
.loglevel = LOGL_INFO,
},
};
int gtphub_log_filter_fn(const struct log_context *ctx,
struct log_target *tar)
{
return 0;
}
static const struct log_info gtphub_log_info = {
.filter_fn = gtphub_log_filter_fn,
.cat = gtphub_categories,
.num_cat = ARRAY_SIZE(gtphub_categories),
};
void log_cfg(struct gtphub_cfg *cfg)
{
int side_idx, plane_idx;
for_each_side_and_plane(side_idx, plane_idx) {
struct gtphub_cfg_addr *a;
a = &cfg->to_gsns[side_idx][plane_idx].bind;
LOGP(DGTPHUB, LOGL_NOTICE,
"to-%ss bind, %s: %s port %d\n",
gtphub_side_idx_names[side_idx],
gtphub_plane_idx_names[plane_idx],
a->addr_str, a->port);
}
}
static void signal_handler(int signal)
{
fprintf(stdout, "signal %d received\n", signal);
switch (signal) {
case SIGINT:
case SIGTERM:
osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
sleep(1);
exit(0);
break;
case SIGABRT:
/* in case of abort, we want to obtain a talloc report
* and then return to the caller, who will abort the process */
case SIGUSR1:
case SIGUSR2:
talloc_report_full(osmo_gtphub_ctx, stderr);
break;
default:
break;
}
}
extern int bsc_vty_go_parent(struct vty *vty);
static struct vty_app_info vty_info = {
.name = "OsmoGTPhub",
.version = PACKAGE_VERSION,
.go_parent_cb = bsc_vty_go_parent,
.is_config_node = bsc_vty_is_config_node,
};
struct cmdline_cfg {
const char *config_file;
const char *restart_counter_file;
int daemonize;
};
static uint8_t next_restart_count(const char *path)
{
int umask_was = umask(022);
uint8_t counter = 0;
FILE *f = fopen(path, "r");
if (f) {
int rc = fscanf(f, "%hhu", &counter);
if (rc != 1)
goto failed_to_read;
char c;
while (fread(&c, 1, 1, f) > 0) {
switch (c) {
case ' ':
case '\t':
case '\n':
case '\r':
break;
default:
goto failed_to_read;
}
}
fclose(f);
}
counter ++;
f = fopen(path, "w");
if (!f)
goto failed_to_write;
if (fprintf(f, "%" PRIu8 "\n", counter) < 2)
goto failed_to_write;
if (fclose(f)) {
f = NULL;
goto failed_to_write;
}
umask(umask_was);
LOGP(DGTPHUB, LOGL_NOTICE, "Restarted with counter %hhu\n", counter);
return counter;
failed_to_read:
fclose(f);
umask(umask_was);
LOGP(DGTPHUB, LOGL_FATAL, "Restart counter file cannot be parsed:"
" %s\n", path);
exit(1);
failed_to_write:
if (f)
fclose(f);
umask(umask_was);
LOGP(DGTPHUB, LOGL_FATAL, "Restart counter file cannot be written:"
" %s\n", path);
exit(1);
}
static void print_help(struct cmdline_cfg *ccfg)
{
printf("gtphub commandline options\n");
printf(" -h,--help This text.\n");
printf(" -D,--daemonize Fork the process into a background daemon.\n");
printf(" -d,--debug <cat> Enable Debugging for this category.\n");
printf(" Pass '-d list' to get a category listing.\n");
printf(" -s,--disable-color\n");
printf(" -c,--config-file <path> The config file to use [%s].\n",
ccfg->config_file);
printf(" -e,--log-level <nr> Set a global log level.\n");
printf(" -r,--restart-file <path> File for counting restarts [%s].\n",
ccfg->restart_counter_file);
}
static void list_categories(void)
{
printf("Avaliable debug categories:\n");
int i;
for (i = 0; i < gtphub_log_info.num_cat; ++i) {
if (!gtphub_log_info.cat[i].name)
continue;
printf("%s\n", gtphub_log_info.cat[i].name);
}
}
static void handle_options(struct cmdline_cfg *ccfg, int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"debug", 1, 0, 'd'},
{"daemonize", 0, 0, 'D'},
{"config-file", 1, 0, 'c'},
{"disable-color", 0, 0, 's'},
{"timestamp", 0, 0, 'T'},
{"log-level", 1, 0, 'e'},
{"restart-file", 1, 0, 'r'},
{NULL, 0, 0, 0}
};
c = getopt_long(argc, argv, "hd:Dc:sTe:r:",
long_options, &option_index);
if (c == -1) {
if (optind < argc) {
LOGP(DGTPHUB, LOGL_FATAL,
"Excess commandline arguments ('%s').\n",
argv[optind]);
exit(2);
}
break;
}
switch (c) {
case 'h':
//print_usage();
print_help(ccfg);
exit(0);
case 's':
log_set_use_color(osmo_stderr_target, 0);
break;
case 'd':
if (strcmp("list", optarg) == 0) {
list_categories();
exit(0);
} else
log_parse_category_mask(osmo_stderr_target, optarg);
break;
case 'D':
ccfg->daemonize = 1;
break;
case 'c':
ccfg->config_file = optarg;
break;
case 'T':
log_set_print_timestamp(osmo_stderr_target, 1);
break;
case 'e':
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
case 'r':
ccfg->restart_counter_file = optarg;
break;
default:
LOGP(DGTPHUB, LOGL_FATAL, "Invalid command line argument, abort.\n");
exit(1);
break;
}
}
}
int main(int argc, char **argv)
{
int rc;
struct cmdline_cfg _ccfg;
struct cmdline_cfg *ccfg = &_ccfg;
memset(ccfg, '\0', sizeof(*ccfg));
ccfg->config_file = "./gtphub.conf";
ccfg->restart_counter_file = "./gtphub_restart_count";
struct gtphub_cfg _cfg;
struct gtphub_cfg *cfg = &_cfg;
memset(cfg, '\0', sizeof(*cfg));
struct gtphub _hub;
struct gtphub *hub = &_hub;
osmo_gtphub_ctx = talloc_named_const(NULL, 0, "osmo_gtphub");
msgb_talloc_ctx_init(osmo_gtphub_ctx, 0);
signal(SIGINT, &signal_handler);
signal(SIGTERM, &signal_handler);
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
osmo_init_logging(&gtphub_log_info);
vty_info.copyright = gtphub_copyright;
vty_init(&vty_info);
logging_vty_add_cmds(NULL);
gtphub_vty_init(hub, cfg);
rate_ctr_init(osmo_gtphub_ctx);
handle_options(ccfg, argc, argv);
rc = gtphub_cfg_read(cfg, ccfg->config_file);
if (rc < 0) {
LOGP(DGTPHUB, LOGL_FATAL, "Cannot parse config file '%s'\n",
ccfg->config_file);
exit(2);
}
/* start telnet after reading config for vty_get_bind_addr() */
rc = telnet_init_dynif(osmo_gtphub_ctx, 0, vty_get_bind_addr(),
OSMO_VTY_PORT_GTPHUB);
if (rc < 0)
exit(1);
if (gtphub_start(hub, cfg,
next_restart_count(ccfg->restart_counter_file))
!= 0)
return -1;
log_cfg(cfg);
if (ccfg->daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
LOGP(DGTPHUB, LOGL_FATAL, "Error during daemonize");
exit(1);
}
}
while (1) {
rc = osmo_select_main(0);
if (rc < 0)
exit(3);
}
/* not reached */
exit(0);
}

View File

@ -1,60 +0,0 @@
/* GTP Hub Implementation */
/* (C) 2015 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* gtphub_sock.c.
*
* This file is kept separate so that these functions can be wrapped for
* gtphub_test.c. When a function and its callers are in the same compilational
* unit, the wrappability may be optimized away.
*
* Author: Neels Hofmeyr
*
* 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/gtphub.h>
#include <openbsc/debug.h>
/* Convenience makro, note: only within this C file. */
#define LOG(level, fmt, args...) \
LOGP(DGTPHUB, level, fmt, ##args)
int gtphub_write(const struct osmo_fd *to,
const struct osmo_sockaddr *to_addr,
const uint8_t *buf, size_t buf_len)
{
errno = 0;
ssize_t sent = sendto(to->fd, buf, buf_len, 0,
(struct sockaddr*)&to_addr->a, to_addr->l);
LOG(LOGL_DEBUG, "to %s\n", osmo_sockaddr_to_str(to_addr));
if (sent == -1) {
LOG(LOGL_ERROR, "error: %s\n", strerror(errno));
return -EINVAL;
}
if (sent != buf_len)
LOG(LOGL_ERROR, "sent(%d) != data_len(%d)\n",
(int)sent, (int)buf_len);
else
LOG(LOGL_DEBUG, "Sent %d: %s%s\n",
(int)sent,
osmo_hexdump(buf, sent > 1000? 1000 : sent),
sent > 1000 ? "..." : "");
return 0;
}

View File

@ -1,613 +0,0 @@
/* (C) 2015 by sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* Author: Neels Hofmeyr
*
* 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 <string.h>
#include <inttypes.h>
#include <ares.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osmocom/core/talloc.h>
#include <osmocom/vty/command.h>
#include <osmocom/vty/misc.h>
#include <openbsc/vty.h>
#include <openbsc/gtphub.h>
/* TODO split GRX ares from sgsn into a separate struct and allow use without
* globals. */
#include <openbsc/sgsn.h>
extern struct sgsn_instance *sgsn;
static struct gtphub *g_hub = 0;
static struct gtphub_cfg *g_cfg = 0;
static struct cmd_node gtphub_node = {
GTPHUB_NODE,
"%s(config-gtphub)# ",
1,
};
#define GTPH_DEFAULT_CONTROL_PORT 2123
#define GTPH_DEFAULT_USER_PORT 2152
static void write_addrs(struct vty *vty, const char *name,
struct gtphub_cfg_addr *c, struct gtphub_cfg_addr *u)
{
if ((c->port == GTPH_DEFAULT_CONTROL_PORT)
&& (u->port == GTPH_DEFAULT_USER_PORT)
&& (strcmp(c->addr_str, u->addr_str) == 0)) {
/* Default port numbers and same IP address: write "short"
* variant. */
vty_out(vty, " %s %s%s",
name,
c->addr_str,
VTY_NEWLINE);
return;
}
vty_out(vty, " %s ctrl %s %d user %s %d%s",
name,
c->addr_str, (int)c->port,
u->addr_str, (int)u->port,
VTY_NEWLINE);
struct ares_addr_node *server;
for (server = sgsn->ares_servers; server; server = server->next)
vty_out(vty, " grx-dns-add %s%s", inet_ntoa(server->addr.addr4), VTY_NEWLINE);
}
static int config_write_gtphub(struct vty *vty)
{
vty_out(vty, "gtphub%s", VTY_NEWLINE);
write_addrs(vty, "bind-to-sgsns",
&g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind,
&g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind);
write_addrs(vty, "bind-to-ggsns",
&g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind,
&g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind);
if (g_cfg->sgsn_use_sender) {
vty_out(vty, "sgsn-use-sender%s", VTY_NEWLINE);
}
if (g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str) {
write_addrs(vty, "sgsn-proxy",
&g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL],
&g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER]);
}
if (g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str) {
write_addrs(vty, "ggsn-proxy",
&g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL],
&g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER]);
}
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub, cfg_gtphub_cmd,
"gtphub",
"Configure the GTP hub\n")
{
vty->node = GTPHUB_NODE;
return CMD_SUCCESS;
}
#define BIND_ARGS "ctrl ADDR <0-65535> user ADDR <0-65535>"
#define BIND_DOCS \
"Set GTP-C bind\n" \
"GTP-C local IP address (v4 or v6)\n" \
"GTP-C local port\n" \
"Set GTP-U bind\n" \
"GTP-U local IP address (v4 or v6)\n" \
"GTP-U local port\n"
DEFUN(cfg_gtphub_bind_to_sgsns_short, cfg_gtphub_bind_to_sgsns_short_cmd,
"bind-to-sgsns ADDR",
"GTP Hub Parameters\n"
"Set the local bind address to listen for SGSNs, for both GTP-C and GTP-U\n"
"Local IP address (v4 or v6)\n"
)
{
int i;
for_each_plane(i)
g_cfg->to_gsns[GTPH_SIDE_SGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT;
g_cfg->to_gsns[GTPH_SIDE_SGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT;
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub_bind_to_ggsns_short, cfg_gtphub_bind_to_ggsns_short_cmd,
"bind-to-ggsns ADDR",
"GTP Hub Parameters\n"
"Set the local bind address to listen for GGSNs, for both GTP-C and GTP-U\n"
"Local IP address (v4 or v6)\n"
)
{
int i;
for_each_plane(i)
g_cfg->to_gsns[GTPH_SIDE_GGSN][i].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].bind.port = GTPH_DEFAULT_CONTROL_PORT;
g_cfg->to_gsns[GTPH_SIDE_GGSN][GTPH_PLANE_USER].bind.port = GTPH_DEFAULT_USER_PORT;
return CMD_SUCCESS;
}
static int handle_binds(struct gtphub_cfg_bind *b, const char **argv)
{
b[GTPH_PLANE_CTRL].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
b[GTPH_PLANE_CTRL].bind.port = atoi(argv[1]);
b[GTPH_PLANE_USER].bind.addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
b[GTPH_PLANE_USER].bind.port = atoi(argv[3]);
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub_bind_to_sgsns, cfg_gtphub_bind_to_sgsns_cmd,
"bind-to-sgsns " BIND_ARGS,
"GTP Hub Parameters\n"
"Set the local bind addresses and ports to listen for SGSNs\n"
BIND_DOCS
)
{
return handle_binds(g_cfg->to_gsns[GTPH_SIDE_SGSN], argv);
}
DEFUN(cfg_gtphub_bind_to_ggsns, cfg_gtphub_bind_to_ggsns_cmd,
"bind-to-ggsns " BIND_ARGS,
"GTP Hub Parameters\n"
"Set the local bind addresses and ports to listen for GGSNs\n"
BIND_DOCS
)
{
return handle_binds(g_cfg->to_gsns[GTPH_SIDE_GGSN], argv);
}
DEFUN(cfg_gtphub_ggsn_proxy_short, cfg_gtphub_ggsn_proxy_short_cmd,
"ggsn-proxy ADDR",
"GTP Hub Parameters\n"
"Redirect all GGSN bound traffic to default ports on this address (another gtphub)\n"
"Remote IP address (v4 or v6)\n"
)
{
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT;
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT;
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub_ggsn_proxy, cfg_gtphub_ggsn_proxy_cmd,
"ggsn-proxy " BIND_ARGS,
"GTP Hub Parameters\n"
"Redirect all GGSN bound traffic to these addresses and ports (another gtphub)\n"
BIND_DOCS
)
{
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]);
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
g_cfg->proxy[GTPH_SIDE_GGSN][GTPH_PLANE_USER].port = atoi(argv[3]);
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub_sgsn_proxy_short, cfg_gtphub_sgsn_proxy_short_cmd,
"sgsn-proxy ADDR",
"GTP Hub Parameters\n"
"Redirect all SGSN bound traffic to default ports on this address (another gtphub)\n"
"Remote IP address (v4 or v6)\n"
)
{
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = GTPH_DEFAULT_CONTROL_PORT;
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = GTPH_DEFAULT_USER_PORT;
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub_sgsn_proxy, cfg_gtphub_sgsn_proxy_cmd,
"sgsn-proxy " BIND_ARGS,
"GTP Hub Parameters\n"
"Redirect all SGSN bound traffic to these addresses and ports (another gtphub)\n"
BIND_DOCS
)
{
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].addr_str = talloc_strdup(tall_vty_ctx, argv[0]);
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_CTRL].port = atoi(argv[1]);
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].addr_str = talloc_strdup(tall_vty_ctx, argv[2]);
g_cfg->proxy[GTPH_SIDE_SGSN][GTPH_PLANE_USER].port = atoi(argv[3]);
return CMD_SUCCESS;
}
#define SGSN_USE_SENDER_STR \
"Ignore SGSN's Address IEs, use sender address and port (useful over NAT)\n"
DEFUN(cfg_gtphub_sgsn_use_sender,
cfg_gtphub_sgsn_use_sender_cmd,
"sgsn-use-sender",
SGSN_USE_SENDER_STR)
{
g_cfg->sgsn_use_sender = 1;
return CMD_SUCCESS;
}
DEFUN(cfg_gtphub_no_sgsn_use_sender,
cfg_gtphub_no_sgsn_use_sender_cmd,
"no sgsn-use-sender",
NO_STR SGSN_USE_SENDER_STR)
{
g_cfg->sgsn_use_sender = 0;
return CMD_SUCCESS;
}
/* Copied from sgsn_vty.h */
DEFUN(cfg_grx_ggsn, cfg_grx_ggsn_cmd,
"grx-dns-add A.B.C.D",
"Add DNS server\nIPv4 address\n")
{
struct ares_addr_node *node = talloc_zero(tall_bsc_ctx, struct ares_addr_node);
node->family = AF_INET;
inet_aton(argv[0], &node->addr.addr4);
node->next = sgsn->ares_servers;
sgsn->ares_servers = node;
return CMD_SUCCESS;
}
static void show_bind_stats_all(struct vty *vty)
{
int plane_idx;
for_each_plane(plane_idx) {
vty_out(vty, "- %s Plane:%s",
gtphub_plane_idx_names[plane_idx], VTY_NEWLINE);
int side_idx;
for_each_side(side_idx) {
struct gtphub_bind *b = &g_hub->to_gsns[side_idx][plane_idx];
vty_out(vty, " - local addr to/from %ss: %s port %d%s",
gtphub_side_idx_names[side_idx],
gsn_addr_to_str(&b->local_addr), (int)b->local_port,
VTY_NEWLINE);
vty_out_rate_ctr_group(vty, " ", b->counters_io);
}
}
}
static void show_tunnel_stats(struct vty *vty, struct gtphub_tunnel *tun)
{
int plane_idx;
for_each_plane(plane_idx) {
vty_out(vty, "- %s Plane:%s",
gtphub_plane_idx_names[plane_idx], VTY_NEWLINE);
int side_idx;
for_each_side(side_idx) {
struct gtphub_tunnel_endpoint *te = &tun->endpoint[side_idx][plane_idx];
vty_out(vty, " - to/from %s:%s",
gtphub_side_idx_names[side_idx],
VTY_NEWLINE);
vty_out_rate_ctr_group(vty, " ", te->counters_io);
}
}
}
static void show_peer_summary(struct vty *vty, const char *prefix,
int side_idx, int plane_idx,
struct gtphub_peer *p, int with_io_stats)
{
struct gtphub_peer_addr *pa;
int p2l = strlen(prefix) + 4 + 1;
char prefix2[p2l];
memset(prefix2, ' ', p2l - 1);
prefix2[p2l - 1] = '\0';
if (with_io_stats) {
llist_for_each_entry(pa, &p->addresses, entry) {
vty_out(vty, "%s- %s %s %s%s", prefix,
gtphub_side_idx_names[side_idx],
gtphub_plane_idx_names[plane_idx],
gsn_addr_to_str(&pa->addr),
VTY_NEWLINE);
struct gtphub_peer_port *pp;
llist_for_each_entry(pp, &pa->ports, entry) {
vty_out(vty, "%s Port %" PRIu16 "%s", prefix, pp->port, VTY_NEWLINE);
vty_out_rate_ctr_group(vty, prefix2, pp->counters_io);
}
}
} else {
llist_for_each_entry(pa, &p->addresses, entry) {
vty_out(vty, "%s- %s %s %s", prefix,
gtphub_side_idx_names[side_idx],
gtphub_plane_idx_names[plane_idx],
gsn_addr_to_str(&pa->addr));
struct gtphub_peer_port *pp;
llist_for_each_entry(pp, &pa->ports, entry) {
vty_out(vty, ":%" PRIu16, pp->port);
}
vty_out(vty, VTY_NEWLINE);
}
}
}
static void show_peers_summary(struct vty *vty)
{
int side_idx;
int plane_idx;
int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}};
for_each_side(side_idx) {
for_each_plane(plane_idx) {
struct gtphub_peer *p;
llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) {
count[side_idx][plane_idx] ++;
}
}
}
vty_out(vty, "Peers Count:%s", VTY_NEWLINE);
for_each_side_and_plane(side_idx, plane_idx) {
vty_out(vty, " %s %s peers: %d%s",
gtphub_side_idx_names[side_idx],
gtphub_plane_idx_names[plane_idx],
count[side_idx][plane_idx],
VTY_NEWLINE);
}
}
static void show_peers_all(struct vty *vty, int with_io_stats)
{
int side_idx;
int plane_idx;
int count[GTPH_SIDE_N][GTPH_PLANE_N] = {{0}};
vty_out(vty, "All Peers%s%s",
with_io_stats? " with I/O stats" : "",
VTY_NEWLINE);
for_each_side(side_idx) {
vty_out(vty, "- %s%s", gtphub_side_idx_names[side_idx], VTY_NEWLINE);
for_each_plane(plane_idx) {
struct gtphub_peer *p;
llist_for_each_entry(p, &g_hub->to_gsns[side_idx][plane_idx].peers, entry) {
count[side_idx][plane_idx] ++;
show_peer_summary(vty, " ", side_idx, plane_idx, p, with_io_stats);
}
}
}
for_each_side_and_plane(side_idx, plane_idx) {
vty_out(vty, "%s %s peers: %d%s",
gtphub_side_idx_names[side_idx],
gtphub_plane_idx_names[plane_idx],
count[side_idx][plane_idx],
VTY_NEWLINE);
}
}
static void show_tunnels_summary(struct vty *vty)
{
time_t now = gtphub_now();
const int w = 36;
int max_expiry = g_hub->expire_slowly.expiry_in_seconds;
float seconds_per_step = ((float)max_expiry) / w;
/* Print TEI mapping expiry in an ASCII histogram, like:
TEI map summary
Legend: '_'=0 '.'<=1% ':'<=2% '|'<=10% '#'>10% (10.0 m/step)
CTRL: 30 mappings, valid for 360m[# :. | . : . ]1m
USER: 30 mappings, valid for 360m[# :. | . : . ]1m
4 TEI mappings in total, last expiry in 359.4 min
*/
vty_out(vty,
"Tunnels summary%s"
" Legend: ' '=0 '.'<=1%% ':'<=2%% '|'<=10%% '#'>10%% (%.1f m/step)%s",
VTY_NEWLINE,
seconds_per_step / 60.,
VTY_NEWLINE);
int last_expiry = 0;
unsigned int count = 0;
int histogram[w];
memset(histogram, 0, sizeof(histogram));
struct gtphub_tunnel *t;
llist_for_each_entry(t, &g_hub->tunnels, entry) {
count ++;
int expiry = t->expiry_entry.expiry - now;
last_expiry = (last_expiry > expiry) ? last_expiry : expiry;
int hi = ((float)expiry) / seconds_per_step;
if (hi < 0)
hi = 0;
if (hi > (w - 1))
hi = w - 1;
histogram[hi] ++;
}
vty_out(vty,
" %u tunnels, valid for %dm[",
count, max_expiry / 60);
int i;
for (i = w - 1; i >= 0; i--) {
char c;
int val = histogram[i];
int percent = 100. * val / count;
if (!val)
c = ' ';
else if (percent <= 1)
c = '.';
else if (percent <= 2)
c = ':';
else if (percent <= 10)
c = '|';
else c = '#';
vty_out(vty, "%c", c);
}
vty_out(vty, "]1m%s", VTY_NEWLINE);
vty_out(vty, " last expiry in %.1f min%s",
((float)last_expiry) / 60.,
VTY_NEWLINE);
}
static void show_tunnels_all(struct vty *vty, int with_io_stats)
{
time_t now = gtphub_now();
vty_out(vty, "All tunnels%s:%s"
"Legend: TEI=<hex>: SGSN <-> GGSN (expiry in minutes), with each:%s"
" <IP-Ctrl>[/<IP-User>] (TEI C=<TEI-Ctrl-hex> U=<TEI-User-hex>)%s",
with_io_stats? "with I/O stats" : "",
VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE);
unsigned int count = 0;
unsigned int incomplete = 0;
struct gtphub_tunnel *tun;
llist_for_each_entry(tun, &g_hub->tunnels, entry) {
vty_out(vty,
"%s (expiry in %dm)%s",
gtphub_tunnel_str(tun),
(int)((tun->expiry_entry.expiry - now) / 60),
VTY_NEWLINE);
count ++;
if (!gtphub_tunnel_complete(tun))
incomplete ++;
if (with_io_stats)
show_tunnel_stats(vty, tun);
}
vty_out(vty, "Total: %u tunnels (of which %u incomplete)%s",
count, incomplete, VTY_NEWLINE);
}
#define SHOW_GTPHUB_STRS SHOW_STR "Show info on running GTP hub\n"
#define SHOW_GTPHUB_PEERS_STRS SHOW_GTPHUB_STRS "Active peers\n"
#define SHOW_GTPHUB_TUNS_STRS SHOW_GTPHUB_STRS "Active tunnels\n"
DEFUN(show_gtphub_peers_summary, show_gtphub_peers_summary_cmd, "show gtphub peers summary",
SHOW_GTPHUB_PEERS_STRS "Summary of all peers\n")
{
show_peers_summary(vty);
return CMD_SUCCESS;
}
DEFUN(show_gtphub_peers_list, show_gtphub_peers_list_cmd, "show gtphub peers list",
SHOW_GTPHUB_PEERS_STRS "List all peers\n")
{
show_peers_all(vty, 0);
return CMD_SUCCESS;
}
DEFUN(show_gtphub_peers_stats, show_gtphub_peers_stats_cmd, "show gtphub peers stats",
SHOW_GTPHUB_PEERS_STRS "List all peers with I/O stats\n")
{
show_peers_all(vty, 1);
return CMD_SUCCESS;
}
DEFUN(show_gtphub_tunnels_summary, show_gtphub_tunnels_summary_cmd, "show gtphub tunnels summary",
SHOW_GTPHUB_TUNS_STRS "Summary of all tunnels\n")
{
show_tunnels_summary(vty);
return CMD_SUCCESS;
}
DEFUN(show_gtphub_tunnels_list, show_gtphub_tunnels_list_cmd, "show gtphub tunnels list",
SHOW_GTPHUB_TUNS_STRS "List all tunnels\n")
{
show_tunnels_all(vty, 0);
return CMD_SUCCESS;
}
DEFUN(show_gtphub_tunnels_stats, show_gtphub_tunnels_stats_cmd, "show gtphub tunnels stats",
SHOW_GTPHUB_TUNS_STRS "List all tunnels with I/O stats\n")
{
show_tunnels_all(vty, 1);
return CMD_SUCCESS;
}
DEFUN(show_gtphub, show_gtphub_cmd, "show gtphub all",
SHOW_GTPHUB_STRS "Summarize everything about the GTP hub\n")
{
show_bind_stats_all(vty);
show_peers_summary(vty);
show_tunnels_summary(vty);
return CMD_SUCCESS;
}
int gtphub_vty_init(struct gtphub *global_hub, struct gtphub_cfg *global_cfg)
{
g_hub = global_hub;
g_cfg = global_cfg;
install_element_ve(&show_gtphub_cmd);
install_element_ve(&show_gtphub_peers_summary_cmd);
install_element_ve(&show_gtphub_peers_list_cmd);
install_element_ve(&show_gtphub_peers_stats_cmd);
install_element_ve(&show_gtphub_tunnels_summary_cmd);
install_element_ve(&show_gtphub_tunnels_list_cmd);
install_element_ve(&show_gtphub_tunnels_stats_cmd);
install_element(CONFIG_NODE, &cfg_gtphub_cmd);
install_node(&gtphub_node, config_write_gtphub);
vty_install_default(GTPHUB_NODE);
install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_short_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_sgsns_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_short_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_bind_to_ggsns_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_short_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_ggsn_proxy_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_short_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_proxy_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_sgsn_use_sender_cmd);
install_element(GTPHUB_NODE, &cfg_gtphub_no_sgsn_use_sender_cmd);
install_element(GTPHUB_NODE, &cfg_grx_ggsn_cmd);
return 0;
}
int gtphub_cfg_read(struct gtphub_cfg *cfg, const char *config_file)
{
int rc;
rc = vty_read_config_file(config_file, NULL);
if (rc < 0) {
fprintf(stderr, "Failed to parse the config file: '%s'\n", config_file);
return rc;
}
return 0;
}

View File

@ -1,23 +0,0 @@
!
! Osmocom SGSN (0.9.0.474-0ede2) configuration saved from vty
!!
!
line vty
no login
!
sgsn
gtp local-ip 192.168.100.11
ggsn 0 remote-ip 192.168.100.239
ggsn 0 gtp-version 1
ns
timer tns-block 3
timer tns-block-retries 3
timer tns-reset 3
timer tns-reset-retries 3
timer tns-test 30
timer tns-alive 3
timer tns-alive-retries 10
encapsulation udp local-ip 192.168.100.11
encapsulation udp local-port 23000
encapsulation framerelay-gre enabled 0
bssgp

View File

@ -1,173 +0,0 @@
/* C-ARES DNS resolver integration */
/*
* (C) 2015 by Holger Hans Peter Freyther
* 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/sgsn.h>
#include <openbsc/debug.h>
#include <netdb.h>
struct cares_event_fd {
struct llist_head head;
struct osmo_fd fd;
};
struct cares_cb_data {
ares_host_callback cb;
void *data;
};
static void osmo_ares_reschedule(struct sgsn_instance *sgsn);
static void ares_cb(void *_arg, int status, int timeouts, struct hostent *hostent)
{
struct cares_cb_data *arg = _arg;
arg->cb(arg->data, status, timeouts, hostent);
osmo_ares_reschedule(sgsn);
talloc_free(arg);
}
static int ares_osmo_fd_cb(struct osmo_fd *fd, unsigned int what)
{
LOGP(DGPRS, LOGL_DEBUG, "C-ares fd(%d) ready(%d)\n", fd->fd, what);
ares_process_fd(sgsn->ares_channel,
(what & BSC_FD_READ) ? fd->fd : ARES_SOCKET_BAD,
(what & BSC_FD_WRITE) ? fd->fd : ARES_SOCKET_BAD);
osmo_ares_reschedule(sgsn);
return 0;
}
static void ares_timeout_cb(void *data)
{
struct sgsn_instance *sgsn = data;
LOGP(DGPRS, LOGL_DEBUG, "C-ares triggering timeout\n");
ares_process_fd(sgsn->ares_channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
osmo_ares_reschedule(sgsn);
}
static void osmo_ares_reschedule(struct sgsn_instance *sgsn)
{
struct timeval *timeout, tv;
osmo_timer_del(&sgsn->ares_timer);
timeout = ares_timeout(sgsn->ares_channel, NULL, &tv);
if (timeout) {
LOGP(DGPRS, LOGL_DEBUG, "C-ares scheduling timeout %llu.%llu\n",
(unsigned long long) tv.tv_sec,
(unsigned long long) tv.tv_usec);
osmo_timer_setup(&sgsn->ares_timer, ares_timeout_cb, sgsn);
osmo_timer_schedule(&sgsn->ares_timer, tv.tv_sec, tv.tv_usec);
}
}
static void setup_ares_osmo_fd(void *data, int fd, int read, int write)
{
struct cares_event_fd *ufd, *tmp;
/* delete the entry */
if (read == 0 && write == 0) {
llist_for_each_entry_safe(ufd, tmp, &sgsn->ares_fds, head) {
if (ufd->fd.fd != fd)
continue;
LOGP(DGPRS, LOGL_DEBUG,
"Removing C-ares watched fd (%d)\n", fd);
osmo_fd_unregister(&ufd->fd);
llist_del(&ufd->head);
talloc_free(ufd);
return;
}
}
/* Search for the fd or create a new one */
llist_for_each_entry(ufd, &sgsn->ares_fds, head) {
if (ufd->fd.fd != fd)
continue;
LOGP(DGPRS, LOGL_DEBUG, "Updating C-ares fd (%d)\n", fd);
goto update_fd;
}
LOGP(DGPRS, LOGL_DEBUG, "Registering C-ares fd (%d)\n", fd);
ufd = talloc_zero(tall_bsc_ctx, struct cares_event_fd);
ufd->fd.fd = fd;
ufd->fd.cb = ares_osmo_fd_cb;
ufd->fd.data = data;
if (osmo_fd_register(&ufd->fd) != 0)
LOGP(DGPRS, LOGL_ERROR, "Failed to register C-ares fd (%d)\n", fd);
llist_add(&ufd->head, &sgsn->ares_fds);
update_fd:
if (read)
ufd->fd.when |= BSC_FD_READ;
else
ufd->fd.when &= ~BSC_FD_READ;
if (write)
ufd->fd.when |= BSC_FD_WRITE;
else
ufd->fd.when &= ~BSC_FD_WRITE;
osmo_ares_reschedule(sgsn);
}
int sgsn_ares_query(struct sgsn_instance *sgsn, const char *name,
ares_host_callback cb, void *data)
{
struct cares_cb_data *cb_data;
cb_data = talloc_zero(tall_bsc_ctx, struct cares_cb_data);
cb_data->cb = cb;
cb_data->data = data;
ares_gethostbyname(sgsn->ares_channel, name, AF_INET, ares_cb, cb_data);
osmo_ares_reschedule(sgsn);
return 0;
}
int sgsn_ares_init(struct sgsn_instance *sgsn)
{
struct ares_options options;
int optmask;
int rc;
INIT_LLIST_HEAD(&sgsn->ares_fds);
memset(&options, 0, sizeof(options));
options.sock_state_cb = setup_ares_osmo_fd;
options.sock_state_cb_data = sgsn;
optmask = ARES_OPT_FLAGS | ARES_OPT_SOCK_STATE_CB | ARES_OPT_DOMAINS;
if (sgsn->ares_servers)
optmask |= ARES_OPT_SERVERS;
ares_library_init(ARES_LIB_INIT_ALL);
rc = ares_init_options(&sgsn->ares_channel, &options, optmask);
if (rc != ARES_SUCCESS)
return rc;
if (sgsn->ares_servers)
rc = ares_set_servers(sgsn->ares_channel, sgsn->ares_servers);
return rc;
}
osmo_static_assert(ARES_SUCCESS == 0, ares_success_zero);

View File

@ -1,312 +0,0 @@
/* MS authorization and subscriber data handling */
/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/core/utils.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/gprs_subscriber.h>
#include <openbsc/debug.h>
const struct value_string auth_state_names[] = {
{ SGSN_AUTH_ACCEPTED, "accepted"},
{ SGSN_AUTH_REJECTED, "rejected"},
{ SGSN_AUTH_UNKNOWN, "unknown"},
{ SGSN_AUTH_AUTHENTICATE, "authenticate" },
{ SGSN_AUTH_UMTS_RESYNC, "UMTS-resync" },
{ 0, NULL }
};
const struct value_string *sgsn_auth_state_names = auth_state_names;
void sgsn_auth_init(void)
{
INIT_LLIST_HEAD(&sgsn->cfg.imsi_acl);
}
/* temporary IMSI ACL hack */
struct imsi_acl_entry *sgsn_acl_lookup(const char *imsi, struct sgsn_config *cfg)
{
struct imsi_acl_entry *acl;
llist_for_each_entry(acl, &cfg->imsi_acl, list) {
if (!strcmp(imsi, acl->imsi))
return acl;
}
return NULL;
}
int sgsn_acl_add(const char *imsi, struct sgsn_config *cfg)
{
struct imsi_acl_entry *acl;
if (sgsn_acl_lookup(imsi, cfg))
return -EEXIST;
acl = talloc_zero(NULL, struct imsi_acl_entry);
if (!acl)
return -ENOMEM;
osmo_strlcpy(acl->imsi, imsi, sizeof(acl->imsi));
llist_add(&acl->list, &cfg->imsi_acl);
return 0;
}
int sgsn_acl_del(const char *imsi, struct sgsn_config *cfg)
{
struct imsi_acl_entry *acl;
acl = sgsn_acl_lookup(imsi, cfg);
if (!acl)
return -ENODEV;
llist_del(&acl->list);
talloc_free(acl);
return 0;
}
enum sgsn_auth_state sgsn_auth_state(struct sgsn_mm_ctx *mmctx)
{
char mccmnc[16];
int check_net = 0;
int check_acl = 0;
OSMO_ASSERT(mmctx);
switch (sgsn->cfg.auth_policy) {
case SGSN_AUTH_POLICY_OPEN:
return SGSN_AUTH_ACCEPTED;
case SGSN_AUTH_POLICY_CLOSED:
check_net = 1;
check_acl = 1;
break;
case SGSN_AUTH_POLICY_ACL_ONLY:
check_acl = 1;
break;
case SGSN_AUTH_POLICY_REMOTE:
if (!mmctx->subscr)
return mmctx->auth_state;
if (mmctx->subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING_MASK)
return mmctx->auth_state;
if (sgsn->cfg.require_authentication &&
(!mmctx->is_authenticated ||
mmctx->subscr->sgsn_data->auth_triplets_updated))
return SGSN_AUTH_AUTHENTICATE;
if (mmctx->subscr->authorized)
return SGSN_AUTH_ACCEPTED;
return SGSN_AUTH_REJECTED;
}
if (!strlen(mmctx->imsi)) {
LOGMMCTXP(LOGL_NOTICE, mmctx,
"Missing IMSI, authorization state not known\n");
return SGSN_AUTH_UNKNOWN;
}
if (check_net) {
/* We simply assume that the IMSI exists, as long as it is part
* of 'our' network */
snprintf(mccmnc, sizeof(mccmnc), "%03d%02d",
mmctx->ra.mcc, mmctx->ra.mnc);
if (strncmp(mccmnc, mmctx->imsi, 5) == 0)
return SGSN_AUTH_ACCEPTED;
}
if (check_acl && sgsn_acl_lookup(mmctx->imsi, &sgsn->cfg))
return SGSN_AUTH_ACCEPTED;
return SGSN_AUTH_REJECTED;
}
/*
* This function is directly called by e.g. the GMM layer. It returns either
* after calling sgsn_auth_update directly or after triggering an asynchronous
* procedure which will call sgsn_auth_update later on.
*/
int sgsn_auth_request(struct sgsn_mm_ctx *mmctx)
{
struct gprs_subscr *subscr;
struct gsm_auth_tuple *at;
int need_update_location;
int rc;
LOGMMCTXP(LOGL_DEBUG, mmctx, "Requesting authorization\n");
if (sgsn->cfg.auth_policy != SGSN_AUTH_POLICY_REMOTE) {
sgsn_auth_update(mmctx);
return 0;
}
need_update_location = sgsn->cfg.require_update_location &&
(mmctx->subscr == NULL ||
mmctx->pending_req == GSM48_MT_GMM_ATTACH_REQ);
/* This has the side effect of registering the subscr with the mmctx */
subscr = gprs_subscr_get_or_create_by_mmctx(mmctx);
gprs_subscr_put(subscr);
OSMO_ASSERT(mmctx->subscr != NULL);
if (sgsn->cfg.require_authentication && !mmctx->is_authenticated) {
/* Find next tuple */
at = sgsn_auth_get_tuple(mmctx, mmctx->auth_triplet.key_seq);
if (!at) {
/* No valid tuple found, request fresh ones */
mmctx->auth_triplet.key_seq = GSM_KEY_SEQ_INVAL;
LOGMMCTXP(LOGL_INFO, mmctx,
"Requesting authentication tuples\n");
rc = gprs_subscr_request_auth_info(mmctx, NULL, NULL);
if (rc >= 0)
return 0;
return rc;
}
mmctx->auth_triplet = *at;
} else if (need_update_location) {
LOGMMCTXP(LOGL_INFO, mmctx,
"Missing information, requesting subscriber data\n");
rc = gprs_subscr_request_update_location(mmctx);
if (rc >= 0)
return 0;
return rc;
}
sgsn_auth_update(mmctx);
return 0;
}
void sgsn_auth_update(struct sgsn_mm_ctx *mmctx)
{
enum sgsn_auth_state auth_state;
struct gprs_subscr *subscr = mmctx->subscr;
struct gsm_auth_tuple *at;
int gmm_cause;
auth_state = sgsn_auth_state(mmctx);
LOGMMCTXP(LOGL_DEBUG, mmctx, "Updating authorization (%s -> %s)\n",
get_value_string(sgsn_auth_state_names, mmctx->auth_state),
get_value_string(sgsn_auth_state_names, auth_state));
if (auth_state == SGSN_AUTH_UNKNOWN && subscr &&
!(subscr->flags & GPRS_SUBSCRIBER_UPDATE_PENDING_MASK)) {
/* Reject requests if gprs_subscr_request_update_location fails */
LOGMMCTXP(LOGL_ERROR, mmctx,
"Missing information, authorization not possible\n");
auth_state = SGSN_AUTH_REJECTED;
}
if (auth_state == SGSN_AUTH_AUTHENTICATE &&
mmctx->auth_triplet.key_seq == GSM_KEY_SEQ_INVAL) {
/* The current tuple is not valid, but we are possibly called
* because new auth tuples have been received */
at = sgsn_auth_get_tuple(mmctx, mmctx->auth_triplet.key_seq);
if (!at) {
LOGMMCTXP(LOGL_ERROR, mmctx,
"Missing auth tuples, authorization not possible\n");
auth_state = SGSN_AUTH_REJECTED;
} else {
mmctx->auth_triplet = *at;
}
}
if (mmctx->auth_state == auth_state)
return;
LOGMMCTXP(LOGL_INFO, mmctx, "Got authorization update: state %s -> %s\n",
get_value_string(sgsn_auth_state_names, mmctx->auth_state),
get_value_string(sgsn_auth_state_names, auth_state));
mmctx->auth_state = auth_state;
switch (auth_state) {
case SGSN_AUTH_AUTHENTICATE:
if (subscr)
subscr->sgsn_data->auth_triplets_updated = 0;
gsm0408_gprs_authenticate(mmctx);
break;
case SGSN_AUTH_ACCEPTED:
gsm0408_gprs_access_granted(mmctx);
break;
case SGSN_AUTH_REJECTED:
gmm_cause =
subscr ? subscr->sgsn_data->error_cause :
SGSN_ERROR_CAUSE_NONE;
if (subscr && (subscr->flags & GPRS_SUBSCRIBER_CANCELLED) != 0)
gsm0408_gprs_access_cancelled(mmctx, gmm_cause);
else
gsm0408_gprs_access_denied(mmctx, gmm_cause);
break;
default:
break;
}
}
struct gsm_auth_tuple *sgsn_auth_get_tuple(struct sgsn_mm_ctx *mmctx,
unsigned key_seq)
{
unsigned count;
unsigned idx;
struct gsm_auth_tuple *at = NULL;
struct sgsn_subscriber_data *sdata;
if (!mmctx->subscr)
return NULL;
if (key_seq == GSM_KEY_SEQ_INVAL)
/* Start with 0 after increment module array size */
idx = ARRAY_SIZE(sdata->auth_triplets) - 1;
else
idx = key_seq;
sdata = mmctx->subscr->sgsn_data;
/* Find next tuple */
for (count = ARRAY_SIZE(sdata->auth_triplets); count > 0; count--) {
idx = (idx + 1) % ARRAY_SIZE(sdata->auth_triplets);
if (sdata->auth_triplets[idx].key_seq == GSM_KEY_SEQ_INVAL)
continue;
if (sdata->auth_triplets[idx].use_count == 0) {
at = &sdata->auth_triplets[idx];
at->use_count = 1;
return at;
}
}
return NULL;
}

View File

@ -1,259 +0,0 @@
/* GPRS SGSN CDR dumper */
/* (C) 2015 by Holger Hans Peter Freyther
* 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/sgsn.h>
#include <openbsc/signal.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/debug.h>
#include <osmocom/gsm/apn.h>
#include <openbsc/vty.h>
#include <gtp.h>
#include <pdp.h>
#include <arpa/inet.h>
#include <time.h>
#include <stdio.h>
#include <inttypes.h>
/* TODO...avoid going through a global */
extern struct sgsn_instance *sgsn;
/**
* The CDR module will generate an entry like:
*
* IMSI, # Subscriber IMSI
* IMEI, # Subscriber IMEI
* MSISDN, # Subscriber MISDN
* Charging_Timestamp, # Event start Time
* Charging_UTC, # Time zone of event start time
* Duration, # Session DURATION
* Cell_Id, # CELL_ID
* Location_Area, # LAC
* GGSN_ADDR, # GGSN_ADDR
* SGSN_ADDR, # SGSN_ADDR
* APNI, # APNI
* PDP_ADDR, # PDP_ADDR
* VOL_IN, # VOL_IN in Bytes
* VOL_OUT, # VOL_OUT in Bytes
* CAUSE_FOR_TERM, # CAUSE_FOR_TERM
*/
static void maybe_print_header(FILE *cdr_file)
{
if (ftell(cdr_file) != 0)
return;
fprintf(cdr_file, "timestamp,imsi,imei,msisdn,cell_id,lac,hlr,event,pdp_duration,ggsn_addr,sgsn_addr,apni,eua_addr,vol_in,vol_out,charging_id\n");
}
static void cdr_log_mm(struct sgsn_instance *inst, const char *ev,
struct sgsn_mm_ctx *mmctx)
{
FILE *cdr_file;
struct tm tm;
struct timeval tv;
if (!inst->cfg.cdr.filename)
return;
cdr_file = fopen(inst->cfg.cdr.filename, "a");
if (!cdr_file) {
LOGP(DGPRS, LOGL_ERROR, "Failed to open %s\n",
inst->cfg.cdr.filename);
return;
}
maybe_print_header(cdr_file);
gettimeofday(&tv, NULL);
gmtime_r(&tv.tv_sec, &tm);
fprintf(cdr_file, "%04d%02d%02d%02d%02d%02d%03d,%s,%s,%s,%d,%d,%s,%s\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
(int)(tv.tv_usec / 1000),
mmctx->imsi,
mmctx->imei,
mmctx->msisdn,
mmctx->gb.cell_id,
mmctx->ra.lac,
mmctx->hlr,
ev);
fclose(cdr_file);
}
static void extract_eua(struct ul66_t *eua, char *eua_addr)
{
if (eua->l < 2)
return;
/* there is no addr for ETSI/PPP */
if ((eua->v[0] & 0x0F) != 1) {
strcpy(eua_addr, "ETSI");
return;
}
if (eua->v[1] == 0x21 && eua->l == 6)
inet_ntop(AF_INET, &eua->v[2], eua_addr, INET_ADDRSTRLEN);
else if (eua->v[1] == 0x57 && eua->l == 18)
inet_ntop(AF_INET6, &eua->v[2], eua_addr, INET6_ADDRSTRLEN);
else {
/* e.g. both IPv4 and IPv6 */
strcpy(eua_addr, "Unknown address");
}
}
static void cdr_log_pdp(struct sgsn_instance *inst, const char *ev,
struct sgsn_pdp_ctx *pdp)
{
FILE *cdr_file;
char apni[(pdp->lib ? pdp->lib->apn_use.l : 0) + 1];
char ggsn_addr[INET_ADDRSTRLEN + 1];
char sgsn_addr[INET_ADDRSTRLEN + 1];
char eua_addr[INET6_ADDRSTRLEN + 1];
struct tm tm;
struct timeval tv;
time_t duration;
struct timespec tp;
if (!inst->cfg.cdr.filename)
return;
memset(apni, 0, sizeof(apni));
memset(ggsn_addr, 0, sizeof(ggsn_addr));
memset(eua_addr, 0, sizeof(eua_addr));
if (pdp->lib) {
osmo_apn_to_str(apni, pdp->lib->apn_use.v, pdp->lib->apn_use.l);
inet_ntop(AF_INET, &pdp->lib->hisaddr0.s_addr, ggsn_addr, sizeof(ggsn_addr));
extract_eua(&pdp->lib->eua, eua_addr);
}
if (pdp->ggsn)
inet_ntop(AF_INET, &pdp->ggsn->gsn->gsnc.s_addr, sgsn_addr, sizeof(sgsn_addr));
cdr_file = fopen(inst->cfg.cdr.filename, "a");
if (!cdr_file) {
LOGP(DGPRS, LOGL_ERROR, "Failed to open %s\n",
inst->cfg.cdr.filename);
return;
}
maybe_print_header(cdr_file);
clock_gettime(CLOCK_MONOTONIC, &tp);
gettimeofday(&tv, NULL);
/* convert the timestamp to UTC */
gmtime_r(&tv.tv_sec, &tm);
/* Check the duration of the PDP context */
duration = tp.tv_sec - pdp->cdr_start.tv_sec;
fprintf(cdr_file,
"%04d%02d%02d%02d%02d%02d%03d,%s,%s,%s,%d,%d,%s,%s,%ld,%s,%s,%s,%s,%" PRIu64 ",%" PRIu64 ",%u\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
(int)(tv.tv_usec / 1000),
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->gb.cell_id : -1,
pdp->mm ? pdp->mm->ra.lac : -1,
pdp->mm ? pdp->mm->hlr : "N/A",
ev,
(unsigned long ) duration,
ggsn_addr,
sgsn_addr,
apni,
eua_addr,
pdp->cdr_bytes_in,
pdp->cdr_bytes_out,
pdp->cdr_charging_id);
fclose(cdr_file);
}
static void cdr_pdp_timeout(void *_data)
{
struct sgsn_pdp_ctx *pdp = _data;
cdr_log_pdp(sgsn, "pdp-periodic", pdp);
osmo_timer_schedule(&pdp->cdr_timer, sgsn->cfg.cdr.interval, 0);
}
static int handle_sgsn_sig(unsigned int subsys, unsigned int signal,
void *handler_data, void *_signal_data)
{
struct sgsn_signal_data *signal_data = _signal_data;
struct sgsn_instance *inst = handler_data;
if (subsys != SS_SGSN)
return 0;
switch (signal) {
case S_SGSN_ATTACH:
cdr_log_mm(inst, "attach", signal_data->mm);
break;
case S_SGSN_UPDATE:
cdr_log_mm(inst, "update", signal_data->mm);
break;
case S_SGSN_DETACH:
cdr_log_mm(inst, "detach", signal_data->mm);
break;
case S_SGSN_MM_FREE:
cdr_log_mm(inst, "free", signal_data->mm);
break;
case S_SGSN_PDP_ACT:
clock_gettime(CLOCK_MONOTONIC, &signal_data->pdp->cdr_start);
signal_data->pdp->cdr_charging_id = signal_data->pdp->lib->cid;
cdr_log_pdp(inst, "pdp-act", signal_data->pdp);
osmo_timer_setup(&signal_data->pdp->cdr_timer, cdr_pdp_timeout,
signal_data->pdp);
osmo_timer_schedule(&signal_data->pdp->cdr_timer, inst->cfg.cdr.interval, 0);
break;
case S_SGSN_PDP_DEACT:
cdr_log_pdp(inst, "pdp-deact", signal_data->pdp);
osmo_timer_del(&signal_data->pdp->cdr_timer);
break;
case S_SGSN_PDP_TERMINATE:
cdr_log_pdp(inst, "pdp-terminate", signal_data->pdp);
osmo_timer_del(&signal_data->pdp->cdr_timer);
break;
case S_SGSN_PDP_FREE:
cdr_log_pdp(inst, "pdp-free", signal_data->pdp);
osmo_timer_del(&signal_data->pdp->cdr_timer);
break;
}
return 0;
}
int sgsn_cdr_init(struct sgsn_instance *sgsn)
{
/* register for CDR related events */
sgsn->cfg.cdr.interval = 10 * 60;
osmo_signal_register_handler(SS_SGSN, handle_sgsn_sig, sgsn);
return 0;
}

View File

@ -1,69 +0,0 @@
/* Control Interface Implementation for the SGSN */
/*
* (C) 2014 by Holger Hans Peter Freyther
* (C) 2014 by 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 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/ctrl/control_if.h>
#include <osmocom/ctrl/control_cmd.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/sgsn.h>
#include <openbsc/debug.h>
#include <pdp.h>
extern vector ctrl_node_vec;
static int get_subscriber_list(struct ctrl_cmd *cmd, void *d)
{
struct sgsn_mm_ctx *mm;
cmd->reply = talloc_strdup(cmd, "");
llist_for_each_entry(mm, &sgsn_mm_ctxts, list) {
char *addr = NULL;
struct sgsn_pdp_ctx *pdp;
if (strlen(mm->imsi) == 0)
continue;
llist_for_each_entry(pdp, &mm->pdp_list, list)
addr = gprs_pdpaddr2str(pdp->lib->eua.v,
pdp->lib->eua.l);
cmd->reply = talloc_asprintf_append(
cmd->reply,
"%s,%s\n", mm->imsi, addr ? addr : "");
}
return CTRL_CMD_REPLY;
}
CTRL_CMD_DEFINE_RO(subscriber_list, "subscriber-list-active-v1");
int sgsn_ctrl_cmds_install(void)
{
int rc = 0;
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_list);
return rc;
}
struct ctrl_handle *sgsn_controlif_setup(struct gsm_network *net,
const char *bind_addr, uint16_t port)
{
return ctrl_interface_setup_dynip(net, bind_addr, port, NULL);
}

View File

@ -1,885 +0,0 @@
/* GPRS SGSN integration with libgtp of OpenGGSN */
/* libgtp implements the GPRS Tunelling Protocol GTP per TS 09.60 / 29.060 */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010 by On-Waves
* (C) 2015 by Holger Hans Peter Freyther
* 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 <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <signal.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#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>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <openbsc/signal.h>
#include <openbsc/debug.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/gprs_sgsn.h>
#include <openbsc/gprs_gmm.h>
#include <openbsc/gprs_subscriber.h>
#include <openbsc/gprs_sndcp.h>
#ifdef BUILD_IU
#include <osmocom/ranap/iu_client.h>
#include <osmocom/ranap/ranap_ies_defs.h>
#endif
#include <gtp.h>
#include <pdp.h>
/* TS 23.003: The MSISDN shall take the dummy MSISDN value composed of
* 15 digits set to 0 (encoded as an E.164 international number) when
* the MSISDN is not available in messages in which the presence of the
* MSISDN parameter */
static const uint8_t dummy_msisdn[] =
{ 0x91, /* No extension, international, E.164 */
0, 0, 0, 0, 0, 0, 0, /* 14 digits of zeroes */
0xF0 /* 15th digit of zero + padding */ };
const struct value_string gtp_cause_strs[] = {
{ GTPCAUSE_REQ_IMSI, "Request IMSI" },
{ GTPCAUSE_REQ_IMEI, "Request IMEI" },
{ GTPCAUSE_REQ_IMSI_IMEI, "Request IMSI and IMEI" },
{ GTPCAUSE_NO_ID_NEEDED, "No identity needed" },
{ GTPCAUSE_MS_REFUSES_X, "MS refuses" },
{ GTPCAUSE_MS_NOT_RESP_X, "MS is not GPRS responding" },
{ GTPCAUSE_ACC_REQ, "Request accepted" },
{ GTPCAUSE_NON_EXIST, "Non-existent" },
{ GTPCAUSE_INVALID_MESSAGE, "Invalid message format" },
{ GTPCAUSE_IMSI_NOT_KNOWN, "IMSI not known" },
{ GTPCAUSE_MS_DETACHED, "MS is GPRS detached" },
{ GTPCAUSE_MS_NOT_RESP, "MS is not GPRS responding" },
{ GTPCAUSE_MS_REFUSES, "MS refuses" },
{ GTPCAUSE_NO_RESOURCES, "No resources available" },
{ GTPCAUSE_NOT_SUPPORTED, "Service not supported" },
{ GTPCAUSE_MAN_IE_INCORRECT, "Mandatory IE incorrect" },
{ GTPCAUSE_MAN_IE_MISSING, "Mandatory IE missing" },
{ GTPCAUSE_OPT_IE_INCORRECT, "Optional IE incorrect" },
{ GTPCAUSE_SYS_FAIL, "System failure" },
{ GTPCAUSE_ROAMING_REST, "Roaming restrictions" },
{ GTPCAUSE_PTIMSI_MISMATCH, "P-TMSI Signature mismatch" },
{ GTPCAUSE_CONN_SUSP, "GPRS connection suspended" },
{ GTPCAUSE_AUTH_FAIL, "Authentication failure" },
{ GTPCAUSE_USER_AUTH_FAIL, "User authentication failed" },
{ GTPCAUSE_CONTEXT_NOT_FOUND, "Context not found" },
{ GTPCAUSE_ADDR_OCCUPIED, "All dynamic PDP addresses occupied" },
{ GTPCAUSE_NO_MEMORY, "No memory is available" },
{ GTPCAUSE_RELOC_FAIL, "Relocation failure" },
{ GTPCAUSE_UNKNOWN_MAN_EXTHEADER, "Unknown mandatory ext. header" },
{ GTPCAUSE_SEM_ERR_TFT, "Semantic error in TFT operation" },
{ GTPCAUSE_SYN_ERR_TFT, "Syntactic error in TFT operation" },
{ GTPCAUSE_SEM_ERR_FILTER, "Semantic errors in packet filter" },
{ GTPCAUSE_SYN_ERR_FILTER, "Syntactic errors in packet filter" },
{ GTPCAUSE_MISSING_APN, "Missing or unknown APN" },
{ GTPCAUSE_UNKNOWN_PDP, "Unknown PDP address or PDP type" },
{ 0, NULL }
};
/* Generate the GTP IMSI IE according to 09.60 Section 7.9.2 */
static uint64_t imsi_str2gtp(char *str)
{
uint64_t imsi64 = 0;
unsigned int n;
unsigned int imsi_len = strlen(str);
if (imsi_len > 16) {
LOGP(DGPRS, LOGL_NOTICE, "IMSI length > 16 not supported!\n");
return 0;
}
for (n = 0; n < 16; n++) {
uint64_t val;
if (n < imsi_len)
val = (str[n]-'0') & 0xf;
else
val = 0xf;
imsi64 |= (val << (n*4));
}
return imsi64;
}
/* generate a PDP context based on the IE's from the 04.08 message,
* and send the GTP create pdp context request to the GGSN */
struct sgsn_pdp_ctx *sgsn_create_pdp_ctx(struct sgsn_ggsn_ctx *ggsn,
struct sgsn_mm_ctx *mmctx,
uint16_t nsapi,
struct tlv_parsed *tp)
{
struct gprs_ra_id raid;
struct sgsn_pdp_ctx *pctx;
struct pdp_t *pdp;
uint64_t imsi_ui64;
size_t qos_len;
const uint8_t *qos;
int rc;
LOGP(DGPRS, LOGL_ERROR, "Create PDP Context\n");
pctx = sgsn_pdp_ctx_alloc(mmctx, nsapi);
if (!pctx) {
LOGP(DGPRS, LOGL_ERROR, "Couldn't allocate PDP Ctx\n");
return NULL;
}
imsi_ui64 = imsi_str2gtp(mmctx->imsi);
rc = pdp_newpdp(&pdp, imsi_ui64, nsapi, NULL);
if (rc) {
LOGP(DGPRS, LOGL_ERROR, "Out of libgtp PDP Contexts\n");
return NULL;
}
pdp->priv = pctx;
pctx->lib = pdp;
pctx->ggsn = ggsn;
//pdp->peer = /* sockaddr_in of GGSN (receive) */
//pdp->ipif = /* not used by library */
pdp->version = ggsn->gtp_version;
pdp->hisaddr0 = ggsn->remote_addr;
pdp->hisaddr1 = ggsn->remote_addr;
//pdp->cch_pdp = 512; /* Charging Flat Rate */
/* MS provided APN, subscription was verified by the caller */
pdp->selmode = 0xFC | 0x00;
/* IMSI, TEID/TEIC, FLLU/FLLC, TID, NSAPI set in pdp_newpdp */
/* Put the MSISDN in case we have it */
if (mmctx->subscr && mmctx->subscr->sgsn_data->msisdn_len) {
pdp->msisdn.l = mmctx->subscr->sgsn_data->msisdn_len;
if (pdp->msisdn.l > sizeof(pdp->msisdn.v))
pdp->msisdn.l = sizeof(pdp->msisdn.v);
memcpy(pdp->msisdn.v, mmctx->subscr->sgsn_data->msisdn,
pdp->msisdn.l);
} else {
/* use the dummy 15-digits-zero MSISDN value */
pdp->msisdn.l = sizeof(dummy_msisdn);
memcpy(pdp->msisdn.v, dummy_msisdn, pdp->msisdn.l);
}
/* End User Address from GMM requested PDP address */
pdp->eua.l = TLVP_LEN(tp, OSMO_IE_GSM_REQ_PDP_ADDR);
if (pdp->eua.l > sizeof(pdp->eua.v))
pdp->eua.l = sizeof(pdp->eua.v);
memcpy(pdp->eua.v, TLVP_VAL(tp, OSMO_IE_GSM_REQ_PDP_ADDR),
pdp->eua.l);
/* Highest 4 bits of first byte need to be set to 1, otherwise
* the IE is identical with the 04.08 PDP Address IE */
pdp->eua.v[0] |= 0xf0;
/* APN name from GMM */
pdp->apn_use.l = TLVP_LEN(tp, GSM48_IE_GSM_APN);
if (pdp->apn_use.l > sizeof(pdp->apn_use.v))
pdp->apn_use.l = sizeof(pdp->apn_use.v);
memcpy(pdp->apn_use.v, TLVP_VAL(tp, GSM48_IE_GSM_APN),
pdp->apn_use.l);
/* Protocol Configuration Options from GMM */
pdp->pco_req.l = TLVP_LEN(tp, GSM48_IE_GSM_PROTO_CONF_OPT);
if (pdp->pco_req.l > sizeof(pdp->pco_req.v))
pdp->pco_req.l = sizeof(pdp->pco_req.v);
memcpy(pdp->pco_req.v, TLVP_VAL(tp, GSM48_IE_GSM_PROTO_CONF_OPT),
pdp->pco_req.l);
/* QoS options from GMM or remote */
if (TLVP_LEN(tp, OSMO_IE_GSM_SUB_QOS) > 0) {
qos_len = TLVP_LEN(tp, OSMO_IE_GSM_SUB_QOS);
qos = TLVP_VAL(tp, OSMO_IE_GSM_SUB_QOS);
} else {
qos_len = TLVP_LEN(tp, OSMO_IE_GSM_REQ_QOS);
qos = TLVP_VAL(tp, OSMO_IE_GSM_REQ_QOS);
}
if (qos_len <= 3) {
pdp->qos_req.l = qos_len + 1;
if (pdp->qos_req.l > sizeof(pdp->qos_req.v))
pdp->qos_req.l = sizeof(pdp->qos_req.v);
pdp->qos_req.v[0] = 0; /* Allocation/Retention policy */
memcpy(&pdp->qos_req.v[1], qos, pdp->qos_req.l - 1);
} else {
pdp->qos_req.l = qos_len;
if (pdp->qos_req.l > sizeof(pdp->qos_req.v))
pdp->qos_req.l = sizeof(pdp->qos_req.v);
memcpy(pdp->qos_req.v, qos, pdp->qos_req.l);
}
/* charging characteristics if present */
if (TLVP_LEN(tp, OSMO_IE_GSM_CHARG_CHAR) >= sizeof(pdp->cch_pdp))
pdp->cch_pdp = tlvp_val16be(tp, OSMO_IE_GSM_CHARG_CHAR);
/* SGSN address for control plane */
pdp->gsnlc.l = sizeof(sgsn->cfg.gtp_listenaddr.sin_addr);
memcpy(pdp->gsnlc.v, &sgsn->cfg.gtp_listenaddr.sin_addr,
sizeof(sgsn->cfg.gtp_listenaddr.sin_addr));
/* 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));
/* Encode RAT Type according to TS 29.060 7.7.50 */
pdp->rattype.l = 1;
if (mmctx->ran_type == MM_CTX_T_UTRAN_Iu)
pdp->rattype.v[0] = 1;
else
pdp->rattype.v[0] = 2;
pdp->rattype_given = 1;
/* Include RAI and ULI all the time */
pdp->rai_given = 1;
pdp->rai.l = 6;
/* Routing Area Identifier with LAC and RAC fixed values, as
* requested in 29.006 7.3.1 */
raid = mmctx->ra;
raid.lac = 0xFFFE;
raid.rac = 0xFF;
gsm48_construct_ra(pdp->rai.v, &raid);
/* Encode User Location Information accordint to TS 29.060 7.7.51 */
pdp->userloc_given = 1;
pdp->userloc.l = 8;
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->userloc.v[0] = 1; /* SAI for UTRAN */
/* SAI is like CGI but with SAC instead of CID, so we can abuse this function */
bssgp_create_cell_id(&pdp->userloc.v[1], &mmctx->ra, mmctx->iu.sac);
break;
}
/* include the IMEI(SV) */
pdp->imeisv_given = 1;
gsm48_encode_bcd_number(&pdp->imeisv.v[0], 8, 0, mmctx->imei);
pdp->imeisv.l = pdp->imeisv.v[0];
memmove(&pdp->imeisv.v[0], &pdp->imeisv.v[1], 8);
/* change pdp state to 'requested' */
pctx->state = PDP_STATE_CR_REQ;
rc = gtp_create_context_req(ggsn->gsn, pdp, pctx);
/* FIXME */
return pctx;
}
/* SGSN wants to delete a PDP context */
int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx)
{
LOGPDPCTXP(LOGL_ERROR, pctx, "Delete PDP Context\n");
/* FIXME: decide if we need teardown or not ! */
return gtp_delete_context_req(pctx->ggsn->gsn, pctx->lib, pctx, 1);
}
struct cause_map {
uint8_t cause_in;
uint8_t cause_out;
};
static uint8_t cause_map(const struct cause_map *map, uint8_t in, uint8_t deflt)
{
const struct cause_map *m;
for (m = map; m->cause_in && m->cause_out; m++) {
if (m->cause_in == in)
return m->cause_out;
}
return deflt;
}
/* how do we map from gtp cause to SM cause */
static const struct cause_map gtp2sm_cause_map[] = {
{ GTPCAUSE_NO_RESOURCES, GSM_CAUSE_INSUFF_RSRC },
{ GTPCAUSE_NOT_SUPPORTED, GSM_CAUSE_SERV_OPT_NOTSUPP },
{ GTPCAUSE_MAN_IE_INCORRECT, GSM_CAUSE_INV_MAND_INFO },
{ GTPCAUSE_MAN_IE_MISSING, GSM_CAUSE_INV_MAND_INFO },
{ GTPCAUSE_OPT_IE_INCORRECT, GSM_CAUSE_PROTO_ERR_UNSPEC },
{ GTPCAUSE_SYS_FAIL, GSM_CAUSE_NET_FAIL },
{ GTPCAUSE_ROAMING_REST, GSM_CAUSE_REQ_SERV_OPT_NOTSUB },
{ GTPCAUSE_PTIMSI_MISMATCH, GSM_CAUSE_PROTO_ERR_UNSPEC },
{ GTPCAUSE_CONN_SUSP, GSM_CAUSE_PROTO_ERR_UNSPEC },
{ GTPCAUSE_AUTH_FAIL, GSM_CAUSE_AUTH_FAILED },
{ GTPCAUSE_USER_AUTH_FAIL, GSM_CAUSE_ACT_REJ_GGSN },
{ GTPCAUSE_CONTEXT_NOT_FOUND, GSM_CAUSE_PROTO_ERR_UNSPEC },
{ GTPCAUSE_ADDR_OCCUPIED, GSM_CAUSE_INSUFF_RSRC },
{ GTPCAUSE_NO_MEMORY, GSM_CAUSE_INSUFF_RSRC },
{ GTPCAUSE_RELOC_FAIL, GSM_CAUSE_PROTO_ERR_UNSPEC },
{ GTPCAUSE_UNKNOWN_MAN_EXTHEADER, GSM_CAUSE_PROTO_ERR_UNSPEC },
{ GTPCAUSE_MISSING_APN, GSM_CAUSE_MISSING_APN },
{ GTPCAUSE_UNKNOWN_PDP, GSM_CAUSE_UNKNOWN_PDP },
{ 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_pdp_ctx *pctx = cbp;
uint8_t reject_cause;
LOGPDPCTXP(LOGL_INFO, pctx, "Received CREATE PDP CTX CONF, cause=%d(%s)\n",
cause, get_value_string(gtp_cause_strs, cause));
if (!pctx->mm) {
LOGP(DGPRS, LOGL_INFO,
"No MM context, aborting CREATE PDP CTX CONF\n");
return -EIO;
}
/* Check for cause value if it was really successful */
if (cause < 0) {
LOGP(DGPRS, LOGL_NOTICE, "Create PDP ctx req timed out\n");
if (pdp && pdp->version == 1) {
pdp->version = 0;
gtp_create_context_req(sgsn->gsn, pdp, cbp);
return 0;
} else {
reject_cause = GSM_CAUSE_NET_FAIL;
goto reject;
}
}
/* Check for cause value if it was really successful */
if (cause != GTPCAUSE_ACC_REQ) {
reject_cause = cause_map(gtp2sm_cause_map, cause,
GSM_CAUSE_ACT_REJ_GGSN);
goto reject;
}
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);
return 0;
#else
return -ENOTSUP;
#endif
}
LOGP(DGPRS, LOGL_ERROR, "Unknown ran_type %d\n",
pctx->mm->ran_type);
reject_cause = GSM_CAUSE_PROTO_ERR_UNSPEC;
reject:
/*
* In case of a timeout pdp will be NULL but we have a valid pointer
* in pctx->lib. For other rejects pctx->lib and pdp might be the
* same.
*/
pctx->state = PDP_STATE_NONE;
if (pctx->lib && pctx->lib != pdp)
pdp_freepdp(pctx->lib);
pctx->lib = NULL;
if (pdp)
pdp_freepdp(pdp);
/* Send PDP CTX ACT REJ to MS */
gsm48_tx_gsm_act_pdp_rej(pctx->mm, pctx->ti, reject_cause,
0, NULL);
sgsn_pdp_ctx_free(pctx);
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)
{
struct sgsn_signal_data sig_data;
struct sgsn_pdp_ctx *pctx = cbp;
int rc = 0;
LOGPDPCTXP(LOGL_INFO, pctx, "Received DELETE PDP CTX CONF, cause=%d(%s)\n",
cause, get_value_string(gtp_cause_strs, cause));
memset(&sig_data, 0, sizeof(sig_data));
sig_data.pdp = pctx;
osmo_signal_dispatch(SS_SGSN, S_SGSN_PDP_DEACT, &sig_data);
if (pctx->mm) {
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 */
ranap_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);
} else {
LOGPDPCTXP(LOGL_NOTICE, pctx,
"Not deactivating SNDCP layer since the MM context "
"is not available\n");
}
/* unlink the now non-existing library handle from the pdp
* context */
pctx->lib = NULL;
sgsn_pdp_ctx_free(pctx);
return rc;
}
/* Confirmation of an GTP ECHO request */
static int echo_conf(struct pdp_t *pdp, void *cbp, int recovery)
{
if (recovery < 0) {
LOGP(DGPRS, LOGL_NOTICE, "GTP Echo Request timed out\n");
/* FIXME: if version == 1, retry with version 0 */
} else {
DEBUGP(DGPRS, "GTP Rx Echo Response\n");
}
return 0;
}
/* Any message received by GGSN contains a recovery IE */
static int cb_recovery(struct sockaddr_in *peer, uint8_t recovery)
{
struct sgsn_ggsn_ctx *ggsn;
ggsn = sgsn_ggsn_ctx_by_addr(&peer->sin_addr);
if (!ggsn) {
LOGP(DGPRS, LOGL_NOTICE, "Received Recovery IE for unknown GGSN\n");
return -EINVAL;
}
if (ggsn->remote_restart_ctr == -1) {
/* First received ECHO RESPONSE, note the restart ctr */
ggsn->remote_restart_ctr = recovery;
} else if (ggsn->remote_restart_ctr != recovery) {
/* counter has changed (GGSN restart): release all PDP */
LOGP(DGPRS, LOGL_NOTICE, "GGSN recovery (%u->%u), "
"releasing all PDP contexts\n",
ggsn->remote_restart_ctr, recovery);
ggsn->remote_restart_ctr = recovery;
drop_all_pdp_for_ggsn(ggsn);
}
return 0;
}
/* libgtp callback for confirmations */
static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp)
{
DEBUGP(DGPRS, "libgtp cb_conf(type=%d, cause=%d, pdp=%p, cbp=%p)\n",
type, cause, pdp, cbp);
if (cause == EOF)
LOGP(DGPRS, LOGL_ERROR, "libgtp EOF (type=%u, pdp=%p, cbp=%p)\n",
type, pdp, cbp);
switch (type) {
case GTP_ECHO_REQ:
/* libgtp hands us the RECOVERY number instead of a cause */
return echo_conf(pdp, cbp, cause);
case GTP_CREATE_PDP_REQ:
return create_pdp_conf(pdp, cbp, cause);
case GTP_DELETE_PDP_REQ:
return delete_pdp_conf(pdp, cbp, cause);
default:
break;
}
return 0;
}
/* Called whenever a PDP context is deleted for any reason */
static int cb_delete_context(struct pdp_t *pdp)
{
LOGP(DGPRS, LOGL_INFO, "PDP Context was deleted\n");
return 0;
}
/* Called when we receive a Version Not Supported message */
static int cb_unsup_ind(struct sockaddr_in *peer)
{
LOGP(DGPRS, LOGL_INFO, "GTP Version not supported Indication "
"from %s:%u\n", inet_ntoa(peer->sin_addr),
ntohs(peer->sin_port));
return 0;
}
/* Called when we receive a Supported Ext Headers Notification */
static int cb_extheader_ind(struct sockaddr_in *peer)
{
LOGP(DGPRS, LOGL_INFO, "GTP Supported Ext Headers Noficiation "
"from %s:%u\n", inet_ntoa(peer->sin_addr),
ntohs(peer->sin_port));
return 0;
}
/* Called whenever we recive a DATA packet */
static int cb_data_ind(struct pdp_t *lib, void *packet, unsigned int len)
{
struct bssgp_paging_info pinfo;
struct sgsn_pdp_ctx *pdp;
struct sgsn_mm_ctx *mm;
struct msgb *msg;
uint8_t *ud;
pdp = lib->priv;
if (!pdp) {
LOGP(DGPRS, LOGL_NOTICE,
"GTP DATA IND from GGSN for unknown PDP\n");
return -EIO;
}
mm = pdp->mm;
if (!mm) {
LOGP(DGPRS, LOGL_ERROR,
"PDP context (address=%u) without MM context!\n",
pdp->address);
return -EIO;
}
DEBUGP(DGPRS, "GTP DATA IND from GGSN for %s, length=%u\n", mm->imsi,
len);
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 */
ranap_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->gb.tlli;
msgb_bvci(msg) = mm->gb.bvci;
msgb_nsei(msg) = mm->gb.nsei;
switch (mm->gmm_state) {
case GMM_REGISTERED_SUSPENDED:
/* initiate PS PAGING procedure */
memset(&pinfo, 0, sizeof(pinfo));
pinfo.mode = BSSGP_PAGING_PS;
pinfo.scope = BSSGP_PAGING_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->gb.nsei, 0, &pinfo);
rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PAGING_PS]);
/* FIXME: queue the packet we received from GTP */
break;
case GMM_REGISTERED_NORMAL:
break;
default:
LOGP(DGPRS, LOGL_ERROR, "GTP DATA IND for TLLI %08X in state "
"%u\n", mm->gb.tlli, mm->gmm_state);
msgb_free(msg);
return -1;
}
rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_OUT]);
rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_OUT], len);
rate_ctr_inc(&mm->ctrg->ctr[GMM_CTR_PKTS_UDATA_OUT]);
rate_ctr_add(&mm->ctrg->ctr[GMM_CTR_BYTES_UDATA_OUT], len);
/* It is easier to have a global count */
pdp->cdr_bytes_out += len;
return sndcp_unitdata_req(msg, &mm->gb.llme->lle[pdp->sapi],
pdp->nsapi, mm);
}
/* Called by SNDCP when it has received/re-assembled a N-PDU */
int sgsn_rx_sndcp_ud_ind(struct gprs_ra_id *ra_id, int32_t tlli, uint8_t nsapi,
struct msgb *msg, uint32_t npdu_len, uint8_t *npdu)
{
struct sgsn_mm_ctx *mmctx;
struct sgsn_pdp_ctx *pdp;
/* look-up the MM context for this message */
mmctx = sgsn_mm_ctx_by_tlli(tlli, ra_id);
if (!mmctx) {
LOGP(DGPRS, LOGL_ERROR,
"Cannot find MM CTX for TLLI %08x\n", tlli);
return -EIO;
}
/* look-up the PDP context for this message */
pdp = sgsn_pdp_ctx_by_nsapi(mmctx, nsapi);
if (!pdp) {
LOGP(DGPRS, LOGL_ERROR, "Cannot find PDP CTX for "
"TLLI=%08x, NSAPI=%u\n", tlli, nsapi);
return -EIO;
}
if (!pdp->lib) {
LOGP(DGPRS, LOGL_ERROR, "PDP CTX without libgtp\n");
return -EIO;
}
rate_ctr_inc(&pdp->ctrg->ctr[PDP_CTR_PKTS_UDATA_IN]);
rate_ctr_add(&pdp->ctrg->ctr[PDP_CTR_BYTES_UDATA_IN], npdu_len);
rate_ctr_inc(&mmctx->ctrg->ctr[GMM_CTR_PKTS_UDATA_IN]);
rate_ctr_add(&mmctx->ctrg->ctr[GMM_CTR_BYTES_UDATA_IN], npdu_len);
/* It is easier to have a global count */
pdp->cdr_bytes_in += npdu_len;
return gtp_data_req(pdp->ggsn->gsn, pdp->lib, npdu, npdu_len);
}
/* libgtp select loop integration */
static int sgsn_gtp_fd_cb(struct osmo_fd *fd, unsigned int what)
{
struct sgsn_instance *sgi = fd->data;
int rc;
if (!(what & BSC_FD_READ))
return 0;
switch (fd->priv_nr) {
case 0:
rc = gtp_decaps0(sgi->gsn);
break;
case 1:
rc = gtp_decaps1c(sgi->gsn);
break;
case 2:
rc = gtp_decaps1u(sgi->gsn);
break;
default:
rc = -EINVAL;
break;
}
return rc;
}
static void sgsn_gtp_tmr_start(struct sgsn_instance *sgi)
{
struct timeval next;
/* Retrieve next retransmission as struct timeval */
gtp_retranstimeout(sgi->gsn, &next);
/* re-schedule the timer */
osmo_timer_schedule(&sgi->gtp_timer, next.tv_sec, next.tv_usec/1000);
}
/* timer callback for libgtp retransmissions and ping */
static void sgsn_gtp_tmr_cb(void *data)
{
struct sgsn_instance *sgi = data;
/* Do all the retransmissions as needed */
gtp_retrans(sgi->gsn);
sgsn_gtp_tmr_start(sgi);
}
int sgsn_gtp_init(struct sgsn_instance *sgi)
{
int rc;
struct gsn_t *gsn;
rc = gtp_new(&sgi->gsn, sgi->cfg.gtp_statedir,
&sgi->cfg.gtp_listenaddr.sin_addr, GTP_MODE_SGSN);
if (rc) {
LOGP(DGPRS, LOGL_ERROR, "Failed to create GTP: %d\n", rc);
return rc;
}
gsn = sgi->gsn;
sgi->gtp_fd0.fd = gsn->fd0;
sgi->gtp_fd0.priv_nr = 0;
sgi->gtp_fd0.data = sgi;
sgi->gtp_fd0.when = BSC_FD_READ;
sgi->gtp_fd0.cb = sgsn_gtp_fd_cb;
rc = osmo_fd_register(&sgi->gtp_fd0);
if (rc < 0)
return rc;
sgi->gtp_fd1c.fd = gsn->fd1c;
sgi->gtp_fd1c.priv_nr = 1;
sgi->gtp_fd1c.data = sgi;
sgi->gtp_fd1c.when = BSC_FD_READ;
sgi->gtp_fd1c.cb = sgsn_gtp_fd_cb;
rc = osmo_fd_register(&sgi->gtp_fd1c);
if (rc < 0) {
osmo_fd_unregister(&sgi->gtp_fd0);
return rc;
}
sgi->gtp_fd1u.fd = gsn->fd1u;
sgi->gtp_fd1u.priv_nr = 2;
sgi->gtp_fd1u.data = sgi;
sgi->gtp_fd1u.when = BSC_FD_READ;
sgi->gtp_fd1u.cb = sgsn_gtp_fd_cb;
rc = osmo_fd_register(&sgi->gtp_fd1u);
if (rc < 0) {
osmo_fd_unregister(&sgi->gtp_fd0);
osmo_fd_unregister(&sgi->gtp_fd1c);
return rc;
}
/* Start GTP re-transmission timer */
osmo_timer_setup(&sgi->gtp_timer, sgsn_gtp_tmr_cb, sgi);
sgsn_gtp_tmr_start(sgi);
/* Register callbackcs with libgtp */
gtp_set_cb_delete_context(gsn, cb_delete_context);
gtp_set_cb_conf(gsn, cb_conf);
gtp_set_cb_recovery(gsn, cb_recovery);
gtp_set_cb_data_ind(gsn, cb_data_ind);
gtp_set_cb_unsup_ind(gsn, cb_unsup_ind);
gtp_set_cb_extheader_ind(gsn, cb_extheader_ind);
return 0;
}

View File

@ -1,476 +0,0 @@
/* GPRS SGSN Implementation */
/* (C) 2010 by Harald Welte <laforge@gnumonks.org>
* (C) 2010 by On-Waves
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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 <unistd.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <signal.h>
#include <sys/fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/stats.h>
#include <osmocom/gsm/gsup.h>
#include <osmocom/gprs/gprs_ns.h>
#include <osmocom/gprs/gprs_bssgp.h>
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
#include <osmocom/vty/stats.h>
#include <osmocom/vty/ports.h>
#include <osmocom/ctrl/control_vty.h>
#include <openbsc/signal.h>
#include <openbsc/debug.h>
#include <openbsc/vty.h>
#include <openbsc/sgsn.h>
#include <openbsc/gprs_llc.h>
#include <openbsc/gprs_gmm.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/ctrl/ports.h>
#include <osmocom/sigtran/protocol/m3ua.h>
#include <gtp.h>
#include "../../bscconfig.h"
#if BUILD_IU
#include <osmocom/ranap/iu_client.h>
#endif
#define _GNU_SOURCE
#include <getopt.h>
void *tall_bsc_ctx;
struct gprs_ns_inst *sgsn_nsi;
static int daemonize = 0;
const char *openbsc_copyright =
"Copyright (C) 2010 Harald Welte and On-Waves\r\n"
"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n"
"This is free software: you are free to change and redistribute it.\r\n"
"There is NO WARRANTY, to the extent permitted by law.\r\n";
static struct sgsn_instance sgsn_inst = {
.config_file = "osmo_sgsn.cfg",
.cfg = {
.gtp_statedir = "./",
.auth_policy = SGSN_AUTH_POLICY_CLOSED,
.gsup_server_port = OSMO_GSUP_PORT,
},
};
struct sgsn_instance *sgsn = &sgsn_inst;
/* call-back function for the NS protocol */
static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc,
struct msgb *msg, uint16_t bvci)
{
int rc = 0;
switch (event) {
case GPRS_NS_EVT_UNIT_DATA:
/* hand the message into the BSSGP implementation */
rc = bssgp_rcvmsg(msg);
break;
default:
LOGP(DGPRS, LOGL_ERROR, "SGSN: Unknown event %u from NS\n", event);
if (msg)
msgb_free(msg);
rc = -EIO;
break;
}
return rc;
}
/* call-back function for the BSSGP protocol */
int bssgp_prim_cb(struct osmo_prim_hdr *oph, void *ctx)
{
struct osmo_bssgp_prim *bp;
bp = container_of(oph, struct osmo_bssgp_prim, oph);
switch (oph->sap) {
case SAP_BSSGP_LL:
switch (oph->primitive) {
case PRIM_BSSGP_UL_UD:
return gprs_llc_rcvmsg(oph->msg, bp->tp);
}
break;
case SAP_BSSGP_GMM:
switch (oph->primitive) {
case PRIM_BSSGP_GMM_SUSPEND:
return gprs_gmm_rx_suspend(bp->ra_id, bp->tlli);
case PRIM_BSSGP_GMM_RESUME:
return gprs_gmm_rx_resume(bp->ra_id, bp->tlli,
bp->u.resume.suspend_ref);
}
break;
case SAP_BSSGP_NM:
break;
}
return 0;
}
static void signal_handler(int signal)
{
fprintf(stdout, "signal %u received\n", signal);
switch (signal) {
case SIGINT:
case SIGTERM:
osmo_signal_dispatch(SS_L_GLOBAL, S_L_GLOBAL_SHUTDOWN, NULL);
sleep(1);
exit(0);
break;
case SIGABRT:
/* in case of abort, we want to obtain a talloc report
* and then return to the caller, who will abort the process */
case SIGUSR1:
talloc_report(tall_vty_ctx, stderr);
talloc_report_full(tall_bsc_ctx, stderr);
break;
case SIGUSR2:
talloc_report_full(tall_vty_ctx, stderr);
break;
default:
break;
}
}
/* NSI that BSSGP uses when transmitting on NS */
extern struct gprs_ns_inst *bssgp_nsi;
extern int bsc_vty_go_parent(struct vty *vty);
static struct vty_app_info vty_info = {
.name = "OsmoSGSN",
.version = PACKAGE_VERSION,
.go_parent_cb = bsc_vty_go_parent,
.is_config_node = bsc_vty_is_config_node,
};
static void print_help(void)
{
printf("Some useful help...\n");
printf(" -h --help\tthis text\n");
printf(" -D --daemonize\tFork the process into a background daemon\n");
printf(" -d option --debug\tenable Debugging\n");
printf(" -s --disable-color\n");
printf(" -c --config-file\tThe config file to use [%s]\n", sgsn->config_file);
printf(" -e --log-level number\tSet a global log level\n");
}
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"debug", 1, 0, 'd'},
{"daemonize", 0, 0, 'D'},
{"config-file", 1, 0, 'c'},
{"disable-color", 0, 0, 's'},
{"timestamp", 0, 0, 'T'},
{ "version", 0, 0, 'V' },
{"log-level", 1, 0, 'e'},
{NULL, 0, 0, 0}
};
c = getopt_long(argc, argv, "hd:Dc:sTVe:",
long_options, &option_index);
if (c == -1)
break;
switch (c) {
case 'h':
//print_usage();
print_help();
exit(0);
case 's':
log_set_use_color(osmo_stderr_target, 0);
break;
case 'd':
log_parse_category_mask(osmo_stderr_target, optarg);
break;
case 'D':
daemonize = 1;
break;
case 'c':
sgsn_inst.config_file = strdup(optarg);
break;
case 'T':
log_set_print_timestamp(osmo_stderr_target, 1);
break;
case 'V':
print_version(1);
exit(0);
break;
case 'e':
log_set_log_level(osmo_stderr_target, atoi(optarg));
break;
default:
/* ignore */
break;
}
}
}
/* default categories */
static struct log_info_cat gprs_categories[] = {
[DMM] = {
.name = "DMM",
.description = "Layer3 Mobility Management (MM)",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DPAG] = {
.name = "DPAG",
.description = "Paging Subsystem",
.color = "\033[1;38m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DMEAS] = {
.name = "DMEAS",
.description = "Radio Measurement Processing",
.enabled = 0, .loglevel = LOGL_NOTICE,
},
[DREF] = {
.name = "DREF",
.description = "Reference Counting",
.enabled = 0, .loglevel = LOGL_NOTICE,
},
[DGPRS] = {
.name = "DGPRS",
.description = "GPRS Packet Service",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DNS] = {
.name = "DNS",
.description = "GPRS Network Service (NS)",
.enabled = 1, .loglevel = LOGL_INFO,
},
[DBSSGP] = {
.name = "DBSSGP",
.description = "GPRS BSS Gateway Protocol (BSSGP)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DLLC] = {
.name = "DLLC",
.description = "GPRS Logical Link Control Protocol (LLC)",
.enabled = 1, .loglevel = LOGL_DEBUG,
},
[DSNDCP] = {
.name = "DSNDCP",
.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 = {
.filter_fn = gprs_log_filter_fn,
.cat = gprs_categories,
.num_cat = ARRAY_SIZE(gprs_categories),
};
int sgsn_ranap_iu_event(struct ue_conn_ctx *ctx, enum ranap_iu_event_type type, void *data);
int main(int argc, char **argv)
{
struct ctrl_handle *ctrl;
struct gsm_network dummy_network;
struct osmo_sccp_instance *sccp;
int rc;
srand(time(NULL));
tall_bsc_ctx = talloc_named_const(NULL, 0, "osmo_sgsn");
msgb_talloc_ctx_init(tall_bsc_ctx, 0);
signal(SIGINT, &signal_handler);
signal(SIGTERM, &signal_handler);
signal(SIGABRT, &signal_handler);
signal(SIGUSR1, &signal_handler);
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
osmo_init_logging(&gprs_log_info);
osmo_stats_init(tall_bsc_ctx);
vty_info.copyright = openbsc_copyright;
vty_init(&vty_info);
logging_vty_add_cmds(NULL);
osmo_stats_vty_add_cmds(&gprs_log_info);
sgsn_vty_init(&sgsn_inst.cfg);
ctrl_vty_init(tall_bsc_ctx);
osmo_ss7_init();
handle_options(argc, argv);
rate_ctr_init(tall_bsc_ctx);
gprs_ns_set_log_ss(DNS);
bssgp_set_log_ss(DBSSGP);
sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb, tall_bsc_ctx);
if (!sgsn_nsi) {
LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n");
exit(1);
}
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);
bssgp_vty_init();
gprs_llc_vty_init();
gprs_sndcp_vty_init();
sgsn_auth_init();
sgsn_cdr_init(&sgsn_inst);
/* FIXME: register signal handler for SS_L_NS */
rc = sgsn_parse_config(sgsn_inst.config_file);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Error in config file\n");
exit(2);
}
/* start telnet after reading config for vty_get_bind_addr() */
rc = telnet_init_dynif(tall_bsc_ctx, &dummy_network,
vty_get_bind_addr(), OSMO_VTY_PORT_SGSN);
if (rc < 0)
exit(1);
/* start control interface after reading config for
* ctrl_vty_get_bind_addr() */
ctrl = sgsn_controlif_setup(NULL, ctrl_vty_get_bind_addr(),
OSMO_CTRL_PORT_SGSN);
if (!ctrl) {
LOGP(DGPRS, LOGL_ERROR, "Failed to create CTRL interface.\n");
exit(1);
}
if (sgsn_ctrl_cmds_install() != 0) {
LOGP(DGPRS, LOGL_ERROR, "Failed to install CTRL commands.\n");
exit(1);
}
rc = sgsn_gtp_init(&sgsn_inst);
if (rc) {
LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on GTP socket\n");
exit(2);
}
rc = gprs_subscr_init(&sgsn_inst);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Cannot set up subscriber management\n");
exit(2);
}
rc = gprs_ns_nsip_listen(sgsn_nsi);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen on NSIP socket\n");
exit(2);
}
rc = gprs_ns_frgre_listen(sgsn_nsi);
if (rc < 0) {
LOGP(DGPRS, LOGL_FATAL, "Cannot bind/listen GRE "
"socket. Do you have CAP_NET_RAW?\n");
exit(2);
}
if (sgsn->cfg.dynamic_lookup) {
if (sgsn_ares_init(sgsn) != 0) {
LOGP(DGPRS, LOGL_FATAL,
"Failed to initialize c-ares(%d)\n", rc);
exit(4);
}
}
#ifdef BUILD_IU
sccp = osmo_sccp_simple_client(tall_bsc_ctx, "OsmoSGSN",
2 /* FIXME: configurable */,
OSMO_SS7_ASP_PROT_M3UA, 0,
"127.0.0.4" /* FIXME: configurable */,
M3UA_PORT,
"127.0.0.1" /* FIXME: configurable */);
if (!sccp) {
printf("Setting up SCCP client failed.\n");
return 8;
}
ranap_iu_init(tall_bsc_ctx, DRANAP, "OsmoSGSN-IuPS", sccp, gsm0408_gprs_rcvmsg_iu, sgsn_ranap_iu_event);
#endif
if (daemonize) {
rc = osmo_daemonize();
if (rc < 0) {
perror("Error during daemonize");
exit(1);
}
}
while (1) {
rc = osmo_select_main(0);
if (rc < 0)
exit(3);
}
/* not reached */
exit(0);
}

File diff suppressed because it is too large Load Diff

View File

@ -1,813 +0,0 @@
/*
* 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);
}
}

View File

@ -1,767 +0,0 @@
/*
* 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

@ -28,7 +28,7 @@
#include <arpa/inet.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/gsm_04_08_utils.h>
#include <osmocom/gsm/gsm_utils.h>
#include <openbsc/abis_rsl.h>
#include <openbsc/chan_alloc.h>
@ -46,6 +46,7 @@
#include <osmocom/gsm/rsl.h>
#include <osmocom/core/talloc.h>
#include <openbsc/pcu_if.h>
#include <openbsc/bsc_api.h>
#define RSL_ALLOC_SIZE 1024
#define RSL_ALLOC_HEADROOM 128

View File

@ -30,9 +30,8 @@
#include <openbsc/chan_alloc.h>
#include <openbsc/handover.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/trau_mux.h>
#include <openbsc/vlr.h>
#include <openbsc/gsm_04_08_utils.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm48.h>

View File

@ -21,7 +21,6 @@
#include <openbsc/gsm_data.h>
#include <osmocom/gsm/gsm_utils.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/abis_rsl.h>
#include <openbsc/abis_nm.h>
#include <openbsc/debug.h>

View File

@ -31,6 +31,7 @@
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm0502.h>
#include <osmocom/ctrl/control_if.h>
#include <osmocom/gsm/gsm48.h>
#include <arpa/inet.h>
@ -44,7 +45,6 @@
#include <osmocom/gsm/abis_nm.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/meas_rep.h>
#include <openbsc/db.h>
#include <openbsc/vty.h>
#include <osmocom/gprs/gprs_ns.h>
#include <openbsc/system_information.h>
@ -56,8 +56,8 @@
#include <openbsc/osmo_bsc_rf.h>
#include <openbsc/pcu_if.h>
#include <openbsc/common_cs.h>
#include <openbsc/vlr.h>
#include <openbsc/handover.h>
#include <openbsc/gsm_04_08_utils.h>
#include <inttypes.h>
@ -1021,26 +1021,6 @@ DEFUN(show_ts,
return CMD_SUCCESS;
}
static void subscr_dump_vty(struct vty *vty, struct vlr_subscr *vsub)
{
OSMO_ASSERT(vsub);
if (strlen(vsub->name))
vty_out(vty, " Name: '%s'%s", vsub->name, VTY_NEWLINE);
if (strlen(vsub->msisdn))
vty_out(vty, " Extension: %s%s", vsub->msisdn,
VTY_NEWLINE);
if (strlen(vsub->imsi))
vty_out(vty, " IMSI: %s%s", vsub->imsi, VTY_NEWLINE);
if (vsub->tmsi != GSM_RESERVED_TMSI)
vty_out(vty, " TMSI: %08X%s", vsub->tmsi,
VTY_NEWLINE);
if (vsub->tmsi_new != GSM_RESERVED_TMSI)
vty_out(vty, " new TMSI: %08X%s", vsub->tmsi_new,
VTY_NEWLINE);
vty_out(vty, " Use count: %u%s", vsub->use_count, VTY_NEWLINE);
}
static void bsc_subscr_dump_vty(struct vty *vty, struct bsc_subscr *bsub)
{
if (strlen(bsub->imsi))
@ -1166,9 +1146,9 @@ static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan)
vty_out(vty, " Channel Mode / Codec: %s%s",
get_value_string(gsm48_cmode_names, lchan->tch_mode),
VTY_NEWLINE);
if (lchan->conn && lchan->conn->vsub) {
if (lchan->conn && lchan->conn->bsub) {
vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
subscr_dump_vty(vty, lchan->conn->vsub);
bsc_subscr_dump_vty(vty, lchan->conn->bsub);
} else
vty_out(vty, " No Subscriber%s", VTY_NEWLINE);
if (is_ipaccess_bts(lchan->ts->trx->bts)) {

View File

@ -32,6 +32,7 @@
#include <openbsc/debug.h>
#include <openbsc/rtp_proxy.h>
#include <openbsc/signal.h>
#include <openbsc/gsm_04_08_utils.h>
#include <osmocom/core/talloc.h>

View File

@ -32,11 +32,11 @@
#include <openbsc/abis_rsl.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/transaction.h>
#include <openbsc/paging.h>
#include <openbsc/signal.h>
#include <openbsc/bsc_api.h>
#include <openbsc/gsm_04_08_utils.h>
/* should ip.access BTS use direct RTP streams between each other (1),
* or should OpenBSC always act as RTP relay/proxy in between (0) ? */

View File

@ -33,14 +33,14 @@
#include <openbsc/gsm_data.h>
#include <osmocom/gsm/gsm_utils.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/abis_rsl.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/signal.h>
#include <osmocom/core/talloc.h>
#include <openbsc/transaction.h>
#include <openbsc/trau_mux.h>
#include <openbsc/vlr.h>
#include <openbsc/bsc_subscriber.h>
#include <openbsc/gsm_04_08_utils.h>
struct bsc_handover {
struct llist_head list;
@ -262,7 +262,7 @@ static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan)
net = new_lchan->ts->trx->bts->network;
LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN "
"%u->%u\n", vlr_subscr_name(ho->old_lchan->conn->vsub),
"%u->%u\n", bsc_subscr_name(ho->old_lchan->conn->bsub),
ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr,
ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn);

View File

@ -20,6 +20,7 @@
#include <openbsc/common_cs.h>
#include <openbsc/osmo_bsc.h>
#include <openbsc/bsc_msc_data.h>
#include <openbsc/gsm_04_08_utils.h>
struct gsm_network *bsc_network_init(void *ctx,
uint16_t country_code,

View File

@ -50,7 +50,6 @@
#include <openbsc/gsm_data.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/bsc_api.h>
#include <openbsc/vlr.h>
void *tall_paging_ctx;

View File

@ -30,13 +30,15 @@
#include <osmocom/core/bitvec.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/sysinfo.h>
#include <osmocom/gsm/gsm48_ie.h>
#include <osmocom/gsm/gsm48.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/gsm_data.h>
#include <openbsc/abis_rsl.h>
#include <openbsc/rest_octets.h>
#include <openbsc/arfcn_range_encode.h>
#include <openbsc/gsm_04_08_utils.h>
/*
* DCS1800 and PCS1900 have overlapping ARFCNs. We would need to set the

View File

@ -23,12 +23,13 @@
#include <stdbool.h>
#include <osmocom/gsm/gsm0480.h>
#include <osmocom/gsm/gsm48.h>
#include <openbsc/common_cs.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_04_11.h>
#include <openbsc/gsm_04_08_utils.h>
/* Warning: if bsc_network_init() is not called, some of the members of
* gsm_network are not initialized properly and must not be used! (In

View File

@ -23,25 +23,7 @@ libcommon_a_SOURCES = \
debug.c \
gsm_data.c \
gsm_data_shared.c \
gsup_client.c \
oap_client.c \
socket.c \
talloc_ctx.c \
gsm_subscriber_base.c \
$(NULL)
noinst_PROGRAMS = \
gsup_test_client \
$(NULL)
gsup_test_client_SOURCES = \
gsup_test_client.c \
$(NULL)
gsup_test_client_LDADD = \
libcommon.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(LIBOSMOABIS_LIBS) \
-lrt \
$(NULL)

View File

@ -32,6 +32,7 @@
#include <osmocom/gsm/abis_nm.h>
#include <osmocom/core/statistics.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm48.h>
#include <openbsc/gsm_data.h>
#include <openbsc/bsc_msc_data.h>

View File

@ -31,34 +31,7 @@
#include <osmocom/core/utils.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/debug.h>
#include <openbsc/vlr.h>
LLIST_HEAD(active_subscribers);
void *tall_subscr_ctx;
/* return static buffer with printable name of VLR subscriber */
const char *vlr_subscr_name(struct vlr_subscr *vsub)
{
static char buf[32];
if (!vsub)
return "unknown";
if (vsub->msisdn[0])
snprintf(buf, sizeof(buf), "MSISDN:%s", vsub->msisdn);
else if (vsub->imsi[0])
snprintf(buf, sizeof(buf), "IMSI:%s", vsub->imsi);
else if (vsub->tmsi != GSM_RESERVED_TMSI)
snprintf(buf, sizeof(buf), "TMSI:0x%08x", vsub->tmsi);
else if (vsub->tmsi_new != GSM_RESERVED_TMSI)
snprintf(buf, sizeof(buf), "TMSI(new):0x%08x", vsub->tmsi_new);
else
return "unknown";
buf[sizeof(buf)-1] = '\0';
return buf;
}
const char *vlr_subscr_msisdn_or_name(struct vlr_subscr *vsub)
{
if (!vsub || !vsub->msisdn[0])
return vlr_subscr_name(vsub);
return vsub->msisdn;
}

View File

@ -1,347 +0,0 @@
/* Generic Subscriber Update Protocol client */
/* (C) 2014-2016 by Sysmocom s.f.m.c. GmbH
* All Rights Reserved
*
* Author: Jacob Erlbeck
* Author: Neels Hofmeyr
*
* 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/gsup_client.h>
#include <osmocom/abis/ipa.h>
#include <osmocom/gsm/protocol/ipaccess.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#include <openbsc/debug.h>
#include <errno.h>
#include <string.h>
extern void *tall_bsc_ctx;
static void start_test_procedure(struct gsup_client *gsupc);
static void gsup_client_send_ping(struct gsup_client *gsupc)
{
struct msgb *msg = gsup_client_msgb_alloc();
msg->l2h = msgb_put(msg, 1);
msg->l2h[0] = IPAC_MSGT_PING;
ipa_msg_push_header(msg, IPAC_PROTO_IPACCESS);
ipa_client_conn_send(gsupc->link, msg);
}
static int gsup_client_connect(struct gsup_client *gsupc)
{
int rc;
if (gsupc->is_connected)
return 0;
if (osmo_timer_pending(&gsupc->connect_timer)) {
LOGP(DLGSUP, LOGL_DEBUG,
"GSUP connect: connect timer already running\n");
osmo_timer_del(&gsupc->connect_timer);
}
if (osmo_timer_pending(&gsupc->ping_timer)) {
LOGP(DLGSUP, LOGL_DEBUG,
"GSUP connect: ping timer already running\n");
osmo_timer_del(&gsupc->ping_timer);
}
if (ipa_client_conn_clear_queue(gsupc->link) > 0)
LOGP(DLGSUP, LOGL_DEBUG, "GSUP connect: discarded stored messages\n");
rc = ipa_client_conn_open(gsupc->link);
if (rc >= 0) {
LOGP(DLGSUP, LOGL_NOTICE, "GSUP connecting to %s:%d\n",
gsupc->link->addr, gsupc->link->port);
return 0;
}
LOGP(DLGSUP, LOGL_ERROR, "GSUP failed to connect to %s:%d: %s\n",
gsupc->link->addr, gsupc->link->port, strerror(-rc));
if (rc == -EBADF || rc == -ENOTSOCK || rc == -EAFNOSUPPORT ||
rc == -EINVAL)
return rc;
osmo_timer_schedule(&gsupc->connect_timer,
GSUP_CLIENT_RECONNECT_INTERVAL, 0);
LOGP(DLGSUP, LOGL_INFO, "Scheduled timer to retry GSUP connect to %s:%d\n",
gsupc->link->addr, gsupc->link->port);
return 0;
}
static void connect_timer_cb(void *gsupc_)
{
struct gsup_client *gsupc = gsupc_;
if (gsupc->is_connected)
return;
gsup_client_connect(gsupc);
}
static void client_send(struct gsup_client *gsupc, int proto_ext,
struct msgb *msg_tx)
{
ipa_prepend_header_ext(msg_tx, proto_ext);
ipa_msg_push_header(msg_tx, IPAC_PROTO_OSMO);
ipa_client_conn_send(gsupc->link, msg_tx);
/* msg_tx is now queued and will be freed. */
}
static void gsup_client_oap_register(struct gsup_client *gsupc)
{
struct msgb *msg_tx;
int rc;
rc = oap_client_register(&gsupc->oap_state, &msg_tx);
if ((rc < 0) || (!msg_tx)) {
LOGP(DLGSUP, LOGL_ERROR, "GSUP OAP set up, but cannot register.\n");
return;
}
client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
}
static void gsup_client_updown_cb(struct ipa_client_conn *link, int up)
{
struct gsup_client *gsupc = link->data;
LOGP(DLGSUP, LOGL_INFO, "GSUP link to %s:%d %s\n",
link->addr, link->port, up ? "UP" : "DOWN");
gsupc->is_connected = up;
if (up) {
start_test_procedure(gsupc);
if (gsupc->oap_state.state == OAP_INITIALIZED)
gsup_client_oap_register(gsupc);
osmo_timer_del(&gsupc->connect_timer);
} else {
osmo_timer_del(&gsupc->ping_timer);
osmo_timer_schedule(&gsupc->connect_timer,
GSUP_CLIENT_RECONNECT_INTERVAL, 0);
}
}
static int gsup_client_oap_handle(struct gsup_client *gsupc, struct msgb *msg_rx)
{
int rc;
struct msgb *msg_tx;
/* If the oap_state is disabled, this will reject the messages. */
rc = oap_client_handle(&gsupc->oap_state, msg_rx, &msg_tx);
msgb_free(msg_rx);
if (rc < 0)
return rc;
if (msg_tx)
client_send(gsupc, IPAC_PROTO_EXT_OAP, msg_tx);
return 0;
}
static int gsup_client_read_cb(struct ipa_client_conn *link, struct msgb *msg)
{
struct ipaccess_head *hh = (struct ipaccess_head *) msg->data;
struct ipaccess_head_ext *he = (struct ipaccess_head_ext *) msgb_l2(msg);
struct gsup_client *gsupc = (struct gsup_client *)link->data;
int rc;
struct ipaccess_unit ipa_dev = {
/* see gsup_client_create() on const vs non-const */
.unit_name = (char*)gsupc->unit_name,
};
OSMO_ASSERT(ipa_dev.unit_name);
msg->l2h = &hh->data[0];
rc = ipaccess_bts_handle_ccm(link, &ipa_dev, msg);
if (rc < 0) {
LOGP(DLGSUP, LOGL_NOTICE,
"GSUP received an invalid IPA/CCM message from %s:%d\n",
link->addr, link->port);
/* Link has been closed */
gsupc->is_connected = 0;
msgb_free(msg);
return -1;
}
if (rc == 1) {
uint8_t msg_type = *(msg->l2h);
/* CCM message */
if (msg_type == IPAC_MSGT_PONG) {
LOGP(DLGSUP, LOGL_DEBUG, "GSUP receiving PONG\n");
gsupc->got_ipa_pong = 1;
}
msgb_free(msg);
return 0;
}
if (hh->proto != IPAC_PROTO_OSMO)
goto invalid;
if (!he || msgb_l2len(msg) < sizeof(*he))
goto invalid;
msg->l2h = &he->data[0];
if (he->proto == IPAC_PROTO_EXT_GSUP) {
OSMO_ASSERT(gsupc->read_cb != NULL);
gsupc->read_cb(gsupc, msg);
/* expecting read_cb() to free msg */
} else if (he->proto == IPAC_PROTO_EXT_OAP) {
return gsup_client_oap_handle(gsupc, msg);
/* gsup_client_oap_handle frees msg */
} else
goto invalid;
return 0;
invalid:
LOGP(DLGSUP, LOGL_NOTICE,
"GSUP received an invalid IPA message from %s:%d, size = %d\n",
link->addr, link->port, msgb_length(msg));
msgb_free(msg);
return -1;
}
static void ping_timer_cb(void *gsupc_)
{
struct gsup_client *gsupc = gsupc_;
LOGP(DLGSUP, LOGL_INFO, "GSUP ping callback (%s, %s PONG)\n",
gsupc->is_connected ? "connected" : "not connected",
gsupc->got_ipa_pong ? "got" : "didn't get");
if (gsupc->got_ipa_pong) {
start_test_procedure(gsupc);
return;
}
LOGP(DLGSUP, LOGL_NOTICE, "GSUP ping timed out, reconnecting\n");
ipa_client_conn_close(gsupc->link);
gsupc->is_connected = 0;
gsup_client_connect(gsupc);
}
static void start_test_procedure(struct gsup_client *gsupc)
{
osmo_timer_setup(&gsupc->ping_timer, ping_timer_cb, gsupc);
gsupc->got_ipa_pong = 0;
osmo_timer_schedule(&gsupc->ping_timer, GSUP_CLIENT_PING_INTERVAL, 0);
LOGP(DLGSUP, LOGL_DEBUG, "GSUP sending PING\n");
gsup_client_send_ping(gsupc);
}
struct gsup_client *gsup_client_create(const char *unit_name,
const char *ip_addr,
unsigned int tcp_port,
gsup_client_read_cb_t read_cb,
struct oap_client_config *oapc_config)
{
struct gsup_client *gsupc;
int rc;
gsupc = talloc_zero(tall_bsc_ctx, struct gsup_client);
OSMO_ASSERT(gsupc);
/* struct ipaccess_unit has a non-const unit_name, so let's copy to be
* able to have a non-const unit_name here as well. To not taint the
* public gsup_client API, let's store it in a const char* anyway. */
gsupc->unit_name = talloc_strdup(gsupc, unit_name);
OSMO_ASSERT(gsupc->unit_name);
/* a NULL oapc_config will mark oap_state disabled. */
rc = oap_client_init(oapc_config, &gsupc->oap_state);
if (rc != 0)
goto failed;
gsupc->link = ipa_client_conn_create(gsupc,
/* no e1inp */ NULL,
0,
ip_addr, tcp_port,
gsup_client_updown_cb,
gsup_client_read_cb,
/* default write_cb */ NULL,
gsupc);
if (!gsupc->link)
goto failed;
osmo_timer_setup(&gsupc->connect_timer, connect_timer_cb, gsupc);
rc = gsup_client_connect(gsupc);
if (rc < 0)
goto failed;
gsupc->read_cb = read_cb;
return gsupc;
failed:
gsup_client_destroy(gsupc);
return NULL;
}
void gsup_client_destroy(struct gsup_client *gsupc)
{
osmo_timer_del(&gsupc->connect_timer);
osmo_timer_del(&gsupc->ping_timer);
if (gsupc->link) {
ipa_client_conn_close(gsupc->link);
ipa_client_conn_destroy(gsupc->link);
gsupc->link = NULL;
}
talloc_free(gsupc);
}
int gsup_client_send(struct gsup_client *gsupc, struct msgb *msg)
{
if (!gsupc || !gsupc->is_connected) {
LOGP(DGPRS, LOGL_ERROR, "GSUP not connected, unable to send %s\n", msgb_hexdump(msg));
msgb_free(msg);
return -ENOTCONN;
}
client_send(gsupc, IPAC_PROTO_EXT_GSUP, msg);
return 0;
}
struct msgb *gsup_client_msgb_alloc(void)
{
return msgb_alloc_headroom(4000, 64, __func__);
}

View File

@ -1,298 +0,0 @@
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/select.h>
#include <osmocom/core/application.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/gsup.h>
#include <openbsc/gsup_client.h>
#include <openbsc/debug.h>
static struct gsup_client *g_gc;
/***********************************************************************
* IMSI Operation
***********************************************************************/
static LLIST_HEAD(g_imsi_ops);
struct imsi_op_stats {
uint32_t num_alloc;
uint32_t num_released;
uint32_t num_rx_success;
uint32_t num_rx_error;
uint32_t num_timeout;
};
enum imsi_op_type {
IMSI_OP_SAI,
IMSI_OP_LU,
IMSI_OP_ISD,
_NUM_IMSI_OP
};
static const struct value_string imsi_op_names[] = {
{ IMSI_OP_SAI, "SAI" },
{ IMSI_OP_LU, "LU" },
{ IMSI_OP_ISD, "ISD" },
{ 0, NULL }
};
static struct imsi_op_stats imsi_op_stats[_NUM_IMSI_OP];
struct imsi_op {
struct llist_head list;
char imsi[17];
enum imsi_op_type type;
struct osmo_timer_list timer;
};
static struct imsi_op *imsi_op_find(const char *imsi,
enum imsi_op_type type)
{
struct imsi_op *io;
llist_for_each_entry(io, &g_imsi_ops, list) {
if (!strcmp(io->imsi, imsi) && io->type == type)
return io;
}
return NULL;
}
static void imsi_op_timer_cb(void *data);
static struct imsi_op *imsi_op_alloc(void *ctx, const char *imsi,
enum imsi_op_type type)
{
struct imsi_op *io;
if (imsi_op_find(imsi, type))
return NULL;
io = talloc_zero(ctx, struct imsi_op);
osmo_strlcpy(io->imsi, imsi, sizeof(io->imsi));
io->type = type;
osmo_timer_setup(&io->timer, imsi_op_timer_cb, io);
llist_add(&io->list, &g_imsi_ops);
imsi_op_stats[type].num_alloc++;
return io;
}
static void imsi_op_release(struct imsi_op *io)
{
osmo_timer_del(&io->timer);
llist_del(&io->list);
imsi_op_stats[io->type].num_released++;
talloc_free(io);
}
static void imsi_op_timer_cb(void *data)
{
struct imsi_op *io = data;
printf("%s: Timer expiration\n", io->imsi);
imsi_op_stats[io->type].num_timeout++;
imsi_op_release(io);
}
/* allocate + generate + send Send-Auth-Info */
int req_auth_info(const char *imsi)
{
struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_SAI);
struct osmo_gsup_message gsup = {0};
struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__);
osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi));
gsup.message_type = OSMO_GSUP_MSGT_SEND_AUTH_INFO_REQUEST;
osmo_gsup_encode(msg, &gsup);
return gsup_client_send(g_gc, msg);
}
/* allocate + generate + send Send-Auth-Info */
int req_loc_upd(const char *imsi)
{
struct imsi_op *io = imsi_op_alloc(g_gc, imsi, IMSI_OP_LU);
struct osmo_gsup_message gsup = {0};
struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__);
osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi));
gsup.message_type = OSMO_GSUP_MSGT_UPDATE_LOCATION_REQUEST;
osmo_gsup_encode(msg, &gsup);
return gsup_client_send(g_gc, msg);
}
int resp_isd(struct imsi_op *io)
{
struct osmo_gsup_message gsup = {0};
struct msgb *msg = msgb_alloc_headroom(1200, 200, __func__);
osmo_strlcpy(gsup.imsi, io->imsi, sizeof(gsup.imsi));
gsup.message_type = OSMO_GSUP_MSGT_INSERT_DATA_RESULT;
osmo_gsup_encode(msg, &gsup);
imsi_op_release(io);
return gsup_client_send(g_gc, msg);
}
/* receive an incoming GSUP message */
static void imsi_op_rx_gsup(struct imsi_op *io, const struct osmo_gsup_message *gsup)
{
int is_error = 0;
if (OSMO_GSUP_IS_MSGT_ERROR(gsup->message_type)) {
imsi_op_stats[io->type].num_rx_error++;
is_error = 1;
} else
imsi_op_stats[io->type].num_rx_success++;
switch (io->type) {
case IMSI_OP_SAI:
printf("%s; SAI Response%s\n", io->imsi, is_error ? ": ERROR" : "");
/* now that we have auth tuples, request LU */
req_loc_upd(io->imsi);
imsi_op_release(io);
break;
case IMSI_OP_LU:
printf("%s; LU Response%s\n", io->imsi, is_error ? ": ERROR" : "");
imsi_op_release(io);
break;
case IMSI_OP_ISD:
printf("%s; ISD Request%s\n", io->imsi, is_error ? ": ERROR" : "");
resp_isd(io);
break;
default:
printf("%s: Unknown\n", io->imsi);
imsi_op_release(io);
break;
}
}
static int op_type_by_gsup_msgt(enum osmo_gsup_message_type msg_type)
{
switch (msg_type) {
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_RESULT:
case OSMO_GSUP_MSGT_SEND_AUTH_INFO_ERROR:
return IMSI_OP_SAI;
case OSMO_GSUP_MSGT_UPDATE_LOCATION_RESULT:
case OSMO_GSUP_MSGT_UPDATE_LOCATION_ERROR:
return IMSI_OP_LU;
case OSMO_GSUP_MSGT_INSERT_DATA_REQUEST:
return IMSI_OP_ISD;
default:
printf("Unknown GSUP msg_type %u\n", msg_type);
return -1;
}
}
static int gsupc_read_cb(struct gsup_client *gsupc, struct msgb *msg)
{
struct osmo_gsup_message gsup_msg = {0};
struct imsi_op *io;
int rc;
DEBUGP(DGPRS, "Rx GSUP %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
rc = osmo_gsup_decode(msgb_l2(msg), msgb_l2len(msg), &gsup_msg);
if (rc < 0)
return rc;
if (!gsup_msg.imsi[0])
return -1;
rc = op_type_by_gsup_msgt(gsup_msg.message_type);
if (rc < 0)
return rc;
switch (rc) {
case IMSI_OP_SAI:
case IMSI_OP_LU:
io = imsi_op_find(gsup_msg.imsi, rc);
if (!io)
return -1;
break;
case IMSI_OP_ISD:
/* ISD is an inbound transaction */
io = imsi_op_alloc(g_gc, gsup_msg.imsi, IMSI_OP_ISD);
break;
}
imsi_op_rx_gsup(io, &gsup_msg);
msgb_free(msg);
return 0;
}
static void print_report(void)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(imsi_op_stats); i++) {
struct imsi_op_stats *st = &imsi_op_stats[i];
const char *name = get_value_string(imsi_op_names, i);
printf("%s: %u alloc, %u released, %u success, %u error , %u tout\n",
name, st->num_alloc, st->num_released, st->num_rx_success,
st->num_rx_error, st->num_timeout);
}
}
static void sig_cb(int sig)
{
switch (sig) {
case SIGINT:
print_report();
exit(0);
break;
}
}
void *tall_bsc_ctx = NULL;
/* default categories */
static struct log_info_cat default_categories[] = {
};
static const struct log_info gsup_test_client_log_info = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};
int main(int argc, char **argv)
{
unsigned long long i;
char *server_host = "127.0.0.1";
uint16_t server_port = OSMO_GSUP_PORT;
osmo_init_logging(&gsup_test_client_log_info);
g_gc = gsup_client_create("GSUPTEST", server_host, server_port,
gsupc_read_cb, NULL);
signal(SIGINT, sig_cb);
for (i = 0; i < 10000; i++) {
unsigned long long imsi = 901790000000000 + i;
char imsi_buf[17];
snprintf(imsi_buf, sizeof(imsi_buf), "%015llu", imsi);
req_auth_info(imsi_buf);
osmo_select_main(0);
}
while (1) {
osmo_select_main(0);
}
print_report();
exit(0);
}

View File

@ -28,12 +28,14 @@
#include <openbsc/gsm_data.h>
#include <openbsc/debug.h>
#include <openbsc/ipaccess.h>
#include <openbsc/common_cs.h>
#include <osmocom/core/talloc.h>
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/protocol/gsm_04_11.h>
#include <osmocom/gsm/gsm48.h>
int bsc_filter_barr_find(struct rb_root *root, const char *imsi, int *cm, int *lu)
{

View File

@ -1,75 +0,0 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
-I$(top_builddir) \
$(NULL)
AM_CFLAGS = \
-Wall \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOABIS_CFLAGS) \
$(COVERAGE_CFLAGS) \
$(LIBCRYPTO_CFLAGS) \
$(LIBSMPP34_CFLAGS) \
$(LIBASN1C_CFLAGS) \
$(LIBOSMOSIGTRAN_CFLAGS) \
$(NULL)
noinst_HEADERS = \
meas_feed.h \
$(NULL)
noinst_LIBRARIES = \
libmsc.a \
$(NULL)
libmsc_a_SOURCES = \
a_iface.c \
a_iface_bssap.c \
auth.c \
msc_vty.c \
db.c \
gsm_04_08.c \
gsm_04_11.c \
gsm_04_14.c \
gsm_04_80.c \
gsm_subscriber.c \
mncc.c \
mncc_builtin.c \
mncc_sock.c \
msc_ifaces.c \
rrlp.c \
silent_call.c \
sms_queue.c \
ussd.c \
vty_interface_layer3.c \
transaction.c \
osmo_msc.c \
ctrl_commands.c \
meas_feed.c \
subscr_conn.c \
$(NULL)
if BUILD_IU
libmsc_a_SOURCES += \
iucs.c \
iucs_ranap.c \
$(NULL)
else
libmsc_a_SOURCES += \
iu_dummy.c \
$(NULL)
endif
if BUILD_SMPP
noinst_HEADERS += \
smpp_smsc.h \
$(NULL)
libmsc_a_SOURCES += \
smpp_smsc.c \
smpp_openbsc.c \
smpp_vty.c \
smpp_utils.c \
$(NULL)
endif

View File

@ -1,591 +0,0 @@
/* (C) 2017 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 <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#include <osmocom/sigtran/sccp_helpers.h>
#include <osmocom/sigtran/sccp_sap.h>
#include <osmocom/sigtran/osmo_ss7.h>
#include <osmocom/sigtran/protocol/m3ua.h>
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <osmocom/gsm/gsm0808_utils.h>
#include <openbsc/debug.h>
#include <openbsc/msc_ifaces.h>
#include <openbsc/a_iface.h>
#include <openbsc/a_iface_bssap.h>
#include <openbsc/transaction.h>
#include <osmocom/legacy_mgcp/mgcpgw_client.h>
#include <osmocom/core/byteswap.h>
#include <osmocom/sccp/sccp_types.h>
#include <openbsc/a_reset.h>
#include <openbsc/osmo_msc.h>
/* A pointer to the GSM network we work with. By the current paradigm,
* there can only be one gsm_network per MSC. The pointer is set once
* when calling a_init() */
static struct gsm_network *gsm_network = NULL;
/* A struct to track currently active connections. We need that information
* to handle failure sitautions. In case of a problem, we must know which
* connections are currently open and which BSC is responsible. We also need
* the data to perform our connection checks (a_reset). All other logic will
* look at the connection ids and addresses that are supplied by the
* primitives */
struct bsc_conn {
struct llist_head list;
uint32_t conn_id; /* Connection identifier */
};
/* Internal list with connections we currently maintain. This
* list is of type struct bsc_conn (see above) */
static LLIST_HEAD(active_connections);
/* Record info of a new active connection in the active connection list */
static void record_bsc_con(const void *ctx, uint32_t conn_id)
{
struct bsc_conn *conn;
conn = talloc_zero(ctx, struct bsc_conn);
OSMO_ASSERT(conn);
conn->conn_id = conn_id;
llist_add_tail(&conn->list, &active_connections);
}
/* Delete info of a closed connection from the active connection list */
void a_delete_bsc_con(uint32_t conn_id)
{
struct bsc_conn *conn;
struct bsc_conn *conn_temp;
LOGP(DMSC, LOGL_DEBUG,
"Removing connection from active sccp-connection list (conn_id=%i)\n",
conn_id);
llist_for_each_entry_safe(conn, conn_temp, &active_connections, list) {
if (conn->conn_id == conn_id) {
llist_del(&conn->list);
talloc_free(conn);
}
}
}
/* Check if a specified connection id has an active SCCP connection */
static bool check_connection_active(uint32_t conn_id)
{
struct bsc_conn *conn;
/* Find the address for the current connection id */
llist_for_each_entry(conn, &active_connections, list) {
if (conn->conn_id == conn_id) {
return true;
}
}
return false;
}
/* Get the reset context for a specifiec calling (BSC) address */
static struct a_reset_ctx *get_reset_ctx_by_sccp_addr(const struct osmo_sccp_addr *addr)
{
struct bsc_context *bsc_ctx;
struct osmo_ss7_instance *ss7;
if (!addr)
return NULL;
llist_for_each_entry(bsc_ctx, &gsm_network->a.bscs, list) {
if (memcmp(&bsc_ctx->bsc_addr, addr, sizeof(*addr)) == 0)
return bsc_ctx->reset;
}
ss7 = osmo_ss7_instance_find(gsm_network->a.cs7_instance);
OSMO_ASSERT(ss7);
LOGP(DMSC, LOGL_ERROR, "The calling BSC (%s) is unknown to this MSC ...\n",
osmo_sccp_addr_name(ss7, addr));
return NULL;
}
/* Send DTAP message via A-interface */
int a_iface_tx_dtap(struct msgb *msg)
{
struct gsm_subscriber_connection *conn;
struct msgb *msg_resp;
/* FIXME: Set this to some meaninful value! */
uint8_t link_id = 0x00;
OSMO_ASSERT(msg);
conn = (struct gsm_subscriber_connection *)msg->dst;
OSMO_ASSERT(conn);
OSMO_ASSERT(conn->a.scu);
LOGP(DMSC, LOGL_DEBUG, "Passing DTAP message from MSC to BSC (conn_id=%i)\n", conn->a.conn_id);
msg->l3h = msg->data;
msg_resp = gsm0808_create_dtap(msg, link_id);
if (!msg_resp) {
LOGP(DMSC, LOGL_ERROR, "Unable to generate BSSMAP DTAP message!\n");
return -EINVAL;
} else
LOGP(DMSC, LOGL_DEBUG, "Massage will be sent as BSSMAP DTAP message!\n");
LOGP(DMSC, LOGL_DEBUG, "N-DATA.req(%u, %s)\n", conn->a.conn_id, osmo_hexdump(msg_resp->data, msg_resp->len));
return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg_resp);
}
/* Send Cipher mode command via A-interface */
int a_iface_tx_cipher_mode(const struct gsm_subscriber_connection *conn,
int cipher, const const uint8_t *key, int len, int include_imeisv)
{
/* TODO generalize for A- and Iu interfaces, don't name after 08.08 */
struct msgb *msg_resp;
struct gsm0808_encrypt_info ei;
OSMO_ASSERT(conn);
LOGP(DMSC, LOGL_DEBUG, "Passing Cipher mode command message from MSC to BSC (conn_id=%i)\n", conn->a.conn_id);
uint8_t crm = 0x01;
uint8_t *crm_ptr = NULL;
/* Setup encryption information */
if (len > ENCRY_INFO_KEY_MAXLEN || !key) {
LOGP(DMSC, LOGL_ERROR,
"Cipher mode command message could not be generated due to invalid key! (conn_id=%i)\n",
conn->a.conn_id);
return -EINVAL;
} else {
memcpy(&ei.key, key, len);
ei.key_len = len;
}
if (include_imeisv)
crm_ptr = &crm;
ei.perm_algo[0] = (uint8_t) (1 << cipher);
ei.perm_algo_len = 1;
msg_resp = gsm0808_create_cipher(&ei, crm_ptr);
LOGP(DMSC, LOGL_DEBUG, "N-DATA.req(%u, %s)\n", conn->a.conn_id, osmo_hexdump(msg_resp->data, msg_resp->len));
return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg_resp);
}
/* Page a subscriber via A-interface */
int a_iface_tx_paging(const char *imsi, uint32_t tmsi, uint16_t lac)
{
struct bsc_context *bsc_ctx;
struct gsm0808_cell_id_list cil;
struct msgb *msg;
int page_count = 0;
struct osmo_ss7_instance *ss7;
OSMO_ASSERT(imsi);
cil.id_discr = CELL_IDENT_LAC;
cil.id_list_lac[0] = lac;
cil.id_list_len = 1;
ss7 = osmo_ss7_instance_find(gsm_network->a.cs7_instance);
OSMO_ASSERT(ss7);
/* Deliver paging request to all known BSCs */
llist_for_each_entry(bsc_ctx, &gsm_network->a.bscs, list) {
if (a_reset_conn_ready(bsc_ctx->reset)) {
LOGP(DMSC, LOGL_DEBUG,
"Passing paging message from MSC %s to BSC %s (imsi=%s, tmsi=0x%08x, lac=%u)\n",
osmo_sccp_addr_name(ss7, &bsc_ctx->msc_addr),
osmo_sccp_addr_name(ss7, &bsc_ctx->bsc_addr), imsi, tmsi, lac);
msg = gsm0808_create_paging(imsi, &tmsi, &cil, NULL);
osmo_sccp_tx_unitdata_msg(bsc_ctx->sccp_user,
&bsc_ctx->msc_addr, &bsc_ctx->bsc_addr, msg);
page_count++;
} else {
LOGP(DMSC, LOGL_DEBUG,
"Connection down, dropping paging from MSC %s to BSC %s (imsi=%s, tmsi=0x%08x, lac=%u)\n",
osmo_sccp_addr_name(ss7, &bsc_ctx->msc_addr),
osmo_sccp_addr_name(ss7, &bsc_ctx->bsc_addr), imsi, tmsi, lac);
}
}
if (page_count <= 0)
LOGP(DMSC, LOGL_ERROR, "Could not deliver paging because none of the associated BSCs is available!\n");
return page_count;
}
/* Convert speech version field */
static uint8_t convert_Abis_sv_to_A_sv(int speech_ver)
{
/* The speech versions that are transmitted in the Bearer capability
* information element, that is transmitted on the Abis interfece
* use a different encoding than the permitted speech version
* identifier, that is signalled in the channel type element on the A
* interface. (See also 3GPP TS 48.008, 3.2.2.1 and 3GPP TS 24.008,
* 10.5.103 */
switch (speech_ver) {
case GSM48_BCAP_SV_FR:
return GSM0808_PERM_FR1;
break;
case GSM48_BCAP_SV_HR:
return GSM0808_PERM_HR1;
break;
case GSM48_BCAP_SV_EFR:
return GSM0808_PERM_FR2;
break;
case GSM48_BCAP_SV_AMR_F:
return GSM0808_PERM_FR3;
break;
case GSM48_BCAP_SV_AMR_H:
return GSM0808_PERM_HR3;
break;
case GSM48_BCAP_SV_AMR_OFW:
return GSM0808_PERM_FR4;
break;
case GSM48_BCAP_SV_AMR_OHW:
return GSM0808_PERM_HR4;
break;
case GSM48_BCAP_SV_AMR_FW:
return GSM0808_PERM_FR5;
break;
case GSM48_BCAP_SV_AMR_OH:
return GSM0808_PERM_HR6;
break;
}
/* If nothing matches, tag the result as invalid */
LOGP(DMSC, LOGL_ERROR, "Invalid permitted speech version / rate detected, discarding.\n");
return 0xFF;
}
/* Convert speech preference field */
static uint8_t convert_Abis_prev_to_A_pref(int radio)
{
/* The Radio channel requirement field that is transmitted in the
* Bearer capability information element, that is transmitted on the
* Abis interfece uses a different encoding than the Channel rate and
* type field that is signalled in the channel type element on the A
* interface. (See also 3GPP TS 48.008, 3.2.2.1 and 3GPP TS 24.008,
* 10.5.102 */
switch (radio) {
case GSM48_BCAP_RRQ_FR_ONLY:
return GSM0808_SPEECH_FULL_BM;
case GSM48_BCAP_RRQ_DUAL_FR:
return GSM0808_SPEECH_FULL_PREF;
case GSM48_BCAP_RRQ_DUAL_HR:
return GSM0808_SPEECH_HALF_PREF;
}
LOGP(DMSC, LOGL_ERROR, "Invalid speech version / rate combination preference, defaulting to full rate.\n");
return GSM0808_SPEECH_FULL_BM;
}
/* Assemble the channel type field */
static int enc_channel_type(struct gsm0808_channel_type *ct, const struct gsm_mncc_bearer_cap *bc)
{
unsigned int i;
uint8_t sv;
unsigned int count = 0;
bool only_gsm_hr = true;
OSMO_ASSERT(ct);
OSMO_ASSERT(bc);
ct->ch_indctr = GSM0808_CHAN_SPEECH;
for (i = 0; i < ARRAY_SIZE(bc->speech_ver); i++) {
if (bc->speech_ver[i] == -1)
break;
sv = convert_Abis_sv_to_A_sv(bc->speech_ver[i]);
if (sv != 0xFF) {
/* Detect if something else than
* GSM HR V1 is supported */
if (sv == GSM0808_PERM_HR2 ||
sv == GSM0808_PERM_HR3 || sv == GSM0808_PERM_HR4 || sv == GSM0808_PERM_HR6)
only_gsm_hr = false;
ct->perm_spch[count] = sv;
count++;
}
}
ct->perm_spch_len = count;
if (only_gsm_hr)
/* Note: We must avoid the usage of GSM HR1 as this
* codec only offers very poor audio quality. If the
* MS only supports GSM HR1 (and full rate), and has
* a preference for half rate. Then we will ignore the
* preference and assume a preference for full rate. */
ct->ch_rate_type = GSM0808_SPEECH_FULL_BM;
else
ct->ch_rate_type = convert_Abis_prev_to_A_pref(bc->radio);
if (count)
return 0;
else
return -EINVAL;
}
/* Assemble the speech codec field */
static int enc_speech_codec_list(struct gsm0808_speech_codec_list *scl, const struct gsm0808_channel_type *ct)
{
unsigned int i;
int rc;
memset(scl, 0, sizeof(*scl));
for (i = 0; i < ct->perm_spch_len; i++) {
rc = gsm0808_speech_codec_from_chan_type(&scl->codec[i], ct->perm_spch[i]);
if (rc != 0)
return -EINVAL;
}
scl->len = i;
return 0;
}
/* Send assignment request via A-interface */
int a_iface_tx_assignment(const struct gsm_trans *trans)
{
struct gsm_subscriber_connection *conn;
struct gsm0808_channel_type ct;
struct gsm0808_speech_codec_list scl;
uint32_t *ci_ptr = NULL;
struct msgb *msg;
struct sockaddr_storage rtp_addr;
struct sockaddr_in rtp_addr_in;
int rc;
OSMO_ASSERT(trans);
conn = trans->conn;
OSMO_ASSERT(conn);
LOGP(DMSC, LOGL_ERROR, "Sending assignment command to BSC (conn_id %u)\n", conn->a.conn_id);
/* Channel type */
rc = enc_channel_type(&ct, &trans->bearer_cap);
if (rc < 0) {
LOGP(DMSC, LOGL_ERROR, "Faild to generate channel type -- assignment not sent!\n");
return -EINVAL;
}
/* Speech codec list */
rc = enc_speech_codec_list(&scl, &ct);
if (rc < 0) {
LOGP(DMSC, LOGL_ERROR, "Faild to generate Speech codec list -- assignment not sent!\n");
return -EINVAL;
}
/* Package RTP-Address data */
memset(&rtp_addr_in, 0, sizeof(rtp_addr_in));
rtp_addr_in.sin_family = AF_INET;
rtp_addr_in.sin_port = osmo_htons(conn->rtp.port_subscr);
rtp_addr_in.sin_addr.s_addr = osmo_htonl(mgcpgw_client_remote_addr_n(gsm_network->mgcpgw.client));
memset(&rtp_addr, 0, sizeof(rtp_addr));
memcpy(&rtp_addr, &rtp_addr_in, sizeof(rtp_addr_in));
msg = gsm0808_create_ass(&ct, NULL, &rtp_addr, &scl, ci_ptr);
LOGP(DMSC, LOGL_DEBUG, "N-DATA.req(%u, %s)\n", conn->a.conn_id, osmo_hexdump(msg->data, msg->len));
return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg);
}
/* Send clear command via A-interface */
int a_iface_tx_clear_cmd(struct gsm_subscriber_connection *conn)
{
struct msgb *msg;
LOGP(DMSC, LOGL_NOTICE, "Sending clear command to BSC (conn_id=%u)\n", conn->a.conn_id);
msg = gsm0808_create_clear_command(GSM0808_CAUSE_CALL_CONTROL);
return osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg);
}
/* Callback function: Close all open connections */
static void a_reset_cb(const void *priv)
{
struct msgb *msg;
struct bsc_context *bsc_ctx = (struct bsc_context*) priv;
struct osmo_ss7_instance *ss7;
/* Skip if the A interface is not properly initalized yet */
if (!gsm_network)
return;
/* Clear all now orphaned subscriber connections */
a_clear_all(bsc_ctx->sccp_user, &bsc_ctx->bsc_addr);
/* Send reset to the remote BSC */
ss7 = osmo_ss7_instance_find(gsm_network->a.cs7_instance);
OSMO_ASSERT(ss7);
LOGP(DMSC, LOGL_NOTICE, "Sending RESET to BSC %s\n", osmo_sccp_addr_name(ss7, &bsc_ctx->bsc_addr));
msg = gsm0808_create_reset();
osmo_sccp_tx_unitdata_msg(bsc_ctx->sccp_user, &bsc_ctx->msc_addr,
&bsc_ctx->bsc_addr, msg);
}
/* Add a new BSC connection to our internal list with known BSCs */
static void add_bsc(const struct osmo_sccp_addr *msc_addr, const struct osmo_sccp_addr *bsc_addr,
struct osmo_sccp_user *scu)
{
struct bsc_context *bsc_ctx;
struct osmo_ss7_instance *ss7;
OSMO_ASSERT(bsc_addr);
OSMO_ASSERT(msc_addr);
OSMO_ASSERT(scu);
/* Check if we already know this BSC, if yes, skip adding it. */
if (get_reset_ctx_by_sccp_addr(bsc_addr))
return;
ss7 = osmo_ss7_instance_find(gsm_network->a.cs7_instance);
OSMO_ASSERT(ss7);
LOGP(DMSC, LOGL_NOTICE, "Adding new BSC connection for BSC %s...\n", osmo_sccp_addr_name(ss7, bsc_addr));
/* Generate and fill up a new bsc context */
bsc_ctx = talloc_zero(gsm_network, struct bsc_context);
OSMO_ASSERT(bsc_ctx);
memcpy(&bsc_ctx->bsc_addr, bsc_addr, sizeof(*bsc_addr));
memcpy(&bsc_ctx->msc_addr, msc_addr, sizeof(*msc_addr));
bsc_ctx->sccp_user = scu;
llist_add_tail(&bsc_ctx->list, &gsm_network->a.bscs);
/* Start reset procedure to make the new connection active */
bsc_ctx->reset = a_reset_alloc(bsc_ctx, osmo_sccp_addr_name(ss7, bsc_addr), a_reset_cb, bsc_ctx);
}
/* Callback function, called by the SSCP stack when data arrives */
static int sccp_sap_up(struct osmo_prim_hdr *oph, void *_scu)
{
struct osmo_sccp_user *scu = _scu;
struct osmo_scu_prim *scu_prim = (struct osmo_scu_prim *)oph;
int rc = 0;
struct a_conn_info a_conn_info;
memset(&a_conn_info, 0, sizeof(a_conn_info));
a_conn_info.network = gsm_network;
a_conn_info.reset = NULL;
switch (OSMO_PRIM_HDR(&scu_prim->oph)) {
case OSMO_PRIM(OSMO_SCU_PRIM_N_CONNECT, PRIM_OP_INDICATION):
/* Handle inbound connection indication */
add_bsc(&scu_prim->u.connect.called_addr, &scu_prim->u.connect.calling_addr, scu);
a_conn_info.conn_id = scu_prim->u.connect.conn_id;
a_conn_info.msc_addr = &scu_prim->u.connect.called_addr;
a_conn_info.bsc_addr = &scu_prim->u.connect.calling_addr;
a_conn_info.reset = get_reset_ctx_by_sccp_addr(&scu_prim->u.unitdata.calling_addr);
if (a_reset_conn_ready(a_conn_info.reset) == false) {
rc = osmo_sccp_tx_disconn(scu, a_conn_info.conn_id, a_conn_info.msc_addr,
SCCP_RETURN_CAUSE_UNQUALIFIED);
break;
}
osmo_sccp_tx_conn_resp(scu, scu_prim->u.connect.conn_id, &scu_prim->u.connect.called_addr, NULL, 0);
if (msgb_l2len(oph->msg) > 0) {
LOGP(DMSC, LOGL_DEBUG, "N-CONNECT.ind(%u, %s)\n",
scu_prim->u.connect.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
rc = sccp_rx_dt(scu, &a_conn_info, oph->msg);
} else
LOGP(DMSC, LOGL_DEBUG, "N-CONNECT.ind(%u)\n", scu_prim->u.connect.conn_id);
record_bsc_con(scu, scu_prim->u.connect.conn_id);
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_DATA, PRIM_OP_INDICATION):
/* Handle incoming connection oriented data */
a_conn_info.conn_id = scu_prim->u.data.conn_id;
LOGP(DMSC, LOGL_DEBUG, "N-DATA.ind(%u, %s)\n",
scu_prim->u.data.conn_id, osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
sccp_rx_dt(scu, &a_conn_info, oph->msg);
break;
case OSMO_PRIM(OSMO_SCU_PRIM_N_UNITDATA, PRIM_OP_INDICATION):
/* Handle inbound UNITDATA */
add_bsc(&scu_prim->u.unitdata.called_addr, &scu_prim->u.unitdata.calling_addr, scu);
a_conn_info.msc_addr = &scu_prim->u.unitdata.called_addr;
a_conn_info.bsc_addr = &scu_prim->u.unitdata.calling_addr;
a_conn_info.reset = get_reset_ctx_by_sccp_addr(&scu_prim->u.unitdata.calling_addr);
DEBUGP(DMSC, "N-UNITDATA.ind(%s)\n", osmo_hexdump(msgb_l2(oph->msg), msgb_l2len(oph->msg)));
sccp_rx_udt(scu, &a_conn_info, oph->msg);
break;
default:
LOGP(DMSC, LOGL_ERROR, "Unhandled SIGTRAN primitive: %u:%u\n", oph->primitive, oph->operation);
break;
}
return rc;
}
/* Clear all subscriber connections on a specified BSC */
void a_clear_all(struct osmo_sccp_user *scu, const struct osmo_sccp_addr *bsc_addr)
{
struct gsm_subscriber_connection *conn;
struct gsm_subscriber_connection *conn_temp;
struct gsm_network *network = gsm_network;
OSMO_ASSERT(scu);
OSMO_ASSERT(bsc_addr);
llist_for_each_entry_safe(conn, conn_temp, &network->subscr_conns, entry) {
/* Clear only A connections and connections that actually
* belong to the specified BSC */
if (conn->via_ran == RAN_GERAN_A && memcmp(bsc_addr, &conn->a.bsc_addr, sizeof(conn->a.bsc_addr)) == 0) {
LOGP(DMSC, LOGL_NOTICE, "Dropping orphaned subscriber connection (conn_id %i)\n",
conn->a.conn_id);
msc_clear_request(conn, GSM48_CC_CAUSE_SWITCH_CONG);
/* If there is still an SCCP connection active, remove it now */
if (check_connection_active(conn->a.conn_id)) {
osmo_sccp_tx_disconn(scu, conn->a.conn_id, bsc_addr,
SCCP_RELEASE_CAUSE_END_USER_ORIGINATED);
a_delete_bsc_con(conn->a.conn_id);
}
}
}
}
/* Initalize A interface connection between to MSC and BSC */
int a_init(struct osmo_sccp_instance *sccp, struct gsm_network *network)
{
OSMO_ASSERT(sccp);
OSMO_ASSERT(network);
/* FIXME: Remove hardcoded parameters, use parameters in parameter list */
LOGP(DMSC, LOGL_NOTICE, "Initalizing SCCP connection to stp...\n");
/* Set GSM network variable, there can only be
* one network by design */
if (gsm_network != NULL) {
OSMO_ASSERT(gsm_network == network);
} else
gsm_network = network;
/* SCCP Protocol stack */
osmo_sccp_user_bind(sccp, "OsmoMSC-A", sccp_sap_up, SCCP_SSN_BSSAP);
return 0;
}

View File

@ -1,716 +0,0 @@
/* (C) 2017 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 <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#include <osmocom/sigtran/sccp_helpers.h>
#include <osmocom/sccp/sccp_types.h>
#include <osmocom/gsm/gsm0808.h>
#include <osmocom/gsm/gsm0808_utils.h>
#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <openbsc/a_iface_bssap.h>
#include <openbsc/a_iface.h>
#include <openbsc/osmo_msc.h>
#include <osmocom/core/byteswap.h>
#include <openbsc/a_reset.h>
#define IP_V4_ADDR_LEN 4
/*
* Helper functions to lookup and allocate subscribers
*/
/* Allocate a new subscriber connection */
static struct gsm_subscriber_connection *subscr_conn_allocate_a(const struct a_conn_info *a_conn_info,
struct gsm_network *network,
uint16_t lac, struct osmo_sccp_user *scu, int conn_id)
{
struct gsm_subscriber_connection *conn;
LOGP(DMSC, LOGL_NOTICE, "Allocating A-Interface subscriber conn: lac %i, conn_id %i\n", lac, conn_id);
conn = talloc_zero(network, struct gsm_subscriber_connection);
if (!conn)
return NULL;
conn->network = network;
conn->via_ran = RAN_GERAN_A;
conn->lac = lac;
conn->a.conn_id = conn_id;
conn->a.scu = scu;
/* Also backup the calling address of the BSC, this allows us to
* identify later which BSC is responsible for this subscriber connection */
memcpy(&conn->a.bsc_addr, a_conn_info->bsc_addr, sizeof(conn->a.bsc_addr));
llist_add_tail(&conn->entry, &network->subscr_conns);
LOGP(DMSC, LOGL_NOTICE, "A-Interface subscriber connection successfully allocated!\n");
return conn;
}
/* Return an existing A subscriber connection record for the given
* connection IDs, or return NULL if not found. */
static struct gsm_subscriber_connection *subscr_conn_lookup_a(const struct gsm_network *network, int conn_id)
{
struct gsm_subscriber_connection *conn;
OSMO_ASSERT(network);
DEBUGP(DMSC, "Looking for A subscriber: conn_id %i\n", conn_id);
/* FIXME: log_subscribers() is defined in iucs.c as static inline, if
* maybe this function should be public to reach it from here? */
/* log_subscribers(network); */
llist_for_each_entry(conn, &network->subscr_conns, entry) {
if (conn->via_ran == RAN_GERAN_A && conn->a.conn_id == conn_id) {
DEBUGP(DIUCS, "Found A subscriber for conn_id %i\n", conn_id);
return conn;
}
}
DEBUGP(DMSC, "No A subscriber found for conn_id %i\n", conn_id);
return NULL;
}
/*
* BSSMAP handling for UNITDATA
*/
/* Endpoint to handle BSSMAP reset */
static void bssmap_rx_reset(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
{
struct gsm_network *network = a_conn_info->network;
struct osmo_ss7_instance *ss7;
ss7 = osmo_ss7_instance_find(network->a.cs7_instance);
OSMO_ASSERT(ss7);
LOGP(DMSC, LOGL_NOTICE, "Rx RESET from BSC %s, sending RESET ACK\n",
osmo_sccp_addr_name(ss7, a_conn_info->bsc_addr));
osmo_sccp_tx_unitdata_msg(scu, a_conn_info->msc_addr, a_conn_info->bsc_addr, gsm0808_create_reset_ack());
/* Make sure all orphand subscriber connections will be cleard */
a_clear_all(scu, a_conn_info->bsc_addr);
msgb_free(msg);
}
/* Endpoint to handle BSSMAP reset acknowlegement */
static void bssmap_rx_reset_ack(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info,
struct msgb *msg)
{
struct gsm_network *network = a_conn_info->network;
struct osmo_ss7_instance *ss7;
ss7 = osmo_ss7_instance_find(network->a.cs7_instance);
OSMO_ASSERT(ss7);
if (a_conn_info->reset == NULL) {
LOGP(DMSC, LOGL_ERROR, "Received RESET ACK from an unknown BSC %s, ignoring...\n",
osmo_sccp_addr_name(ss7, a_conn_info->bsc_addr));
goto fail;
}
LOGP(DMSC, LOGL_NOTICE, "Received RESET ACK from BSC %s\n", osmo_sccp_addr_name(ss7, a_conn_info->bsc_addr));
/* Confirm that we managed to get the reset ack message
* towards the connection reset logic */
a_reset_ack_confirm(a_conn_info->reset);
fail:
msgb_free(msg);
}
/* Handle UNITDATA BSSMAP messages */
static void bssmap_rcvmsg_udt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
{
/* Note: When in the MSC role, RESET ACK is the only valid message that
* can be received via UNITDATA */
if (msgb_l3len(msg) < 1) {
LOGP(DMSC, LOGL_NOTICE, "Error: No data received -- discarding message!\n");
return;
}
LOGP(DMSC, LOGL_NOTICE, "Rx BSC UDT BSSMAP %s\n", gsm0808_bssmap_name(msg->l3h[0]));
switch (msg->l3h[0]) {
case BSS_MAP_MSG_RESET:
bssmap_rx_reset(scu, a_conn_info, msg);
break;
case BSS_MAP_MSG_RESET_ACKNOWLEDGE:
bssmap_rx_reset_ack(scu, a_conn_info, msg);
break;
default:
LOGP(DMSC, LOGL_NOTICE, "Unimplemented message format: %s -- message discarded!\n",
gsm0808_bssmap_name(msg->l3h[0]));
msgb_free(msg);
}
}
/* Receive incoming connection less data messages via sccp */
void sccp_rx_udt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
{
/* Note: The only valid message type that can be received
* via UNITDATA are BSS Management messages */
struct bssmap_header *bs;
OSMO_ASSERT(scu);
OSMO_ASSERT(a_conn_info);
OSMO_ASSERT(msg);
LOGP(DMSC, LOGL_NOTICE, "Rx BSC UDT: %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
if (msgb_l2len(msg) < sizeof(*bs)) {
LOGP(DMSC, LOGL_ERROR, "Error: Header is too short -- discarding message!\n");
msgb_free(msg);
return;
}
bs = (struct bssmap_header *)msgb_l2(msg);
if (bs->length < msgb_l2len(msg) - sizeof(*bs)) {
LOGP(DMSC, LOGL_ERROR, "Error: Message is too short -- discarding message!\n");
msgb_free(msg);
return;
}
switch (bs->type) {
case BSSAP_MSG_BSS_MANAGEMENT:
msg->l3h = &msg->l2h[sizeof(struct bssmap_header)];
bssmap_rcvmsg_udt(scu, a_conn_info, msg);
break;
default:
LOGP(DMSC, LOGL_ERROR,
"Error: Unimplemented message type: %s -- message discarded!\n", gsm0808_bssmap_name(bs->type));
msgb_free(msg);
}
}
/*
* BSSMAP handling for connection oriented data
*/
/* Endpoint to handle BSSMAP clear request */
static int bssmap_rx_clear_rqst(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
{
struct gsm_network *network = a_conn_info->network;
struct tlv_parsed tp;
int rc;
struct msgb *msg_resp;
uint8_t cause;
struct gsm_subscriber_connection *conn;
LOGP(DMSC, LOGL_NOTICE, "BSC requested to clear connection (conn_id=%i)\n", a_conn_info->conn_id);
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE)) {
LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n");
goto fail;
}
cause = TLVP_VAL(&tp, GSM0808_IE_CAUSE)[0];
/* Respond with clear command */
msg_resp = gsm0808_create_clear_command(GSM0808_CAUSE_CALL_CONTROL);
rc = osmo_sccp_tx_data_msg(scu, a_conn_info->conn_id, msg_resp);
/* If possible, inform the MSC about the clear request */
conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
if (!conn)
goto fail;
msc_clear_request(conn, cause);
msgb_free(msg);
return rc;
fail:
msgb_free(msg);
return -EINVAL;
}
/* Endpoint to handle BSSMAP clear complete */
static int bssmap_rx_clear_complete(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
{
int rc;
LOGP(DMSC, LOGL_NOTICE, "Releasing connection (conn_id=%i)\n", a_conn_info->conn_id);
rc = osmo_sccp_tx_disconn(scu, a_conn_info->conn_id,
a_conn_info->msc_addr, SCCP_RELEASE_CAUSE_END_USER_ORIGINATED);
/* Remove the record from the list with active connections. */
a_delete_bsc_con(a_conn_info->conn_id);
msgb_free(msg);
return rc;
}
/* Endpoint to handle layer 3 complete messages */
static int bssmap_rx_l3_compl(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
{
struct tlv_parsed tp;
struct {
uint8_t ident;
struct gsm48_loc_area_id lai;
uint16_t ci;
} __attribute__ ((packed)) lai_ci;
uint16_t mcc;
uint16_t mnc;
uint16_t lac;
uint8_t data_length;
const uint8_t *data;
int rc;
struct gsm_network *network = a_conn_info->network;
struct gsm_subscriber_connection *conn;
LOGP(DMSC, LOGL_NOTICE, "BSC has completed layer 3 connection (conn_id=%i)\n", a_conn_info->conn_id);
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
if (!TLVP_PRESENT(&tp, GSM0808_IE_CELL_IDENTIFIER)) {
LOGP(DMSC, LOGL_ERROR, "Mandatory CELL IDENTIFIER not present -- discarding message!\n");
goto fail;
}
if (!TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_INFORMATION)) {
LOGP(DMSC, LOGL_ERROR, "Mandatory LAYER 3 INFORMATION not present -- discarding message!\n");
goto fail;
}
/* Parse Cell ID element */
/* FIXME: Encapsulate this in a parser/generator function inside
* libosmocore, add support for all specified cell identification
* discriminators (see 3GPP ts 3.2.2.17 Cell Identifier) */
data_length = TLVP_LEN(&tp, GSM0808_IE_CELL_IDENTIFIER);
data = TLVP_VAL(&tp, GSM0808_IE_CELL_IDENTIFIER);
if (sizeof(lai_ci) != data_length) {
LOGP(DMSC, LOGL_ERROR,
"Unable to parse element CELL IDENTIFIER (wrong field length) -- discarding message!\n");
goto fail;
}
memcpy(&lai_ci, data, sizeof(lai_ci));
if (lai_ci.ident != CELL_IDENT_WHOLE_GLOBAL) {
LOGP(DMSC, LOGL_ERROR,
"Unable to parse element CELL IDENTIFIER (wrong cell identification discriminator) -- discarding message!\n");
goto fail;
}
if (gsm48_decode_lai(&lai_ci.lai, &mcc, &mnc, &lac) != 0) {
LOGP(DMSC, LOGL_ERROR,
"Unable to parse element CELL IDENTIFIER (lai decoding failed) -- discarding message!\n");
goto fail;
}
/* Parse Layer 3 Information element */
/* FIXME: This is probably to hackish, compiler also complains "assignment discards const qualifier..." */
msg->l3h = TLVP_VAL(&tp, GSM0808_IE_LAYER_3_INFORMATION);
msg->tail = msg->l3h + TLVP_LEN(&tp, GSM0808_IE_LAYER_3_INFORMATION);
/* Create new subscriber context */
conn = subscr_conn_allocate_a(a_conn_info, network, lac, scu, a_conn_info->conn_id);
/* Handover location update to the MSC code */
/* msc_compl_l3() takes ownership of dtap_msg
* message buffer */
rc = msc_compl_l3(conn, msg, 0);
if (rc == MSC_CONN_ACCEPT) {
LOGP(DMSC, LOGL_NOTICE, "User has been accepted by MSC.\n");
return 0;
} else if (rc == MSC_CONN_REJECT)
LOGP(DMSC, LOGL_NOTICE, "User has been rejected by MSC.\n");
else
LOGP(DMSC, LOGL_NOTICE, "User has been rejected by MSC (unknown error)\n");
return -EINVAL;
fail:
msgb_free(msg);
return -EINVAL;
}
/* Endpoint to handle BSSMAP classmark update */
static int bssmap_rx_classmark_upd(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
{
struct gsm_network *network = a_conn_info->network;
struct gsm_subscriber_connection *conn;
struct tlv_parsed tp;
const uint8_t *cm2 = NULL;
const uint8_t *cm3 = NULL;
uint8_t cm2_len = 0;
uint8_t cm3_len = 0;
conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
if (!conn)
goto fail;
LOGP(DMSC, LOGL_NOTICE, "BSC sends clasmark update (conn_id=%i)\n", conn->a.conn_id);
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
if (!TLVP_PRESENT(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T2)) {
LOGP(DMSC, LOGL_ERROR, "Mandatory Classmark Information Type 2 not present -- discarding message!\n");
goto fail;
}
cm2 = TLVP_VAL(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T2);
cm2_len = TLVP_LEN(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T2);
if (TLVP_PRESENT(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T3)) {
cm3 = TLVP_VAL(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T3);
cm3_len = TLVP_LEN(&tp, GSM0808_IE_CLASSMARK_INFORMATION_T3);
}
/* Inform MSC about the classmark change */
msc_classmark_chg(conn, cm2, cm2_len, cm3, cm3_len);
msgb_free(msg);
return 0;
fail:
msgb_free(msg);
return -EINVAL;
}
/* Endpoint to handle BSSMAP cipher mode complete */
static int bssmap_rx_ciph_compl(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info,
struct msgb *msg)
{
/* FIXME: The field GSM0808_IE_LAYER_3_MESSAGE_CONTENTS is optional by
* means of the specification. So there can be messages without L3 info.
* In this case, the code will crash becrause msc_cipher_mode_compl()
* is not able to deal with msg = NULL and apperently
* msc_cipher_mode_compl() was never meant to be used without L3 data.
* This needs to be discussed further! */
struct gsm_network *network = a_conn_info->network;
struct gsm_subscriber_connection *conn;
struct tlv_parsed tp;
uint8_t alg_id = 1;
conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
if (!conn)
goto fail;
LOGP(DMSC, LOGL_NOTICE, "BSC sends cipher mode complete (conn_id=%i)\n", conn->a.conn_id);
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
if (TLVP_PRESENT(&tp, GSM0808_IE_CHOSEN_ENCR_ALG)) {
alg_id = TLVP_VAL(&tp, GSM0808_IE_CHOSEN_ENCR_ALG)[0] - 1;
}
if (TLVP_PRESENT(&tp, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS)) {
msg->l3h = TLVP_VAL(&tp, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS);
msg->tail = msg->l3h + TLVP_LEN(&tp, GSM0808_IE_LAYER_3_MESSAGE_CONTENTS);
} else {
msgb_free(msg);
msg = NULL;
}
/* Hand over cipher mode complete message to the MSC,
* msc_cipher_mode_compl() takes ownership for msg */
msc_cipher_mode_compl(conn, msg, alg_id);
return 0;
fail:
msgb_free(msg);
return -EINVAL;
}
/* Endpoint to handle BSSMAP cipher mode reject */
static int bssmap_rx_ciph_rej(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
{
struct gsm_network *network = a_conn_info->network;
struct gsm_subscriber_connection *conn;
struct tlv_parsed tp;
uint8_t cause;
conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
if (!conn)
goto fail;
LOGP(DMSC, LOGL_NOTICE, "BSC sends cipher mode reject (conn_id=%i)\n", conn->a.conn_id);
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
if (!TLVP_PRESENT(&tp, BSS_MAP_MSG_CIPHER_MODE_REJECT)) {
LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n");
goto fail;
}
cause = TLVP_VAL(&tp, BSS_MAP_MSG_CIPHER_MODE_REJECT)[0];
LOGP(DMSC, LOGL_NOTICE, "Cipher mode rejection cause: %i\n", cause);
/* FIXME: Can we do something meaningful here? e.g. report to the
* msc code somehow that the cipher mode command has failed. */
msgb_free(msg);
return 0;
fail:
msgb_free(msg);
return -EINVAL;
}
/* Endpoint to handle BSSMAP assignment failure */
static int bssmap_rx_ass_fail(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
{
struct gsm_network *network = a_conn_info->network;
struct gsm_subscriber_connection *conn;
struct tlv_parsed tp;
uint8_t cause;
uint8_t *rr_cause_ptr = NULL;
uint8_t rr_cause;
conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
if (!conn)
goto fail;
LOGP(DMSC, LOGL_NOTICE, "BSC sends assignment failure message (conn_id=%i)\n", conn->a.conn_id);
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE)) {
LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n");
goto fail;
}
cause = TLVP_VAL(&tp, GSM0808_IE_CAUSE)[0];
if (TLVP_PRESENT(&tp, GSM0808_IE_RR_CAUSE)) {
rr_cause = TLVP_VAL(&tp, GSM0808_IE_RR_CAUSE)[0];
rr_cause_ptr = &rr_cause;
}
/* FIXME: In AoIP, the Assignment failure will carry also an optional
* Codec List (BSS Supported) element. It has to be discussed if we
* can ignore this element. If not, The msc_assign_fail() function
* call has to change. However msc_assign_fail() does nothing in the
* end. So probably we can just leave it as it is. Even for AoIP */
/* Inform the MSC about the assignment failure event */
msc_assign_fail(conn, cause, rr_cause_ptr);
msgb_free(msg);
return 0;
fail:
msgb_free(msg);
return -EINVAL;
}
/* Endpoint to handle sapi "n" reject */
static int bssmap_rx_sapi_n_rej(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info,
struct msgb *msg)
{
struct gsm_network *network = a_conn_info->network;
struct gsm_subscriber_connection *conn;
struct tlv_parsed tp;
uint8_t dlci;
conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
if (!conn)
goto fail;
LOGP(DMSC, LOGL_NOTICE, "BSC sends sapi \"n\" reject message (conn_id=%i)\n", conn->a.conn_id);
/* Note: The MSC code seems not to care about the cause code, but by
* the specification it is mandatory, so we check its presence. See
* also 3GPP TS 48.008 3.2.1.34 SAPI "n" REJECT */
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
if (!TLVP_PRESENT(&tp, GSM0808_IE_CAUSE)) {
LOGP(DMSC, LOGL_ERROR, "Cause code is missing -- discarding message!\n");
goto fail;
}
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
if (!TLVP_PRESENT(&tp, GSM0808_IE_DLCI)) {
LOGP(DMSC, LOGL_ERROR, "DLCI is missing -- discarding message!\n");
goto fail;
}
dlci = TLVP_VAL(&tp, GSM0808_IE_DLCI)[0];
/* Inform the MSC about the sapi "n" reject event */
msc_sapi_n_reject(conn, dlci);
msgb_free(msg);
return 0;
fail:
msgb_free(msg);
return -EINVAL;
}
/* Endpoint to handle assignment complete */
static int bssmap_rx_ass_compl(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info,
struct msgb *msg)
{
struct gsm_network *network = a_conn_info->network;
struct gsm_subscriber_connection *conn;
struct mgcpgw_client *mgcp;
struct tlv_parsed tp;
struct sockaddr_storage rtp_addr;
struct sockaddr_in *rtp_addr_in;
int rc;
conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
if (!conn)
goto fail;
mgcp = conn->network->mgcpgw.client;
OSMO_ASSERT(mgcp);
LOGP(DMSC, LOGL_NOTICE, "BSC sends assignment complete message (conn_id=%i)\n", conn->a.conn_id);
tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l3h + 1, msgb_l3len(msg) - 1, 0, 0);
if (!TLVP_PRESENT(&tp, GSM0808_IE_AOIP_TRASP_ADDR)) {
LOGP(DMSC, LOGL_ERROR, "AoIP transport identifier missing -- discarding message!\n");
goto fail;
}
/* Decode AoIP transport address element */
rc = gsm0808_dec_aoip_trasp_addr(&rtp_addr, TLVP_VAL(&tp, GSM0808_IE_AOIP_TRASP_ADDR),
TLVP_LEN(&tp, GSM0808_IE_AOIP_TRASP_ADDR));
if (rc < 0) {
LOGP(DMSC, LOGL_ERROR, "Unable to decode aoip transport address.\n");
goto fail;
}
/* use address / port supplied with the AoIP
* transport address element */
if (rtp_addr.ss_family == AF_INET) {
rtp_addr_in = (struct sockaddr_in *)&rtp_addr;
conn->rtp.port_subscr = osmo_ntohs(rtp_addr_in->sin_port);
/* FIXME: We also get the IP-Address of the remote (e.g. BTS)
* end with the response. Currently we just ignore that address.
* Instead we expect that our local MGCP gateway and the code
* controlling it, magically knows the IP of the remote end. */
} else {
LOGP(DMSC, LOGL_ERROR, "Unsopported addressing scheme. (supports only IPV4)\n");
goto fail;
}
/* FIXME: Seems to be related to authentication or,
encryption. Is this really in the right place? */
msc_rx_sec_mode_compl(conn);
msgb_free(msg);
return 0;
fail:
msgb_free(msg);
return -EINVAL;
}
/* Handle incoming connection oriented BSSMAP messages */
static int rx_bssmap(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
{
if (msgb_l3len(msg) < 1) {
LOGP(DMSC, LOGL_NOTICE, "Error: No data received -- discarding message!\n");
msgb_free(msg);
return -1;
}
LOGP(DMSC, LOGL_NOTICE, "Rx MSC DT1 BSSMAP %s\n", gsm0808_bssmap_name(msg->l3h[0]));
switch (msg->l3h[0]) {
case BSS_MAP_MSG_CLEAR_RQST:
return bssmap_rx_clear_rqst(scu, a_conn_info, msg);
break;
case BSS_MAP_MSG_CLEAR_COMPLETE:
return bssmap_rx_clear_complete(scu, a_conn_info, msg);
break;
case BSS_MAP_MSG_COMPLETE_LAYER_3:
return bssmap_rx_l3_compl(scu, a_conn_info, msg);
break;
case BSS_MAP_MSG_CLASSMARK_UPDATE:
return bssmap_rx_classmark_upd(scu, a_conn_info, msg);
break;
case BSS_MAP_MSG_CIPHER_MODE_COMPLETE:
return bssmap_rx_ciph_compl(scu, a_conn_info, msg);
break;
case BSS_MAP_MSG_CIPHER_MODE_REJECT:
return bssmap_rx_ciph_rej(scu, a_conn_info, msg);
break;
case BSS_MAP_MSG_ASSIGMENT_FAILURE:
return bssmap_rx_ass_fail(scu, a_conn_info, msg);
break;
case BSS_MAP_MSG_SAPI_N_REJECT:
return bssmap_rx_sapi_n_rej(scu, a_conn_info, msg);
break;
case BSS_MAP_MSG_ASSIGMENT_COMPLETE:
return bssmap_rx_ass_compl(scu, a_conn_info, msg);
break;
default:
LOGP(DMSC, LOGL_ERROR, "Unimplemented msg type: %s\n", gsm0808_bssmap_name(msg->l3h[0]));
msgb_free(msg);
return -EINVAL;
}
return -EINVAL;
}
/* Endpoint to handle regular BSSAP DTAP messages */
static int rx_dtap(const struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
{
struct gsm_network *network = a_conn_info->network;
struct gsm_subscriber_connection *conn;
conn = subscr_conn_lookup_a(network, a_conn_info->conn_id);
if (!conn) {
msgb_free(msg);
return -EINVAL;
}
LOGP(DMSC, LOGL_NOTICE, "BSC sends layer 3 dtap (conn_id=%i)\n", conn->a.conn_id);
/* msc_dtap expects the dtap payload in l3h */
msg->l3h = msg->l2h + 3;
/* Forward dtap payload into the msc,
* msc_dtap() takes ownership for msg */
msc_dtap(conn, conn->a.conn_id, msg);
return 0;
}
/* Handle incoming connection oriented messages */
int sccp_rx_dt(struct osmo_sccp_user *scu, const struct a_conn_info *a_conn_info, struct msgb *msg)
{
OSMO_ASSERT(scu);
OSMO_ASSERT(a_conn_info);
OSMO_ASSERT(msg);
LOGP(DMSC, LOGL_NOTICE, "Rx BSC DT: %s\n", osmo_hexdump(msgb_l2(msg), msgb_l2len(msg)));
if (msgb_l2len(msg) < sizeof(struct bssmap_header)) {
LOGP(DMSC, LOGL_NOTICE, "The header is too short -- discarding message!\n");
msgb_free(msg);
}
switch (msg->l2h[0]) {
case BSSAP_MSG_BSS_MANAGEMENT:
msg->l3h = &msg->l2h[sizeof(struct bssmap_header)];
return rx_bssmap(scu, a_conn_info, msg);
break;
case BSSAP_MSG_DTAP:
return rx_dtap(scu, a_conn_info, msg);
break;
default:
LOGP(DMSC, LOGL_ERROR, "Unimplemented BSSAP msg type: %s\n", gsm0808_bssap_name(msg->l2h[0]));
msgb_free(msg);
return -EINVAL;
}
return -EINVAL;
}

View File

@ -1,42 +0,0 @@
/* Authentication related functions */
/*
* (C) 2010 by Sylvain Munaut <tnt@246tNt.com>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU 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/db.h>
#include <openbsc/debug.h>
#include <openbsc/auth.h>
#include <openbsc/gsm_data.h>
#include <osmocom/gsm/comp128.h>
#include <osmocom/core/utils.h>
#include <openssl/rand.h>
#include <stdlib.h>
const struct value_string auth_action_names[] = {
OSMO_VALUE_STRING(AUTH_ERROR),
OSMO_VALUE_STRING(AUTH_NOT_AVAIL),
OSMO_VALUE_STRING(AUTH_DO_AUTH_THEN_CIPH),
OSMO_VALUE_STRING(AUTH_DO_CIPH),
OSMO_VALUE_STRING(AUTH_DO_AUTH),
{ 0, NULL }
};

View File

@ -1,88 +0,0 @@
/*
* (C) 2014 by Holger Hans Peter Freyther
* (C) 2014 by 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 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/ctrl/control_cmd.h>
#include <osmocom/core/utils.h>
#include <openbsc/gsm_data.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/db.h>
#include <openbsc/debug.h>
#include <openbsc/vlr.h>
#include <stdbool.h>
static struct gsm_network *msc_ctrl_net = NULL;
static int verify_subscriber_modify(struct ctrl_cmd *cmd, const char *value, void *d)
{
return 0;
}
static int set_subscriber_modify(struct ctrl_cmd *cmd, void *data)
{
cmd->reply = "Command moved to osmo-hlr, no longer available here";
return CTRL_CMD_ERROR;
}
CTRL_CMD_DEFINE_WO(subscriber_modify, "subscriber-modify-v1");
static int set_subscriber_delete(struct ctrl_cmd *cmd, void *data)
{
cmd->reply = "Command moved to osmo-hlr, no longer available here";
return CTRL_CMD_ERROR;
}
CTRL_CMD_DEFINE_WO_NOVRF(subscriber_delete, "subscriber-delete-v1");
static int get_subscriber_list(struct ctrl_cmd *cmd, void *d)
{
struct vlr_subscr *vsub;
if (!msc_ctrl_net) {
cmd->reply = "MSC CTRL commands not initialized";
return CTRL_CMD_ERROR;
}
if (!msc_ctrl_net->vlr) {
cmd->reply = "VLR not initialized";
return CTRL_CMD_ERROR;
}
cmd->reply = talloc_strdup(cmd, "");
llist_for_each_entry(vsub, &msc_ctrl_net->vlr->subscribers, list) {
cmd->reply = talloc_asprintf_append(cmd->reply, "%s,%s\n",
vsub->imsi, vsub->msisdn);
}
printf("%s\n", cmd->reply); /* <-- what? */
return CTRL_CMD_REPLY;
}
CTRL_CMD_DEFINE_RO(subscriber_list, "subscriber-list-active-v1");
int msc_ctrl_cmds_install(struct gsm_network *net)
{
int rc = 0;
msc_ctrl_net = net;
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_modify);
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_delete);
rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_subscriber_list);
return rc;
}

File diff suppressed because it is too large Load Diff

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