From 2720e7310d3cce36bc3cbcf87d3e8eb2eecf5c75 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Mon, 17 May 2010 00:44:57 +0200 Subject: [PATCH] [GPRS] Initial untested support for libgtp libgtp of the OpenGGSN project will allow us to speak the GTPv0/v1 protocol of the interface between SGSN and GGSN. This commit includes code for the main libgtp integration (file descriptor, select loop, timer) as well as code to encode/send a CREATE PDP CONTEXT request. --- openbsc/include/openbsc/gprs_sgsn.h | 10 +- openbsc/include/openbsc/gsm_04_08_gprs.h | 14 + openbsc/include/openbsc/sgsn.h | 16 + openbsc/src/gprs/Makefile.am | 4 +- openbsc/src/gprs/gprs_gmm.c | 140 ++++++-- openbsc/src/gprs/gprs_sgsn.c | 3 +- openbsc/src/gprs/gprs_sndcp.c | 59 ++++ openbsc/src/gprs/sgsn_libgtp.c | 421 +++++++++++++++++++++++ openbsc/src/gprs/sgsn_main.c | 17 +- 9 files changed, 637 insertions(+), 47 deletions(-) create mode 100644 openbsc/src/gprs/sgsn_libgtp.c diff --git a/openbsc/include/openbsc/gprs_sgsn.h b/openbsc/include/openbsc/gprs_sgsn.h index bdc0b1c8f..f70c6bed2 100644 --- a/openbsc/include/openbsc/gprs_sgsn.h +++ b/openbsc/include/openbsc/gprs_sgsn.h @@ -3,6 +3,10 @@ #include +#define GSM_IMSI_LENGTH 17 +#define GSM_IMEI_LENGTH 17 +#define GSM_EXTENSION_LENGTH 15 + /* TS 04.08 4.1.3.3 GMM mobility management states on the network side */ enum gprs_mm_state { GMM_DEREGISTERED, /* 4.1.3.3.1.1 */ @@ -20,11 +24,15 @@ enum gprs_ciph_algo { #define MS_RADIO_ACCESS_CAPA +struct sgsn_instance; + /* 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; + struct sgsn_instance *sgsn; + char imsi[GSM_IMSI_LENGTH]; enum gprs_mm_state mm_state; uint32_t p_tmsi; @@ -82,7 +90,7 @@ struct sgsn_pdp_ctx { unsigned int id; enum pdp_ctx_state state; enum pdp_type type; - uint32_t addresss; + uint32_t address; char *apn_subscribed; char *apn_used; uint16_t nsapi; diff --git a/openbsc/include/openbsc/gsm_04_08_gprs.h b/openbsc/include/openbsc/gsm_04_08_gprs.h index 344b2774b..d9fb285bf 100644 --- a/openbsc/include/openbsc/gsm_04_08_gprs.h +++ b/openbsc/include/openbsc/gsm_04_08_gprs.h @@ -46,6 +46,15 @@ #define GPRS_ATT_T_ATT_WHILE_IMSI 2 #define GPRS_ATT_T_COMBINED 3 +/* Chapter 10.5.5.5 / Table 10.5.138 */ +#define GPRS_DET_T_MO_GPRS 1 +#define GPRS_DET_T_MO_IMSI 2 +#define GPRS_DET_T_MO_COMBINED 3 +/* Network to MS direction */ +#define GPRS_DET_T_MT_REATT_REQ 1 +#define GPRS_DET_T_MT_REATT_NOTREQ 2 +#define GPRS_DET_T_MT_IMSI 3 + /* Chapter 10.5.5.18 / Table 105.150 */ #define GPRS_UPD_T_RA 0 #define GPRS_UPD_T_RA_LA 1 @@ -72,6 +81,11 @@ enum gsm48_gprs_ie_sm { GSM48_IE_GSM_TIMEZONE = 0x46, /* 10.5.3.8 */ GSM48_IE_GSM_UTC_AND_TZ = 0x47, /* 10.5.3.9 */ GSM48_IE_GSM_LSA_ID = 0x48, /* 10.5.3.11 */ + + /* Fake IEs that are not present on the Layer3 air interface, + * but which we use to simplify internal APIs */ + OSMO_IE_GSM_REQ_QOS = 0xfd, + OSMO_IE_GSM_REQ_PDP_ADDR = 0xfe, }; /* Chapter 9.4.15 / Table 9.4.15 */ diff --git a/openbsc/include/openbsc/sgsn.h b/openbsc/include/openbsc/sgsn.h index 2dc53c13d..2c4378bca 100644 --- a/openbsc/include/openbsc/sgsn.h +++ b/openbsc/include/openbsc/sgsn.h @@ -12,10 +12,26 @@ struct sgsn_config { u_int32_t nsip_listen_ip; u_int16_t nsip_listen_port; + char *gtp_statedir; + struct sockaddr_in gtp_listenaddr; + /* misc */ struct gprs_ns_inst *nsi; }; +struct sgsn_instance { + char *config_file; + struct sgsn_config cfg; + /* File descriptor wrappers for LibGTP */ + struct bsc_fd gtp_fd0; + struct bsc_fd gtp_fd1c; + struct bsc_fd gtp_fd1u; + /* Timer for libGTP */ + struct timer_list gtp_timer; + /* GSN instance for libgtp */ + struct gsn_t *gsn; +}; + /* sgsn_vty.c */ diff --git a/openbsc/src/gprs/Makefile.am b/openbsc/src/gprs/Makefile.am index 6d6314599..31d3a10c8 100644 --- a/openbsc/src/gprs/Makefile.am +++ b/openbsc/src/gprs/Makefile.am @@ -13,6 +13,6 @@ osmo_gbproxy_SOURCES = gb_proxy.c gb_proxy_main.c gb_proxy_vty.c \ $(top_srcdir)/src/socket.c $(top_srcdir)/src/debug.c osmo_gbproxy_LDADD = $(top_builddir)/src/libvty.a -osmo_sgsn_SOURCES = sgsn_main.c sgsn_vty.c gprs_ns_vty.c \ +osmo_sgsn_SOURCES = sgsn_main.c sgsn_vty.c gprs_ns_vty.c sgsn_libgtp.c \ $(top_srcdir)/src/socket.c $(top_srcdir)/src/debug.c -osmo_sgsn_LDADD = $(top_builddir)/src/libvty.a libsgsn.a +osmo_sgsn_LDADD = $(top_builddir)/src/libvty.a libsgsn.a -lgtp diff --git a/openbsc/src/gprs/gprs_gmm.c b/openbsc/src/gprs/gprs_gmm.c index 4a42113f0..fc9d00743 100644 --- a/openbsc/src/gprs/gprs_gmm.c +++ b/openbsc/src/gprs/gprs_gmm.c @@ -48,8 +48,10 @@ #include #include +/* Protocol related stuff, should go into libosmocore */ + /* 10.5.5.14 GPRS MM Cause / Table 10.5.147 */ -struct value_string gmm_cause_names[] = { +const struct value_string gmm_cause_names[] = { /* FIXME */ { GMM_CAUSE_SEM_INCORR_MSG, "Semantically incorrect message" }, { GMM_CAUSE_INV_MAND_INFO, "Invalid mandatory information" }, @@ -67,7 +69,7 @@ struct value_string gmm_cause_names[] = { }; /* 10.5.6.6 SM Cause / Table 10.5.157 */ -struct value_string gsm_cause_names[] = { +const struct value_string gsm_cause_names[] = { { GSM_CAUSE_INSUFF_RSRC, "Insufficient resources" }, { GSM_CAUSE_MISSING_APN, "Missing or unknown APN" }, { GSM_CAUSE_UNKNOWN_PDP, "Unknown PDP address or PDP type" }, @@ -101,34 +103,31 @@ struct value_string gsm_cause_names[] = { { 0, NULL } }; -static const char *att_name(uint8_t type) -{ - switch (type) { - case GPRS_ATT_T_ATTACH: - return "GPRS attach"; - case GPRS_ATT_T_ATT_WHILE_IMSI: - return "GPRS attach while IMSI attached"; - case GPRS_ATT_T_COMBINED: - return "Combined GPRS/IMSI attach"; - default: - return "unknown"; - } -} +/* 10.5.5.2 */ +const struct value_string gprs_att_t_strs[] = { + { GPRS_ATT_T_ATTACH, "GPRS attach" }, + { GPRS_ATT_T_ATT_WHILE_IMSI, "GPRS attach while IMSI attached" }, + { GPRS_ATT_T_COMBINED, "Combined GPRS/IMSI attach" }, + { 0, NULL } +}; -static const char *upd_name(uint8_t type) -{ - switch (type) { - case GPRS_UPD_T_RA: - return "RA updating"; - case GPRS_UPD_T_RA_LA: - return "combined RA/LA updating"; - case GPRS_UPD_T_RA_LA_IMSI_ATT: - return "combined RA/LA updating + IMSI attach"; - case GPRS_UPD_T_PERIODIC: - return "periodic updating"; - } - return "unknown"; -} +const struct value_string gprs_upd_t_strs[] = { + { GPRS_UPD_T_RA, "RA updating" }, + { GPRS_UPD_T_RA_LA, "combined RA/LA updating" }, + { GPRS_UPD_T_RA_LA_IMSI_ATT, "combined RA/LA updating + IMSI attach" }, + { GPRS_UPD_T_PERIODIC, "periodic updating" }, + { 0, NULL } +}; + +/* 10.5.5.5 */ +const struct value_string gprs_det_t_mo_strs[] = { + { GPRS_DET_T_MO_GPRS, "GPRS detach" }, + { GPRS_DET_T_MO_IMSI, "IMSI detach" }, + { GPRS_DET_T_MO_COMBINED, "Combined GPRS/IMSI detach" }, + { 0, NULL } +}; + +/* Our implementation, should be kept in SGSN */ /* Send a message through the underlying layer */ static int gsm48_gmm_sendmsg(struct msgb *msg, int command) @@ -215,6 +214,22 @@ static int gsm48_tx_gmm_att_rej(struct msgb *old_msg, uint8_t gmm_cause) return gsm48_gmm_sendmsg(msg, 0); } +/* Chapter 9.4.6.2 Detach accept */ +static gsm48_tx_gmm_det_ack(struct msgb *old_msg, uint8_t force_stby) +{ + struct msgb *msg = gsm48_msgb_alloc(); + struct gsm48_hdr *gh; + + DEBUGP(DMM, "<- GPRS DETACH ACCEPT\n"); + + gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); + gh->proto_discr = GSM48_PDISC_MM_GPRS; + gh->msg_type = GSM48_MT_GMM_DETACH_ACK; + gh->data[0] = force_stby; + + return gsm48_gmm_sendmsg(msg, 0); +} + /* Transmit Chapter 9.4.12 Identity Request */ static int gsm48_tx_gmm_id_req(struct msgb *old_msg, uint8_t id_type) { @@ -260,7 +275,7 @@ static int gsm48_rx_gmm_id_resp(struct msgb *msg) struct sgsn_mm_ctx *ctx; gsm48_mi_to_string(mi_string, sizeof(mi_string), &gh->data[1], gh->data[0]); - DEBUGP(DMM, "GMM IDENTITY RESPONSE: mi_type=0x%02x MI(%s) ", + DEBUGP(DMM, "-> GMM IDENTITY RESPONSE: mi_type=0x%02x MI(%s) ", mi_type, mi_string); bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); @@ -320,7 +335,7 @@ static int gsm48_rx_gmm_att_req(struct msgb *msg) uint16_t cid; struct sgsn_mm_ctx *ctx; - DEBUGP(DMM, "GMM ATTACH REQUEST "); + DEBUGP(DMM, "-> GMM ATTACH REQUEST "); /* As per TS 04.08 Chapter 4.7.1.4, the attach request arrives either * with a foreign TLLI (P-TMSI that was allocated to the MS before), @@ -352,7 +367,8 @@ static int gsm48_rx_gmm_att_req(struct msgb *msg) gsm48_mi_to_string(mi_string, sizeof(mi_string), mi, mi_len); - DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, att_name(att_type)); + DEBUGPC(DMM, "MI(%s) type=\"%s\" ", mi_string, + get_value_string(gprs_att_t_strs, att_type)); /* Old routing area identification 10.5.5.15 */ old_ra_info = cur; @@ -411,6 +427,38 @@ err_inval: return gsm48_tx_gmm_att_rej(msg, GMM_CAUSE_SEM_INCORR_MSG); } +/* Section 4.7.4.1 / 9.4.5.2 MO Detach request */ +static int gsm48_rx_gmm_det_req(struct msgb *msg) +{ + struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); + struct sgsn_mm_ctx *ctx; + uint8_t detach_type, power_off; + struct gprs_ra_id ra_id; + + bssgp_parse_cell_id(&ra_id, msgb_bcid(msg)); + ctx = sgsn_mm_ctx_by_tlli(msgb_tlli(msg), &ra_id); + if (!ctx) { + LOGP(DMM, LOGL_NOTICE, "-> GMM DETACH REQUEST for unknown " + "TLLI=0x%08x\n", msgb_tlli(msg)); + /* FIXME: send detach reject */ + } + + detach_type = gh->data[0] & 0x7; + power_off = gh->data[0] & 0x8; + + /* FIXME: In 24.008 there is an optional P-TMSI and P-TMSI signature IE */ + + DEBUGP(DMM, "-> GMM DETACH REQUEST TLLI=0x%08x type=%s %s\n", + msgb_tlli(msg), get_value_string(gprs_det_t_mo_strs, detach_type), + power_off ? "Power-off" : ""); + + /* Mark MM state as deregistered */ + ctx->mm_state = GMM_DEREGISTERED; + + /* force_stby = 0 */ + return gsm48_tx_gmm_det_ack(msg, 0); +} + /* Chapter 9.4.15: Routing area update accept */ static int gsm48_tx_gmm_ra_upd_ack(struct msgb *old_msg) { @@ -471,7 +519,8 @@ static int gsm48_rx_gmm_ra_upd_req(struct msgb *msg) /* Update Type 10.5.5.18 */ upd_type = *cur++ & 0x0f; - DEBUGP(DMM, "GMM RA UPDATE REQUEST type=\"%s\" ", upd_name(upd_type)); + DEBUGP(DMM, "-> GMM RA UPDATE REQUEST type=\"%s\" ", + get_value_string(gprs_upd_t_strs, upd_type)); /* Old routing area identification 10.5.5.15 */ gsm48_parse_ra(&old_ra_id, cur); @@ -516,7 +565,7 @@ static int gsm48_rx_gmm_status(struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); - DEBUGP(DMM, "GPRS MM STATUS (cause: %s)\n", + DEBUGP(DMM, "-> GPRS MM STATUS (cause: %s)\n", get_value_string(gmm_cause_names, gh->data[0])); return 0; @@ -541,19 +590,23 @@ static int gsm0408_rcv_gmm(struct msgb *msg) case GSM48_MT_GMM_STATUS: rc = gsm48_rx_gmm_status(msg); break; + case GSM48_MT_GMM_DETACH_REQ: + rc = gsm48_rx_gmm_det_req(msg); + break; case GSM48_MT_GMM_RA_UPD_COMPL: /* only in case SGSN offered new P-TMSI */ case GSM48_MT_GMM_ATTACH_COMPL: /* only in case SGSN offered new P-TMSI */ - case GSM48_MT_GMM_DETACH_REQ: case GSM48_MT_GMM_PTMSI_REALL_COMPL: case GSM48_MT_GMM_AUTH_CIPH_RESP: DEBUGP(DMM, "Unimplemented GSM 04.08 GMM msg type 0x%02x\n", gh->msg_type); + /* FIXME: Send GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL */ break; default: DEBUGP(DMM, "Unknown GSM 04.08 GMM msg type 0x%02x\n", gh->msg_type); + /* FIXME: Send GMM_CAUSE_MSGT_NOTEXIST_NOTIMPL */ break; } @@ -641,7 +694,7 @@ static int gsm48_rx_gsm_act_pdp_req(struct msgb *msg) uint8_t *req_qos, *req_pdpa; struct tlv_parsed tp; - DEBUGP(DMM, "ACTIVATE PDP CONTEXT REQ: "); + DEBUGP(DMM, "-> ACTIVATE PDP CONTEXT REQ: "); req_qos_len = act_req->data[0]; req_qos = act_req->data + 1; /* 10.5.6.5 */ req_pdpa_len = act_req->data[1 + req_qos_len]; @@ -679,11 +732,22 @@ static int gsm48_rx_gsm_act_pdp_req(struct msgb *msg) break; } + /* put the non-TLV elements in the TLV parser structure to + * pass them on to the SGSN / GTP code */ + tp.lv[OSMO_IE_GSM_REQ_QOS].len = req_qos_len; + tp.lv[OSMO_IE_GSM_REQ_QOS].val = req_qos; + tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].len = req_pdpa_len; + tp.lv[OSMO_IE_GSM_REQ_PDP_ADDR].val = req_pdpa; + /* FIXME: parse TLV for AP name and protocol config options */ if (TLVP_PRESENT(&tp, GSM48_IE_GSM_APN)) {} if (TLVP_PRESENT(&tp, GSM48_IE_GSM_PROTO_CONF_OPT)) {} +#if 0 + return sgsn_create_pdp_ctx(ggsn, &tp); +#else return gsm48_tx_gsm_act_pdp_acc(msg, act_req); +#endif } /* Section 9.5.8: Deactivate PDP Context Request */ @@ -691,7 +755,7 @@ static int gsm48_rx_gsm_deact_pdp_req(struct msgb *msg) { struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_gmmh(msg); - DEBUGP(DMM, "DEACTIVATE PDP CONTEXT REQ (cause: %s)\n", + DEBUGP(DMM, "-> DEACTIVATE PDP CONTEXT REQ (cause: %s)\n", get_value_string(gsm_cause_names, gh->data[0])); return gsm48_tx_gsm_deact_pdp_acc(msg); @@ -701,7 +765,7 @@ static int gsm48_rx_gsm_status(struct msgb *msg) { struct gsm48_hdr *gh = msgb_l3(msg); - DEBUGP(DMM, "GPRS SM STATUS (cause: %s)\n", + DEBUGP(DMM, "-> GPRS SM STATUS (cause: %s)\n", get_value_string(gsm_cause_names, gh->data[0])); return 0; diff --git a/openbsc/src/gprs/gprs_sgsn.c b/openbsc/src/gprs/gprs_sgsn.c index ba4671955..90ede7651 100644 --- a/openbsc/src/gprs/gprs_sgsn.c +++ b/openbsc/src/gprs/gprs_sgsn.c @@ -81,8 +81,9 @@ struct sgsn_mm_ctx *sgsn_mm_ctx_by_imsi(const char *imsi) struct sgsn_mm_ctx *sgsn_mm_ctx_alloc(uint32_t tlli, const struct gprs_ra_id *raid) { - struct sgsn_mm_ctx *ctx = talloc_zero(NULL, struct sgsn_mm_ctx); + struct sgsn_mm_ctx *ctx; + ctx = talloc_zero(tall_bsc_ctx, struct sgsn_mm_ctx); if (!ctx) return NULL; diff --git a/openbsc/src/gprs/gprs_sndcp.c b/openbsc/src/gprs/gprs_sndcp.c index 0d1a39004..99a7638be 100644 --- a/openbsc/src/gprs/gprs_sndcp.c +++ b/openbsc/src/gprs/gprs_sndcp.c @@ -54,6 +54,9 @@ struct sndcp_udata_hdr { uint8_t npdu_low; }; +struct sndcp_entity { +}; + /* Entry point for the LL-UNITDATA.indication */ int sndcp_unitdata_ind(struct msgb *msg, uint8_t sapi, uint8_t *hdr, uint8_t len) { @@ -68,3 +71,59 @@ int sndcp_unitdata_ind(struct msgb *msg, uint8_t sapi, uint8_t *hdr, uint8_t len npdu = (suh->npdu_high << 8) | suh->npdu_low; } +/* Section 5.1.2.1 LL-RESET.ind */ +static int sndcp_ll_reset_ind(struct sndcp_entity *se,) +{ + /* treat all outstanding SNDCP-LLC request type primitives as not sent */ + /* reset all SNDCP XID parameters to default values */ +} + +/* Section 5.1.2.17 LL-UNITDATA.ind */ +static int sndcp_ll_unitdata_ind() +{ +} + +static int sndcp_ll_status_ind() +{ + /* inform the SM sub-layer by means of SNSM-STATUS.req */ +} + +static struct sndcp_state_list {{ + uint32_t states; + unsigned int type; + int (*rout)(struct sndcp_entity *se, struct msgb *msg); +} sndcp_state_list[] = { + { ALL_STATES, + LL_RESET_IND, sndcp_ll_reset_ind }, + { ALL_STATES, + LL_ESTABLISH_IND, sndcp_ll_est_ind }, + { SBIT(SNDCP_S_EST_RQD), + LL_ESTABLISH_RESP, sndcp_ll_est_ind }, + { SBIT(SNDCP_S_EST_RQD), + LL_ESTABLISH_CONF, sndcp_ll_est_conf }, + { SBIT(SNDCP_S_ +}; + +static int sndcp_rx_llc_prim() +{ + case LL_ESTABLISH_REQ: + case LL_RELEASE_REQ: + case LL_XID_REQ: + case LL_DATA_REQ: + LL_UNITDATA_REQ, /* TLLI, SN-PDU, Ref, QoS, Radio Prio, Ciph */ + + switch (prim) { + case LL_RESET_IND: + case LL_ESTABLISH_IND: + case LL_ESTABLISH_RESP: + case LL_ESTABLISH_CONF: + case LL_RELEASE_IND: + case LL_RELEASE_CONF: + case LL_XID_IND: + case LL_XID_RESP: + case LL_XID_CONF: + case LL_DATA_IND: + case LL_DATA_CONF: + case LL_UNITDATA_IND: + case LL_STATUS_IND: +} diff --git a/openbsc/src/gprs/sgsn_libgtp.c b/openbsc/src/gprs/sgsn_libgtp.c new file mode 100644 index 000000000..fcaee8bbb --- /dev/null +++ b/openbsc/src/gprs/sgsn_libgtp.c @@ -0,0 +1,421 @@ +/* GPRS SGSN integration with libgtp of OpenGGSN */ + +/* (C) 2010 by Harald Welte + * (C) 2010 by On Waves + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +//#include +//#include +#include +#include + +#include +#include + +struct ggsn_ctx { + unsigned int gtp_version; + struct in_addr remote_addr; + struct gsn_t *gsn; +}; + +const struct value_string gtp_cause_strs[] = { + { GTPCAUSE_REQ_IMSI, "Request IMSI" }, + { GTPCAUSE_REQ_IMEI, "Request IMEI" }, + { GTPCAUSE_REQ_IMSI_IMEI, "Request IMSI and IMEI" }, + { GTPCAUSE_NO_ID_NEEDED, "No identity needed" }, + { GTPCAUSE_MS_REFUSES_X, "MS refuses" }, + { GTPCAUSE_MS_NOT_RESP_X, "MS is not GPRS responding" }, + { GTPCAUSE_ACC_REQ, "Request accepted" }, + { GTPCAUSE_NON_EXIST, "Non-existent" }, + { GTPCAUSE_INVALID_MESSAGE, "Invalid message format" }, + { GTPCAUSE_IMSI_NOT_KNOWN, "IMSI not known" }, + { GTPCAUSE_MS_DETACHED, "MS is GPRS detached" }, + { GTPCAUSE_MS_NOT_RESP, "MS is not GPRS responding" }, + { GTPCAUSE_MS_REFUSES, "MS refuses" }, + { GTPCAUSE_NO_RESOURCES, "No resources available" }, + { GTPCAUSE_NOT_SUPPORTED, "Service not supported" }, + { GTPCAUSE_MAN_IE_INCORRECT, "Mandatory IE incorrect" }, + { GTPCAUSE_MAN_IE_MISSING, "Mandatory IE missing" }, + { GTPCAUSE_OPT_IE_INCORRECT, "Optional IE incorrect" }, + { GTPCAUSE_SYS_FAIL, "System failure" }, + { GTPCAUSE_ROAMING_REST, "Roaming restrictions" }, + { GTPCAUSE_PTIMSI_MISMATCH, "P-TMSI Signature mismatch" }, + { GTPCAUSE_CONN_SUSP, "GPRS connection suspended" }, + { GTPCAUSE_AUTH_FAIL, "Authentication failure" }, + { GTPCAUSE_USER_AUTH_FAIL, "User authentication failed" }, + { GTPCAUSE_CONTEXT_NOT_FOUND, "Context not found" }, + { GTPCAUSE_ADDR_OCCUPIED, "All dynamic PDP addresses occupied" }, + { GTPCAUSE_NO_MEMORY, "No memory is available" }, + { GTPCAUSE_RELOC_FAIL, "Relocation failure" }, + { GTPCAUSE_UNKNOWN_MAN_EXTHEADER, "Unknown mandatory ext. header" }, + { GTPCAUSE_SEM_ERR_TFT, "Semantic error in TFT operation" }, + { GTPCAUSE_SYN_ERR_TFT, "Syntactic error in TFT operation" }, + { GTPCAUSE_SEM_ERR_FILTER, "Semantic errors in packet filter" }, + { GTPCAUSE_SYN_ERR_FILTER, "Syntactic errors in packet filter" }, + { GTPCAUSE_MISSING_APN, "Missing or unknown APN" }, + { GTPCAUSE_UNKNOWN_PDP, "Unknown PDP address or PDP type" }, + { 0, NULL } +}; + +/* generate a PDP context based on the IE's from the 04.08 message, + * and send the GTP create pdp context request to the GGSN */ +int sgsn_create_pdp_ctx(struct ggsn_ctx *ggsn, struct sgsn_mm_ctx *mmctx, + uint16_t nsapi, struct tlv_parsed *tp) +{ + struct sgsn_instance *sgsn = mmctx->sgsn; + struct pdp_t *pdp; + uint64_t imsi_ui64; + int rc; + + rc = pdp_newpdp(&pdp, imsi_ui64, nsapi, NULL); + if (rc) { + LOGP(DGPRS, LOGL_ERROR, "Out of PDP Contexts\n"); + return -ENOMEM; + } + //pdp->peer = /* sockaddr_in of GGSN (receive) */ + //pdp->ipif = /* not used by library */ + pdp->version = ggsn->gtp_version; + pdp->hisaddr0 = ggsn->remote_addr; + pdp->hisaddr1 = ggsn->remote_addr; + //pdp->cch_pdp = 512; /* Charging Flat Rate */ + + /* MS provided APN, subscription not verified */ + pdp->selmode = 0x01; + + /* IMSI, TEID/TEIC, FLLU/FLLC, TID, NSAPI set in pdp_newpdp */ + + /* FIXME: MSISDN in BCD format from mmctx */ + //pdp->msisdn.l/.v + + /* End User Address from GMM requested PDP address */ + pdp->eua.l = TLVP_LEN(tp, OSMO_IE_GSM_REQ_PDP_ADDR); + if (pdp->eua.l > sizeof(pdp->eua.v)) + pdp->eua.l = sizeof(pdp->eua.v); + memcpy(pdp->eua.v, TLVP_VAL(tp, OSMO_IE_GSM_REQ_PDP_ADDR), + pdp->eua.l); + /* Highest 4 bits of first byte need to be set to 1, otherwise + * the IE is identical with the 04.08 PDP Address IE */ + pdp->eua.v[0] |= 0xf0; + + /* APN name from GMM */ + pdp->apn_use.l = TLVP_LEN(tp, GSM48_IE_GSM_APN); + if (pdp->apn_use.l > sizeof(pdp->apn_use.v)) + pdp->apn_use.l = sizeof(pdp->apn_use.v); + memcpy(pdp->apn_use.v, TLVP_VAL(tp, GSM48_IE_GSM_APN), + pdp->apn_use.l); + + /* Protocol Configuration Options from GMM */ + pdp->pco_req.l = TLVP_LEN(tp, GSM48_IE_GSM_PROTO_CONF_OPT); + if (pdp->pco_req.l > sizeof(pdp->pco_req.v)) + pdp->pco_req.l = sizeof(pdp->pco_req.v); + memcpy(pdp->pco_req.v, TLVP_VAL(tp, GSM48_IE_GSM_PROTO_CONF_OPT), + pdp->pco_req.l); + + /* QoS options from GMM */ + pdp->qos_req.l = TLVP_LEN(tp, OSMO_IE_GSM_REQ_QOS); + if (pdp->qos_req.l > sizeof(pdp->qos_req.v)) + pdp->qos_req.l = sizeof(pdp->qos_req.v); + memcpy(pdp->qos_req.v, TLVP_VAL(tp, OSMO_IE_GSM_REQ_QOS), + pdp->qos_req.l); + + /* SGSN address for control plane */ + pdp->gsnlc.l = sizeof(sgsn->cfg.gtp_listenaddr); + memcpy(pdp->gsnlc.v, &sgsn->cfg.gtp_listenaddr, + sizeof(sgsn->cfg.gtp_listenaddr)); + + /* SGSN address for user plane */ + pdp->gsnlu.l = sizeof(sgsn->cfg.gtp_listenaddr); + memcpy(pdp->gsnlu.v, &sgsn->cfg.gtp_listenaddr, + sizeof(sgsn->cfg.gtp_listenaddr)); + + /* FIXME: change pdp state to 'requested' */ + + /* FIXME: pass along a pointer to the MM CTX */ + return gtp_create_context_req(ggsn->gsn, pdp, mmctx); +} + +/* The GGSN has confirmed the creation of a PDP Context */ +static int create_pdp_conf(struct pdp_t *pdp, void *cbp, int cause) +{ + struct sgsn_mm_ctx *mmctx = cbp; + + DEBUGP(DGPRS, "Received CREATE PDP CTX CONF, cause=%d(%s)\n", + cause, get_value_string(gtp_cause_strs, cause)); + + /* Check for cause value if it was really successful */ + if (cause < 0) { + LOGP(DGPRS, LOGL_NOTICE, "Create PDP ctx req timed out\n"); + if (pdp->version == 1) { + pdp->version = 0; + gtp_create_context_req(mmctx->sgsn->gsn, pdp, cbp); + return 0; + } else { + pdp_freepdp(pdp); + return EOF; + } + } + + /* Check for cause value if it was really successful */ + if (cause != GTPCAUSE_ACC_REQ) { + pdp_freepdp(pdp); + return EOF; + } + + /* FIXME: Determine MM ctx for the PDP ctx */ + /* FIXME: Send PDP CTX ACT ACK/REJ to MS */ + return 0; +} + +/* If we receive a 04.08 DEACT PDP CTX REQ or GPRS DETACH, we need to + * look-up the PDP context and request its deletion from the SGSN */ +int sgsn_delete_pdp_ctx(struct ggsn_ctx *ggsn, struct sgsn_mm_ctx *mmctx, + struct tlv_parsed *tp) +{ + //return gtp_delete_context_req(gsn, pdp, cbp, teardown); +} + +/* Confirmation of a PDP Context Delete */ +static int delete_pdp_conf(struct pdp_t *pdp, int cause) +{ + DEBUGP(DGPRS, "Received DELETE PDP CTX CONF, cause=%d(%s)\n", + cause, get_value_string(gtp_cause_strs, cause)); + return 0; +} + +/* Confirmation of an GTP ECHO request */ +static int echo_conf(int recovery) +{ + if (recovery < 0) { + DEBUGP(DGPRS, "GTP Echo Request timed out\n"); + /* FIXME: if version == 1, retry with version 0 */ + } else { + DEBUGP(DGPRS, "GTP Rx Echo Response\n"); + } + return 0; +} + +/* libgtp callback for confirmations */ +static int cb_conf(int type, int cause, struct pdp_t *pdp, void *cbp) +{ + DEBUGP(DGPRS, "libgtp cb_conf(type=%d, cause=%d, pdp=%p, cbp=%p)\n", + type, cause, pdp, cbp); + + if (cause == EOF) + LOGP(DGPRS, LOGL_ERROR, "libgtp EOF (type=%u, pdp=%p, cbp=%p)\n", + type, pdp, cbp); + + switch (type) { + case GTP_ECHO_REQ: + return echo_conf(cause); + case GTP_CREATE_PDP_REQ: + return create_pdp_conf(pdp, cbp, cause); + case GTP_DELETE_PDP_REQ: + return delete_pdp_conf(pdp, cause); + default: + break; + } + return 0; +} + +/* Called whenever a PDP context is deleted for any reason */ +static int cb_delete_context(struct pdp_t *pdp) +{ + LOGP(DGPRS, LOGL_INFO, "PDP Context was deleted\n"); + return 0; +} + +/* Called when we receive a Version Not Supported message */ +static int cb_unsup_ind(struct sockaddr_in *peer) +{ + LOGP(DGPRS, LOGL_INFO, "GTP Version not supported Indication " + "from %s:%u\n", inet_ntoa(peer->sin_addr), + ntohs(peer->sin_port)); + return 0; +} + +/* Called when we receive a Supported Ext Headers Notification */ +static int cb_extheader_ind(struct sockaddr_in *peer) +{ + LOGP(DGPRS, LOGL_INFO, "GTP Supported Ext Headers Noficiation " + "from %s:%u\n", inet_ntoa(peer->sin_addr), + ntohs(peer->sin_port)); + return 0; +} + +/* Called whenever we recive a DATA packet */ +static int cb_data_ind(struct pdp_t *pdp, void *packet, unsigned int len) +{ + DEBUGP(DGPRS, "GTP DATA IND from GGSN, length=%u\n", len); + + return 0; +} + +/* libgtp select loop integration */ +static int sgsn_gtp_fd_cb(struct bsc_fd *fd, unsigned int what) +{ + struct sgsn_instance *sgi = fd->data; + int rc; + + if (!(what & BSC_FD_READ)) + return 0; + + switch (fd->priv_nr) { + case 0: + rc = gtp_decaps0(sgi->gsn); + break; + case 1: + rc = gtp_decaps1c(sgi->gsn); + break; + case 2: + rc = gtp_decaps1u(sgi->gsn); + break; + } + return rc; +} + +static void timeval_normalize(struct timeval *tv) +{ + unsigned int sec = tv->tv_usec / 1000000; + tv->tv_sec += sec; + tv->tv_usec -= sec * 1000000; +} + +/* diff = a - b */ +static int timeval_diff(struct timeval *diff, + struct timeval *a, + struct timeval *b) +{ + /* Step 1: normalize input values */ + timeval_normalize(a); + timeval_normalize(b); + + if (b->tv_sec > a->tv_sec || + b->tv_sec == a->tv_sec && b->tv_usec > a->tv_usec) { + b->tv_sec = b->tv_usec = 0; + return -ERANGE; + } + + if (b->tv_usec > a->tv_usec) { + a->tv_sec -= 1; + a->tv_usec += 1000000; + } + + diff->tv_usec = a->tv_usec - b->tv_usec; + diff->tv_sec = a->tv_sec - b->tv_sec; + + return 0; +} + +static void sgsn_gtp_tmr_start(struct sgsn_instance *sgi) +{ + struct timeval now, next, diff; + + /* Retrieve next retransmission as struct timeval */ + gtp_retranstimeout(sgi->gsn, &next); + + /* Calculate the difference to now */ + gettimeofday(&now, NULL); + timeval_diff(&diff, &next, &now); + + /* re-schedule the timer */ + bsc_schedule_timer(&sgi->gtp_timer, diff.tv_sec, diff.tv_usec/1000); +} + +/* timer callback for libgtp retransmissions and ping */ +static void sgsn_gtp_tmr_cb(void *data) +{ + struct sgsn_instance *sgi = data; + + /* Do all the retransmissions as needed */ + gtp_retrans(sgi->gsn); + + sgsn_gtp_tmr_start(sgi); +} + +int sgsn_gtp_init(struct sgsn_instance *sgi) +{ + int rc; + struct gsn_t *gsn; + + rc = gtp_new(&sgi->gsn, sgi->cfg.gtp_statedir, + &sgi->cfg.gtp_listenaddr.sin_addr, GTP_MODE_SGSN); + if (rc) { + LOGP(DGPRS, LOGL_ERROR, "Failed to create GTP: %d\n", rc); + return rc; + } + gsn = sgi->gsn; + + sgi->gtp_fd0.fd = gsn->fd0; + sgi->gtp_fd0.priv_nr = 0; + sgi->gtp_fd0.data = sgi; + sgi->gtp_fd0.cb = sgsn_gtp_fd_cb; + rc = bsc_register_fd(&sgi->gtp_fd0); + if (rc < 0) + return rc; + + sgi->gtp_fd1c.fd = gsn->fd1c; + sgi->gtp_fd1c.priv_nr = 1; + sgi->gtp_fd1c.data = sgi; + sgi->gtp_fd1c.cb = sgsn_gtp_fd_cb; + bsc_register_fd(&sgi->gtp_fd1c); + if (rc < 0) + return rc; + + sgi->gtp_fd1u.fd = gsn->fd1u; + sgi->gtp_fd1u.priv_nr = 2; + sgi->gtp_fd1u.data = sgi; + sgi->gtp_fd1u.cb = sgsn_gtp_fd_cb; + bsc_register_fd(&sgi->gtp_fd1u); + if (rc < 0) + return rc; + + /* Start GTP re-transmission timer */ + sgi->gtp_timer.cb = sgsn_gtp_tmr_cb; + sgsn_gtp_tmr_start(sgi); + + /* Register callbackcs with libgtp */ + gtp_set_cb_delete_context(gsn, cb_delete_context); + gtp_set_cb_conf(gsn, cb_conf); + gtp_set_cb_data_ind(gsn, cb_data_ind); + gtp_set_cb_unsup_ind(gsn, cb_unsup_ind); + gtp_set_cb_extheader_ind(gsn, cb_extheader_ind); + + return 0; +} diff --git a/openbsc/src/gprs/sgsn_main.c b/openbsc/src/gprs/sgsn_main.c index 7aa6e139e..ac895a2ce 100644 --- a/openbsc/src/gprs/sgsn_main.c +++ b/openbsc/src/gprs/sgsn_main.c @@ -46,6 +46,8 @@ #include #include +#include + #include "../../bscconfig.h" /* this is here for the vty... it will never be called */ @@ -64,8 +66,12 @@ const char *openbsc_copyright = "This is free software: you are free to change and redistribute it.\n" "There is NO WARRANTY, to the extent permitted by law.\n"; -static char *config_file = "osmo_sgsn.cfg"; -static struct sgsn_config sgcfg; +struct sgsn_instance sgsn_inst = { + .config_file = "osmo_sgsn.cfg", + .cfg = { + .gtp_statedir = "./", + }, +}; /* call-back function for the NS protocol */ static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, @@ -152,17 +158,18 @@ int main(int argc, char **argv) LOGP(DGPRS, LOGL_ERROR, "Unable to instantiate NS\n"); exit(1); } - bssgp_nsi = sgcfg.nsi = sgsn_nsi; + bssgp_nsi = sgsn_inst.cfg.nsi = sgsn_nsi; gprs_ns_vty_init(bssgp_nsi); /* FIXME: register signal handler for SS_NS */ - rc = sgsn_parse_config(config_file, &sgcfg); + rc = sgsn_parse_config(sgsn_inst.config_file, &sgsn_inst.cfg); if (rc < 0) { LOGP(DGPRS, LOGL_FATAL, "Cannot parse config file\n"); exit(2); } - nsip_listen(sgsn_nsi, sgcfg.nsip_listen_port); + rc = sgsn_gtp_init(&sgsn_inst); + nsip_listen(sgsn_nsi, sgsn_inst.cfg.nsip_listen_port); while (1) { rc = bsc_select_main(0);