From fdc0f2ecdb18b9176f87f55ee17f054ce107e8c7 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Sun, 25 Oct 2009 09:02:07 +0000 Subject: Initial commit for ticket #950: QoS support: - implementation: - PJLIB (sock_qos*.*) - added QoS support in: - SIP UDP transport, - SIP TCP transport, - media UDP transport (done in pjsua-lib), - pjnath ICE stream transport, - pjnath STUN socket, - pjnath TURN client - added QoS options in pjsua-lib: - QoS fields in pjsua_transport_config - added "--set-qos" parameter in pjsua Notes: - QoS in TLS transport is not yet implemented, waiting for #957 - build ok on VS6, VS2005 (multiple targets), Carbide, and Mingw - no run-time testing yet git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@2966 74dad513-b988-da41-8d7b-12977e46ad98 --- build.symbian/pjlib.mmp | 2 + pjlib/build/Makefile | 3 +- pjlib/build/pjlib.dsp | 12 + pjlib/build/pjlib.vcproj | 2442 ++++++++++++++++--------------- pjlib/include/pj/config.h | 31 + pjlib/include/pj/sock.h | 10 + pjlib/include/pj/sock_qos.h | 427 ++++++ pjlib/include/pjlib.h | 1 + pjlib/src/pj/sock_bsd.c | 14 +- pjlib/src/pj/sock_common.c | 5 + pjlib/src/pj/sock_qos_bsd.c | 130 ++ pjlib/src/pj/sock_qos_common.c | 154 ++ pjlib/src/pj/sock_qos_dummy.c | 76 + pjlib/src/pj/sock_qos_symbian.cpp | 91 ++ pjlib/src/pj/sock_qos_wm.c | 103 ++ pjlib/src/pj/sock_symbian.cpp | 1 + pjnath/include/pjnath/config.h | 4 +- pjnath/include/pjnath/ice_strans.h | 35 + pjnath/include/pjnath/stun_sock.h | 27 + pjnath/include/pjnath/turn_sock.h | 49 +- pjnath/src/pjnath/ice_strans.c | 31 +- pjnath/src/pjnath/stun_sock.c | 15 +- pjnath/src/pjnath/turn_sock.c | 31 +- pjsip-apps/src/pjsua/pjsua_app.c | 20 +- pjsip/include/pjsip/sip_transport_tcp.h | 87 ++ pjsip/include/pjsua-lib/pjsua.h | 18 + pjsip/src/pjsip/sip_transport_tcp.c | 134 +- pjsip/src/pjsua-lib/pjsua_core.c | 46 +- pjsip/src/pjsua-lib/pjsua_media.c | 30 +- 29 files changed, 2742 insertions(+), 1287 deletions(-) create mode 100644 pjlib/include/pj/sock_qos.h create mode 100644 pjlib/src/pj/sock_qos_bsd.c create mode 100644 pjlib/src/pj/sock_qos_common.c create mode 100644 pjlib/src/pj/sock_qos_dummy.c create mode 100644 pjlib/src/pj/sock_qos_symbian.cpp create mode 100644 pjlib/src/pj/sock_qos_wm.c diff --git a/build.symbian/pjlib.mmp b/build.symbian/pjlib.mmp index 1385b974..14576e33 100644 --- a/build.symbian/pjlib.mmp +++ b/build.symbian/pjlib.mmp @@ -50,6 +50,7 @@ SOURCE pool_caching.c SOURCE rand.c SOURCE rbtree.c SOURCE sock_common.c +SOURCE sock_qos_common.c SOURCE types.c @@ -75,6 +76,7 @@ SOURCE ssl_sock_common.c SOURCE ssl_sock_symbian.cpp SOURCE sock_symbian.cpp SOURCE sock_select_symbian.cpp +SOURCE sock_qos_symbian.cpp SOURCE timer_symbian.cpp SOURCE unicode_symbian.cpp diff --git a/pjlib/build/Makefile b/pjlib/build/Makefile index c3819f34..439c7fa6 100644 --- a/pjlib/build/Makefile +++ b/pjlib/build/Makefile @@ -25,7 +25,8 @@ export PJLIB_OBJS += $(OS_OBJS) $(M_OBJS) $(CC_OBJS) $(HOST_OBJS) \ activesock.o array.o config.o ctype.o errno.o except.o fifobuf.o \ guid.o hash.o ip_helper_generic.o list.o lock.o log.o os_time_common.o \ pool.o pool_buf.o pool_caching.o pool_dbg.o rand.o \ - rbtree.o sock_common.o string.o timer.o types.o + rbtree.o sock_common.o sock_qos_common.o sock_qos_bsd.o \ + string.o timer.o types.o export PJLIB_CFLAGS += $(_CFLAGS) ############################################################################### diff --git a/pjlib/build/pjlib.dsp b/pjlib/build/pjlib.dsp index 39d800cd..2b042567 100644 --- a/pjlib/build/pjlib.dsp +++ b/pjlib/build/pjlib.dsp @@ -341,6 +341,14 @@ SOURCE=..\src\pj\sock_common.c # End Source File # Begin Source File +SOURCE=..\src\pj\sock_qos_bsd.c +# End Source File +# Begin Source File + +SOURCE=..\src\pj\sock_qos_common.c +# End Source File +# Begin Source File + SOURCE=..\src\pj\sock_select.c # End Source File # Begin Source File @@ -589,6 +597,10 @@ SOURCE=..\include\pj\sock.h # End Source File # Begin Source File +SOURCE=..\include\pj\sock_qos.h +# End Source File +# Begin Source File + SOURCE=..\include\pj\sock_select.h # End Source File # Begin Source File diff --git a/pjlib/build/pjlib.vcproj b/pjlib/build/pjlib.vcproj index 2f036039..7f4ee00e 100644 --- a/pjlib/build/pjlib.vcproj +++ b/pjlib/build/pjlib.vcproj @@ -34,11 +34,11 @@ + + + + - - + + + + - - + + - - - - - - + + + + + + + + @@ -5893,17 +5909,15 @@ /> - - - + + + + + diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h index 0ae89972..71ca8a04 100644 --- a/pjlib/include/pj/config.h +++ b/pjlib/include/pj/config.h @@ -748,6 +748,37 @@ # define PJ_HAS_STRICMP_ALNUM 0 #endif + +/* + * Types of QoS backend implementation. + */ + +/** + * Dummy QoS backend implementation, will always return error on all + * the APIs. + */ +#define PJ_QOS_DUMMY 1 + +/** QoS backend based on setsockopt(IP_TOS) */ +#define PJ_QOS_BSD 2 + +/** QoS backend for Windows Mobile 6 */ +#define PJ_QOS_WM 3 + +/** QoS backend for Symbian */ +#define PJ_QOS_SYMBIAN 4 + +/** + * Force the use of some QoS backend API for some platforms. + */ +#ifndef PJ_QOS_IMPLEMENTATION +# if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE && _WIN32_WCE >= 0x502 + /* Windows Mobile 6 or later */ +# define PJ_QOS_IMPLEMENTATION PJ_QOS_WM +# endif +#endif + + /** @} */ /******************************************************************** diff --git a/pjlib/include/pj/sock.h b/pjlib/include/pj/sock.h index a72b63e3..3245367e 100644 --- a/pjlib/include/pj/sock.h +++ b/pjlib/include/pj/sock.h @@ -297,6 +297,10 @@ extern const pj_uint16_t PJ_TCP_NODELAY; * @see pj_SO_REUSEADDR */ extern const pj_uint16_t PJ_SO_REUSEADDR; +/** Set the protocol-defined priority for all packets to be sent on socket. + */ +extern const pj_uint16_t PJ_SO_PRIORITY; + /** IP multicast interface. @see pj_IP_MULTICAST_IF() */ extern const pj_uint16_t PJ_IP_MULTICAST_IF; @@ -329,6 +333,9 @@ extern const pj_uint16_t PJ_IP_DROP_MEMBERSHIP; /** Get #PJ_SO_REUSEADDR constant */ PJ_DECL(pj_uint16_t) pj_SO_REUSEADDR(void); + /** Get #PJ_SO_PRIORITY constant */ + PJ_DECL(pj_uint16_t) pj_SO_PRIORITY(void); + /** Get #PJ_IP_MULTICAST_IF constant */ PJ_DECL(pj_uint16_t) pj_IP_MULTICAST_IF(void); @@ -359,6 +366,9 @@ extern const pj_uint16_t PJ_IP_DROP_MEMBERSHIP; /** Get #PJ_SO_REUSEADDR constant */ # define pj_SO_REUSEADDR() PJ_SO_REUSEADDR + /** Get #PJ_SO_PRIORITY constant */ +# define pj_SO_PRIORITY() PJ_SO_PRIORITY + /** Get #PJ_IP_MULTICAST_IF constant */ # define pj_IP_MULTICAST_IF() PJ_IP_MULTICAST_IF diff --git a/pjlib/include/pj/sock_qos.h b/pjlib/include/pj/sock_qos.h new file mode 100644 index 00000000..12dd4b80 --- /dev/null +++ b/pjlib/include/pj/sock_qos.h @@ -0,0 +1,427 @@ +/* $Id$ */ +/* + * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) + * Copyright (C) 2003-2008 Benny Prijono + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __PJ_SOCK_QOS_H__ +#define __PJ_SOCK_QOS_H__ + +/** + * @file sock_qos.h + * @brief Socket QoS API + */ + +#include + +PJ_BEGIN_DECL + + +/** + * @defgroup socket_qos Socket Quality of Service (QoS) API + * @ingroup PJ_SOCK + * @{ + + + \section intro QoS Technologies + + QoS settings are available for both Layer 2 and 3 of TCP/IP protocols: + + \subsection intro_ieee8021p Layer 2: IEEE 802.1p for Ethernet + + IEEE 802.1p tagging will mark frames sent by a host for prioritized + delivery using a 3-bit Priority field in the virtual local area network + (VLAN) header of the Ethernet frame. The VLAN header is placed inside + the Ethernet header, between the Source Address field and either the + Length field (for an IEEE 802.3 frame) or the EtherType field (for an + Ethernet II frame). + + \subsection intro_wmm Layer 2: WMM + + At the Network Interface layer for IEEE 802.11 wireless, the Wi-Fi + Alliance certification for Wi-Fi Multimedia (WMM) defines four access + categories for prioritizing network traffic. These access categories + are (in order of highest to lowest priority) voice, video, best-effort, + and background. Host support for WMM prioritization requires that both + wireless network adapters and their drivers support WMM. Wireless + access points (APs) must have WMM enabled. + + \subsection intro_dscp Layer 3: DSCP + + At the Internet layer, you can use Differentiated Services/Diffserv and + set the value of the Differentiated Services Code Point (DSCP) in the + IP header. As defined in RFC 2472, the DSCP value is the high-order 6 bits + of the IP version 4 (IPv4) TOS field and the IP version 6 (IPv6) Traffic + Class field. + + \subsection intro_other Layer 3: Other + + Other mechanisms exist (such as RSVP, IntServ) but this will not be + implemented. + + + \section availability QoS Availability + + \subsection linux Linux + + DSCP is available via IP TOS option. + + Ethernet 802.1p tagging is done by setting setsockopt(SO_PRIORITY) option + of the socket, then with the set_egress_map option of the vconfig utility + to convert this to set vlan-qos field of the packet. + + WMM is not known to be available. + + \subsection windows Windows and Windows Mobile + + (It's a mess!) + + DSCP is settable with setsockopt() on Windows 2000 or older, but Windows + would silently ignore this call on WinXP or later, unless administrator + modifies the registry. On Windows 2000, Windows XP, and Windows Server + 2003, GQoS (Generic QoS) API is the standard API, but this API may not be + supported in the future. On Vista and Windows 7, the is a new QoS2 API, + also known as Quality Windows Audio-Video Experience (qWAVE). + + IEEE 802.1p tagging is available via Traffic Control (TC) API, available + on Windows XP SP2, but this needs administrator access. For Vista and + later, it's in qWAVE. + + WMM is available for mobile platforms on Windows Mobile 6 platform and + Windows Embedded CE 6, via setsockopt(IP_DSCP_TRAFFIC_TYPE). qWAVE + supports this as well. + + \subsection symbian Symbian S60 3rd Ed + + Both DSCP and WMM is supported via RSocket::SetOpt() with will set both + Layer 2 and Layer 3 QoS settings accordingly. Internally, PJLIB sets the + DSCP field of the socket, and based on certain DSCP values mapping, + Symbian will set the WMM tag accordingly. + + \section api PJLIB's QoS API Abstraction + + Based on the above, the following API is implemented. + + Declare the following "standard" traffic types. + + \code + typedef enum pj_qos_type + { + PJ_QOS_TYPE_BEST_EFFORT, + PJ_QOS_TYPE_BACKGROUND, + PJ_QOS_TYPE_VIDEO, + PJ_QOS_TYPE_VOICE, + PJ_QOS_TYPE_CONTROL + } pj_qos_type; + \endcode + + The traffic classes above will determine how the Layer 2 and 3 QoS + settings will be used. The standard mapping between the classes above + to the corresponding Layer 2 and 3 settings are as follows: + + \code + ================================================================= + PJLIB Traffic Type IP DSCP WMM 802.1p + ----------------------------------------------------------------- + BEST_EFFORT 0x00 BE (Bulk Effort) 0 + BACKGROUND 0x08 BK (Bulk) 2 + VIDEO 0x28 VI (Video) 5 + VOICE 0x30 VO (Voice) 6 + CONTROL 0x38 VO (Voice) 7 + ================================================================= + \endcode + + There are two sets of API provided to manipulate the QoS parameters. + + \subsection portable_api Portable API + + The first set of API is: + + \code + // Set QoS parameters + PJ_DECL(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, + pj_qos_type val); + + // Get QoS parameters + PJ_DECL(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, + pj_qos_type *p_val); + \endcode + + The API will set the traffic type according to the DSCP class, for both + Layer 2 and Layer 3 QoS settings, where it's available. If any of the + layer QoS setting is not settable, the API will silently ignore it. + If both layers are not setable, the API will return error. + + The API above is the recommended use of QoS, since it is the most + portable across all platforms. + + \subsection detail_api Fine Grained Control API + + The second set of API is intended for application that wants to fine + tune the QoS parameters. + + The Layer 2 and 3 QoS parameters are stored in pj_qos_params structure: + + \code + typedef enum pj_qos_flag + { + PJ_QOS_PARAM_HAS_DSCP = 1, + PJ_QOS_PARAM_HAS_802_1_P = 2, + PJ_QOS_PARAM_HAS_WMM = 4 + } pj_qos_flag; + + typedef enum pj_qos_wmm_prio + { + PJ_QOS_WMM_PRIO_BULK_EFFORT, + PJ_QOS_WMM_PRIO_BULK, + PJ_QOS_WMM_PRIO_VIDEO, + PJ_QOS_WMM_PRIO_VOICE + } pj_qos_wmm_prio; + + typedef struct pj_qos_params + { + pj_uint8_t flags; // Determines which values to + // set, bitmask of pj_qos_flag + pj_uint8_t dscp_val; // DSCP value to set + pj_uint8_t so_prio; // SO_PRIORITY value + pj_qos_wmm_prio wmm_prio; // WMM priority value + } pj_qos_params; + \endcode + + The second set of API with more fine-grained control over the parameters + are: + + \code + // Retrieve QoS params for the specified traffic type + PJ_DECL(pj_status_t) pj_qos_get_params(pj_qos_type type, + pj_qos_params *p); + + // Set QoS parameters to the socket + PJ_DECL(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, + const pj_qos_params *p); + + // Get QoS parameters from the socket + PJ_DECL(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, + pj_qos_params *p); + \endcode + + + Important: + + The pj_sock_set/get_qos_params() APIs are not portable, and it's probably + only going to be implemented on Linux. Application should always try to + use pj_sock_set_qos_type() instead. + */ + + +/** + * High level traffic classification. + */ +typedef enum pj_qos_type +{ + PJ_QOS_TYPE_BEST_EFFORT, /**< Best effort traffic (default value). + Any QoS function calls with specifying + this value are effectively no-op */ + PJ_QOS_TYPE_BACKGROUND, /**< Background traffic. */ + PJ_QOS_TYPE_VIDEO, /**< Video traffic. */ + PJ_QOS_TYPE_VOICE, /**< Voice traffic. */ + PJ_QOS_TYPE_CONTROL /**< Control traffic. */ +} pj_qos_type; + +/** + * Bitmask flag to indicate which QoS layer setting is set in the + * \a flags field of the #pj_qos_params structure. + */ +typedef enum pj_qos_flag +{ + PJ_QOS_PARAM_HAS_DSCP = 1, /**< DSCP field is set. */ + PJ_QOS_PARAM_HAS_802_1_P = 2, /**< IEEE 802.1p field is set */ + PJ_QOS_PARAM_HAS_WMM = 4 /**< WMM field is set. */ +} pj_qos_flag; + + +/** + * Standard WMM priorities. + */ +typedef enum pj_qos_wmm_prio +{ + PJ_QOS_WMM_PRIO_BULK_EFFORT, /**< Bulk effort priority */ + PJ_QOS_WMM_PRIO_BULK, /**< Bulk priority. */ + PJ_QOS_WMM_PRIO_VIDEO, /**< Video priority */ + PJ_QOS_WMM_PRIO_VOICE /**< Voice priority */ +} pj_qos_wmm_prio; + + +/** + * QoS parameters to be set or retrieved to/from the socket. + */ +typedef struct pj_qos_params +{ + pj_uint8_t flags; /**< Determines which values to + set, bitmask of pj_qos_flag */ + pj_uint8_t dscp_val; /**< DSCP value to set */ + pj_uint8_t so_prio; /**< SO_PRIORITY value */ + pj_qos_wmm_prio wmm_prio; /**< WMM priority value */ +} pj_qos_params; + + + +/** + * This is the high level and portable API to enable QoS on the specified + * socket, by setting the traffic type to the specified parameter. + * + * @param sock The socket. + * @param type Traffic type to be set. + * + * @return PJ_SUCCESS if at least Layer 2 or Layer 3 setting is + * successfully set. If both Layer 2 and Layer 3 settings + * can't be set, this function will return error. + */ +PJ_DECL(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, + pj_qos_type type); + +/** + * This is the high level and portable API to get the traffic type that has + * been set on the socket. On occasions where the Layer 2 or Layer 3 settings + * were modified by using low level API, this function may return approximation + * of the closest QoS type that matches the settings. + * + * @param sock The socket. + * @param p_type Pointer to receive the traffic type of the socket. + * + * @return PJ_SUCCESS if traffic type for the socket can be obtained + * or approximated.. + */ +PJ_DECL(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, + pj_qos_type *p_type); + + +/** + * This is a convenience function to apply QoS to the socket, and print error + * logging if the operations failed. Both QoS traffic type and the low level + * QoS parameters can be applied with this function. + * + * @param sock The socket handle. + * @param qos_type QoS traffic type. The QoS traffic type will be applied + * only if the value is not PJ_QOS_TYPE_BEST_EFFORT, + * @param qos_params Optional low-level QoS parameters. This will be + * applied only if this argument is not NULL and the + * flags inside the structure is non-zero. Upon return, + * the flags will indicate which parameters have been + * applied successfully. + * @param log_level This function will print to log at this level upon + * encountering errors. + * @param log_sender Optional sender name in the log. + * @param sock_name Optional name to help identify the socket in the log. + * + * @return PJ_SUCCESS if at least Layer 2 or Layer 3 setting is + * successfully set. If both Layer 2 and Layer 3 settings + * can't be set, this function will return error. + * + * @see pj_sock_apply_qos2() + */ +PJ_DECL(pj_status_t) pj_sock_apply_qos(pj_sock_t sock, + pj_qos_type qos_type, + pj_qos_params *qos_params, + unsigned log_level, + const char *log_sender, + const char *sock_name); + +/** + * Variant of #pj_sock_apply_qos() where the \a qos_params parameter is + * const. + * + * @see pj_sock_apply_qos() + */ +PJ_DECL(pj_status_t) pj_sock_apply_qos2(pj_sock_t sock, + pj_qos_type qos_type, + const pj_qos_params *qos_params, + unsigned log_level, + const char *log_sender, + const char *sock_name); + +/** + * Retrieve the standard mapping of QoS params for the specified traffic + * type. + * + * @param type The traffic type from which the QoS parameters + * are to be retrieved. + * @param p_param Pointer to receive the QoS parameters. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_qos_get_params(pj_qos_type type, + pj_qos_params *p_param); + + +/** + * Retrieve the traffic type that matches the specified QoS parameters. + * If no exact matching is found, this function will return an + * approximation of the closest matching traffic type for the specified + * QoS parameters. + * + * @param param Structure containing QoS parameters to map into + * "standard" traffic types. + * @param p_type Pointer to receive the traffic type. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_qos_get_type(const pj_qos_params *param, + pj_qos_type *p_type); + + +/** + * This is a low level API to set QoS parameters to the socket. + * + * @param sock The socket. + * @param param Structure containing QoS parameters to be applied + * to the socket. Upon return, the \a flags field + * of this structure will be set with bitmask value + * indicating which QoS settings have successfully + * been applied to the socket. + * + * @return PJ_SUCCESS if at least one field setting has been + * successfully set. If no setting can't be set, + * this function will return error. + */ +PJ_DECL(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, + pj_qos_params *param); + +/** + * This is a low level API to get QoS parameters from the socket. + * + * @param sock The socket. + * @param p_param Pointer to receive the parameters. Upon returning + * successfully, the \a flags field of this structure + * will be initialized with the appropriate bitmask + * to indicate which fields have been successfully + * retrieved. + * + * @return PJ_SUCCESS on success or the appropriate error code. + */ +PJ_DECL(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, + pj_qos_params *p_param); + + +/** + * @} + */ + + +PJ_END_DECL + +#endif /* __PJ_SOCK_QOS_H__ */ + diff --git a/pjlib/include/pjlib.h b/pjlib/include/pjlib.h index fa88510b..648c594a 100644 --- a/pjlib/include/pjlib.h +++ b/pjlib/include/pjlib.h @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include diff --git a/pjlib/src/pj/sock_bsd.c b/pjlib/src/pj/sock_bsd.c index 6cf24fe2..36761bce 100644 --- a/pjlib/src/pj/sock_bsd.c +++ b/pjlib/src/pj/sock_bsd.c @@ -63,7 +63,7 @@ const pj_uint16_t PJ_SOL_IP = SOL_IP; #elif defined(PJ_WIN32) && PJ_WIN32 const pj_uint16_t PJ_SOL_IP = IPPROTO_IP; #else -const pj_uint16_t PJ_SOL_IP = 0xFFFF; +const pj_uint16_t PJ_SOL_IP = 0; #endif /* SOL_IP */ #if defined(SOL_TCP) @@ -73,7 +73,7 @@ const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP; #elif defined(PJ_WIN32) && PJ_WIN32 const pj_uint16_t PJ_SOL_TCP = IPPROTO_TCP; #else -const pj_uint16_t PJ_SOL_TCP = 0xFFFF; +const pj_uint16_t PJ_SOL_TCP = 6; #endif /* SOL_TCP */ #ifdef SOL_UDP @@ -83,7 +83,7 @@ const pj_uint16_t PJ_SOL_UDP = IPPROTO_UDP; #elif defined(PJ_WIN32) && PJ_WIN32 const pj_uint16_t PJ_SOL_UDP = IPPROTO_UDP; #else -const pj_uint16_t PJ_SOL_UDP = 0xFFFF; +const pj_uint16_t PJ_SOL_UDP = 17; #endif /* SOL_UDP */ #ifdef SOL_IPV6 @@ -95,7 +95,7 @@ const pj_uint16_t PJ_SOL_IPV6 = SOL_IPV6; const pj_uint16_t PJ_SOL_IPV6 = 41; # endif #else -const pj_uint16_t PJ_SOL_IPV6 = 0xFFFF; +const pj_uint16_t PJ_SOL_IPV6 = 41; #endif /* SOL_IPV6 */ /* IP_TOS */ @@ -135,6 +135,12 @@ const pj_uint16_t PJ_SO_RCVBUF = SO_RCVBUF; const pj_uint16_t PJ_SO_SNDBUF = SO_SNDBUF; const pj_uint16_t PJ_TCP_NODELAY= TCP_NODELAY; const pj_uint16_t PJ_SO_REUSEADDR= SO_REUSEADDR; +#if defined(SO_PRIORITY) +const pj_uint16_t PJ_SO_PRIORITY = SO_PRIORITY; +#else +/* This is from Linux, YMMV */ +const pj_uint16_t PJ_SO_PRIORITY = 12; +#endif /* Multicasting is not supported e.g. in PocketPC 2003 SDK */ #ifdef IP_MULTICAST_IF diff --git a/pjlib/src/pj/sock_common.c b/pjlib/src/pj/sock_common.c index 0e098c48..64fc9d47 100644 --- a/pjlib/src/pj/sock_common.c +++ b/pjlib/src/pj/sock_common.c @@ -1072,6 +1072,11 @@ PJ_DEF(pj_uint16_t) pj_SO_REUSEADDR(void) return PJ_SO_REUSEADDR; } +PJ_DEF(pj_uint16_t) pj_SO_PRIORITY(void) +{ + return PJ_SO_PRIORITY; +} + PJ_DEF(pj_uint16_t) pj_IP_MULTICAST_IF(void) { return PJ_IP_MULTICAST_IF; diff --git a/pjlib/src/pj/sock_qos_bsd.c b/pjlib/src/pj/sock_qos_bsd.c new file mode 100644 index 00000000..32b2828e --- /dev/null +++ b/pjlib/src/pj/sock_qos_bsd.c @@ -0,0 +1,130 @@ +/* $Id$ */ +/* + * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include + +/* This is the implementation of QoS with BSD socket's setsockopt(), + * using IP_TOS and SO_PRIORITY + */ +#if !defined(PJ_QOS_IMPLEMENTATION) || PJ_QOS_IMPLEMENTATION==PJ_QOS_BSD + +PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, + pj_qos_params *param) +{ + pj_status_t last_err = PJ_ENOTSUP; + pj_status_t status; + + /* No op? */ + if (!param->flags) + return PJ_SUCCESS; + + /* Clear WMM field since we don't support it */ + param->flags &= ~(PJ_QOS_PARAM_HAS_WMM); + + /* Set TOS/DSCP */ + if (param->flags & PJ_QOS_PARAM_HAS_DSCP) { + int val = param->dscp_val; + status = pj_sock_setsockopt(sock, pj_SOL_IP(), pj_IP_TOS(), + &val, sizeof(val)); + if (status != PJ_SUCCESS) { + param->flags &= ~(PJ_QOS_PARAM_HAS_DSCP); + last_err = status; + } + } + + /* Set SO_PRIORITY */ + if (param->flags & PJ_QOS_PARAM_HAS_802_1_P) { + int val = param->so_prio; + status = pj_sock_setsockopt(sock, pj_SOL_SOCKET(), pj_SO_PRIORITY(), + &val, sizeof(val)); + if (status != PJ_SUCCESS) { + param->flags &= ~(PJ_QOS_PARAM_HAS_802_1_P); + last_err = status; + } + } + + return param->flags ? PJ_SUCCESS : last_err; +} + +PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, + pj_qos_type type) +{ + pj_qos_params param; + pj_status_t status; + + status = pj_qos_get_params(type, ¶m); + if (status != PJ_SUCCESS) + return status; + + return pj_sock_set_qos_params(sock, ¶m); +} + + +PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, + pj_qos_params *p_param) +{ + pj_status_t last_err = PJ_ENOTSUP; + int val, optlen; + pj_status_t status; + + pj_bzero(p_param, sizeof(*p_param)); + + /* Get DSCP/TOS value */ + optlen = sizeof(val); + status = pj_sock_getsockopt(sock, pj_SOL_IP(), pj_IP_TOS(), + &val, &optlen); + if (status == PJ_SUCCESS) { + p_param->flags |= PJ_QOS_PARAM_HAS_DSCP; + p_param->dscp_val = (pj_uint8_t)val; + } else { + last_err = status; + } + + /* Get SO_PRIORITY */ + optlen = sizeof(val); + status = pj_sock_getsockopt(sock, pj_SOL_SOCKET(), pj_SO_PRIORITY(), + &val, &optlen); + if (status == PJ_SUCCESS) { + p_param->flags |= PJ_QOS_PARAM_HAS_802_1_P; + p_param->so_prio = (pj_uint8_t)val; + } else { + last_err = status; + } + + /* WMM is not supported */ + + return p_param->flags ? PJ_SUCCESS : last_err; +} + +PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, + pj_qos_type *p_type) +{ + pj_qos_params param; + pj_status_t status; + + status = pj_sock_get_qos_params(sock, ¶m); + if (status != PJ_SUCCESS) + return status; + + return pj_qos_get_type(¶m, p_type); +} + +#endif /* PJ_QOS_IMPLEMENTATION */ diff --git a/pjlib/src/pj/sock_qos_common.c b/pjlib/src/pj/sock_qos_common.c new file mode 100644 index 00000000..a59b606b --- /dev/null +++ b/pjlib/src/pj/sock_qos_common.c @@ -0,0 +1,154 @@ +/* $Id$ */ +/* + * Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include + +#define THIS_FILE "sock_qos_common.c" +#define ALL_FLAGS (PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_802_1_P | \ + PJ_QOS_PARAM_HAS_WMM) + +/* "Standard" mapping between traffic type and QoS params */ +static const pj_qos_params qos_map[] = +{ + /* flags dscp prio wmm_prio */ + {ALL_FLAGS, 0x00, 0, PJ_QOS_WMM_PRIO_BULK_EFFORT}, /* BE */ + {ALL_FLAGS, 0x08, 2, PJ_QOS_WMM_PRIO_BULK}, /* BK */ + {ALL_FLAGS, 0x28, 5, PJ_QOS_WMM_PRIO_VIDEO}, /* VI */ + {ALL_FLAGS, 0x30, 6, PJ_QOS_WMM_PRIO_VOICE}, /* VO */ + {ALL_FLAGS, 0x38, 7, PJ_QOS_WMM_PRIO_VOICE} /* CO */ +}; + + +/* Retrieve the mapping for the specified type */ +PJ_DEF(pj_status_t) pj_qos_get_params(pj_qos_type type, + pj_qos_params *p_param) +{ + PJ_ASSERT_RETURN(type<=PJ_QOS_TYPE_CONTROL && p_param, PJ_EINVAL); + pj_memcpy(p_param, &qos_map[type], sizeof(*p_param)); + return PJ_SUCCESS; +} + +/* Get the matching traffic type */ +PJ_DEF(pj_status_t) pj_qos_get_type( const pj_qos_params *param, + pj_qos_type *p_type) +{ + unsigned dscp_type = PJ_QOS_TYPE_BEST_EFFORT, + prio_type = PJ_QOS_TYPE_BEST_EFFORT, + wmm_type = PJ_QOS_TYPE_BEST_EFFORT; + unsigned i, count=0; + + PJ_ASSERT_RETURN(param && p_type, PJ_EINVAL); + + if (param->flags & PJ_QOS_PARAM_HAS_DSCP) { + for (i=0; i<=PJ_QOS_TYPE_CONTROL; ++i) { + if (param->dscp_val >= qos_map[i].dscp_val) + dscp_type = (pj_qos_type)i; + } + ++count; + } + + if (param->flags & PJ_QOS_PARAM_HAS_802_1_P) { + for (i=0; i<=PJ_QOS_TYPE_CONTROL; ++i) { + if (param->so_prio >= qos_map[i].so_prio) + prio_type = (pj_qos_type)i; + } + ++count; + } + + if (param->flags & PJ_QOS_PARAM_HAS_WMM) { + for (i=0; i<=PJ_QOS_TYPE_CONTROL; ++i) { + if (param->wmm_prio >= qos_map[i].wmm_prio) + wmm_type = (pj_qos_type)i; + } + ++count; + } + + if (count) + *p_type = (pj_qos_type)((dscp_type + prio_type + wmm_type) / count); + else + *p_type = PJ_QOS_TYPE_BEST_EFFORT; + + return PJ_SUCCESS; +} + +/* Apply QoS */ +PJ_DEF(pj_status_t) pj_sock_apply_qos( pj_sock_t sock, + pj_qos_type qos_type, + pj_qos_params *qos_params, + unsigned log_level, + const char *log_sender, + const char *sock_name) +{ + char fmt[60]; + pj_status_t qos_type_rc = PJ_SUCCESS, + qos_params_rc = PJ_SUCCESS; + + if (!log_sender) + log_sender = THIS_FILE; + if (!sock_name) + sock_name = "socket"; + + if (qos_type != PJ_QOS_TYPE_BEST_EFFORT) { + qos_type_rc = pj_sock_set_qos_type(sock, qos_type); + + if (qos_type_rc != PJ_SUCCESS) { + pj_ansi_snprintf(fmt, sizeof(fmt), + "Error setting QoS type %d to %s", + qos_type, sock_name); + pj_perror(log_level, log_sender, qos_type_rc, fmt, 0); + } + } + + if (qos_params && qos_params->flags) { + qos_params_rc = pj_sock_set_qos_params(sock, qos_params); + if (qos_params_rc != PJ_SUCCESS) { + pj_ansi_snprintf(fmt, sizeof(fmt), + "Error setting QoS params (flags=%d) to %s", + qos_params->flags, sock_name); + pj_perror(log_level, log_sender, qos_params_rc, fmt, 0); + if (qos_type_rc != PJ_SUCCESS) + return qos_params_rc; + } + } else if (qos_type_rc != PJ_SUCCESS) + return qos_type_rc; + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pj_sock_apply_qos2( pj_sock_t sock, + pj_qos_type qos_type, + const pj_qos_params *qos_params, + unsigned log_level, + const char *log_sender, + const char *sock_name) +{ + pj_qos_params qos_params_buf, *qos_params_copy = NULL; + + if (qos_params) { + pj_memcpy(&qos_params_buf, qos_params, sizeof(*qos_params)); + qos_params_copy = &qos_params_buf; + } + + return pj_sock_apply_qos(sock, qos_type, qos_params_copy, + log_level, log_sender, sock_name); +} diff --git a/pjlib/src/pj/sock_qos_dummy.c b/pjlib/src/pj/sock_qos_dummy.c new file mode 100644 index 00000000..946b26bc --- /dev/null +++ b/pjlib/src/pj/sock_qos_dummy.c @@ -0,0 +1,76 @@ +/* $Id$ */ +/* + * Copyright (C) 2009 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include + +/* Dummy implementation of QoS API. + * (this is controlled by pjlib's config.h) + */ +#if defined(PJ_QOS_IMPLEMENTATION) && PJ_QOS_IMPLEMENTATION==PJ_QOS_DUMMY + +#define THIS_FILE "sock_qos_dummy.c" + + +PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, + pj_qos_params *param) +{ + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(param); + + PJ_LOG(4,(THIS_FILE, "pj_sock_set_qos_params() is not implemented " + "for this platform")); + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, + pj_qos_type type) +{ + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(type); + + PJ_LOG(4,(THIS_FILE, "pj_sock_set_qos_type() is not implemented " + "for this platform")); + return PJ_ENOTSUP; +} + + +PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, + pj_qos_params *p_param) +{ + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(p_param); + + PJ_LOG(4,(THIS_FILE, "pj_sock_get_qos_params() is not implemented " + "for this platform")); + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, + pj_qos_type *p_type) +{ + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(p_type); + + PJ_LOG(4,(THIS_FILE, "pj_sock_get_qos_type() is not implemented " + "for this platform")); + return PJ_ENOTSUP; +} + +#endif /* PJ_QOS_DUMMY */ diff --git a/pjlib/src/pj/sock_qos_symbian.cpp b/pjlib/src/pj/sock_qos_symbian.cpp new file mode 100644 index 00000000..59912ece --- /dev/null +++ b/pjlib/src/pj/sock_qos_symbian.cpp @@ -0,0 +1,91 @@ +/* $Id$ */ +/* + * Copyright (C) 2009 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include "os_symbian.h" + +PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, + pj_qos_params *param) +{ + CPjSocket *pjsock = (CPjSocket*)sock; + RSocket & rsock = pjsock->Socket(); + pj_status_t last_err = PJ_ENOTSUP; + + /* SO_PRIORITY and WMM are not supported */ + param->flags &= ~(PJ_QOS_PARAM_HAS_802_1_P | PJ_QOS_PARAM_HAS_WMM); + + if (param->flags & PJ_QOS_PARAM_HAS_DSCP) { + TInt err; + + err = rsock.SetOpt(KSoIpTOS, KProtocolInetIp, + (param->dscp_val << 2)); + if (err != KErrNone) { + last_err = PJ_RETURN_OS_ERROR(err); + param->flags &= ~(PJ_QOS_PARAM_HAS_DSCP); + } + } + + return param->flags ? PJ_SUCCESS : last_err; +} + +PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, + pj_qos_type type) +{ + pj_qos_params param; + pj_status_t status; + + status = pj_qos_get_params(type, ¶m); + if (status != PJ_SUCCESS) + return status; + + return pj_sock_set_qos_params(sock, ¶m); +} + + +PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, + pj_qos_params *p_param) +{ + CPjSocket *pjsock = (CPjSocket*)sock; + RSocket & rsock = pjsock->Socket(); + TInt err, dscp; + + pj_bzero(p_param, sizeof(*p_param)); + + err = rsock.GetOpt(KSoIpTOS, KProtocolInetIp, dscp); + if (err == KErrNone) { + p_param->flags |= PJ_QOS_PARAM_HAS_DSCP; + p_param->dscp_val = (dscp >> 2); + return PJ_SUCCESS; + } else { + return PJ_RETURN_OS_ERROR(err); + } +} + +PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, + pj_qos_type *p_type) +{ + pj_qos_params param; + pj_status_t status; + + status = pj_sock_get_qos_params(sock, ¶m); + if (status != PJ_SUCCESS) + return status; + + return pj_qos_get_type(¶m, p_type); +} + diff --git a/pjlib/src/pj/sock_qos_wm.c b/pjlib/src/pj/sock_qos_wm.c new file mode 100644 index 00000000..ecdf2ed2 --- /dev/null +++ b/pjlib/src/pj/sock_qos_wm.c @@ -0,0 +1,103 @@ +/* $Id$ */ +/* + * Copyright (C) 2009 Teluu Inc. (http://www.teluu.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include + +#include + +/* QoS implementation for Windows Mobile 6, must be enabled explicitly + * (this is controlled by pjlib's config.h) + */ +#if defined(PJ_QOS_IMPLEMENTATION) && PJ_QOS_IMPLEMENTATION==PJ_QOS_WM + +#define THIS_FILE "sock_qos_wm.c" + +/* Mapping between our traffic type and WM's DSCP traffic types */ +static const int dscp_map[] = +{ + DSCPBestEffort, + DSCPBackground, + DSCPVideo, + DSCPAudio, + DSCPControl +}; + +PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock, + pj_qos_params *param) +{ + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(param); + + PJ_LOG(4,(THIS_FILE, "pj_sock_set_qos_params() is not implemented " + "for this platform")); + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock, + pj_qos_type type) +{ + int value; + + PJ_ASSERT_RETURN(type < PJ_ARRAY_SIZE(dscp_map), PJ_EINVAL); + + value = dscp_map[type]; + return pj_sock_setsockopt(sock, IPPROTO_IP, IP_DSCP_TRAFFIC_TYPE, + &value, sizeof(value)); +} + + +PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock, + pj_qos_params *p_param) +{ + PJ_UNUSED_ARG(sock); + PJ_UNUSED_ARG(p_param); + + PJ_LOG(4,(THIS_FILE, "pj_sock_get_qos_params() is not implemented " + "for this platform")); + return PJ_ENOTSUP; +} + +PJ_DEF(pj_status_t) pj_sock_get_qos_type(pj_sock_t sock, + pj_qos_type *p_type) +{ + pj_status_t status; + int value, optlen; + unsigned i; + + optlen = sizeof(value); + value = 0; + status = pj_sock_getsockopt(sock, IPPROTO_IP, IP_DSCP_TRAFFIC_TYPE, + &value, &optlen); + if (status != PJ_SUCCESS) + return status; + + *p_type = PJ_QOS_TYPE_BEST_EFFORT; + for (i=0; i #include #include +#include PJ_BEGIN_DECL @@ -247,6 +248,32 @@ typedef struct pj_stun_sock_cfg */ int ka_interval; + /** + * QoS traffic type to be set on this transport. When application wants + * to apply QoS tagging to the transport, it's preferable to set this + * field rather than \a qos_param fields since this is more portable. + * + * Default value is PJ_QOS_TYPE_BEST_EFFORT. + */ + pj_qos_type qos_type; + + /** + * Set the low level QoS parameters to the transport. This is a lower + * level operation than setting the \a qos_type field and may not be + * supported on all platforms. + * + * By default all settings in this structure are disabled. + */ + pj_qos_params qos_params; + + /** + * Specify if STUN socket should ignore any errors when setting the QoS + * traffic type/parameters. + * + * Default: PJ_TRUE + */ + pj_bool_t qos_ignore_error; + } pj_stun_sock_cfg; diff --git a/pjnath/include/pjnath/turn_sock.h b/pjnath/include/pjnath/turn_sock.h index f6776b83..fe034612 100644 --- a/pjnath/include/pjnath/turn_sock.h +++ b/pjnath/include/pjnath/turn_sock.h @@ -25,6 +25,7 @@ * @brief TURN relay using UDP client as transport protocol */ #include +#include PJ_BEGIN_DECL @@ -100,6 +101,48 @@ typedef struct pj_turn_sock_cb } pj_turn_sock_cb; +/** + * This structure describes options that can be specified when creating + * the TURN socket. Application should call #pj_turn_sock_cfg_default() + * to initialize this structure with its default values before using it. + */ +typedef struct pj_turn_sock_cfg +{ + /** + * QoS traffic type to be set on this transport. When application wants + * to apply QoS tagging to the transport, it's preferable to set this + * field rather than \a qos_param fields since this is more portable. + * + * Default value is PJ_QOS_TYPE_BEST_EFFORT. + */ + pj_qos_type qos_type; + + /** + * Set the low level QoS parameters to the transport. This is a lower + * level operation than setting the \a qos_type field and may not be + * supported on all platforms. + * + * By default all settings in this structure are not set. + */ + pj_qos_params qos_params; + + /** + * Specify if STUN socket should ignore any errors when setting the QoS + * traffic type/parameters. + * + * Default: PJ_TRUE + */ + pj_bool_t qos_ignore_error; + +} pj_turn_sock_cfg; + + +/** + * Initialize pj_turn_sock_cfg structure with default values. + */ +PJ_DECL(void) pj_turn_sock_cfg_default(pj_turn_sock_cfg *cfg); + + /** * Create a TURN transport instance with the specified address family and * connection type. Once TURN transport instance is created, application @@ -114,7 +157,9 @@ typedef struct pj_turn_sock_cb * @param conn_type Connection type to the TURN server. Both TCP and * UDP are supported. * @param cb Callback to receive events from the TURN transport. - * @param options Option flags, currently this value must be zero. + * @param setting Optional settings to be specified to the transport. + * If this parameter is NULL, default values will be + * used. * @param user_data Arbitrary application data to be associated with * this transport. * @param p_turn_sock Pointer to receive the created instance of the @@ -127,7 +172,7 @@ PJ_DECL(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg, int af, pj_turn_tp_type conn_type, const pj_turn_sock_cb *cb, - unsigned options, + const pj_turn_sock_cfg *setting, void *user_data, pj_turn_sock **p_turn_sock); diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c index cf79ad9d..d702c656 100644 --- a/pjnath/src/pjnath/ice_strans.c +++ b/pjnath/src/pjnath/ice_strans.c @@ -211,6 +211,7 @@ PJ_DEF(void) pj_ice_strans_cfg_default(pj_ice_strans_cfg *cfg) pj_stun_config_init(&cfg->stun_cfg, NULL, 0, NULL, NULL); pj_stun_sock_cfg_default(&cfg->stun.cfg); pj_turn_alloc_param_default(&cfg->turn.alloc_param); + pj_turn_sock_cfg_default(&cfg->turn.cfg); pj_ice_sess_options_default(&cfg->opt); @@ -273,6 +274,17 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id) stun_sock_cb.on_status = &stun_on_status; stun_sock_cb.on_data_sent = &stun_on_data_sent; + /* Override component specific QoS settings, if any */ + if (ice_st->cfg.comp[comp_id-1].qos_type) { + ice_st->cfg.stun.cfg.qos_type = + ice_st->cfg.comp[comp_id-1].qos_type; + } + if (ice_st->cfg.comp[comp_id-1].qos_params.flags) { + pj_memcpy(&ice_st->cfg.stun.cfg.qos_params, + &ice_st->cfg.comp[comp_id-1].qos_params, + sizeof(ice_st->cfg.stun.cfg.qos_params)); + } + /* Create the STUN transport */ status = pj_stun_sock_create(&ice_st->cfg.stun_cfg, NULL, ice_st->cfg.af, &stun_sock_cb, @@ -391,10 +403,22 @@ static pj_status_t create_comp(pj_ice_strans *ice_st, unsigned comp_id) turn_sock_cb.on_rx_data = &turn_on_rx_data; turn_sock_cb.on_state = &turn_on_state; + /* Override with component specific QoS settings, if any */ + if (ice_st->cfg.comp[comp_id-1].qos_type) { + ice_st->cfg.turn.cfg.qos_type = + ice_st->cfg.comp[comp_id-1].qos_type; + } + if (ice_st->cfg.comp[comp_id-1].qos_params.flags) { + pj_memcpy(&ice_st->cfg.turn.cfg.qos_params, + &ice_st->cfg.comp[comp_id-1].qos_params, + sizeof(ice_st->cfg.turn.cfg.qos_params)); + } + + /* Create the TURN transport */ status = pj_turn_sock_create(&ice_st->cfg.stun_cfg, ice_st->cfg.af, ice_st->cfg.turn.conn_type, - &turn_sock_cb, 0, comp, - &comp->turn_sock); + &turn_sock_cb, &ice_st->cfg.turn.cfg, + comp, &comp->turn_sock); if (status != PJ_SUCCESS) { return status; } @@ -453,7 +477,8 @@ PJ_DEF(pj_status_t) pj_ice_strans_create( const char *name, if (status != PJ_SUCCESS) return status; - PJ_ASSERT_RETURN(comp_cnt && cb && p_ice_st, PJ_EINVAL); + PJ_ASSERT_RETURN(comp_cnt && cb && p_ice_st && + comp_cnt <= PJ_ICE_MAX_COMP , PJ_EINVAL); if (name == NULL) name = "ice%p"; diff --git a/pjnath/src/pjnath/stun_sock.c b/pjnath/src/pjnath/stun_sock.c index a0d5ff2c..6bdd77e4 100644 --- a/pjnath/src/pjnath/stun_sock.c +++ b/pjnath/src/pjnath/stun_sock.c @@ -136,6 +136,8 @@ PJ_DEF(void) pj_stun_sock_cfg_default(pj_stun_sock_cfg *cfg) cfg->max_pkt_size = PJ_STUN_SOCK_PKT_LEN; cfg->async_cnt = 1; cfg->ka_interval = PJ_STUN_KEEP_ALIVE_SEC; + cfg->qos_type = PJ_QOS_TYPE_BEST_EFFORT; + cfg->qos_ignore_error = PJ_TRUE; } @@ -200,6 +202,14 @@ PJ_DEF(pj_status_t) pj_stun_sock_create( pj_stun_config *stun_cfg, if (status != PJ_SUCCESS) goto on_error; + /* Apply QoS, if specified */ + status = pj_sock_apply_qos2(stun_sock->sock_fd, cfg->qos_type, + &cfg->qos_params, 2, stun_sock->obj_name, + NULL); + if (status != PJ_SUCCESS && !cfg->qos_ignore_error) + goto on_error; + + /* Bind socket */ if (pj_sockaddr_has_addr(&cfg->bound_addr)) { status = pj_sock_bind(stun_sock->sock_fd, &cfg->bound_addr, pj_sockaddr_get_len(&cfg->bound_addr)); @@ -758,10 +768,7 @@ static pj_bool_t on_data_recvfrom(pj_activesock_t *asock, /* Log socket error */ if (status != PJ_SUCCESS) { - char errmsg[PJ_ERR_MSG_SIZE]; - - pj_strerror(status, errmsg, sizeof(errmsg)); - PJ_LOG(2,(stun_sock->obj_name, "recvfrom() error: %s", errmsg)); + pj_perror(2, stun_sock->obj_name, status, "recvfrom() error", 0); return PJ_TRUE; } diff --git a/pjnath/src/pjnath/turn_sock.c b/pjnath/src/pjnath/turn_sock.c index 0c71d5cb..287b0299 100644 --- a/pjnath/src/pjnath/turn_sock.c +++ b/pjnath/src/pjnath/turn_sock.c @@ -46,6 +46,7 @@ struct pj_turn_sock pj_turn_alloc_param alloc_param; pj_stun_config cfg; + pj_turn_sock_cfg setting; pj_bool_t destroy_request; pj_timer_entry timer; @@ -92,6 +93,14 @@ static void destroy(pj_turn_sock *turn_sock); static void timer_cb(pj_timer_heap_t *th, pj_timer_entry *e); +/* Init config */ +PJ_DEF(void) pj_turn_sock_cfg_default(pj_turn_sock_cfg *cfg) +{ + pj_bzero(cfg, sizeof(*cfg)); + cfg->qos_type = PJ_QOS_TYPE_BEST_EFFORT; + cfg->qos_ignore_error = PJ_TRUE; +} + /* * Create. */ @@ -99,21 +108,26 @@ PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg, int af, pj_turn_tp_type conn_type, const pj_turn_sock_cb *cb, - unsigned options, + const pj_turn_sock_cfg *setting, void *user_data, pj_turn_sock **p_turn_sock) { pj_turn_sock *turn_sock; pj_turn_session_cb sess_cb; + pj_turn_sock_cfg default_setting; pj_pool_t *pool; const char *name_tmpl; pj_status_t status; PJ_ASSERT_RETURN(cfg && p_turn_sock, PJ_EINVAL); PJ_ASSERT_RETURN(af==pj_AF_INET() || af==pj_AF_INET6(), PJ_EINVAL); - PJ_ASSERT_RETURN(options==0, PJ_EINVAL); PJ_ASSERT_RETURN(conn_type!=PJ_TURN_TP_TCP || PJ_HAS_TCP, PJ_EINVAL); + if (!setting) { + pj_turn_sock_cfg_default(&default_setting); + setting = &default_setting; + } + switch (conn_type) { case PJ_TURN_TP_UDP: name_tmpl = "udprel%p"; @@ -139,6 +153,9 @@ PJ_DEF(pj_status_t) pj_turn_sock_create(pj_stun_config *cfg, /* Copy STUN config (this contains ioqueue, timer heap, etc.) */ pj_memcpy(&turn_sock->cfg, cfg, sizeof(*cfg)); + /* Copy setting (QoS parameters etc */ + pj_memcpy(&turn_sock->setting, setting, sizeof(*setting)); + /* Set callback */ if (cb) { pj_memcpy(&turn_sock->cb, cb, sizeof(*cb)); @@ -652,6 +669,16 @@ static void turn_on_state(pj_turn_session *sess, return; } + /* Apply QoS, if specified */ + status = pj_sock_apply_qos2(sock, turn_sock->setting.qos_type, + &turn_sock->setting.qos_params, + (turn_sock->setting.qos_ignore_error?2:1), + turn_sock->pool->obj_name, NULL); + if (status != PJ_SUCCESS && !turn_sock->setting.qos_ignore_error) { + pj_turn_sock_destroy(turn_sock); + return; + } + /* Create active socket */ pj_bzero(&asock_cb, sizeof(asock_cb)); asock_cb.on_data_read = &on_data_read; diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index a4449d81..aa935b5b 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -59,6 +59,7 @@ static struct app_config pjsua_media_config media_cfg; pj_bool_t no_refersub; pj_bool_t ipv6; + pj_bool_t enable_qos; pj_bool_t no_tcp; pj_bool_t no_udp; pj_bool_t use_tls; @@ -207,6 +208,7 @@ static void usage(void) #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 puts (" --ipv6 Use IPv6 instead for SIP and media."); #endif + puts (" --set-qos Enable QoS tagging for SIP and media."); puts (" --local-port=port Set TCP/UDP port. This implicitly enables both "); puts (" TCP and UDP transports on the specified port, unless"); puts (" if TCP or UDP is disabled."); @@ -500,7 +502,7 @@ static pj_status_t parse_args(int argc, char *argv[], OPT_TLS_NEG_TIMEOUT, OPT_TLS_SRV_NAME, OPT_CAPTURE_DEV, OPT_PLAYBACK_DEV, OPT_CAPTURE_LAT, OPT_PLAYBACK_LAT, OPT_NO_TONES, OPT_JB_MAX_SIZE, - OPT_STDOUT_REFRESH, OPT_STDOUT_REFRESH_TEXT, OPT_IPV6, + OPT_STDOUT_REFRESH, OPT_STDOUT_REFRESH_TEXT, OPT_IPV6, OPT_QOS, #ifdef _IONBF OPT_STDOUT_NO_BUF, #endif @@ -616,6 +618,7 @@ static pj_status_t parse_args(int argc, char *argv[], #if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6 { "ipv6", 0, 0, OPT_IPV6}, #endif + { "set-qos", 0, 0, OPT_QOS}, { "use-timer", 0, 0, OPT_TIMER}, { "timer-se", 1, 0, OPT_TIMER_SE}, { "timer-min-se", 1, 0, OPT_TIMER_MIN_SE}, @@ -1327,7 +1330,17 @@ static pj_status_t parse_args(int argc, char *argv[], cfg->ipv6 = PJ_TRUE; break; #endif - + case OPT_QOS: + cfg->enable_qos = PJ_TRUE; + /* Set RTP traffic type to Voice */ + cfg->rtp_cfg.qos_type = PJ_QOS_TYPE_VOICE; + /* Directly apply DSCP value to SIP traffic. Say lets + * set it to CS3 (DSCP 011000). Note that this will not + * work on all platforms. + */ + cfg->udp_cfg.qos_params.flags = PJ_QOS_PARAM_HAS_DSCP; + cfg->udp_cfg.qos_params.dscp_val = 0x18; + break; default: PJ_LOG(1,(THIS_FILE, "Argument \"%s\" is not valid. Use --help to see help", @@ -1607,6 +1620,9 @@ static int write_settings(const struct app_config *config, if (config->ipv6) { pj_strcat2(&cfg, "--ipv6\n"); } + if (config->enable_qos) { + pj_strcat2(&cfg, "--set-qos\n"); + } /* UDP Transport. */ pj_ansi_sprintf(line, "--local-port %d\n", config->udp_cfg.port); diff --git a/pjsip/include/pjsip/sip_transport_tcp.h b/pjsip/include/pjsip/sip_transport_tcp.h index c2ef1db0..50f82564 100644 --- a/pjsip/include/pjsip/sip_transport_tcp.h +++ b/pjsip/include/pjsip/sip_transport_tcp.h @@ -26,6 +26,7 @@ */ #include +#include /* Only declare the API if PJ_HAS_TCP is true */ @@ -43,6 +44,74 @@ PJ_BEGIN_DECL * the transport to the framework. */ +/** + * Settings to be specified when creating the TCP transport. Application + * should initialize this structure with its default values by calling + * pjsip_tcp_transport_cfg_default(). + */ +typedef struct pjsip_tcp_transport_cfg +{ + /** + * Address family to use. Valid values are pj_AF_INET() and + * pj_AF_INET6(). Default is pj_AF_INET(). + */ + int af; + + /** + * Optional address to bind the socket to. Default is to bind to + * PJ_INADDR_ANY and to any available port. + */ + pj_sockaddr bind_addr; + + /** + * Optional published address, which is the address to be + * advertised as the address of this SIP transport. + * By default the bound address will be used as the published address. + */ + pjsip_host_port addr_name; + + /** + * Number of simultaneous asynchronous accept() operations to be + * supported. It is recommended that the number here corresponds to + * the number of processors in the system (or the number of SIP + * worker threads). + * + * Default: 1 + */ + unsigned async_cnt; + + /** + * QoS traffic type to be set on this transport. When application wants + * to apply QoS tagging to the transport, it's preferable to set this + * field rather than \a qos_param fields since this is more portable. + * + * Default is QoS not set. + */ + pj_qos_type qos_type; + + /** + * Set the low level QoS parameters to the transport. This is a lower + * level operation than setting the \a qos_type field and may not be + * supported on all platforms. + * + * Default is QoS not set. + */ + pj_qos_params qos_params; + +} pjsip_tcp_transport_cfg; + + +/** + * Initialize pjsip_tcp_transport_cfg structure with default values for + * the specifed address family. + * + * @param cfg The structure to initialize. + * @param af Address family to be used. + */ +PJ_DECL(void) pjsip_tcp_transport_cfg_default(pjsip_tcp_transport_cfg *cfg, + int af); + + /** * Register support for SIP TCP transport by creating TCP listener on * the specified address and port. This function will create an @@ -110,6 +179,24 @@ PJ_DECL(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt, unsigned async_cnt, pjsip_tpfactory **p_factory); +/** + * Another variant of #pjsip_tcp_transport_start(). + * + * @param endpt The SIP endpoint. + * @param cfg TCP transport settings. Application should initialize + * this setting with #pjsip_tcp_transport_cfg_default(). + * @param p_factory Optional pointer to receive the instance of the + * SIP TCP transport factory just created. + * + * @return PJ_SUCCESS when the transport has been successfully + * started and registered to transport manager, or + * the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsip_tcp_transport_start3( + pjsip_endpoint *endpt, + const pjsip_tcp_transport_cfg *cfg, + pjsip_tpfactory **p_factory + ); PJ_END_DECL diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 4727448c..356cab76 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -1517,6 +1517,24 @@ typedef struct pjsua_transport_config */ pjsip_tls_setting tls_setting; + /** + * QoS traffic type to be set on this transport. When application wants + * to apply QoS tagging to the transport, it's preferable to set this + * field rather than \a qos_param fields since this is more portable. + * + * Default is QoS not set. + */ + pj_qos_type qos_type; + + /** + * Set the low level QoS parameters to the transport. This is a lower + * level operation than setting the \a qos_type field and may not be + * supported on all platforms. + * + * Default is QoS not set. + */ + pj_qos_params qos_params; + } pjsua_transport_config; diff --git a/pjsip/src/pjsip/sip_transport_tcp.c b/pjsip/src/pjsip/sip_transport_tcp.c index 1692459a..8b89d707 100644 --- a/pjsip/src/pjsip/sip_transport_tcp.c +++ b/pjsip/src/pjsip/sip_transport_tcp.c @@ -57,6 +57,8 @@ struct tcp_listener pjsip_endpoint *endpt; pjsip_tpmgr *tpmgr; pj_activesock_t *asock; + pj_qos_type qos_type; + pj_qos_params qos_params; }; @@ -164,6 +166,17 @@ static void sockaddr_to_host_port( pj_pool_t *pool, host_port->port = pj_sockaddr_get_port(addr); } +/* + * Initialize pjsip_tcp_transport_cfg structure with default values. + */ +PJ_DEF(void) pjsip_tcp_transport_cfg_default(pjsip_tcp_transport_cfg *cfg, + int af) +{ + pj_bzero(cfg, sizeof(*cfg)); + cfg->af = af; + pj_sockaddr_init(cfg->af, &cfg->bind_addr, NULL, 0); + cfg->async_cnt = 1; +} /**************************************************************************** @@ -174,32 +187,33 @@ static void sockaddr_to_host_port( pj_pool_t *pool, * This is the public API to create, initialize, register, and start the * TCP listener. */ -PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt, - const pj_sockaddr_in *local, - const pjsip_host_port *a_name, - unsigned async_cnt, - pjsip_tpfactory **p_factory) +PJ_DEF(pj_status_t) pjsip_tcp_transport_start3( + pjsip_endpoint *endpt, + const pjsip_tcp_transport_cfg *cfg, + pjsip_tpfactory **p_factory + ) { pj_pool_t *pool; pj_sock_t sock = PJ_INVALID_SOCKET; struct tcp_listener *listener; pj_activesock_cfg asock_cfg; pj_activesock_cb listener_cb; - pj_sockaddr_in *listener_addr; + pj_sockaddr *listener_addr; int addr_len; pj_status_t status; /* Sanity check */ - PJ_ASSERT_RETURN(endpt && async_cnt, PJ_EINVAL); + PJ_ASSERT_RETURN(endpt && cfg->async_cnt, PJ_EINVAL); /* Verify that address given in a_name (if any) is valid */ - if (a_name && a_name->host.slen) { - pj_sockaddr_in tmp; - - status = pj_sockaddr_in_init(&tmp, &a_name->host, - (pj_uint16_t)a_name->port); - if (status != PJ_SUCCESS || tmp.sin_addr.s_addr == PJ_INADDR_ANY || - tmp.sin_addr.s_addr == PJ_INADDR_NONE) + if (cfg->addr_name.host.slen) { + pj_sockaddr tmp; + + status = pj_sockaddr_init(cfg->af, &tmp, &cfg->addr_name.host, + (pj_uint16_t)cfg->addr_name.port); + if (status != PJ_SUCCESS || !pj_sockaddr_has_addr(&tmp) || + (cfg->af==pj_AF_INET() && + tmp.ipv4.sin_addr.s_addr==PJ_INADDR_NONE)) { /* Invalid address */ return PJ_EINVAL; @@ -217,6 +231,9 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt, listener->factory.type_name = "tcp"; listener->factory.flag = pjsip_transport_get_flag_from_type(PJSIP_TRANSPORT_TCP); + listener->qos_type = cfg->qos_type; + pj_memcpy(&listener->qos_params, &cfg->qos_params, + sizeof(cfg->qos_params)); pj_ansi_strcpy(listener->factory.obj_name, "tcplis"); @@ -226,24 +243,27 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt, goto on_error; - /* Create and bind socket */ - status = pj_sock_socket(pj_AF_INET(), pj_SOCK_STREAM(), 0, &sock); + /* Create socket */ + status = pj_sock_socket(cfg->af, pj_SOCK_STREAM(), 0, &sock); if (status != PJ_SUCCESS) goto on_error; - listener_addr = (pj_sockaddr_in*)&listener->factory.local_addr; - if (local) { - pj_memcpy(listener_addr, local, sizeof(pj_sockaddr_in)); - } else { - pj_sockaddr_in_init(listener_addr, NULL, 0); - } + /* Apply QoS, if specified */ + status = pj_sock_apply_qos2(sock, cfg->qos_type, &cfg->qos_params, + 2, listener->factory.obj_name, + "SIP TCP listener socket"); - status = pj_sock_bind(sock, listener_addr, sizeof(pj_sockaddr_in)); + /* Bind socket */ + listener_addr = &listener->factory.local_addr; + pj_sockaddr_cp(listener_addr, &cfg->bind_addr); + + status = pj_sock_bind(sock, listener_addr, + pj_sockaddr_get_len(listener_addr)); if (status != PJ_SUCCESS) goto on_error; /* Retrieve the bound address */ - addr_len = sizeof(pj_sockaddr_in); + addr_len = pj_sockaddr_get_len(listener_addr); status = pj_sock_getsockname(sock, listener_addr, &addr_len); if (status != PJ_SUCCESS) goto on_error; @@ -251,12 +271,12 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt, /* If published host/IP is specified, then use that address as the * listener advertised address. */ - if (a_name && a_name->host.slen) { + if (cfg->addr_name.host.slen) { /* Copy the address */ - listener->factory.addr_name = *a_name; + listener->factory.addr_name = cfg->addr_name; pj_strdup(listener->factory.pool, &listener->factory.addr_name.host, - &a_name->host); - listener->factory.addr_name.port = a_name->port; + &cfg->addr_name.host); + listener->factory.addr_name.port = cfg->addr_name.port; } else { /* No published address is given, use the bound address */ @@ -264,24 +284,27 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt, /* If the address returns 0.0.0.0, use the default * interface address as the transport's address. */ - if (listener_addr->sin_addr.s_addr == 0) { + if (!pj_sockaddr_has_addr(listener_addr)) { pj_sockaddr hostip; status = pj_gethostip(pj_AF_INET(), &hostip); if (status != PJ_SUCCESS) goto on_error; - listener_addr->sin_addr.s_addr = hostip.ipv4.sin_addr.s_addr; + pj_memcpy(pj_sockaddr_get_addr(listener_addr), + pj_sockaddr_get_addr(&hostip), + pj_sockaddr_get_addr_len(&hostip)); } /* Save the address name */ sockaddr_to_host_port(listener->factory.pool, - &listener->factory.addr_name, listener_addr); + &listener->factory.addr_name, + (pj_sockaddr_in*)listener_addr); } /* If port is zero, get the bound port */ if (listener->factory.addr_name.port == 0) { - listener->factory.addr_name.port = pj_ntohs(listener_addr->sin_port); + listener->factory.addr_name.port = pj_sockaddr_get_port(listener_addr); } pj_ansi_snprintf(listener->factory.obj_name, @@ -296,9 +319,11 @@ PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt, /* Create active socket */ - if (async_cnt > MAX_ASYNC_CNT) async_cnt = MAX_ASYNC_CNT; pj_activesock_cfg_default(&asock_cfg); - asock_cfg.async_cnt = async_cnt; + if (cfg->async_cnt > MAX_ASYNC_CNT) + asock_cfg.async_cnt = MAX_ASYNC_CNT; + else + asock_cfg.async_cnt = cfg->async_cnt; pj_bzero(&listener_cb, sizeof(listener_cb)); listener_cb.on_accept_complete = &on_accept_complete; @@ -344,6 +369,35 @@ on_error: } +/* + * This is the public API to create, initialize, register, and start the + * TCP listener. + */ +PJ_DEF(pj_status_t) pjsip_tcp_transport_start2(pjsip_endpoint *endpt, + const pj_sockaddr_in *local, + const pjsip_host_port *a_name, + unsigned async_cnt, + pjsip_tpfactory **p_factory) +{ + pjsip_tcp_transport_cfg cfg; + + pjsip_tcp_transport_cfg_default(&cfg, pj_AF_INET()); + + if (local) + pj_sockaddr_cp(&cfg.bind_addr, local); + else + pj_sockaddr_init(cfg.af, &cfg.bind_addr, NULL, 0); + + if (a_name) + pj_memcpy(&cfg.addr_name, a_name, sizeof(*a_name)); + + if (async_cnt) + cfg.async_cnt = async_cnt; + + return pjsip_tcp_transport_start3(endpt, &cfg, p_factory); +} + + /* * This is the public API to create, initialize, register, and start the * TCP listener. @@ -774,6 +828,12 @@ static pj_status_t lis_create_transport(pjsip_tpfactory *factory, if (status != PJ_SUCCESS) return status; + /* Apply QoS, if specified */ + status = pj_sock_apply_qos2(sock, listener->qos_type, + &listener->qos_params, + 2, listener->factory.obj_name, + "outgoing SIP TCP socket"); + /* Bind to any port */ status = pj_sock_bind_in(sock, 0, 0); if (status != PJ_SUCCESS) { @@ -878,6 +938,12 @@ static pj_bool_t on_accept_complete(pj_activesock_t *asock, pj_sockaddr_print(src_addr, addr, sizeof(addr), 3), sock)); + /* Apply QoS, if specified */ + status = pj_sock_apply_qos2(sock, listener->qos_type, + &listener->qos_params, + 2, listener->factory.obj_name, + "incoming SIP TCP socket"); + /* * Incoming connection! * Create TCP transport for the new socket. diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 773785ed..0e390763 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -1526,12 +1526,12 @@ static const char *addr_string(const pj_sockaddr_t *addr) * address via STUN, depending on config). */ static pj_status_t create_sip_udp_sock(int af, - const pj_str_t *bind_param, - int port, + const pjsua_transport_config *cfg, pj_sock_t *p_sock, pj_sockaddr *p_pub_addr) { char stun_ip_addr[PJ_INET6_ADDRSTRLEN]; + unsigned port = cfg->port; pj_str_t stun_srv; pj_sock_t sock; pj_sockaddr bind_addr; @@ -1545,8 +1545,8 @@ static pj_status_t create_sip_udp_sock(int af, } /* Initialize bound address */ - if (bind_param->slen) { - status = pj_sockaddr_init(af, &bind_addr, bind_param, + if (cfg->bound_addr.slen) { + status = pj_sockaddr_init(af, &bind_addr, &cfg->bound_addr, (pj_uint16_t)port); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, @@ -1558,12 +1558,19 @@ static pj_status_t create_sip_udp_sock(int af, pj_sockaddr_init(af, &bind_addr, NULL, (pj_uint16_t)port); } + /* Create socket */ status = pj_sock_socket(af, pj_SOCK_DGRAM(), 0, &sock); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "socket() error", status); return status; } + /* Apply QoS, if specified */ + status = pj_sock_apply_qos2(sock, cfg->qos_type, + &cfg->qos_params, + 2, THIS_FILE, "SIP UDP socket"); + + /* Bind socket */ status = pj_sock_bind(sock, &bind_addr, pj_sockaddr_get_len(&bind_addr)); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "bind() error", status); @@ -1711,8 +1718,7 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, * (only when public address is not specified). */ status = create_sip_udp_sock(pjsip_transport_type_get_af(type), - &cfg->bound_addr, cfg->port, - &sock, &pub_addr); + cfg, &sock, &pub_addr); if (status != PJ_SUCCESS) goto on_return; @@ -1743,9 +1749,10 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, * Create TCP transport. */ pjsua_transport_config config; - pjsip_host_port a_name; pjsip_tpfactory *tcp; - pj_sockaddr_in local_addr; + pjsip_tcp_transport_cfg tcp_cfg; + + pjsip_tcp_transport_cfg_default(&tcp_cfg, pj_AF_INET()); /* Supply default config if it's not specified */ if (cfg == NULL) { @@ -1753,14 +1760,14 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, cfg = &config; } - /* Init local address */ - pj_sockaddr_in_init(&local_addr, 0, 0); - + /* Configure bind address */ if (cfg->port) - local_addr.sin_port = pj_htons((pj_uint16_t)cfg->port); + pj_sockaddr_set_port(&tcp_cfg.bind_addr, (pj_uint16_t)cfg->port); if (cfg->bound_addr.slen) { - status = pj_sockaddr_in_set_str_addr(&local_addr,&cfg->bound_addr); + status = pj_sockaddr_set_str_addr(tcp_cfg.af, + &tcp_cfg.bind_addr, + &cfg->bound_addr); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to resolve transport bound address", @@ -1769,14 +1776,17 @@ PJ_DEF(pj_status_t) pjsua_transport_create( pjsip_transport_type_e type, } } - /* Init published name */ - pj_bzero(&a_name, sizeof(pjsip_host_port)); + /* Set published name */ if (cfg->public_addr.slen) - a_name.host = cfg->public_addr; + tcp_cfg.addr_name.host = cfg->public_addr; + + /* Copy the QoS settings */ + tcp_cfg.qos_type = cfg->qos_type; + pj_memcpy(&tcp_cfg.qos_params, &cfg->qos_params, + sizeof(cfg->qos_params)); /* Create the TCP transport */ - status = pjsip_tcp_transport_start2(pjsua_var.endpt, &local_addr, - &a_name, 1, &tcp); + status = pjsip_tcp_transport_start3(pjsua_var.endpt, &tcp_cfg, &tcp); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating SIP TCP listener", diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index fff917ba..06d12779 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -384,13 +384,19 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg, /* Loop retry to bind RTP and RTCP sockets. */ for (i=0; iqos_type, + &cfg->qos_params, + 2, THIS_FILE, "RTP socket"); + + /* Bind RTP socket */ status=pj_sock_bind_in(sock[0], pj_ntohl(bound_addr.sin_addr.s_addr), next_rtp_port); if (status != PJ_SUCCESS) { @@ -399,7 +405,7 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg, continue; } - /* Create and bind RTCP socket. */ + /* Create RTCP socket. */ status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &sock[1]); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "socket() error", status); @@ -407,6 +413,12 @@ static pj_status_t create_rtp_rtcp_sock(const pjsua_transport_config *cfg, return status; } + /* Apply QoS to RTCP socket, if specified */ + status = pj_sock_apply_qos2(sock[1], cfg->qos_type, + &cfg->qos_params, + 2, THIS_FILE, "RTCP socket"); + + /* Bind RTCP socket */ status=pj_sock_bind_in(sock[1], pj_ntohl(bound_addr.sin_addr.s_addr), (pj_uint16_t)(next_rtp_port+1)); if (status != PJ_SUCCESS) { @@ -881,7 +893,7 @@ static pj_status_t parse_host_port(const pj_str_t *host_port, } /* Create ICE media transports (when ice is enabled) */ -static pj_status_t create_ice_media_transports(void) +static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg) { char stunip[PJ_INET6_ADDRSTRLEN]; pj_ice_strans_cfg ice_cfg; @@ -915,6 +927,11 @@ static pj_status_t create_ice_media_transports(void) if (pjsua_var.media_cfg.ice_max_host_cands >= 0) ice_cfg.stun.max_host_cands = pjsua_var.media_cfg.ice_max_host_cands; + /* Copy QoS setting to STUN setting */ + ice_cfg.stun.cfg.qos_type = cfg->qos_type; + pj_memcpy(&ice_cfg.stun.cfg.qos_params, &cfg->qos_params, + sizeof(cfg->qos_params)); + /* Configure TURN settings */ if (pjsua_var.media_cfg.enable_turn) { status = parse_host_port(&pjsua_var.media_cfg.turn_server, @@ -930,6 +947,11 @@ static pj_status_t create_ice_media_transports(void) pj_memcpy(&ice_cfg.turn.auth_cred, &pjsua_var.media_cfg.turn_auth_cred, sizeof(ice_cfg.turn.auth_cred)); + + /* Copy QoS setting to TURN setting */ + ice_cfg.turn.cfg.qos_type = cfg->qos_type; + pj_memcpy(&ice_cfg.turn.cfg.qos_params, &cfg->qos_params, + sizeof(cfg->qos_params)); } /* Create each media transport */ @@ -1025,7 +1047,7 @@ PJ_DEF(pj_status_t) pjsua_media_transports_create( /* Create the transports */ if (pjsua_var.media_cfg.enable_ice) { - status = create_ice_media_transports(); + status = create_ice_media_transports(&cfg); } else { status = create_udp_media_transports(&cfg); } -- cgit v1.2.3