-
Notifications
You must be signed in to change notification settings - Fork 0
/
l4isup.c
5409 lines (4824 loc) · 165 KB
/
l4isup.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* l4isup.c - ISUP protocol
*
* Copyright (C) 2006-2011 Netfors ApS.
*
* Author: Anders Baekgaard <ab@netfors.com>
* Based on work by: Kristian Nielsen <kn@sifira.dk>,
*
* This file is part of chan_ss7.
*
* chan_ss7 is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* chan_ss7 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with chan_ss7; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <math.h>
#include "asterisk.h"
#include "asterisk/logger.h"
#include "asterisk/options.h"
#include "asterisk/channel.h"
#include "asterisk/frame.h"
#include "asterisk/utils.h"
#include "asterisk/sched.h"
#include "asterisk/cli.h"
#include "asterisk/lock.h"
#include "asterisk/causes.h"
#include "asterisk/pbx.h"
#include "asterisk/dsp.h"
#include "asterisk/callerid.h"
#include "asterisk/indications.h"
#include "asterisk/module.h"
#include "asterisk/alaw.h"
#include "asterisk/ulaw.h"
#ifdef USE_ZAPTEL
#include "zaptel.h"
#define DAHDI_DIALING ZT_DIALING
#define DAHDI_EVENT_DIALCOMPLETE ZT_EVENT_DIALCOMPLETE
#define DAHDI_GETGAINS ZT_GETGAINS
#define DAHDI_LAW_ALAW ZT_LAW_ALAW
#define DAHDI_LAW_MULAW ZT_LAW_MULAW
#define DAHDI_SETGAINS ZT_SETGAINS
#ifdef ZT_TONEDETECT
#define DAHDI_TONEDETECT ZT_TONEDETECT
#define DAHDI_TONEDETECT_ON ZT_TONEDETECT_ON
#define DAHDI_TONEDETECT_MUTE ZT_TONEDETECT_MUTE
#define DAHDI_EVENT_DTMFDOWN ZT_EVENT_DTMFDOWN
#define DAHDI_EVENT_DTMFUP ZT_EVENT_DTMFUP
#endif
#define dahdi_gains zt_gains
#else
#include <dahdi/user.h>
#endif
#ifndef DSP_FEATURE_DIGIT_DETECT
#define DSP_FEATURE_DIGIT_DETECT DSP_FEATURE_DTMF_DETECT
#endif
#include "astversion.h"
#include "config.h"
#include "lffifo.h"
#include "utils.h"
#include "mtp.h"
#include "transport.h"
#include "isup.h"
#include "l4isup.h"
#include "cluster.h"
#include "mtp3io.h"
#ifdef MODULETEST
#include "moduletest.h"
#endif
enum circuit_states {
/* Circuit idle, ready to accept or initiate calls. */
ST_IDLE,
/* An IAM has been received, but no ACM or CON has been sent back yet. */
ST_GOT_IAM,
/* An IAM has been sent to initiate a call, but no ACM or CON has been
received back yet. */
ST_SENT_IAM,
/* We have sent an ACM and have to send an ANM now */
ST_SENT_ACM,
/* We have sent IAM and received ACM, so waiting for ANM. */
ST_GOT_ACM,
/* A call is connected (incoming or outgoing). */
ST_CONNECTED,
/* A continuity check is ongoing */
ST_CONCHECK,
/* A REL message has been received, but RLC has not been sent
yet. ast_softhangup() has been called on the channel.*/
ST_GOT_REL,
/* A REL has been sent (from ss7_hangup), and so the struct ast_channel *
has been deallocated, but the circuit is still waiting for RLC to be
received before being able to initiate new calls. If timer T16 or T17
is running, this state instead means that a "circuit reset" has been
sent, and we are waiting for RLC. If a REL is received in this state,
send RLC and stay in this state, still waiting for RLC */
ST_SENT_REL,
};
struct ss7_chan {
/* The first few fields of this struct are protected by the global lock
mutex, not by the ss7_chan->lock mutex embedded in the struct. This is
necessary to preserve locking order and avoid deadlocks. */
struct ast_channel *owner;
struct ss7_chan *next_idle; /* Linked list of idle CICs */
struct link* link; /* Link carrying circuit */
int cic;
int reset_done; /* False until circuit has been init reset */
int hangupcause;
int dohangup;
int has_inband_ind;
int charge_indicator;
int is_digital;
block blocked;
/* Circuit equipped */
int equipped;
ast_mutex_t lock; /* Protects rest of this struct */
enum circuit_states state;
int zaptel_fd;
int t1;
int t2;
int t5;
int t6;
int t7;
int t9;
int t12;
int t14;
int t16;
int t17;
int t18;
int t19;
int t20;
int t21;
int t22;
int t23;
int t35;
int t36;
struct iam iam; /* Last incoming IAM parameters */
char* addr; /* called addr */
int attempts; /* Number of outgoing call attempts on addr */
int echocan_start;
int echocancel;
struct timeval lastread;
unsigned char buffer[AST_FRIENDLY_OFFSET + AUDIO_READSIZE];
struct ast_frame frame;
int sending_dtmf;
struct ast_dsp *dsp;
int grs_count; /* Count of CICs in ISUP GRS message */
int cgb_mask; /* Mask of CICs in ISUP CGB message */
int law;
char context[AST_MAX_CONTEXT];
char language[MAX_LANGUAGE];
};
/* Locking order (deadlock avoidance): global lock, chan->lock, pvt->lock
*/
/* Global list of idle CICs, sorted in order of "time free". Linked through
the ss7_chan->next_idle pointers. Protected by global lock. */
static struct timeval now;
static struct timeval mtp_fifo_full_report;
/* used by moduletest.c */
int isup_called_party_num_encode(struct ss7_chan *pvt, char *number, unsigned char *param, int plen);
int isup_called_party_num_encode_no_st(struct ss7_chan *pvt, char *number, unsigned char *param, int plen);
int isup_calling_party_num_encode(char *number, int pres_restr, int si, unsigned char *param, int plen);
static pthread_t continuity_check_thread = AST_PTHREADT_NULL;
static int continuity_check_thread_running = 0;
AST_MUTEX_DEFINE_STATIC(continuity_check_lock);
static int continuity_check_changes = 0;
static int must_stop_continuity_check_thread = 0;
static void isup_send_grs(struct ss7_chan *pvt, int count, int do_timers);
#if defined(USE_ASTERISK_1_2) || defined(USE_ASTERISK_1_4) || defined(USE_ASTERISK_1_6)
static struct ast_channel *ss7_requester(const char *type, int format, void *data, int *cause);
#elif defined(USE_ASTERISK_1_8)
static struct ast_channel *ss7_requester(const char *type, format_t format, const struct ast_channel *requestor, void *data, int *cause);
#else
static struct ast_channel *ss7_requester(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
#endif
static int ss7_send_digit_begin(struct ast_channel *chan, char digit);
static int ss7_send_digit_end(struct ast_channel *chan, char digit, unsigned int duration);
#if defined(USE_ASTERISK_1_2) || defined(USE_ASTERISK_1_4) || defined(USE_ASTERISK_1_6) || defined(USE_ASTERISK_1_8)
static int ss7_call(struct ast_channel *chan, char *addr, int timeout);
#else
static int ss7_call(struct ast_channel *chan, const char *addr, int timeout);
#endif
static int ss7_hangup(struct ast_channel *chan);
static int ss7_answer(struct ast_channel *chan);
static struct ast_frame *ss7_read(struct ast_channel * chan);
static int ss7_write(struct ast_channel * chan, struct ast_frame *frame);
static struct ast_frame *ss7_exception(struct ast_channel *chan);
static int ss7_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
#ifdef USE_ASTERISK_1_2
static int ss7_indicate(struct ast_channel *chan, int condition);
#else
static int ss7_indicate(struct ast_channel *chan, int condition, const void* data, size_t datalen);
#endif
static void t7_clear(struct ss7_chan *pvt);
static void t1_start(struct ss7_chan *pvt);
static void t5_start(struct ss7_chan *pvt);
static void t16_start(struct ss7_chan *pvt);
static void t16_clear(struct ss7_chan *pvt);
static void t17_start(struct ss7_chan *pvt);
static void t19_start(struct ss7_chan *pvt);
static void t21_start(struct ss7_chan *pvt);
static int do_group_circuit_block_unblock(struct linkset* linkset, int firstcic, unsigned long cgb_mask, int sup_type_ind, int own_cics_only, int do_timers, int do_block);
static struct ss7_chan* reattempt_call(struct ss7_chan *pvt);
static void *continuity_check_thread_main(void *data);
static void handle_complete_address(struct ss7_chan *pvt);
static const char type[] = "SS7";
static const char tdesc[] = "SS7 Protocol Driver";
static struct ast_channel_tech ss7_tech = {
.type = type,
.description = tdesc,
#if defined(USE_ASTERISK_1_2) || defined(USE_ASTERISK_1_4) || defined(USE_ASTERISK_1_6) || defined(USE_ASTERISK_1_8)
.capabilities = AST_FORMAT_ALAW | AST_FORMAT_ULAW,
#endif
.requester = ss7_requester,
#ifdef USE_ASTERISK_1_2
.send_digit = ss7_send_digit_begin,
#else
.send_digit_begin = ss7_send_digit_begin,
.send_digit_end = ss7_send_digit_end,
#endif
.call = ss7_call,
.hangup = ss7_hangup,
.answer = ss7_answer,
.read = ss7_read,
.write = ss7_write,
.exception = ss7_exception,
.fixup = ss7_fixup,
.indicate = ss7_indicate,
};
/* Send fifo for sending protocol requests to the MTP thread.
The fifo is lock-free (one thread may put and another get simultaneously),
but multiple threads doing put must be serialized with this mutex. */
AST_MUTEX_DEFINE_STATIC(mtp_send_mutex);
static struct lffifo **mtp_send_fifo;
#if defined(USE_ASTERISK_1_2) || defined(USE_ASTERISK_1_4) || defined(USE_ASTERISK_1_6) || defined(USE_ASTERISK_1_8)
#define ast_channel_tech_pvt(chan) chan->tech_pvt
#define ast_channel_tech_pvt_set(chan, pvt) chan->tech_pvt = pvt;
#endif
#ifdef USE_ASTERISK_1_2
#define ast_channel_lock(chan) ast_mutex_lock(&chan->lock)
#define ast_channel_unlock(chan) ast_mutex_unlock(&chan->lock)
#define ast_strdup(s) strdup(s)
#define ast_malloc(d) malloc(d)
#endif
#ifdef USE_ASTERISK_1_2
#define AST_FRAME_SET_BUFFER(fr, _base, _ofs, _datalen) \
{ \
(fr)->data = (char *)_base + (_ofs); \
(fr)->offset = (_ofs); \
(fr)->datalen = (_datalen); \
}
#define FRAME_DATA(fr,offset) (((unsigned char*) (fr)->data) + offset)
#else
#ifdef USE_ASTERISK_1_4
#define FRAME_DATA(fr,offset) (((unsigned char*) (fr)->data) + offset)
#else
#define FRAME_DATA(fr,offset) (((unsigned char*) (fr)->data.ptr) + offset)
#endif
#endif
static int usecnt = 0;
#ifdef USE_ASTERISK_1_2
AST_MUTEX_DEFINE_STATIC(usecnt_lock);
int usecount(void);
static void incr_usecount(void)
{
ast_mutex_lock(&usecnt_lock);
usecnt++;
ast_mutex_unlock(&usecnt_lock);
}
static void decr_usecount(void)
{
ast_mutex_lock(&usecnt_lock);
usecnt--;
if (usecnt < 0)
ast_log(LOG_WARNING, "Usecnt < 0???\n");
ast_mutex_unlock(&usecnt_lock);
}
int usecount(void)
{
int res;
ast_mutex_lock(&usecnt_lock);
res = usecnt;
ast_mutex_unlock(&usecnt_lock);
return res;
}
#else
static void incr_usecount(void)
{
ast_atomic_fetchadd_int(&usecnt, 1);
ast_update_use_count();
}
static void decr_usecount(void)
{
ast_atomic_fetchadd_int(&usecnt, -1);
ast_update_use_count();
if (usecnt < 0)
ast_log(LOG_WARNING, "Usecnt < 0???\n");
}
#endif
static int str2redirectreason(const char *str)
{
if (strcmp(str, "UNKNOWN") == 0)
return 0x00;
else if (strcmp(str, "BUSY") == 0)
return 0x01;
else if (strcmp(str, "NO_REPLY") == 0)
return 0x02;
else if (strcmp(str, "UNCONDITIONAL") == 0)
return 0x03;
else if (strcmp(str, "UNREACHABLE") == 0)
return 0x06;
else {
ast_log(LOG_NOTICE, "Invalid redirection reason value '%s' in PRIREDIRECTREASON variable.\n", str);
return 0x00;
}
}
static void request_hangup(struct ast_channel* chan, int hangupcause)
{
#if defined(USE_ASTERISK_1_2) || defined(USE_ASTERISK_1_4) || defined(USE_ASTERISK_1_6) || defined(USE_ASTERISK_1_8)
chan->hangupcause = hangupcause;
#else
ast_channel_hangupcause_set(chan, hangupcause);
#endif
ast_softhangup_nolock(chan, AST_SOFTHANGUP_DEV);
}
/* Lookup OPC for circuit */
static inline int peeropc(struct ss7_chan* pvt)
{
return pvt->link->linkset->opc;
}
/* Lookup DPC for circuit */
static inline int peerdpc(struct ss7_chan* pvt)
{
return pvt->link->linkset->dpc;
}
/* Lookup variant for circuit */
static inline ss7_variant variant(struct ss7_chan* pvt)
{
return pvt->link->linkset->variant;
}
static void mtp_enqueue_isup_packet(struct link* link, int cic, unsigned char *msg, int msglen, int reqtyp)
{
int res;
unsigned char req_buf[MTP_REQ_MAX_SIZE];
struct mtp_req *req = (struct mtp_req *)req_buf;
struct linkset* linkset = link->linkset;
struct link* slink = NULL;
int lsi = link->linkset->lsi;
if(sizeof(struct mtp_req) + msglen > sizeof(req_buf)) {
ast_log(LOG_ERROR, "Attempt to send oversized ISUP message of len "
"%d > %zu.\n", msglen, sizeof(req_buf) - sizeof(struct mtp_req));
return;
}
switch (link->linkset->loadshare) {
case LOADSHARE_NONE:
if (!link->schannel.mask)
slink = link;
break;
case LOADSHARE_LINKSET:
if (linkset->n_slinks)
slink = linkset->slinks[cic % linkset->n_slinks];
break;
case LOADSHARE_COMBINED_LINKSET:
{
int n_slinks = 0;
int six;
for (lsi = 0; lsi < n_linksets; lsi++)
if (linksets[lsi].enabled)
if (&linksets[lsi] == linkset ||
(is_combined_linkset(linkset, &linksets[lsi])))
n_slinks += linksets[lsi].n_slinks;
if (n_slinks) {
six = cic % n_slinks;
n_slinks = 0;
for (lsi = 0; lsi < n_linksets; lsi++)
if (linksets[lsi].enabled)
if (&linksets[lsi] == linkset ||
(is_combined_linkset(linkset, &linksets[lsi]))) {
if (six - n_slinks < linksets[lsi].n_slinks) {
slink = linksets[lsi].slinks[six - n_slinks];
if (slink && (*slink->mtp3server_host) && (slink->mtp3fd == -1))
slink = NULL;
else
break;
}
if (!slink) {
int i;
for (i = 0; (i < linksets[lsi].n_slinks) && !slink; i++) {
slink = linksets[lsi].slinks[i];
if (slink && (*slink->mtp3server_host) && (slink->mtp3fd == -1)) {
slink = NULL;
}
}
}
n_slinks += linksets[lsi].n_slinks;
}
}
}
break;
}
if (slink)
lsi = slink->linkset->lsi;
else
lsi = linkset->lsi;
memset(req, 0, sizeof(*req));
req->typ = reqtyp;
req->isup.slink = slink;
req->isup.link = link;
req->isup.slinkix = slink ? slink->linkix : 0;
req->len = msglen;
memcpy(req->buf, msg, msglen);
if(slink && slink->mtp3fd > -1) {
res = mtp3_send(slink->mtp3fd, (unsigned char *)req, sizeof(struct mtp_req) + req->len);
if (res < 0) {
ast_log(LOG_DEBUG, "closing connection to mtp3d, fd %d, res: %d, err: %s\n", slink->mtp3fd, res, strerror(errno));
close(slink->mtp3fd);
slink->mtp3fd = -1;
}
{
struct isup_msg isup_msg;
res = decode_isup_msg(&isup_msg, slink->linkset->variant, msg, msglen);
ast_log(LOG_DEBUG, "Sent to mtp3d: %s (CIC %d), link '%s'\n", isupmsg(isup_msg.typ), cic, slink->name);
}
return;
}
ast_mutex_lock(&mtp_send_mutex);
if (!mtp_send_fifo || !mtp_send_fifo[lsi]) {
if (cluster_receivers_alive(linkset)) {
ast_log(LOG_DEBUG, "MTP send fifo not ready, forwarding to cluster, lsi=%d.\n", lsi);
cluster_mtp_forward(req);
}
else
ast_log(LOG_WARNING, "MTP send fifo not ready, lsi=%d.\n", lsi);
ast_mutex_unlock(&mtp_send_mutex);
return;
}
ast_log(LOG_DEBUG, "Queue packet CIC=%d, len=%d, linkset='%s', link='%s', slinkset='%s', slink='%s'\n", cic, msglen, linkset->name, link->name, linksets[lsi].name, slink ? slink->name : "(none)");
res = lffifo_put(mtp_send_fifo[lsi], (unsigned char *)req, sizeof(struct mtp_req) + req->len);
ast_mutex_unlock(&mtp_send_mutex);
if(res != 0) {
gettimeofday(&now, NULL);
if (timediff_msec(now, mtp_fifo_full_report) > 30000) {
ast_log(LOG_WARNING, "MTP send fifo full (MTP thread blocked?).\n");
gettimeofday(&mtp_fifo_full_report, NULL);
}
}
}
static void mtp_enqueue_isup(struct ss7_chan* pvt, unsigned char *msg, int msglen)
{
mtp_enqueue_isup_packet(pvt->link, pvt->cic, msg, msglen, MTP_REQ_ISUP);
}
static void mtp_enqueue_isup_forward(struct ss7_chan* pvt, unsigned char *msg, int msglen)
{
mtp_enqueue_isup_packet(pvt->link, pvt->cic, msg, msglen, MTP_REQ_ISUP_FORWARD);
}
/* Deprecated, use find_pvt_with_pc */
__attribute__((__deprecated__))
static struct ss7_chan* find_pvt(struct link* slink, int cic)
{
struct linkset* ls;
int lsi;
ls = slink->linkset;
if (ls->cic_list[cic])
return ls->cic_list[cic];
for (lsi = 0; lsi < n_linksets; lsi++)
if (is_combined_linkset(ls, &linksets[lsi]))
if (linksets[lsi].cic_list[cic])
return linksets[lsi].cic_list[cic];
return NULL;
}
static struct ss7_chan* find_pvt_with_pc(struct link* slink, int cic, int pc)
{
struct linkset* ls;
int lsi;
ls = slink->linkset;
if (ls->dpc == pc) {
if (ls->cic_list[cic])
return ls->cic_list[cic];
for (lsi = 0; lsi < n_linksets; lsi++)
if (is_combined_linkset(ls, &linksets[lsi]))
if (linksets[lsi].cic_list[cic])
return linksets[lsi].cic_list[cic];
} else {
for (lsi = 0; lsi < n_linksets; lsi++)
if (is_combined_linkset(ls, &linksets[lsi]))
if (linksets[lsi].dpc == pc)
if (linksets[lsi].cic_list[cic])
return linksets[lsi].cic_list[cic];
}
return NULL;
}
/* This function must be called with the global lock mutex held. */
static void remove_from_idlelist(struct ss7_chan *pvt) {
struct linkset* linkset = pvt->link->linkset;
struct ss7_chan *prev, *cur;
cur = linkset->group_linkset->idle_list;
prev = NULL;
while(cur != NULL) {
if(pvt->cic == cur->cic) {
if(prev == NULL) {
linkset->group_linkset->idle_list = pvt->next_idle;
} else {
prev->next_idle = pvt->next_idle;
}
pvt->next_idle = NULL;
return;
}
prev = cur;
cur = cur->next_idle;
}
ast_log(LOG_NOTICE, "Trying to remove CIC=%d from idle list, but not found?!?.\n", pvt->cic);
}
/* This function must be called with the global lock mutex held. */
static void add_to_idlelist(struct ss7_chan *pvt) {
struct linkset* linkset = pvt->link->linkset;
struct ss7_chan *cur;
cur = linkset->group_linkset->idle_list;
while(cur != NULL) {
if(pvt->cic == cur->cic) {
ast_log(LOG_NOTICE, "Trying to add CIC=%d to idle list, but already there?!?\n", pvt->cic);
return;
}
cur = cur->next_idle;
}
pvt->next_idle = linkset->group_linkset->idle_list;
linkset->group_linkset->idle_list = pvt;
}
/* This implements hunting policy. It must be called with the global lock mutex
held. */
/* This implements the policy: Primary hunting group odd CICs, secondary
hunting group even CICs. Choose least recently used CIC. */
static struct ss7_chan *cic_hunt_odd_lru(struct linkset* linkset, int first_cic, int last_cic) {
struct ss7_chan *cur, *prev, *best, *best_prev;
int odd;
best = NULL;
best_prev = NULL;
for(odd = 1; odd >= 0; odd--) {
for(cur = linkset->group_linkset->idle_list, prev = NULL; cur != NULL; prev = cur, cur = cur->next_idle) {
/* Don't select lines that are resetting or blocked. */
if(!cur->reset_done || (cur->blocked & (BL_LH|BL_RM|BL_RH|BL_UNEQUIPPED|BL_LINKDOWN|BL_NOUSE))) {
continue;
}
/* is this cic within the selected range? */
if(cur->cic < first_cic || cur->cic > last_cic) {
continue;
}
if((cur->cic % 2) == odd) {
best = cur;
best_prev = prev;
}
}
if(best != NULL) {
if(best_prev == NULL) {
linkset->group_linkset->idle_list = best->next_idle;
} else {
best_prev->next_idle = best->next_idle;
}
best->next_idle = NULL;
return best;
}
}
ast_log(LOG_WARNING, "No idle circuit found, linkset=%s.\n", linkset->name);
return NULL;
}
/* This implements the policy: Primary hunting group even CICs, secondary
hunting group odd CICs. Choose most recently used CIC. */
static struct ss7_chan *cic_hunt_even_mru(struct linkset* linkset, int first_cic, int last_cic) {
struct ss7_chan *cur, *prev, *best, *best_prev;
best = NULL;
best_prev = NULL;
for(cur = linkset->group_linkset->idle_list, prev = NULL; cur != NULL; prev = cur, cur = cur->next_idle) {
/* Don't select lines that are resetting or blocked. */
if(!cur->reset_done || (cur->blocked & (BL_LH|BL_RM|BL_RH|BL_UNEQUIPPED|BL_LINKDOWN|BL_NOUSE))) {
continue;
}
/* is this cic within the selected range? */
if(cur->cic < first_cic || cur->cic > last_cic) {
continue;
}
if((cur->cic % 2) == 0) {
/* Choose the first idle even circuit, if any. */
best = cur;
best_prev = prev;
break;
} else if(best == NULL) {
/* Remember the first odd circuit, in case no even circuits are
available. */
best = cur;
best_prev = prev;
}
}
if(best != NULL) {
if(best_prev == NULL) {
linkset->group_linkset->idle_list = best->next_idle;
} else {
best_prev->next_idle = best->next_idle;
}
best->next_idle = NULL;
return best;
} else {
ast_log(LOG_WARNING, "No idle circuit found, linkset=%s.\n", linkset->name);
return NULL;
}
}
/* This implements the policy: Sequential low to high CICs */
static struct ss7_chan *cic_hunt_seq_lth_htl(struct linkset* linkset, int lth, int first_cic, int last_cic)
{
struct ss7_chan *cur, *prev, *best = NULL, *best_prev = NULL;
for(cur = linkset->group_linkset->idle_list, prev = NULL; cur != NULL; prev = cur, cur = cur->next_idle) {
/* Don't select lines that are resetting or blocked. */
if(!cur->reset_done || (cur->blocked & (BL_LH|BL_RM|BL_RH|BL_UNEQUIPPED|BL_LINKDOWN|BL_NOUSE))) {
continue;
}
/* is this cic within the selected range? */
if(cur->cic < first_cic || cur->cic > last_cic) {
continue;
}
if (!best) {
best = cur;
continue;
}
if (lth) {
if (cur->cic < best->cic) {
best = cur;
best_prev = prev;
}
}
else {
if (cur->cic > best->cic) {
best = cur;
best_prev = prev;
}
}
}
if(best != NULL) {
if(best_prev == NULL) {
linkset->group_linkset->idle_list = best->next_idle;
} else {
best_prev->next_idle = best->next_idle;
}
best->next_idle = NULL;
return best;
} else {
ast_log(LOG_WARNING, "No idle circuit found, linkset=%s.\n", linkset->name);
return NULL;
}
}
static void handle_redir_info(struct ast_channel *chan, struct isup_redir_info* inf)
{
if(inf->is_redirect) {
char *string_reason;
char count[4];
char redir[4];
int res;
snprintf(redir, sizeof(redir), "%d", inf->is_redirect);
/* The names here are taken to match with those used in chan_zap.c
redirectingreason2str(). */
switch(inf->reason) {
case 1:
string_reason = "BUSY";
break;
case 2:
/* Cause 4 "deflection during alerting"; not sure, but it seems to be
more or less equivalent to "no reply".*/
case 4:
string_reason = "NO_REPLY";
break;
case 3:
/* Cause 5 "deflection immediate response"; not sure, but it seems to
be more or less equivalent to "unconditional".*/
case 5:
string_reason = "UNCONDITIONAL";
break;
case 6:
string_reason = "UNREACHABLE";
break;
default:
string_reason = "UNKNOWN";
break;
}
/* Set SS7_REDIRECTCOUNT */
res = snprintf(count, sizeof(count), "%d",inf->count + 1);
pbx_builtin_setvar_helper(chan, "__SS7_REDIRECTCOUNT", (res > 0) ? count : "1");
/* Use underscore variable to make it inherit like other callerid info. */
pbx_builtin_setvar_helper(chan, "__PRIREDIRECTREASON", string_reason);
pbx_builtin_setvar_helper(chan, "SS7_REDIR", redir);
}
}
/* Send a "release" message. */
static void isup_send_rel(struct ss7_chan *pvt, int cause) {
struct ast_channel *chan = pvt->owner;
char* redir = chan ? (char*) pbx_builtin_getvar_helper(chan, "SS7_REDIR") : NULL;
char* rdni = chan ? (char*) pbx_builtin_getvar_helper(chan, "SS7_RDNI") : NULL;
unsigned char msg[MTP_MAX_PCK_SIZE];
int current, varptr;
unsigned char param[2];
const char *strp;
isup_msg_init(msg, sizeof(msg), variant(pvt), peeropc(pvt), peerdpc(pvt), pvt->cic, ISUP_REL, ¤t);
isup_msg_start_variable_part(msg, sizeof(msg), &varptr, ¤t, 1, 1);
param[0] = 0x85; /* Last octet, ITU-T coding, private network */
param[1] = 0x80 | (cause & 0x7f); /* Last octet */
isup_msg_add_variable(msg, sizeof(msg), &varptr, ¤t, param, 2);
isup_msg_start_optional_part(msg, sizeof(msg), &varptr, ¤t);
if (redir) {
unsigned char param_redir[2];
unsigned char reason = 0x03, rcount = 1;
param_redir[0] = atoi(redir);
strp = chan ? (char*) pbx_builtin_getvar_helper(chan, "PRIREDIRECTREASON") : NULL;
if (strp)
reason = str2redirectreason(strp);
/* Read SS7_REDIRECTCOUNT and use it to set redirection counter (see ITU-T Q.763 3.44) */
strp = pbx_builtin_getvar_helper(chan, "SS7_REDIRECTCOUNT");
if (strp) {
char *endptr;
unsigned long val = strtoul(strp, &endptr, 0);
if ((strp == endptr) || val < 0 || val > 7)
ast_log(LOG_NOTICE, "Invalid redirection count value '%ld' "
"in SS7_REDRECTCOUNT variable.\n", val);
else
rcount = val;
}
param_redir[1] = ((reason & 0x0F) << 4) | (rcount & 0x07); /* redirecting reason, counter */
isup_msg_add_optional(msg, sizeof(msg), ¤t, IP_REDIRECTION_INFORMATION,
param_redir, 2);
}
if (rdni && *rdni) {
unsigned char param_rdni[2 + PHONENUM_MAX];
int res = isup_calling_party_num_encode(rdni, 0 /* no pres_restr */, 0 /* national use: user provided, not verified */, param_rdni, sizeof(param_rdni));
isup_msg_add_optional(msg, sizeof(msg), ¤t, IP_REDIRECTION_NUMBER, param_rdni, res);
}
isup_msg_end_optional_part(msg, sizeof(msg), ¤t);
mtp_enqueue_isup(pvt, msg, current);
}
/* Send a "release confirmed" message. */
static void isup_send_rlc(struct ss7_chan* pvt) {
unsigned char msg[MTP_MAX_PCK_SIZE];
int current, varptr;
int cic = pvt->cic;
isup_msg_init(msg, sizeof(msg), variant(pvt), peeropc(pvt), peerdpc(pvt), cic, ISUP_RLC, ¤t);
if (variant(pvt) != ANSI_SS7) {
isup_msg_start_variable_part(msg, sizeof(msg), &varptr, ¤t, 0, 1);
isup_msg_start_optional_part(msg, sizeof(msg), &varptr, ¤t);
isup_msg_end_optional_part(msg, sizeof(msg), ¤t);
}
mtp_enqueue_isup(pvt, msg, current);
}
/* Send a "reset circuit" message. */
static void isup_send_rsc(struct ss7_chan* pvt) {
unsigned char msg[MTP_MAX_PCK_SIZE];
int current, varptr;
int cic = pvt->cic;
isup_msg_init(msg, sizeof(msg), variant(pvt), peeropc(pvt), peerdpc(pvt), cic, ISUP_RSC, ¤t);
isup_msg_start_variable_part(msg, sizeof(msg), &varptr, ¤t, 0, 0);
mtp_enqueue_isup(pvt, msg, current);
}
/* Send an "address complete" message. */
static void isup_send_acm(struct ss7_chan* pvt) {
unsigned char msg[MTP_MAX_PCK_SIZE];
int current, varptr;
unsigned char param[2];
int cic = pvt->cic;
isup_msg_init(msg, sizeof(msg), variant(pvt), peeropc(pvt), peerdpc(pvt), cic, ISUP_ACM, ¤t);
param[0] = 0x12;
param[1] = 0x14;
isup_msg_add_fixed(msg, sizeof(msg), ¤t, param, 2);
if (pvt->has_inband_ind) {
unsigned char param_opt_backw_ind[1];
param_opt_backw_ind[0] = 0x01;
isup_msg_start_variable_part(msg, sizeof(msg), &varptr, ¤t, 0, 1);
isup_msg_start_optional_part(msg, sizeof(msg), &varptr, ¤t);
isup_msg_add_optional(msg, sizeof(msg), ¤t, IP_OPTIONAL_BACKWARD_CALL_INDICATORS,
param_opt_backw_ind, 1);
isup_msg_end_optional_part(msg, sizeof(msg), ¤t);
}
else {
isup_msg_start_variable_part(msg, sizeof(msg), &varptr, ¤t, 0, 1);
}
mtp_enqueue_isup(pvt, msg, current);
}
/* Send a "circuit group blocking" message. */
static void isup_send_cgb(struct ss7_chan* pvt, int mask) {
int sup_type_ind = 0x00; /* Maintenance oriented supervision message type */
int cic = pvt->cic;
if (pvt->equipped)
sup_type_ind = 0x00; /* Maintenance oriented supervision message type */
else
sup_type_ind = 0x01; /* Hardware failure oriented */
do_group_circuit_block_unblock(pvt->link->linkset, cic, mask, sup_type_ind, 0, 0, 1);
}
/* Send a "circuit group unblocking" message. */
static void isup_send_cgu(struct ss7_chan* pvt, int mask) {
int sup_type_ind = 0x00; /* Maintenance oriented supervision message type */
int cic = pvt->cic;
if (pvt->equipped)
sup_type_ind = 0x00; /* Maintenance oriented supervision message type */
else
sup_type_ind = 0x01; /* Hardware failure oriented */
do_group_circuit_block_unblock(pvt->link->linkset, cic, mask, sup_type_ind, 0, 0, 0);
}
/* Send a "blocked" message. */
static void isup_send_blk(struct ss7_chan *pvt)
{
unsigned char msg[MTP_MAX_PCK_SIZE];
int current, varptr;
isup_msg_init(msg, sizeof(msg), variant(pvt), peeropc(pvt), peerdpc(pvt), pvt->cic, ISUP_BLK, ¤t);
isup_msg_start_variable_part(msg, sizeof(msg), &varptr, ¤t, 0, 0);
mtp_enqueue_isup(pvt, msg, current);
}
/* Send an "unblocked" message. */
static void isup_send_ubl(struct ss7_chan *pvt)
{
unsigned char msg[MTP_MAX_PCK_SIZE];
int current, varptr;
isup_msg_init(msg, sizeof(msg), variant(pvt), peeropc(pvt), peerdpc(pvt), pvt->cic, ISUP_UBL, ¤t);
isup_msg_start_variable_part(msg, sizeof(msg), &varptr, ¤t, 0, 0);
mtp_enqueue_isup(pvt, msg, current);
}
/* Reset circuit. Called with pvt->lock held */
static void reset_circuit(struct ss7_chan* pvt)
{
isup_send_rsc(pvt);
t16_start(pvt);
}
/* Initiate release circuit. Called with pvt->lock held */
static void initiate_release_circuit(struct ss7_chan* pvt, int cause)
{
pvt->hangupcause = cause; /* Remember for REL retransmit */
/* We sometimes get hangupcause=0 (seen when no match in dialplan, not
even invalid handler). This doesn't work too well, for example
ast_softhangup() doesn't actually hang up when hangupcause=0. */
if(pvt->hangupcause == 0) {
pvt->hangupcause = AST_CAUSE_NORMAL_CLEARING;
}
isup_send_rel(pvt, pvt->hangupcause);
pvt->state = ST_SENT_REL;
/* Set up timer T1 and T5 waiting for RLC. */
t1_start(pvt);
t5_start(pvt);
}
/* Setup a new channel, for an incoming or an outgoing call.
Assumes called with global lock and pvt->lock held. */
#if defined(USE_ASTERISK_1_2) || defined(USE_ASTERISK_1_4) || defined(USE_ASTERISK_1_6) || defined(USE_ASTERISK_1_8)
static struct ast_channel *ss7_new(struct ss7_chan *pvt, int state, char* cid_num, char* exten)
#else
static struct ast_channel *ss7_new(struct ss7_chan *pvt, int state, char* cid_num, const char* exten)
#endif
{
struct ast_channel *chan;
#if defined(USE_ASTERISK_1_2) || defined(USE_ASTERISK_1_4) || defined(USE_ASTERISK_1_6) || defined(USE_ASTERISK_1_8)
int format;
#else
struct ast_format format;
#endif
#ifdef USE_ASTERISK_1_2
chan = ast_channel_alloc(1);
if(!chan) {
return NULL;
}
snprintf(chan->name, sizeof(chan->name), "%s/%s/%d", type, pvt->link->linkset->name, pvt->cic);
chan->type = type;
#else
#if defined(USE_ASTERISK_1_4) || defined(USE_ASTERISK_1_6)
chan = ast_channel_alloc(1, state, cid_num, NULL, NULL, exten, pvt->context, 0, "%s/%s/%d", type, pvt->link->linkset->name, pvt->cic);
#else
chan = ast_channel_alloc(1, state, cid_num, NULL, NULL, exten, pvt->context, NULL, 0, "%s/%s/%d", type, pvt->link->linkset->name, pvt->cic);
#endif
ast_jb_configure(chan, ss7_get_global_jbconf());
if(!chan) {
return NULL;
}
#endif
#if defined(USE_ASTERISK_1_2) || defined(USE_ASTERISK_1_4) || defined(USE_ASTERISK_1_6) || defined(USE_ASTERISK_1_8)
chan->tech = &ss7_tech;
if (variant(pvt) == ANSI_SS7)
format = AST_FORMAT_ULAW;
else
format = AST_FORMAT_ALAW;
chan->nativeformats = format;
chan->rawreadformat = format;
chan->rawwriteformat = format;
chan->readformat = format;
chan->writeformat = format;
ast_setstate(chan, state);
chan->fds[0] = pvt->zaptel_fd;
#else
ast_channel_tech_set(chan, &ss7_tech);
if (variant(pvt) == ANSI_SS7)
ast_format_set(&format, AST_FORMAT_ULAW, 0);