blob: 113cabde6d56c4ec6d91f38350177addb78365ef [file] [log] [blame]
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +08001/* MTP layer3 main handling code */
2/*
3 * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
4 * (C) 2010 by On-Waves
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 */
22#include <mtp_data.h>
23#include <mtp_level3.h>
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +080024#include <cellmgr_debug.h>
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080025
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +080026#include <osmocore/talloc.h>
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080027
28#include <sccp/sccp.h>
29
30#include <arpa/inet.h>
31
32#include <string.h>
33
34static void *tall_mtp_ctx = NULL;
35
36static struct msgb *mtp_msg_alloc(struct mtp_link *link)
37{
38 struct mtp_level_3_hdr *hdr;
39 struct msgb *msg = msgb_alloc_headroom(4096, 128, "mtp-msg");
40 if (!msg) {
41 LOGP(DINP, LOGL_ERROR, "Failed to allocate mtp msg\n");
42 return NULL;
43 }
44
45 msg->l2h = msgb_put(msg, sizeof(*hdr));
46 hdr = (struct mtp_level_3_hdr *) msg->l2h;
47 hdr->addr = MTP_ADDR(0x0, link->dpc, link->opc);
48 return msg;
49}
50
51static struct msgb *mtp_create_sltm(struct mtp_link *link)
52{
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +080053 const uint8_t test_ptrn[14] = { 'G', 'S', 'M', 'M', 'M', 'S', };
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080054 struct mtp_level_3_hdr *hdr;
55 struct mtp_level_3_mng *mng;
56 struct msgb *msg = mtp_msg_alloc(link);
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +080057 uint8_t *data;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080058 if (!msg)
59 return NULL;
60
61 hdr = (struct mtp_level_3_hdr *) msg->l2h;
62 hdr->ni = MTP_NI_NATION_NET;
63 hdr->ser_ind = MTP_SI_MNT_REG_MSG;
64
65 mng = (struct mtp_level_3_mng *) msgb_put(msg, sizeof(*mng));
66 mng->cmn.h0 = MTP_TST_MSG_GRP;
67 mng->cmn.h1 = MTP_TST_MSG_SLTM;
68 mng->length = ARRAY_SIZE(test_ptrn);
69
70 data = msgb_put(msg, ARRAY_SIZE(test_ptrn));
71 memcpy(data, test_ptrn, ARRAY_SIZE(test_ptrn));
72
73 /* remember the last tst ptrn... once we have some */
74 memcpy(link->test_ptrn, test_ptrn, ARRAY_SIZE(test_ptrn));
75
76 return msg;
77}
78
79static struct msgb *mtp_create_slta(struct mtp_link *link, struct mtp_level_3_mng *in_mng, int l3_len)
80{
81 struct mtp_level_3_hdr *hdr;
82 struct mtp_level_3_mng *mng;
83 struct msgb *out = mtp_msg_alloc(link);
84
85 if (!out)
86 return NULL;
87
88 hdr = (struct mtp_level_3_hdr *) out->l2h;
89 hdr->ni = MTP_NI_NATION_NET;
90 hdr->ser_ind = MTP_SI_MNT_REG_MSG;
91 mng = (struct mtp_level_3_mng *) msgb_put(out, sizeof(*mng));
92 mng->cmn.h0 = MTP_TST_MSG_GRP;
93 mng->cmn.h1 = MTP_TST_MSG_SLTA;
94 mng->length = l3_len - 2;
95 msgb_put(out, mng->length);
96 memcpy(mng->data, in_mng->data, mng->length);
97
98 return out;
99}
100
101static struct msgb *mtp_tfp_alloc(struct mtp_link *link, int apoc)
102{
103 struct mtp_level_3_hdr *hdr;
104 struct mtp_level_3_prohib *prb;
105 struct msgb *out = mtp_msg_alloc(link);
106
107 if (!out)
108 return NULL;
109
110 hdr = (struct mtp_level_3_hdr *) out->l2h;
111 hdr->ni = MTP_NI_NATION_NET;
112 hdr->ser_ind = MTP_SI_MNT_SNM_MSG;
113 prb = (struct mtp_level_3_prohib *) msgb_put(out, sizeof(*prb));
114 prb->cmn.h0 = MTP_PROHIBIT_MSG_GRP;
115 prb->cmn.h1 = MTP_PROHIBIT_MSG_SIG;
116 prb->apoc = MTP_MAKE_APOC(apoc);
117 return out;
118}
119
120static struct msgb *mtp_tra_alloc(struct mtp_link *link)
121{
122 struct mtp_level_3_hdr *hdr;
123 struct mtp_level_3_cmn *cmn;
124 struct msgb *out = mtp_msg_alloc(link);
125
126 if (!out)
127 return NULL;
128
129 hdr = (struct mtp_level_3_hdr *) out->l2h;
130 hdr->ni = MTP_NI_NATION_NET;
131 hdr->ser_ind = MTP_SI_MNT_SNM_MSG;
132 cmn = (struct mtp_level_3_cmn *) msgb_put(out, sizeof(*cmn));
133 cmn->h0 = MTP_TRF_RESTR_MSG_GRP;
134 cmn->h1 = MTP_RESTR_MSG_ALLWED;
135 return out;
136}
137
138static struct msgb *mtp_sccp_alloc_ssa(struct mtp_link *link, int sls)
139{
140 struct sccp_data_unitdata *udt;
141 struct sccp_con_ctrl_prt_mgt *prt;
142 struct mtp_level_3_hdr *hdr;
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +0800143 uint8_t *data;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800144
145
146 struct msgb *out = mtp_msg_alloc(link);
147
148 if (!out)
149 return NULL;
150
151 hdr = (struct mtp_level_3_hdr *) out->l2h;
152 hdr->ni = MTP_NI_NATION_NET;
153 hdr->ser_ind = MTP_SI_MNT_SCCP;
154
155 /* this appears to be round robin or such.. */
156 hdr->addr = MTP_ADDR(sls % 16, link->dpc, link->opc);
157
158 /* generate the UDT message... libsccp does not offer formating yet */
159 udt = (struct sccp_data_unitdata *) msgb_put(out, sizeof(*udt));
160 udt->type = SCCP_MSG_TYPE_UDT;
161 udt->proto_class = SCCP_PROTOCOL_CLASS_0;
162 udt->variable_called = 3;
163 udt->variable_calling = 5;
164 udt->variable_data = 7;
165
166 /* put the called and calling address. It is LV */
167 data = msgb_put(out, 2 + 1);
168 data[0] = 2;
169 data[1] = 0x42;
170 data[2] = 0x1;
171
172 data = msgb_put(out, 2 + 1);
173 data[0] = 2;
174 data[1] = 0x42;
175 data[2] = 0x1;
176
177 data = msgb_put(out, 1);
178 data[0] = sizeof(*prt);
179
180 prt = (struct sccp_con_ctrl_prt_mgt *) msgb_put(out, sizeof(*prt));
181 prt->sst = SCCP_SSA;
182 prt->assn = 254;
183 prt->apoc = MTP_MAKE_APOC(link->opc);
184 prt->mul_ind = 0;
185
186 return out;
187}
188
189void mtp_link_init(void)
190{
191 tall_mtp_ctx = talloc_named_const(NULL, 1, "mtp-link");
192}
193
194static void mtp_send_sltm(struct mtp_link *link)
195{
196 struct msgb *msg;
197
198 link->sltm_pending = 1;
199 msg = mtp_create_sltm(link);
200 if (!msg) {
201 LOGP(DINP, LOGL_ERROR, "Failed to allocate SLTM.\n");
202 return;
203 }
204
205 mtp_link_submit(link, msg);
206}
207
208static void mtp_sltm_t1_timeout(void *_link)
209{
210 struct mtp_link *link = (struct mtp_link *) _link;
211
212 if (link->slta_misses == 0) {
213 LOGP(DINP, LOGL_ERROR, "No SLTM response. Retrying. Link: %p\n", link);
214 ++link->slta_misses;
215 mtp_send_sltm(link);
216 bsc_schedule_timer(&link->t1_timer, MTP_T1);
217 } else {
218 LOGP(DINP, LOGL_ERROR, "Two missing SLTAs. Restart link: %p\n", link);
219 link->sccp_up = 0;
220 link->running = 0;
221 bsc_del_timer(&link->t2_timer);
222 mtp_link_sccp_down(link);
223 mtp_link_restart(link);
224 }
225}
226
227static void mtp_sltm_t2_timeout(void *_link)
228{
229 struct mtp_link *link = (struct mtp_link *) _link;
230
231 if (!link->running) {
232 LOGP(DINP, LOGL_INFO, "Not restarting SLTM timer on link: %p\n", link);
233 return;
234 }
235
236 link->slta_misses = 0;
237 mtp_send_sltm(link);
238
239 bsc_schedule_timer(&link->t1_timer, MTP_T1);
240
241 if (link->sltm_once && link->was_up)
242 LOGP(DINP, LOGL_INFO, "Not sending SLTM again as configured.\n");
243 else
244 bsc_schedule_timer(&link->t2_timer, MTP_T2);
245}
246
247static void mtp_delayed_start(void *link)
248{
249 mtp_sltm_t2_timeout(link);
250}
251
252struct mtp_link *mtp_link_alloc(void)
253{
254 struct mtp_link *link;
255
256 link = talloc_zero(tall_mtp_ctx, struct mtp_link);
257 if (!link)
258 return NULL;
259
260 link->t1_timer.data = link;
261 link->t1_timer.cb = mtp_sltm_t1_timeout;
262 link->t2_timer.data = link;
263 link->t2_timer.cb = mtp_sltm_t2_timeout;
264 link->delay_timer.data = link;
265 link->delay_timer.cb = mtp_delayed_start;
266 INIT_LLIST_HEAD(&link->pending_msgs);
267 return link;
268}
269
270void mtp_link_stop(struct mtp_link *link)
271{
272 bsc_del_timer(&link->t1_timer);
273 bsc_del_timer(&link->t2_timer);
274 bsc_del_timer(&link->delay_timer);
275 link->sccp_up = 0;
276 link->running = 0;
277 link->sltm_pending = 0;
278
279 mtp_link_sccp_down(link);
280}
281
282void mtp_link_reset(struct mtp_link *link)
283{
284 mtp_link_stop(link);
285 link->running = 1;
286 bsc_schedule_timer(&link->delay_timer, START_DELAY);
287}
288
289static int mtp_link_sign_msg(struct mtp_link *link, struct mtp_level_3_hdr *hdr, int l3_len)
290{
291 struct msgb *msg;
292 struct mtp_level_3_cmn *cmn;
293
294 if (hdr->spare != 0 || hdr->ni != MTP_NI_NATION_NET || l3_len < 1) {
295 LOGP(DINP, LOGL_ERROR, "Unhandled data (%d, %d, %d)\n",
296 hdr->spare, hdr->ni, l3_len);
297 return -1;
298 }
299
300 cmn = (struct mtp_level_3_cmn *) &hdr->data[0];
301 LOGP(DINP, LOGL_DEBUG, "reg msg: h0: 0x%x h1: 0x%x\n",
302 cmn->h0, cmn->h1);
303
304 switch (cmn->h0) {
305 case MTP_TRF_RESTR_MSG_GRP:
306 switch (cmn->h1) {
307 case MTP_RESTR_MSG_ALLWED:
308 LOGP(DINP, LOGL_INFO, "Received Restart Allowed. SST should be next: %p\n", link);
309 link->sccp_up = 0;
310 mtp_link_sccp_down(link);
311
312 msg = mtp_tfp_alloc(link, 0);
313 if (!msg)
314 return -1;
315 mtp_link_submit(link, msg);
316
317 msg = mtp_tra_alloc(link);
318 if (!msg)
319 return -1;
320
321 mtp_link_submit(link, msg);
322 return 0;
323 break;
324 }
325 break;
326 }
327
328 abort();
329 return -1;
330}
331
332static int mtp_link_regular_msg(struct mtp_link *link, struct mtp_level_3_hdr *hdr, int l3_len)
333{
334 struct msgb *out;
335 struct mtp_level_3_mng *mng;
336
337 if (hdr->spare != 0 || hdr->ni != MTP_NI_NATION_NET || l3_len < 2) {
338 LOGP(DINP, LOGL_ERROR, "Unhandled data (%d, %d, %d)\n",
339 hdr->spare, hdr->ni, l3_len);
340 return -1;
341 }
342
343 mng = (struct mtp_level_3_mng *) &hdr->data[0];
344 LOGP(DINP, LOGL_DEBUG, "reg msg: h0: 0x%x h1: 0x%x\n",
345 mng->cmn.h0, mng->cmn.h1);
346
347 switch (mng->cmn.h0) {
348 case MTP_TST_MSG_GRP:
349 switch (mng->cmn.h1) {
350 case MTP_TST_MSG_SLTM:
351 /* simply respond to the request... */
352 out = mtp_create_slta(link, mng, l3_len);
353 if (!out)
354 return -1;
355 mtp_link_submit(link, out);
356 return 0;
357 break;
358 case MTP_TST_MSG_SLTA:
359 if (mng->length != 14) {
360 LOGP(DINP, LOGL_ERROR, "Wrongly sized SLTA: %u\n", mng->length);
361 return -1;
362 }
363
364 if (l3_len != 16) {
365 LOGP(DINP, LOGL_ERROR, "Wrongly sized SLTA: %u\n", mng->length);
366 return -1;
367 }
368
369 if (memcmp(mng->data, link->test_ptrn, sizeof(link->test_ptrn)) != 0) {
370 LOGP(DINP, LOGL_ERROR, "Wrong test pattern SLTA\n");
371 return -1;
372 }
373
374 /* we had a matching slta */
375 bsc_del_timer(&link->t1_timer);
376 link->sltm_pending = 0;
377 mtp_link_slta_recv(link);
378 return 0;
379 break;
380 }
381 break;
382 }
383
384 return -1;
385}
386
387static int mtp_link_sccp_data(struct mtp_link *link, struct mtp_level_3_hdr *hdr, struct msgb *msg, int l3_len)
388{
389 struct msgb *out;
390 struct sccp_con_ctrl_prt_mgt *prt;
391
392 msg->l2h = &hdr->data[0];
393 if (msgb_l2len(msg) != l3_len) {
394 LOGP(DINP, LOGL_ERROR, "Size is wrong after playing with the l2h header.\n");
395 return -1;
396 }
397
398
399 if (link->sccp_up) {
400 mtp_link_forward_sccp(link, msg, MTP_LINK_SLS(hdr->addr));
401 return 0;
402 } else {
403 struct sccp_parse_result sccp;
404 memset(&sccp, 0, sizeof(sccp));
405 if (sccp_parse_header(msg, &sccp) != 0) {
406 LOGP(DINP, LOGL_ERROR, "Failed to parsed SCCP header.\n");
407 return -1;
408 }
409
410 if (sccp_determine_msg_type(msg) != SCCP_MSG_TYPE_UDT) {
411 LOGP(DINP, LOGL_ERROR, "Dropping sccp data: 0x%x\n",
412 sccp_determine_msg_type(msg));
413 return -1;
414 }
415
416 if (msgb_l3len(msg) != 5) {
417 LOGP(DINP, LOGL_ERROR, "SCCP UDT msg of unexpected size: %u\n",
418 msgb_l3len(msg));
419 return -1;
420 }
421
422 if (msg->l3h[0] != SCCP_SST) {
423 LOGP(DINP, LOGL_ERROR, "Expected SCCP SST but got 0x%x\n",
424 msg->l3h[0]);
425 return -1;
426 }
427
428 prt = (struct sccp_con_ctrl_prt_mgt *) &msg->l3h[0];
429 if (prt->assn != 254 || prt->apoc != MTP_MAKE_APOC(link->opc)) {
430 LOGP(DINP, LOGL_ERROR, "Unknown SSN/APOC assn: %u, apoc: %u/%u\n",
431 prt->assn, ntohs(prt->apoc), prt->apoc);
432 return -1;
433 }
434
435 out = mtp_sccp_alloc_ssa(link, MTP_LINK_SLS(hdr->addr));
436 if (!out)
437 return -1;
438
439 link->sccp_up = 1;
440 link->was_up = 1;
441 LOGP(DINP, LOGL_INFO, "SCCP is established. %p\n", link);
442 mtp_link_submit(link, out);
443 }
444 return 0;
445}
446
447int mtp_link_data(struct mtp_link *link, struct msgb *msg)
448{
449 int rc = -1;
450 struct mtp_level_3_hdr *hdr;
451 int l3_len;
452
453 if (!msg->l2h || msgb_l2len(msg) < sizeof(*hdr))
454 return -1;
455
456 if (!link->running) {
457 LOGP(DINP, LOGL_ERROR, "Link is not running. Call mtp_link_reset first: %p\n", link);
458 return -1;
459 }
460
461 hdr = (struct mtp_level_3_hdr *) msg->l2h;
462 l3_len = msgb_l2len(msg) - sizeof(*hdr);
463
464 switch (hdr->ser_ind) {
465 case MTP_SI_MNT_SNM_MSG:
466 rc = mtp_link_sign_msg(link, hdr, l3_len);
467 break;
468 case MTP_SI_MNT_REG_MSG:
469 rc = mtp_link_regular_msg(link, hdr, l3_len);
470 break;
471 case MTP_SI_MNT_SCCP:
472 rc = mtp_link_sccp_data(link, hdr, msg, l3_len);
473 break;
474 default:
475 fprintf(stderr, "Unhandled: %u\n", hdr->ser_ind);
476 break;
477 }
478
479 return rc;
480}
481
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +0800482int mtp_link_submit_sccp_data(struct mtp_link *link, int sls, const uint8_t *data, unsigned int length)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800483{
Holger Hans Peter Freyther5aa17012010-07-31 04:37:26 +0800484 uint8_t *put_ptr;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800485 struct mtp_level_3_hdr *hdr;
486 struct msgb *msg;
487
488 if (!link->sccp_up) {
489 LOGP(DINP, LOGL_ERROR, "SCCP msg after TRA and before SSA. Dropping it.\n");
490 return -1;
491 }
492
493 msg = mtp_msg_alloc(link);
494 if (!msg)
495 return -1;
496
497 hdr = (struct mtp_level_3_hdr *) msg->l2h;
498 hdr->ni = MTP_NI_NATION_NET;
499 hdr->ser_ind = MTP_SI_MNT_SCCP;
500
501 hdr->addr = MTP_ADDR(sls % 16, link->dpc, link->opc);
502
503 /* copy the raw sccp data */
504 put_ptr = msgb_put(msg, length);
505 memcpy(put_ptr, data, length);
506
507 mtp_link_submit(link, msg);
508 return 0;
509}