Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 221742) +++ channels/chan_sip.c (working copy) @@ -756,6 +756,8 @@ /* }@ */ static struct ast_flags global_flags[2] = {{0}}; /*!< global SIP_ flags */ +static int global_t38_maxdatagram; /*!< global T.38 FaxMaxDatagram override */ + static char used_context[AST_MAX_CONTEXT]; /*!< name of automatically created context for unloading */ @@ -988,10 +990,10 @@ #define SIP_PAGE2_SUBSCRIBEMWIONLY (1 << 18) /*!< GP: Only issue MWI notification if subscribed to */ #define SIP_PAGE2_IGNORESDPVERSION (1 << 19) /*!< GDP: Ignore the SDP session version number we receive and treat all sessions as new */ -#define SIP_PAGE2_T38SUPPORT (7 << 20) /*!< GDP: T.38 Fax Support */ +#define SIP_PAGE2_T38SUPPORT (3 << 20) /*!< GDP: T.38 Fax Support */ #define SIP_PAGE2_T38SUPPORT_UDPTL (1 << 20) /*!< GDP: T.38 Fax Support (no error correction) */ #define SIP_PAGE2_T38SUPPORT_UDPTL_FEC (2 << 20) /*!< GDP: T.38 Fax Support (FEC error correction) */ -#define SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY (4 << 20) /*!< GDP: T.38 Fax Support (redundancy error correction) */ +#define SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY (3 << 20) /*!< GDP: T.38 Fax Support (redundancy error correction) */ #define SIP_PAGE2_CALL_ONHOLD (3 << 23) /*!< D: Call hold states: */ #define SIP_PAGE2_CALL_ONHOLD_ACTIVE (1 << 23) /*!< D: Active hold */ @@ -1220,7 +1222,8 @@ int noncodeccapability; /*!< DTMF RFC2833 telephony-event */ int jointnoncodeccapability; /*!< Joint Non codec capability */ int redircodecs; /*!< Redirect codecs */ - int maxcallbitrate; /*!< Maximum Call Bitrate for Video Calls */ + int maxcallbitrate; /*!< Maximum Call Bitrate for Video Calls */ + int t38_maxdatagram; /*!< T.38 FaxMaxDatagram override */ struct sip_proxy *outboundproxy; /*!< Outbound proxy for this dialog */ struct t38properties t38; /*!< T38 settings */ struct sockaddr_in udptlredirip; /*!< Where our T.38 UDPTL should be going if not to us */ @@ -1419,6 +1422,7 @@ int capability; /*!< Codec capability */ int inUse; /*!< Number of calls in use */ int call_limit; /*!< Limit of concurrent calls */ + int t38_maxdatagram; /*!< T.38 FaxMaxDatagram override */ enum transfermodes allowtransfer; /*! SIP Refer restriction scheme */ struct ast_ha *ha; /*!< ACL setting */ struct ast_variable *chanvars; /*!< Variables to set for channel created by user */ @@ -1470,6 +1474,7 @@ int onHold; /*!< Peer has someone on hold */ int call_limit; /*!< Limit of concurrent calls */ int busy_level; /*!< Level of active channels where we signal busy */ + int t38_maxdatagram; /*!< T.38 FaxMaxDatagram override */ enum transfermodes allowtransfer; /*! SIP Refer restriction scheme */ char vmexten[AST_MAX_EXTENSION]; /*!< Dialplan extension for MWI notify message*/ char language[MAX_LANGUAGE]; /*!< Default language for prompts */ @@ -4291,6 +4296,7 @@ /* t38pt_udptl was enabled in the peer and not in [general] */ dialog->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr); } + dialog->t38_maxdatagram = peer->t38_maxdatagram; set_t38_capabilities(dialog); } else if (dialog->udptl) { ast_udptl_destroy(dialog->udptl); @@ -6243,8 +6249,10 @@ p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); if (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT)) p->trtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr); - if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) + if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) { p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr); + p->t38_maxdatagram = global_t38_maxdatagram; + } if (!p->rtp|| (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && !p->vrtp) || (ast_test_flag(&p->flags[1], SIP_PAGE2_TEXTSUPPORT) && !p->trtp)) { ast_log(LOG_WARNING, "Unable to create RTP audio %s%ssession: %s\n", @@ -7399,6 +7407,8 @@ ast_debug(3, "FaxVersion: %d\n", x); p->t38.their_parms.version = x; } else if ((sscanf(a, "T38FaxMaxDatagram:%30d", &x) == 1) || (sscanf(a, "T38MaxDatagram:%30d", &x) == 1)) { + /* override the supplied value if the configuration requests it */ + x = MAX(x, p->t38_maxdatagram); found = 1; ast_debug(3, "FaxMaxDatagram: %d\n", x); ast_udptl_set_far_max_datagram(p->udptl, x); @@ -12223,6 +12233,9 @@ ast_rtp_destroy(p->trtp); p->trtp = NULL; } + if (p->udptl) { + p->t38_maxdatagram = user->t38_maxdatagram; + } } unref_user(user); return res; @@ -12267,6 +12280,7 @@ ast_copy_flags(&p->flags[1], &peer->flags[1], SIP_PAGE2_FLAGS_TO_COPY); if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->udptl) { + p->t38_maxdatagram = peer->t38_maxdatagram; set_t38_capabilities(p); } @@ -12357,6 +12371,9 @@ ast_rtp_destroy(p->trtp); p->trtp = NULL; } + if (p->udptl) { + p->t38_maxdatagram = peer->t38_maxdatagram; + } if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) || (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) p->noncodeccapability |= AST_RTP_DTMF; @@ -13439,6 +13456,18 @@ } } +static struct _map_x_s faxecmodes[] = { + { SIP_PAGE2_T38SUPPORT_UDPTL, "None"}, + { SIP_PAGE2_T38SUPPORT_UDPTL_FEC, "FEC"}, + { SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY, "Redundancy"}, + { -1, NULL}, +}; + +static const char *faxec2str(int faxec) +{ + return map_x_s(faxecmodes, faxec, "Unknown"); +} + /*! \brief Show one peer in detail (main function) */ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct message *m, int argc, const char *argv[]) { @@ -13515,7 +13544,9 @@ ast_cli(fd, " Insecure : %s\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE))); ast_cli(fd, " Nat : %s\n", nat2str(ast_test_flag(&peer->flags[0], SIP_NAT))); ast_cli(fd, " ACL : %s\n", cli_yesno(peer->ha != NULL)); - ast_cli(fd, " T38 pt UDPTL : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT))); + ast_cli(fd, " T.38 support : %s\n", cli_yesno(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT))); + ast_cli(fd, " T.38 EC mode : %s\n", faxec2str(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT))); + ast_cli(fd, " T.38 MaxDtgrm: %d\n", peer->t38_maxdatagram); ast_cli(fd, " CanReinvite : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_CAN_REINVITE))); ast_cli(fd, " PromiscRedir : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_PROMISCREDIR))); ast_cli(fd, " User=Phone : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_USEREQPHONE))); @@ -13622,6 +13653,9 @@ astman_append(s, "SIP-UserPhone: %s\r\n", (ast_test_flag(&peer->flags[0], SIP_USEREQPHONE)?"Y":"N")); astman_append(s, "SIP-VideoSupport: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_VIDEOSUPPORT)?"Y":"N")); astman_append(s, "SIP-TextSupport: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_TEXTSUPPORT)?"Y":"N")); + astman_append(s, "SIP-T.38Support: %s\r\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT)?"Y":"N")); + astman_append(s, "SIP-T.38EC: %s\r\n", faxec2str(ast_test_flag(&peer->flags[1], SIP_PAGE2_T38SUPPORT))); + astman_append(s, "SIP-T.38MaxDtgrm: %d\r\n", peer->t38_maxdatagram); astman_append(s, "SIP-Sess-Timers: %s\r\n", stmode2str(peer->stimer.st_mode_oper)); astman_append(s, "SIP-Sess-Refresh: %s\r\n", strefresher2str(peer->stimer.st_ref)); astman_append(s, "SIP-Sess-Expires: %d\r\n", peer->stimer.st_max_se); @@ -13901,7 +13935,9 @@ ast_cli(a->fd, " Record SIP history: %s\n", recordhistory ? "On" : "Off"); ast_cli(a->fd, " Call Events: %s\n", global_callevents ? "On" : "Off"); - ast_cli(a->fd, " T38 fax pt UDPTL: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT))); + ast_cli(a->fd, " T.38 support: %s\n", cli_yesno(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT))); + ast_cli(a->fd, " T.38 EC mode: %s\n", faxec2str(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT))); + ast_cli(a->fd, " T.38 MaxDtgrm: %d\n", global_t38_maxdatagram); if (!realtimepeers && !realtimeusers && !realtimeregs) ast_cli(a->fd, " SIP realtime: Disabled\n" ); @@ -20733,6 +20769,45 @@ } /*! + \brief Handle T.38 configuration options common to users and peers + \returns non-zero if any config options were handled, zero otherwise +*/ +static int handle_t38_options(struct ast_flags *flags, struct ast_flags *mask, struct ast_variable *v, + int *maxdatagram) +{ + int res = 1; + + if (!strcasecmp(v->name, "t38pt_udptl")) { + char *buf = ast_strdupa(v->value); + char *word, *next = buf; + + ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT); + + while ((word = strsep(&next, ","))) { + if (ast_true(word) || !strcasecmp(word, "fec")) { + ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT); + ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL_FEC); + } else if (!strcasecmp(word, "redundancy")) { + ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT); + ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY); + } else if (!strcasecmp(word, "none")) { + ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT); + ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL); + } else if (!strncasecmp(word, "maxdatagram=", 12)) { + if (sscanf(&word[12], "%30d", maxdatagram) != 1) { + ast_log(LOG_WARNING, "Invalid maxdatagram '%s' at line %d of %s\n", v->value, v->lineno, config); + *maxdatagram = global_t38_maxdatagram; + } + } + } + } else { + res = 0; + } + + return res; +} + +/*! \brief Handle flag-type options common to configuration of devices - users and peers \param flags array of two struct ast_flags \param mask array of two struct ast_flags @@ -20834,25 +20909,6 @@ } else if (!strcasecmp(v->name, "ignoresdpversion")) { ast_set_flag(&mask[1], SIP_PAGE2_IGNORESDPVERSION); ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_IGNORESDPVERSION); - } else if (!strcasecmp(v->name, "t38pt_udptl")) { - char buf[16], *word, *next = buf; - - ast_set_flag(&mask[1], SIP_PAGE2_T38SUPPORT); - - ast_copy_string(buf, v->value, sizeof(buf)); - - while ((word = strsep(&next, ","))) { - if (ast_true(word) || !strcasecmp(word, "fec")) { - ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT); - ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL_FEC); - } else if (!strcasecmp(word, "redundancy")) { - ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT); - ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY); - } else if (!strcasecmp(word, "none")) { - ast_clear_flag(&flags[1], SIP_PAGE2_T38SUPPORT); - ast_set_flag(&flags[1], SIP_PAGE2_T38SUPPORT_UDPTL); - } - } } else if (!strcasecmp(v->name, "rfc2833compensate")) { ast_set_flag(&mask[1], SIP_PAGE2_RFC2833_COMPENSATE); ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RFC2833_COMPENSATE); @@ -21062,6 +21118,7 @@ user->allowtransfer = global_allowtransfer; user->maxcallbitrate = default_maxcallbitrate; user->autoframing = global_autoframing; + user->t38_maxdatagram = global_t38_maxdatagram; if (global_callcounter) user->call_limit=999; user->prefs = default_prefs; @@ -21079,6 +21136,9 @@ for (; v || ((v = alt) && !(alt=NULL)); v = v->next) { if (handle_common_options(&userflags[0], &mask[0], v)) continue; + if (handle_t38_options(&userflags[0], &mask[0], v, &user->t38_maxdatagram)) { + continue; + } if (!strcasecmp(v->name, "context")) { ast_copy_string(user->context, v->value, sizeof(user->context)); } else if (!strcasecmp(v->name, "subscribecontext")) { @@ -21240,6 +21300,7 @@ peer->stimer.st_max_se = global_max_se; peer->timer_t1 = global_t1; peer->timer_b = global_timer_b; + peer->t38_maxdatagram = global_t38_maxdatagram; clear_peer_mailboxes(peer); } @@ -21363,6 +21424,9 @@ if (handle_common_options(&peerflags[0], &mask[0], v)) { continue; } + if (handle_t38_options(&peerflags[0], &mask[0], v, &peer->t38_maxdatagram)) { + continue; + } if (!strcasecmp(v->name, "transport") && !ast_strlen_zero(v->value)) { char *val = ast_strdupa(v->value); char *trans; @@ -22048,6 +22112,7 @@ global_timer_b = 64 * SIP_TIMER_T1; global_t1min = DEFAULT_T1MIN; global_qualifyfreq = DEFAULT_QUALIFYFREQ; + global_t38_maxdatagram = -1; global_matchexterniplocally = FALSE; @@ -22062,6 +22127,9 @@ for (v = ast_variable_browse(cfg, "general"); v; v = v->next) { if (handle_common_options(&global_flags[0], &dummy[0], v)) continue; + if (handle_t38_options(&global_flags[0], &dummy[0], v, &global_t38_maxdatagram)) { + continue; + } /* handle jb conf */ if (!ast_jb_read_conf(&global_jbconf, v->name, v->value)) continue; Index: main/udptl.c =================================================================== --- main/udptl.c (revision 221742) +++ main/udptl.c (working copy) @@ -711,7 +711,7 @@ static void calculate_local_max_datagram(struct ast_udptl *udptl) { - unsigned int new_max = 200; + unsigned int new_max = 0; /* calculate the amount of space required to receive an IFP * using the current error correction mode, and ensure that our @@ -741,32 +741,28 @@ static void calculate_far_max_ifp(struct ast_udptl *udptl) { - unsigned new_max = 60; + unsigned new_max = 0; /* calculate the maximum IFP the local endpoint should * generate based on the far end's maximum datagram size - * and the current error correction mode. some endpoints - * bogus 'max datagram' values that would result in unusable - * (too small) maximum IFP values, so we have a a reasonable - * minimum value to ensure that we can actually construct - * UDPTL packets. + * and the current error correction mode. */ switch (udptl->error_correction_scheme) { case UDPTL_ERROR_CORRECTION_NONE: /* only need room for sequence number and length indicators */ - new_max = MAX(new_max, udptl->far_max_datagram - 6); + new_max = udptl->far_max_datagram - 6; break; case UDPTL_ERROR_CORRECTION_REDUNDANCY: /* need room for sequence number, length indicators and the * configured number of redundant packets */ - new_max = MAX(new_max, (udptl->far_max_datagram - 8) / (udptl->error_correction_entries + 1)); + new_max = (udptl->far_max_datagram - 8) / (udptl->error_correction_entries + 1); break; case UDPTL_ERROR_CORRECTION_FEC: /* need room for sequence number, length indicators and a * a single IFP of the maximum size expected */ - new_max = MAX(new_max, (udptl->far_max_datagram - 10) / 2); + new_max = (udptl->far_max_datagram - 10) / 2; break; } /* subtract 25% of space for insurance */ Index: configs/sip.conf.sample =================================================================== --- configs/sip.conf.sample (revision 221742) +++ configs/sip.conf.sample (working copy) @@ -395,20 +395,31 @@ ; the peer side of a "type=friend" object if this ; setting is turned on. -;----------------------------------------- T.38 FAX PASSTHROUGH SUPPORT ----------------------- +;----------------------------------------- T.38 FAX SUPPORT ---------------------------------- ; ; This setting is available in the [general] section as well as in device configurations. -; Setting this to yes, enables T.38 fax (UDPTL) passthrough on SIP to SIP calls, provided -; both parties have T38 support enabled in their Asterisk configuration -; This has to be enabled in the general section for all devices to work. You can then -; disable it on a per device basis. -; T.38 faxing only works in SIP to SIP calls. It defaults to off. +; Setting this to yes enables T.38 FAX (UDPTL) on SIP calls; it defaults to off. ; ; t38pt_udptl = yes ; Enables T.38 with FEC error correction. ; t38pt_udptl = yes,fec ; Enables T.38 with FEC error correction. ; t38pt_udptl = yes,redundancy ; Enables T.38 with redundancy error correction. ; t38pt_udptl = yes,none ; Enables T.38 with no error correction. ; +; In some cases, T.38 endpoints will provide a T38FaxMaxDatagram value (during T.38 setup) that +; is based on an incorrect interpretation of the T.38 recommendation, and results in failures +; because Asterisk does not believe it can send T.38 packets of a reasonable size to that +; endpoint (Cisco media gateways are one example of this situation). In these cases, during a +; T.38 call you will see warning messages on the console/in the logs from the Asterisk UDPTL +; stack complaining about lack of buffer space to send T.38 FAX packets. If this occurs, you +; can set an override (globally, or on a per-device basis) to make Asterisk ignore the +; T38FaxMaxDatagram value specified by the other endpoint, and use a configured value instead. +; This can be done by appending 'maxdatagram=' to the t38pt_udptl configuration option, +; like this: +; +; t38pt_udptl = yes,fec,maxdatagram=400 ; Enables T.38 with FEC error correction and overrides +; ; the other endpoint's provided value to assume we can +; ; send 400 byte T.38 FAX packets to it. +; ;----------------------------------------- OUTBOUND SIP REGISTRATIONS ------------------------ ; Asterisk can register as a SIP user agent to a SIP proxy (provider) ; Format for the register statement is: