aboutsummaryrefslogtreecommitdiffstats
path: root/include/osmocom/sgsn
diff options
context:
space:
mode:
Diffstat (limited to 'include/osmocom/sgsn')
-rw-r--r--include/osmocom/sgsn/Makefile.am28
-rw-r--r--include/osmocom/sgsn/common.h6
-rw-r--r--include/osmocom/sgsn/crc24.h10
-rw-r--r--include/osmocom/sgsn/debug.h47
-rw-r--r--include/osmocom/sgsn/gb_proxy.h288
-rw-r--r--include/osmocom/sgsn/gprs_gb_parse.h59
-rw-r--r--include/osmocom/sgsn/gprs_gmm.h35
-rw-r--r--include/osmocom/sgsn/gprs_llc.h284
-rw-r--r--include/osmocom/sgsn/gprs_llc_xid.h57
-rw-r--r--include/osmocom/sgsn/gprs_sgsn.h484
-rw-r--r--include/osmocom/sgsn/gprs_sndcp.h79
-rw-r--r--include/osmocom/sgsn/gprs_sndcp_comp.h82
-rw-r--r--include/osmocom/sgsn/gprs_sndcp_dcomp.h53
-rw-r--r--include/osmocom/sgsn/gprs_sndcp_pcomp.h46
-rw-r--r--include/osmocom/sgsn/gprs_sndcp_xid.h218
-rw-r--r--include/osmocom/sgsn/gprs_subscriber.h31
-rw-r--r--include/osmocom/sgsn/gprs_utils.h55
-rw-r--r--include/osmocom/sgsn/gsup_client.h63
-rw-r--r--include/osmocom/sgsn/gtphub.h523
-rw-r--r--include/osmocom/sgsn/oap_client.h82
-rw-r--r--include/osmocom/sgsn/rest_octets.h139
-rw-r--r--include/osmocom/sgsn/sgsn.h191
-rw-r--r--include/osmocom/sgsn/signal.h52
-rw-r--r--include/osmocom/sgsn/slhc.h187
-rw-r--r--include/osmocom/sgsn/v42bis.h147
-rw-r--r--include/osmocom/sgsn/v42bis_private.h126
-rw-r--r--include/osmocom/sgsn/vty.h9
27 files changed, 3381 insertions, 0 deletions
diff --git a/include/osmocom/sgsn/Makefile.am b/include/osmocom/sgsn/Makefile.am
new file mode 100644
index 000000000..150212971
--- /dev/null
+++ b/include/osmocom/sgsn/Makefile.am
@@ -0,0 +1,28 @@
+noinst_HEADERS = \
+ common.h \
+ crc24.h \
+ debug.h \
+ gb_proxy.h \
+ gprs_gb_parse.h \
+ gprs_gmm.h \
+ gprs_llc.h \
+ gprs_llc_xid.h \
+ gprs_sgsn.h \
+ gprs_sndcp_comp.h \
+ gprs_sndcp_dcomp.h \
+ gprs_sndcp.h \
+ gprs_sndcp_pcomp.h \
+ gprs_sndcp_xid.h \
+ gprs_subscriber.h \
+ gprs_utils.h \
+ gsup_client.h \
+ gtphub.h \
+ oap_client.h \
+ rest_octets.h \
+ sgsn.h \
+ signal.h \
+ slhc.h \
+ v42bis.h \
+ v42bis_private.h \
+ vty.h \
+ $(NULL)
diff --git a/include/osmocom/sgsn/common.h b/include/osmocom/sgsn/common.h
new file mode 100644
index 000000000..d91b3d39e
--- /dev/null
+++ b/include/osmocom/sgsn/common.h
@@ -0,0 +1,6 @@
+#pragma once
+
+enum nsap_addr_enc {
+ NSAP_ADDR_ENC_X213,
+ NSAP_ADDR_ENC_V4RAW,
+};
diff --git a/include/osmocom/sgsn/crc24.h b/include/osmocom/sgsn/crc24.h
new file mode 100644
index 000000000..756638c03
--- /dev/null
+++ b/include/osmocom/sgsn/crc24.h
@@ -0,0 +1,10 @@
+#ifndef _CRC24_H
+#define _CRC24_H
+
+#include <stdint.h>
+
+#define INIT_CRC24 0xffffff
+
+uint32_t crc24_calc(uint32_t fcs, uint8_t *cp, unsigned int len);
+
+#endif
diff --git a/include/osmocom/sgsn/debug.h b/include/osmocom/sgsn/debug.h
new file mode 100644
index 000000000..65e197d52
--- /dev/null
+++ b/include/osmocom/sgsn/debug.h
@@ -0,0 +1,47 @@
+#pragma once
+
+#include <stdio.h>
+#include <osmocom/core/linuxlist.h>
+
+#define DEBUG
+#include <osmocom/core/logging.h>
+
+/* Debug Areas of the code */
+enum {
+ DRLL,
+ DCC,
+ DMM,
+ DRR,
+ DRSL,
+ DNM,
+ DMNCC,
+ DPAG,
+ DMEAS,
+ DSCCP,
+ DMSC,
+ DMGCP,
+ DHO,
+ DDB,
+ DREF,
+ DGPRS,
+ DNS,
+ DBSSGP,
+ DLLC,
+ DSNDCP,
+ DSLHC,
+ DNAT,
+ DCTRL,
+ DSMPP,
+ DFILTER,
+ DGTPHUB,
+ DRANAP,
+ DSUA,
+ DV42BIS,
+ DPCU,
+ DVLR,
+ DIUCS,
+ DSIGTRAN,
+ Debug_LastEntry,
+};
+
+extern const struct log_info log_info;
diff --git a/include/osmocom/sgsn/gb_proxy.h b/include/osmocom/sgsn/gb_proxy.h
new file mode 100644
index 000000000..e10894fc3
--- /dev/null
+++ b/include/osmocom/sgsn/gb_proxy.h
@@ -0,0 +1,288 @@
+#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
diff --git a/include/osmocom/sgsn/gprs_gb_parse.h b/include/osmocom/sgsn/gprs_gb_parse.h
new file mode 100644
index 000000000..9f43faed6
--- /dev/null
+++ b/include/osmocom/sgsn/gprs_gb_parse.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <osmocom/sgsn/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);
diff --git a/include/osmocom/sgsn/gprs_gmm.h b/include/osmocom/sgsn/gprs_gmm.h
new file mode 100644
index 000000000..6324c5da7
--- /dev/null
+++ b/include/osmocom/sgsn/gprs_gmm.h
@@ -0,0 +1,35 @@
+#ifndef _GPRS_GMM_H
+#define _GPRS_GMM_H
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/sgsn/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 */
diff --git a/include/osmocom/sgsn/gprs_llc.h b/include/osmocom/sgsn/gprs_llc.h
new file mode 100644
index 000000000..376ae9a1a
--- /dev/null
+++ b/include/osmocom/sgsn/gprs_llc.h
@@ -0,0 +1,284 @@
+#ifndef _GPRS_LLC_H
+#define _GPRS_LLC_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <osmocom/sgsn/gprs_sgsn.h>
+#include <osmocom/sgsn/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
diff --git a/include/osmocom/sgsn/gprs_llc_xid.h b/include/osmocom/sgsn/gprs_llc_xid.h
new file mode 100644
index 000000000..d340d40b7
--- /dev/null
+++ b/include/osmocom/sgsn/gprs_llc_xid.h
@@ -0,0 +1,57 @@
+/* GPRS LLC XID field encoding/decoding as per 3GPP TS 44.064 */
+
+/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/linuxlist.h>
+
+/* 3GPP TS 44.064 6.4.1.6 Exchange Identification (XID)
+ command/response parameter field */
+struct gprs_llc_xid_field {
+ struct llist_head list;
+ uint8_t type; /* See also Table 6: LLC layer parameter
+ negotiation */
+ uint8_t *data; /* Payload data (memory is owned by the
+ * creator of the struct) */
+ unsigned int data_len; /* Payload length */
+};
+
+/* Transform a list with XID fields into a XID message (dst) */
+int gprs_llc_compile_xid(uint8_t *dst, int dst_maxlen,
+ const struct llist_head *xid_fields);
+
+/* Transform a XID message (dst) into a list of XID fields */
+struct llist_head *gprs_llc_parse_xid(const void *ctx, const uint8_t *src,
+ int src_len);
+
+/* Create a duplicate of an XID-Field */
+struct gprs_llc_xid_field *gprs_llc_dup_xid_field(const void *ctx,
+ const struct gprs_llc_xid_field *xid_field);
+
+/* Copy an llist with xid fields */
+struct llist_head *gprs_llc_copy_xid(const void *ctx,
+ const struct llist_head *xid_fields);
+
+/* Dump a list with XID fields (Debug) */
+void gprs_llc_dump_xid_fields(const struct llist_head *xid_fields,
+ unsigned int logl);
+
diff --git a/include/osmocom/sgsn/gprs_sgsn.h b/include/osmocom/sgsn/gprs_sgsn.h
new file mode 100644
index 000000000..c47fb0905
--- /dev/null
+++ b/include/osmocom/sgsn/gprs_sgsn.h
@@ -0,0 +1,484 @@
+#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 <osmocom/crypt/auth.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;
+
+struct gsm_auth_tuple {
+ int use_count;
+ int key_seq;
+ struct osmo_auth_vector vec;
+};
+#define GSM_KEY_SEQ_INVAL 7 /* GSM 04.08 - 10.5.1.2 */
+
+/* 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 */
diff --git a/include/osmocom/sgsn/gprs_sndcp.h b/include/osmocom/sgsn/gprs_sndcp.h
new file mode 100644
index 000000000..d970240e4
--- /dev/null
+++ b/include/osmocom/sgsn/gprs_sndcp.h
@@ -0,0 +1,79 @@
+#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 */
diff --git a/include/osmocom/sgsn/gprs_sndcp_comp.h b/include/osmocom/sgsn/gprs_sndcp_comp.h
new file mode 100644
index 000000000..c04f7d49a
--- /dev/null
+++ b/include/osmocom/sgsn/gprs_sndcp_comp.h
@@ -0,0 +1,82 @@
+/* GPRS SNDCP header compression entity management tools */
+
+/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/sgsn/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);
diff --git a/include/osmocom/sgsn/gprs_sndcp_dcomp.h b/include/osmocom/sgsn/gprs_sndcp_dcomp.h
new file mode 100644
index 000000000..3e851421c
--- /dev/null
+++ b/include/osmocom/sgsn/gprs_sndcp_dcomp.h
@@ -0,0 +1,53 @@
+/* GPRS SNDCP data compression handler */
+
+/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/sgsn/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);
diff --git a/include/osmocom/sgsn/gprs_sndcp_pcomp.h b/include/osmocom/sgsn/gprs_sndcp_pcomp.h
new file mode 100644
index 000000000..3e3131b52
--- /dev/null
+++ b/include/osmocom/sgsn/gprs_sndcp_pcomp.h
@@ -0,0 +1,46 @@
+/* GPRS SNDCP header compression handler */
+
+/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Philipp Maier
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/sgsn/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);
diff --git a/include/osmocom/sgsn/gprs_sndcp_xid.h b/include/osmocom/sgsn/gprs_sndcp_xid.h
new file mode 100644
index 000000000..e64bc5237
--- /dev/null
+++ b/include/osmocom/sgsn/gprs_sndcp_xid.h
@@ -0,0 +1,218 @@
+/* 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);
+
diff --git a/include/osmocom/sgsn/gprs_subscriber.h b/include/osmocom/sgsn/gprs_subscriber.h
new file mode 100644
index 000000000..be78febff
--- /dev/null
+++ b/include/osmocom/sgsn/gprs_subscriber.h
@@ -0,0 +1,31 @@
+/* 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__)
diff --git a/include/osmocom/sgsn/gprs_utils.h b/include/osmocom/sgsn/gprs_utils.h
new file mode 100644
index 000000000..e06364dad
--- /dev/null
+++ b/include/osmocom/sgsn/gprs_utils.h
@@ -0,0 +1,55 @@
+/* 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>
+
+#include <osmocom/core/msgb.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);
+
+#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);
+}
diff --git a/include/osmocom/sgsn/gsup_client.h b/include/osmocom/sgsn/gsup_client.h
new file mode 100644
index 000000000..29092ad5b
--- /dev/null
+++ b/include/osmocom/sgsn/gsup_client.h
@@ -0,0 +1,63 @@
+/* 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 <osmocom/sgsn/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);
+
diff --git a/include/osmocom/sgsn/gtphub.h b/include/osmocom/sgsn/gtphub.h
new file mode 100644
index 000000000..8fd9f38cf
--- /dev/null
+++ b/include/osmocom/sgsn/gtphub.h
@@ -0,0 +1,523 @@
+/* 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 <osmocom/sgsn/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);
diff --git a/include/osmocom/sgsn/oap_client.h b/include/osmocom/sgsn/oap_client.h
new file mode 100644
index 000000000..80c86d5d6
--- /dev/null
+++ b/include/osmocom/sgsn/oap_client.h
@@ -0,0 +1,82 @@
+/* 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);
diff --git a/include/osmocom/sgsn/rest_octets.h b/include/osmocom/sgsn/rest_octets.h
new file mode 100644
index 000000000..0dbe7ae13
--- /dev/null
+++ b/include/osmocom/sgsn/rest_octets.h
@@ -0,0 +1,139 @@
+#ifndef _REST_OCTETS_H
+#define _REST_OCTETS_H
+
+#include <stdbool.h>
+#include <osmocom/sgsn/gsm_04_08.h>
+#include <osmocom/gsm/sysinfo.h>
+
+/* 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);
+int rest_octets_si6(uint8_t *data, bool is1800_net);
+
+struct gsm48_si_selection_params {
+ uint16_t penalty_time:5,
+ temp_offs:3,
+ cell_resel_off:6,
+ cbq:1,
+ present:1;
+};
+
+struct gsm48_si_power_offset {
+ uint8_t power_offset:2,
+ present:1;
+};
+
+struct gsm48_si3_gprs_ind {
+ uint8_t si13_position:1,
+ ra_colour:3,
+ present:1;
+};
+
+struct gsm48_lsa_params {
+ uint32_t prio_thr:3,
+ lsa_offset:3,
+ mcc:12,
+ mnc:12;
+ unsigned int present;
+};
+
+struct gsm48_si_ro_info {
+ struct gsm48_si_selection_params selection_params;
+ struct gsm48_si_power_offset power_offset;
+ uint8_t si2ter_indicator;
+ uint8_t early_cm_ctrl;
+ struct {
+ uint8_t where:3,
+ present:1;
+ } scheduling;
+ struct gsm48_si3_gprs_ind gprs_ind;
+ /* SI 3 specific */
+ uint8_t si2quater_indicator;
+ /* SI 4 specific */
+ struct gsm48_lsa_params lsa_params;
+ uint16_t cell_id;
+ uint8_t break_ind; /* do we have SI7 + SI8 ? */
+};
+
+
+/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */
+int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3);
+
+/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */
+int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4, int len);
+
+enum pbcch_carrier_type {
+ PBCCH_BCCH,
+ PBCCH_ARFCN,
+ PBCCH_MAIO
+};
+
+/* TS 03.60 Chapter 6.3.3.1: Network Mode of Operation */
+enum gprs_nmo {
+ GPRS_NMO_I = 0, /* CS pagin on GPRS paging or traffic channel */
+ GPRS_NMO_II = 1, /* all paging on CCCH */
+ GPRS_NMO_III = 2, /* no paging coordination */
+};
+
+/* TS 04.60 12.24 */
+struct gprs_cell_options {
+ enum gprs_nmo nmo;
+ /* T3168: wait for packet uplink assignment message */
+ uint32_t t3168; /* in milliseconds */
+ /* T3192: wait for release of the TBF after reception of the final block */
+ uint32_t t3192; /* in milliseconds */
+ uint32_t drx_timer_max;/* in seconds */
+ uint32_t bs_cv_max;
+ uint8_t supports_egprs_11bit_rach;
+ bool ctrl_ack_type_use_block; /* use PACKET CONTROL ACKNOWLEDGMENT */
+
+ uint8_t ext_info_present;
+ struct {
+ uint8_t egprs_supported;
+ uint8_t use_egprs_p_ch_req;
+ uint8_t bep_period;
+ uint8_t pfc_supported;
+ uint8_t dtm_supported;
+ uint8_t bss_paging_coordination;
+ } ext_info;
+};
+
+/* TS 04.60 Table 12.9.2 */
+struct gprs_power_ctrl_pars {
+ uint8_t alpha;
+ uint8_t t_avg_w;
+ uint8_t t_avg_t;
+ uint8_t pc_meas_chan;
+ uint8_t n_avg_i;
+};
+
+struct gsm48_si13_info {
+ struct gprs_cell_options cell_opts;
+ struct gprs_power_ctrl_pars pwr_ctrl_pars;
+ uint8_t bcch_change_mark;
+ uint8_t si_change_field;
+ uint8_t pbcch_present;
+
+ union {
+ struct {
+ uint8_t rac;
+ uint8_t spgc_ccch_sup;
+ uint8_t net_ctrl_ord;
+ uint8_t prio_acc_thr;
+ } no_pbcch;
+ struct {
+ uint8_t psi1_rep_per;
+ uint8_t pb;
+ uint8_t tsc;
+ uint8_t tn;
+ enum pbcch_carrier_type carrier_type;
+ uint16_t arfcn;
+ uint8_t maio;
+ } pbcch;
+ };
+};
+
+/* Generate SI13 Rest Octests (Chapter 10.5.2.37b) */
+int rest_octets_si13(uint8_t *data, const struct gsm48_si13_info *si13);
+
+#endif /* _REST_OCTETS_H */
diff --git a/include/osmocom/sgsn/sgsn.h b/include/osmocom/sgsn/sgsn.h
new file mode 100644
index 000000000..e4eda17d2
--- /dev/null
+++ b/include/osmocom/sgsn/sgsn.h
@@ -0,0 +1,191 @@
+#ifndef _SGSN_H
+#define _SGSN_H
+
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/crypt/gprs_cipher.h>
+#include <osmocom/gprs/gprs_ns.h>
+#include <osmocom/sgsn/gprs_sgsn.h>
+#include <osmocom/sgsn/oap_client.h>
+#include <osmocom/sgsn/common.h>
+
+#include <ares.h>
+
+struct gprs_gsup_client;
+struct hostent;
+
+enum sgsn_auth_policy {
+ SGSN_AUTH_POLICY_OPEN,
+ SGSN_AUTH_POLICY_CLOSED,
+ SGSN_AUTH_POLICY_ACL_ONLY,
+ SGSN_AUTH_POLICY_REMOTE
+};
+
+
+enum sgsn_rate_ctr_keys {
+ CTR_LLC_DL_BYTES,
+ CTR_LLC_UL_BYTES,
+ CTR_LLC_DL_PACKETS,
+ CTR_LLC_UL_PACKETS,
+ CTR_GPRS_ATTACH_REQUEST,
+ CTR_GPRS_ATTACH_ACKED,
+ CTR_GPRS_ATTACH_REJECTED,
+ CTR_GPRS_DETACH_REQUEST,
+ CTR_GPRS_DETACH_ACKED,
+ CTR_GPRS_ROUTING_AREA_REQUEST,
+ CTR_GPRS_ROUTING_AREA_ACKED,
+ CTR_GPRS_ROUTING_AREA_REJECT,
+ /* PDP single packet counter / GSM 04.08 9.5.1 - 9.5.9 */
+ CTR_PDP_ACTIVATE_REQUEST,
+ CTR_PDP_ACTIVATE_REJECT,
+ CTR_PDP_ACTIVATE_ACCEPT,
+ CTR_PDP_REQUEST_ACTIVATE, /* unused */
+ CTR_PDP_REQUEST_ACTIVATE_REJ, /* unused */
+ CTR_PDP_MODIFY_REQUEST, /* unsued */
+ CTR_PDP_MODIFY_ACCEPT, /* unused */
+ CTR_PDP_DL_DEACTIVATE_REQUEST,
+ CTR_PDP_DL_DEACTIVATE_ACCEPT,
+ CTR_PDP_UL_DEACTIVATE_REQUEST,
+ CTR_PDP_UL_DEACTIVATE_ACCEPT,
+};
+
+struct sgsn_cdr {
+ char *filename;
+ int interval;
+};
+
+struct sgsn_config {
+ /* parsed from config file */
+
+ char *gtp_statedir;
+ struct sockaddr_in gtp_listenaddr;
+
+ /* misc */
+ struct gprs_ns_inst *nsi;
+
+ enum sgsn_auth_policy auth_policy;
+ enum gprs_ciph_algo cipher;
+ struct llist_head imsi_acl;
+
+ struct sockaddr_in gsup_server_addr;
+ int gsup_server_port;
+
+ int require_authentication;
+ int require_update_location;
+
+ /* CDR configuration */
+ struct sgsn_cdr cdr;
+
+ struct {
+ int T3312;
+ int T3322;
+ int T3350;
+ int T3360;
+ int T3370;
+ int T3313;
+ int T3314;
+ int T3316;
+ int T3385;
+ int T3386;
+ int T3395;
+ int T3397;
+ } timers;
+
+ int dynamic_lookup;
+
+ struct oap_client_config oap;
+
+ /* RFC1144 TCP/IP header compression */
+ struct {
+ int active;
+ int passive;
+ int s01;
+ } pcomp_rfc1144;
+
+ /* V.42vis data compression */
+ struct {
+ int active;
+ int passive;
+ int p0;
+ int p1;
+ int p2;
+ } dcomp_v42bis;
+
+ struct {
+ int rab_assign_addr_enc;
+ } iu;
+};
+
+struct sgsn_instance {
+ char *config_file;
+ struct sgsn_config cfg;
+ /* File descriptor wrappers for LibGTP */
+ struct osmo_fd gtp_fd0;
+ struct osmo_fd gtp_fd1c;
+ struct osmo_fd gtp_fd1u;
+ /* Timer for libGTP */
+ struct osmo_timer_list gtp_timer;
+ /* GSN instance for libgtp */
+ struct gsn_t *gsn;
+ /* Subscriber */
+ struct gsup_client *gsup_client;
+ /* LLME inactivity timer */
+ struct osmo_timer_list llme_timer;
+
+ /* c-ares event loop integration */
+ struct osmo_timer_list ares_timer;
+ struct llist_head ares_fds;
+ ares_channel ares_channel;
+ struct ares_addr_node *ares_servers;
+
+ struct rate_ctr_group *rate_ctrs;
+};
+
+extern struct sgsn_instance *sgsn;
+
+/* sgsn_vty.c */
+
+int sgsn_vty_init(struct sgsn_config *cfg);
+int sgsn_parse_config(const char *config_file);
+
+/* sgsn.c */
+
+/* Main input function for Gb proxy */
+int sgsn_rcvmsg(struct msgb *msg, struct gprs_nsvc *nsvc, uint16_t ns_bvci);
+
+
+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);
+int sgsn_delete_pdp_ctx(struct sgsn_pdp_ctx *pctx);
+void sgsn_pdp_upd_gtp_u(struct sgsn_pdp_ctx *pdp, void *addr, size_t alen);
+
+/* gprs_sndcp.c */
+
+/* Entry point for the SNSM-ACTIVATE.indication */
+int sndcp_sm_activate_ind(struct gprs_llc_lle *lle, uint8_t nsapi);
+/* Entry point for the SNSM-DEACTIVATE.indication */
+int sndcp_sm_deactivate_ind(struct gprs_llc_lle *lle, uint8_t nsapi);
+/* 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);
+int sndcp_unitdata_req(struct msgb *msg, struct gprs_llc_lle *lle, uint8_t nsapi,
+ void *mmcontext);
+int sndcp_llunitdata_ind(struct msgb *msg, struct gprs_llc_lle *lle,
+ uint8_t *hdr, uint16_t len);
+
+
+/*
+ * CDR related functionality
+ */
+int sgsn_cdr_init(struct sgsn_instance *sgsn);
+
+
+/*
+ * C-ARES related functionality
+ */
+int sgsn_ares_init(struct sgsn_instance *sgsn);
+int sgsn_ares_query(struct sgsn_instance *sgsm, const char *name, ares_host_callback cb, void *data);
+
+#endif
diff --git a/include/osmocom/sgsn/signal.h b/include/osmocom/sgsn/signal.h
new file mode 100644
index 000000000..4b6ba56f6
--- /dev/null
+++ b/include/osmocom/sgsn/signal.h
@@ -0,0 +1,52 @@
+/* Generic signalling/notification infrastructure */
+/* (C) 2009-2010, 2015 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 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/>.
+ *
+ */
+
+#ifndef OPENBSC_SIGNAL_H
+#define OPENBSC_SIGNAL_H
+
+#include <stdlib.h>
+#include <errno.h>
+
+#include <osmocom/core/signal.h>
+
+enum signal_subsystems {
+ SS_SGSN,
+};
+
+/* GPRS SGSN signals SS_SGSN */
+enum signal_sgsn {
+ S_SGSN_ATTACH,
+ S_SGSN_DETACH,
+ S_SGSN_UPDATE,
+ S_SGSN_PDP_ACT,
+ S_SGSN_PDP_DEACT,
+ S_SGSN_PDP_TERMINATE,
+ S_SGSN_PDP_FREE,
+ S_SGSN_MM_FREE,
+};
+
+struct sgsn_mm_ctx;
+struct sgsn_signal_data {
+ struct sgsn_mm_ctx *mm;
+ struct sgsn_pdp_ctx *pdp; /* non-NULL for PDP_ACT, PDP_DEACT, PDP_FREE */
+};
+
+#endif
diff --git a/include/osmocom/sgsn/slhc.h b/include/osmocom/sgsn/slhc.h
new file mode 100644
index 000000000..cd5a47cf4
--- /dev/null
+++ b/include/osmocom/sgsn/slhc.h
@@ -0,0 +1,187 @@
+#ifndef _SLHC_H
+#define _SLHC_H
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Header: slcompress.h,v 1.10 89/12/31 08:53:02 van Exp $
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ *
+ *
+ * modified for KA9Q Internet Software Package by
+ * Katie Stevens (dkstevens@ucdavis.edu)
+ * University of California, Davis
+ * Computing Services
+ * - 01-31-90 initial adaptation
+ *
+ * - Feb 1991 Bill_Simpson@um.cc.umich.edu
+ * variable number of conversation slots
+ * allow zero or one slots
+ * separate routines
+ * status display
+ */
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits). The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet. The next two octets are the TCP checksum
+ * from the original datagram. The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowledgment, sequence number and IP ID. (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.) Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0. (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type. There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows. Top
+ * three bits are actual packet type. For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+
+#include <linux/ip.h>
+#include <linux/tcp.h>
+
+/* SLIP compression masks for len/vers byte */
+#define SL_TYPE_IP 0x40
+#define SL_TYPE_UNCOMPRESSED_TCP 0x70
+#define SL_TYPE_COMPRESSED_TCP 0x80
+#define SL_TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+/*
+ * data type and sizes conversion assumptions:
+ *
+ * VJ code KA9Q style generic
+ * u_char byte_t unsigned char 8 bits
+ * u_short int16 unsigned short 16 bits
+ * u_int int16 unsigned short 16 bits
+ * u_long unsigned long unsigned long 32 bits
+ * int int32 long 32 bits
+ */
+
+typedef __u8 byte_t;
+typedef __u32 int32;
+
+/*
+ * "state" data for each active tcp conversation on the wire. This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+ byte_t cs_this; /* connection id number (xmit) */
+ struct cstate *next; /* next in ring (xmit) */
+ struct iphdr cs_ip; /* ip/tcp hdr from most recent packet */
+ struct tcphdr cs_tcp;
+ unsigned char cs_ipopt[64];
+ unsigned char cs_tcpopt[64];
+ int cs_hsize;
+};
+#define NULLSLSTATE (struct cstate *)0
+
+/*
+ * all the state data for one serial line (we need one of these per line).
+ */
+struct slcompress {
+ struct cstate *tstate; /* transmit connection states (array)*/
+ struct cstate *rstate; /* receive connection states (array)*/
+
+ byte_t tslot_limit; /* highest transmit slot id (0-l)*/
+ byte_t rslot_limit; /* highest receive slot id (0-l)*/
+
+ byte_t xmit_oldest; /* oldest xmit in ring */
+ byte_t xmit_current; /* most recent xmit id */
+ byte_t recv_current; /* most recent rcvd id */
+
+ byte_t flags;
+#define SLF_TOSS 0x01 /* tossing rcvd frames until id received */
+
+ int32 sls_o_nontcp; /* outbound non-TCP packets */
+ int32 sls_o_tcp; /* outbound TCP packets */
+ int32 sls_o_uncompressed; /* outbound uncompressed packets */
+ int32 sls_o_compressed; /* outbound compressed packets */
+ int32 sls_o_searches; /* searches for connection state */
+ int32 sls_o_misses; /* times couldn't find conn. state */
+
+ int32 sls_i_uncompressed; /* inbound uncompressed packets */
+ int32 sls_i_compressed; /* inbound compressed packets */
+ int32 sls_i_error; /* inbound error packets */
+ int32 sls_i_tossed; /* inbound packets tossed because of error */
+
+ int32 sls_i_runt;
+ int32 sls_i_badcheck;
+};
+#define NULLSLCOMPR (struct slcompress *)0
+
+/* In slhc.c: */
+struct slcompress *slhc_init(const void *ctx, int rslots, int tslots);
+
+void slhc_free(struct slcompress *comp);
+
+int slhc_compress(struct slcompress *comp, unsigned char *icp, int isize,
+ unsigned char *ocp, unsigned char **cpp, int compress_cid);
+int slhc_uncompress(struct slcompress *comp, unsigned char *icp, int isize);
+int slhc_remember(struct slcompress *comp, unsigned char *icp, int isize);
+int slhc_toss(struct slcompress *comp);
+
+void slhc_i_status(struct slcompress *comp);
+void slhc_o_status(struct slcompress *comp);
+
+#endif /* _SLHC_H */
diff --git a/include/osmocom/sgsn/v42bis.h b/include/osmocom/sgsn/v42bis.h
new file mode 100644
index 000000000..607a58e51
--- /dev/null
+++ b/include/osmocom/sgsn/v42bis.h
@@ -0,0 +1,147 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * v42bis.h
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2005, 2011 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*! \page v42bis_page V.42bis modem data compression
+\section v42bis_page_sec_1 What does it do?
+The v.42bis specification defines a data compression scheme, to work in
+conjunction with the error correction scheme defined in V.42.
+
+\section v42bis_page_sec_2 How does it work?
+*/
+
+#include <stdint.h>
+
+#if !defined(_SPANDSP_V42BIS_H_)
+#define _SPANDSP_V42BIS_H_
+
+#define SPAN_DECLARE(x) x
+
+#define V42BIS_MIN_STRING_SIZE 6
+#define V42BIS_MAX_STRING_SIZE 250
+#define V42BIS_MIN_DICTIONARY_SIZE 512
+#define V42BIS_MAX_BITS 12
+#define V42BIS_MAX_CODEWORDS 4096 /* 2^V42BIS_MAX_BITS */
+#define V42BIS_MAX_OUTPUT_LENGTH 1024
+
+enum
+{
+ V42BIS_P0_NEITHER_DIRECTION = 0,
+ V42BIS_P0_INITIATOR_RESPONDER,
+ V42BIS_P0_RESPONDER_INITIATOR,
+ V42BIS_P0_BOTH_DIRECTIONS
+};
+
+enum
+{
+ V42BIS_COMPRESSION_MODE_DYNAMIC = 0,
+ V42BIS_COMPRESSION_MODE_ALWAYS,
+ V42BIS_COMPRESSION_MODE_NEVER
+};
+
+typedef void (*put_msg_func_t)(void *user_data, const uint8_t *msg, int len);
+
+/*!
+ V.42bis compression/decompression descriptor. This defines the working state for a
+ single instance of V.42bis compress/decompression.
+*/
+typedef struct v42bis_state_s v42bis_state_t;
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+/*! Compress a block of octets.
+ \param s The V.42bis context.
+ \param buf The data to be compressed.
+ \param len The length of the data buffer.
+ \return 0 */
+SPAN_DECLARE(int) v42bis_compress(v42bis_state_t *s, const uint8_t buf[], int len);
+
+/*! Flush out any data remaining in a compression buffer.
+ \param s The V.42bis context.
+ \return 0 */
+SPAN_DECLARE(int) v42bis_compress_flush(v42bis_state_t *s);
+
+/*! Decompress a block of octets.
+ \param s The V.42bis context.
+ \param buf The data to be decompressed.
+ \param len The length of the data buffer.
+ \return 0 */
+SPAN_DECLARE(int) v42bis_decompress(v42bis_state_t *s, const uint8_t buf[], int len);
+
+/*! Flush out any data remaining in the decompression buffer.
+ \param s The V.42bis context.
+ \return 0 */
+SPAN_DECLARE(int) v42bis_decompress_flush(v42bis_state_t *s);
+
+/*! Set the compression mode.
+ \param s The V.42bis context.
+ \param mode One of the V.42bis compression modes -
+ V42BIS_COMPRESSION_MODE_DYNAMIC,
+ V42BIS_COMPRESSION_MODE_ALWAYS,
+ V42BIS_COMPRESSION_MODE_NEVER */
+SPAN_DECLARE(void) v42bis_compression_control(v42bis_state_t *s, int mode);
+
+/*! Initialise a V.42bis context.
+ \param s The V.42bis context.
+ \param negotiated_p0 The negotiated P0 parameter, from the V.42bis spec.
+ \param negotiated_p1 The negotiated P1 parameter, from the V.42bis spec.
+ \param negotiated_p2 The negotiated P2 parameter, from the V.42bis spec.
+ \param encode_handler Encode callback handler.
+ \param encode_user_data An opaque pointer passed to the encode callback handler.
+ \param max_encode_len The maximum length that should be passed to the encode handler.
+ \param decode_handler Decode callback handler.
+ \param decode_user_data An opaque pointer passed to the decode callback handler.
+ \param max_decode_len The maximum length that should be passed to the decode handler.
+ \return The V.42bis context. */
+SPAN_DECLARE(v42bis_state_t *) v42bis_init(const void *ctx,
+ v42bis_state_t *s,
+ int negotiated_p0,
+ int negotiated_p1,
+ int negotiated_p2,
+ put_msg_func_t encode_handler,
+ void *encode_user_data,
+ int max_encode_len,
+ put_msg_func_t decode_handler,
+ void *decode_user_data,
+ int max_decode_len);
+
+/*! Release a V.42bis context.
+ \param s The V.42bis context.
+ \return 0 if OK */
+SPAN_DECLARE(int) v42bis_release(v42bis_state_t *s);
+
+/*! Free a V.42bis context.
+ \param s The V.42bis context.
+ \return 0 if OK */
+SPAN_DECLARE(int) v42bis_free(v42bis_state_t *s);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/include/osmocom/sgsn/v42bis_private.h b/include/osmocom/sgsn/v42bis_private.h
new file mode 100644
index 000000000..daa5ea315
--- /dev/null
+++ b/include/osmocom/sgsn/v42bis_private.h
@@ -0,0 +1,126 @@
+/*
+ * SpanDSP - a series of DSP components for telephony
+ *
+ * private/v42bis.h
+ *
+ * Written by Steve Underwood <steveu@coppice.org>
+ *
+ * Copyright (C) 2005 Steve Underwood
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#if !defined(_SPANDSP_PRIVATE_V42BIS_H_)
+#define _SPANDSP_PRIVATE_V42BIS_H_
+
+/*!
+ V.42bis dictionary node.
+ Note that 0 is not a valid node to point to (0 is always a control code), so 0 is used
+ as a "no such value" marker in this structure.
+*/
+typedef struct
+{
+ /*! \brief The value of the octet represented by the current dictionary node */
+ uint8_t node_octet;
+ /*! \brief The parent of this node */
+ uint16_t parent;
+ /*! \brief The first child of this node */
+ uint16_t child;
+ /*! \brief The next node at the same depth */
+ uint16_t next;
+} v42bis_dict_node_t;
+
+/*!
+ V.42bis compression or decompression. This defines the working state for a single instance
+ of V.42bis compression or decompression.
+*/
+typedef struct
+{
+ /*! \brief Compression enabled. */
+ int v42bis_parm_p0;
+ /*! \brief Compression mode. */
+ int compression_mode;
+ /*! \brief Callback function to handle output data. */
+ put_msg_func_t handler;
+ /*! \brief An opaque pointer passed in calls to the data handler. */
+ void *user_data;
+ /*! \brief The maximum amount to be passed to the data handler. */
+ int max_output_len;
+
+ /*! \brief TRUE if we are in transparent (i.e. uncompressable) mode */
+ int transparent;
+ /*! \brief Next empty dictionary entry */
+ uint16_t v42bis_parm_c1;
+ /*! \brief Current codeword size */
+ uint16_t v42bis_parm_c2;
+ /*! \brief Threshold for codeword size change */
+ uint16_t v42bis_parm_c3;
+ /*! \brief The current update point in the dictionary */
+ uint16_t update_at;
+ /*! \brief The last entry matched in the dictionary */
+ uint16_t last_matched;
+ /*! \brief The last entry added to the dictionary */
+ uint16_t last_added;
+ /*! \brief Total number of codewords in the dictionary */
+ int v42bis_parm_n2;
+ /*! \brief Maximum permitted string length */
+ int v42bis_parm_n7;
+ /*! \brief The dictionary */
+ v42bis_dict_node_t dict[V42BIS_MAX_CODEWORDS];
+
+ /*! \brief The octet string in progress */
+ uint8_t string[V42BIS_MAX_STRING_SIZE];
+ /*! \brief The current length of the octet string in progress */
+ int string_length;
+ /*! \brief The amount of the octet string in progress which has already
+ been flushed out of the buffer */
+ int flushed_length;
+
+ /*! \brief Compression performance metric */
+ uint16_t compression_performance;
+
+ /*! \brief Outgoing bit buffer (compression), or incoming bit buffer (decompression) */
+ uint32_t bit_buffer;
+ /*! \brief Outgoing bit count (compression), or incoming bit count (decompression) */
+ int bit_count;
+
+ /*! \brief The output composition buffer */
+ uint8_t output_buf[V42BIS_MAX_OUTPUT_LENGTH];
+ /*! \brief The length of the contents of the output composition buffer */
+ int output_octet_count;
+
+ /*! \brief The current value of the escape code */
+ uint8_t escape_code;
+ /*! \brief TRUE if we just hit an escape code, and are waiting for the following octet */
+ int escaped;
+} v42bis_comp_state_t;
+
+/*!
+ V.42bis compression/decompression descriptor. This defines the working state for a
+ single instance of V.42bis compress/decompression.
+*/
+struct v42bis_state_s
+{
+ /*! \brief Compression state. */
+ v42bis_comp_state_t compress;
+ /*! \brief Decompression state. */
+ v42bis_comp_state_t decompress;
+
+ /*! \brief Error and flow logging control */
+};
+
+#endif
+/*- End of file ------------------------------------------------------------*/
diff --git a/include/osmocom/sgsn/vty.h b/include/osmocom/sgsn/vty.h
new file mode 100644
index 000000000..d2e3d9a73
--- /dev/null
+++ b/include/osmocom/sgsn/vty.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <osmocom/vty/command.h>
+
+enum bsc_vty_node {
+ GBPROXY_NODE = _LAST_OSMOVTY_NODE + 1,
+ SGSN_NODE,
+ GTPHUB_NODE,
+};