Resolve another warning.
[asterisk/asterisk.git] / res / res_pktccops.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2009, Attila Domjan
5  *
6  * Attila Domjan <attila.domjan.hu@gmail.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!\file
20  *
21  * \brief PacketCable COPS
22  * 
23  * \author Attila Domjan <attila.domjan.hu@gmail.com>
24  */
25
26 #include "asterisk.h"
27
28 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
29
30 #include <sys/types.h>
31 #include <sys/socket.h>
32 #include <fcntl.h>
33 #include <netdb.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <unistd.h>
37 #include <string.h>
38 #include <time.h>
39 #include <errno.h>
40 #include <arpa/inet.h>
41 #include <signal.h>
42
43 #include "asterisk/file.h"
44 #include "asterisk/logger.h"
45 #include "asterisk/channel.h"
46 #include "asterisk/config.h"
47 #include "asterisk/options.h"
48 #include "asterisk/pbx.h"
49 #include "asterisk/module.h"
50 #include "asterisk/cli.h"
51 #include "asterisk/lock.h"
52 #define AST_API_MODULE
53 #include "asterisk/pktccops.h"
54
55 #define DEFAULT_COPS_PORT "2126"
56
57 #define COPS_HEADER_SIZE 8
58 #define COPS_OBJECT_HEADER_SIZE 4
59 #define GATE_SET_OBJ_SIZE 144
60 #define GATEID_OBJ_SIZE 8
61 #define GATE_INFO_OBJ_SIZE 24
62
63 #define PKTCCOPS_SCOMMAND_GATE_ALLOC 1
64 #define PKTCCOPS_SCOMMAND_GATE_ALLOC_ACK 2
65 #define PKTCCOPS_SCOMMAND_GATE_ALLOC_ERR 3
66 #define PKTCCOPS_SCOMMAND_GATE_SET 4
67 #define PKTCCOPS_SCOMMAND_GATE_SET_ACK 5
68 #define PKTCCOPS_SCOMMAND_GATE_SET_ERR 6
69 #define PKTCCOPS_SCOMMAND_GATE_INFO 7
70 #define PKTCCOPS_SCOMMAND_GATE_INFO_ACK 8
71 #define PKTCCOPS_SCOMMAND_GATE_INFO_ERR 9
72 #define PKTCCOPS_SCOMMAND_GATE_DELETE 10
73 #define PKTCCOPS_SCOMMAND_GATE_DELETE_ACK 11
74 #define PKTCCOPS_SCOMMAND_GATE_DELETE_ERR 12
75 #define PKTCCOPS_SCOMMAND_GATE_OPEN 13
76 #define PKTCCOPS_SCOMMAND_GATE_CLOSE 14
77
78 AST_MUTEX_DEFINE_STATIC(pktccops_lock);
79 static pthread_t pktccops_thread = AST_PTHREADT_NULL;
80 static uint16_t cops_trid = 0;
81
82 struct pktcobj {
83         uint16_t length;
84         unsigned char cnum;
85         unsigned char ctype;
86         char *contents;
87         struct pktcobj *next; 
88 };
89
90 struct copsmsg {
91         unsigned char verflag;
92         unsigned char opcode;
93         uint16_t clienttype;
94         uint32_t length;
95         struct pktcobj *object;
96         char *msg; /* != NULL if not packet cable message received */
97 };
98
99 struct gatespec {
100         int direction; /* 0-DS, 1-US */
101         int protocolid;
102         int flags; /* 0x00 */
103         int sessionclass; /* normal voip: 0x01, high priority voip: 0x02, unspecified: 0x00 */
104         uint32_t srcip;
105         uint32_t dstip;
106         uint16_t srcp;
107         uint16_t dstp;
108         int diffserv;
109         uint16_t t1;
110         uint16_t t7;
111         uint16_t t8;
112         uint32_t r; /* Token Bucket Rate */
113         uint32_t b; /* token Bucket Size */
114         uint32_t p; /* Peak Data Rate */
115         uint32_t m; /* Minimum Policed Size*/
116         uint32_t mm; /* Maximum Policed Size */
117         uint32_t rate;
118         uint32_t s; /* Allowable Jitter*/
119 };
120
121
122 struct cops_cmts {
123         AST_LIST_ENTRY(cops_cmts) list;
124         char name[80];
125         char host[80];
126         char port[80];
127         uint16_t t1;    
128         uint16_t t7;
129         uint16_t t8;
130         uint32_t keepalive;
131
132         uint32_t handle;
133         int state;
134         time_t contime;
135         time_t katimer;
136         int sfd;
137         int need_delete;
138 };
139
140 struct cops_ippool {
141         AST_LIST_ENTRY(cops_ippool) list;
142         uint32_t start;
143         uint32_t stop;
144         struct cops_cmts *cmts;
145 };
146
147 static uint16_t t1 = 250;
148 static uint16_t t7 = 200;
149 static uint16_t t8 = 300;
150 static uint32_t keepalive = 60;
151 static int pktccopsdebug = 0;
152 static int pktcreload = 0;
153 static int gateinfoperiod = 60;
154 static int gatetimeout = 150;
155
156 AST_LIST_HEAD_STATIC(cmts_list, cops_cmts);
157 AST_LIST_HEAD_STATIC(ippool_list, cops_ippool);
158 AST_LIST_HEAD_STATIC(gate_list, cops_gate);
159
160 static int pktccops_add_ippool(struct cops_ippool *ippool);
161 static struct cops_gate *cops_gate_cmd(int cmd, struct cops_cmts *cmts, uint16_t trid, uint32_t mta, uint32_t actcount, float bitrate, uint32_t psize, uint32_t ssip, uint16_t ssport, struct cops_gate *gate);
162 static void pktccops_unregister_ippools(void);
163 static int load_pktccops_config(void);
164
165 static uint32_t ftoieeef(float n)
166 {
167         uint32_t res;
168         memcpy(&res, &n, 4);
169         return htonl(res);
170 }
171
172 static uint16_t cops_constructgatespec(struct gatespec *gs, char *res)
173 {
174         if (res == NULL) {
175                 return 0;
176         }
177         
178         *res = (char) gs->direction;
179         *(res + 1) = (char) gs->protocolid;
180         *(res + 2) = (char) gs->flags;
181         *(res + 3) = (char) gs->sessionclass;
182
183         *((uint32_t *) (res + 4)) = gs->srcip;
184         *((uint32_t *) (res + 8)) = gs->dstip;
185
186         *((uint16_t *) (res + 12)) = gs->srcp;
187         *((uint16_t *) (res + 14)) = gs->dstp;
188
189         *(res + 16) = (char) gs->diffserv;
190         *(res + 17) = 0; /* reserved */
191         *(res + 18) = 0; /* reserved */
192         *(res + 19) = 0; /* reserved */
193
194         *((uint16_t *) (res + 20)) = gs->t1;
195         *(res + 22) = 0; /* reserved */
196         *(res + 23) = 0; /* reserved */
197
198         *((uint16_t *) (res + 24)) = gs->t7;
199         *((uint16_t *) (res + 26)) = gs->t8;
200
201         *((uint32_t *) (res + 28)) = gs->r;
202         *((uint32_t *) (res + 32)) = gs->b;
203         *((uint32_t *) (res + 36)) = gs->p;
204         *((uint32_t *) (res + 40)) = gs->m;
205         *((uint32_t *) (res + 44)) = gs->mm;
206         *((uint32_t *) (res + 48)) = gs->rate;
207         *((uint32_t *) (res + 52)) = gs->s;
208         return 56; /* length */
209 };
210
211 static uint16_t cops_construct_gate (int cmd, char *p,  uint16_t trid,
212                 uint32_t mtahost, uint32_t actcount, float rate, uint32_t psizegateid,
213                 uint32_t ssip, uint16_t ssport, uint32_t gateid, struct cops_cmts *cmts)
214 {
215         struct gatespec gs;
216         int offset = 0;
217         
218         ast_debug(3, "CMD: %d\n", cmd);
219
220         /* Transaction Identifier 8 octets */
221         *(p + offset++) = 0;
222         *(p + offset++) = 8; /* length */
223         *(p + offset++) = 1; /* snum */
224         *(p + offset++) = 1; /* stype */
225         *((uint16_t *) (p + offset)) = htons(trid);
226         offset += 2;
227         *(p + offset++) = 0;
228         *(p + offset++) = (cmd == GATE_DEL) ? PKTCCOPS_SCOMMAND_GATE_DELETE : (cmd != GATE_INFO) ? PKTCCOPS_SCOMMAND_GATE_SET : PKTCCOPS_SCOMMAND_GATE_INFO; /* 4: GATE-SET, 7: GATE-INFO */
229
230         /*Subscriper Identifier 8 octets */
231         *(p + offset++) = 0;
232         *(p + offset++) = 8; /* length */
233         *(p + offset++) = 2; /* snum */
234         *(p + offset++) = 1; /* stype */
235         *((uint32_t *) (p + offset)) = htonl(mtahost);
236         offset += 4;
237         
238         if (cmd == GATE_INFO || cmd == GATE_SET_HAVE_GATEID || cmd == GATE_DEL) {
239                 /* Gate ID 8 Octets */
240                 *(p + offset++) = 0;
241                 *(p + offset++) = 8; /* length */
242                 *(p + offset++) = 3; /* snum */
243                 *(p + offset++) = 1; /* stype */
244                 *((uint32_t *) (p + offset)) = htonl(gateid);
245                 offset += 4;
246                 if (cmd == GATE_INFO || cmd == GATE_DEL) {
247                         return offset;
248                 }
249         
250         }
251
252         /* Activity Count 8 octets */
253         *(p + offset++) = 0;
254         *(p + offset++) = 8; /* length */
255         *(p + offset++) = 4; /* snum */
256         *(p + offset++) = 1; /* stype */
257         *((uint32_t *) (p + offset)) = htonl(actcount);
258         offset += 4;
259
260
261         /* Gate Spec 2*60 Octets */
262         gs.direction = 0; /* DS */
263         gs.protocolid = 17; /* UDP */
264         gs.flags = 0;
265         gs.sessionclass = 1;
266         gs.srcip = htonl(ssip);
267         gs.dstip = htonl(mtahost);
268         gs.srcp = htons(ssport);
269         gs.dstp = 0;
270 /*      gs.diffserv = 0xa0;*/
271         gs.diffserv = 0;
272         gs.t1 = htons(cmts->t1);
273         gs.t7 = htons(cmts->t7);
274         gs.t8 = htons(cmts->t8);
275         gs.r = ftoieeef(rate);
276         gs.b = ftoieeef(psizegateid);
277         gs.p = ftoieeef(rate);
278         gs.m = htonl((uint32_t) psizegateid);
279         gs.mm = htonl((uint32_t) psizegateid);
280         gs.rate = ftoieeef(rate);
281         gs.s = htonl(800);
282
283
284         *(p + offset) = 0;
285         offset++;
286         *(p + offset) = 60; /* length */
287         offset++;
288         *(p + offset) = 5; /* snum */
289         offset++;
290         *(p + offset) = 1; /* stype */
291         offset++;
292         offset += cops_constructgatespec(&gs, p + offset);
293
294
295         gs.direction = 1; /* US */
296         gs.srcip = htonl(mtahost);
297         gs.dstip = htonl(ssip);
298         gs.srcp = 0;
299         gs.dstp = htons(ssport);
300         *(p + offset) = 0;
301         offset++;
302         *(p + offset) = 60; /* length */
303         offset++;
304         *(p + offset) = 5; /* snum */
305         offset++;
306         *(p + offset) = 1; /* stype */
307         offset++;
308         offset += cops_constructgatespec(&gs, p + offset);
309
310         return(offset);
311 }
312
313 static int cops_getmsg (int sfd, struct copsmsg *recmsg)
314 {
315         int len, lent;
316         char buf[COPS_HEADER_SIZE];
317         struct pktcobj *pobject = NULL;
318         uint16_t *ubuf = (uint16_t *) buf;
319         recmsg->msg = NULL;
320         recmsg->object = NULL;
321         len = recv(sfd, buf, COPS_HEADER_SIZE, MSG_DONTWAIT);
322         if (len < COPS_HEADER_SIZE) {
323                 return len;
324         }
325         recmsg->verflag = *buf;
326         recmsg->opcode = *(buf + 1);
327         recmsg->clienttype = ntohs(*((uint16_t *) (buf + 2)));
328         recmsg->length = ntohl(*((uint32_t *) (buf + 4)));
329         /* Eg KA msg*/
330         if (recmsg->clienttype != 0x8008 ) {
331                 if (!(recmsg->msg = malloc(recmsg->length - COPS_HEADER_SIZE))) {
332                         return -1;
333                 }
334                 lent = recv(sfd, recmsg->msg, recmsg->length - COPS_HEADER_SIZE, MSG_DONTWAIT);
335                 if (lent < recmsg->length - COPS_HEADER_SIZE) {
336                         return lent;
337                 }
338                 len += len;
339         } else {
340                 /* PacketCable Objects */
341                 while (len < recmsg->length) {
342                         if (len == COPS_HEADER_SIZE) {
343                                 /* 1st round */
344                                 if (!(recmsg->object = malloc(sizeof(struct pktcobj)))) {
345                                         return -1;
346                                 }
347                                 pobject = recmsg->object;
348                         } else {
349                                 if (!(pobject->next = malloc(sizeof(struct pktcobj)))) {
350                                         return -1;
351                                 }
352                                 pobject = pobject->next;
353                         }
354                         pobject->next = NULL;
355                         lent = recv(sfd, buf, COPS_OBJECT_HEADER_SIZE, MSG_DONTWAIT);
356                         if (lent < COPS_OBJECT_HEADER_SIZE) {
357                                 ast_debug(3, "Too short object header len: %i\n", lent);
358                                 return lent;
359                         }
360                         len += lent;
361                         pobject->length = ntohs(*ubuf);
362                         pobject->cnum = *(buf + 2);
363                         pobject->ctype = *(buf + 3);
364                         if (!(pobject->contents = malloc(pobject->length - COPS_OBJECT_HEADER_SIZE))) {
365                                 return -1;
366                         }
367                         lent = recv(sfd, pobject->contents, pobject->length - COPS_OBJECT_HEADER_SIZE, MSG_DONTWAIT);
368                         if (lent < pobject->length - COPS_OBJECT_HEADER_SIZE) {
369                                 ast_debug(3, "Too short object content len: %i\n", lent);
370                                 return lent;
371                         }
372                         len += lent;
373                 }
374         }
375         return len;
376 }
377
378 static int cops_sendmsg (int sfd, struct copsmsg * sendmsg)
379 {
380         char *buf;
381         int bufpos;
382         struct pktcobj *pobject;
383         
384         if (sfd < 0) {
385                 return -1;
386         }
387
388         ast_debug(3, "COPS: sending opcode: %i len: %i\n", sendmsg->opcode, sendmsg->length);
389         if (sendmsg->length < COPS_HEADER_SIZE) {
390                 ast_log(LOG_WARNING, "COPS: invalid msg size!!!\n");
391                 return -1;
392         }
393         if (!(buf = malloc((size_t) sendmsg->length))) {
394                 return -1;
395         }
396         *buf = sendmsg->verflag ;
397         *(buf + 1) = sendmsg->opcode;
398         *((uint16_t *)(buf + 2)) = htons(sendmsg->clienttype);
399         *((uint32_t *)(buf + 4)) = htonl(sendmsg->length);
400
401         if (sendmsg->msg != NULL) {
402                 memcpy(buf + COPS_HEADER_SIZE, sendmsg->msg, sendmsg->length - COPS_HEADER_SIZE);
403         } else if (sendmsg->object != NULL) {
404                 bufpos = 8;
405                 pobject = sendmsg->object;
406                 while(pobject != NULL) {
407                         ast_debug(3, "COPS: Sending Object : cnum: %i ctype %i len: %i\n", pobject->cnum, pobject->ctype, pobject->length);
408                         if (sendmsg->length < bufpos + pobject->length) {
409                                 ast_log(LOG_WARNING, "COPS: Invalid msg size len: %i objectlen: %i\n", sendmsg->length, pobject->length);
410                                 free(buf);
411                                 return -1;
412                         }
413                         *(uint16_t *) (buf + bufpos) = htons(pobject->length);
414                         *(buf + bufpos + 2) = pobject->cnum;
415                         *(buf + bufpos + 3) = pobject->ctype;
416                         if (sendmsg->length < pobject->length + bufpos) {
417                                 ast_log(LOG_WARNING, "COPS: Error sum of object len more the msg len %i < %i\n", sendmsg->length, pobject->length + bufpos);
418                                 free(buf);
419                                 return -1;
420                         }
421                         memcpy((buf + bufpos + 4), pobject->contents, pobject->length - 4);
422                         bufpos += pobject->length;
423                         pobject = pobject->next;
424                 }
425         }
426         
427         errno = 0;
428         if (send(sfd, buf, sendmsg->length, MSG_NOSIGNAL | MSG_DONTWAIT ) == -1) {
429                 ast_log(LOG_WARNING, "COPS: Send failed errno=%i\n", errno);
430                 free(buf);
431                 return -2;
432         }
433         free(buf);
434         return 0;
435 }
436
437 static void cops_freemsg(struct copsmsg *p)
438 {
439         struct pktcobj *pnext;
440         free(p->msg);
441         p->msg = NULL;
442         while (p->object != NULL) {
443                         pnext = p->object->next;
444                         ast_free(p->object->contents);
445                         p->object->contents = NULL;
446                         ast_free(p->object);
447                         p->object = pnext;
448         }
449         p->object = NULL;
450 }
451
452 struct cops_gate * AST_OPTIONAL_API_NAME(ast_pktccops_gate_alloc)(int cmd,
453                 struct cops_gate *gate, uint32_t mta, uint32_t actcount, float bitrate,
454                 uint32_t psize, uint32_t ssip, uint16_t ssport,
455                 int (* const got_dq_gi) (struct cops_gate *gate),
456                 int (* const gate_remove) (struct cops_gate *gate))
457 {
458         while (pktcreload) {
459                 sched_yield();
460         }
461
462         if (cmd == GATE_SET_HAVE_GATEID && gate) {
463                 ast_debug(3, "------- gate modify gateid 0x%x ssip: 0x%x\n", gate->gateid, ssip);
464                 /* TODO implement it */
465                 ast_log(LOG_WARNING, "Modify GateID not implemented\n");
466         } 
467         
468         if ((gate = cops_gate_cmd(cmd, NULL, cops_trid++, mta, actcount, bitrate, psize, ssip, ssport, gate))) {
469                 ast_debug(3, "COPS: Allocating gate for mta: 0x%x\n", mta);
470                 gate->got_dq_gi = got_dq_gi;
471                 gate->gate_remove = gate_remove;
472                 return(gate);
473         } else {
474                 ast_debug(3, "COPS: Couldn't allocate gate for mta: 0x%x\n", mta); 
475                 return NULL;
476         }
477 }
478
479 static struct cops_gate *cops_gate_cmd(int cmd, struct cops_cmts *cmts,
480                 uint16_t trid, uint32_t mta, uint32_t actcount, float bitrate,
481                 uint32_t psize, uint32_t ssip, uint16_t ssport, struct cops_gate *gate)
482 {
483         struct copsmsg *gateset;
484         struct cops_gate *new;
485         struct cops_ippool *ippool;
486
487         if (cmd == GATE_DEL) {
488                 if (gate == NULL) {
489                         return NULL;
490                 } else {
491                         cmts = gate->cmts;
492                 }
493         }
494
495         if (!cmts) {
496                 AST_LIST_LOCK(&ippool_list);
497                 AST_LIST_TRAVERSE(&ippool_list, ippool, list) {
498                         if (mta >= ippool->start && mta <= ippool->stop) {
499                                 cmts = ippool->cmts;
500                                 break;
501                         }
502                 }
503                 AST_LIST_UNLOCK(&ippool_list);
504                 if (!cmts) {
505                         ast_log(LOG_WARNING, "COPS: couldn't find cmts for mta: 0x%x\n", mta);
506                         return NULL;
507                 }
508                 if (cmts->sfd < 0) {
509                         ast_log(LOG_WARNING, "CMTS: %s not connected\n", cmts->name);
510                         return NULL;
511                 }
512         }
513
514         if (cmd == GATE_SET) {
515                 new = ast_calloc(1, sizeof(*new));
516                 new->gateid = 0;
517                 new->trid = trid;
518                 new->mta = mta;
519                 new->state = GATE_ALLOC_PROGRESS;
520                 new->checked = time(NULL);
521                 new->allocated = time(NULL);
522                 new->cmts = cmts;
523                 new->got_dq_gi = NULL;
524                 new->gate_remove = NULL;
525                 new->gate_open = NULL;
526                 new->tech_pvt = NULL;
527                 new->deltimer = 0;
528                 AST_LIST_LOCK(&gate_list);
529                 AST_LIST_INSERT_HEAD(&gate_list, new, list);
530                 AST_LIST_UNLOCK(&gate_list);
531                 gate = new;
532         } else {
533                 if (gate) {
534                         gate->trid = trid;
535                 }
536         }
537         
538         gate->in_transaction = time(NULL);
539
540         if (!(gateset = malloc(sizeof(struct copsmsg)))) {
541                 free(gateset);
542                 return NULL;
543         }
544         gateset->msg = NULL;
545         gateset->verflag = 0x10;
546         gateset->opcode = 2; /* Decision */
547         gateset->clienttype = 0x8008; /* =PacketCable */
548         
549         /* Handle object */
550         gateset->object = malloc(sizeof(struct pktcobj));
551         if (!gateset->object) {
552                 cops_freemsg(gateset);
553                 free(gateset);
554                 return NULL;
555         }
556         gateset->object->length = COPS_OBJECT_HEADER_SIZE + 4;
557         gateset->object->cnum = 1; /* Handle */
558         gateset->object->ctype = 1; /* client */
559         if (!(gateset->object->contents = malloc(sizeof(uint32_t)))) {
560                 cops_freemsg(gateset);
561                 free(gateset);
562                 return NULL;
563         }
564         *((uint32_t *) gateset->object->contents) = htonl(cmts->handle);
565
566         /* Context Object */
567         if (!(gateset->object->next = malloc(sizeof(struct pktcobj)))) {
568                 cops_freemsg(gateset);
569                 free(gateset);
570                 return NULL;
571         }
572         gateset->object->next->length = COPS_OBJECT_HEADER_SIZE + 4;
573         gateset->object->next->cnum = 2; /* Context */
574         gateset->object->next->ctype = 1; /* Context */
575         if (!(gateset->object->next->contents = malloc(sizeof(uint32_t)))) {
576                 cops_freemsg(gateset);
577                 free(gateset);
578                 return NULL;
579         }
580         *((uint32_t *) gateset->object->next->contents) = htonl(0x00080000); /* R-Type = 8 configuration request, M-Type = 0 */
581
582         /* Decision Object: Flags */
583         if (!(gateset->object->next->next = malloc(sizeof(struct pktcobj)))) {
584                 cops_freemsg(gateset);
585                 free(gateset);
586                 return NULL;
587         }
588         gateset->object->next->next->length = COPS_OBJECT_HEADER_SIZE + 4;
589         gateset->object->next->next->cnum = 6; /* Decision */
590         gateset->object->next->next->ctype = 1; /* Flags */
591         if (!(gateset->object->next->next->contents = malloc(sizeof(uint32_t)))) {
592                 cops_freemsg(gateset);
593                 free(gateset);
594                 return NULL;
595         }
596         *((uint32_t *) gateset->object->next->next->contents) = htonl(0x00010001); /* Install, Trigger Error */
597
598         /* Decision Object: Data */
599         if (!(gateset->object->next->next->next = malloc(sizeof(struct pktcobj)))) {
600                 cops_freemsg(gateset);
601                 free(gateset);
602                 return NULL;
603         }
604         gateset->object->next->next->next->length = COPS_OBJECT_HEADER_SIZE + ((cmd != GATE_INFO && cmd != GATE_DEL) ? GATE_SET_OBJ_SIZE : GATE_INFO_OBJ_SIZE) + ((cmd == GATE_SET_HAVE_GATEID) ? GATEID_OBJ_SIZE : 0);
605         gateset->object->next->next->next->cnum = 6; /* Decision */
606         gateset->object->next->next->next->ctype = 4; /* Decision Data */
607         gateset->object->next->next->next->contents = malloc(((cmd != GATE_INFO && cmd != GATE_DEL) ? GATE_SET_OBJ_SIZE : GATE_INFO_OBJ_SIZE) + ((cmd == GATE_SET_HAVE_GATEID) ? GATEID_OBJ_SIZE : 0));
608         if (!gateset->object->next->next->next->contents) {
609                 cops_freemsg(gateset);
610                 free(gateset);
611                 return NULL;
612         }
613         gateset->object->next->next->next->next = NULL;
614         
615         gateset->length = COPS_HEADER_SIZE + gateset->object->length + gateset->object->next->length + gateset->object->next->next->length + gateset->object->next->next->next->length;
616
617         if ((cmd == GATE_INFO || cmd == GATE_SET_HAVE_GATEID || cmd == GATE_DEL) && gate) {
618                 ast_debug(1, "Construct gate with gateid: 0x%x\n", gate->gateid);
619                 cops_construct_gate(cmd, gateset->object->next->next->next->contents, trid, mta, actcount, bitrate, psize, ssip, ssport, gate->gateid, cmts);
620         } else {
621                 ast_debug(1, "Construct new gate\n");
622                 cops_construct_gate(cmd, gateset->object->next->next->next->contents, trid, mta, actcount, bitrate, psize, ssip, ssport, 0, cmts);
623         }
624         if (pktccopsdebug) {
625                 ast_debug(3, "send cmd\n");
626         }
627         cops_sendmsg(cmts->sfd, gateset);
628         cops_freemsg(gateset);
629         free(gateset);
630         return gate;
631 }
632
633 static int cops_connect(char *host, char *port)
634 {
635         int s, sfd = -1, flags;
636         struct addrinfo hints;
637         struct addrinfo *rp;
638         struct addrinfo *result;
639
640         memset(&hints, 0, sizeof(struct addrinfo));
641
642         hints.ai_family = AF_UNSPEC;    
643         hints.ai_socktype = SOCK_STREAM;
644         hints.ai_flags = 0;
645         hints.ai_protocol = 0;
646
647         s = getaddrinfo(host, port, &hints, &result);
648         if (s != 0) {
649                 ast_log(LOG_WARNING, "COPS: getaddrinfo: %s\n", gai_strerror(s));
650                 return -1;
651         }
652
653         for (rp = result; rp != NULL; rp = rp->ai_next) {
654                 sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
655                 if (sfd == -1) {
656                         ast_log(LOG_WARNING, "Failed socket\n");
657                 }
658                 flags = fcntl(sfd, F_GETFL);
659                 fcntl(sfd, F_SETFL, flags | O_NONBLOCK);
660                 connect(sfd, rp->ai_addr, rp->ai_addrlen);
661                 if (sfd == -1) {
662                         ast_log(LOG_WARNING, "Failed connect\n");
663                 }
664         }
665         freeaddrinfo(result);
666
667         ast_debug(3, "Connecting to cmts:  %s:%s\n", host, port);
668         return(sfd);
669 }
670
671 #define PKTCCOPS_DESTROY_CURRENT_GATE   \
672                 AST_LIST_REMOVE_CURRENT(list);  \
673                 if (gate->gate_remove) {        \
674                         gate->gate_remove(gate);    \
675                 }                               \
676                 ast_free(gate);
677
678 static void *do_pktccops(void *data)
679 {
680         int res, nfds, len;
681         struct copsmsg *recmsg, *sendmsg;
682         struct copsmsg recmsgb, sendmsgb;
683         fd_set rfds;
684         struct timeval tv;
685         struct pktcobj *pobject;        
686         struct cops_cmts *cmts;
687         struct cops_gate *gate;
688         char *sobjp;
689         uint16_t snst, sobjlen, scommand, recvtrid, actcount, reason, subreason;
690         uint32_t gateid, subscrid, pktcerror;
691         time_t last_exec = 0;
692
693         recmsg = &recmsgb;
694         sendmsg = &sendmsgb;
695
696         ast_debug(3, "COPS: thread started\n");
697
698         for (;;) {
699                 tv.tv_sec = 1;
700                 tv.tv_usec = 0;
701                 FD_ZERO(&rfds);
702                 nfds = 0;
703                 AST_LIST_LOCK(&cmts_list);
704                 AST_LIST_TRAVERSE(&cmts_list, cmts, list) {
705                         if (last_exec != time(NULL)) {
706                                 if (cmts->state == 2 && cmts->katimer + cmts->keepalive < time(NULL)) {
707                                         ast_log(LOG_WARNING, "KA timer (%is) expired cmts: %s\n",  cmts->keepalive, cmts->name);
708                                         cmts->state = 0;
709                                         cmts->katimer = -1;
710                                         close(cmts->sfd);
711                                         cmts->sfd = -1;
712                                 }
713                         }
714                         if (cmts->sfd > 0) {
715                                 FD_SET(cmts->sfd, &rfds);
716                                 if (cmts->sfd > nfds) nfds = cmts->sfd;
717                         } else {
718                                 cmts->sfd = cops_connect(cmts->host, cmts->port);
719                                 if (cmts->sfd > 0) {
720                                         cmts->state = 1;
721                                         if (cmts->sfd > 0) {
722                                                 FD_SET(cmts->sfd, &rfds);
723                                                 if (cmts->sfd > nfds) nfds = cmts->sfd;
724                                         }
725                                 }
726                         }
727                 }
728                 AST_LIST_UNLOCK(&cmts_list);
729
730                 if (last_exec != time(NULL)) {
731                         last_exec = time(NULL);
732                         AST_LIST_LOCK(&gate_list);
733                         AST_LIST_TRAVERSE_SAFE_BEGIN(&gate_list, gate, list) {
734                                 if (gate) {
735                                         if (gate->deltimer && gate->deltimer < time(NULL)) {
736                                                 gate->deltimer = time(NULL) + 5;
737                                                 gate->trid = cops_trid++;
738                                                 cops_gate_cmd(GATE_DEL, gate->cmts, gate->trid, 0, 0, 0, 0, 0, 0, gate);
739                                                 ast_debug(3, "COPS: requested Gate-Del: CMTS: %s gateid: 0x%x\n", (gate->cmts) ? gate->cmts->name : "null", gate->gateid);
740                                         }
741                                         if (time(NULL) - gate->checked > gatetimeout) {
742                                                 ast_debug(3, "COPS: remove from list GATE, CMTS: %s gateid: 0x%x\n", (gate->cmts) ? gate->cmts->name : "null", gate->gateid);
743                                                 gate->state = GATE_TIMEOUT;
744                                                 PKTCCOPS_DESTROY_CURRENT_GATE;
745                                         } else if (time(NULL) - gate->checked > gateinfoperiod && (gate->state == GATE_ALLOCATED || gate->state == GATE_OPEN)) {
746                                                 if (gate->cmts && (!gate->in_transaction || ( gate->in_transaction + 5 ) < time(NULL))) {
747                                                         gate->trid = cops_trid++;
748                                                         ast_debug(3, "COPS: Gate-Info send to CMTS: %s gateid: 0x%x\n", gate->cmts->name, gate->gateid);
749                                                         cops_gate_cmd(GATE_INFO, gate->cmts, gate->trid, gate->mta, 0, 0, 0, 0, 0, gate);
750                                                 }
751                                         }
752                                 }
753                         }
754                         AST_LIST_TRAVERSE_SAFE_END;
755                         AST_LIST_UNLOCK(&gate_list);
756                 }
757
758                 if (pktcreload == 2) {
759                         pktcreload = 0;
760                 }
761                 if ((res = select(nfds + 1, &rfds, NULL, NULL, &tv))) {
762                         AST_LIST_LOCK(&cmts_list);
763                         AST_LIST_TRAVERSE(&cmts_list, cmts, list) {
764                                 if (FD_ISSET(cmts->sfd, &rfds)) {
765                                         len = cops_getmsg(cmts->sfd, recmsg);
766                                         if (len > 0) {
767                                                 ast_debug(3, "COPS: got from %s:\n Header: versflag=0x%.2x opcode=%i clienttype=0x%.4x msglength=%i\n",
768                                                         cmts->name, recmsg->verflag, recmsg->opcode, recmsg->clienttype, recmsg->length);
769                                                 if (recmsg->object != NULL) {
770                                                         pobject = recmsg->object;
771                                                         while (pobject != NULL) {
772                                                                 ast_debug(3, " OBJECT: length=%i cnum=%i ctype=%i\n", pobject->length, pobject->cnum, pobject->ctype);
773                                                                 if (recmsg->opcode == 1 && pobject->cnum == 1 && pobject->ctype == 1 ) {
774                                                                         cmts->handle = ntohl(*((uint32_t *) pobject->contents));
775                                                                         ast_debug(3, "    REQ client handle: %i\n", cmts->handle);
776                                                                         cmts->state = 2;
777                                                                         cmts->katimer = time(NULL);
778                                                                 } else if (pobject->cnum == 9 && pobject->ctype == 1) {
779                                                                         sobjp = pobject->contents;
780                                                                         subscrid = 0;
781                                                                         recvtrid = 0;
782                                                                         scommand = 0;
783                                                                         pktcerror = 0;
784                                                                         actcount = 0;
785                                                                         gateid = 0;
786                                                                         reason = 0;
787                                                                         subreason = 0;
788                                                                         while (sobjp < (pobject->contents + pobject->length - 4)) {
789                                                                                 sobjlen = ntohs(*((uint16_t *) sobjp));
790                                                                                 snst = ntohs(*((uint16_t *) (sobjp + 2)));
791                                                                                 ast_debug(3, "   S-Num S-type: 0x%.4x len: %i\n", snst, sobjlen);
792                                                                                 if (snst == 0x0101 ) {
793                                                                                         recvtrid = ntohs(*((uint16_t *) (sobjp + 4)));
794                                                                                         scommand = ntohs(*((uint16_t *) (sobjp + 6)));                                  
795                                                                                         ast_debug(3, "     Transaction Identifier command: %i trid %i\n", scommand, recvtrid);
796                                                                                 } else if (snst == 0x0201) {
797                                                                                         subscrid = ntohl(*((uint32_t *) (sobjp + 4)));
798                                                                                         ast_debug(3, "     Subscriber ID: 0x%.8x\n", subscrid);
799                                                                                 } else if (snst == 0x0301) {
800                                                                                         gateid = ntohl(*((uint32_t *) (sobjp + 4)));
801                                                                                         ast_debug(3, "      Gate ID: 0x%x 0x%.8x\n", gateid, gateid);
802                                                                                 } else if (snst == 0x0401) {
803                                                                                         actcount = ntohs(*((uint16_t *) (sobjp + 6)));
804                                                                                         ast_debug(3, "      Activity Count: %i\n", actcount);
805                                                                                 } else if (snst == 0x0901) {
806                                                                                         pktcerror = ntohl(*((uint32_t *) (sobjp + 4)));
807                                                                                         ast_debug(3, "      PKTC Error: 0x%.8x\n", pktcerror);
808                                                                                 } else if (snst == 0x0d01) {
809                                                                                         reason = ntohs(*((uint16_t *) (sobjp + 4)));
810                                                                                         subreason = ntohs(*((uint16_t *) (sobjp + 6)));
811                                                                                         ast_debug(3, "      Reason: %u Subreason: %u\n", reason, subreason);
812                                                                                 }
813                                                                                 sobjp += sobjlen;
814                                                                                 if (!sobjlen)
815                                                                                         break;
816                                                                         }
817                                                                         if (scommand == PKTCCOPS_SCOMMAND_GATE_CLOSE || scommand == PKTCCOPS_SCOMMAND_GATE_OPEN) {
818                                                                                 AST_LIST_LOCK(&gate_list);
819                                                                                 AST_LIST_TRAVERSE_SAFE_BEGIN(&gate_list, gate, list) {
820                                                                                         if (gate->cmts == cmts && gate->gateid == gateid) {
821                                                                                                 if (scommand == PKTCCOPS_SCOMMAND_GATE_CLOSE && gate->state != GATE_CLOSED && gate->state != GATE_CLOSED_ERR ) {
822                                                                                                         ast_debug(3, "COPS Gate Close Gate ID: 0x%x TrId: %i CMTS: %s\n", gateid, recvtrid, cmts->name);
823                                                                                                         if (subreason) {
824                                                                                                                 gate->state = GATE_CLOSED_ERR;
825                                                                                                                 PKTCCOPS_DESTROY_CURRENT_GATE;
826                                                                                                         } else {
827                                                                                                                 gate->state = GATE_CLOSED;
828                                                                                                                 PKTCCOPS_DESTROY_CURRENT_GATE;
829                                                                                                         }
830                                                                                                         break;
831                                                                                                 } else if (scommand == PKTCCOPS_SCOMMAND_GATE_OPEN && gate->state == GATE_ALLOCATED) {
832                                                                                                         ast_debug(3, "COPS Gate Open Gate ID: 0x%x TrId: %i CMTS: %s\n", gateid, recvtrid, cmts->name);
833                                                                                                         gate->state = GATE_OPEN;
834                                                                                                         if (gate->gate_open) {
835                                                                                                                 ast_debug(3, "Calling GATE-OPEN callback function\n");
836                                                                                                                 gate->gate_open(gate);
837                                                                                                                 gate->gate_open = NULL;
838                                                                                                         }
839                                                                                                         break;
840                                                                                                 } 
841                                                                                         }
842                                                                                 }
843                                                                                 AST_LIST_TRAVERSE_SAFE_END;
844                                                                                 AST_LIST_UNLOCK(&gate_list);
845                                                                         } else if (scommand == PKTCCOPS_SCOMMAND_GATE_SET_ACK || scommand == PKTCCOPS_SCOMMAND_GATE_SET_ERR || scommand == PKTCCOPS_SCOMMAND_GATE_INFO_ACK || scommand == PKTCCOPS_SCOMMAND_GATE_INFO_ERR || scommand == PKTCCOPS_SCOMMAND_GATE_DELETE_ACK) {
846                                                                                 AST_LIST_LOCK(&gate_list);
847                                                                                 AST_LIST_TRAVERSE_SAFE_BEGIN(&gate_list, gate, list) {
848                                                                                         if (gate->cmts == cmts && gate->trid == recvtrid) {
849                                                                                                 gate->gateid = gateid;
850                                                                                                 gate->checked = time(NULL);
851                                                                                                 if (scommand == PKTCCOPS_SCOMMAND_GATE_SET_ACK) {
852                                                                                                         ast_debug(3, "COPS Gate Set Ack Gate ID: 0x%x TrId: %i CMTS: %s\n", gateid, recvtrid, cmts->name);
853                                                                                                         gate->state = GATE_ALLOCATED;
854                                                                                                         if (gate->got_dq_gi) {
855                                                                                                                 gate->got_dq_gi(gate);
856                                                                                                                 gate->got_dq_gi = NULL;
857                                                                                                         }
858                                                                                                 } else if (scommand == PKTCCOPS_SCOMMAND_GATE_SET_ERR) {
859                                                                                                         ast_debug(3, "COPS Gate Set Error TrId: %i ErrorCode: 0x%.8x CMTS: %s\n ", recvtrid, pktcerror, cmts->name);
860                                                                                                         gate->state = GATE_ALLOC_FAILED;
861                                                                                                         if (gate->got_dq_gi) {
862                                                                                                                 gate->got_dq_gi(gate);
863                                                                                                                 gate->got_dq_gi = NULL;
864                                                                                                         }
865                                                                                                         PKTCCOPS_DESTROY_CURRENT_GATE;
866                                                                                                 } else if (scommand == PKTCCOPS_SCOMMAND_GATE_INFO_ACK) {
867                                                                                                         ast_debug(3, "COPS Gate Info Ack Gate ID: 0x%x TrId: %i CMTS: %s\n", gateid, recvtrid, cmts->name);
868                                                                                                 } else if (scommand == PKTCCOPS_SCOMMAND_GATE_INFO_ERR) {
869                                                                                                         ast_debug(3, "COPS Gate Info Error Gate ID: 0x%x TrId: %i CMTS: %s\n", gateid, recvtrid, cmts->name);
870                                                                                                         gate->state = GATE_ALLOC_FAILED;
871                                                                                                         PKTCCOPS_DESTROY_CURRENT_GATE;
872                                                                                                 } else if (scommand == PKTCCOPS_SCOMMAND_GATE_DELETE_ACK) {
873                                                                                                         ast_debug(3, "COPS Gate Deleted Gate ID: 0x%x TrId: %i CMTS: %s\n", gateid, recvtrid, cmts->name);
874                                                                                                         gate->state = GATE_DELETED;
875                                                                                                         PKTCCOPS_DESTROY_CURRENT_GATE;
876                                                                                                 }
877                                                                                                 gate->in_transaction = 0;
878                                                                                                 break;
879                                                                                         }
880                                                                                 }
881                                                                                 AST_LIST_TRAVERSE_SAFE_END;
882                                                                                 AST_LIST_UNLOCK(&gate_list);
883                                                                         }
884                                                                 }
885                                                                 pobject = pobject->next;
886                                                         }
887                                                 }
888
889                                                 if (recmsg->opcode == 6 && recmsg->object && recmsg->object->cnum == 11 && recmsg->object->ctype == 1) {
890                                                         ast_debug(3, "COPS: Client open %s\n", cmts->name);
891                                                         sendmsg->msg = NULL;
892                                                         sendmsg->verflag = 0x10;
893                                                         sendmsg->opcode = 7; /* Client Accept */
894                                                         sendmsg->clienttype = 0x8008; /* =PacketCable */
895                                                         sendmsg->length = COPS_HEADER_SIZE + COPS_OBJECT_HEADER_SIZE + 4;
896                                                         sendmsg->object = malloc(sizeof(struct pktcobj));
897                                                         sendmsg->object->length = 4 + COPS_OBJECT_HEADER_SIZE;
898                                                         sendmsg->object->cnum = 10; /* keppalive timer*/
899                                                         sendmsg->object->ctype = 1;
900                                                         sendmsg->object->contents = malloc(sizeof(uint32_t));
901                                                         *((uint32_t *) sendmsg->object->contents) = htonl(cmts->keepalive & 0x0000ffff);
902                                                         sendmsg->object->next = NULL;
903                                                         cops_sendmsg(cmts->sfd, sendmsg);
904                                                         cops_freemsg(sendmsg);
905                                                 } else if (recmsg->opcode == 9) {
906                                                         ast_debug(3, "COPS: Keepalive Request got echoing back %s\n", cmts->name);
907                                                         cops_sendmsg(cmts->sfd, recmsg);
908                                                         cmts->state = 2;
909                                                         cmts->katimer = time(NULL);
910                                                 }
911                                         } 
912                                         if (len <= 0) {
913                                                 ast_debug(3, "COPS: lost connection to %s\n", cmts->name);
914                                                 close(cmts->sfd);
915                                                 cmts->sfd = -1;
916                                                 cmts->state = 0;
917                                         }
918                                         cops_freemsg(recmsg);
919                                 }
920                         }
921                         AST_LIST_UNLOCK(&cmts_list);                    
922                 }
923                 if (pktcreload) {
924                         ast_debug(3, "Reloading pktccops...\n");
925                         AST_LIST_LOCK(&gate_list);
926                         AST_LIST_LOCK(&cmts_list);
927                         pktccops_unregister_ippools();
928                         AST_LIST_TRAVERSE(&cmts_list, cmts, list) {
929                                 cmts->need_delete = 1;
930                         }
931                         load_pktccops_config();
932                         AST_LIST_TRAVERSE_SAFE_BEGIN(&cmts_list, cmts, list) {
933                                 if (cmts && cmts->need_delete) {
934                                         AST_LIST_TRAVERSE(&gate_list, gate, list) {
935                                                 if (gate->cmts == cmts) {
936                                                         ast_debug(3, "Null gate %s\n", gate->cmts->name);
937                                                         gate->cmts = NULL;
938                                                 }
939                                                 gate->in_transaction = 0;
940                                         }
941                                         AST_LIST_UNLOCK(&gate_list);
942                                         ast_debug(3, "removing cmts: %s\n", cmts->name);
943                                         if (cmts->sfd > 0) {
944                                                 close(cmts->sfd);
945                                         }
946                                         AST_LIST_REMOVE_CURRENT(list);
947                                         free(cmts);
948                                 }
949                         }
950                         AST_LIST_TRAVERSE_SAFE_END;
951                         AST_LIST_UNLOCK(&cmts_list);
952                         AST_LIST_UNLOCK(&gate_list);
953                         pktcreload = 2;
954                 }
955                 pthread_testcancel();
956         }
957         return NULL;
958 }
959
960 static int restart_pktc_thread(void)
961 {
962         if (pktccops_thread == AST_PTHREADT_STOP) {
963                 return 0;
964         }
965         if (ast_mutex_lock(&pktccops_lock)) {
966                 ast_log(LOG_WARNING, "Unable to lock pktccops\n");
967                 return -1;
968         }
969         if (pktccops_thread == pthread_self()) {
970                 ast_mutex_unlock(&pktccops_lock);
971                 ast_log(LOG_WARNING, "Cannot kill myself\n");
972                 return -1;
973         }
974         if (pktccops_thread != AST_PTHREADT_NULL) {
975                 /* Wake up the thread */
976                 pthread_kill(pktccops_thread, SIGURG);
977         } else {
978                 /* Start a new monitor */
979                 if (ast_pthread_create_background(&pktccops_thread, NULL, do_pktccops, NULL) < 0) {
980                         ast_mutex_unlock(&pktccops_lock);
981                         ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
982                         return -1;
983                 }
984         }
985         ast_mutex_unlock(&pktccops_lock);
986         return 0;
987 }
988
989 static int load_pktccops_config(void)
990 {
991         static char *cfg = "res_pktccops.conf";
992         struct ast_config *config;
993         struct ast_variable *v;
994         struct cops_cmts *cmts;
995         struct cops_ippool *new_ippool;
996         const char *host, *cat, *port;
997         int sfd, update;
998         int res = 0;
999         uint16_t t1_temp, t7_temp, t8_temp;
1000         uint32_t keepalive_temp;
1001         unsigned int a,b,c,d,e,f,g,h;
1002         struct ast_flags config_flags = {0};
1003
1004         if (!(config = ast_config_load(cfg, config_flags))) {
1005                 ast_log(LOG_WARNING, "Unable to load config file res_pktccops.conf\n");
1006                 return -1;
1007         }
1008         for (cat = ast_category_browse(config, NULL); cat; cat = ast_category_browse(config, cat)) {
1009                 if (!strcmp(cat, "general")) {
1010                         for (v = ast_variable_browse(config, cat); v; v = v->next) {
1011                                 if (!strcasecmp(v->name, "t1")) {
1012                                         t1 = atoi(v->value);
1013                                 } else if (!strcasecmp(v->name, "t7")) {
1014                                         t7 = atoi(v->value);
1015                                 } else if (!strcasecmp(v->name, "t8")) {
1016                                         t8 = atoi(v->value);
1017                                 } else if (!strcasecmp(v->name, "keepalive")) {
1018                                         keepalive = atoi(v->value);
1019                                 } else if (!strcasecmp(v->name, "gateinfoperiod")) {
1020                                         gateinfoperiod = atoi(v->value);
1021                                 } else if (!strcasecmp(v->name, "gatetimeout")) {
1022                                         gatetimeout = atoi(v->value);
1023                                 } else {
1024                                         ast_log(LOG_WARNING, "Unkown option %s in general section of res_ptkccops.conf\n", v->name);
1025                                 }
1026                         }                       
1027                 } else {
1028                         /* Defaults */
1029                         host = NULL;
1030                         port = NULL;
1031                         sfd = 0;
1032                         t1_temp = t1;
1033                         t7_temp = t7;
1034                         t8_temp = t8;
1035                         keepalive_temp = keepalive;
1036
1037                         for (v = ast_variable_browse(config, cat); v; v = v->next) {
1038                                 if (!strcasecmp(v->name, "host")) {
1039                                         host = v->value;                                
1040                                 } else if (!strcasecmp(v->name, "port")) {
1041                                         port = v->value;
1042                                 } else if (!strcasecmp(v->name, "t1")) {
1043                                         t1_temp = atoi(v->value);
1044                                 } else if (!strcasecmp(v->name, "t7")) {
1045                                         t7_temp = atoi(v->value);
1046                                 } else if (!strcasecmp(v->name, "t8")) {
1047                                         t8_temp = atoi(v->value);
1048                                 } else if (!strcasecmp(v->name, "keepalive")) {
1049                                         keepalive_temp = atoi(v->value);
1050                                 } else if (!strcasecmp(v->name, "pool")) {
1051                                         /* we weill parse it in 2nd round */
1052                                 } else {
1053                                         ast_log(LOG_WARNING, "Unkown option %s in res_ptkccops.conf\n", v->name);
1054                                 }
1055                         }
1056
1057                         update = 0;
1058                         AST_LIST_TRAVERSE(&cmts_list, cmts, list) {
1059                                 if (!strcmp(cmts->name, cat)) {
1060                                         update = 1;
1061                                         break;
1062                                 }
1063
1064                         }
1065                         if (!update) {
1066                                 cmts = ast_calloc(1, sizeof(*cmts));
1067                                 if (!cmts) {
1068                                         res = -1;
1069                                         break;
1070                                 }
1071                                 AST_LIST_INSERT_HEAD(&cmts_list, cmts, list);
1072                         }
1073                         if (cat) {
1074                                 ast_copy_string(cmts->name, cat, sizeof(cmts->name));
1075                         }
1076                         if (host) {
1077                                 ast_copy_string(cmts->host, host, sizeof(cmts->host));
1078                         }
1079                         if (port) {
1080                                 ast_copy_string(cmts->port, port, sizeof(cmts->port));
1081                         } else {
1082                                 ast_copy_string(cmts->port, DEFAULT_COPS_PORT, sizeof(cmts->port));
1083                         }
1084
1085                         cmts->t1 = t1_temp;
1086                         cmts->t7 = t7_temp;
1087                         cmts->t8 = t8_temp;
1088                         cmts->keepalive = keepalive_temp;
1089                         if (!update) {
1090                                 cmts->state = 0;
1091                                 cmts->sfd = -1;
1092                         }
1093                         cmts->need_delete = 0;
1094                         for (v = ast_variable_browse(config, cat); v; v = v->next) {
1095                                 /* parse ipppol when we have cmts ptr */
1096                                 if (!strcasecmp(v->name, "pool")) {
1097                                         if (sscanf(v->value, "%3u.%3u.%3u.%3u %3u.%3u.%3u.%3u", &a, &b, &c, &d, &e, &f, &g, &h) == 8) {
1098                                                 new_ippool = ast_calloc(1, sizeof(*new_ippool));
1099                                                 if (!new_ippool) {
1100                                                         res = -1;
1101                                                         break;
1102                                                 }
1103                                                 new_ippool->start = a << 24 | b << 16 | c << 8 | d;
1104                                                 new_ippool->stop = e << 24 | f << 16 | g << 8 | h;
1105                                                 new_ippool->cmts = cmts;
1106                                                 pktccops_add_ippool(new_ippool);
1107                                         } else {
1108                                                 ast_log(LOG_WARNING, "Invalid ip pool format in res_pktccops.conf\n");
1109                                         }
1110                                 }
1111                         }
1112                 }
1113         }
1114         ast_config_destroy(config);
1115         return res;
1116 }
1117
1118 static char *pktccops_show_cmtses(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1119 {
1120         struct cops_cmts *cmts;
1121         char statedesc[16];
1122         int katimer;
1123         
1124         switch(cmd) {
1125         case CLI_INIT:
1126                 e->command = "pktccops show cmtses";
1127                 e->usage = 
1128                         "Usage: pktccops show cmtses\n"
1129                         "       List PacketCable COPS CMTSes.\n";
1130
1131                 return NULL;
1132         case CLI_GENERATE:
1133                 return NULL;
1134         }
1135
1136         ast_cli(a->fd, "%-16s %-24s %-12s %7s\n", "Name        ", "Host                ", "Status    ", "KA timer  ");
1137         ast_cli(a->fd, "%-16s %-24s %-12s %7s\n", "------------", "--------------------", "----------", "-----------");
1138         AST_LIST_LOCK(&cmts_list);
1139         AST_LIST_TRAVERSE(&cmts_list, cmts, list) {
1140                 katimer = -1;
1141                 if (cmts->state == 2) {
1142                         ast_copy_string(statedesc, "Connected", sizeof(statedesc));
1143                         katimer = (int) (time(NULL) - cmts->katimer);
1144                 } else if (cmts->state == 1) {
1145                         ast_copy_string(statedesc, "Connecting", sizeof(statedesc));
1146                 } else {
1147                         ast_copy_string(statedesc, "N/A", sizeof(statedesc));
1148                 }
1149                 ast_cli(a->fd, "%-16s %-15s:%-8s %-12s %-7d\n", cmts->name, cmts->host, cmts->port, statedesc, katimer);
1150         }
1151         AST_LIST_UNLOCK(&cmts_list);
1152         return CLI_SUCCESS;
1153 }
1154
1155 static char *pktccops_show_gates(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1156 {
1157         struct cops_gate *gate;
1158         char state_desc[16];
1159
1160         switch(cmd) {
1161         case CLI_INIT:
1162                 e->command = "pktccops show gates";
1163                 e->usage = 
1164                         "Usage: pktccops show gates\n"
1165                         "       List PacketCable COPS GATEs.\n";
1166
1167                 return NULL;
1168         case CLI_GENERATE:
1169                 return NULL;
1170         }
1171
1172         ast_cli(a->fd, "%-16s %-12s %-12s %-10s %-10s %-10s\n" ,"CMTS", "Gate-Id","MTA", "Status", "AllocTime", "CheckTime");
1173         ast_cli(a->fd, "%-16s %-12s %-12s %-10s %-10s %-10s\n" ,"--------------" ,"----------", "----------", "--------", "--------", "--------\n");
1174         AST_LIST_LOCK(&cmts_list);
1175         AST_LIST_LOCK(&gate_list);
1176         AST_LIST_TRAVERSE(&gate_list, gate, list) {
1177                 if (gate->state == GATE_ALLOC_FAILED) {
1178                         ast_copy_string(state_desc, "Failed", sizeof(state_desc));
1179                 } else if (gate->state == GATE_ALLOC_PROGRESS) {
1180                         ast_copy_string(state_desc, "In Progress", sizeof(state_desc));
1181                 } else if (gate->state == GATE_ALLOCATED) {
1182                         ast_copy_string(state_desc, "Allocated", sizeof(state_desc));
1183                 } else if (gate->state == GATE_CLOSED) {
1184                         ast_copy_string(state_desc, "Closed", sizeof(state_desc));
1185                 } else if (gate->state == GATE_CLOSED_ERR) {
1186                         ast_copy_string(state_desc, "ClosedErr", sizeof(state_desc));
1187                 } else if (gate->state == GATE_OPEN) {
1188                         ast_copy_string(state_desc, "Open", sizeof(state_desc));
1189                 } else if (gate->state == GATE_DELETED) {
1190                         ast_copy_string(state_desc, "Deleted", sizeof(state_desc));
1191                 } else {
1192                         ast_copy_string(state_desc, "N/A", sizeof(state_desc));
1193                 }
1194                 
1195                 ast_cli(a->fd, "%-16s 0x%.8x   0x%08x   %-10s %10i %10i %u\n", (gate->cmts) ? gate->cmts->name : "null" , gate->gateid, gate->mta, 
1196                         state_desc, (int) (time(NULL) - gate->allocated), (gate->checked) ? (int) (time(NULL) - gate->checked) : 0, (unsigned int) gate->in_transaction);
1197         }
1198         AST_LIST_UNLOCK(&cmts_list);
1199         AST_LIST_UNLOCK(&gate_list);
1200         return CLI_SUCCESS;
1201 }
1202
1203 static char *pktccops_show_pools(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1204 {
1205         struct cops_ippool *ippool;
1206         char start[32];
1207         char stop[32];
1208
1209         switch(cmd) {
1210         case CLI_INIT:
1211                 e->command = "pktccops show pools";
1212                 e->usage = 
1213                         "Usage: pktccops show pools\n"
1214                         "       List PacketCable COPS ip pools of MTAs.\n";
1215
1216                 return NULL;
1217         case CLI_GENERATE:
1218                 return NULL;
1219         }
1220
1221         ast_cli(a->fd, "%-16s %-18s %-7s\n", "Start     ", "Stop      ", "CMTS    ");
1222         ast_cli(a->fd, "%-16s %-18s %-7s\n", "----------", "----------", "--------");
1223         AST_LIST_LOCK(&ippool_list);
1224         AST_LIST_TRAVERSE(&ippool_list, ippool, list) {
1225                 snprintf(start, sizeof(start), "%3u.%3u.%3u.%3u", ippool->start >> 24, (ippool->start >> 16) & 0x000000ff, (ippool->start >> 8) & 0x000000ff, ippool->start & 0x000000ff);
1226
1227                 snprintf(stop, sizeof(stop), "%3u.%3u.%3u.%3u", ippool->stop >> 24, (ippool->stop >> 16) & 0x000000ff, (ippool->stop >> 8) & 0x000000ff, ippool->stop & 0x000000ff);
1228                 ast_cli(a->fd, "%-16s %-18s %-16s\n", start, stop, ippool->cmts->name);
1229         }
1230         AST_LIST_UNLOCK(&ippool_list);
1231         return CLI_SUCCESS;
1232 }
1233
1234 static char *pktccops_gatedel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1235 {
1236         int found = 0;
1237         int trid;
1238         uint32_t gateid;
1239         struct cops_gate *gate;
1240         struct cops_cmts *cmts;
1241
1242         switch (cmd) {
1243         case CLI_INIT:
1244                 e->command = "pktccops gatedel";
1245                 e->usage = 
1246                         "Usage: pktccops gatedel <cmts> <gateid>\n"
1247                         "       Send Gate-Del to cmts.\n";
1248                 return NULL;
1249         case CLI_GENERATE:
1250                 return NULL;
1251         }
1252
1253         if (a->argc < 4)
1254                 return CLI_SHOWUSAGE;
1255
1256         AST_LIST_LOCK(&cmts_list);
1257         AST_LIST_TRAVERSE(&cmts_list, cmts, list) {
1258                 if (!strcmp(cmts->name, a->argv[2])) {
1259                         ast_cli(a->fd, "Found cmts: %s\n", cmts->name);
1260                         found = 1;
1261                         break;
1262                 }
1263         }
1264         AST_LIST_UNLOCK(&cmts_list);
1265         
1266         if (!found)
1267                 return CLI_SHOWUSAGE;
1268
1269         trid = cops_trid++;
1270         if (!sscanf(a->argv[3], "%x", &gateid)) {
1271                 ast_cli(a->fd, "bad gate specification (%s)\n", a->argv[3]);    
1272                 return CLI_SHOWUSAGE;
1273         }
1274
1275         found = 0;
1276         AST_LIST_LOCK(&gate_list);
1277         AST_LIST_TRAVERSE(&gate_list, gate, list) {
1278                 if (gate->gateid == gateid && gate->cmts == cmts) {
1279                         found = 1;
1280                         break;
1281                 }
1282         }
1283                 
1284         if (!found) {
1285                 ast_cli(a->fd, "gate not found: %s\n", a->argv[3]);
1286                 return CLI_SHOWUSAGE;
1287         }
1288
1289         AST_LIST_UNLOCK(&gate_list);
1290         cops_gate_cmd(GATE_DEL, cmts, trid, 0, 0, 0, 0, 0, 0, gate);
1291         return CLI_SUCCESS;
1292 }
1293
1294 static char *pktccops_gateset(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1295 {
1296         int foundcmts = 0;
1297         int trid;
1298         unsigned int an,bn,cn,dn;
1299         uint32_t mta, ssip;
1300         struct cops_cmts *cmts;
1301         struct cops_gate *gate;
1302
1303         switch (cmd) {
1304         case CLI_INIT:
1305                 e->command = "pktccops gateset";
1306                 e->usage = 
1307                         "Usage: pktccops gateset <cmts> <mta> <acctcount> <bitrate> <packet size> <switch ip> <switch port>\n"
1308                         "       Send Gate-Set to cmts.\n";
1309                 return NULL;
1310         case CLI_GENERATE:
1311                 return NULL;
1312         }
1313
1314         if (a->argc < 9)
1315                 return CLI_SHOWUSAGE;
1316
1317         if (!strncmp(a->argv[2], "null", sizeof(a->argv[2]))) {
1318                 cmts = NULL;
1319         } else {
1320                 AST_LIST_LOCK(&cmts_list);
1321                 AST_LIST_TRAVERSE(&cmts_list, cmts, list) {
1322                         if (!strcmp(cmts->name, a->argv[2])) {
1323                                 ast_cli(a->fd, "Found cmts: %s\n", cmts->name);
1324                                 foundcmts = 1;
1325                                 break;
1326                         }
1327                 }
1328                 AST_LIST_UNLOCK(&cmts_list);
1329                 if (!foundcmts) {
1330                         ast_cli(a->fd, "CMTS not found: %s\n", a->argv[2]);
1331                         return CLI_SHOWUSAGE;
1332                 }
1333         }
1334
1335         trid = cops_trid++;
1336         if (sscanf(a->argv[3], "%3u.%3u.%3u.%3u", &an, &bn, &cn, &dn) != 4) {
1337                 ast_cli(a->fd, "MTA specification (%s) does not look like an ipaddr\n", a->argv[3]);
1338                 return CLI_SHOWUSAGE;
1339         }
1340         mta = an << 24 | bn << 16 | cn << 8 | dn;
1341
1342         if (sscanf(a->argv[7], "%3u.%3u.%3u.%3u", &an, &bn, &cn, &dn) != 4) {
1343                 ast_cli(a->fd, "SSIP specification (%s) does not look like an ipaddr\n", a->argv[7]);
1344                 return CLI_SHOWUSAGE;
1345         }
1346         ssip = an << 24 | bn << 16 | cn << 8 | dn;
1347
1348         gate = cops_gate_cmd(GATE_SET, cmts, trid, mta, atoi(a->argv[4]), atof(a->argv[5]), atoi(a->argv[6]), ssip, atoi(a->argv[8]), NULL);
1349         return CLI_SUCCESS;
1350 }
1351
1352 static char *pktccops_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1353 {
1354         switch (cmd) {
1355         case CLI_INIT:
1356                 e->command = "pktccops set debug {on|off}";
1357                 e->usage = 
1358                         "Usage: pktccops set debug {on|off}\n"
1359                         "                               Turn on/off debuging\n";
1360                 return NULL;
1361         case CLI_GENERATE:
1362                 return NULL;
1363         }
1364
1365         if (a->argc != e->args)
1366                 return CLI_SHOWUSAGE;
1367         if (!strncasecmp(a->argv[e->args - 1], "on", 2)) {
1368                 pktccopsdebug = 1;
1369                 ast_cli(a->fd, "PktcCOPS Debugging Enabled\n");
1370         } else if (!strncasecmp(a->argv[e->args - 1], "off", 2)) {
1371                 pktccopsdebug = 0;
1372                 ast_cli(a->fd, "PktcCOPS Debugging Disabled\n");
1373         } else {
1374                 return CLI_SHOWUSAGE;
1375         }
1376         return CLI_SUCCESS;
1377
1378 }
1379
1380 static struct ast_cli_entry cli_pktccops[] = {
1381         AST_CLI_DEFINE(pktccops_show_cmtses, "List PacketCable COPS CMTSes"),
1382         AST_CLI_DEFINE(pktccops_show_gates, "List PacketCable COPS GATEs"),
1383         AST_CLI_DEFINE(pktccops_show_pools, "List PacketCable MTA pools"),
1384         AST_CLI_DEFINE(pktccops_gateset, "Send Gate-Set to cmts"),
1385         AST_CLI_DEFINE(pktccops_gatedel, "Send Gate-Det to cmts"),
1386         AST_CLI_DEFINE(pktccops_debug, "Enable/Disable COPS debugging")
1387 };
1388
1389 static int pktccops_add_ippool(struct cops_ippool *ippool)
1390 {
1391         if (ippool) {
1392                 AST_LIST_LOCK(&ippool_list);
1393                 AST_LIST_INSERT_HEAD(&ippool_list, ippool, list);
1394                 AST_LIST_UNLOCK(&ippool_list);
1395                 return 0;
1396         } else {
1397                 ast_log(LOG_WARNING, "Attempted to register NULL ippool?\n");
1398                 return -1;
1399         }
1400 }
1401
1402 static void pktccops_unregister_cmtses(void)
1403 {
1404         struct cops_cmts *cmts;
1405         struct cops_gate *gate;
1406         AST_LIST_LOCK(&cmts_list);
1407         while ((cmts = AST_LIST_REMOVE_HEAD(&cmts_list, list))) {
1408                 if (cmts->sfd > 0) {
1409                         close(cmts->sfd);
1410                 }
1411                 free(cmts);
1412         }
1413         AST_LIST_UNLOCK(&cmts_list);
1414
1415         AST_LIST_LOCK(&gate_list);
1416         while ((gate = AST_LIST_REMOVE_HEAD(&gate_list, list))) {
1417                 free(gate);
1418         }
1419         AST_LIST_UNLOCK(&gate_list);
1420 }
1421
1422 static void pktccops_unregister_ippools(void)
1423 {
1424         struct cops_ippool *ippool;
1425         AST_LIST_LOCK(&ippool_list);
1426         while ((ippool = AST_LIST_REMOVE_HEAD(&ippool_list, list))) {
1427                 free(ippool);
1428         }
1429         AST_LIST_UNLOCK(&ippool_list);
1430 }
1431
1432 static int load_module(void)
1433 {
1434         int res;
1435         AST_LIST_LOCK(&cmts_list);
1436         res = load_pktccops_config();
1437         AST_LIST_UNLOCK(&cmts_list);
1438         if (res == -1) {
1439                 return AST_MODULE_LOAD_DECLINE;
1440         }
1441         ast_cli_register_multiple(cli_pktccops, sizeof(cli_pktccops) / sizeof(struct ast_cli_entry));
1442         restart_pktc_thread();
1443         return 0;
1444 }
1445
1446 static int unload_module(void)
1447 {
1448         if (!ast_mutex_lock(&pktccops_lock)) {
1449                 if (pktccops_thread && (pktccops_thread != AST_PTHREADT_STOP)) {
1450                         pthread_cancel(pktccops_thread);
1451                         pthread_kill(pktccops_thread, SIGURG);
1452                         pthread_join(pktccops_thread, NULL);
1453                 }
1454                 pktccops_thread = AST_PTHREADT_STOP;
1455                 ast_mutex_unlock(&pktccops_lock);
1456         } else {
1457                 ast_log(LOG_WARNING, "Unable to lock the pktccops_thread\n");
1458                 return -1;
1459         }
1460
1461         ast_cli_unregister_multiple(cli_pktccops, sizeof(cli_pktccops) / sizeof(struct ast_cli_entry));
1462         pktccops_unregister_cmtses();
1463         pktccops_unregister_ippools();
1464         pktccops_thread = AST_PTHREADT_NULL;
1465         return 0;
1466 }
1467
1468 static int reload_module(void)
1469 {
1470         /* Prohibit unloading */
1471         if (pktcreload) {
1472                 ast_log(LOG_NOTICE, "Previous reload in progress, please wait!\n");
1473                 return -1;
1474         }
1475         pktcreload = 1;
1476         return 0;
1477 }
1478
1479 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "PktcCOPS manager",
1480                 .load = load_module,
1481                 .unload = unload_module,
1482                 .reload = reload_module,
1483                );
1484