blob: 3729d91f3582e3200b4fcc6f259f8bdcb14b3d34 [file] [log] [blame]
Holger Hans Peter Freyther908bc9b2010-09-29 19:32:55 +08001/* USSD Filter Code */
2
3/*
4 * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * (C) 2010 by On-Waves
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (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
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 *
22 */
23
24#include <openbsc/bsc_nat.h>
25#include <openbsc/bsc_nat_sccp.h>
Holger Hans Peter Freyther1ff40d82010-10-13 20:22:36 +020026#include <openbsc/ipaccess.h>
27#include <openbsc/socket.h>
Holger Hans Peter Freyther908bc9b2010-09-29 19:32:55 +080028
Holger Hans Peter Freyther6f744c32010-10-11 10:07:37 +020029#include <osmocore/protocol/gsm_08_08.h>
Holger Hans Peter Freyther6f744c32010-10-11 10:07:37 +020030#include <osmocore/gsm0480.h>
Holger Hans Peter Freyther1ff40d82010-10-13 20:22:36 +020031#include <osmocore/talloc.h>
32#include <osmocore/tlv.h>
Holger Hans Peter Freyther6f744c32010-10-11 10:07:37 +020033
Holger Hans Peter Freyther5cd2fae2010-10-15 10:09:31 +020034#include <osmocom/sccp/sccp.h>
35
Holger Hans Peter Freyther1ff40d82010-10-13 20:22:36 +020036#include <sys/socket.h>
Holger Hans Peter Freyther6f744c32010-10-11 10:07:37 +020037#include <string.h>
Holger Hans Peter Freyther1ff40d82010-10-13 20:22:36 +020038#include <unistd.h>
Holger Hans Peter Freyther6f744c32010-10-11 10:07:37 +020039
Holger Hans Peter Freyther1ff40d82010-10-13 20:22:36 +020040struct bsc_nat_ussd_con {
41 struct write_queue queue;
42 struct bsc_nat *nat;
43 int authorized;
44
45 struct timer_list auth_timeout;
46};
47
48static void ussd_auth_con(struct tlv_parsed *, struct bsc_nat_ussd_con *);
49
50static struct bsc_nat_ussd_con *bsc_nat_ussd_alloc(struct bsc_nat *nat)
51{
52 struct bsc_nat_ussd_con *con;
53
54 con = talloc_zero(nat, struct bsc_nat_ussd_con);
55 if (!con)
56 return NULL;
57
58 con->nat = nat;
59 return con;
60}
61
62static void bsc_nat_ussd_destroy(struct bsc_nat_ussd_con *con)
63{
64 if (con->nat->ussd_con == con)
65 con->nat->ussd_con = NULL;
66 close(con->queue.bfd.fd);
67 bsc_unregister_fd(&con->queue.bfd);
68 bsc_del_timer(&con->auth_timeout);
69 write_queue_clear(&con->queue);
70 talloc_free(con);
71}
72
Holger Hans Peter Freyther8ef837f2010-10-16 17:34:37 +020073static int forward_sccp(struct bsc_nat *nat, struct msgb *msg)
74{
75 struct sccp_connections *con;
76 struct bsc_nat_parsed *parsed;
77
78
79 parsed = bsc_nat_parse(msg);
80 if (!parsed) {
81 LOGP(DNAT, LOGL_ERROR, "Can not parse msg from USSD.\n");
82 msgb_free(msg);
83 return -1;
84 }
85
86 if (!parsed->dest_local_ref) {
87 LOGP(DNAT, LOGL_ERROR, "No destination local reference.\n");
88 msgb_free(msg);
89 return -1;
90 }
91
92 con = bsc_nat_find_con_by_bsc(nat, parsed->dest_local_ref);
93 if (!con || !con->bsc) {
94 LOGP(DNAT, LOGL_ERROR, "No active connection found.\n");
95 msgb_free(msg);
96 return -1;
97 }
98
99 talloc_free(parsed);
100 bsc_write_msg(&con->bsc->write_queue, msg);
101 return 0;
102}
103
Holger Hans Peter Freyther1ff40d82010-10-13 20:22:36 +0200104static int ussd_read_cb(struct bsc_fd *bfd)
105{
106 int error;
107 struct bsc_nat_ussd_con *conn = bfd->data;
108 struct msgb *msg = ipaccess_read_msg(bfd, &error);
109 struct ipaccess_head *hh;
110
111 if (!msg) {
112 LOGP(DNAT, LOGL_ERROR, "USSD Connection was lost.\n");
113 bsc_nat_ussd_destroy(conn);
114 return -1;
115 }
116
117 LOGP(DNAT, LOGL_NOTICE, "MSG from USSD: %s proto: %d\n",
118 hexdump(msg->data, msg->len), msg->l2h[0]);
119 hh = (struct ipaccess_head *) msg->data;
120
121 if (hh->proto == IPAC_PROTO_IPACCESS) {
122 if (msg->l2h[0] == IPAC_MSGT_ID_RESP) {
123 struct tlv_parsed tvp;
124 ipaccess_idtag_parse(&tvp,
125 (unsigned char *) msg->l2h + 2,
126 msgb_l2len(msg) - 2);
127 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 Freyther8ef837f2010-10-16 17:34:37 +0200133 forward_sccp(conn->nat, msg);
Holger Hans Peter Freyther1ff40d82010-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,
252 ntohl(addr.s_addr), 5001, ussd_listen_cb);
253}
Holger Hans Peter Freyther908bc9b2010-09-29 19:32:55 +0800254
Holger Hans Peter Freyther5cd2fae2010-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 Freyther8ef837f2010-10-16 17:34:37 +0200258 struct msgb *msg, *copy;
Holger Hans Peter Freyther5cd2fae2010-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 Freyther8ef837f2010-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 Freyther5cd2fae2010-10-15 10:09:31 +0200281 msg->l2h = msgb_put(msg, 1);
282 msg->l2h[0] = IPAC_MSGT_SCCP_STATE;
283
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 Freyther8ef837f2010-10-16 17:34:37 +0200294 bsc_do_write(&ussd->queue, copy, IPAC_PROTO_SCCP);
Holger Hans Peter Freyther5cd2fae2010-10-15 10:09:31 +0200295
296 return 0;
297}
298
Holger Hans Peter Freyther908bc9b2010-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 Freyther6f744c32010-10-11 10:07:37 +0200302 uint32_t len;
303 uint8_t msg_type;
304 struct gsm48_hdr *hdr48;
305 struct bsc_nat_acc_lst *lst;
306 struct ussd_request req;
307
308 /*
309 * various checks to avoid the decoding work. Right now we only want to
310 * decode if the connection was created for USSD, we do have a USSD access
311 * list, a query, a IMSI and such...
312 */
313 if (con->con_type != NAT_CON_TYPE_SSA)
314 return 0;
315
316 if (!con->imsi)
317 return 0;
318
319 if (!con->bsc->nat->ussd_lst_name)
320 return 0;
321 if (!con->bsc->nat->ussd_query)
322 return 0;
323
324 if (parsed->bssap != BSSAP_MSG_DTAP)
325 return 0;
326
327 if (strlen(con->imsi) > GSM_IMSI_LENGTH)
328 return 0;
329
330 hdr48 = bsc_unpack_dtap(parsed, msg, &len);
331 if (!hdr48)
332 return 0;
333
334 msg_type = hdr48->msg_type & 0xbf;
335 if (hdr48->proto_discr != GSM48_PDISC_NC_SS || msg_type != GSM0480_MTYPE_REGISTER)
336 return 0;
337
338 /* now check if it is a IMSI we care about */
339 lst = bsc_nat_acc_lst_find(con->bsc->nat, con->bsc->nat->ussd_lst_name);
340 if (!lst)
341 return 0;
342
343 if (bsc_nat_lst_check_allow(lst, con->imsi) != 0)
344 return 0;
345
346 /* now decode the message and see if we really want to handle it */
347 memset(&req, 0, sizeof(req));
348 if (gsm0480_decode_ussd_request(hdr48, len, &req) != 1)
349 return 0;
350 if (req.text[0] == 0xff)
351 return 0;
352
353 if (strcmp(req.text, con->bsc->nat->ussd_query) != 0)
354 return 0;
355
356 /* found a USSD query for our subscriber */
357 LOGP(DNAT, LOGL_NOTICE, "Found USSD query for %s\n", con->imsi);
Holger Hans Peter Freyther5cd2fae2010-10-15 10:09:31 +0200358 if (forward_ussd(con, &req, msg) != 0)
359 return 0;
Holger Hans Peter Freyther6f744c32010-10-11 10:07:37 +0200360 return 1;
Holger Hans Peter Freyther908bc9b2010-09-29 19:32:55 +0800361}