blob: 5d599c7340cea497f687fe1a45ffb38cc4191c63 [file] [log] [blame]
Holger Hans Peter Freyther17870cf2010-09-29 19:32:55 +08001/* USSD Filter Code */
2
3/*
Holger Hans Peter Freythera18b1162011-04-01 17:32:21 +02004 * (C) 2010-2011 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * (C) 2010-2011 by On-Waves
Holger Hans Peter Freyther17870cf2010-09-29 19:32:55 +08006 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
Harald Welte9af6ddf2011-01-01 15:25:50 +01009 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
Holger Hans Peter Freyther17870cf2010-09-29 19:32:55 +080011 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welte9af6ddf2011-01-01 15:25:50 +010016 * GNU Affero General Public License for more details.
Holger Hans Peter Freyther17870cf2010-09-29 19:32:55 +080017 *
Harald Welte9af6ddf2011-01-01 15:25:50 +010018 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Holger Hans Peter Freyther17870cf2010-09-29 19:32:55 +080020 *
21 */
22
23#include <openbsc/bsc_nat.h>
24#include <openbsc/bsc_nat_sccp.h>
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +020025#include <openbsc/ipaccess.h>
26#include <openbsc/socket.h>
Holger Hans Peter Freyther17870cf2010-09-29 19:32:55 +080027
Harald Welted36ff762011-03-23 18:26:56 +010028#include <osmocom/gsm/protocol/gsm_08_08.h>
29#include <osmocom/gsm/gsm0480.h>
Pablo Neira Ayuso136f4532011-03-22 16:47:59 +010030#include <osmocom/core/talloc.h>
Harald Welted36ff762011-03-23 18:26:56 +010031#include <osmocom/gsm/tlv.h>
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +020032
Holger Hans Peter Freyther4c401e72010-10-15 10:09:31 +020033#include <osmocom/sccp/sccp.h>
34
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +020035#include <sys/socket.h>
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +020036#include <string.h>
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +020037#include <unistd.h>
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +020038
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +020039static void ussd_auth_con(struct tlv_parsed *, struct bsc_nat_ussd_con *);
40
41static struct bsc_nat_ussd_con *bsc_nat_ussd_alloc(struct bsc_nat *nat)
42{
43 struct bsc_nat_ussd_con *con;
44
45 con = talloc_zero(nat, struct bsc_nat_ussd_con);
46 if (!con)
47 return NULL;
48
49 con->nat = nat;
50 return con;
51}
52
53static void bsc_nat_ussd_destroy(struct bsc_nat_ussd_con *con)
54{
Holger Hans Peter Freyther54f53522010-10-27 11:01:55 +020055 if (con->nat->ussd_con == con) {
56 bsc_close_ussd_connections(con->nat);
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +020057 con->nat->ussd_con = NULL;
Holger Hans Peter Freyther54f53522010-10-27 11:01:55 +020058 }
59
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +020060 close(con->queue.bfd.fd);
61 bsc_unregister_fd(&con->queue.bfd);
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +020062 osmo_timer_del(&con->auth_timeout);
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +020063 write_queue_clear(&con->queue);
64 talloc_free(con);
65}
66
Holger Hans Peter Freyther90bbccf2010-10-16 17:34:37 +020067static int forward_sccp(struct bsc_nat *nat, struct msgb *msg)
68{
69 struct sccp_connections *con;
70 struct bsc_nat_parsed *parsed;
71
72
73 parsed = bsc_nat_parse(msg);
74 if (!parsed) {
75 LOGP(DNAT, LOGL_ERROR, "Can not parse msg from USSD.\n");
76 msgb_free(msg);
77 return -1;
78 }
79
80 if (!parsed->dest_local_ref) {
81 LOGP(DNAT, LOGL_ERROR, "No destination local reference.\n");
82 msgb_free(msg);
83 return -1;
84 }
85
86 con = bsc_nat_find_con_by_bsc(nat, parsed->dest_local_ref);
87 if (!con || !con->bsc) {
88 LOGP(DNAT, LOGL_ERROR, "No active connection found.\n");
89 msgb_free(msg);
90 return -1;
91 }
92
93 talloc_free(parsed);
94 bsc_write_msg(&con->bsc->write_queue, msg);
95 return 0;
96}
97
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +020098static int ussd_read_cb(struct bsc_fd *bfd)
99{
100 int error;
101 struct bsc_nat_ussd_con *conn = bfd->data;
102 struct msgb *msg = ipaccess_read_msg(bfd, &error);
103 struct ipaccess_head *hh;
104
105 if (!msg) {
106 LOGP(DNAT, LOGL_ERROR, "USSD Connection was lost.\n");
107 bsc_nat_ussd_destroy(conn);
108 return -1;
109 }
110
111 LOGP(DNAT, LOGL_NOTICE, "MSG from USSD: %s proto: %d\n",
112 hexdump(msg->data, msg->len), msg->l2h[0]);
113 hh = (struct ipaccess_head *) msg->data;
114
115 if (hh->proto == IPAC_PROTO_IPACCESS) {
116 if (msg->l2h[0] == IPAC_MSGT_ID_RESP) {
117 struct tlv_parsed tvp;
Pablo Neira Ayusoca05d432011-04-11 16:32:50 +0200118 int ret;
119 ret = ipaccess_idtag_parse(&tvp,
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +0200120 (unsigned char *) msg->l2h + 2,
121 msgb_l2len(msg) - 2);
Pablo Neira Ayusoca05d432011-04-11 16:32:50 +0200122 if (ret < 0) {
123 LOGP(DNAT, LOGL_ERROR, "ignoring IPA response "
124 "message with malformed TLVs\n");
125 return ret;
126 }
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +0200127 if (TLVP_PRESENT(&tvp, IPAC_IDTAG_UNITNAME))
128 ussd_auth_con(&tvp, conn);
129 }
130
131 msgb_free(msg);
132 } else if (hh->proto == IPAC_PROTO_SCCP) {
Holger Hans Peter Freyther90bbccf2010-10-16 17:34:37 +0200133 forward_sccp(conn->nat, msg);
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +0200134 } else {
135 msgb_free(msg);
136 }
137
138 return 0;
139}
140
141static void ussd_auth_cb(void *_data)
142{
143 LOGP(DNAT, LOGL_ERROR, "USSD module didn't authenticate\n");
144 bsc_nat_ussd_destroy((struct bsc_nat_ussd_con *) _data);
145}
146
147static void ussd_auth_con(struct tlv_parsed *tvp, struct bsc_nat_ussd_con *conn)
148{
149 const char *token;
150 int len;
151 if (!conn->nat->ussd_token) {
152 LOGP(DNAT, LOGL_ERROR, "No USSD token set. Closing\n");
153 bsc_nat_ussd_destroy(conn);
154 return;
155 }
156
157 token = (const char *) TLVP_VAL(tvp, IPAC_IDTAG_UNITNAME);
158 len = TLVP_LEN(tvp, IPAC_IDTAG_UNITNAME);
159 if (strncmp(conn->nat->ussd_token, token, len) != 0) {
160 LOGP(DNAT, LOGL_ERROR, "Wrong USSD token by client: %d\n",
161 conn->queue.bfd.fd);
162 bsc_nat_ussd_destroy(conn);
163 return;
164 }
165
166 /* it is authenticated now */
167 if (conn->nat->ussd_con && conn->nat->ussd_con != conn)
168 bsc_nat_ussd_destroy(conn->nat->ussd_con);
169
170 LOGP(DNAT, LOGL_ERROR, "USSD token specified. USSD provider is connected.\n");
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +0200171 osmo_timer_del(&conn->auth_timeout);
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +0200172 conn->authorized = 1;
173 conn->nat->ussd_con = conn;
174}
175
176static void ussd_start_auth(struct bsc_nat_ussd_con *conn)
177{
178 struct msgb *msg;
179
180 conn->auth_timeout.data = conn;
181 conn->auth_timeout.cb = ussd_auth_cb;
Pablo Neira Ayusobf540cb2011-05-06 12:11:06 +0200182 osmo_timer_schedule(&conn->auth_timeout, conn->nat->auth_timeout, 0);
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +0200183
184 msg = msgb_alloc_headroom(4096, 128, "auth message");
185 if (!msg) {
186 LOGP(DNAT, LOGL_ERROR, "Failed to allocate auth msg\n");
187 return;
188 }
189
190 msgb_v_put(msg, IPAC_MSGT_ID_GET);
191 bsc_do_write(&conn->queue, msg, IPAC_PROTO_IPACCESS);
192}
193
194static int ussd_listen_cb(struct bsc_fd *bfd, unsigned int what)
195{
196 struct bsc_nat_ussd_con *conn;
197 struct bsc_nat *nat;
198 struct sockaddr_in sa;
199 socklen_t sa_len = sizeof(sa);
200 int fd;
201
202 if (!(what & BSC_FD_READ))
203 return 0;
204
205 fd = accept(bfd->fd, (struct sockaddr *) &sa, &sa_len);
206 if (fd < 0) {
207 perror("accept");
208 return fd;
209 }
210
211 nat = (struct bsc_nat *) bfd->data;
212 counter_inc(nat->stats.ussd.reconn);
213
214 conn = bsc_nat_ussd_alloc(nat);
215 if (!conn) {
216 LOGP(DNAT, LOGL_ERROR, "Failed to allocate USSD con struct.\n");
217 close(fd);
218 return -1;
219 }
220
221 write_queue_init(&conn->queue, 10);
222 conn->queue.bfd.data = conn;
223 conn->queue.bfd.fd = fd;
224 conn->queue.bfd.when = BSC_FD_READ;
225 conn->queue.read_cb = ussd_read_cb;
226 conn->queue.write_cb = bsc_write_cb;
227
228 if (bsc_register_fd(&conn->queue.bfd) < 0) {
229 LOGP(DNAT, LOGL_ERROR, "Failed to register USSD fd.\n");
230 bsc_nat_ussd_destroy(conn);
231 return -1;
232 }
233
234 LOGP(DNAT, LOGL_NOTICE, "USSD Connection on %d with IP: %s\n",
235 fd, inet_ntoa(sa.sin_addr));
236
237 /* do authentication */
238 ussd_start_auth(conn);
239 return 0;
240}
241
242int bsc_ussd_init(struct bsc_nat *nat)
243{
244 struct in_addr addr;
245
246 addr.s_addr = INADDR_ANY;
247 if (nat->ussd_local)
248 inet_aton(nat->ussd_local, &addr);
249
250 nat->ussd_listen.data = nat;
251 return make_sock(&nat->ussd_listen, IPPROTO_TCP,
Holger Hans Peter Freyther0d93fb42011-04-11 10:27:10 +0200252 ntohl(addr.s_addr), 5001, 0, ussd_listen_cb, nat);
Holger Hans Peter Freytherc16c2dc2010-10-13 20:22:36 +0200253}
Holger Hans Peter Freyther17870cf2010-09-29 19:32:55 +0800254
Holger Hans Peter Freyther123bc322011-04-16 14:06:18 +0200255static int forward_ussd_simple(struct sccp_connections *con, struct msgb *input)
256{
257 struct msgb *copy;
258 struct bsc_nat_ussd_con *ussd;
259
260 if (!con->bsc->nat->ussd_con)
261 return -1;
262
263 copy = msgb_alloc_headroom(4096, 128, "forward bts");
264 if (!copy) {
265 LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n");
266 return -1;
267 }
268
269 /* copy the data into the copy */
270 copy->l2h = msgb_put(copy, msgb_l2len(input));
271 memcpy(copy->l2h, input->l2h, msgb_l2len(input));
272
273 /* send it out */
274 ussd = con->bsc->nat->ussd_con;
275 bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP);
276 return 0;
277}
278
Holger Hans Peter Freyther4c401e72010-10-15 10:09:31 +0200279static int forward_ussd(struct sccp_connections *con, const struct ussd_request *req,
280 struct msgb *input)
281{
Holger Hans Peter Freyther90bbccf2010-10-16 17:34:37 +0200282 struct msgb *msg, *copy;
Holger Hans Peter Freyther4c401e72010-10-15 10:09:31 +0200283 struct ipac_msgt_sccp_state *state;
284 struct bsc_nat_ussd_con *ussd;
285
286 if (!con->bsc->nat->ussd_con)
287 return -1;
288
289 msg = msgb_alloc_headroom(4096, 128, "forward ussd");
290 if (!msg) {
291 LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n");
292 return -1;
293 }
294
Holger Hans Peter Freyther90bbccf2010-10-16 17:34:37 +0200295 copy = msgb_alloc_headroom(4096, 128, "forward bts");
296 if (!copy) {
297 LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n");
298 msgb_free(msg);
299 return -1;
300 }
301
302 copy->l2h = msgb_put(copy, msgb_l2len(input));
303 memcpy(copy->l2h, input->l2h, msgb_l2len(input));
304
Holger Hans Peter Freyther4c401e72010-10-15 10:09:31 +0200305 msg->l2h = msgb_put(msg, 1);
Holger Hans Peter Freyther368a0a72011-01-07 16:54:46 +0100306 msg->l2h[0] = IPAC_MSGT_SCCP_OLD;
Holger Hans Peter Freyther4c401e72010-10-15 10:09:31 +0200307
308 /* fill out the data */
309 state = (struct ipac_msgt_sccp_state *) msgb_put(msg, sizeof(*state));
310 state->trans_id = req->transaction_id;
311 state->invoke_id = req->invoke_id;
312 memcpy(&state->src_ref, &con->remote_ref, sizeof(con->remote_ref));
313 memcpy(&state->dst_ref, &con->real_ref, sizeof(con->real_ref));
314 memcpy(state->imsi, con->imsi, strlen(con->imsi));
315
316 ussd = con->bsc->nat->ussd_con;
317 bsc_do_write(&ussd->queue, msg, IPAC_PROTO_IPACCESS);
Holger Hans Peter Freyther90bbccf2010-10-16 17:34:37 +0200318 bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP);
Holger Hans Peter Freyther4c401e72010-10-15 10:09:31 +0200319
320 return 0;
321}
322
Holger Hans Peter Freyther17870cf2010-09-29 19:32:55 +0800323int bsc_check_ussd(struct sccp_connections *con, struct bsc_nat_parsed *parsed,
324 struct msgb *msg)
325{
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200326 uint32_t len;
327 uint8_t msg_type;
Holger Hans Peter Freyther5cde92c2011-04-13 18:56:13 +0200328 uint8_t proto;
Holger Hans Peter Freyther123bc322011-04-16 14:06:18 +0200329 uint8_t ti;
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200330 struct gsm48_hdr *hdr48;
331 struct bsc_nat_acc_lst *lst;
332 struct ussd_request req;
333
334 /*
335 * various checks to avoid the decoding work. Right now we only want to
336 * decode if the connection was created for USSD, we do have a USSD access
337 * list, a query, a IMSI and such...
338 */
339 if (con->con_type != NAT_CON_TYPE_SSA)
340 return 0;
341
342 if (!con->imsi)
343 return 0;
344
Holger Hans Peter Freytheref38e852011-04-06 11:27:52 +0200345 /* We have not verified the IMSI yet */
346 if (!con->authorized)
347 return 0;
348
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200349 if (!con->bsc->nat->ussd_lst_name)
350 return 0;
351 if (!con->bsc->nat->ussd_query)
352 return 0;
353
354 if (parsed->bssap != BSSAP_MSG_DTAP)
355 return 0;
356
357 if (strlen(con->imsi) > GSM_IMSI_LENGTH)
358 return 0;
359
360 hdr48 = bsc_unpack_dtap(parsed, msg, &len);
361 if (!hdr48)
362 return 0;
363
Holger Hans Peter Freyther5cde92c2011-04-13 18:56:13 +0200364 proto = hdr48->proto_discr & 0x0f;
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200365 msg_type = hdr48->msg_type & 0xbf;
Holger Hans Peter Freyther123bc322011-04-16 14:06:18 +0200366 ti = (hdr48->proto_discr & 0x70) >> 4;
367 if (proto != GSM48_PDISC_NC_SS)
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200368 return 0;
369
Holger Hans Peter Freyther123bc322011-04-16 14:06:18 +0200370 if (msg_type == GSM0480_MTYPE_REGISTER) {
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200371
Holger Hans Peter Freyther123bc322011-04-16 14:06:18 +0200372 /* now check if it is a IMSI we care about */
373 lst = bsc_nat_acc_lst_find(con->bsc->nat,
374 con->bsc->nat->ussd_lst_name);
375 if (!lst)
376 return 0;
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200377
Holger Hans Peter Freyther123bc322011-04-16 14:06:18 +0200378 if (bsc_nat_lst_check_allow(lst, con->imsi) != 0)
379 return 0;
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200380
Holger Hans Peter Freyther123bc322011-04-16 14:06:18 +0200381 /* now decode the message and see if we really want to handle it */
382 memset(&req, 0, sizeof(req));
383 if (gsm0480_decode_ussd_request(hdr48, len, &req) != 1)
384 return 0;
385 if (req.text[0] == 0xff)
386 return 0;
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200387
Holger Hans Peter Freyther123bc322011-04-16 14:06:18 +0200388 if (regexec(&con->bsc->nat->ussd_query_re,
389 req.text, 0, NULL, 0) == REG_NOMATCH)
390 return 0;
391
392 /* found a USSD query for our subscriber */
393 LOGP(DNAT, LOGL_NOTICE, "Found USSD query for %s\n", con->imsi);
394 con->ussd_ti[ti] = 1;
395 if (forward_ussd(con, &req, msg) != 0)
396 return 0;
397 return 1;
398 } else if (msg_type == GSM0480_MTYPE_FACILITY && con->ussd_ti[ti]) {
399 LOGP(DNAT, LOGL_NOTICE, "Forwarding message part of TI: %d %s\n",
400 ti, con->imsi);
401 if (forward_ussd_simple(con, msg) != 0)
402 return 0;
403 return 1;
404 }
405
406 return 0;
Holger Hans Peter Freyther17870cf2010-09-29 19:32:55 +0800407}