Allow non-compliant T.38 endpoints to be supportable via configuration option.
authorKevin P. Fleming <kpfleming@digium.com>
Mon, 5 Oct 2009 19:45:00 +0000 (19:45 +0000)
committerKevin P. Fleming <kpfleming@digium.com>
Mon, 5 Oct 2009 19:45:00 +0000 (19:45 +0000)
Many T.38 endpoints incorrectly send the maximum IFP frame size they can accept
as the T38FaxMaxDatagram value in their SDP, when in fact this value is
supposed to be the maximum UDPTL payload size (datagram size) they can accept.
If the value they supply is small enough (a commonly supplied value is '72'),
T.38 UDPTL transmissions will likely fail completely because the UDPTL packets
will not have enough room for a primary IFP frame and the redundancy used for
error correction. If this occurs, the Asterisk UDPTL stack will emit log messages
warning that data loss may occur, and that the value may need to be overridden.

This patch extends the 't38pt_udptl' configuration option in sip.conf to allow
the administrator to override the value supplied by the remote endpoint and
supply a value that allows T.38 FAX transmissions to be successful with that
endpoint. In addition, in any SIP call where the override takes effect, a debug
message will be printed to that effect. This patch also removes the
T38FaxMaxDatagram configuration option from udptl.conf.sample, since it has not
actually had any effect for a number of releases.

In addition, this patch cleans up the T.38 documentation in sip.conf.sample
(which incorrectly documented that T.38 support was passthrough only).

(issue #15586)
Reported by: globalnetinc

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@222110 65c4cc65-6c06-0410-ace0-fbb531ad65f3

UPGRADE.txt
channels/chan_sip.c
configs/sip.conf.sample
configs/udptl.conf.sample
main/udptl.c

index 1c41d90..61d4ec1 100644 (file)
@@ -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
index 7f8b3c0..4bfe696 100644 (file)
@@ -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;
index 23c1697..f32d609 100644 (file)
@@ -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
index 97df842..1d635da 100644 (file)
@@ -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
index edaa3c7..db2ae8f 100644 (file)
@@ -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 */