blob: 8c8963e4744429829f9b92fe37db4c82eb5fe93f [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);
62 bsc_del_timer(&con->auth_timeout);
63 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");
171 bsc_del_timer(&conn->auth_timeout);
172 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;
182 bsc_schedule_timer(&conn->auth_timeout, conn->nat->auth_timeout, 0);
183
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 Freyther4c401e72010-10-15 10:09:31 +0200255static int forward_ussd(struct sccp_connections *con, const struct ussd_request *req,
256 struct msgb *input)
257{
Holger Hans Peter Freyther90bbccf2010-10-16 17:34:37 +0200258 struct msgb *msg, *copy;
Holger Hans Peter Freyther4c401e72010-10-15 10:09:31 +0200259 struct ipac_msgt_sccp_state *state;
260 struct bsc_nat_ussd_con *ussd;
261
262 if (!con->bsc->nat->ussd_con)
263 return -1;
264
265 msg = msgb_alloc_headroom(4096, 128, "forward ussd");
266 if (!msg) {
267 LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n");
268 return -1;
269 }
270
Holger Hans Peter Freyther90bbccf2010-10-16 17:34:37 +0200271 copy = msgb_alloc_headroom(4096, 128, "forward bts");
272 if (!copy) {
273 LOGP(DNAT, LOGL_ERROR, "Allocation failed, not forwarding.\n");
274 msgb_free(msg);
275 return -1;
276 }
277
278 copy->l2h = msgb_put(copy, msgb_l2len(input));
279 memcpy(copy->l2h, input->l2h, msgb_l2len(input));
280
Holger Hans Peter Freyther4c401e72010-10-15 10:09:31 +0200281 msg->l2h = msgb_put(msg, 1);
Holger Hans Peter Freyther368a0a72011-01-07 16:54:46 +0100282 msg->l2h[0] = IPAC_MSGT_SCCP_OLD;
Holger Hans Peter Freyther4c401e72010-10-15 10:09:31 +0200283
284 /* fill out the data */
285 state = (struct ipac_msgt_sccp_state *) msgb_put(msg, sizeof(*state));
286 state->trans_id = req->transaction_id;
287 state->invoke_id = req->invoke_id;
288 memcpy(&state->src_ref, &con->remote_ref, sizeof(con->remote_ref));
289 memcpy(&state->dst_ref, &con->real_ref, sizeof(con->real_ref));
290 memcpy(state->imsi, con->imsi, strlen(con->imsi));
291
292 ussd = con->bsc->nat->ussd_con;
293 bsc_do_write(&ussd->queue, msg, IPAC_PROTO_IPACCESS);
Holger Hans Peter Freyther90bbccf2010-10-16 17:34:37 +0200294 bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP);
Holger Hans Peter Freyther4c401e72010-10-15 10:09:31 +0200295
296 return 0;
297}
298
Holger Hans Peter Freyther17870cf2010-09-29 19:32:55 +0800299int bsc_check_ussd(struct sccp_connections *con, struct bsc_nat_parsed *parsed,
300 struct msgb *msg)
301{
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200302 uint32_t len;
303 uint8_t msg_type;
Holger Hans Peter Freyther5cde92c2011-04-13 18:56:13 +0200304 uint8_t proto;
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200305 struct gsm48_hdr *hdr48;
306 struct bsc_nat_acc_lst *lst;
307 struct ussd_request req;
308
309 /*
310 * various checks to avoid the decoding work. Right now we only want to
311 * decode if the connection was created for USSD, we do have a USSD access
312 * list, a query, a IMSI and such...
313 */
314 if (con->con_type != NAT_CON_TYPE_SSA)
315 return 0;
316
317 if (!con->imsi)
318 return 0;
319
Holger Hans Peter Freytheref38e852011-04-06 11:27:52 +0200320 /* We have not verified the IMSI yet */
321 if (!con->authorized)
322 return 0;
323
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200324 if (!con->bsc->nat->ussd_lst_name)
325 return 0;
326 if (!con->bsc->nat->ussd_query)
327 return 0;
328
329 if (parsed->bssap != BSSAP_MSG_DTAP)
330 return 0;
331
332 if (strlen(con->imsi) > GSM_IMSI_LENGTH)
333 return 0;
334
335 hdr48 = bsc_unpack_dtap(parsed, msg, &len);
336 if (!hdr48)
337 return 0;
338
Holger Hans Peter Freyther5cde92c2011-04-13 18:56:13 +0200339 proto = hdr48->proto_discr & 0x0f;
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200340 msg_type = hdr48->msg_type & 0xbf;
Holger Hans Peter Freyther5cde92c2011-04-13 18:56:13 +0200341 if (proto != GSM48_PDISC_NC_SS || msg_type != GSM0480_MTYPE_REGISTER)
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200342 return 0;
343
344 /* now check if it is a IMSI we care about */
345 lst = bsc_nat_acc_lst_find(con->bsc->nat, con->bsc->nat->ussd_lst_name);
346 if (!lst)
347 return 0;
348
349 if (bsc_nat_lst_check_allow(lst, con->imsi) != 0)
350 return 0;
351
352 /* now decode the message and see if we really want to handle it */
353 memset(&req, 0, sizeof(req));
354 if (gsm0480_decode_ussd_request(hdr48, len, &req) != 1)
355 return 0;
356 if (req.text[0] == 0xff)
357 return 0;
358
Holger Hans Peter Freythera18b1162011-04-01 17:32:21 +0200359 if (regexec(&con->bsc->nat->ussd_query_re, req.text, 0, NULL, 0) == REG_NOMATCH)
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200360 return 0;
361
362 /* found a USSD query for our subscriber */
363 LOGP(DNAT, LOGL_NOTICE, "Found USSD query for %s\n", con->imsi);
Holger Hans Peter Freyther4c401e72010-10-15 10:09:31 +0200364 if (forward_ussd(con, &req, msg) != 0)
365 return 0;
Holger Hans Peter Freyther3229f442010-10-11 10:07:37 +0200366 return 1;
Holger Hans Peter Freyther17870cf2010-09-29 19:32:55 +0800367}