Re #1964: Implement QoS for darwin OS which supports SO_NET_SERVICE_TYPE.

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@5445 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Riza Sulistyo 2016-10-05 09:52:39 +00:00
parent 88044b49a5
commit b1490d57d8
14 changed files with 473 additions and 9 deletions

View File

@ -5824,6 +5824,14 @@ case $target in
ac_os_objs="$ac_os_objs os_info_iphone.o"
;;
esac
# QoS
case $target in
*darwin*)
;;
*)
ac_os_objs="$ac_os_objs sock_qos_bsd.o"
;;
esac
# UUID
case $target in
*android*)
@ -5842,7 +5850,7 @@ esac
case $target in
*darwin*)
ac_os_objs="$ac_os_objs os_core_darwin.o"
ac_os_objs="$ac_os_objs sock_qos_darwin.o os_core_darwin.o"
;;
esac

View File

@ -462,6 +462,14 @@ case $target in
ac_os_objs="$ac_os_objs os_info_iphone.o"
;;
esac
# QoS
case $target in
*darwin*)
;;
*)
ac_os_objs="$ac_os_objs sock_qos_bsd.o"
;;
esac
# UUID
case $target in
*android*)
@ -480,7 +488,7 @@ esac
case $target in
*darwin*)
ac_os_objs="$ac_os_objs os_core_darwin.o"
ac_os_objs="$ac_os_objs sock_qos_darwin.o os_core_darwin.o"
;;
esac

View File

@ -34,7 +34,7 @@ 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 \
os_info.o pool.o pool_buf.o pool_caching.o pool_dbg.o rand.o \
rbtree.o sock_common.o sock_qos_common.o sock_qos_bsd.o \
rbtree.o sock_common.o sock_qos_common.o \
ssl_sock_common.o ssl_sock_ossl.o ssl_sock_dump.o \
string.o timer.o types.o
export PJLIB_CFLAGS += $(_CFLAGS)

View File

@ -18,6 +18,8 @@ export PJLIB_OBJS += ioqueue_select.o
export PJLIB_OBJS += file_access_unistd.o file_io_ansi.o
export PJLIB_OBJS += sock_qos_darwin.o
#
# TEST_OBJS are operating system specific object files to be included in
# the test application.

View File

@ -21,6 +21,8 @@ export PJLIB_OBJS += compat/sigjmp.o compat/setjmp_i386.o \
export PJLIB_OBJS += ioqueue_epoll.o
#export PJLIB_OBJS += ioqueue_select.o
export PJLIB_OBJS += sock_qos_bsd.o
#
# TEST_OBJS are operating system specific object files to be included in
# the test application.

View File

@ -20,6 +20,8 @@ else
export PJLIB_OBJS += ioqueue_select.o
endif
export PJLIB_OBJS += sock_qos_bsd.o
#
# TEST_OBJS are operating system specific object files to be included in
# the test application.

View File

@ -18,6 +18,7 @@ export PJLIB_OBJS += addr_resolv_sock.o guid_simple.o \
export PJLIB_OBJS += ioqueue_select.o
export PJLIB_OBJS += file_access_unistd.o file_io_ansi.o
export PJLIB_OBJS += sock_qos_bsd.o
#
# TEST_OBJS are operating system specific object files to be included in

View File

@ -17,6 +17,8 @@ export PJLIB_OBJS += addr_resolv_sock.o file_access_unistd.o \
export PJLIB_OBJS += ioqueue_select.o
#export PJLIB_OBJS += ioqueue_epoll.o
export PJLIB_OBJS += sock_qos_bsd.o
#
# TEST_OBJS are operating system specific object files to be included in
# the test application.

View File

@ -19,6 +19,8 @@ export PJLIB_OBJS += ioqueue_select.o
export PJLIB_OBJS += file_io_win32.o file_access_win32.o
#export PJLIB_OBJS += file_io_ansi.o
export PJLIB_OBJS += sock_qos_bsd.o
#
# TEST_OBJS are operating system specific object files to be included in
# the test application.

View File

@ -159,18 +159,21 @@
# define OSERR_ECONNRESET WSAECONNRESET
# define OSERR_ENOTCONN WSAENOTCONN
# define OSERR_EAFNOSUPPORT WSAEAFNOSUPPORT
# define OSERR_ENOPROTOOPT WSAENOPROTOOPT
#elif defined(PJ_SYMBIAN) && PJ_SYMBIAN!=0
# define OSERR_EWOULDBLOCK -1
# define OSERR_EINPROGRESS -1
# define OSERR_ECONNRESET -1
# define OSERR_ENOTCONN -1
# define OSERR_EAFNOSUPPORT -1
# define OSERR_ENOPROTOOPT -1
#else
# define OSERR_EWOULDBLOCK EWOULDBLOCK
# define OSERR_EINPROGRESS EINPROGRESS
# define OSERR_ECONNRESET ECONNRESET
# define OSERR_ENOTCONN ENOTCONN
# define OSERR_EAFNOSUPPORT EAFNOSUPPORT
# define OSERR_ENOPROTOOPT ENOPROTOOPT
#endif

View File

@ -842,6 +842,9 @@
/** QoS backend for Symbian */
#define PJ_QOS_SYMBIAN 4
/** QoS backend for Darwin */
#define PJ_QOS_DARWIN 5
/**
* Force the use of some QoS backend API for some platforms.
*/

View File

@ -121,10 +121,11 @@ PJ_BEGIN_DECL
typedef enum pj_qos_type
{
PJ_QOS_TYPE_BEST_EFFORT,
PJ_QOS_TYPE_BACKGROUND,
PJ_QOS_TYPE_BACKGROUND,
PJ_QOS_TYPE_VIDEO,
PJ_QOS_TYPE_VOICE,
PJ_QOS_TYPE_CONTROL
PJ_QOS_TYPE_CONTROL,
PJ_QOS_TYPE_SIGNALLING
} pj_qos_type;
\endcode
@ -141,6 +142,7 @@ PJ_BEGIN_DECL
VIDEO 0x28 VI (Video) 5
VOICE 0x30 VO (Voice) 6
CONTROL 0x38 VO (Voice) 7
SIGNALLING 0x28 VI (Video) 5
=================================================================
\endcode
@ -235,10 +237,11 @@ 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_BACKGROUND, /**< Background traffic. */
PJ_QOS_TYPE_VIDEO, /**< Video traffic. */
PJ_QOS_TYPE_VOICE, /**< Voice traffic. */
PJ_QOS_TYPE_CONTROL /**< Control traffic. */
PJ_QOS_TYPE_CONTROL, /**< Control traffic. */
PJ_QOS_TYPE_SIGNALLING /**< Signalling traffic. */
} pj_qos_type;
/**

View File

@ -31,10 +31,11 @@ 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, 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 */
{ALL_FLAGS, 0x38, 7, PJ_QOS_WMM_PRIO_VOICE}, /* CO */
{ALL_FLAGS, 0x28, 5, PJ_QOS_WMM_PRIO_VIDEO} /* SIG */
};

View File

@ -0,0 +1,427 @@
/* $Id$ */
/*
* Copyright (C) 2008-2011 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 <pj/sock_qos.h>
#include <pj/assert.h>
#include <pj/errno.h>
#include <pj/string.h>
#include <pj/compat/socket.h>
/* This is the implementation of QoS with BSD socket's setsockopt(),
* using Darwin-specific SO_NET_SERVICE_TYPE if available, and IP_TOS/
* IPV6_TCLASS as fallback.
*/
#if !defined(PJ_QOS_IMPLEMENTATION) || PJ_QOS_IMPLEMENTATION==PJ_QOS_IOS
#include <sys/socket.h>
#ifdef SO_NET_SERVICE_TYPE
static pj_status_t sock_set_net_service_type(pj_sock_t sock, int val)
{
pj_status_t status;
status = pj_sock_setsockopt(sock, pj_SOL_SOCKET(), SO_NET_SERVICE_TYPE,
&val, sizeof(val));
if (status == PJ_STATUS_FROM_OS(OSERR_ENOPROTOOPT))
status = PJ_ENOTSUP;
return status;
}
#endif
static pj_status_t sock_set_net_service_type_type(pj_sock_t sock,
pj_qos_type type)
{
#ifdef SO_NET_SERVICE_TYPE
int val = NET_SERVICE_TYPE_BE;
switch (type) {
case PJ_QOS_TYPE_BEST_EFFORT:
val = NET_SERVICE_TYPE_BE;
break;
case PJ_QOS_TYPE_BACKGROUND:
val = NET_SERVICE_TYPE_BK;
break;
case PJ_QOS_TYPE_SIGNALLING:
val = NET_SERVICE_TYPE_SIG;
break;
case PJ_QOS_TYPE_VIDEO:
val = NET_SERVICE_TYPE_VI;
break;
case PJ_QOS_TYPE_VOICE:
case PJ_QOS_TYPE_CONTROL:
default:
val = NET_SERVICE_TYPE_VO;
break;
}
return sock_set_net_service_type(sock, val);
#else
return PJ_ENOTSUP;
#endif
}
static pj_status_t sock_set_net_service_type_params(pj_sock_t sock,
pj_qos_params *param)
{
#ifdef SO_NET_SERVICE_TYPE
pj_status_t status;
int val = -1;
PJ_ASSERT_RETURN(param, PJ_EINVAL);
/*
* Sources:
* - IETF draft-szigeti-tsvwg-ieee-802-11e-01
* - iOS 10 SDK, sys/socket.h
*/
if (val == -1 && param->flags & PJ_QOS_PARAM_HAS_DSCP) {
if (param->dscp_val == 0) /* DF */
val = NET_SERVICE_TYPE_BE;
else if (param->dscp_val < 0x10) /* CS1, AF11, AF12, AF13 */
val = NET_SERVICE_TYPE_BK;
else if (param->dscp_val == 0x10) /* CS2 */
val = NET_SERVICE_TYPE_OAM;
else if (param->dscp_val < 0x18) /* AF21, AF22, AF23 */
val = NET_SERVICE_TYPE_RD;
else if (param->dscp_val < 0x20) /* CS3, AF31, AF32, AF33 */
val = NET_SERVICE_TYPE_AV;
else if (param->dscp_val == 0x20) /* CS4 */
val = NET_SERVICE_TYPE_RD;
else if (param->dscp_val < 0x28) /* AF41, AF42, AF43 */
val = NET_SERVICE_TYPE_VI;
else if (param->dscp_val == 0x28) /* CS5 */
val = NET_SERVICE_TYPE_SIG;
else
val = NET_SERVICE_TYPE_VO; /* VOICE-ADMIT, EF, CS6, etc. */
}
if (val == -1 && param->flags & PJ_QOS_PARAM_HAS_WMM) {
switch (param->wmm_prio) {
case PJ_QOS_WMM_PRIO_BULK_EFFORT:
val = NET_SERVICE_TYPE_BE;
break;
case PJ_QOS_WMM_PRIO_BULK:
val = NET_SERVICE_TYPE_BK;
break;
case PJ_QOS_WMM_PRIO_VIDEO:
val = NET_SERVICE_TYPE_VI;
break;
case PJ_QOS_WMM_PRIO_VOICE:
val = NET_SERVICE_TYPE_VO;
break;
}
}
if (val == -1) {
pj_qos_type type;
status = pj_qos_get_type(param, &type);
if (status == PJ_SUCCESS)
return sock_set_net_service_type_type(sock, type);
val = NET_SERVICE_TYPE_BE;
}
return sock_set_net_service_type(sock, val);
#else
return PJ_ENOTSUP;
#endif
}
static pj_status_t sock_set_ip_ds(pj_sock_t sock, pj_qos_params *param)
{
pj_status_t status = PJ_SUCCESS;
PJ_ASSERT_RETURN(param, PJ_EINVAL);
if (param->flags & PJ_QOS_PARAM_HAS_DSCP) {
/* We need to know if the socket is IPv4 or IPv6 */
pj_sockaddr sa;
int salen = sizeof(salen);
/* Value is dscp_val << 2 */
int val = (param->dscp_val << 2);
status = pj_sock_getsockname(sock, &sa, &salen);
if (status != PJ_SUCCESS)
return status;
if (sa.addr.sa_family == pj_AF_INET()) {
/* In IPv4, the DS field goes in the TOS field */
status = pj_sock_setsockopt(sock, pj_SOL_IP(), pj_IP_TOS(),
&val, sizeof(val));
} else if (sa.addr.sa_family == pj_AF_INET6()) {
/* In IPv6, the DS field goes in the Traffic Class field */
status = pj_sock_setsockopt(sock, pj_SOL_IPV6(),
pj_IPV6_TCLASS(),
&val, sizeof(val));
} else
status = PJ_EINVAL;
if (status != PJ_SUCCESS) {
param->flags &= ~(PJ_QOS_PARAM_HAS_DSCP);
}
}
return status;
}
PJ_DEF(pj_status_t) pj_sock_set_qos_params(pj_sock_t sock,
pj_qos_params *param)
{
pj_status_t status;
PJ_ASSERT_RETURN(param, PJ_EINVAL);
/* No op? */
if (!param->flags)
return PJ_SUCCESS;
/* Clear prio field since we don't support it */
param->flags &= ~(PJ_QOS_PARAM_HAS_SO_PRIO);
/* Try SO_NET_SERVICE_TYPE */
status = sock_set_net_service_type_params(sock, param);
if (status == PJ_SUCCESS)
return status;
if (status != PJ_ENOTSUP) {
/* SO_NET_SERVICE_TYPE sets both DSCP and WMM */
param->flags &= ~(PJ_QOS_PARAM_HAS_DSCP);
param->flags &= ~(PJ_QOS_PARAM_HAS_WMM);
return status;
}
/* Fall back to IP_TOS/IPV6_TCLASS */
return sock_set_ip_ds(sock, param);
}
PJ_DEF(pj_status_t) pj_sock_set_qos_type(pj_sock_t sock,
pj_qos_type type)
{
pj_status_t status;
pj_qos_params param;
/* Try SO_NET_SERVICE_TYPE */
status = sock_set_net_service_type_type(sock, type);
if (status == PJ_SUCCESS || status != PJ_ENOTSUP)
return status;
/* Fall back to IP_TOS/IPV6_TCLASS */
status = pj_qos_get_params(type, &param);
if (status != PJ_SUCCESS)
return status;
return sock_set_ip_ds(sock, &param);
}
#ifdef SO_NET_SERVICE_TYPE
static pj_status_t sock_get_net_service_type(pj_sock_t sock, int *p_val)
{
pj_status_t status;
int optlen = sizeof(*p_val);
PJ_ASSERT_RETURN(p_val, PJ_EINVAL);
status = pj_sock_getsockopt(sock, pj_SOL_SOCKET(), SO_NET_SERVICE_TYPE,
p_val, &optlen);
if (status == PJ_STATUS_FROM_OS(OSERR_ENOPROTOOPT))
status = PJ_ENOTSUP;
return status;
}
#endif
static pj_status_t sock_get_net_service_type_type(pj_sock_t sock,
pj_qos_type *p_type)
{
#ifdef SO_NET_SERVICE_TYPE
pj_status_t status;
int val;
PJ_ASSERT_RETURN(p_type, PJ_EINVAL);
status = sock_get_net_service_type(sock, &val);
if (status == PJ_SUCCESS) {
switch (val) {
default:
case NET_SERVICE_TYPE_BE:
*p_type = PJ_QOS_TYPE_BEST_EFFORT;
break;
case NET_SERVICE_TYPE_BK:
*p_type = PJ_QOS_TYPE_BACKGROUND;
break;
case NET_SERVICE_TYPE_SIG:
*p_type = PJ_QOS_TYPE_SIGNALLING;
break;
case NET_SERVICE_TYPE_VI:
case NET_SERVICE_TYPE_RV:
case NET_SERVICE_TYPE_AV:
case NET_SERVICE_TYPE_OAM:
case NET_SERVICE_TYPE_RD:
*p_type = PJ_QOS_TYPE_VIDEO;
break;
case NET_SERVICE_TYPE_VO:
*p_type = PJ_QOS_TYPE_VOICE;
break;
}
}
return status;
#else
return PJ_ENOTSUP;
#endif
}
static pj_status_t sock_get_net_service_type_params(pj_sock_t sock,
pj_qos_params *p_param)
{
#ifdef SO_NET_SERVICE_TYPE
pj_status_t status;
int val;
PJ_ASSERT_RETURN(p_param, PJ_EINVAL);
status = sock_get_net_service_type(sock, &val);
if (status == PJ_SUCCESS) {
pj_bzero(p_param, sizeof(*p_param));
/* Note: these are just educated guesses, chosen for symmetry with
* sock_set_net_service_type_params: we can't know the actual values
* chosen by the OS, or even if DSCP/WMM are used at all.
*
* The source for mapping DSCP to WMM is:
* - IETF draft-szigeti-tsvwg-ieee-802-11e-01
*/
switch (val) {
default:
case NET_SERVICE_TYPE_BE:
p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
p_param->dscp_val = 0; /* DF */
p_param->wmm_prio = PJ_QOS_WMM_PRIO_BULK_EFFORT; /* AC_BE */
break;
case NET_SERVICE_TYPE_BK:
p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
p_param->dscp_val = 0x08; /* CS1 */
p_param->wmm_prio = PJ_QOS_WMM_PRIO_BULK; /* AC_BK */
break;
case NET_SERVICE_TYPE_SIG:
p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
p_param->dscp_val = 0x28; /* CS5 */
p_param->wmm_prio = PJ_QOS_WMM_PRIO_VIDEO; /* AC_VI */
break;
case NET_SERVICE_TYPE_VI:
p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
p_param->dscp_val = 0x22; /* AF41 */
p_param->wmm_prio = PJ_QOS_WMM_PRIO_VIDEO; /* AC_VI */
break;
case NET_SERVICE_TYPE_VO:
p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
p_param->dscp_val = 0x30; /* CS6 */
p_param->wmm_prio = PJ_QOS_WMM_PRIO_VOICE; /* AC_VO */
break;
case NET_SERVICE_TYPE_RV:
p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
p_param->dscp_val = 0x22; /* AF41 */
p_param->wmm_prio = PJ_QOS_WMM_PRIO_VIDEO; /* AC_VI */
break;
case NET_SERVICE_TYPE_AV:
p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
p_param->dscp_val = 0x18; /* CS3 */
p_param->wmm_prio = PJ_QOS_WMM_PRIO_VIDEO; /* AC_VI */
break;
case NET_SERVICE_TYPE_OAM:
p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
p_param->dscp_val = 0x10; /* CS2 */
p_param->wmm_prio = PJ_QOS_WMM_PRIO_BULK_EFFORT; /* AC_BE */
break;
case NET_SERVICE_TYPE_RD:
p_param->flags = PJ_QOS_PARAM_HAS_DSCP | PJ_QOS_PARAM_HAS_WMM;
p_param->dscp_val = 0x20; /* CS4 */
p_param->wmm_prio = PJ_QOS_WMM_PRIO_VIDEO; /* AC_VI */
break;
}
}
return status;
#else
return PJ_ENOTSUP;
#endif
}
PJ_DEF(pj_status_t) pj_sock_get_qos_params(pj_sock_t sock,
pj_qos_params *p_param)
{
pj_status_t status;
int val, optlen;
pj_sockaddr sa;
int salen = sizeof(salen);
PJ_ASSERT_RETURN(p_param, PJ_EINVAL);
pj_bzero(p_param, sizeof(*p_param));
/* Try SO_NET_SERVICE_TYPE */
status = sock_get_net_service_type_params(sock, p_param);
if (status != PJ_ENOTSUP)
return status;
/* Fall back to IP_TOS/IPV6_TCLASS */
status = pj_sock_getsockname(sock, &sa, &salen);
if (status != PJ_SUCCESS)
return status;
optlen = sizeof(val);
if (sa.addr.sa_family == pj_AF_INET()) {
status = pj_sock_getsockopt(sock, pj_SOL_IP(), pj_IP_TOS(),
&val, &optlen);
} else if (sa.addr.sa_family == pj_AF_INET6()) {
status = pj_sock_getsockopt(sock, pj_SOL_IPV6(), pj_IPV6_TCLASS(),
&val, &optlen);
} else
status = PJ_EINVAL;
if (status == PJ_SUCCESS) {
p_param->flags |= PJ_QOS_PARAM_HAS_DSCP;
p_param->dscp_val = (pj_uint8_t)(val >> 2);
}
return status;
}
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;
PJ_ASSERT_RETURN(p_type, PJ_EINVAL);
status = sock_get_net_service_type_type(sock, p_type);
if (status != PJ_ENOTSUP)
return status;
status = pj_sock_get_qos_params(sock, &param);
if (status != PJ_SUCCESS)
return status;
return pj_qos_get_type(&param, p_type);
}
#endif /* PJ_QOS_IMPLEMENTATION */