root/openbsc/src/sccp/sccp.c @ 62c87b5237ef9e859e582201fa518db5d861df0d

Revision 62c87b5237ef9e859e582201fa518db5d861df0d, 34.8 kB (checked in by Holger Hans Peter Freyther <zecke@selfish.org>, 7 months ago)

Merge branch 'on-waves/sccp'

  • Property mode set to 100644
Line 
1/*
2 * SCCP management code
3 *
4 * (C) 2009, 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * (C) 2009, 2010 by On-Waves
6 *
7 * All Rights Reserved
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 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 General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 *
23 */
24
25#include <string.h>
26
27#include <openbsc/msgb.h>
28#include <openbsc/debug.h>
29#include <openbsc/talloc.h>
30
31#include <sccp/sccp.h>
32
33
34static void *tall_sccp_ctx;
35static LLIST_HEAD(sccp_connections);
36
37#define SCCP_MSG_SIZE 4096
38#define SCCP_MSG_HEADROOM 128
39
40/* global data */
41const struct sockaddr_sccp sccp_ssn_bssap = {
42        .sccp_family    = 0,
43        .sccp_ssn       = SCCP_SSN_BSSAP,
44};
45
46struct sccp_system {
47        /* layer3 -> layer2 */
48        int (*write_data)(struct msgb *data, void *context);
49        void *write_context;
50};
51
52
53static struct sccp_system sccp_system = {
54        .write_data = NULL,
55};
56
57struct sccp_data_callback {
58        /* connection based */
59        int (*accept_cb)(struct sccp_connection *, void *);
60        void *accept_context;
61
62        /* connection less */
63        int (*read_cb)(struct msgb *, unsigned int, void *);
64        void *read_context;
65
66        u_int8_t ssn;
67        struct llist_head callback;
68};
69
70static LLIST_HEAD(sccp_callbacks);
71
72static struct sccp_data_callback *_find_ssn(u_int8_t ssn)
73{
74        struct sccp_data_callback *cb;
75
76        llist_for_each_entry(cb, &sccp_callbacks, callback) {
77                if (cb->ssn == ssn)
78                        return cb;
79        }
80
81        /* need to add one */
82        cb = talloc_zero(tall_sccp_ctx, struct sccp_data_callback);
83        if (!cb) {
84                DEBUGP(DSCCP, "Failed to allocate sccp callback.\n");
85                return NULL;
86        }
87
88        cb->ssn = ssn;
89        llist_add_tail(&cb->callback, &sccp_callbacks);
90        return cb;
91}
92
93
94static int _send_msg(struct msgb *msg)
95{
96        return sccp_system.write_data(msg, sccp_system.write_context);
97}
98
99/*
100 * parsing routines
101 */
102static int copy_address(struct sccp_address *addr, u_int8_t offset, struct msgb *msgb)
103{
104        struct sccp_called_party_address *party;
105
106        int room = msgb_l2len(msgb) - offset;
107        u_int8_t read = 0;
108        u_int8_t length;
109
110        if (room <= 0) {
111                DEBUGP(DSCCP, "Not enough room for an address: %u\n", room);
112                return -1;
113        }
114
115        length = msgb->l2h[offset];
116        if (room <= length) {
117                DEBUGP(DSCCP, "Not enough room for optional data %u %u\n", room, length);
118                return -1;
119        }
120
121
122        party = (struct sccp_called_party_address *)(msgb->l2h + offset + 1);
123        if (party->point_code_indicator) {
124                if (length <= read + 2) {
125                    DEBUGP(DSCCP, "POI does not fit %u\n", length);
126                    return -1;
127                }
128
129
130                memcpy(&addr->poi, &party->data[read], 2);
131                read += 2;
132        }
133
134        if (party->ssn_indicator) {
135                if (length <= read + 1) {
136                    DEBUGP(DSCCP, "SSN does not fit %u\n", length);
137                    return -1;
138                }
139
140                addr->ssn = party->data[read];
141                read += 1;
142        }
143
144        if (party->global_title_indicator) {
145                DEBUGP(DSCCP, "GTI not supported %u\n", *(u_int8_t *)party);
146                return -1;
147        }
148
149        addr->address = *party;
150        return 0;
151}
152
153static int check_address(struct sccp_address *addr)
154{
155        /* ignore point_code_indicator... it should be zero... but */
156        if (addr->address.ssn_indicator != 1
157            || addr->address.global_title_indicator == 1
158            || addr->address.routing_indicator != 1) {
159                DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
160                        *(u_int8_t *)&addr->address, addr->ssn);
161                return -1;
162        }
163
164        return 0;
165}
166
167static int _sccp_parse_optional_data(const int offset,
168                                     struct msgb *msgb, struct sccp_optional_data *data)
169{
170        u_int16_t room = msgb_l2len(msgb) - offset;
171        u_int16_t read = 0;
172
173        while (room > read) {
174                u_int8_t type = msgb->l2h[offset + read];
175                if (type == SCCP_PNC_END_OF_OPTIONAL)
176                        return 0;
177
178                if (read + 1 >= room) {
179                        DEBUGP(DSCCP, "no place for length\n");
180                        return 0;
181                }
182
183                u_int8_t length = msgb->l2h[offset + read + 1];
184                read += 2 + length;
185
186
187                if (room <= read) {
188                        DEBUGP(DSCCP, "no space for the data: type: %d read: %d room: %d l2: %d\n",
189                               type, read, room, msgb_l2len(msgb));
190                        return 0;
191                }
192
193                if (type == SCCP_PNC_DATA) {
194                        data->data_len = length;
195                        data->data_start = offset + read - length;
196                }
197
198        }
199
200        return -1;
201}
202
203int _sccp_parse_connection_request(struct msgb *msgb, struct sccp_parse_result *result)
204{
205        static const u_int32_t header_size =
206                        sizeof(struct sccp_connection_request);
207        static const u_int32_t optional_offset =
208                        offsetof(struct sccp_connection_request, optional_start);
209        static const u_int32_t called_offset =
210                        offsetof(struct sccp_connection_request, variable_called);
211
212        struct sccp_connection_request *req = (struct sccp_connection_request *)msgb->data;
213        struct sccp_optional_data optional_data;
214
215        /* header check */
216        if (msgb_l2len(msgb) < header_size) {
217                DEBUGP(DSCCP, "msgb < header_size %u %u\n",
218                        msgb_l2len(msgb), header_size);
219                return -1;
220        }
221
222        /* copy out the calling and called address. Add the offset */
223        if (copy_address(&result->called, called_offset + req->variable_called, msgb) != 0)
224                return -1;
225
226        if (check_address(&result->called) != 0) {
227                DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
228                        *(u_int8_t *)&result->called.address, result->called.ssn);
229                return -1;
230        }
231
232        result->source_local_reference = &req->source_local_reference;
233
234        /*
235         * parse optional data.
236         */
237        memset(&optional_data, 0, sizeof(optional_data));
238        if (_sccp_parse_optional_data(optional_offset + req->optional_start, msgb, &optional_data) != 0) {
239                DEBUGP(DSCCP, "parsing of optional data failed.\n");
240                return -1;
241        }
242
243        if (optional_data.data_len != 0) {
244                msgb->l3h = &msgb->l2h[optional_data.data_start];
245                result->data_len = optional_data.data_len;
246        } else {
247                result->data_len = 0;
248        }
249
250        return 0;
251}
252
253int _sccp_parse_connection_released(struct msgb *msgb, struct sccp_parse_result *result)
254{
255        static int header_size = sizeof(struct sccp_connection_released);
256        static int optional_offset = offsetof(struct sccp_connection_released, optional_start);
257
258        struct sccp_optional_data optional_data;
259        struct sccp_connection_released *rls = (struct sccp_connection_released *) msgb->l2h;
260
261        /* we don't have enough size for the struct */
262        if (msgb_l2len(msgb) < header_size) {
263                DEBUGP(DSCCP, "msgb > header_size %u %u\n",
264                        msgb_l2len(msgb), header_size);
265                return -1;
266        }
267
268        memset(&optional_data, 0, sizeof(optional_data));
269        if (_sccp_parse_optional_data(optional_offset + rls->optional_start, msgb, &optional_data) != 0) {
270                DEBUGP(DSCCP, "parsing of optional data failed.\n");
271                return -1;
272        }
273
274        result->source_local_reference = &rls->source_local_reference;
275        result->destination_local_reference = &rls->destination_local_reference;
276
277        if (optional_data.data_len != 0) {
278                msgb->l3h = &msgb->l2h[optional_data.data_start];
279                result->data_len = optional_data.data_len;
280        } else {
281                result->data_len = 0;
282        }
283
284        return 0;
285}
286
287int _sccp_parse_connection_refused(struct msgb *msgb, struct sccp_parse_result *result)
288{
289        static const u_int32_t header_size =
290                        sizeof(struct sccp_connection_refused);
291        static int optional_offset = offsetof(struct sccp_connection_refused, optional_start);
292
293        struct sccp_optional_data optional_data;
294        struct sccp_connection_refused *ref;
295
296        /* header check */
297        if (msgb_l2len(msgb) < header_size) {
298                DEBUGP(DSCCP, "msgb < header_size %u %u\n",
299                        msgb_l2len(msgb), header_size);
300                return -1;
301        }
302
303        ref = (struct sccp_connection_refused *) msgb->l2h;
304
305        result->destination_local_reference = &ref->destination_local_reference;
306
307        memset(&optional_data, 0, sizeof(optional_data));
308        if (_sccp_parse_optional_data(optional_offset + ref->optional_start, msgb, &optional_data) != 0) {
309                DEBUGP(DSCCP, "parsing of optional data failed.\n");
310                return -1;
311        }
312
313        /* optional data */
314        if (optional_data.data_len != 0) {
315                msgb->l3h = &msgb->l2h[optional_data.data_start];
316                result->data_len = optional_data.data_len;
317        } else {
318                result->data_len = 0;
319        }
320
321        return 0;
322}
323
324int _sccp_parse_connection_confirm(struct msgb *msgb, struct sccp_parse_result *result)
325{
326        static u_int32_t header_size =
327                    sizeof(struct sccp_connection_confirm);
328        static const u_int32_t optional_offset =
329                        offsetof(struct sccp_connection_confirm, optional_start);
330
331        struct sccp_optional_data optional_data;
332        struct sccp_connection_confirm *con;
333
334        /* header check */
335        if (msgb_l2len(msgb) < header_size) {
336                DEBUGP(DSCCP, "msgb < header_size %u %u\n",
337                        msgb_l2len(msgb), header_size);
338                return -1;
339        }
340
341        con = (struct sccp_connection_confirm *) msgb->l2h;
342        result->destination_local_reference = &con->destination_local_reference;
343        result->source_local_reference = &con->source_local_reference;
344
345        memset(&optional_data, 0, sizeof(optional_data));
346        if (_sccp_parse_optional_data(optional_offset + con->optional_start, msgb, &optional_data) != 0) {
347                DEBUGP(DSCCP, "parsing of optional data failed.\n");
348                return -1;
349        }
350
351        if (optional_data.data_len != 0) {
352                msgb->l3h = &msgb->l2h[optional_data.data_start];
353                result->data_len = optional_data.data_len;
354        } else {
355                result->data_len = 0;
356        }
357
358        return 0;
359}
360
361int _sccp_parse_connection_release_complete(struct msgb *msgb, struct sccp_parse_result *result)
362{
363        static int header_size = sizeof(struct sccp_connection_release_complete);
364
365        struct sccp_connection_release_complete *cmpl;
366
367        /* header check */
368        if (msgb_l2len(msgb) < header_size) {
369                DEBUGP(DSCCP, "msgb < header_size %u %u\n",
370                        msgb_l2len(msgb), header_size);
371                return -1;
372        }
373
374        cmpl = (struct sccp_connection_release_complete *) msgb->l2h;
375        result->source_local_reference = &cmpl->source_local_reference;
376        result->destination_local_reference = &cmpl->destination_local_reference;
377
378        return 0;
379}
380
381int _sccp_parse_connection_dt1(struct msgb *msgb, struct sccp_parse_result *result)
382{
383        static int header_size = sizeof(struct sccp_data_form1);
384        static int variable_offset = offsetof(struct sccp_data_form1, variable_start);
385
386        struct sccp_data_form1 *dt1 = (struct sccp_data_form1 *)msgb->l2h;
387
388        /* we don't have enough size for the struct */
389        if (msgb_l2len(msgb) < header_size) {
390                DEBUGP(DSCCP, "msgb > header_size %u %u\n",
391                        msgb_l2len(msgb), header_size);
392                return -1;
393        }
394
395        if (dt1->segmenting != 0) {
396                DEBUGP(DSCCP, "This packet has segmenting, not supported: %d\n", dt1->segmenting);
397                return -1;
398        }
399
400        result->destination_local_reference = &dt1->destination_local_reference;
401
402        /* some more  size checks in here */
403        if (msgb_l2len(msgb) < variable_offset + dt1->variable_start + 1) {
404                DEBUGP(DSCCP, "Not enough space for variable start: %u %u\n",
405                        msgb_l2len(msgb), dt1->variable_start);
406                return -1;
407        }
408
409        result->data_len = msgb->l2h[variable_offset + dt1->variable_start];
410        msgb->l3h = &msgb->l2h[dt1->variable_start + variable_offset + 1];
411
412        if (msgb_l3len(msgb) < result->data_len) {
413                DEBUGP(DSCCP, "Not enough room for the payload: %u %u\n",
414                        msgb_l3len(msgb), result->data_len);
415                return -1;
416        }
417
418        return 0;
419}
420
421int _sccp_parse_udt(struct msgb *msgb, struct sccp_parse_result *result)
422{
423        static const u_int32_t header_size = sizeof(struct sccp_data_unitdata);
424        static const u_int32_t called_offset = offsetof(struct sccp_data_unitdata, variable_called);
425        static const u_int32_t calling_offset = offsetof(struct sccp_data_unitdata, variable_calling);
426        static const u_int32_t data_offset = offsetof(struct sccp_data_unitdata, variable_data);
427
428        struct sccp_data_unitdata *udt = (struct sccp_data_unitdata *)msgb->l2h;
429
430        if (msgb_l2len(msgb) < header_size) {
431                DEBUGP(DSCCP, "msgb < header_size %u %u\n",
432                        msgb_l2len(msgb), header_size);
433                return -1;
434        }
435
436        /* copy out the calling and called address. Add the off */
437        if (copy_address(&result->called, called_offset + udt->variable_called, msgb) != 0)
438                return -1;
439
440        if (check_address(&result->called) != 0) {
441                DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
442                        *(u_int8_t *)&result->called.address, result->called.ssn);
443                return -1;
444        }
445
446        if (copy_address(&result->calling, calling_offset + udt->variable_calling, msgb) != 0)
447                return -1;
448
449        if (check_address(&result->calling) != 0) {
450                DEBUGP(DSCCP, "Invalid called address according to 08.06: 0x%x 0x%x\n",
451                        *(u_int8_t *)&result->called.address, result->called.ssn);
452        }
453
454        /* we don't have enough size for the data */
455        if (msgb_l2len(msgb) < data_offset + udt->variable_data + 1) {
456                DEBUGP(DSCCP, "msgb < header + offset %u %u %u\n",
457                        msgb_l2len(msgb), header_size, udt->variable_data);
458                return -1;
459        }
460
461
462        msgb->l3h = &udt->data[udt->variable_data];
463        result->data_len = msgb_l3len(msgb);
464
465        if (msgb_l3len(msgb) !=  msgb->l3h[-1]) {
466                DEBUGP(DSCCP, "msgb is truncated is: %u should: %u\n",
467                        msgb_l3len(msgb), msgb->l3h[-1]);
468                return -1;
469        }
470
471        return 0;
472}
473
474
475/*
476 * Send UDT. Currently we have a fixed address...
477 */
478static int _sccp_send_data(int class, const struct sockaddr_sccp *in,
479                           const struct sockaddr_sccp *out, struct msgb *payload)
480{
481        struct sccp_data_unitdata *udt;
482        u_int8_t *data;
483        int ret;
484
485        if (msgb_l3len(payload) > 256) {
486                DEBUGP(DSCCP, "The payload is too big for one udt\n");
487                return -1;
488        }
489
490        struct msgb *msg = msgb_alloc_headroom(SCCP_MSG_SIZE,
491                                               SCCP_MSG_HEADROOM, "sccp: udt");
492        msg->l2h = &msg->data[0];
493        udt = (struct sccp_data_unitdata *)msgb_put(msg, sizeof(*udt));
494
495        udt->type = SCCP_MSG_TYPE_UDT;
496        udt->proto_class = class;
497        udt->variable_called = 3;
498        udt->variable_calling = 5;
499        udt->variable_data = 7;
500
501        /* for variable data we start with a size and the data */
502        data = msgb_put(msg, 1 + 2);
503        data[0] = 2;
504        data[1] = 0x42;
505        data[2] = out->sccp_ssn;
506
507        data = msgb_put(msg, 1 + 2);
508        data[0] = 2;
509        data[1] = 0x42;
510        data[2] = in->sccp_ssn;
511
512        /* copy the payload */
513        data = msgb_put(msg, 1 + msgb_l3len(payload));
514        data[0] = msgb_l3len(payload);
515        memcpy(&data[1], payload->l3h, msgb_l3len(payload));
516
517        ret = _send_msg(msg);
518        msgb_free(msg);
519
520        return ret;
521}
522
523static int _sccp_handle_read(struct msgb *msgb)
524{
525        struct sccp_data_callback *cb;
526        struct sccp_parse_result result;
527
528        if (_sccp_parse_udt(msgb, &result) != 0)
529                return -1;
530
531        cb = _find_ssn(result.called.ssn);
532        if (!cb || !cb->read_cb) {
533                DEBUGP(DSCCP, "No routing for UDT for called SSN: %u\n", result.called.ssn);
534                return -1;
535        }
536
537        /* sanity check */
538        return cb->read_cb(msgb, msgb_l3len(msgb), cb->read_context);
539}
540
541/*
542 * handle connection orientated methods
543 */
544static int source_local_reference_is_free(struct sccp_source_reference *reference)
545{
546        struct sccp_connection *connection;
547
548        llist_for_each_entry(connection, &sccp_connections, list) {
549                if (memcmp(reference, &connection->source_local_reference, sizeof(*reference)) == 0)
550                        return -1;
551        }
552
553        return 0;
554}
555
556static int destination_local_reference_is_free(struct sccp_source_reference *reference)
557{
558        struct sccp_connection *connection;
559
560        llist_for_each_entry(connection, &sccp_connections, list) {
561                if (memcmp(reference, &connection->destination_local_reference, sizeof(*reference)) == 0)
562                        return -1;
563        }
564
565        return 0;
566}
567
568static int assign_source_local_reference(struct sccp_connection *connection)
569{
570        static u_int32_t last_ref = 0x30000;
571        int wrapped = 0;
572
573        do {
574                struct sccp_source_reference reference;
575                reference.octet1 = (last_ref >>  0) & 0xff;
576                reference.octet2 = (last_ref >>  8) & 0xff;
577                reference.octet3 = (last_ref >> 16) & 0xff;
578
579                ++last_ref;
580                /* do not use the reversed word and wrap around */
581                if ((last_ref & 0x00FFFFFF) == 0x00FFFFFF) {
582                        DEBUGP(DSCCP, "Wrapped searching for a free code\n");
583                        last_ref = 0;
584                        ++wrapped;
585                }
586
587                if (source_local_reference_is_free(&reference) == 0) {
588                        connection->source_local_reference = reference;
589                        return 0;
590                }
591        } while (wrapped != 2);
592
593        DEBUGP(DSCCP, "Finding a free reference failed\n");
594        return -1;
595}
596
597static void _sccp_set_connection_state(struct sccp_connection *connection, int new_state)
598{
599        int old_state = connection->connection_state;
600
601        connection->connection_state = new_state;
602        if (connection->state_cb)
603                connection->state_cb(connection, old_state);
604}
605
606static int _sccp_send_refuse(struct sccp_source_reference *src_ref, int cause)
607{
608        struct msgb *msgb;
609        struct sccp_connection_refused *ref;
610        u_int8_t *data;
611        int ret;
612
613        msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
614                                   SCCP_MSG_HEADROOM, "sccp ref");
615        msgb->l2h = &msgb->data[0];
616
617        ref = (struct sccp_connection_refused *) msgb_put(msgb, sizeof(*ref));
618        ref->type = SCCP_MSG_TYPE_CREF;
619        memcpy(&ref->destination_local_reference, src_ref,
620               sizeof(struct sccp_source_reference));
621        ref->cause = cause;
622        ref->optional_start = 1;
623
624        data = msgb_put(msgb, 1);
625        data[0] = SCCP_PNC_END_OF_OPTIONAL;
626
627        ret = _send_msg(msgb);
628        msgb_free(msgb);
629        return ret;
630}
631
632static int _sccp_send_connection_confirm(struct sccp_connection *connection)
633{
634        struct msgb *response;
635        struct sccp_connection_confirm *confirm;
636        u_int8_t *optional_data;
637        int ret;
638
639        if (assign_source_local_reference(connection) != 0)
640                return -1;
641
642        response = msgb_alloc_headroom(SCCP_MSG_SIZE,
643                                       SCCP_MSG_HEADROOM, "sccp confirm");
644        response->l2h = &response->data[0];
645
646        confirm = (struct sccp_connection_confirm *) msgb_put(response, sizeof(*confirm));
647
648        confirm->type = SCCP_MSG_TYPE_CC;
649        memcpy(&confirm->destination_local_reference,
650               &connection->destination_local_reference,
651               sizeof(connection->destination_local_reference));
652        memcpy(&confirm->source_local_reference,
653               &connection->source_local_reference,
654               sizeof(connection->source_local_reference));
655        confirm->proto_class = 2;
656        confirm->optional_start = 1;
657
658        optional_data = (u_int8_t *) msgb_put(response, 1);
659        optional_data[0] = SCCP_PNC_END_OF_OPTIONAL;
660
661        ret = _send_msg(response);
662        msgb_free(response);
663
664        _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_ESTABLISHED);
665        return ret;
666}
667
668static int _sccp_send_connection_request(struct sccp_connection *connection,
669                                         const struct sockaddr_sccp *called, struct msgb *msg)
670{
671        struct msgb *request;
672        struct sccp_connection_request *req;
673        u_int8_t *data;
674        u_int8_t extra_size = 3 + 1;
675        int ret;
676
677
678        if (msg && (msgb_l3len(msg) < 3 || msgb_l3len(msg) > 130)) {
679                DEBUGP(DSCCP, "Invalid amount of data... %d\n", msgb_l3len(msg));
680                return -1;
681        }
682
683        /* try to find a id */
684        if (assign_source_local_reference(connection) != 0) {
685                DEBUGP(DSCCP, "Assigning a local reference failed.\n");
686                _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_SETUP_ERROR);
687                return -1;
688        }
689
690
691        if (msg)
692                extra_size += 2 + msgb_l3len(msg);
693        request = msgb_alloc_headroom(SCCP_MSG_SIZE,
694                                      SCCP_MSG_HEADROOM, "sccp connection request");
695        request->l2h = &request->data[0];
696        req = (struct sccp_connection_request *) msgb_put(request, sizeof(*req));
697
698        req->type = SCCP_MSG_TYPE_CR;
699        memcpy(&req->source_local_reference, &connection->source_local_reference,
700               sizeof(connection->source_local_reference));
701        req->proto_class = 2;
702        req->variable_called = 2;
703        req->optional_start = 4;
704
705        /* write the called party address */
706        data = msgb_put(request, 1 + 2);
707        data[0] = 2;
708        data[1] = 0x42;
709        data[2] = called->sccp_ssn;
710
711        /* write the payload */
712        if (msg) {
713            data = msgb_put(request, 2 + msgb_l3len(msg));
714            data[0] = SCCP_PNC_DATA;
715            data[1] = msgb_l3len(msg);
716            memcpy(&data[2], msg->l3h, msgb_l3len(msg));
717        }
718
719        data = msgb_put(request, 1);
720        data[0] = SCCP_PNC_END_OF_OPTIONAL;
721
722        llist_add_tail(&connection->list, &sccp_connections);
723        _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REQUEST);
724
725        ret = _send_msg(request);
726        msgb_free(request);
727
728        return ret;
729}
730
731static int _sccp_send_connection_data(struct sccp_connection *conn, struct msgb *_data)
732{
733        struct msgb *msgb;
734        struct sccp_data_form1 *dt1;
735        u_int8_t *data;
736        int extra_size;
737        int ret;
738
739        if (msgb_l3len(_data) < 2 || msgb_l3len(_data) > 256) {
740                DEBUGP(DSCCP, "data size too big, segmenting unimplemented.\n");
741                return -1;
742        }
743
744        extra_size = 1 + msgb_l3len(_data);
745        msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
746                                   SCCP_MSG_HEADROOM, "sccp dt1");
747        msgb->l2h = &msgb->data[0];
748
749        dt1 = (struct sccp_data_form1 *) msgb_put(msgb, sizeof(*dt1));
750        dt1->type = SCCP_MSG_TYPE_DT1;
751        memcpy(&dt1->destination_local_reference, &conn->destination_local_reference,
752               sizeof(struct sccp_source_reference));
753        dt1->segmenting = 0;
754
755        /* copy the data */
756        dt1->variable_start = 1;
757        data = msgb_put(msgb, extra_size);
758        data[0] = extra_size - 1;
759        memcpy(&data[1], _data->l3h, extra_size - 1);
760
761        ret = _send_msg(msgb);
762        msgb_free(msgb);
763
764        return ret;
765}
766
767static int _sccp_send_connection_it(struct sccp_connection *conn)
768{
769        struct msgb *msgb;
770        struct sccp_data_it *it;
771        int ret;
772
773        msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
774                                   SCCP_MSG_HEADROOM, "sccp it");
775        msgb->l2h = &msgb->data[0];
776        it = (struct sccp_data_it *) msgb_put(msgb, sizeof(*it));
777        it->type = SCCP_MSG_TYPE_IT;
778        memcpy(&it->destination_local_reference, &conn->destination_local_reference,
779                sizeof(struct sccp_source_reference));
780        memcpy(&it->source_local_reference, &conn->source_local_reference,
781                sizeof(struct sccp_source_reference));
782
783        it->proto_class = 0x2;
784        it->sequencing[0] = it->sequencing[1] = 0;
785        it->credit = 0;
786
787        ret = _send_msg(msgb);
788        msgb_free(msgb);
789        return ret;
790}
791
792static int _sccp_send_connection_released(struct sccp_connection *conn, int cause)
793{
794        struct msgb *msg;
795        struct sccp_connection_released *rel;
796        u_int8_t *data;
797        int ret;
798
799        msg = msgb_alloc_headroom(SCCP_MSG_SIZE, SCCP_MSG_HEADROOM,
800                                  "sccp: connection released");
801        msg->l2h = &msg->data[0];
802        rel = (struct sccp_connection_released *) msgb_put(msg, sizeof(*rel));
803        rel->type = SCCP_MSG_TYPE_RLSD;
804        rel->release_cause = cause;
805
806        /* copy the source references */
807        memcpy(&rel->destination_local_reference, &conn->destination_local_reference,
808               sizeof(struct sccp_source_reference));
809        memcpy(&rel->source_local_reference, &conn->source_local_reference,
810               sizeof(struct sccp_source_reference));
811
812        data = msgb_put(msg, 1);
813        data[0] = SCCP_PNC_END_OF_OPTIONAL;
814
815        _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_RELEASE);
816        ret = _send_msg(msg);
817        msgb_free(msg);
818
819        return ret;
820}
821
822/*
823 * Open a connection. The following is going to happen:
824 *
825 *      - Verify the packet, e.g. that we have no other connection
826 *        that id.
827 *      - Ask the user if he wants to accept the connection
828 *      - Try to open the connection by assigning a source local reference
829 *        and sending the packet
830 */
831static int _sccp_handle_connection_request(struct msgb *msgb)
832{
833        struct sccp_parse_result result;
834
835        struct sccp_data_callback *cb;
836        struct sccp_connection *connection;
837
838        if (_sccp_parse_connection_request(msgb, &result) != 0)
839                return -1;
840
841        cb = _find_ssn(result.called.ssn);
842        if (!cb || !cb->accept_cb) {
843                DEBUGP(DSCCP, "No routing for CR for called SSN: %u\n", result.called.ssn);
844                return -1;
845        }
846
847        /* check if the system wants this connection */
848        connection = talloc_zero(tall_sccp_ctx, struct sccp_connection);
849        if (!connection) {
850                DEBUGP(DSCCP, "Allocation failed\n");
851                return -1;
852        }
853
854        /*
855         * sanity checks:
856         *      - Is the source_local_reference in any other connection?
857         * then will call accept, assign a "destination" local reference
858         * and send a connection confirm, otherwise we will send a refuseed
859         * one....
860         */
861        if (destination_local_reference_is_free(result.source_local_reference) != 0) {
862                DEBUGP(DSCCP, "Need to reject connection with existing reference\n");
863                _sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE);
864                talloc_free(connection);
865                return -1;
866        }
867
868        connection->incoming = 1;
869        connection->destination_local_reference = *result.source_local_reference;
870
871        if (cb->accept_cb(connection, cb->accept_context) != 0) {
872                _sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_END_USER_ORIGINATED);
873                _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
874                talloc_free(connection);
875                return 0;
876        }
877
878
879        llist_add_tail(&connection->list, &sccp_connections);
880
881        if (_sccp_send_connection_confirm(connection) != 0) {
882                DEBUGP(DSCCP, "Sending confirm failed... no available source reference?\n");
883
884                _sccp_send_refuse(result.source_local_reference, SCCP_REFUSAL_SCCP_FAILURE);
885                _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_REFUSED);
886                llist_del(&connection->list);
887                talloc_free(connection);
888
889                return -1;
890        }
891
892        /*
893         * If we have data let us forward things.
894         */
895        if (result.data_len != 0 && connection->data_cb) {
896                connection->data_cb(connection, msgb, result.data_len);
897        }
898
899        return 0;
900}
901
902/* Handle the release confirmed */
903static int _sccp_handle_connection_release_complete(struct msgb *msgb)
904{
905        struct sccp_parse_result result;
906        struct sccp_connection *conn;
907
908        if (_sccp_parse_connection_release_complete(msgb, &result) != 0)
909                return -1;
910
911        /* find the connection */
912        llist_for_each_entry(conn, &sccp_connections, list) {
913                if (conn->data_cb
914                    && memcmp(&conn->source_local_reference,
915                              result.destination_local_reference,
916                              sizeof(conn->source_local_reference)) == 0
917                    && memcmp(&conn->destination_local_reference,
918                              result.source_local_reference,
919                              sizeof(conn->destination_local_reference)) == 0) {
920                    goto found;
921                }
922        }
923
924
925        DEBUGP(DSCCP, "Release complete of unknown connection\n");
926        return -1;
927
928found:
929        llist_del(&conn->list);
930        _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_RELEASE_COMPLETE);
931        return 0;
932}
933
934/* Handle the Data Form 1 message */
935static int _sccp_handle_connection_dt1(struct msgb *msgb)
936{
937        struct sccp_parse_result result;
938        struct sccp_connection *conn;
939
940        if (_sccp_parse_connection_dt1(msgb, &result) != 0)
941                return -1;
942
943        /* lookup if we have a connection with the given reference */
944        llist_for_each_entry(conn, &sccp_connections, list) {
945                if (conn->data_cb
946                    && memcmp(&conn->source_local_reference,
947                              result.destination_local_reference,
948                              sizeof(conn->source_local_reference)) == 0) {
949                        goto found;
950                }
951        }
952
953        DEBUGP(DSCCP, "No connection found for dt1 data\n");
954        return -1;
955
956found:
957        conn->data_cb(conn, msgb, result.data_len);
958        return 0;
959}
960
961/* confirm a connection release */
962static int _sccp_send_connection_release_complete(struct sccp_connection *connection)
963{
964        struct msgb *msgb;
965        struct sccp_connection_release_complete *rlc;
966        int ret;
967
968        msgb = msgb_alloc_headroom(SCCP_MSG_SIZE,
969                                   SCCP_MSG_HEADROOM, "sccp rlc");
970        msgb->l2h = &msgb->data[0];
971
972        rlc = (struct sccp_connection_release_complete *) msgb_put(msgb, sizeof(*rlc));
973        rlc->type = SCCP_MSG_TYPE_RLC;
974        memcpy(&rlc->destination_local_reference,
975               &connection->destination_local_reference, sizeof(struct sccp_source_reference));
976        memcpy(&rlc->source_local_reference,
977               &connection->source_local_reference, sizeof(struct sccp_source_reference));
978
979        ret = _send_msg(msgb);
980        msgb_free(msgb);
981
982        /*
983         * Remove from the list of active connections and set the state. User code
984         * should now free the entry.
985         */
986        llist_del(&connection->list);
987        _sccp_set_connection_state(connection, SCCP_CONNECTION_STATE_RELEASE_COMPLETE);
988
989        return ret;
990}
991
992/* connection released, send a released confirm */
993static int _sccp_handle_connection_released(struct msgb *msgb)
994{
995        struct sccp_parse_result result;
996        struct sccp_connection *conn;
997
998        if (_sccp_parse_connection_released(msgb, &result) == -1)
999                return -1;
1000
1001        /* lookup if we have a connection with the given reference */
1002        llist_for_each_entry(conn, &sccp_connections, list) {
1003                if (conn->data_cb
1004                    && memcmp(&conn->source_local_reference,
1005                              result.destination_local_reference,
1006                              sizeof(conn->source_local_reference)) == 0
1007                    && memcmp(&conn->destination_local_reference,
1008                              result.source_local_reference,
1009                              sizeof(conn->destination_local_reference)) == 0) {
1010                    goto found;
1011                }
1012        }
1013
1014
1015        DEBUGP(DSCCP, "Unknown connection was released.\n");
1016        return -1;
1017
1018        /* we have found a connection */
1019found:
1020        /* optional data */
1021        if (result.data_len != 0 && conn->data_cb) {
1022                conn->data_cb(conn, msgb, result.data_len);
1023        }
1024
1025        /* generate a response */
1026        if (_sccp_send_connection_release_complete(conn) != 0) {
1027                DEBUGP(DSCCP, "Sending release confirmed failed\n");
1028                return -1;
1029        }
1030
1031        return 0;
1032}
1033
1034static int _sccp_handle_connection_refused(struct msgb *msgb)
1035{
1036        struct sccp_parse_result result;
1037        struct sccp_connection *conn;
1038
1039        if (_sccp_parse_connection_refused(msgb, &result) != 0)
1040                return -1;
1041
1042        /* lookup if we have a connection with the given reference */
1043        llist_for_each_entry(conn, &sccp_connections, list) {
1044                if (conn->incoming == 0 && conn->data_cb
1045                    && memcmp(&conn->source_local_reference,
1046                              result.destination_local_reference,
1047                              sizeof(conn->source_local_reference)) == 0) {
1048                    goto found;
1049                }
1050        }
1051
1052        DEBUGP(DSCCP, "Refused but no connection found\n");
1053        return -1;
1054
1055found:
1056        /* optional data */
1057        if (result.data_len != 0 && conn->data_cb) {
1058                conn->data_cb(conn, msgb, result.data_len);
1059        }
1060
1061
1062        llist_del(&conn->list);
1063        _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_REFUSED);
1064        return 0;
1065}
1066
1067static int _sccp_handle_connection_confirm(struct msgb *msgb)
1068{
1069        struct sccp_parse_result result;
1070        struct sccp_connection *conn;
1071
1072        if (_sccp_parse_connection_confirm(msgb, &result) != 0)
1073                return -1;
1074
1075        /* lookup if we have a connection with the given reference */
1076        llist_for_each_entry(conn, &sccp_connections, list) {
1077                if (conn->incoming == 0 && conn->data_cb
1078                    && memcmp(&conn->source_local_reference,
1079                              result.destination_local_reference,
1080                              sizeof(conn->source_local_reference)) == 0) {
1081                    goto found;
1082                }
1083        }
1084
1085        DEBUGP(DSCCP, "Confirmed but no connection found\n");
1086        return -1;
1087
1088found:
1089        /* copy the addresses of the connection */
1090        conn->destination_local_reference = *result.source_local_reference;
1091        _sccp_set_connection_state(conn, SCCP_CONNECTION_STATE_ESTABLISHED);
1092
1093        /* optional data */
1094        if (result.data_len != 0 && conn->data_cb) {
1095                conn->data_cb(conn, msgb, result.data_len);
1096        }
1097
1098        return 0;
1099}
1100
1101
1102int sccp_system_init(int (*outgoing)(struct msgb *data, void *ctx), void *ctx)
1103{
1104        sccp_system.write_data = outgoing;
1105        sccp_system.write_context = ctx;
1106
1107        return 0;
1108}
1109
1110/* oh my god a real SCCP packet. need to dispatch it now */
1111int sccp_system_incoming(struct msgb *msgb)
1112{
1113        if (msgb_l2len(msgb) < 1 ) {
1114                DEBUGP(DSCCP, "Too short packet\n");
1115                return -1;
1116        }
1117
1118        int type = msgb->l2h[0];
1119
1120        switch(type) {
1121        case SCCP_MSG_TYPE_CR:
1122                return _sccp_handle_connection_request(msgb);
1123                break;
1124        case SCCP_MSG_TYPE_RLSD:
1125                return _sccp_handle_connection_released(msgb);
1126                break;
1127        case SCCP_MSG_TYPE_CREF:
1128                return _sccp_handle_connection_refused(msgb);
1129                break;
1130        case SCCP_MSG_TYPE_CC:
1131                return _sccp_handle_connection_confirm(msgb);
1132                break;
1133        case SCCP_MSG_TYPE_RLC:
1134                return _sccp_handle_connection_release_complete(msgb);
1135                break;
1136        case SCCP_MSG_TYPE_DT1:
1137                return _sccp_handle_connection_dt1(msgb);
1138                break;
1139        case SCCP_MSG_TYPE_UDT:
1140                return _sccp_handle_read(msgb);
1141                break;
1142        default:
1143                DEBUGP(DSCCP, "unimplemented msg type: %d\n", type);
1144        };
1145
1146        return -1;
1147}
1148
1149/* create a packet from the data */
1150int sccp_connection_write(struct sccp_connection *connection, struct msgb *data)
1151{
1152        if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
1153            || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
1154                DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n",
1155                       connection, connection->connection_state);
1156                return -1;
1157        }
1158
1159        return _sccp_send_connection_data(connection, data);
1160}
1161
1162/*
1163 * Send a Inactivity Test message. The owner of the connection
1164 * should start a timer and call this method regularily. Calling
1165 * this every 60 seconds should be good enough.
1166 */
1167int sccp_connection_send_it(struct sccp_connection *connection)
1168{
1169        if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
1170            || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
1171                DEBUGP(DSCCP, "sccp_connection_write: Wrong connection state: %p %d\n",
1172                       connection, connection->connection_state);
1173                return -1;
1174        }
1175
1176        return _sccp_send_connection_it(connection);
1177}
1178
1179/* send a connection release and wait for the connection released */
1180int sccp_connection_close(struct sccp_connection *connection, int cause)
1181{
1182        if (connection->connection_state < SCCP_CONNECTION_STATE_CONFIRM
1183            || connection->connection_state > SCCP_CONNECTION_STATE_ESTABLISHED) {
1184                DEBUGPC(DSCCP, "Can not close the connection. It was never opened: %p %d\n",
1185                        connection, connection->connection_state);
1186                return -1;
1187        }
1188
1189        return _sccp_send_connection_released(connection, cause);
1190}
1191
1192int sccp_connection_free(struct sccp_connection *connection)
1193{
1194        if (connection->connection_state > SCCP_CONNECTION_STATE_NONE
1195            && connection->connection_state < SCCP_CONNECTION_STATE_RELEASE_COMPLETE) {
1196                DEBUGP(DSCCP, "The connection needs to be released before it is freed");
1197                return -1;
1198        }
1199
1200        talloc_free(connection);
1201        return 0;
1202}
1203
1204struct sccp_connection *sccp_connection_socket(void)
1205{
1206        return talloc_zero(tall_sccp_ctx, struct sccp_connection);
1207}
1208
1209int sccp_connection_connect(struct sccp_connection *conn,
1210                            const struct sockaddr_sccp *local,
1211                            struct msgb *data)
1212{
1213        return _sccp_send_connection_request(conn, local, data);
1214}
1215
1216int sccp_connection_set_incoming(const struct sockaddr_sccp *sock,
1217                                 int (*accept_cb)(struct sccp_connection *, void *), void *context)
1218{
1219        struct sccp_data_callback *cb;
1220
1221        if (!sock)
1222                return -2;
1223
1224        cb = _find_ssn(sock->sccp_ssn);
1225        if (!cb)
1226                return -1;
1227
1228        cb->accept_cb = accept_cb;
1229        cb->accept_context = context;
1230        return 0;
1231}
1232
1233int sccp_write(struct msgb *data, const struct sockaddr_sccp *in,
1234               const struct sockaddr_sccp *out, int class)
1235{
1236        return _sccp_send_data(class, in, out, data);
1237}
1238
1239int sccp_set_read(const struct sockaddr_sccp *sock,
1240                  int (*read_cb)(struct msgb *, unsigned int, void *), void *context)
1241{
1242        struct sccp_data_callback *cb;
1243
1244        if (!sock)
1245                return -2;
1246
1247        cb  = _find_ssn(sock->sccp_ssn);
1248        if (!cb)
1249                return -1;
1250
1251        cb->read_cb = read_cb;
1252        cb->read_context = context;
1253        return 0;
1254}
1255
1256static_assert(sizeof(struct sccp_source_reference) <= sizeof(u_int32_t), enough_space);
1257
1258u_int32_t sccp_src_ref_to_int(struct sccp_source_reference *ref)
1259{
1260        u_int32_t src_ref = 0;
1261        memcpy(&src_ref, ref, sizeof(*ref));
1262        return src_ref;
1263}
1264
1265struct sccp_source_reference sccp_src_ref_from_int(u_int32_t int_ref)
1266{
1267        struct sccp_source_reference ref;
1268        memcpy(&ref, &int_ref, sizeof(ref));
1269        return ref;
1270}
1271
1272int sccp_determine_msg_type(struct msgb *msg)
1273{
1274        if (msgb_l2len(msg) < 1)
1275                return -1;
1276
1277        return msg->l2h[0];
1278}
1279
1280int sccp_parse_header(struct msgb *msg, struct sccp_parse_result *result)
1281{
1282        int type;
1283
1284        if (msgb_l2len(msg) < 1)
1285                return -1;
1286
1287        type = msg->l2h[0];
1288        switch(type) {
1289        case SCCP_MSG_TYPE_CR:
1290                return _sccp_parse_connection_request(msg, result);
1291                break;
1292        case SCCP_MSG_TYPE_RLSD:
1293                return _sccp_parse_connection_released(msg, result);
1294                break;
1295        case SCCP_MSG_TYPE_CREF:
1296                return _sccp_parse_connection_refused(msg, result);
1297                break;
1298        case SCCP_MSG_TYPE_CC:
1299                return _sccp_parse_connection_confirm(msg, result);
1300                break;
1301        case SCCP_MSG_TYPE_RLC:
1302                return _sccp_parse_connection_release_complete(msg, result);
1303                break;
1304        case SCCP_MSG_TYPE_DT1:
1305                return _sccp_parse_connection_dt1(msg, result);
1306                break;
1307        case SCCP_MSG_TYPE_UDT:
1308                return _sccp_parse_udt(msg, result);
1309                break;
1310        };
1311
1312        return -1;
1313}
1314
1315static __attribute__((constructor)) void on_dso_load(void)
1316{
1317        tall_sccp_ctx = talloc_named_const(NULL, 1, "sccp");
1318}
1319
1320static __attribute__((destructor)) void on_dso_unload(void)
1321{
1322        talloc_report_full(tall_sccp_ctx, stderr);
1323}
Note: See TracBrowser for help on using the browser.