Rework of T.38 negotiation and UDPTL API to address interoperability problems
authorKevin P. Fleming <kpfleming@digium.com>
Thu, 23 Jul 2009 21:57:24 +0000 (21:57 +0000)
committerKevin P. Fleming <kpfleming@digium.com>
Thu, 23 Jul 2009 21:57:24 +0000 (21:57 +0000)
Over the past couple of months, a number of issues with Asterisk
negotiating (and successfully completing) T.38 sessions with various
endpoints have been found. This patch attempts to address many of
them, primarily focused around ensuring that the endpoints'
MaxDatagram size is honored, and in addition by ensuring that T.38
session parameter negotiation is performed correctly according to the
ITU T.38 Recommendation.

The major changes here are:

1) T.38 applications in Asterisk (app_fax) only generate/receive IFP
packets, they do not ever work with UDPTL packets. As a result of
this, they cannot be allowed to generate packets that would overflow
the other endpoints' MaxDatagram size after the UDPTL stack adds any
error correction information. With this patch, the application is told
the maximum *IFP* size it can generate, based on a calculation using
the far end MaxDatagram size and the active error correction mode on
the T.38 session. The same is true for sending *our* MaxDatagram size
to the remote endpoint; it is computed from the value that the
application says it can accept (for a single IFP packet) combined with
the active error correction mode.

2) All treatment of T.38 session parameters as 'capabilities' in
chan_sip has been removed; these parameters are not at all like
audio/video stream capabilities. There are strict rules to follow for
computing an answer to a T.38 offer, and chan_sip now follows those
rules, using the desired parameters from the application (or channel)
that wants to accept the T.38 negotiation.

3) chan_sip now stores and forwards ast_control_t38_parameters
structures for tracking 'our' and 'their' T.38 session parameters;
this greatly simplifies negotiation, especially for pass-through
calls.

4) Since T.38 negotiation without specifying parameters or receiving
the final negotiated parameters is not very worthwhile, the
AST_CONTROL_T38 control frame has been removed. A note has been added
to UPGRADE.txt about this removal, since any out-of-tree applications
that use it will no longer function properly until they are upgraded
to use AST_CONTROL_T38_PARAMETERS.

Review: https://reviewboard.asterisk.org/r/310/

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

UPGRADE.txt
apps/app_fax.c
channels/chan_sip.c
include/asterisk/frame.h
include/asterisk/udptl.h
main/channel.c
main/frame.c
main/rtp_engine.c
main/udptl.c

index 8097c4b..dda6d66 100644 (file)
 ===
 ===========================================================
 
+T.38 changes in 1.6.0.11, 1.6.1.2, 1.6.2.0:
+
+Beginning with each of these releases in their respective branches,
+Asterisk's internal methods of negotiating T.38 (FAX over IP) sessions
+changed in non-backwards-compatible ways. Any applications that previously
+used AST_CONTROL_T38 control frames will have to be upgraded to use
+AST_CONTROL_T38_PARAMETERS control frames instead; app_fax.c is a good
+example of how to generate and respond to these frames. These changes were
+made to solve significant T.38 interoperability problems between Asterisk
+and various SIP/T.38 endpoints identified by many users of Asterisk.
+
 From 1.6.2 to 1.6.3:
 
 * Asterisk-addons no longer exists as an independent package.  Those modules
index 6c132c1..c2156f0 100644 (file)
@@ -471,11 +471,12 @@ static int transmit_audio(fax_session *s)
                        if (fr && fr->frametype == AST_FRAME_DTMF && fr->subclass == 'f') {
                                struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REQUEST_NEGOTIATE,
                                                                                 .version = 0,
-                                                                                .max_datagram = 400,
+                                                                                .max_ifp = 800,
                                                                                 .rate = AST_T38_RATE_9600,
                                                                                 .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
                                                                                 .fill_bit_removal = 1,
                                                                                 .transcoding_mmr = 1,
+                                                                                .transcoding_jbig = 1,
                                };
                                ast_debug(1, "Fax tone detected. Requesting T38\n");
                                ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
@@ -509,20 +510,18 @@ static int transmit_audio(fax_session *s)
                                res = 1;
                                break;
                        } else if (parameters->request_response == AST_T38_REQUEST_NEGOTIATE) {
+                               struct ast_control_t38_parameters our_parameters = { .request_response = AST_T38_NEGOTIATED,
+                                                                                    .version = 0,
+                                                                                    .max_ifp = 800,
+                                                                                    .rate = AST_T38_RATE_9600,
+                                                                                    .rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
+                                                                                    .fill_bit_removal = 1,
+                                                                                    .transcoding_mmr = 1,
+                                                                                    .transcoding_jbig = 1,
+                               };
                                ast_debug(1, "T38 request received, accepting\n");
-                               if (parameters->version > 0) {
-                                       /* Only T.38 Version 0 is supported at this time */
-                                       parameters->version = 0;
-                               }
-                               if (parameters->max_datagram > 400) {
-                                       /* Limit incoming datagram size to our default */
-                                       /* TODO: this need to come from the udptl stack, not be hardcoded */
-                                       parameters->max_datagram = 400;
-                               }
-                               /* we only support bit rates up to 9.6kbps */
-                               parameters->rate = AST_T38_RATE_9600;
                                /* Complete T38 switchover */
-                               ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, parameters, sizeof(*parameters));
+                               ast_indicate_data(s->chan, AST_CONTROL_T38_PARAMETERS, &our_parameters, sizeof(our_parameters));
                                /* Do not break audio loop, wait until channel driver finally acks switchover
                                   with AST_T38_NEGOTIATED */
                        }
@@ -593,7 +592,7 @@ static int transmit_t38(fax_session *s)
                return -1;
        }
 
-       t38_set_max_datagram_size(t38state, s->t38parameters.max_datagram);
+       t38_set_max_datagram_size(t38state, s->t38parameters.max_ifp);
 
        if (s->t38parameters.fill_bit_removal) {
                t38_set_fill_bit_removal(t38state, TRUE);
index 7297da8..29e0f95 100644 (file)
@@ -1511,10 +1511,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: T38 Fax Passthrough Support */
-#define SIP_PAGE2_T38SUPPORT_UDPTL             (1 << 20)       /*!< GDP: T38 Fax Passthrough Support (no error correction) */
-#define SIP_PAGE2_T38SUPPORT_UDPTL_FEC         (2 << 20)       /*!< GDP: T38 Fax Passthrough Support (FEC error correction) */
-#define SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY  (4 << 20)       /*!< GDP: T38 Fax Passthrough Support (redundancy error correction) */
+#define SIP_PAGE2_T38SUPPORT                   (7 << 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_CALL_ONHOLD          (3 << 23)       /*!< D: Call hold states: */
 #define SIP_PAGE2_CALL_ONHOLD_ACTIVE    (1 << 23)       /*!< D: Active hold */
@@ -1538,36 +1538,6 @@ struct sip_auth {
 
 /*@}*/ 
 
-/*! \name SIPflagsT38
-       T.38 set of flags */
-
-/*@{*/ 
-#define T38FAX_FILL_BIT_REMOVAL                        (1 << 0)        /*!< Default: 0 (unset)*/
-#define T38FAX_TRANSCODING_MMR                 (1 << 1)        /*!< Default: 0 (unset)*/
-#define T38FAX_TRANSCODING_JBIG                        (1 << 2)        /*!< Default: 0 (unset)*/
-/* Rate management */
-#define T38FAX_RATE_MANAGEMENT_TRANSFERRED_TCF (0 << 3)
-#define T38FAX_RATE_MANAGEMENT_LOCAL_TCF       (1 << 3)        /*!< Unset for transferredTCF (UDPTL), set for localTCF (TPKT) */
-/* UDP Error correction */
-#define T38FAX_UDP_EC_NONE                     (0 << 4)        /*!< two bits, if unset NO t38UDPEC field in T38 SDP*/
-#define T38FAX_UDP_EC_FEC                      (1 << 4)        /*!< Set for t38UDPFEC */
-#define T38FAX_UDP_EC_REDUNDANCY               (2 << 4)        /*!< Set for t38UDPRedundancy */
-/* T38 Spec version */
-#define T38FAX_VERSION                         (3 << 6)        /*!< two bits, 2 values so far, up to 4 values max */
-#define T38FAX_VERSION_0                       (0 << 6)        /*!< Version 0 */
-#define T38FAX_VERSION_1                       (1 << 6)        /*!< Version 1 */
-/* Maximum Fax Rate */
-#define T38FAX_RATE_2400                       (1 << 8)        /*!< 2400 bps t38FaxRate */
-#define T38FAX_RATE_4800                       (1 << 9)        /*!< 4800 bps t38FaxRate */
-#define T38FAX_RATE_7200                       (1 << 10)       /*!< 7200 bps t38FaxRate */
-#define T38FAX_RATE_9600                       (1 << 11)       /*!< 9600 bps t38FaxRate */
-#define T38FAX_RATE_12000                      (1 << 12)       /*!< 12000 bps t38FaxRate */
-#define T38FAX_RATE_14400                      (1 << 13)       /*!< 14400 bps t38FaxRate */
-
-/*!< This is default: NO MMR and JBIG transcoding, NO fill bit removal, transferredTCF TCF, UDP FEC, Version 0 and 9600 max fax rate */
-static int global_t38_capability = T38FAX_VERSION_0 | T38FAX_RATE_2400 | T38FAX_RATE_4800 | T38FAX_RATE_7200 | T38FAX_RATE_9600;
-/*@}*/ 
-
 /*! \brief debugging state
  * We store separately the debugging requests from the config file
  * and requests from the CLI. Debugging is enabled if either is set
@@ -1598,11 +1568,9 @@ enum t38state {
 
 /*! \brief T.38 channel settings (at some point we need to make this alloc'ed */
 struct t38properties {
-       struct ast_flags t38support;    /*!< Flag for udptl, rtp or tcp support for this session */
-       int capability;                 /*!< Our T38 capability */
-       int peercapability;             /*!< Peers T38 capability */
-       int jointcapability;            /*!< Supported T38 capability at both ends */
        enum t38state state;            /*!< T.38 state */
+       struct ast_control_t38_parameters our_parms;
+       struct ast_control_t38_parameters their_parms;
 };
 
 /*! \brief Parameters to know status of transfer */
@@ -4190,7 +4158,7 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
                sip_pvt_lock(p);
 
                /* Now if T38 support is enabled we need to look and see what the current state is to get what we want to report back */
-               if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT)) {
+               if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) {
                        switch (p->t38.state) {
                        case T38_LOCAL_REINVITE:
                        case T38_PEER_REINVITE:
@@ -4922,57 +4890,12 @@ static void do_setnat(struct sip_pvt *p)
        }
 }
 
-/*! \brief Helper function which interprets T.38 capabilities and fills a parameters structure in */
-static void fill_t38_parameters(int capabilities, struct ast_control_t38_parameters *parameters, struct sip_pvt *p)
-{
-       if (capabilities & T38FAX_VERSION_0) {
-               parameters->version = 0;
-       } else if (capabilities & T38FAX_VERSION_1) {
-               parameters->version = 1;
-       }
-
-       if (capabilities & T38FAX_RATE_14400) {
-               parameters->rate = AST_T38_RATE_14400;
-       } else if (capabilities & T38FAX_RATE_12000) {
-               parameters->rate = AST_T38_RATE_12000;
-       } else if (capabilities & T38FAX_RATE_9600) {
-               parameters->rate = AST_T38_RATE_9600;
-       } else if (capabilities & T38FAX_RATE_7200) {
-               parameters->rate = AST_T38_RATE_7200;
-       } else if (capabilities & T38FAX_RATE_4800) {
-               parameters->rate = AST_T38_RATE_4800;
-       } else if (capabilities & T38FAX_RATE_2400) {
-               parameters->rate = AST_T38_RATE_2400;
-       }
-
-       if (capabilities & T38FAX_RATE_MANAGEMENT_TRANSFERRED_TCF) {
-               parameters->rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF;
-       } else if (capabilities & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) {
-               parameters->rate_management = AST_T38_RATE_MANAGEMENT_LOCAL_TCF;
-       }
-
-       if (capabilities & T38FAX_FILL_BIT_REMOVAL) {
-               parameters->fill_bit_removal = 1;
-       }
-
-       if (capabilities & T38FAX_TRANSCODING_MMR) {
-               parameters->transcoding_mmr = 1;
-       }
-
-       if (capabilities & T38FAX_TRANSCODING_JBIG) {
-               parameters->transcoding_jbig = 1;
-       }
-
-       parameters->max_datagram = ast_udptl_get_far_max_datagram(p->udptl);
-}
-
 /*! \brief Change the T38 state on a SIP dialog */
 static void change_t38_state(struct sip_pvt *p, int state)
 {
        int old = p->t38.state;
        struct ast_channel *chan = p->owner;
-       enum ast_control_t38 message = 0;
-       struct ast_control_t38_parameters parameters = { 0, };
+       struct ast_control_t38_parameters parameters = { .request_response = 0 };
 
        /* Don't bother changing if we are already in the state wanted */
        if (old == state)
@@ -4987,21 +4910,21 @@ static void change_t38_state(struct sip_pvt *p, int state)
 
        /* Given the state requested and old state determine what control frame we want to queue up */
        if (state == T38_PEER_REINVITE) {
-               message = parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
-               fill_t38_parameters(p->t38.peercapability, &parameters, p);
+               parameters = p->t38.their_parms;
+               parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
+               parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
        } else if (state == T38_ENABLED) {
-               message = parameters.request_response = AST_T38_NEGOTIATED;
-               fill_t38_parameters(p->t38.jointcapability, &parameters, p);
+               parameters = p->t38.their_parms;
+               parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
+               parameters.request_response = AST_T38_NEGOTIATED;
        } else if (state == T38_DISABLED && old == T38_ENABLED)
-               message = parameters.request_response = AST_T38_TERMINATED;
+               parameters.request_response = AST_T38_TERMINATED;
        else if (state == T38_DISABLED && old == T38_LOCAL_REINVITE)
-               message = parameters.request_response = AST_T38_REFUSED;
+               parameters.request_response = AST_T38_REFUSED;
 
        /* Woot we got a message, create a control frame and send it on! */
        if (parameters.request_response)
                ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
-       if (message)
-               ast_queue_control_data(chan, AST_CONTROL_T38, &message, sizeof(message));
 
        if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT) && !p->outgoing_call) {
                /* fax detection is enabled and this is an incoming call */
@@ -5029,19 +4952,14 @@ static void change_t38_state(struct sip_pvt *p, int state)
 /*! \brief Set the global T38 capabilities on a SIP dialog structure */
 static void set_t38_capabilities(struct sip_pvt *p)
 {
-       p->t38.capability = global_t38_capability;
        if (p->udptl) {
                if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY) {
                         ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
-                       p->t38.capability |= T38FAX_UDP_EC_REDUNDANCY;
                } else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL_FEC) {
                        ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
-                       p->t38.capability |= T38FAX_UDP_EC_FEC;
                } else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) == SIP_PAGE2_T38SUPPORT_UDPTL) {
                        ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
-                       p->t38.capability |= T38FAX_UDP_EC_NONE;
                }
-               p->t38.capability |= T38FAX_RATE_MANAGEMENT_TRANSFERRED_TCF;
        }
 }
 
@@ -5139,9 +5057,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);
                }
-               ast_copy_flags(&dialog->t38.t38support, &peer->flags[1], SIP_PAGE2_T38SUPPORT);
                set_t38_capabilities(dialog);
-               dialog->t38.jointcapability = dialog->t38.capability;
        } else if (dialog->udptl) {
                ast_udptl_destroy(dialog->udptl);
                dialog->udptl = NULL;
@@ -5441,9 +5357,6 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
        } else {
                int xmitres;
 
-               p->t38.jointcapability = p->t38.capability;
-               ast_debug(2, "Our T38 capability (%d), joint T38 capability (%d)\n", p->t38.capability, p->t38.jointcapability);
-
                sip_pvt_lock(p);
                xmitres = transmit_invite(p, SIP_INVITE, 1, 2);
                sip_pvt_unlock(p);
@@ -6321,15 +6234,10 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
                                we simply forget the frames if we get modem frames before the bridge is up.
                                Fax will re-transmit.
                        */
-                       if (ast->_state == AST_STATE_UP) {
-                               if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->t38.state == T38_DISABLED) {
-                                       if (!p->pendinginvite) {
-                                               change_t38_state(p, T38_LOCAL_REINVITE);
-                                               transmit_reinvite_with_sdp(p, TRUE, FALSE);
-                                       }
-                               } else if (p->udptl && p->t38.state == T38_ENABLED) {
-                                       res = ast_udptl_write(p->udptl, frame);
-                               }
+                       if ((ast->_state == AST_STATE_UP) &&
+                           p->udptl &&
+                           (p->t38.state == T38_ENABLED)) {
+                               res = ast_udptl_write(p->udptl, frame);
                        }
                        sip_pvt_unlock(p);
                }
@@ -6459,66 +6367,34 @@ static int sip_transfer(struct ast_channel *ast, const char *dest)
 }
 
 /*! \brief Helper function which updates T.38 capability information and triggers a reinvite */
-static void interpret_t38_parameters(struct sip_pvt *p, enum ast_control_t38 request_response, const struct ast_control_t38_parameters *parameters)
+static void interpret_t38_parameters(struct sip_pvt *p, const struct ast_control_t38_parameters *parameters)
 {
-       if (parameters) {
-               if (!parameters->version) {
-                       p->t38.capability = p->t38.jointcapability |= T38FAX_VERSION_0;
-               } else if (parameters->version == 1) {
-                       p->t38.capability = p->t38.jointcapability |= T38FAX_VERSION_1;
-               }
-
-               if (parameters->rate == AST_T38_RATE_14400) {
-                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
-               } else if (parameters->rate == AST_T38_RATE_12000) {
-                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
-               } else if (parameters->rate == AST_T38_RATE_9600) {
-                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
-               } else if (parameters->rate == AST_T38_RATE_7200) {
-                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
-               } else if (parameters->rate == AST_T38_RATE_4800) {
-                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_4800 | T38FAX_RATE_2400;
-               } else if (parameters->rate == AST_T38_RATE_2400) {
-                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_2400;
-               }
-
-               if (parameters->rate_management == AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF) {
-                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_MANAGEMENT_TRANSFERRED_TCF;
-               } else if (parameters->rate_management == AST_T38_RATE_MANAGEMENT_LOCAL_TCF) {
-                       p->t38.capability = p->t38.jointcapability |= T38FAX_RATE_MANAGEMENT_LOCAL_TCF;
-               }
-
-               if (parameters->fill_bit_removal) {
-                       p->t38.capability = p->t38.jointcapability |= T38FAX_FILL_BIT_REMOVAL;
-               } else {
-                       p->t38.capability = p->t38.jointcapability &= ~T38FAX_FILL_BIT_REMOVAL;
-               }
-
-               if (parameters->transcoding_mmr) {
-                       p->t38.capability = p->t38.jointcapability |= T38FAX_TRANSCODING_MMR;
-               } else {
-                       p->t38.capability = p->t38.jointcapability &= ~T38FAX_TRANSCODING_MMR;
-               }
-
-               if (parameters->transcoding_jbig) {
-                       p->t38.capability = p->t38.jointcapability |= T38FAX_TRANSCODING_JBIG;
-               } else {
-                       p->t38.capability = p->t38.jointcapability &= ~T38FAX_TRANSCODING_JBIG;
-               }
-
-               if (p->udptl && request_response == AST_T38_REQUEST_NEGOTIATE) {
-                       ast_udptl_set_local_max_datagram(p->udptl, parameters->max_datagram ? parameters->max_datagram : 400);
-               }
-       }
-
-       switch (request_response) {
+       switch (parameters->request_response) {
        case AST_T38_NEGOTIATED:
        case AST_T38_REQUEST_NEGOTIATE:         /* Request T38 */
                if (p->t38.state == T38_PEER_REINVITE) {
                        AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
+                       p->t38.our_parms = *parameters;
+                       /* modify our parameters to conform to the peer's parameters,
+                        * based on the rules in the ITU T.38 recommendation
+                        */
+                       if (!p->t38.their_parms.fill_bit_removal) {
+                               p->t38.our_parms.fill_bit_removal = FALSE;
+                       }
+                       if (!p->t38.their_parms.transcoding_mmr) {
+                               p->t38.our_parms.transcoding_mmr = FALSE;
+                       }
+                       if (!p->t38.their_parms.transcoding_jbig) {
+                               p->t38.our_parms.transcoding_jbig = FALSE;
+                       }
+                       p->t38.our_parms.version = MIN(p->t38.our_parms.version, p->t38.their_parms.version);
+                       p->t38.our_parms.rate_management = p->t38.their_parms.rate_management;
+                       ast_udptl_set_local_max_ifp(p->udptl, p->t38.our_parms.max_ifp);
                        change_t38_state(p, T38_ENABLED);
                        transmit_response_with_t38_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
-               } else if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && p->t38.state != T38_ENABLED) {
+               } else if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->t38.state != T38_ENABLED) {
+                       p->t38.our_parms = *parameters;
+                       ast_udptl_set_local_max_ifp(p->udptl, p->t38.our_parms.max_ifp);
                        change_t38_state(p, T38_LOCAL_REINVITE);
                        if (!p->pendinginvite) {
                                transmit_reinvite_with_sdp(p, TRUE, FALSE);
@@ -6626,19 +6502,12 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
                } else
                        res = -1;
                break;
-       case AST_CONTROL_T38:   /* T38 control frame */
-               if (datalen != sizeof(enum ast_control_t38)) {
-                       ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38. Expected %d, got %d\n", (int)sizeof(enum ast_control_t38), (int)datalen);
-               } else {
-                       interpret_t38_parameters(p, *((enum ast_control_t38 *) data), NULL);
-               }
-               break;
        case AST_CONTROL_T38_PARAMETERS:
                if (datalen != sizeof(struct ast_control_t38_parameters)) {
-                       ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38_PARAMETERS. Expected %d, got %d\n", (int)sizeof(struct ast_control_t38_parameters), (int)datalen);
+                       ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38_PARAMETERS. Expected %d, got %d\n", (int) sizeof(struct ast_control_t38_parameters), (int) datalen);
                } else {
                        const struct ast_control_t38_parameters *parameters = data;
-                       interpret_t38_parameters(p, parameters->request_response, parameters);
+                       interpret_t38_parameters(p, parameters);
                }
                break;
        case AST_CONTROL_SRCUPDATE:
@@ -7062,7 +6931,7 @@ static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p
        if (f && p->dsp) {
                f = ast_dsp_process(p->owner, p->dsp, f);
                if (f && f->frametype == AST_FRAME_DTMF) {
-                       if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && f->subclass == 'f') {
+                       if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && f->subclass == 'f') {
                                ast_debug(1, "Fax CNG detected on %s\n", ast->name);
                                *faxdetect = 1;
                        } else {
@@ -7087,7 +6956,7 @@ static struct ast_frame *sip_read(struct ast_channel *ast)
 
        /* If we are NOT bridged to another channel, and we have detected fax tone we issue T38 re-invite to a peer */
        /* If we are bridged then it is the responsibility of the SIP device to issue T38 re-invite if it detects CNG or fax preamble */
-       if (faxdetected && ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT) && (p->t38.state == T38_DISABLED) && !(ast_bridged_channel(ast))) {
+       if (faxdetected && ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && (p->t38.state == T38_DISABLED) && !(ast_bridged_channel(ast))) {
                if (!ast_test_flag(&p->flags[0], SIP_GOTREFER)) {
                        if (!p->pendinginvite) {
                                ast_debug(3, "Sending reinvite on SIP (%s) for T.38 negotiation.\n", ast->name);
@@ -7261,9 +7130,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) {
-               ast_copy_flags(&p->t38.t38support, &p->flags[1], SIP_PAGE2_T38SUPPORT);
                set_t38_capabilities(p);
-               p->t38.jointcapability = p->t38.capability;
        }
        ast_string_field_set(p, context, sip_cfg.default_context);
        ast_string_field_set(p, parkinglot, default_parkinglot);
@@ -8050,7 +7917,6 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
        int vportno = -1;               /*!< RTP Video port number */
        int tportno = -1;               /*!< RTP Text port number */
        int udptlportno = -1;
-       int peert38capability = 0;
        char s[256];
        int old = 0;
 
@@ -8590,6 +8456,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                int found = 0, x;
                
                old = 0;
+               memset(&p->t38.their_parms, 0, sizeof(p->t38.their_parms));
                
                /* Scan trough the a= lines for T38 attributes and set apropriate fileds */
                iterator = req->sdp_start;
@@ -8602,110 +8469,92 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
                                ast_debug(3, "T38MaxBitRate: %d\n", x);
                                switch (x) {
                                case 14400:
-                                       peert38capability |= T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+                                       p->t38.their_parms.rate = AST_T38_RATE_14400;
                                        break;
                                case 12000:
-                                       peert38capability |= T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+                                       p->t38.their_parms.rate = AST_T38_RATE_12000;
                                        break;
                                case 9600:
-                                       peert38capability |= T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+                                       p->t38.their_parms.rate = AST_T38_RATE_9600;
                                        break;
                                case 7200:
-                                       peert38capability |= T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400;
+                                       p->t38.their_parms.rate = AST_T38_RATE_7200;
                                        break;
                                case 4800:
-                                       peert38capability |= T38FAX_RATE_4800 | T38FAX_RATE_2400;
+                                       p->t38.their_parms.rate = AST_T38_RATE_4800;
                                        break;
                                case 2400:
-                                       peert38capability |= T38FAX_RATE_2400;
+                                       p->t38.their_parms.rate = AST_T38_RATE_2400;
                                        break;
                                }
                        } else if ((sscanf(a, "T38FaxVersion:%d", &x) == 1)) {
                                found = 1;
                                ast_debug(3, "FaxVersion: %d\n", x);
-                               if (x == 0)
-                                       peert38capability |= T38FAX_VERSION_0;
-                               else if (x == 1)
-                                       peert38capability |= T38FAX_VERSION_1;
+                               p->t38.their_parms.version = x;
                        } else if ((sscanf(a, "T38FaxMaxDatagram:%d", &x) == 1) || (sscanf(a, "T38MaxDatagram:%d", &x) == 1)) {
                                found = 1;
                                ast_debug(3, "FaxMaxDatagram: %d\n", x);
                                ast_udptl_set_far_max_datagram(p->udptl, x);
-                               if (!ast_udptl_get_local_max_datagram(p->udptl)) {
-                                       ast_udptl_set_local_max_datagram(p->udptl, x);
-                               }
                        } else if ((strncmp(a, "T38FaxFillBitRemoval", 20) == 0)) {
                                found = 1;
-                               if(sscanf(a, "T38FaxFillBitRemoval:%d", &x) == 1) {
-                                   ast_debug(3, "FillBitRemoval: %d\n", x);
-                                   if(x == 1)
-                                       peert38capability |= T38FAX_FILL_BIT_REMOVAL;
+                               if (sscanf(a, "T38FaxFillBitRemoval:%d", &x) == 1) {
+                                       ast_debug(3, "FillBitRemoval: %d\n", x);
+                                       if (x == 1) {
+                                               p->t38.their_parms.fill_bit_removal = TRUE;
+                                       }
                                } else {
-                                   ast_debug(3, "FillBitRemoval\n");
-                                   peert38capability |= T38FAX_FILL_BIT_REMOVAL;
+                                       ast_debug(3, "FillBitRemoval\n");
+                                       p->t38.their_parms.fill_bit_removal = TRUE;
                                }
                        } else if ((strncmp(a, "T38FaxTranscodingMMR", 20) == 0)) {
                                found = 1;
-                               if(sscanf(a, "T38FaxTranscodingMMR:%d", &x) == 1) {
-                                   ast_debug(3, "Transcoding MMR: %d\n", x);
-                                   if(x == 1)
-                                       peert38capability |= T38FAX_TRANSCODING_MMR;
+                               if (sscanf(a, "T38FaxTranscodingMMR:%d", &x) == 1) {
+                                       ast_debug(3, "Transcoding MMR: %d\n", x);
+                                       if (x == 1) {
+                                               p->t38.their_parms.transcoding_mmr = TRUE;
+                                       }
                                } else {
-                                   ast_debug(3, "Transcoding MMR\n");
-                                   peert38capability |= T38FAX_TRANSCODING_MMR;
+                                       ast_debug(3, "Transcoding MMR\n");
+                                       p->t38.their_parms.transcoding_mmr = TRUE;
                                }
                        } else if ((strncmp(a, "T38FaxTranscodingJBIG", 21) == 0)) {
                                found = 1;
-                               if(sscanf(a, "T38FaxTranscodingJBIG:%d", &x) == 1) {
-                                   ast_debug(3, "Transcoding JBIG: %d\n", x);
-                                   if(x == 1)
-                                       peert38capability |= T38FAX_TRANSCODING_JBIG;
+                               if (sscanf(a, "T38FaxTranscodingJBIG:%d", &x) == 1) {
+                                       ast_debug(3, "Transcoding JBIG: %d\n", x);
+                                       if (x == 1) {
+                                               p->t38.their_parms.transcoding_jbig = TRUE;
+                                       }
                                } else {
-                                   ast_debug(3, "Transcoding JBIG\n");
-                                   peert38capability |= T38FAX_TRANSCODING_JBIG;
+                                       ast_debug(3, "Transcoding JBIG\n");
+                                       p->t38.their_parms.transcoding_jbig = TRUE;
                                }
                        } else if ((sscanf(a, "T38FaxRateManagement:%255s", s) == 1)) {
                                found = 1;
                                ast_debug(3, "RateManagement: %s\n", s);
                                if (!strcasecmp(s, "localTCF"))
-                                       peert38capability |= T38FAX_RATE_MANAGEMENT_LOCAL_TCF;
+                                       p->t38.their_parms.rate_management = AST_T38_RATE_MANAGEMENT_LOCAL_TCF;
                                else if (!strcasecmp(s, "transferredTCF"))
-                                       peert38capability |= T38FAX_RATE_MANAGEMENT_TRANSFERRED_TCF;
+                                       p->t38.their_parms.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF;
                        } else if ((sscanf(a, "T38FaxUdpEC:%255s", s) == 1)) {
                                found = 1;
                                ast_debug(3, "UDP EC: %s\n", s);
                                if (!strcasecmp(s, "t38UDPRedundancy")) {
-                                       peert38capability |= T38FAX_UDP_EC_REDUNDANCY;
                                        ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_REDUNDANCY);
                                } else if (!strcasecmp(s, "t38UDPFEC")) {
-                                       peert38capability |= T38FAX_UDP_EC_FEC;
                                        ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_FEC);
                                } else {
-                                       peert38capability |= T38FAX_UDP_EC_NONE;
                                        ast_udptl_set_error_correction_scheme(p->udptl, UDPTL_ERROR_CORRECTION_NONE);
                                }
                        }
                }
-               if (found) { /* Some cisco equipment returns nothing beside c= and m= lines in 200 OK T38 SDP */
-                       p->t38.peercapability = peert38capability;
-                       p->t38.jointcapability = (peert38capability & 255); /* Put everything beside supported speeds settings */
-                       peert38capability &= (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400);
-                       p->t38.jointcapability |= (peert38capability & p->t38.capability); /* Put the lower of our's and peer's speed */
-               }
-               if (debug)
-                       ast_debug(1, "Our T38 capability = (%d), peer T38 capability (%d), joint T38 capability (%d)\n",
-                               p->t38.capability,
-                               p->t38.peercapability,
-                               p->t38.jointcapability);
 
                /* Remote party offers T38, we need to update state */
-               if (t38action == SDP_T38_ACCEPT) {
-                       if (p->t38.state == T38_LOCAL_REINVITE)
-                               change_t38_state(p, T38_ENABLED);
-               } else if (t38action == SDP_T38_INITIATE) {
-                       if (p->owner && p->lastinvite) {
-                               change_t38_state(p, T38_PEER_REINVITE); /* T38 Offered in re-invite from remote party */
-                       }
+               if ((t38action == SDP_T38_ACCEPT) &&
+                   (p->t38.state == T38_LOCAL_REINVITE)) {
+                       change_t38_state(p, T38_ENABLED);
+               } else if ((t38action == SDP_T38_INITIATE) &&
+                          p->owner && p->lastinvite) {
+                       change_t38_state(p, T38_PEER_REINVITE); /* T38 Offered in re-invite from remote party */
                }
        } else {
                change_t38_state(p, T38_DISABLED);
@@ -8744,7 +8593,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
        }
        if (!newjointcapability) {
                /* If T.38 was not negotiated either, totally bail out... */
-               if (!p->t38.jointcapability || !udptlportno) {
+               if ((p->t38.state == T38_DISABLED) || !udptlportno) {
                        ast_log(LOG_NOTICE, "No compatible codecs, not accepting this offer!\n");
                        /* Do NOT Change current setting */
                        return -1;
@@ -9885,30 +9734,22 @@ static void add_tcodec_to_sdp(const struct sip_pvt *p, int codec,
 
 
 /*! \brief Get Max T.38 Transmission rate from T38 capabilities */
-static int t38_get_rate(int t38cap)
+static unsigned int t38_get_rate(enum ast_control_t38_rate rate)
 {
-       int maxrate = (t38cap & (T38FAX_RATE_14400 | T38FAX_RATE_12000 | T38FAX_RATE_9600 | T38FAX_RATE_7200 | T38FAX_RATE_4800 | T38FAX_RATE_2400));
-       
-       if (maxrate & T38FAX_RATE_14400) {
-               ast_debug(2, "T38MaxBitRate 14400 found\n");
-               return 14400;
-       } else if (maxrate & T38FAX_RATE_12000) {
-               ast_debug(2, "T38MaxBitRate 12000 found\n");
-               return 12000;
-       } else if (maxrate & T38FAX_RATE_9600) {
-               ast_debug(2, "T38MaxBitRate 9600 found\n");
-               return 9600;
-       } else if (maxrate & T38FAX_RATE_7200) {
-               ast_debug(2, "T38MaxBitRate 7200 found\n");
-               return 7200;
-       } else if (maxrate & T38FAX_RATE_4800) {
-               ast_debug(2, "T38MaxBitRate 4800 found\n");
-               return 4800;
-       } else if (maxrate & T38FAX_RATE_2400) {
-               ast_debug(2, "T38MaxBitRate 2400 found\n");
+       switch (rate) {
+       case AST_T38_RATE_2400:
                return 2400;
-       } else {
-               ast_debug(2, "Strange, T38MaxBitRate NOT found in peers T38 SDP.\n");
+       case AST_T38_RATE_4800:
+               return 4800;
+       case AST_T38_RATE_7200:
+               return 7200;
+       case AST_T38_RATE_9600:
+               return 9600;
+       case AST_T38_RATE_12000:
+               return 12000;
+       case AST_T38_RATE_14400:
+               return 14400;
+       default:
                return 0;
        }
 }
@@ -10224,35 +10065,38 @@ static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int
                /* We break with the "recommendation" and send our IP, in order that our
                   peer doesn't have to ast_gethostbyname() us */
 
-               if (debug) {
-                       ast_debug(1, "Our T38 capability (%d), peer T38 capability (%d), joint capability (%d)\n",
-                                 p->t38.capability,
-                                 p->t38.peercapability,
-                                 p->t38.jointcapability);
-               }
-
                ast_str_append(&m_modem, 0, "m=image %d udptl t38", ntohs(udptldest.sin_port));
 
-               if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_0)
-                       ast_str_append(&a_modem, 0, "a=T38FaxVersion:0\r\n");
-               if ((p->t38.jointcapability & T38FAX_VERSION) == T38FAX_VERSION_1)
-                       ast_str_append(&a_modem, 0, "a=T38FaxVersion:1\r\n");
-               if ((x = t38_get_rate(p->t38.jointcapability)))
-                       ast_str_append(&a_modem, 0, "a=T38MaxBitRate:%d\r\n", x);
-               if ((p->t38.jointcapability & T38FAX_FILL_BIT_REMOVAL) == T38FAX_FILL_BIT_REMOVAL)
+               ast_str_append(&a_modem, 0, "a=T38Faxversion:%d\r\n", p->t38.our_parms.version);
+               ast_str_append(&a_modem, 0, "a=T38MaxBitRate:%d\r\n", t38_get_rate(p->t38.our_parms.rate));
+               if (p->t38.our_parms.fill_bit_removal) {
                        ast_str_append(&a_modem, 0, "a=T38FaxFillBitRemoval\r\n");
-               if ((p->t38.jointcapability & T38FAX_TRANSCODING_MMR) == T38FAX_TRANSCODING_MMR)
+               }
+               if (p->t38.our_parms.transcoding_mmr) {
                        ast_str_append(&a_modem, 0, "a=T38FaxTranscodingMMR\r\n");
-               if ((p->t38.jointcapability & T38FAX_TRANSCODING_JBIG) == T38FAX_TRANSCODING_JBIG)
+               }
+               if (p->t38.our_parms.transcoding_jbig) {
                        ast_str_append(&a_modem, 0, "a=T38FaxTranscodingJBIG\r\n");
-               ast_str_append(&a_modem, 0, "a=T38FaxRateManagement:%s\r\n", (p->t38.jointcapability & T38FAX_RATE_MANAGEMENT_LOCAL_TCF) ? "localTCF" : "transferredTCF");
-               x = ast_udptl_get_local_max_datagram(p->udptl);
-               ast_str_append(&a_modem, 0, "a=T38FaxMaxBuffer:%d\r\n", x);
-               ast_str_append(&a_modem, 0, "a=T38FaxMaxDatagram:%d\r\n", x);
-               if (p->t38.jointcapability & T38FAX_UDP_EC_REDUNDANCY)
-                       ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:t38UDPRedundancy\r\n");
-               else if (p->t38.jointcapability & T38FAX_UDP_EC_FEC)
+               }
+               switch (p->t38.our_parms.rate_management) {
+               case AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF:
+                       ast_str_append(&a_modem, 0, "a=T38FaxRateManagement:transferredTCF\r\n");
+                       break;
+               case AST_T38_RATE_MANAGEMENT_LOCAL_TCF:
+                       ast_str_append(&a_modem, 0, "a=T38FaxRateManagement:localTCF\r\n");
+                       break;
+               }
+               ast_str_append(&a_modem, 0, "a=T38FaxMaxDatagram:%d\r\n", ast_udptl_get_local_max_datagram(p->udptl));
+               switch (ast_test_flag(&p->flags[1],  SIP_PAGE2_T38SUPPORT)) {
+               case SIP_PAGE2_T38SUPPORT_UDPTL:
+                       break;
+               case SIP_PAGE2_T38SUPPORT_UDPTL_FEC:
                        ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:t38UDPFEC\r\n");
+                       break;
+               case SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY:
+                       ast_str_append(&a_modem, 0, "a=T38FaxUdpEC:t38UDPRedundancy\r\n");
+                       break;
+               }
        }
 
        if (needaudio)
@@ -10335,7 +10179,6 @@ static int transmit_response_with_t38_sdp(struct sip_pvt *p, char *msg, struct s
        }
        respprep(&resp, p, msg, req);
        if (p->udptl) {
-               ast_udptl_offered_from_local(p->udptl, 0);
                add_sdp(&resp, p, 0, 0, 1);
        } else 
                ast_log(LOG_ERROR, "Can't add SDP to response, since we have no UDPTL session allocated. Call-ID %s\n", p->callid);
@@ -10862,7 +10705,6 @@ static int transmit_invite(struct sip_pvt *p, int sipmethod, int sdp, int init)
        if (sdp) {
                memset(p->offered_media, 0, sizeof(p->offered_media));
                if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) {
-                       ast_udptl_offered_from_local(p->udptl, 1);
                        ast_debug(1, "T38 is in state %d on channel %s\n", p->t38.state, p->owner ? p->owner->name : "<none>");
                        add_sdp(&req, p, FALSE, FALSE, TRUE);
                } else if (p->rtp) {
@@ -14132,7 +13974,6 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
 
        if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT) && p->udptl) {
                set_t38_capabilities(p);
-               p->t38.jointcapability = p->t38.capability;
        }
 
        /* Copy SIP extensions profile to peer */
@@ -14231,8 +14072,6 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
                else
                        p->noncodeccapability &= ~AST_RTP_DTMF;
                p->jointnoncodeccapability = p->noncodeccapability;
-               if (p->t38.peercapability)
-                       p->t38.jointcapability &= p->t38.peercapability;
                if (!dialog_initialize_rtp(p)) {
                        if (p->rtp) {
                                ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(p->rtp), p->rtp, &peer->prefs);
@@ -20394,9 +20233,6 @@ 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))) {
                        set_t38_capabilities(p);
-                       p->t38.jointcapability = p->t38.capability;
-                       set_t38_capabilities(p);
-                       p->t38.jointcapability = p->t38.capability;
                }
 
                /* We have a succesful authentication, process the SDP portion if there is one */
index e648007..db90e18 100644 (file)
@@ -319,7 +319,7 @@ enum ast_control_frame_type {
        AST_CONTROL_HOLD = 16,          /*!< Indicate call is placed on hold */
        AST_CONTROL_UNHOLD = 17,        /*!< Indicate call is left from hold */
        AST_CONTROL_VIDUPDATE = 18,     /*!< Indicate video frame update */
-       AST_CONTROL_T38 = 19,           /*!< T38 state change request/notification */
+       _XXX_AST_CONTROL_T38 = 19,      /*!< T38 state change request/notification \deprecated This is no longer supported. Use AST_CONTROL_T38_PARAMETERS instead. */
        AST_CONTROL_SRCUPDATE = 20,     /*!< Indicate source of media has changed */
        AST_CONTROL_TRANSFER = 21,      /*!< Indicate status of a transfer request */
        AST_CONTROL_CONNECTED_LINE = 22,/*!< Indicate connected line has changed */
@@ -350,14 +350,14 @@ enum ast_control_t38_rate_management {
 };
 
 struct ast_control_t38_parameters {
-       enum ast_control_t38 request_response;                /*!< Request or response of the T38 control frame */
-       unsigned int version;                                 /*!< Supported T.38 version */
-       unsigned int max_datagram;                            /*!< Maximum datagram size supported */
-       enum ast_control_t38_rate rate;                       /*!< Maximum fax rate supported */
-       enum ast_control_t38_rate_management rate_management; /*!< Rate management setting */
-       unsigned int fill_bit_removal:1;                      /*!< Set if fill bit removal should be used */
-       unsigned int transcoding_mmr:1;                       /*!< Set if MMR transcoding should be used */
-       unsigned int transcoding_jbig:1;                      /*!< Set if JBIG transcoding should be used */
+       enum ast_control_t38 request_response;                  /*!< Request or response of the T38 control frame */
+       unsigned int version;                                   /*!< Supported T.38 version */
+       unsigned int max_ifp;                                   /*!< Maximum IFP size supported */
+       enum ast_control_t38_rate rate;                         /*!< Maximum fax rate supported */
+       enum ast_control_t38_rate_management rate_management;   /*!< Rate management setting */
+       unsigned int fill_bit_removal:1;                        /*!< Set if fill bit removal can be used */
+       unsigned int transcoding_mmr:1;                         /*!< Set if MMR transcoding can be used */
+       unsigned int transcoding_jbig:1;                        /*!< Set if JBIG transcoding can be used */
 };
 
 enum ast_control_transfer {
index 4d6b890..72b9af4 100644 (file)
@@ -33,7 +33,7 @@
 #include "asterisk/channel.h"
 
 
-enum {
+enum ast_t38_ec_modes {
     UDPTL_ERROR_CORRECTION_NONE,
     UDPTL_ERROR_CORRECTION_FEC,
     UDPTL_ERROR_CORRECTION_REDUNDANCY
@@ -60,11 +60,11 @@ struct ast_udptl *ast_udptl_new(struct sched_context *sched, struct io_context *
 
 struct ast_udptl *ast_udptl_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int callbackmode, struct in_addr in);
 
-void ast_udptl_set_peer(struct ast_udptl *udptl, struct sockaddr_in *them);
+void ast_udptl_set_peer(struct ast_udptl *udptl, const struct sockaddr_in *them);
 
-void ast_udptl_get_peer(struct ast_udptl *udptl, struct sockaddr_in *them);
+void ast_udptl_get_peer(const struct ast_udptl *udptl, struct sockaddr_in *them);
 
-void ast_udptl_get_us(struct ast_udptl *udptl, struct sockaddr_in *us);
+void ast_udptl_get_us(const struct ast_udptl *udptl, struct sockaddr_in *us);
 
 void ast_udptl_destroy(struct ast_udptl *udptl);
 
@@ -78,37 +78,33 @@ int ast_udptl_write(struct ast_udptl *udptl, struct ast_frame *f);
 
 struct ast_frame *ast_udptl_read(struct ast_udptl *udptl);
 
-int ast_udptl_fd(struct ast_udptl *udptl);
+int ast_udptl_fd(const struct ast_udptl *udptl);
 
-int ast_udptl_setqos(struct ast_udptl *udptl, int tos, int cos);
+int ast_udptl_setqos(struct ast_udptl *udptl, unsigned int tos, unsigned int cos);
 
-void ast_udptl_set_m_type(struct ast_udptl* udptl, int pt);
+void ast_udptl_set_m_type(struct ast_udptl *udptl, unsigned int pt);
 
-void ast_udptl_set_udptlmap_type(struct ast_udptl* udptl, int pt,
-                        char* mimeType, char* mimeSubtype);
+void ast_udptl_set_udptlmap_type(struct ast_udptl *udptl, unsigned int pt,
+                                char *mimeType, char *mimeSubtype);
 
-int ast_udptl_lookup_code(struct ast_udptl* udptl, int isAstFormat, int code);
+int ast_udptl_get_error_correction_scheme(const struct ast_udptl *udptl);
 
-void ast_udptl_offered_from_local(struct ast_udptl* udptl, int local);
+void ast_udptl_set_error_correction_scheme(struct ast_udptl *udptl, enum ast_t38_ec_modes ec);
 
-int ast_udptl_get_error_correction_scheme(struct ast_udptl* udptl);
+void ast_udptl_set_local_max_ifp(struct ast_udptl *udptl, unsigned int max_ifp);
 
-void ast_udptl_set_error_correction_scheme(struct ast_udptl* udptl, int ec);
+unsigned int ast_udptl_get_local_max_datagram(const struct ast_udptl *udptl);
 
-int ast_udptl_get_local_max_datagram(struct ast_udptl* udptl);
+void ast_udptl_set_far_max_datagram(struct ast_udptl *udptl, unsigned int max_datagram);
 
-void ast_udptl_set_local_max_datagram(struct ast_udptl* udptl, int max_datagram);
+unsigned int ast_udptl_get_far_max_datagram(const struct ast_udptl *udptl);
 
-int ast_udptl_get_far_max_datagram(struct ast_udptl* udptl);
-
-void ast_udptl_set_far_max_datagram(struct ast_udptl* udptl, int max_datagram);
-
-void ast_udptl_get_current_formats(struct ast_udptl* udptl,
-                            int* astFormats, int* nonAstFormats);
+unsigned int ast_udptl_get_far_max_ifp(const struct ast_udptl *udptl);
 
 void ast_udptl_setnat(struct ast_udptl *udptl, int nat);
 
-int ast_udptl_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc);
+int ast_udptl_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags,
+                    struct ast_frame **fo, struct ast_channel **rc);
 
 int ast_udptl_proto_register(struct ast_udptl_protocol *proto);
 
index dce0747..1052d0f 100644 (file)
@@ -3488,11 +3488,11 @@ static int attribute_const is_visible_indication(enum ast_control_frame_type con
        case AST_CONTROL_TAKEOFFHOOK:
        case AST_CONTROL_ANSWER:
        case AST_CONTROL_HANGUP:
-       case AST_CONTROL_T38:
        case AST_CONTROL_CONNECTED_LINE:
        case AST_CONTROL_REDIRECTING:
        case AST_CONTROL_TRANSFER:
        case AST_CONTROL_T38_PARAMETERS:
+       case _XXX_AST_CONTROL_T38:
                break;
 
        case AST_CONTROL_CONGESTION:
@@ -3585,7 +3585,9 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
 
        /* Handle conditions that we have tones for. */
        switch (condition) {
-       case AST_CONTROL_T38:
+       case _XXX_AST_CONTROL_T38:
+               /* deprecated T.38 control frame */
+               return -1;
        case AST_CONTROL_T38_PARAMETERS:
                /* there is no way to provide 'default' behavior for these
                 * control frames, so we need to return failure, but there
@@ -5503,7 +5505,6 @@ static enum ast_bridge_result ast_generic_bridge(struct ast_channel *c0, struct
                        case AST_CONTROL_HOLD:
                        case AST_CONTROL_UNHOLD:
                        case AST_CONTROL_VIDUPDATE:
-                       case AST_CONTROL_T38:
                        case AST_CONTROL_SRCUPDATE:
                                ast_indicate_data(other, f->subclass, f->data.ptr, f->datalen);
                                if (jb_in_use) {
index 09c11db..fa44340 100644 (file)
@@ -839,24 +839,6 @@ void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix)
                case AST_CONTROL_UNHOLD:
                        strcpy(subclass, "Unhold");
                        break;
-               case AST_CONTROL_T38:
-                       if (f->datalen != sizeof(enum ast_control_t38)) {
-                               message = "Invalid";
-                       } else {
-                               enum ast_control_t38 state = *((enum ast_control_t38 *) f->data.ptr);
-                               if (state == AST_T38_REQUEST_NEGOTIATE)
-                                       message = "Negotiation Requested";
-                               else if (state == AST_T38_REQUEST_TERMINATE)
-                                       message = "Negotiation Request Terminated";
-                               else if (state == AST_T38_NEGOTIATED)
-                                       message = "Negotiated";
-                               else if (state == AST_T38_TERMINATED)
-                                       message = "Terminated";
-                               else if (state == AST_T38_REFUSED)
-                                       message = "Refused";
-                       }
-                       snprintf(subclass, sizeof(subclass), "T38/%s", message);
-                       break;
                case AST_CONTROL_T38_PARAMETERS:
                        if (f->datalen != sizeof(struct ast_control_t38_parameters *)) {
                                message = "Invalid";
index 138b7c0..5735028 100644 (file)
@@ -853,7 +853,6 @@ static enum ast_bridge_result local_bridge_loop(struct ast_channel *c0, struct a
                        if ((fr->subclass == AST_CONTROL_HOLD) ||
                            (fr->subclass == AST_CONTROL_UNHOLD) ||
                            (fr->subclass == AST_CONTROL_VIDUPDATE) ||
-                           (fr->subclass == AST_CONTROL_T38) ||
                            (fr->subclass == AST_CONTROL_SRCUPDATE) ||
                            (fr->subclass == AST_CONTROL_T38_PARAMETERS)) {
                                /* If we are going on hold, then break callback mode and P2P bridging */
@@ -1073,7 +1072,6 @@ static enum ast_bridge_result remote_bridge_loop(struct ast_channel *c0, struct
                        if ((fr->subclass == AST_CONTROL_HOLD) ||
                            (fr->subclass == AST_CONTROL_UNHOLD) ||
                            (fr->subclass == AST_CONTROL_VIDUPDATE) ||
-                           (fr->subclass == AST_CONTROL_T38) ||
                            (fr->subclass == AST_CONTROL_SRCUPDATE) ||
                            (fr->subclass == AST_CONTROL_T38_PARAMETERS)) {
                                if (fr->subclass == AST_CONTROL_HOLD) {
index 7f7a2b7..86a1982 100644 (file)
@@ -81,7 +81,7 @@ static struct sockaddr_in udptldebugaddr;   /*!< Debug packets to/from this host
 #ifdef SO_NO_CHECK
 static int nochecksums;
 #endif
-static int udptlfectype;
+static enum ast_t38_ec_modes udptlfectype;
 static int udptlfecentries;
 static int udptlfecspan;
 static int udptlmaxdatagram;
@@ -100,10 +100,10 @@ typedef struct {
 typedef struct {
        int buf_len;
        uint8_t buf[LOCAL_FAX_MAX_DATAGRAM];
-       int fec_len[MAX_FEC_ENTRIES];
+       unsigned int fec_len[MAX_FEC_ENTRIES];
        uint8_t fec[MAX_FEC_ENTRIES][LOCAL_FAX_MAX_DATAGRAM];
-       int fec_span;
-       int fec_entries;
+       unsigned int fec_span;
+       unsigned int fec_entries;
 } udptl_fec_rx_buffer_t;
 
 /*! \brief Structure for an UDPTL session */
@@ -122,35 +122,51 @@ struct ast_udptl {
        struct io_context *io;
        void *data;
        ast_udptl_callback callback;
-       int udptl_offered_from_local;
 
        /*! This option indicates the error correction scheme used in transmitted UDPTL
-           packets. */
-       int error_correction_scheme;
+        *    packets and expected in received UDPTL packets.
+        */
+       enum ast_t38_ec_modes error_correction_scheme;
 
        /*! This option indicates the number of error correction entries transmitted in
-           UDPTL packets. */
-       int error_correction_entries;
+        *  UDPTL packets and expected in received UDPTL packets.
+        */
+       unsigned int error_correction_entries;
 
        /*! This option indicates the span of the error correction entries in transmitted
-           UDPTL packets (FEC only). */
-       int error_correction_span;
-
-       /*! This option indicates the maximum size of a UDPTL packet that can be accepted by
-           the remote device. */
-       int far_max_datagram_size;
-
-       /*! This option indicates the maximum size of a UDPTL packet that we are prepared to
-           accept. */
-       int local_max_datagram_size;
+        *  UDPTL packets (FEC only).
+        */
+       unsigned int error_correction_span;
+
+       /*! The maximum size UDPTL packet that can be accepted by
+        *  the remote device.
+        */
+       unsigned int far_max_datagram;
+
+       /*! The maximum size UDPTL packet that we are prepared to
+        *  accept.
+        */
+       unsigned int local_max_datagram;
+
+       /*! The maximum IFP that can be submitted for sending
+        * to the remote device. Calculated from far_max_datagram,
+        * error_correction_scheme and error_correction_entries.
+        */
+       unsigned int far_max_ifp;
+
+       /*! The maximum IFP that the local endpoint is prepared
+        * to accept. Along with error_correction_scheme and
+        * error_correction_entries, used to calculate local_max_datagram.
+        */
+       unsigned int local_max_ifp;
 
        int verbose;
 
        struct sockaddr_in far;
 
-       int tx_seq_no;
-       int rx_seq_no;
-       int rx_expected_seq_no;
+       unsigned int tx_seq_no;
+       unsigned int rx_seq_no;
+       unsigned int rx_expected_seq_no;
 
        udptl_fec_tx_buffer_t tx[UDPTL_BUF_MASK + 1];
        udptl_fec_rx_buffer_t rx[UDPTL_BUF_MASK + 1];
@@ -158,23 +174,20 @@ struct ast_udptl {
 
 static AST_RWLIST_HEAD_STATIC(protos, ast_udptl_protocol);
 
-static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len);
-static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, int buflen, uint8_t *ifp, int ifp_len);
-
-static inline int udptl_debug_test_addr(struct sockaddr_in *addr)
+static inline int udptl_debug_test_addr(const struct sockaddr_in *addr)
 {
        if (udptldebug == 0)
                return 0;
        if (udptldebugaddr.sin_addr.s_addr) {
-               if (((ntohs(udptldebugaddr.sin_port) != 0)
-                       && (udptldebugaddr.sin_port != addr->sin_port))
-                       || (udptldebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
+               if (((ntohs(udptldebugaddr.sin_port) != 0) &&
+                    (udptldebugaddr.sin_port != addr->sin_port)) ||
+                   (udptldebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
                        return 0;
        }
        return 1;
 }
 
-static int decode_length(uint8_t *buf, int limit, int *len, int *pvalue)
+static int decode_length(uint8_t *buf, unsigned int limit, unsigned int *len, unsigned int *pvalue)
 {
        if (*len >= limit)
                return -1;
@@ -199,15 +212,16 @@ static int decode_length(uint8_t *buf, int limit, int *len, int *pvalue)
 }
 /*- End of function --------------------------------------------------------*/
 
-static int decode_open_type(uint8_t *buf, int limit, int *len, const uint8_t **p_object, int *p_num_octets)
+static int decode_open_type(uint8_t *buf, unsigned int limit, unsigned int *len, const uint8_t **p_object, unsigned int *p_num_octets)
 {
-       int octet_cnt;
-       int octet_idx;
-       int length;
-       int i;
+       unsigned int octet_cnt;
+       unsigned int octet_idx;
+       unsigned int length;
+       unsigned int i;
        const uint8_t **pbuf;
 
        for (octet_idx = 0, *p_num_octets = 0; ; octet_idx += octet_cnt) {
+               octet_cnt = 0;
                if ((length = decode_length(buf, limit, len, &octet_cnt)) < 0)
                        return -1;
                if (octet_cnt > 0) {
@@ -229,9 +243,9 @@ static int decode_open_type(uint8_t *buf, int limit, int *len, const uint8_t **p
 }
 /*- End of function --------------------------------------------------------*/
 
-static int encode_length(uint8_t *buf, int *len, int value)
+static unsigned int encode_length(uint8_t *buf, unsigned int *len, unsigned int value)
 {
-       int multiplier;
+       unsigned int multiplier;
 
        if (value < 0x80) {
                /* 1 octet */
@@ -257,10 +271,10 @@ static int encode_length(uint8_t *buf, int *len, int value)
 }
 /*- End of function --------------------------------------------------------*/
 
-static int encode_open_type(uint8_t *buf, int buflen, int *len, const uint8_t *data, int num_octets)
+static int encode_open_type(uint8_t *buf, unsigned int buflen, unsigned int *len, const uint8_t *data, unsigned int num_octets)
 {
-       int enclen;
-       int octet_idx;
+       unsigned int enclen;
+       unsigned int octet_idx;
        uint8_t zero_byte;
 
        /* If open type is of zero length, add a single zero byte (10.1) */
@@ -289,7 +303,7 @@ static int encode_open_type(uint8_t *buf, int buflen, int *len, const uint8_t *d
 }
 /*- End of function --------------------------------------------------------*/
 
-static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len)
+static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, unsigned int len)
 {
        int stat1;
        int stat2;
@@ -301,16 +315,16 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len)
        int x;
        int limit;
        int which;
-       int ptr;
-       int count;
+       unsigned int ptr;
+       unsigned int count;
        int total_count;
        int seq_no;
        const uint8_t *ifp;
        const uint8_t *data;
-       int ifp_len;
+       unsigned int ifp_len;
        int repaired[16];
        const uint8_t *bufs[16];
-       int lengths[16];
+       unsigned int lengths[16];
        int span;
        int entries;
        int ifp_no;
@@ -497,7 +511,7 @@ static int udptl_rx_packet(struct ast_udptl *s, uint8_t *buf, int len)
 }
 /*- End of function --------------------------------------------------------*/
 
-static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, int buflen, uint8_t *ifp, int ifp_len)
+static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, unsigned int buflen, uint8_t *ifp, unsigned int ifp_len)
 {
        uint8_t fec[LOCAL_FAX_MAX_DATAGRAM * 2];
        int i;
@@ -507,7 +521,7 @@ static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, int buflen, uin
        int entries;
        int span;
        int m;
-       int len;
+       unsigned int len;
        int limit;
        int high_tide;
 
@@ -612,7 +626,7 @@ static int udptl_build_packet(struct ast_udptl *s, uint8_t *buf, int buflen, uin
        return len;
 }
 
-int ast_udptl_fd(struct ast_udptl *udptl)
+int ast_udptl_fd(const struct ast_udptl *udptl)
 {
        return udptl->fd;
 }
@@ -695,15 +709,67 @@ struct ast_frame *ast_udptl_read(struct ast_udptl *udptl)
        return &udptl->f[0];
 }
 
-void ast_udptl_offered_from_local(struct ast_udptl* udptl, int local)
+static void calculate_local_max_datagram(struct ast_udptl *udptl)
 {
-       if (udptl)
-               udptl->udptl_offered_from_local = local;
-       else
-               ast_log(LOG_WARNING, "udptl structure is null\n");
+       unsigned int new_max = 200;
+
+       /* calculate the amount of space required to receive an IFP
+        * using the current error correction mode, and ensure that our
+        * local max datagram size is at least that big
+        */
+       switch (udptl->error_correction_scheme) {
+       case UDPTL_ERROR_CORRECTION_NONE:
+               /* only need room for sequence number and length indicators */
+               new_max = 6 + udptl->local_max_ifp;
+               break;
+       case UDPTL_ERROR_CORRECTION_REDUNDANCY:
+               /* need room for sequence number, length indicators and the
+                * configured number of redundant packets
+                */
+               new_max = 6 + udptl->local_max_ifp + 2 + (udptl->error_correction_entries * udptl->local_max_ifp);
+               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 = 6 + udptl->local_max_ifp + 4 + udptl->local_max_ifp;
+               break;
+       }
+       /* add 25% of extra space for insurance, but no larger than LOCAL_FAX_MAX_DATAGRAM */
+       udptl->local_max_datagram = MIN(new_max * 1.25, LOCAL_FAX_MAX_DATAGRAM);
+}
+
+static void calculate_far_max_ifp(struct ast_udptl *udptl)
+{
+       unsigned new_max = 40;
+
+       /* calculate the maximum IFP the local endpoint should
+        * generate based on the far end's maximum datagram size
+        * 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 = 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 = (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 = (udptl->far_max_datagram - 10) / 2;
+               break;
+       }
+       /* subtract 25% of space for insurance */
+       udptl->far_max_ifp = new_max * 0.75;
 }
 
-int ast_udptl_get_error_correction_scheme(struct ast_udptl* udptl)
+int ast_udptl_get_error_correction_scheme(const struct ast_udptl *udptl)
 {
        if (udptl)
                return udptl->error_correction_scheme;
@@ -713,60 +779,75 @@ int ast_udptl_get_error_correction_scheme(struct ast_udptl* udptl)
        }
 }
 
-void ast_udptl_set_error_correction_scheme(struct ast_udptl* udptl, int ec)
+void ast_udptl_set_error_correction_scheme(struct ast_udptl *udptl, enum ast_t38_ec_modes ec)
 {
        if (udptl) {
+               udptl->error_correction_scheme = ec;
                switch (ec) {
                case UDPTL_ERROR_CORRECTION_FEC:
                        udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_FEC;
+                       if (udptl->error_correction_entries == 0) {
+                               udptl->error_correction_entries = 3;
+                       }
+                       if (udptl->error_correction_span == 0) {
+                               udptl->error_correction_span = 3;
+                       }
                        break;
                case UDPTL_ERROR_CORRECTION_REDUNDANCY:
                        udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_REDUNDANCY;
-                       break;
-               case UDPTL_ERROR_CORRECTION_NONE:
-                       udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_NONE;
+                       if (udptl->error_correction_entries == 0) {
+                               udptl->error_correction_entries = 3;
+                       }
                        break;
                default:
-                       ast_log(LOG_WARNING, "error correction parameter invalid\n");
+                       /* nothing to do */
+                       break;
                };
+               calculate_local_max_datagram(udptl);
+               calculate_far_max_ifp(udptl);
        } else
                ast_log(LOG_WARNING, "udptl structure is null\n");
 }
 
-int ast_udptl_get_local_max_datagram(struct ast_udptl* udptl)
+unsigned int ast_udptl_get_local_max_datagram(const struct ast_udptl *udptl)
 {
        if (udptl)
-               return udptl->local_max_datagram_size;
+               return udptl->local_max_datagram;
        else {
                ast_log(LOG_WARNING, "udptl structure is null\n");
-               return -1;
+               return 0;
        }
 }
 
-int ast_udptl_get_far_max_datagram(struct ast_udptl* udptl)
+unsigned int ast_udptl_get_far_max_datagram(const struct ast_udptl *udptl)
 {
        if (udptl)
-               return udptl->far_max_datagram_size;
+               return udptl->far_max_datagram;
        else {
                ast_log(LOG_WARNING, "udptl structure is null\n");
-               return -1;
+               return 0;
        }
 }
 
-void ast_udptl_set_local_max_datagram(struct ast_udptl* udptl, int max_datagram)
+void ast_udptl_set_far_max_datagram(struct ast_udptl *udptl, unsigned int max_datagram)
 {
-       if (udptl)
-               udptl->local_max_datagram_size = max_datagram;
-       else
+       if (udptl) {
+               udptl->far_max_datagram = max_datagram;
+               calculate_far_max_ifp(udptl);
+       } else {
                ast_log(LOG_WARNING, "udptl structure is null\n");
+       }
 }
 
-void ast_udptl_set_far_max_datagram(struct ast_udptl* udptl, int max_datagram)
+void ast_udptl_set_local_max_ifp(struct ast_udptl *udptl, unsigned int max_ifp)
 {
-       if (udptl)
-               udptl->far_max_datagram_size = max_datagram;
-       else
-               ast_log(LOG_WARNING, "udptl structure is null\n");
+       udptl->local_max_ifp = max_ifp;
+       calculate_local_max_datagram(udptl);
+}
+
+unsigned int ast_udptl_get_far_max_ifp(const struct ast_udptl *udptl)
+{
+       return udptl->far_max_ifp;
 }
 
 struct ast_udptl *ast_udptl_new_with_bindaddr(struct sched_context *sched, struct io_context *io, int callbackmode, struct in_addr addr)
@@ -780,20 +861,13 @@ struct ast_udptl *ast_udptl_new_with_bindaddr(struct sched_context *sched, struc
        if (!(udptl = ast_calloc(1, sizeof(*udptl))))
                return NULL;
 
-       if (udptlfectype == 2)
-               udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_FEC;
-       else if (udptlfectype == 1)
-               udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_REDUNDANCY;
-       else
-               udptl->error_correction_scheme = UDPTL_ERROR_CORRECTION_NONE;
+       udptl->error_correction_scheme = udptlfectype;
        udptl->error_correction_span = udptlfecspan;
        udptl->error_correction_entries = udptlfecentries;
        
-       udptl->far_max_datagram_size = udptlmaxdatagram;
-       udptl->local_max_datagram_size = udptlmaxdatagram;
+       udptl->far_max_datagram = udptlmaxdatagram;
+       udptl->local_max_datagram = udptlmaxdatagram;
 
-       memset(&udptl->rx, 0, sizeof(udptl->rx));
-       memset(&udptl->tx, 0, sizeof(udptl->tx));
        for (i = 0; i <= UDPTL_BUF_MASK; i++) {
                udptl->rx[i].buf_len = -1;
                udptl->tx[i].buf_len = -1;
@@ -852,18 +926,18 @@ struct ast_udptl *ast_udptl_new(struct sched_context *sched, struct io_context *
        return ast_udptl_new_with_bindaddr(sched, io, callbackmode, ia);
 }
 
-int ast_udptl_setqos(struct ast_udptl *udptl, int tos, int cos)
+int ast_udptl_setqos(struct ast_udptl *udptl, unsigned int tos, unsigned int cos)
 {
        return ast_netsock_set_qos(udptl->fd, tos, cos, "UDPTL");
 }
 
-void ast_udptl_set_peer(struct ast_udptl *udptl, struct sockaddr_in *them)
+void ast_udptl_set_peer(struct ast_udptl *udptl, const struct sockaddr_in *them)
 {
        udptl->them.sin_port = them->sin_port;
        udptl->them.sin_addr = them->sin_addr;
 }
 
-void ast_udptl_get_peer(struct ast_udptl *udptl, struct sockaddr_in *them)
+void ast_udptl_get_peer(const struct ast_udptl *udptl, struct sockaddr_in *them)
 {
        memset(them, 0, sizeof(*them));
        them->sin_family = AF_INET;
@@ -871,7 +945,7 @@ void ast_udptl_get_peer(struct ast_udptl *udptl, struct sockaddr_in *them)
        them->sin_addr = udptl->them.sin_addr;
 }
 
-void ast_udptl_get_us(struct ast_udptl *udptl, struct sockaddr_in *us)
+void ast_udptl_get_us(const struct ast_udptl *udptl, struct sockaddr_in *us)
 {
        memcpy(us, &udptl->us, sizeof(udptl->us));
 }
@@ -893,10 +967,10 @@ void ast_udptl_destroy(struct ast_udptl *udptl)
 
 int ast_udptl_write(struct ast_udptl *s, struct ast_frame *f)
 {
-       int seq;
-       int len;
+       unsigned int seq;
+       unsigned int len;
        int res;
-       uint8_t buf[LOCAL_FAX_MAX_DATAGRAM * 2];
+       uint8_t buf[s->far_max_datagram];
 
        /* If we have no peer, return immediately */    
        if (s->them.sin_addr.s_addr == INADDR_ANY)
@@ -906,11 +980,16 @@ int ast_udptl_write(struct ast_udptl *s, struct ast_frame *f)
        if (f->datalen == 0)
                return 0;
        
-       if (f->frametype != AST_FRAME_MODEM) {
-               ast_log(LOG_WARNING, "UDPTL can only send T.38 data\n");
+       if ((f->frametype != AST_FRAME_MODEM) ||
+           (f->subclass != AST_MODEM_T38)) {
+               ast_log(LOG_WARNING, "UDPTL can only send T.38 data.\n");
                return -1;
        }
 
+       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);
+       }
+
        /* Save seq_no for debug output because udptl_build_packet increments it */
        seq = s->tx_seq_no & 0xFFFF;
 
@@ -1175,7 +1254,7 @@ static void __ast_udptl_reload(int reload)
 
        udptlstart = 4500;
        udptlend = 4999;
-       udptlfectype = 0;
+       udptlfectype = UDPTL_ERROR_CORRECTION_NONE;
        udptlfecentries = 0;
        udptlfecspan = 0;
        udptlmaxdatagram = 0;
@@ -1216,9 +1295,9 @@ static void __ast_udptl_reload(int reload)
                }
                if ((s = ast_variable_retrieve(cfg, "general", "T38FaxUdpEC"))) {
                        if (strcmp(s, "t38UDPFEC") == 0)
-                               udptlfectype = 2;
+                               udptlfectype = UDPTL_ERROR_CORRECTION_FEC;
                        else if (strcmp(s, "t38UDPRedundancy") == 0)
-                               udptlfectype = 1;
+                               udptlfectype = UDPTL_ERROR_CORRECTION_REDUNDANCY;
                }
                if ((s = ast_variable_retrieve(cfg, "general", "T38FaxMaxDatagram"))) {
                        udptlmaxdatagram = atoi(s);