aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--UPGRADE.txt8
-rw-r--r--channels/chan_sip.c113
-rw-r--r--configs/sip.conf.sample26
-rw-r--r--configs/udptl.conf.sample4
-rw-r--r--main/udptl.c20
5 files changed, 122 insertions, 49 deletions
diff --git a/UPGRADE.txt b/UPGRADE.txt
index 1c41d90fe..61d4ec148 100644
--- a/UPGRADE.txt
+++ b/UPGRADE.txt
@@ -67,6 +67,14 @@ From 1.6.1 to 1.6.2:
instead, it is configured on a per-peer (or global) basis in sip.conf, with
the same default as was present in udptl.conf.sample.
+* T.38 FAX maximum datagram size can no longer be configured in updtl.conf;
+ instead, it is either supplied by the application servicing the T.38 channel
+ (for a FAX send or receive) or calculated from the bridged endpoint's
+ maximum datagram size (for a T.38 FAX passthrough call). In addition, sip.conf
+ allows for overriding the value supplied by a remote endpoint, which is useful
+ when T.38 connections are made to gateways that supply incorrectly-calculated
+ maximum datagram sizes.
+
* There have been some changes to the IAX2 protocol to address the security
concerns documented in the security advisory AST-2009-006. Please see the
IAX2 security document, doc/IAX2-security.pdf, for information regarding
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 7f8b3c039..4bfe696da 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1272,6 +1272,8 @@ static int regobjs = 0; /*!< Registry objects */
/* }@ */
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 */
@@ -1518,10 +1520,10 @@ struct sip_auth {
#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 */
@@ -1761,6 +1763,7 @@ struct sip_pvt {
int jointnoncodeccapability; /*!< Joint Non codec capability */
int redircodecs; /*!< Redirect codecs */
int maxcallbitrate; /*!< Maximum Call Bitrate for Video Calls */
+ int t38_maxdatagram; /*!< T.38 FaxMaxDatagram override */
int request_queue_sched_id; /*!< Scheduler ID of any scheduled action to process queued requests */
int provisional_keepalive_sched_id; /*!< Scheduler ID for provisional responses that need to be sent out to avoid cancellation */
const char *last_provisional; /*!< The last successfully transmitted provisonal response message */
@@ -2009,6 +2012,7 @@ struct sip_peer {
int inRinging; /*!< Number of calls ringing */
int onHold; /*!< Peer has someone on hold */
int call_limit; /*!< Limit of concurrent calls */
+ int t38_maxdatagram; /*!< T.38 FaxMaxDatagram override */
int busy_level; /*!< Level of active channels where we signal busy */
enum transfermodes allowtransfer; /*! SIP Refer restriction scheme */
struct ast_codec_pref prefs; /*!< codec prefs */
@@ -5178,6 +5182,7 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
/* 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);
@@ -7235,6 +7240,7 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si
if (sip_methods[intended_method].need_rtp) {
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && (p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr))) {
ast_udptl_setqos(p->udptl, global_tos_audio, global_cos_audio);
+ p->t38_maxdatagram = global_t38_maxdatagram;
}
p->maxcallbitrate = default_maxcallbitrate;
p->autoframing = global_autoframing;
@@ -7263,6 +7269,7 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si
(ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO))
p->noncodeccapability |= AST_RTP_DTMF;
if (p->udptl) {
+ p->t38_maxdatagram = global_t38_maxdatagram;
set_t38_capabilities(p);
}
ast_string_field_set(p, context, sip_cfg.default_context);
@@ -8725,6 +8732,11 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
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 */
+ if (p->t38_maxdatagram > x) {
+ ast_debug(1, "Overriding T38FaxMaxDatagram '%d' with '%d'\n", x, p->t38_maxdatagram);
+ x = p->t38_maxdatagram;
+ }
found = 1;
ast_debug(3, "FaxMaxDatagram: %d\n", x);
ast_udptl_set_far_max_datagram(p->udptl, x);
@@ -14262,6 +14274,7 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
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);
}
@@ -15577,6 +15590,18 @@ static void peer_mailboxes_to_str(struct ast_str **mailbox_str, struct sip_peer
}
}
+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[])
{
@@ -15655,7 +15680,9 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
ast_cli(fd, " Insecure : %s\n", insecure2str(ast_test_flag(&peer->flags[0], SIP_INSECURE)));
ast_cli(fd, " Force rport : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_NAT_FORCE_RPORT)));
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, " DirectMedia : %s\n", cli_yesno(ast_test_flag(&peer->flags[0], SIP_DIRECT_MEDIA)));
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)));
@@ -15767,6 +15794,9 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
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);
@@ -16215,7 +16245,9 @@ static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_
ast_cli(a->fd, " Call Events: %s\n", sip_cfg.callevents ? "On" : "Off");
ast_cli(a->fd, " Auth. Failure Events: %s\n", global_authfailureevents ? "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 && !realtimeregs)
ast_cli(a->fd, " SIP realtime: Disabled\n" );
else
@@ -20493,6 +20525,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
/* If T38 is needed but not present, then make it magically appear */
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && !p->udptl && (p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr))) {
+ p->t38_maxdatagram = global_t38_maxdatagram;
set_t38_capabilities(p);
}
@@ -23726,6 +23759,48 @@ static void set_insecure_flags (struct ast_flags *flags, const char *value, int
}
/*!
+ \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 if (!strcasecmp(v->name, "t38pt_usertpsource")) {
+ ast_set_flag(&mask[1], SIP_PAGE2_UDPTL_DESTINATION);
+ ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_UDPTL_DESTINATION);
+ } else {
+ res = 0;
+ }
+
+ return res;
+}
+
+/*!
\brief Handle flag-type options common to configuration of devices - peers
\param flags array of two struct ast_flags
\param mask array of two struct ast_flags
@@ -23851,34 +23926,12 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask
} else if (!strcasecmp(v->name, "faxdetect")) {
ast_set_flag(&mask[1], SIP_PAGE2_FAX_DETECT);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_FAX_DETECT);
- } 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);
} else if (!strcasecmp(v->name, "buggymwi")) {
ast_set_flag(&mask[1], SIP_PAGE2_BUGGY_MWI);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_BUGGY_MWI);
- } else if (!strcasecmp(v->name, "t38pt_usertpsource")) {
- ast_set_flag(&mask[1], SIP_PAGE2_UDPTL_DESTINATION);
- ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_UDPTL_DESTINATION);
} else if (!strcasecmp(v->name, "constantssrc")) {
ast_set_flag(&mask[1], SIP_PAGE2_CONSTANT_SSRC);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_CONSTANT_SSRC);
@@ -24085,6 +24138,7 @@ static void set_peer_defaults(struct sip_peer *peer)
peer->rtpkeepalive = global_rtpkeepalive;
peer->allowtransfer = sip_cfg.allowtransfer;
peer->autoframing = global_autoframing;
+ peer->t38_maxdatagram = global_t38_maxdatagram;
peer->qualifyfreq = global_qualifyfreq;
if (global_callcounter)
peer->call_limit=INT_MAX;
@@ -24244,6 +24298,9 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
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;
@@ -24955,6 +25012,7 @@ static int reload_config(enum channelreloadreason reason)
global_timer_b = 64 * DEFAULT_TIMER_T1;
global_t1min = DEFAULT_T1MIN;
global_qualifyfreq = DEFAULT_QUALIFYFREQ;
+ global_t38_maxdatagram = -1;
sip_cfg.matchexterniplocally = DEFAULT_MATCHEXTERNIPLOCALLY;
@@ -24971,6 +25029,9 @@ static int reload_config(enum channelreloadreason reason)
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;
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index 23c16979c..f32d60915 100644
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -496,22 +496,32 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
;callcounter = yes ; Enable call counters on devices. This can be set per
; device too.
-;----------------------------------------- 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.
;
-; Faxs Detect will cause the SIP channel to jump to the 'fax' extension (if it exists)
+; 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=<value>' 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.
+;
+; FAX detection will cause the SIP channel to jump to the 'fax' extension (if it exists)
; after T.38 is successfully negotiated.
;
; faxdetect = yes ; Default false
diff --git a/configs/udptl.conf.sample b/configs/udptl.conf.sample
index 97df84213..1d635da5c 100644
--- a/configs/udptl.conf.sample
+++ b/configs/udptl.conf.sample
@@ -12,10 +12,6 @@ udptlend=4999
;
;udptlchecksums=no
;
-; The maximum length of a UDPTL packet
-;
-T38FaxMaxDatagram = 400
-;
; The number of error correction entries in a UDPTL packet
;
udptlfecentries = 3
diff --git a/main/udptl.c b/main/udptl.c
index edaa3c70e..db2ae8f01 100644
--- a/main/udptl.c
+++ b/main/udptl.c
@@ -711,7 +711,7 @@ struct ast_frame *ast_udptl_read(struct ast_udptl *udptl)
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_local_max_datagram(struct ast_udptl *udptl)
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 */
@@ -998,7 +994,9 @@ int ast_udptl_write(struct ast_udptl *s, struct ast_frame *f)
}
if (f->datalen > s->far_max_ifp) {
- ast_log(LOG_WARNING, "UDPTL asked to send %d bytes of IFP when far end only prepared to accept %d bytes; data loss may occur.\n", f->datalen, s->far_max_ifp);
+ ast_log(LOG_WARNING,
+ "UDPTL asked to send %d bytes of IFP when far end only prepared to accept %d bytes; data loss may occur. "
+ "You may need to override the T38FaxMaxDatagram value for this endpoint in the channel driver configuration.\n", f->datalen, s->far_max_ifp);
}
/* Save seq_no for debug output because udptl_build_packet increments it */