00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049 #include "asterisk.h"
00050
00051 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 175347 $")
00052
00053 #include <sys/time.h>
00054 #include <signal.h>
00055 #include <fcntl.h>
00056
00057 #include "asterisk/udptl.h"
00058 #include "asterisk/frame.h"
00059 #include "asterisk/channel.h"
00060 #include "asterisk/acl.h"
00061 #include "asterisk/config.h"
00062 #include "asterisk/lock.h"
00063 #include "asterisk/utils.h"
00064 #include "asterisk/netsock.h"
00065 #include "asterisk/cli.h"
00066 #include "asterisk/unaligned.h"
00067
00068 #define UDPTL_MTU 1200
00069
00070 #if !defined(FALSE)
00071 #define FALSE 0
00072 #endif
00073 #if !defined(TRUE)
00074 #define TRUE (!FALSE)
00075 #endif
00076
00077 static int udptlstart = 4500;
00078 static int udptlend = 4599;
00079 static int udptldebug;
00080 static struct sockaddr_in udptldebugaddr;
00081 #ifdef SO_NO_CHECK
00082 static int nochecksums;
00083 #endif
00084 static int udptlfectype;
00085 static int udptlfecentries;
00086 static int udptlfecspan;
00087 static int udptlmaxdatagram;
00088
00089 #define LOCAL_FAX_MAX_DATAGRAM 1400
00090 #define MAX_FEC_ENTRIES 5
00091 #define MAX_FEC_SPAN 5
00092
00093 #define UDPTL_BUF_MASK 15
00094
00095 typedef struct {
00096 int buf_len;
00097 uint8_t buf[LOCAL_FAX_MAX_DATAGRAM];
00098 } udptl_fec_tx_buffer_t;
00099
00100 typedef struct {
00101 int buf_len;
00102 uint8_t buf[LOCAL_FAX_MAX_DATAGRAM];
00103 int fec_len[MAX_FEC_ENTRIES];
00104 uint8_t fec[MAX_FEC_ENTRIES][LOCAL_FAX_MAX_DATAGRAM];
00105 int fec_span;
00106 int fec_entries;
00107 } udptl_fec_rx_buffer_t;
00108
00109
00110 struct ast_udptl {
00111 int fd;
00112 char resp;
00113 struct ast_frame f[16];
00114 unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];
00115 unsigned int lasteventseqn;
00116 int nat;
00117 int flags;
00118 struct sockaddr_in us;
00119 struct sockaddr_in them;
00120 int *ioid;
00121 struct sched_context *sched;
00122 struct io_context *io;
00123 void *data;
00124 ast_udptl_callback callback;
00125 int udptl_offered_from_local;
00126
00127
00128
00129 int error_correction_scheme;
00130
00131
00132
00133 int error_correction_entries;
00134
00135
00136
00137 int error_correction_span;
00138
00139
00140
00141 int far_max_datagram_size;
00142
00143
00144
00145 int local_max_datagram_size;
00146
00147 int verbose;
00148
00149 struct sockaddr_in far;
00150
00151 int tx_seq_no;
00152 int rx_seq_no;
00153 int rx_expected_seq_no;
00154
00155 udptl_fec_tx_buffer_t tx[UDPTL_BUF_MASK + 1];
00156 udptl_fec_rx_buffer_t rx[UDPTL_BUF_MASK + 1];
00157 };
00158
00159 static AST_RWLIST_HEAD_STATIC(protos, ast_udptl_protocol);
00160
00161 static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len);
00162 static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, int buflen, uint8_t *ifp, int ifp_len);
00163
00164 static inline int udptl_debug_test_addr(struct sockaddr_in *addr)
00165 {
00166 if (udptldebug == 0)
00167 return 0;
00168 if (udptldebugaddr.sin_addr.s_addr) {
00169 if (((ntohs(udptldebugaddr.sin_port) != 0)
00170 && (udptldebugaddr.sin_port != addr->sin_port))
00171 || (udptldebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
00172 return 0;
00173 }
00174 return 1;
00175 }
00176
00177 static int decode_length(uint8_t *buf, int limit, int *len, int *pvalue)
00178 {
00179 if (*len >= limit)
00180 return -1;
00181 if ((buf[*len] & 0x80) == 0) {
00182 *pvalue = buf[*len];
00183 (*len)++;
00184 return 0;
00185 }
00186 if ((buf[*len] & 0x40) == 0) {
00187 if (*len == limit - 1)
00188 return -1;
00189 *pvalue = (buf[*len] & 0x3F) << 8;
00190 (*len)++;
00191 *pvalue |= buf[*len];
00192 (*len)++;
00193 return 0;
00194 }
00195 *pvalue = (buf[*len] & 0x3F) << 14;
00196 (*len)++;
00197
00198 return 1;
00199 }
00200
00201
00202 static int decode_open_type(uint8_t *buf, int limit, int *len, const uint8_t **p_object, int *p_num_octets)
00203 {
00204 int octet_cnt;
00205 int octet_idx;
00206 int stat;
00207 int i;
00208 const uint8_t **pbuf;
00209
00210 for (octet_idx = 0, *p_num_octets = 0; ; octet_idx += octet_cnt) {
00211 if ((stat = decode_length(buf, limit, len, &octet_cnt)) < 0)
00212 return -1;
00213 if (octet_cnt > 0) {
00214 *p_num_octets += octet_cnt;
00215
00216 pbuf = &p_object[octet_idx];
00217 i = 0;
00218
00219 if ((*len + octet_cnt) > limit)
00220 return -1;
00221
00222 *pbuf = &buf[*len];
00223 *len += octet_cnt;
00224 }
00225 if (stat == 0)
00226 break;
00227 }
00228 return 0;
00229 }
00230
00231
00232 static int encode_length(uint8_t *buf, int *len, int value)
00233 {
00234 int multiplier;
00235
00236 if (value < 0x80) {
00237
00238 buf[*len] = value;
00239 (*len)++;
00240 return value;
00241 }
00242 if (value < 0x4000) {
00243
00244
00245 buf[*len] = ((0x8000 | value) >> 8) & 0xFF;
00246 (*len)++;
00247 buf[*len] = value & 0xFF;
00248 (*len)++;
00249 return value;
00250 }
00251
00252 multiplier = (value < 0x10000) ? (value >> 14) : 4;
00253
00254 buf[*len] = 0xC0 | multiplier;
00255 (*len)++;
00256 return multiplier << 14;
00257 }
00258
00259
00260 static int encode_open_type(uint8_t *buf, int buflen, int *len, const uint8_t *data, int num_octets)
00261 {
00262 int enclen;
00263 int octet_idx;
00264 uint8_t zero_byte;
00265
00266
00267 if (num_octets == 0) {
00268 zero_byte = 0;
00269 data = &zero_byte;
00270 num_octets = 1;
00271 }
00272
00273 for (octet_idx = 0; ; num_octets -= enclen, octet_idx += enclen) {
00274 if ((enclen = encode_length(buf, len, num_octets)) < 0)
00275 return -1;
00276 if (enclen + *len > buflen) {
00277 ast_log(LOG_ERROR, "Buffer overflow detected (%d + %d > %d)\n", enclen, *len, buflen);
00278 return -1;
00279 }
00280 if (enclen > 0) {
00281 memcpy(&buf[*len], &data[octet_idx], enclen);
00282 *len += enclen;
00283 }
00284 if (enclen >= num_octets)
00285 break;
00286 }
00287
00288 return 0;
00289 }
00290
00291
00292 static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len)
00293 {
00294 int stat;
00295 int stat2;
00296 int i;
00297 int j;
00298 int k;
00299 int l;
00300 int m;
00301 int x;
00302 int limit;
00303 int which;
00304 int ptr;
00305 int count;
00306 int total_count;
00307 int seq_no;
00308 const uint8_t *ifp;
00309 const uint8_t *data;
00310 int ifp_len;
00311 int repaired[16];
00312 const uint8_t *bufs[16];
00313 int lengths[16];
00314 int span;
00315 int entries;
00316 int ifp_no;
00317
00318 ptr = 0;
00319 ifp_no = 0;
00320 memset(&s->f[0], 0, sizeof(s->f[0]));
00321
00322
00323 if (ptr + 2 > len)
00324 return -1;
00325 seq_no = (buf[0] << 8) | buf[1];
00326 ptr += 2;
00327
00328
00329 if ((stat = decode_open_type(buf, len, &ptr, &ifp, &ifp_len)) != 0)
00330 return -1;
00331
00332 if (ptr + 1 > len)
00333 return -1;
00334 if ((buf[ptr++] & 0x80) == 0) {
00335
00336 if (seq_no > s->rx_seq_no) {
00337
00338
00339 total_count = 0;
00340 do {
00341 if ((stat2 = decode_length(buf, len, &ptr, &count)) < 0)
00342 return -1;
00343 for (i = 0; i < count; i++) {
00344 if ((stat = decode_open_type(buf, len, &ptr, &bufs[total_count + i], &lengths[total_count + i])) != 0)
00345 return -1;
00346 }
00347 total_count += count;
00348 }
00349 while (stat2 > 0);
00350
00351 for (i = total_count; i > 0; i--) {
00352 if (seq_no - i >= s->rx_seq_no) {
00353
00354
00355
00356 s->f[ifp_no].frametype = AST_FRAME_MODEM;
00357 s->f[ifp_no].subclass = AST_MODEM_T38;
00358
00359 s->f[ifp_no].mallocd = 0;
00360 s->f[ifp_no].seqno = seq_no - i;
00361 s->f[ifp_no].datalen = lengths[i - 1];
00362 s->f[ifp_no].data = (uint8_t *) bufs[i - 1];
00363 s->f[ifp_no].offset = 0;
00364 s->f[ifp_no].src = "UDPTL";
00365 if (ifp_no > 0)
00366 AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
00367 AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
00368 ifp_no++;
00369 }
00370 }
00371 }
00372 }
00373 else
00374 {
00375
00376
00377 if (ifp_len > LOCAL_FAX_MAX_DATAGRAM)
00378 return -1;
00379
00380 for ( ; seq_no > s->rx_seq_no; s->rx_seq_no++) {
00381 x = s->rx_seq_no & UDPTL_BUF_MASK;
00382 s->rx[x].buf_len = -1;
00383 s->rx[x].fec_len[0] = 0;
00384 s->rx[x].fec_span = 0;
00385 s->rx[x].fec_entries = 0;
00386 }
00387
00388 x = seq_no & UDPTL_BUF_MASK;
00389
00390 memset(repaired, 0, sizeof(repaired));
00391
00392
00393 memcpy(s->rx[x].buf, ifp, ifp_len);
00394 s->rx[x].buf_len = ifp_len;
00395 repaired[x] = TRUE;
00396
00397
00398
00399
00400 if (ptr + 2 > len)
00401 return -1;
00402 if (buf[ptr++] != 1)
00403 return -1;
00404 span = buf[ptr++];
00405 s->rx[x].fec_span = span;
00406
00407
00408
00409 if (ptr + 1 > len)
00410 return -1;
00411 entries = buf[ptr++];
00412 s->rx[x].fec_entries = entries;
00413
00414
00415 for (i = 0; i < entries; i++) {
00416 if ((stat = decode_open_type(buf, len, &ptr, &data, &s->rx[x].fec_len[i])) != 0)
00417 return -1;
00418 if (s->rx[x].fec_len[i] > LOCAL_FAX_MAX_DATAGRAM)
00419 return -1;
00420
00421
00422 memcpy(s->rx[x].fec[i], data, s->rx[x].fec_len[i]);
00423 #if 0
00424 fprintf(stderr, "FEC: ");
00425 for (j = 0; j < s->rx[x].fec_len[i]; j++)
00426 fprintf(stderr, "%02X ", data[j]);
00427 fprintf(stderr, "\n");
00428 #endif
00429 }
00430
00431
00432
00433 for (l = x; l != ((x - (16 - span*entries)) & UDPTL_BUF_MASK); l = (l - 1) & UDPTL_BUF_MASK) {
00434 if (s->rx[l].fec_len[0] <= 0)
00435 continue;
00436 for (m = 0; m < s->rx[l].fec_entries; m++) {
00437 limit = (l + m) & UDPTL_BUF_MASK;
00438 for (which = -1, k = (limit - s->rx[l].fec_span * s->rx[l].fec_entries) & UDPTL_BUF_MASK; k != limit; k = (k + s->rx[l].fec_entries) & UDPTL_BUF_MASK) {
00439 if (s->rx[k].buf_len <= 0)
00440 which = (which == -1) ? k : -2;
00441 }
00442 if (which >= 0) {
00443
00444 for (j = 0; j < s->rx[l].fec_len[m]; j++) {
00445 s->rx[which].buf[j] = s->rx[l].fec[m][j];
00446 for (k = (limit - s->rx[l].fec_span * s->rx[l].fec_entries) & UDPTL_BUF_MASK; k != limit; k = (k + s->rx[l].fec_entries) & UDPTL_BUF_MASK)
00447 s->rx[which].buf[j] ^= (s->rx[k].buf_len > j) ? s->rx[k].buf[j] : 0;
00448 }
00449 s->rx[which].buf_len = s->rx[l].fec_len[m];
00450 repaired[which] = TRUE;
00451 }
00452 }
00453 }
00454
00455 for (l = (x + 1) & UDPTL_BUF_MASK, j = seq_no - UDPTL_BUF_MASK; l != x; l = (l + 1) & UDPTL_BUF_MASK, j++) {
00456 if (repaired[l]) {
00457
00458 s->f[ifp_no].frametype = AST_FRAME_MODEM;
00459 s->f[ifp_no].subclass = AST_MODEM_T38;
00460
00461 s->f[ifp_no].mallocd = 0;
00462 s->f[ifp_no].seqno = j;
00463 s->f[ifp_no].datalen = s->rx[l].buf_len;
00464 s->f[ifp_no].data = s->rx[l].buf;
00465 s->f[ifp_no].offset = 0;
00466 s->f[ifp_no].src = "UDPTL";
00467 if (ifp_no > 0)
00468 AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
00469 AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
00470 ifp_no++;
00471 }
00472 }
00473 }
00474
00475
00476
00477 if (seq_no >= s->rx_seq_no) {
00478
00479 s->f[ifp_no].frametype = AST_FRAME_MODEM;
00480 s->f[ifp_no].subclass = AST_MODEM_T38;
00481
00482 s->f[ifp_no].mallocd = 0;
00483 s->f[ifp_no].seqno = seq_no;
00484 s->f[ifp_no].datalen = ifp_len;
00485 s->f[ifp_no].data = (uint8_t *) ifp;
00486 s->f[ifp_no].offset = 0;
00487 s->f[ifp_no].src = "UDPTL";
00488 if (ifp_no > 0)
00489 AST_LIST_NEXT(&s->f[ifp_no - 1], frame_list) = &s->f[ifp_no];
00490 AST_LIST_NEXT(&s->f[ifp_no], frame_list) = NULL;
00491
00492 ifp_no++;
00493 }
00494
00495 s->rx_seq_no = seq_no + 1;
00496 return ifp_no;
00497 }
00498
00499
00500 static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, int buflen, uint8_t *ifp, int ifp_len)
00501 {
00502 uint8_t fec[LOCAL_FAX_MAX_DATAGRAM * 2];
00503 int i;
00504 int j;
00505 int seq;
00506 int entry;
00507 int entries;
00508 int span;
00509 int m;
00510 int len;
00511 int limit;
00512 int high_tide;
00513
00514 seq = s->tx_seq_no & 0xFFFF;
00515
00516
00517 entry = seq & UDPTL_BUF_MASK;
00518
00519
00520
00521 s->tx[entry].buf_len = ifp_len;
00522 memcpy(s->tx[entry].buf, ifp, ifp_len);
00523
00524
00525
00526 len = 0;
00527
00528 buf[len++] = (seq >> 8) & 0xFF;
00529 buf[len++] = seq & 0xFF;
00530
00531
00532 if (encode_open_type(buf, buflen, &len, ifp, ifp_len) < 0)
00533 return -1;
00534
00535
00536 switch (s->error_correction_scheme)
00537 {
00538 case UDPTL_ERROR_CORRECTION_NONE:
00539
00540 buf[len++] = 0x00;
00541
00542
00543 if (encode_length(buf, &len, 0) < 0)
00544 return -1;
00545 break;
00546 case UDPTL_ERROR_CORRECTION_REDUNDANCY:
00547
00548 buf[len++] = 0x00;
00549 if (s->tx_seq_no > s->error_correction_entries)
00550 entries = s->error_correction_entries;
00551 else
00552 entries = s->tx_seq_no;
00553
00554
00555 if (encode_length(buf, &len, entries) < 0)
00556 return -1;
00557
00558 for (i = 0; i < entries; i++) {
00559 j = (entry - i - 1) & UDPTL_BUF_MASK;
00560 if (encode_open_type(buf, buflen, &len, s->tx[j].buf, s->tx[j].buf_len) < 0) {
00561 if (option_debug) {
00562 ast_log(LOG_DEBUG, "Encoding failed at i=%d, j=%d\n", i, j);
00563 }
00564 return -1;
00565 }
00566 }
00567 break;
00568 case UDPTL_ERROR_CORRECTION_FEC:
00569 span = s->error_correction_span;
00570 entries = s->error_correction_entries;
00571 if (seq < s->error_correction_span*s->error_correction_entries) {
00572
00573 entries = seq/s->error_correction_span;
00574 if (seq < s->error_correction_span)
00575 span = 0;
00576 }
00577
00578 buf[len++] = 0x80;
00579
00580
00581 buf[len++] = 1;
00582 buf[len++] = span;
00583
00584
00585 buf[len++] = entries;
00586 for (m = 0; m < entries; m++) {
00587
00588 limit = (entry + m) & UDPTL_BUF_MASK;
00589 high_tide = 0;
00590 for (i = (limit - span*entries) & UDPTL_BUF_MASK; i != limit; i = (i + entries) & UDPTL_BUF_MASK) {
00591 if (high_tide < s->tx[i].buf_len) {
00592 for (j = 0; j < high_tide; j++)
00593 fec[j] ^= s->tx[i].buf[j];
00594 for ( ; j < s->tx[i].buf_len; j++)
00595 fec[j] = s->tx[i].buf[j];
00596 high_tide = s->tx[i].buf_len;
00597 } else {
00598 for (j = 0; j < s->tx[i].buf_len; j++)
00599 fec[j] ^= s->tx[i].buf[j];
00600 }
00601 }
00602 if (encode_open_type(buf, buflen, &len, fec, high_tide) < 0)
00603 return -1;
00604 }
00605 break;
00606 }
00607
00608 if (s->verbose)
00609 fprintf(stderr, "\n");
00610
00611 s->tx_seq_no++;
00612 return len;
00613 }
00614
00615 int ast_udptl_fd(struct ast_udptl *udptl)
00616 {
00617 return udptl->fd;
00618 }
00619
00620 void ast_udptl_set_data(struct ast_udptl *udptl, void *data)
00621 {
00622 udptl->data = data;
00623 }
00624
00625 void ast_udptl_set_callback(struct ast_udptl *udptl, ast_udptl_callback callback)
00626 {
00627 udptl->callback = callback;
00628 }
00629
00630 void ast_udptl_setnat(struct ast_udptl *udptl, int nat)
00631 {
00632 udptl->nat = nat;
00633 }
00634
00635 static int udptlread(int *id, int fd, short events, void *cbdata)
00636 {
00637 struct ast_udptl *udptl = cbdata;
00638 struct ast_frame *f;
00639
00640 if ((f = ast_udptl_read(udptl))) {
00641 if (udptl->callback)
00642 udptl->callback(udptl, f, udptl->data);
00643 }
00644 return 1;
00645 }
00646
00647 struct ast_frame *ast_udptl_read(struct ast_udptl *udptl)
00648 {
00649 int res;
00650 struct sockaddr_in sin;
00651 socklen_t len;
00652 uint16_t seqno = 0;
00653 uint16_t *udptlheader;
00654
00655 len = sizeof(sin);
00656
00657
00658 res = recvfrom(udptl->fd,
00659 udptl->rawdata + AST_FRIENDLY_OFFSET,
00660 sizeof(udptl->rawdata) - AST_FRIENDLY_OFFSET,
00661 0,
00662 (struct sockaddr *) &sin,
00663 &len);
00664 udptlheader = (uint16_t *)(udptl->rawdata + AST_FRIENDLY_OFFSET);
00665 if (res < 0) {
00666 if (errno != EAGAIN)
00667 ast_log(LOG_WARNING, "UDPTL read error: %s\n", strerror(errno));
00668 ast_assert(errno != EBADF);
00669 return &ast_null_frame;
00670 }
00671
00672
00673 if (!udptl->them.sin_addr.s_addr || !udptl->them.sin_port)
00674 return &ast_null_frame;
00675
00676 if (udptl->nat) {
00677
00678 if ((udptl->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
00679 (udptl->them.sin_port != sin.sin_port)) {
00680 memcpy(&udptl->them, &sin, sizeof(udptl->them));
00681 ast_debug(1, "UDPTL NAT: Using address %s:%d\n", ast_inet_ntoa(udptl->them.sin_addr), ntohs(udptl->them.sin_port));
00682 }
00683 }
00684
00685 if (udptl_debug_test_addr(&sin)) {
00686 ast_verb(1, "Got UDPTL packet from %s:%d (type %d, seq %d, len %d)\n",
00687 ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), 0, seqno, res);
00688 }
00689 #if 0
00690 printf("Got UDPTL packet from %s:%d (seq %d, len = %d)\n", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), seqno, res);
00691 #endif
00692 if (udptl_rx_packet(udptl, udptl->rawdata + AST_FRIENDLY_OFFSET, res) < 1)
00693 return &ast_null_frame;
00694
00695 return &udptl->f[0];
00696 }
00697
00698 void ast_udptl_offered_from_local(struct ast_udptl* udptl, int local)
00699 {
00700 if (udptl)
00701 udptl->udptl_offered_from_local = local;
00702 else
00703 ast_log(LOG_WARNING, "udptl structure is null\n");
00704 }
00705
00706 int ast_udptl_get_error_correction_scheme(struct ast_udptl* udptl)
00707 {
00708 if (udptl)
00709 return udptl->error_correction_scheme;
00710 else {
00711 ast_log(LOG_WARNING, "udptl structure is null\n");
00712 return -1;
00713 }
00714 }
00715
00716 void ast_udptl_set_error_correction_scheme(struct ast_udptl* udptl, int ec)
00717 {
00718 if (udptl) {
00719 switch (ec) {
00720 case UDPTL_ERROR_CORRECTION_FEC:
00721 udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_FEC;
00722 break;
00723 case UDPTL_ERROR_CORRECTION_REDUNDANCY:
00724 udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_REDUNDANCY;
00725 break;
00726 case UDPTL_ERROR_CORRECTION_NONE:
00727 udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_NONE;
00728 break;
00729 default:
00730 ast_log(LOG_WARNING, "error correction parameter invalid\n");
00731 };
00732 } else
00733 ast_log(LOG_WARNING, "udptl structure is null\n");
00734 }
00735
00736 int ast_udptl_get_local_max_datagram(struct ast_udptl* udptl)
00737 {
00738 if (udptl)
00739 return udptl->local_max_datagram_size;
00740 else {
00741 ast_log(LOG_WARNING, "udptl structure is null\n");
00742 return -1;
00743 }
00744 }
00745
00746 int ast_udptl_get_far_max_datagram(struct ast_udptl* udptl)
00747 {
00748 if (udptl)
00749 return udptl->far_max_datagram_size;
00750 else {
00751 ast_log(LOG_WARNING, "udptl structure is null\n");
00752 return -1;
00753 }
00754 }
00755
00756 void ast_udptl_set_local_max_datagram(struct ast_udptl* udptl, int max_datagram)
00757 {
00758 if (udptl)
00759 udptl->local_max_datagram_size = max_datagram;
00760 else
00761 ast_log(LOG_WARNING, "udptl structure is null\n");
00762 }
00763
00764 void ast_udptl_set_far_max_datagram(struct ast_udptl* udptl, int max_datagram)
00765 {
00766 if (udptl)
00767 udptl->far_max_datagram_size = max_datagram;
00768 else
00769 ast_log(LOG_WARNING, "udptl structure is null\n");
00770 }
00771
00772 struct ast_udptl *ast_udptl_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int callbackmode, struct in_addr addr)
00773 {
00774 struct ast_udptl *udptl;
00775 int x;
00776 int startplace;
00777 int i;
00778 long int flags;
00779
00780 if (!(udptl = ast_calloc(1, sizeof(*udptl))))
00781 return NULL;
00782
00783 if (udptlfectype == 2)
00784 udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_FEC;
00785 else if (udptlfectype == 1)
00786 udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_REDUNDANCY;
00787 else
00788 udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_NONE;
00789 udptl->error_correction_span = udptlfecspan;
00790 udptl->error_correction_entries = udptlfecentries;
00791
00792 udptl->far_max_datagram_size = udptlmaxdatagram;
00793 udptl->local_max_datagram_size = udptlmaxdatagram;
00794
00795 memset(&udptl->rx, 0, sizeof(udptl->rx));
00796 memset(&udptl->tx, 0, sizeof(udptl->tx));
00797 for (i = 0; i <= UDPTL_BUF_MASK; i++) {
00798 udptl->rx[i].buf_len = -1;
00799 udptl->tx[i].buf_len = -1;
00800 }
00801
00802 udptl->them.sin_family = AF_INET;
00803 udptl->us.sin_family = AF_INET;
00804
00805 if ((udptl->fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
00806 ast_free(udptl);
00807 ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
00808 return NULL;
00809 }
00810 flags = fcntl(udptl->fd, F_GETFL);
00811 fcntl(udptl->fd, F_SETFL, flags | O_NONBLOCK);
00812 #ifdef SO_NO_CHECK
00813 if (nochecksums)
00814 setsockopt(udptl->fd, SOL_SOCKET, SO_NO_CHECK, &nochecksums, sizeof(nochecksums));
00815 #endif
00816
00817 x = (udptlstart == udptlend) ? udptlstart : (ast_random() % (udptlend - udptlstart)) + udptlstart;
00818 startplace = x;
00819 for (;;) {
00820 udptl->us.sin_port = htons(x);
00821 udptl->us.sin_addr = addr;
00822 if (bind(udptl->fd, (struct sockaddr *) &udptl->us, sizeof(udptl->us)) == 0)
00823 break;
00824 if (errno != EADDRINUSE) {
00825 ast_log(LOG_WARNING, "Unexpected bind error: %s\n", strerror(errno));
00826 close(udptl->fd);
00827 ast_free(udptl);
00828 return NULL;
00829 }
00830 if (++x > udptlend)
00831 x = udptlstart;
00832 if (x == startplace) {
00833 ast_log(LOG_WARNING, "No UDPTL ports remaining\n");
00834 close(udptl->fd);
00835 ast_free(udptl);
00836 return NULL;
00837 }
00838 }
00839 if (io && sched && callbackmode) {
00840
00841 udptl->sched = sched;
00842 udptl->io = io;
00843 udptl->ioid = ast_io_add(udptl->io, udptl->fd, udptlread, AST_IO_IN, udptl);
00844 }
00845 return udptl;
00846 }
00847
00848 struct ast_udptl *ast_udptl_new(struct sched_context *sched, struct io_context *io, int callbackmode)
00849 {
00850 struct in_addr ia;
00851 memset(&ia, 0, sizeof(ia));
00852 return ast_udptl_new_with_bindaddr(sched, io, callbackmode, ia);
00853 }
00854
00855 int ast_udptl_setqos(struct ast_udptl *udptl, int tos, int cos)
00856 {
00857 return ast_netsock_set_qos(udptl->fd, tos, cos, "UDPTL");
00858 }
00859
00860 void ast_udptl_set_peer(struct ast_udptl *udptl, struct sockaddr_in *them)
00861 {
00862 udptl->them.sin_port = them->sin_port;
00863 udptl->them.sin_addr = them->sin_addr;
00864 }
00865
00866 void ast_udptl_get_peer(struct ast_udptl *udptl, struct sockaddr_in *them)
00867 {
00868 memset(them, 0, sizeof(*them));
00869 them->sin_family = AF_INET;
00870 them->sin_port = udptl->them.sin_port;
00871 them->sin_addr = udptl->them.sin_addr;
00872 }
00873
00874 void ast_udptl_get_us(struct ast_udptl *udptl, struct sockaddr_in *us)
00875 {
00876 memcpy(us, &udptl->us, sizeof(udptl->us));
00877 }
00878
00879 void ast_udptl_stop(struct ast_udptl *udptl)
00880 {
00881 memset(&udptl->them.sin_addr, 0, sizeof(udptl->them.sin_addr));
00882 memset(&udptl->them.sin_port, 0, sizeof(udptl->them.sin_port));
00883 }
00884
00885 void ast_udptl_destroy(struct ast_udptl *udptl)
00886 {
00887 if (udptl->ioid)
00888 ast_io_remove(udptl->io, udptl->ioid);
00889 if (udptl->fd > -1)
00890 close(udptl->fd);
00891 ast_free(udptl);
00892 }
00893
00894 int ast_udptl_write(struct ast_udptl *s, struct ast_frame *f)
00895 {
00896 int seq;
00897 int len;
00898 int res;
00899 uint8_t buf[LOCAL_FAX_MAX_DATAGRAM * 2];
00900
00901
00902 if (s->them.sin_addr.s_addr == INADDR_ANY)
00903 return 0;
00904
00905
00906 if (f->datalen == 0)
00907 return 0;
00908
00909 if (f->frametype != AST_FRAME_MODEM) {
00910 ast_log(LOG_WARNING, "UDPTL can only send T.38 data\n");
00911 return -1;
00912 }
00913
00914
00915 seq = s->tx_seq_no & 0xFFFF;
00916
00917
00918 len = udptl_build_packet(s, buf, sizeof(buf), f->data, f->datalen);
00919
00920 if (len > 0 && s->them.sin_port && s->them.sin_addr.s_addr) {
00921 if ((res = sendto(s->fd, buf, len, 0, (struct sockaddr *) &s->them, sizeof(s->them))) < 0)
00922 ast_log(LOG_NOTICE, "UDPTL Transmission error to %s:%d: %s\n", ast_inet_ntoa(s->them.sin_addr), ntohs(s->them.sin_port), strerror(errno));
00923 #if 0
00924 printf("Sent %d bytes of UDPTL data to %s:%d\n", res, ast_inet_ntoa(udptl->them.sin_addr), ntohs(udptl->them.sin_port));
00925 #endif
00926 if (udptl_debug_test_addr(&s->them))
00927 ast_verb(1, "Sent UDPTL packet to %s:%d (type %d, seq %d, len %d)\n",
00928 ast_inet_ntoa(s->them.sin_addr),
00929 ntohs(s->them.sin_port), 0, seq, len);
00930 }
00931
00932 return 0;
00933 }
00934
00935 void ast_udptl_proto_unregister(struct ast_udptl_protocol *proto)
00936 {
00937 AST_RWLIST_WRLOCK(&protos);
00938 AST_RWLIST_REMOVE(&protos, proto, list);
00939 AST_RWLIST_UNLOCK(&protos);
00940 }
00941
00942 int ast_udptl_proto_register(struct ast_udptl_protocol *proto)
00943 {
00944 struct ast_udptl_protocol *cur;
00945
00946 AST_RWLIST_WRLOCK(&protos);
00947 AST_RWLIST_TRAVERSE(&protos, cur, list) {
00948 if (cur->type == proto->type) {
00949 ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type);
00950 AST_RWLIST_UNLOCK(&protos);
00951 return -1;
00952 }
00953 }
00954 AST_RWLIST_INSERT_TAIL(&protos, proto, list);
00955 AST_RWLIST_UNLOCK(&protos);
00956 return 0;
00957 }
00958
00959 static struct ast_udptl_protocol *get_proto(struct ast_channel *chan)
00960 {
00961 struct ast_udptl_protocol *cur = NULL;
00962
00963 AST_RWLIST_RDLOCK(&protos);
00964 AST_RWLIST_TRAVERSE(&protos, cur, list) {
00965 if (cur->type == chan->tech->type)
00966 break;
00967 }
00968 AST_RWLIST_UNLOCK(&protos);
00969
00970 return cur;
00971 }
00972
00973 int ast_udptl_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
00974 {
00975 struct ast_frame *f;
00976 struct ast_channel *who;
00977 struct ast_channel *cs[3];
00978 struct ast_udptl *p0;
00979 struct ast_udptl *p1;
00980 struct ast_udptl_protocol *pr0;
00981 struct ast_udptl_protocol *pr1;
00982 struct sockaddr_in ac0;
00983 struct sockaddr_in ac1;
00984 struct sockaddr_in t0;
00985 struct sockaddr_in t1;
00986 void *pvt0;
00987 void *pvt1;
00988 int to;
00989
00990 ast_channel_lock(c0);
00991 while (ast_channel_trylock(c1)) {
00992 ast_channel_unlock(c0);
00993 usleep(1);
00994 ast_channel_lock(c0);
00995 }
00996 pr0 = get_proto(c0);
00997 pr1 = get_proto(c1);
00998 if (!pr0) {
00999 ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
01000 ast_channel_unlock(c0);
01001 ast_channel_unlock(c1);
01002 return -1;
01003 }
01004 if (!pr1) {
01005 ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
01006 ast_channel_unlock(c0);
01007 ast_channel_unlock(c1);
01008 return -1;
01009 }
01010 pvt0 = c0->tech_pvt;
01011 pvt1 = c1->tech_pvt;
01012 p0 = pr0->get_udptl_info(c0);
01013 p1 = pr1->get_udptl_info(c1);
01014 if (!p0 || !p1) {
01015
01016 ast_channel_unlock(c0);
01017 ast_channel_unlock(c1);
01018 return -2;
01019 }
01020 if (pr0->set_udptl_peer(c0, p1)) {
01021 ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name);
01022 memset(&ac1, 0, sizeof(ac1));
01023 } else {
01024
01025 ast_udptl_get_peer(p1, &ac1);
01026 }
01027 if (pr1->set_udptl_peer(c1, p0)) {
01028 ast_log(LOG_WARNING, "Channel '%s' failed to talk back to '%s'\n", c1->name, c0->name);
01029 memset(&ac0, 0, sizeof(ac0));
01030 } else {
01031
01032 ast_udptl_get_peer(p0, &ac0);
01033 }
01034 ast_channel_unlock(c0);
01035 ast_channel_unlock(c1);
01036 cs[0] = c0;
01037 cs[1] = c1;
01038 cs[2] = NULL;
01039 for (;;) {
01040 if ((c0->tech_pvt != pvt0) ||
01041 (c1->tech_pvt != pvt1) ||
01042 (c0->masq || c0->masqr || c1->masq || c1->masqr)) {
01043 ast_debug(1, "Oooh, something is weird, backing out\n");
01044
01045 return -3;
01046 }
01047 to = -1;
01048 ast_udptl_get_peer(p1, &t1);
01049 ast_udptl_get_peer(p0, &t0);
01050 if (inaddrcmp(&t1, &ac1)) {
01051 ast_debug(1, "Oooh, '%s' changed end address to %s:%d\n",
01052 c1->name, ast_inet_ntoa(t1.sin_addr), ntohs(t1.sin_port));
01053 ast_debug(1, "Oooh, '%s' was %s:%d\n",
01054 c1->name, ast_inet_ntoa(ac1.sin_addr), ntohs(ac1.sin_port));
01055 memcpy(&ac1, &t1, sizeof(ac1));
01056 }
01057 if (inaddrcmp(&t0, &ac0)) {
01058 ast_debug(1, "Oooh, '%s' changed end address to %s:%d\n",
01059 c0->name, ast_inet_ntoa(t0.sin_addr), ntohs(t0.sin_port));
01060 ast_debug(1, "Oooh, '%s' was %s:%d\n",
01061 c0->name, ast_inet_ntoa(ac0.sin_addr), ntohs(ac0.sin_port));
01062 memcpy(&ac0, &t0, sizeof(ac0));
01063 }
01064 who = ast_waitfor_n(cs, 2, &to);
01065 if (!who) {
01066 ast_debug(1, "Ooh, empty read...\n");
01067
01068 if (ast_check_hangup(c0) || ast_check_hangup(c1))
01069 break;
01070 continue;
01071 }
01072 f = ast_read(who);
01073 if (!f) {
01074 *fo = f;
01075 *rc = who;
01076 ast_debug(1, "Oooh, got a %s\n", f ? "digit" : "hangup");
01077
01078 return 0;
01079 } else {
01080 if (f->frametype == AST_FRAME_MODEM) {
01081
01082 if (who == c0) {
01083 ast_write(c1, f);
01084 } else if (who == c1) {
01085 ast_write(c0, f);
01086 }
01087 }
01088 ast_frfree(f);
01089 }
01090
01091 cs[2] = cs[0];
01092 cs[0] = cs[1];
01093 cs[1] = cs[2];
01094 }
01095 return -1;
01096 }
01097
01098 static char *handle_cli_udptl_debug_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01099 {
01100 struct hostent *hp;
01101 struct ast_hostent ahp;
01102 int port;
01103 char *p;
01104 char *arg;
01105
01106 switch (cmd) {
01107 case CLI_INIT:
01108 e->command = "udptl debug [off|ip]";
01109 e->usage =
01110 "Usage: udptl debug [off]|[ip host[:port]]\n"
01111 " Enable or disable dumping of UDPTL packets.\n"
01112 " If ip is specified, limit the dumped packets to those to and from\n"
01113 " the specified 'host' with optional port.\n";
01114 return NULL;
01115 case CLI_GENERATE:
01116 return NULL;
01117 }
01118
01119 if (a->argc < 2 || a->argc > 4)
01120 return CLI_SHOWUSAGE;
01121
01122 if (a->argc == 2) {
01123 udptldebug = 1;
01124 memset(&udptldebugaddr, 0, sizeof(udptldebugaddr));
01125 ast_cli(a->fd, "UDPTL Debugging Enabled\n");
01126 } else if (a->argc == 3) {
01127 if (strncasecmp(a->argv[2], "off", 3))
01128 return CLI_SHOWUSAGE;
01129 udptldebug = 0;
01130 ast_cli(a->fd, "UDPTL Debugging Disabled\n");
01131 } else {
01132 if (strncasecmp(a->argv[2], "ip", 2))
01133 return CLI_SHOWUSAGE;
01134 port = 0;
01135 arg = a->argv[3];
01136 p = strstr(arg, ":");
01137 if (p) {
01138 *p = '\0';
01139 p++;
01140 port = atoi(p);
01141 }
01142 hp = ast_gethostbyname(arg, &ahp);
01143 if (hp == NULL)
01144 return CLI_SHOWUSAGE;
01145 udptldebugaddr.sin_family = AF_INET;
01146 memcpy(&udptldebugaddr.sin_addr, hp->h_addr, sizeof(udptldebugaddr.sin_addr));
01147 udptldebugaddr.sin_port = htons(port);
01148 if (port == 0)
01149 ast_cli(a->fd, "UDPTL Debugging Enabled for IP: %s\n", ast_inet_ntoa(udptldebugaddr.sin_addr));
01150 else
01151 ast_cli(a->fd, "UDPTL Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(udptldebugaddr.sin_addr), port);
01152 udptldebug = 1;
01153 }
01154
01155 return CLI_SUCCESS;
01156 }
01157
01158 static char *handle_cli_udptl_set_debug(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
01159 {
01160 struct hostent *hp;
01161 struct ast_hostent ahp;
01162 int port;
01163 char *p;
01164 char *arg;
01165
01166 switch (cmd) {
01167 case CLI_INIT:
01168 e->command = "udptl set debug {on|off|ip}";
01169 e->usage =
01170 "Usage: udptl set debug {on|off|ip host[:port]}\n"
01171 " Enable or disable dumping of UDPTL packets.\n"
01172 " If ip is specified, limit the dumped packets to those to and from\n"
01173 " the specified 'host' with optional port.\n";
01174 return NULL;
01175 case CLI_GENERATE:
01176 return NULL;
01177 }
01178
01179 if (a->argc < 4 || a->argc > 5)
01180 return CLI_SHOWUSAGE;
01181
01182 if (a->argc == 4) {
01183 if (!strncasecmp(a->argv[3], "on", 2)) {
01184 udptldebug = 1;
01185 memset(&udptldebugaddr, 0, sizeof(udptldebugaddr));
01186 ast_cli(a->fd, "UDPTL Debugging Enabled\n");
01187 } else if (!strncasecmp(a->argv[3], "off", 3)) {
01188 udptldebug = 0;
01189 ast_cli(a->fd, "UDPTL Debugging Disabled\n");
01190 } else {
01191 return CLI_SHOWUSAGE;
01192 }
01193 } else {
01194 if (strncasecmp(a->argv[3], "ip", 2))
01195 return CLI_SHOWUSAGE;
01196 port = 0;
01197 arg = a->argv[4];
01198 p = strstr(arg, ":");
01199 if (p) {
01200 *p = '\0';
01201 p++;
01202 port = atoi(p);
01203 }
01204 hp = ast_gethostbyname(arg, &ahp);
01205 if (hp == NULL)
01206 return CLI_SHOWUSAGE;
01207 udptldebugaddr.sin_family = AF_INET;
01208 memcpy(&udptldebugaddr.sin_addr, hp->h_addr, sizeof(udptldebugaddr.sin_addr));
01209 udptldebugaddr.sin_port = htons(port);
01210 if (port == 0)
01211 ast_cli(a->fd, "UDPTL Debugging Enabled for IP: %s\n", ast_inet_ntoa(udptldebugaddr.sin_addr));
01212 else
01213 ast_cli(a->fd, "UDPTL Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(udptldebugaddr.sin_addr), port);
01214 udptldebug = 1;
01215 }
01216
01217 return CLI_SUCCESS;
01218 }
01219
01220 static struct ast_cli_entry cli_handle_udptl_debug_deprecated = AST_CLI_DEFINE(handle_cli_udptl_debug_deprecated, "Enable/Disable UDPTL debugging");
01221
01222 static struct ast_cli_entry cli_udptl[] = {
01223 AST_CLI_DEFINE(handle_cli_udptl_set_debug, "Enable/Disable UDPTL debugging", .deprecate_cmd = &cli_handle_udptl_debug_deprecated)
01224 };
01225
01226 static void __ast_udptl_reload(int reload)
01227 {
01228 struct ast_config *cfg;
01229 const char *s;
01230 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
01231
01232 if ((cfg = ast_config_load("udptl.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
01233 return;
01234
01235 udptlstart = 4500;
01236 udptlend = 4999;
01237 udptlfectype = 0;
01238 udptlfecentries = 0;
01239 udptlfecspan = 0;
01240 udptlmaxdatagram = 0;
01241
01242 if (cfg) {
01243 if ((s = ast_variable_retrieve(cfg, "general", "udptlstart"))) {
01244 udptlstart = atoi(s);
01245 if (udptlstart < 1024) {
01246 ast_log(LOG_WARNING, "Ports under 1024 are not allowed for T.38.\n");
01247 udptlstart = 1024;
01248 }
01249 if (udptlstart > 65535) {
01250 ast_log(LOG_WARNING, "Ports over 65535 are invalid.\n");
01251 udptlstart = 65535;
01252 }
01253 }
01254 if ((s = ast_variable_retrieve(cfg, "general", "udptlend"))) {
01255 udptlend = atoi(s);
01256 if (udptlend < 1024) {
01257 ast_log(LOG_WARNING, "Ports under 1024 are not allowed for T.38.\n");
01258 udptlend = 1024;
01259 }
01260 if (udptlend > 65535) {
01261 ast_log(LOG_WARNING, "Ports over 65535 are invalid.\n");
01262 udptlend = 65535;
01263 }
01264 }
01265 if ((s = ast_variable_retrieve(cfg, "general", "udptlchecksums"))) {
01266 #ifdef SO_NO_CHECK
01267 if (ast_false(s))
01268 nochecksums = 1;
01269 else
01270 nochecksums = 0;
01271 #else
01272 if (ast_false(s))
01273 ast_log(LOG_WARNING, "Disabling UDPTL checksums is not supported on this operating system!\n");
01274 #endif
01275 }
01276 if ((s = ast_variable_retrieve(cfg, "general", "T38FaxUdpEC"))) {
01277 if (strcmp(s, "t38UDPFEC") == 0)
01278 udptlfectype = 2;
01279 else if (strcmp(s, "t38UDPRedundancy") == 0)
01280 udptlfectype = 1;
01281 }
01282 if ((s = ast_variable_retrieve(cfg, "general", "T38FaxMaxDatagram"))) {
01283 udptlmaxdatagram = atoi(s);
01284 if (udptlmaxdatagram < 100) {
01285 ast_log(LOG_WARNING, "Too small T38FaxMaxDatagram size. Defaulting to 100.\n");
01286 udptlmaxdatagram = 100;
01287 }
01288 if (udptlmaxdatagram > LOCAL_FAX_MAX_DATAGRAM) {
01289 ast_log(LOG_WARNING, "Too large T38FaxMaxDatagram size. Defaulting to %d.\n", LOCAL_FAX_MAX_DATAGRAM);
01290 udptlmaxdatagram = LOCAL_FAX_MAX_DATAGRAM;
01291 }
01292 }
01293 if ((s = ast_variable_retrieve(cfg, "general", "UDPTLFECentries"))) {
01294 udptlfecentries = atoi(s);
01295 if (udptlfecentries < 1) {
01296 ast_log(LOG_WARNING, "Too small UDPTLFECentries value. Defaulting to 1.\n");
01297 udptlfecentries = 1;
01298 }
01299 if (udptlfecentries > MAX_FEC_ENTRIES) {
01300 ast_log(LOG_WARNING, "Too large UDPTLFECentries value. Defaulting to %d.\n", MAX_FEC_ENTRIES);
01301 udptlfecentries = MAX_FEC_ENTRIES;
01302 }
01303 }
01304 if ((s = ast_variable_retrieve(cfg, "general", "UDPTLFECspan"))) {
01305 udptlfecspan = atoi(s);
01306 if (udptlfecspan < 1) {
01307 ast_log(LOG_WARNING, "Too small UDPTLFECspan value. Defaulting to 1.\n");
01308 udptlfecspan = 1;
01309 }
01310 if (udptlfecspan > MAX_FEC_SPAN) {
01311 ast_log(LOG_WARNING, "Too large UDPTLFECspan value. Defaulting to %d.\n", MAX_FEC_SPAN);
01312 udptlfecspan = MAX_FEC_SPAN;
01313 }
01314 }
01315 ast_config_destroy(cfg);
01316 }
01317 if (udptlstart >= udptlend) {
01318 ast_log(LOG_WARNING, "Unreasonable values for UDPTL start/end\n");
01319 udptlstart = 4500;
01320 udptlend = 4999;
01321 }
01322 ast_verb(2, "UDPTL allocating from port range %d -> %d\n", udptlstart, udptlend);
01323 }
01324
01325 void ast_udptl_reload(void)
01326 {
01327 __ast_udptl_reload(1);
01328 }
01329
01330 void ast_udptl_init(void)
01331 {
01332 ast_cli_register_multiple(cli_udptl, sizeof(cli_udptl) / sizeof(struct ast_cli_entry));
01333 __ast_udptl_reload(0);
01334 }