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 --- 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 ++++++- 5 files changed, 259 insertions(+), 56 deletions(-) (limited to 'pjsip') 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