blob: c8c8e490e00c6392408aa88b336decb5c24d52f0 [file] [log] [blame]
Mychaela N. Falconiaff7c7ea2023-09-21 01:55:51 +00001/* OsmoHLR SMS-over-GSUP routing implementation */
2
3/* Author: Mychaela N. Falconia <falcon@freecalypso.org>, 2023 - however,
4 * Mother Mychaela's contributions are NOT subject to copyright.
5 * No rights reserved, all rights relinquished.
6 *
7 * Based on earlier unmerged work by Vadim Yanitskiy, 2019.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#include <stdint.h>
24#include <string.h>
25#include <errno.h>
26
27#include <osmocom/core/talloc.h>
28#include <osmocom/gsm/gsup.h>
Mychaela N. Falconia786ec5b2023-09-19 17:15:43 +000029#include <osmocom/gsm/gsm48_ie.h>
30#include <osmocom/gsm/protocol/gsm_04_11.h>
Mychaela N. Falconiaff7c7ea2023-09-21 01:55:51 +000031
32#include <osmocom/hlr/hlr.h>
33#include <osmocom/hlr/hlr_sms.h>
34#include <osmocom/hlr/gsup_server.h>
35#include <osmocom/hlr/gsup_router.h>
36#include <osmocom/hlr/logging.h>
37#include <osmocom/hlr/db.h>
38
39/***********************************************************************
40 * core data structures expressing config from VTY
41 ***********************************************************************/
42
43struct hlr_smsc *smsc_find(struct hlr *hlr, const char *name)
44{
45 struct hlr_smsc *smsc;
46
47 llist_for_each_entry(smsc, &hlr->smsc_list, list) {
48 if (!strcmp(smsc->name, name))
49 return smsc;
50 }
51 return NULL;
52}
53
54struct hlr_smsc *smsc_alloc(struct hlr *hlr, const char *name)
55{
56 struct hlr_smsc *smsc = smsc_find(hlr, name);
57 if (smsc)
58 return NULL;
59
60 smsc = talloc_zero(hlr, struct hlr_smsc);
61 smsc->name = talloc_strdup(smsc, name);
62 smsc->hlr = hlr;
63 llist_add_tail(&smsc->list, &hlr->smsc_list);
64
65 return smsc;
66}
67
68void smsc_free(struct hlr_smsc *smsc)
69{
70 llist_del(&smsc->list);
71 talloc_free(smsc);
72}
73
74struct hlr_smsc_route *smsc_route_find(struct hlr *hlr, const char *num_addr)
75{
76 struct hlr_smsc_route *rt;
77
78 llist_for_each_entry(rt, &hlr->smsc_routes, list) {
79 if (!strcmp(rt->num_addr, num_addr))
80 return rt;
81 }
82 return NULL;
83}
84
85struct hlr_smsc_route *smsc_route_alloc(struct hlr *hlr, const char *num_addr,
86 struct hlr_smsc *smsc)
87{
88 struct hlr_smsc_route *rt;
89
90 if (smsc_route_find(hlr, num_addr))
91 return NULL;
92
93 rt = talloc_zero(hlr, struct hlr_smsc_route);
94 rt->num_addr = talloc_strdup(rt, num_addr);
95 rt->smsc = smsc;
96 llist_add_tail(&rt->list, &hlr->smsc_routes);
97
98 return rt;
99}
100
101void smsc_route_free(struct hlr_smsc_route *rt)
102{
103 llist_del(&rt->list);
104 talloc_free(rt);
105}
Mychaela N. Falconia786ec5b2023-09-19 17:15:43 +0000106
107/***********************************************************************
108 * forwarding of MO SMS to SMSCs based on SM-RP-DA
109 ***********************************************************************/
110
111static const struct hlr_smsc *find_smsc_route(const char *smsc_addr)
112{
113 const struct hlr_smsc_route *rt;
114
115 rt = smsc_route_find(g_hlr, smsc_addr);
116 if (rt)
117 return rt->smsc;
118 return g_hlr->smsc_default;
119}
120
121static void respond_with_sm_rp_cause(struct osmo_gsup_req *req,
122 uint8_t sm_rp_cause)
123{
124 struct osmo_gsup_message rsp_msg = { };
125
126 rsp_msg.sm_rp_cause = &sm_rp_cause;
127 osmo_gsup_req_respond(req, &rsp_msg, true, true);
128}
129
130/* Short Message from MSC/VLR towards SMSC */
131void forward_mo_sms(struct osmo_gsup_req *req)
132{
133 uint8_t gsm48_decode_buffer[GSM411_SMSC_ADDR_MAX_OCTETS];
134 char smsc_addr[GSM411_SMSC_ADDR_MAX_DIGITS+1];
135 const struct hlr_smsc *smsc;
136 struct osmo_cni_peer_id dest_peer;
137
138 /* Make sure SM-RP-DA (SMSC address) is present */
139 if (req->gsup.sm_rp_da == NULL || !req->gsup.sm_rp_da_len) {
140 osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
141 "missing SM-RP-DA");
142 return;
143 }
144
145 if (req->gsup.sm_rp_da_type != OSMO_GSUP_SMS_SM_RP_ODA_SMSC_ADDR) {
146 osmo_gsup_req_respond_err(req, GMM_CAUSE_INV_MAND_INFO,
147 "SM-RP-DA type is not SMSC");
148 return;
149 }
150
151 /* Enforce the length constrainst on SM-RP-DA, as specified in
152 * GSM 04.11 section 8.2.5.2. Also enforce absence of ToN/NPI
153 * extension octets at the same time. */
154 if (req->gsup.sm_rp_da_len < GSM411_SMSC_ADDR_MIN_OCTETS ||
155 req->gsup.sm_rp_da_len > GSM411_SMSC_ADDR_MAX_OCTETS ||
156 !(req->gsup.sm_rp_da[0] & 0x80)) {
157 /* This form of bogosity originates from the MS,
158 * not from OsmoMSC or any other Osmocom network elements! */
159 LOGP(DLSMS, LOGL_NOTICE,
160 "Rx '%s' (IMSI-%s) contains invalid SM-RP-DA from MS\n",
161 osmo_gsup_message_type_name(req->gsup.message_type),
162 req->gsup.imsi);
163 respond_with_sm_rp_cause(req, GSM411_RP_CAUSE_SEMANT_INC_MSG);
164 return;
165 }
166
167 /* Decode SMSC address from SM-RP-DA */
168 gsm48_decode_buffer[0] = req->gsup.sm_rp_da_len - 1;
169 memcpy(gsm48_decode_buffer + 1, req->gsup.sm_rp_da + 1,
170 req->gsup.sm_rp_da_len - 1);
171 gsm48_decode_bcd_number2(smsc_addr, sizeof(smsc_addr),
172 gsm48_decode_buffer,
173 req->gsup.sm_rp_da_len, 0);
174
175 /* Look for a route to this SMSC */
176 smsc = find_smsc_route(smsc_addr);
177 if (smsc == NULL) {
178 LOGP(DLSMS, LOGL_NOTICE,
179 "Failed to find a route for '%s' (IMSI-%s, SMSC-Addr-%s)\n",
180 osmo_gsup_message_type_name(req->gsup.message_type),
181 req->gsup.imsi, smsc_addr);
182 respond_with_sm_rp_cause(req,
183 GSM411_RP_CAUSE_MO_NUM_UNASSIGNED);
184 return;
185 }
186
187 /* We got the IPA name of our SMSC - forward the message */
188 osmo_cni_peer_id_set(&dest_peer, OSMO_CNI_PEER_ID_IPA_NAME,
189 (const uint8_t *) smsc->name,
190 strlen(smsc->name) + 1);
191 osmo_gsup_forward_to_local_peer(req->cb_data, &dest_peer, req, NULL);
192}
Mychaela N. Falconiaf6a303c2023-08-26 01:06:57 +0000193
194/***********************************************************************
195 * forwarding of MT SMS from SMSCs to MSC/VLR based on IMSI
196 ***********************************************************************/
197
198void forward_mt_sms(struct osmo_gsup_req *req)
199{
200 struct hlr_subscriber subscr;
201 struct osmo_cni_peer_id dest_peer;
202 int rc;
203
204 rc = db_subscr_get_by_imsi(g_hlr->dbc, req->gsup.imsi, &subscr);
205 if (rc < 0) {
206 osmo_gsup_req_respond_err(req, GMM_CAUSE_IMSI_UNKNOWN,
207 "IMSI unknown");
208 return;
209 }
210 /* is this subscriber currently attached to a VLR? */
211 if (!subscr.vlr_number[0]) {
212 osmo_gsup_req_respond_err(req, GMM_CAUSE_IMPL_DETACHED,
213 "subscriber not attached to a VLR");
214 return;
215 }
216 osmo_cni_peer_id_set(&dest_peer, OSMO_CNI_PEER_ID_IPA_NAME,
217 (const uint8_t *) subscr.vlr_number,
218 strlen(subscr.vlr_number) + 1);
219 osmo_gsup_forward_to_local_peer(req->cb_data, &dest_peer, req, NULL);
220}