diff options
Diffstat (limited to 'src/host/layer23/src/modem/sm.c')
-rw-r--r-- | src/host/layer23/src/modem/sm.c | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/src/host/layer23/src/modem/sm.c b/src/host/layer23/src/modem/sm.c new file mode 100644 index 00000000..1977cd6c --- /dev/null +++ b/src/host/layer23/src/modem/sm.c @@ -0,0 +1,277 @@ +/* GPRS SM interfaces as per 3GPP TS 24.008, TS 24.007 */ +/* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> + * All Rights Reserved + * Author: Pau Espin Pedrol <pespin@sysmocom.de> + * + * 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/lienses/>. + * + */ + +#include <stdbool.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <stdio.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/prim.h> +#include <osmocom/core/utils.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/talloc.h> +#include <osmocom/core/tun.h> +#include <osmocom/gsm/protocol/gsm_04_08_gprs.h> + +#include <osmocom/gprs/gmm/gmm.h> +#include <osmocom/gprs/gmm/gmm_prim.h> +#include <osmocom/gprs/sndcp/sndcp.h> +#include <osmocom/gprs/sndcp/sndcp_prim.h> +#include <osmocom/gprs/sm/sm_prim.h> +#include <osmocom/gprs/sm/sm.h> +#include <osmocom/gprs/rlcmac/rlcmac_prim.h> + +#include <osmocom/bb/common/settings.h> +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/apn.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/modem/sm.h> + + +static int modem_sm_handle_pdp_act_cnf(struct osmocom_ms *ms, struct osmo_gprs_sm_prim *sm_prim) +{ + const char *pdu_name = osmo_gprs_sm_prim_name(sm_prim); + struct osmobb_apn *apn = NULL, *apn_it; + struct osmo_netdev *netdev; + char buf_addr[INET6_ADDRSTRLEN]; + char buf_addr2[INET6_ADDRSTRLEN]; + int rc; + + llist_for_each_entry(apn_it, &ms->gprs.apn_list, list) { + if (apn_it->fsm.fi->state == APN_ST_ACTIVATING) { + apn = apn_it; + break; + } + } + + if (!apn) { + LOGP(DSM, LOGL_ERROR, "Rx %s but have no APN!\n", pdu_name); + return -ENOENT; + } + + if (!sm_prim->smreg.pdp_act_cnf.accepted) { + LOGPAPN(LOGL_ERROR, apn, "Rx %s: Activate PDP failed! cause '%s'\n", pdu_name, + get_value_string(gsm48_gsm_cause_names, sm_prim->smreg.pdp_act_cnf.rej.cause)); + osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_RX_SM_ACT_PDP_CTX_REJ, NULL); + /* TODO: maybe retry ? */ + return 0; + } + + ms->subscr.gprs.ptmsi = sm_prim->smreg.pdp_act_cnf.acc.gmm.allocated_ptmsi; + ms->gmmlayer.tlli = sm_prim->smreg.pdp_act_cnf.acc.gmm.allocated_tlli; + + apn->pdp.pdp_addr_ietf_type = sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_ietf_type; + + netdev = osmo_tundev_get_netdev(apn->tun); + switch (sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_ietf_type) { + case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4: + LOGPAPN(LOGL_INFO, apn, "Rx %s: IPv4=%s\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4.u.sa, buf_addr)); + memcpy(&apn->pdp.pdp_addr_v4, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4, sizeof(struct osmo_sockaddr)); + rc = osmo_netdev_add_addr(netdev, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4, 30); + if (rc < 0) { + LOGPAPN(LOGL_ERROR, apn, "Rx %s: Failed setting IPv4=%s\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4.u.sa, buf_addr)); + return rc; + } + break; + case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV6: + LOGPAPN(LOGL_INFO, apn, "Rx %s: IPv6=%s [FIXME: IPv6 not yet supported!]\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6.u.sa, buf_addr)); + memcpy(&apn->pdp.pdp_addr_v6, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6, sizeof(struct osmo_sockaddr)); + rc = osmo_netdev_add_addr(netdev, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6, 64); + if (rc < 0) { + LOGPAPN(LOGL_ERROR, apn, "Rx %s: Failed setting IPv6=%s\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6.u.sa, buf_addr)); + return rc; + } + break; + case OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4V6: + LOGPAPN(LOGL_INFO, apn, "Rx %s: IPv4=%s IPv6=%s [FIXME: IPv6 not yet supported!]\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4.u.sa, buf_addr), + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6.u.sa, buf_addr2)); + memcpy(&apn->pdp.pdp_addr_v4, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4, sizeof(struct osmo_sockaddr)); + memcpy(&apn->pdp.pdp_addr_v6, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6, sizeof(struct osmo_sockaddr)); + rc = osmo_netdev_add_addr(netdev, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4, 30); + if (rc < 0) { + LOGPAPN(LOGL_ERROR, apn, "Rx %s: Failed setting IPv4=%s\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v4.u.sa, buf_addr)); + return rc; + } + rc = osmo_netdev_add_addr(netdev, &sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6, 64); + if (rc < 0) { + LOGPAPN(LOGL_ERROR, apn, "Rx %s: Failed setting IPv6=%s\n", pdu_name, + osmo_sockaddr_ntop(&sm_prim->smreg.pdp_act_cnf.acc.pdp_addr_v6.u.sa, buf_addr)); + return rc; + } + break; + default: + OSMO_ASSERT(0); + } + + /* TODO: Handle PCO */ + /* TODO: Handle QoS */ + + osmo_fsm_inst_dispatch(apn->fsm.fi, APN_EV_RX_SM_ACT_PDP_CTX_ACC, NULL); + return rc; +} + +static int modem_sm_prim_up_cb(struct osmo_gprs_sm_prim *sm_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_sm_prim_name(sm_prim); + struct osmocom_ms *ms = user_data; + int rc = 0; + + switch (sm_prim->oph.sap) { + case OSMO_GPRS_SM_SAP_SMREG: + switch (OSMO_PRIM_HDR(&sm_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_CONFIRM): + modem_sm_handle_pdp_act_cnf(ms, sm_prim); + break; + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE, PRIM_OP_INDICATION): + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_DEACTIVATE, PRIM_OP_CONFIRM): + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_DEACTIVATE, PRIM_OP_INDICATION): + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_MODIFY, PRIM_OP_CONFIRM): + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_MODIFY, PRIM_OP_INDICATION): + case OSMO_PRIM(OSMO_GPRS_SM_SMREG_PDP_ACTIVATE_SEC, PRIM_OP_CONFIRM): + default: + LOGP(DSM, LOGL_ERROR, "%s(): Rx %s UNIMPLEMENTED\n", __func__, pdu_name); + break; + }; + break; + default: + LOGP(DSM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + + return rc; +} + +int modem_sm_prim_sndcp_up_cb(struct osmo_gprs_sndcp_prim *sndcp_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_sndcp_prim_name(sndcp_prim); + int rc; + + switch (sndcp_prim->oph.sap) { + case OSMO_GPRS_SNDCP_SAP_SNSM: + switch (OSMO_PRIM_HDR(&sndcp_prim->oph)) { + case OSMO_PRIM(OSMO_GPRS_SNDCP_SNSM_ACTIVATE, PRIM_OP_INDICATION): + LOGP(DSM, LOGL_INFO, "%s(): Rx %s\n", __func__, pdu_name); + rc = osmo_gprs_sndcp_prim_dispatch_snsm(sndcp_prim); + rc = 1; /* Tell SM that we take ownership of the prim. */ + break; + default: + LOGP(DSM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + break; + default: + LOGP(DSM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + return rc; +} + +static int modem_sm_prim_down_cb(struct osmo_gprs_sm_prim *sm_prim, void *user_data) +{ + const char *pdu_name = osmo_gprs_sm_prim_name(sm_prim); + int rc = 0; + + switch (sm_prim->oph.sap) { + default: + LOGP(DSM, LOGL_ERROR, "%s(): Unexpected Rx %s\n", __func__, pdu_name); + OSMO_ASSERT(0); + } + + return rc; +} + +static int modem_sm_prim_gmm_down_cb(struct osmo_gprs_gmm_prim *gmm_prim, void *user_data) +{ + int rc; + + rc = osmo_gprs_gmm_prim_upper_down(gmm_prim); + + /* GMM took ownership of the message, tell SM layer to not free it: */ + rc = 1; + return rc; +} + +int modem_sm_init(struct osmocom_ms *ms) +{ + int rc; + rc = osmo_gprs_sm_init(OSMO_GPRS_SM_LOCATION_MS); + if (rc != 0) + return rc; + + osmo_gprs_sm_set_log_cat(OSMO_GPRS_SM_LOGC_SM, DSM); + + osmo_gprs_sm_prim_set_up_cb(modem_sm_prim_up_cb, ms); + osmo_gprs_sm_prim_set_sndcp_up_cb(modem_sm_prim_sndcp_up_cb, ms); + osmo_gprs_sm_prim_set_down_cb(modem_sm_prim_down_cb, ms); + osmo_gprs_sm_prim_set_gmm_down_cb(modem_sm_prim_gmm_down_cb, ms); + + return rc; +} + +int modem_sm_smreg_pdp_act_req(const struct osmocom_ms *ms, const struct osmobb_apn *apn) +{ + struct osmo_gprs_sm_prim *sm_prim; + const struct gsm_subscriber *subscr = &ms->subscr; + enum osmo_gprs_sm_pdp_addr_ietf_type pdp_addr_ietf_type; + struct osmo_sockaddr pdp_addr_any = {0}; + int rc; + + if (apn->cfg.apn_type_mask & APN_TYPE_IPv4v6) { + pdp_addr_ietf_type = OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4V6; + } else if (apn->cfg.apn_type_mask & APN_TYPE_IPv4) { + pdp_addr_ietf_type = OSMO_GPRS_SM_PDP_ADDR_IETF_IPV4; + } else if (apn->cfg.apn_type_mask & APN_TYPE_IPv6) { + pdp_addr_ietf_type = OSMO_GPRS_SM_PDP_ADDR_IETF_IPV6; + } else { + LOGP(DSM, LOGL_ERROR, "APN '%s' has no PDP address type set\n", apn->cfg.name); + return -EINVAL; + } + + sm_prim = osmo_gprs_sm_prim_alloc_smreg_pdp_act_req(); + sm_prim->smreg.pdp_act_req.nsapi = apn->pdp.nsapi; + sm_prim->smreg.pdp_act_req.llc_sapi = apn->pdp.llc_sapi; + sm_prim->smreg.pdp_act_req.pdp_addr_ietf_type = pdp_addr_ietf_type; + sm_prim->smreg.pdp_act_req.pdp_addr_v4 = pdp_addr_any; + sm_prim->smreg.pdp_act_req.pdp_addr_v6 = pdp_addr_any; + memcpy(sm_prim->smreg.pdp_act_req.qos, apn->pdp.qos, apn->pdp.qos_len); + sm_prim->smreg.pdp_act_req.qos_len = apn->pdp.qos_len; + memcpy(sm_prim->smreg.pdp_act_req.pco, apn->pdp.pco, apn->pdp.pco_len); + sm_prim->smreg.pdp_act_req.pco_len = apn->pdp.pco_len; + OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.apn, apn->cfg.name); + sm_prim->smreg.pdp_act_req.gmm.ptmsi = subscr->gprs.ptmsi; + sm_prim->smreg.pdp_act_req.gmm.ptmsi_sig = subscr->gprs.ptmsi; + OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.gmm.imsi, subscr->imsi); + OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.gmm.imei, ms->settings.imei); + OSMO_STRLCPY_ARRAY(sm_prim->smreg.pdp_act_req.gmm.imeisv, ms->settings.imeisv); + memcpy(&sm_prim->smreg.pdp_act_req.gmm.old_rai, &subscr->gprs.rai, sizeof(subscr->gprs.rai)); + + rc = osmo_gprs_sm_prim_upper_down(sm_prim); + if (rc < 0) + LOGP(DSM, LOGL_ERROR, "Failed submitting SMREG-PDP_ACT_REQ.req\n"); + return rc; +} |