blob: 2858c2d002d0795237fa04ffb67aa04daa4c1cd5 [file] [log] [blame]
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/test/channel_transport/udp_socket2_win.h"
#include <assert.h>
#include <stdlib.h>
#include <winsock2.h>
#include "webrtc/base/format_macros.h"
#include "webrtc/system_wrappers/include/sleep.h"
#include "webrtc/test/channel_transport/traffic_control_win.h"
#include "webrtc/test/channel_transport/udp_socket2_manager_win.h"
#pragma warning(disable : 4311)
namespace webrtc {
namespace test {
typedef struct _QOS_DESTADDR
{
QOS_OBJECT_HDR ObjectHdr;
const struct sockaddr* SocketAddress;
ULONG SocketAddressLength;
} QOS_DESTADDR, *LPQOS_DESTADDR;
typedef const QOS_DESTADDR* LPCQOS_DESTADDR;
// TODO (patrikw): seems to be defined in ws2ipdef.h as 3. How come it's
// redefined here (as a different value)?
#define IP_TOS 8
#define QOS_GENERAL_ID_BASE 2000
#define QOS_OBJECT_DESTADDR (0x00000004 + QOS_GENERAL_ID_BASE)
UdpSocket2Windows::UdpSocket2Windows(const int32_t id,
UdpSocketManager* mgr, bool ipV6Enable,
bool disableGQOS)
: _id(id),
_qos(true),
_iProtocol(0),
_outstandingCalls(0),
_outstandingCallComplete(0),
_terminate(false),
_addedToMgr(false),
delete_event_(true, false),
_outstandingCallsDisabled(false),
_clientHandle(NULL),
_flowHandle(NULL),
_filterHandle(NULL),
_flow(NULL),
_gtc(NULL),
_pcp(-2),
_receiveBuffers(0)
{
WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id,
"UdpSocket2Windows::UdpSocket2Windows()");
_wantsIncoming = false;
_mgr = static_cast<UdpSocket2ManagerWindows *>(mgr);
_obj = NULL;
_incomingCb = NULL;
_socket = INVALID_SOCKET;
_ptrCbRWLock = RWLockWrapper::CreateRWLock();
_ptrDestRWLock = RWLockWrapper::CreateRWLock();
_ptrSocketRWLock = RWLockWrapper::CreateRWLock();
// Check if QoS is supported.
BOOL bProtocolFound = FALSE;
WSAPROTOCOL_INFO *lpProtocolBuf = NULL;
WSAPROTOCOL_INFO pProtocolInfo;
if(!disableGQOS)
{
DWORD dwBufLen = 0;
// Set dwBufLen to the size needed to retreive all the requested
// information from WSAEnumProtocols.
int32_t nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen);
lpProtocolBuf = (WSAPROTOCOL_INFO*)malloc(dwBufLen);
nRet = WSAEnumProtocols(NULL, lpProtocolBuf, &dwBufLen);
if (ipV6Enable)
{
_iProtocol=AF_INET6;
} else {
_iProtocol=AF_INET;
}
for (int32_t i=0; i<nRet; i++)
{
if (_iProtocol == lpProtocolBuf[i].iAddressFamily &&
IPPROTO_UDP == lpProtocolBuf[i].iProtocol)
{
if ((XP1_QOS_SUPPORTED ==
(XP1_QOS_SUPPORTED & lpProtocolBuf[i].dwServiceFlags1)))
{
pProtocolInfo = lpProtocolBuf[i];
bProtocolFound = TRUE;
break;
}
}
}
}
if(!bProtocolFound)
{
free(lpProtocolBuf);
_qos=false;
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"UdpSocket2Windows::UdpSocket2Windows(), SOCKET_ERROR_NO_QOS,\
!bProtocolFound");
} else {
_socket = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,
FROM_PROTOCOL_INFO,&pProtocolInfo, 0,
WSA_FLAG_OVERLAPPED);
free(lpProtocolBuf);
if (_socket != INVALID_SOCKET)
{
return;
} else {
_qos = false;
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"UdpSocket2Windows::UdpSocket2Windows(), SOCKET_ERROR_NO_QOS");
}
}
// QoS not supported.
if(ipV6Enable)
{
_socket = WSASocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, 0 , 0,
WSA_FLAG_OVERLAPPED);
}else
{
_socket = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0 , 0,
WSA_FLAG_OVERLAPPED);
}
if (_socket == INVALID_SOCKET)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"UdpSocket2Windows::UdpSocket2Windows(), INVALID_SOCKET,\
WSAerror: %d",
WSAGetLastError());
}
// Disable send buffering on the socket to improve CPU usage.
// This is done by setting SO_SNDBUF to 0.
int32_t nZero = 0;
int32_t nRet = setsockopt(_socket, SOL_SOCKET, SO_SNDBUF,
(char*)&nZero, sizeof(nZero));
if( nRet == SOCKET_ERROR )
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"UdpSocket2Windows::UdpSocket2Windows(), SOCKET_ERROR,\
WSAerror: %d",
WSAGetLastError());
}
}
UdpSocket2Windows::~UdpSocket2Windows()
{
WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id,
"UdpSocket2Windows::~UdpSocket2Windows()");
delete_event_.Wait(rtc::Event::kForever);
delete _ptrCbRWLock;
delete _ptrDestRWLock;
delete _ptrSocketRWLock;
if (_flow)
{
free(_flow);
_flow = NULL;
}
if (_gtc)
{
if(_filterHandle)
{
_gtc->TcDeleteFilter(_filterHandle);
}
if(_flowHandle)
{
_gtc->TcDeleteFlow(_flowHandle);
}
TrafficControlWindows::Release( _gtc);
}
}
bool UdpSocket2Windows::ValidHandle()
{
return GetFd() != INVALID_SOCKET;
}
bool UdpSocket2Windows::SetCallback(CallbackObj obj, IncomingSocketCallback cb)
{
_ptrCbRWLock->AcquireLockExclusive();
_obj = obj;
_incomingCb = cb;
_ptrCbRWLock->ReleaseLockExclusive();
WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
"UdpSocket2Windows(%d)::SetCallback ",(int32_t)this);
if(_addedToMgr)
{
WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
"UdpSocket2Windows(%d)::SetCallback alreadey added",
(int32_t) this);
return false;
}
if (_mgr->AddSocket(this))
{
WEBRTC_TRACE(
kTraceDebug, kTraceTransport, _id,
"UdpSocket2Windows(%d)::SetCallback socket added to manager",
(int32_t)this);
_addedToMgr = true;
return true;
}
WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
"UdpSocket2Windows(%d)::SetCallback error adding me to mgr",
(int32_t) this);
return false;
}
bool UdpSocket2Windows::SetSockopt(int32_t level, int32_t optname,
const int8_t* optval, int32_t optlen)
{
bool returnValue = true;
if(!AquireSocket())
{
return false;
}
if(0 != setsockopt(_socket, level, optname,
reinterpret_cast<const char*>(optval), optlen ))
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"UdpSocket2Windows::SetSockopt(), WSAerror:%d",
WSAGetLastError());
returnValue = false;
}
ReleaseSocket();
return returnValue;
}
bool UdpSocket2Windows::StartReceiving(uint32_t receiveBuffers)
{
WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
"UdpSocket2Windows(%d)::StartReceiving(%d)", (int32_t)this,
receiveBuffers);
_wantsIncoming = true;
int32_t numberOfReceiveBuffersToCreate =
receiveBuffers - _receiveBuffers.Value();
numberOfReceiveBuffersToCreate = (numberOfReceiveBuffersToCreate < 0) ?
0 : numberOfReceiveBuffersToCreate;
int32_t error = 0;
for(int32_t i = 0;
i < numberOfReceiveBuffersToCreate;
i++)
{
if(PostRecv())
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"UdpSocket2Windows::StartReceiving() i=%d", i);
error = -1;
break;
}
++_receiveBuffers;
}
if(error == -1)
{
return false;
}
WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
"Socket receiving using:%d number of buffers",
_receiveBuffers.Value());
return true;
}
bool UdpSocket2Windows::StopReceiving()
{
WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
"UdpSocket2Windows::StopReceiving()");
_wantsIncoming = false;
return true;
}
bool UdpSocket2Windows::Bind(const SocketAddress& name)
{
const struct sockaddr* addr =
reinterpret_cast<const struct sockaddr*>(&name);
bool returnValue = true;
if(!AquireSocket())
{
return false;
}
if (0 != bind(_socket, addr, sizeof(SocketAddress)))
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"UdpSocket2Windows::Bind() WSAerror: %d",
WSAGetLastError());
returnValue = false;
}
ReleaseSocket();
return returnValue;
}
int32_t UdpSocket2Windows::SendTo(const int8_t* buf, size_t len,
const SocketAddress& to)
{
int32_t retVal = 0;
int32_t error = 0;
PerIoContext* pIoContext = _mgr->PopIoContext();
if(pIoContext == 0)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"UdpSocket2Windows(%d)::SendTo(), pIoContext==0",
(int32_t) this);
return -1;
}
// sizeof(pIoContext->buffer) is smaller than the highest number that
// can be represented by a size_t.
if(len >= sizeof(pIoContext->buffer))
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"UdpSocket2Windows(%d)::SendTo(), len= %" PRIuS
" > buffer_size = %d",
(int32_t) this,
len,sizeof(pIoContext->buffer));
len = sizeof(pIoContext->buffer);
}
memcpy(pIoContext->buffer,buf,len);
pIoContext->wsabuf.buf = pIoContext->buffer;
pIoContext->wsabuf.len = static_cast<ULONG>(len);
pIoContext->fromLen=sizeof(SocketAddress);
pIoContext->ioOperation = OP_WRITE;
pIoContext->nTotalBytes = len;
pIoContext->nSentBytes=0;
DWORD numOfbytesSent = 0;
const struct sockaddr* addr = reinterpret_cast<const struct sockaddr*>(&to);
if(!AquireSocket())
{
_mgr->PushIoContext(pIoContext);
return -1;
}
// Assume that the WSASendTo call will be successfull to make sure that
// _outstandingCalls is positive. Roll back if WSASendTo failed.
if(!NewOutstandingCall())
{
_mgr->PushIoContext(pIoContext);
ReleaseSocket();
return -1;
}
retVal = WSASendTo(_socket, &pIoContext->wsabuf, 1, &numOfbytesSent,
0, addr, sizeof(SocketAddress),
&(pIoContext->overlapped), 0);
ReleaseSocket();
if( retVal == SOCKET_ERROR )
{
error = WSAGetLastError();
if(error != ERROR_IO_PENDING)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"UdpSocket2Windows::SendTo() WSAerror: %d",error);
}
}
if(retVal == 0 || (retVal == SOCKET_ERROR && error == ERROR_IO_PENDING))
{
return static_cast<int32_t>(len);
}
error = _mgr->PushIoContext(pIoContext);
if(error)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"UdpSocket2Windows(%d)::SendTo(), error:%d pushing ioContext",
(int32_t)this, error);
}
// Roll back.
OutstandingCallCompleted();
return -1;
}
void UdpSocket2Windows::IOCompleted(PerIoContext* pIOContext,
uint32_t ioSize, uint32_t error)
{
if(pIOContext == NULL || error == ERROR_OPERATION_ABORTED)
{
if ((pIOContext != NULL) &&
!pIOContext->ioInitiatedByPlatformThread &&
(error == ERROR_OPERATION_ABORTED) &&
(pIOContext->ioOperation == OP_READ) &&
_outstandingCallsDisabled)
{
// !pIOContext->initiatedIOByPlatformThread indicate that the I/O
// was not initiated by a PlatformThread thread.
// This may happen if the thread that initiated receiving (e.g.
// by calling StartListen())) is deleted before any packets have
// been received.
// In this case there is no packet in the PerIoContext. Re-use it
// to post a new PostRecv(..).
// Note 1: the PerIoContext will henceforth be posted by a thread
// that is controlled by the socket implementation.
// Note 2: This is more likely to happen to RTCP packets as
// they are less frequent than RTP packets.
// Note 3: _outstandingCallsDisabled being false indicates
// that the socket isn't being shut down.
// Note 4: This should only happen buffers set to receive packets
// (OP_READ).
} else {
if(pIOContext == NULL)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"UdpSocket2Windows::IOCompleted(%d,%d,%d), %d",
(int32_t)pIOContext,
ioSize,
error,
pIOContext ? (int32_t)pIOContext->ioOperation : -1);
} else {
WEBRTC_TRACE(
kTraceDebug,
kTraceTransport,
_id,
"UdpSocket2Windows::IOCompleted() Operation aborted");
}
if(pIOContext)
{
int32_t remainingReceiveBuffers = --_receiveBuffers;
if(remainingReceiveBuffers < 0)
{
assert(false);
}
int32_t err = _mgr->PushIoContext(pIOContext);
if(err)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"UdpSocket2Windows::IOCompleted(), err = %d, when\
pushing ioContext after error",
err);
}
}
OutstandingCallCompleted();
return;
}
} // if (pIOContext == NULL || error == ERROR_OPERATION_ABORTED)
if(pIOContext->ioOperation == OP_WRITE)
{
_mgr->PushIoContext(pIOContext);
}
else if(pIOContext->ioOperation == OP_READ)
{
if(!error && ioSize != 0)
{
_ptrCbRWLock->AcquireLockShared();
if(_wantsIncoming && _incomingCb)
{
_incomingCb(_obj,
reinterpret_cast<const int8_t*>(
pIOContext->wsabuf.buf),
ioSize,
&pIOContext->from);
}
_ptrCbRWLock->ReleaseLockShared();
}
int32_t err = PostRecv(pIOContext);
if(err == 0)
{
// The PerIoContext was posted by a thread controlled by the socket
// implementation.
pIOContext->ioInitiatedByPlatformThread = true;
}
OutstandingCallCompleted();
return;
} else {
// Unknown operation. Should not happen. Return pIOContext to avoid
// memory leak.
assert(false);
_mgr->PushIoContext(pIOContext);
}
OutstandingCallCompleted();
// Don't touch any members after OutstandingCallCompleted() since the socket
// may be deleted at this point.
}
int32_t UdpSocket2Windows::PostRecv()
{
PerIoContext* pIoContext=_mgr->PopIoContext();
if(pIoContext == 0)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"UdpSocket2Windows(%d)::PostRecv(), pIoContext == 0",
(int32_t)this);
return -1;
}
// This function may have been called by thread not controlled by the socket
// implementation.
pIoContext->ioInitiatedByPlatformThread = false;
return PostRecv(pIoContext);
}
int32_t UdpSocket2Windows::PostRecv(PerIoContext* pIoContext)
{
if(pIoContext==0)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"UdpSocket2Windows(%d)::PostRecv(?), pIoContext==0",
(int32_t)this);
return -1;
}
DWORD numOfRecivedBytes = 0;
DWORD flags = 0;
pIoContext->wsabuf.buf = pIoContext->buffer;
pIoContext->wsabuf.len = sizeof(pIoContext->buffer);
pIoContext->fromLen = sizeof(SocketAddress);
pIoContext->ioOperation = OP_READ;
int32_t rxError = 0;
int32_t nRet = 0;
int32_t postingSucessfull = false;
if(!AquireSocket())
{
_mgr->PushIoContext(pIoContext);
return -1;
}
// Assume that the WSARecvFrom() call will be successfull to make sure that
// _outstandingCalls is positive. Roll back if WSARecvFrom() failed.
if(!NewOutstandingCall())
{
_mgr->PushIoContext(pIoContext);
ReleaseSocket();
return -1;
}
for(int32_t tries = 0; tries < 10; tries++)
{
nRet = WSARecvFrom(
_socket,
&(pIoContext->wsabuf),
1,
&numOfRecivedBytes,
&flags,
reinterpret_cast<struct sockaddr*>(&(pIoContext->from)),
&(pIoContext->fromLen),
&(pIoContext->overlapped),
0);
if( nRet == SOCKET_ERROR)
{
rxError = WSAGetLastError();
if(rxError != ERROR_IO_PENDING)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"UdpSocket2Windows(%d)::PostRecv(?), WSAerror:%d when\
posting new recieve,trie:%d",
(int32_t)this,
rxError,
tries);
// Tell the OS that this is a good place to context switch if
// it wants to.
SleepMs(0);
}
}
if((rxError == ERROR_IO_PENDING) || (nRet == 0))
{
postingSucessfull = true;
break;
}
}
ReleaseSocket();
if(postingSucessfull)
{
return 0;
}
int32_t remainingReceiveBuffers = --_receiveBuffers;
if(remainingReceiveBuffers < 0)
{
assert(false);
}
int32_t error = _mgr->PushIoContext(pIoContext);
if(error)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"UdpSocket2Windows(%d)::PostRecv(?), error:%d when PushIoContext",
(int32_t)this,
error);
}
// Roll back.
OutstandingCallCompleted();
return -1;
}
void UdpSocket2Windows::CloseBlocking()
{
LINGER lingerStruct;
lingerStruct.l_onoff = 1;
lingerStruct.l_linger = 0;
if(AquireSocket())
{
setsockopt(_socket, SOL_SOCKET, SO_LINGER,
reinterpret_cast<const char*>(&lingerStruct),
sizeof(lingerStruct));
ReleaseSocket();
}
_wantsIncoming = false;
// Reclaims the socket and prevents it from being used again.
InvalidateSocket();
DisableNewOutstandingCalls();
delete this;
}
bool UdpSocket2Windows::SetQos(int32_t serviceType,
int32_t tokenRate,
int32_t bucketSize,
int32_t peekBandwith,
int32_t minPolicedSize,
int32_t maxSduSize,
const SocketAddress &stRemName,
int32_t overrideDSCP)
{
if(_qos == false)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"UdpSocket2Windows::SetQos(), socket not capable of QOS");
return false;
}
if(overrideDSCP != 0)
{
FLOWSPEC f;
int32_t err = CreateFlowSpec(serviceType, tokenRate, bucketSize,
peekBandwith, minPolicedSize,
maxSduSize, &f);
if(err == -1)
{
return false;
}
SocketAddress socketName;
struct sockaddr_in* name =
reinterpret_cast<struct sockaddr_in*>(&socketName);
int nameLength = sizeof(SocketAddress);
if(AquireSocket())
{
getsockname(_socket, (struct sockaddr*)name, &nameLength);
ReleaseSocket();
}
if(serviceType == 0)
{
// Disable TOS byte setting.
return SetTrafficControl(0, -1, name, &f, &f) == 0;
}
return SetTrafficControl(overrideDSCP, -1, name, &f, &f) == 0;
}
QOS Qos;
DWORD BytesRet;
QOS_DESTADDR QosDestaddr;
memset (&Qos, QOS_NOT_SPECIFIED, sizeof(QOS));
Qos.SendingFlowspec.ServiceType = serviceType;
Qos.SendingFlowspec.TokenRate = tokenRate;
Qos.SendingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED;
Qos.SendingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED;
Qos.SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED;
Qos.SendingFlowspec.Latency = QOS_NOT_SPECIFIED;
Qos.SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
Qos.SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED;
// Only ServiceType is needed for receiving.
Qos.ReceivingFlowspec.ServiceType = serviceType;
Qos.ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED;
Qos.ReceivingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED;
Qos.ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED;
Qos.ReceivingFlowspec.Latency = QOS_NOT_SPECIFIED;
Qos.ReceivingFlowspec.DelayVariation = QOS_NOT_SPECIFIED;
Qos.ReceivingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
Qos.ReceivingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED;
Qos.ProviderSpecific.len = 0;
Qos.ProviderSpecific.buf = NULL;
ZeroMemory((int8_t *)&QosDestaddr, sizeof(QosDestaddr));
OSVERSIONINFOEX osvie;
osvie.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
GetVersionEx((LPOSVERSIONINFO)&osvie);
// Operating system Version number dwMajorVersion dwMinorVersion
// Windows 7 6.1 6 1
// Windows Server 2008 R2 6.1 6 1
// Windows Server 2008 6.0 6 0
// Windows Vista 6.0 6 0
// Windows Server 2003 R2 5.2 5 2
// Windows Server 2003 5.2 5 2
// Windows XP 5.1 5 1
// Windows 2000 5.0 5 0
// SERVICE_NO_QOS_SIGNALING and QOS_DESTADDR should not be used if version
// is 6.0 or greater.
if(osvie.dwMajorVersion >= 6)
{
Qos.SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
Qos.ReceivingFlowspec.ServiceType = serviceType;
} else {
Qos.SendingFlowspec.MinimumPolicedSize =
QOS_NOT_SPECIFIED | SERVICE_NO_QOS_SIGNALING;
Qos.ReceivingFlowspec.ServiceType =
serviceType | SERVICE_NO_QOS_SIGNALING;
QosDestaddr.ObjectHdr.ObjectType = QOS_OBJECT_DESTADDR;
QosDestaddr.ObjectHdr.ObjectLength = sizeof(QosDestaddr);
QosDestaddr.SocketAddress = (SOCKADDR *)&stRemName;
if (AF_INET6 == _iProtocol)
{
QosDestaddr.SocketAddressLength = sizeof(SocketAddressInVersion6);
} else {
QosDestaddr.SocketAddressLength = sizeof(SocketAddressIn);
}
Qos.ProviderSpecific.len = QosDestaddr.ObjectHdr.ObjectLength;
Qos.ProviderSpecific.buf = (char*)&QosDestaddr;
}
if(!AquireSocket()) {
return false;
}
// To set QoS with SIO_SET_QOS the socket must be locally bound first
// or the call will fail with error code 10022.
int32_t result = WSAIoctl(GetFd(), SIO_SET_QOS, &Qos, sizeof(QOS),
NULL, 0, &BytesRet, NULL,NULL);
ReleaseSocket();
if (result == SOCKET_ERROR)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"UdpSocket2Windows::SetQos() WSAerror : %d",
WSAGetLastError());
return false;
}
return true;
}
int32_t UdpSocket2Windows::SetTOS(int32_t serviceType)
{
SocketAddress socketName;
struct sockaddr_in* name =
reinterpret_cast<struct sockaddr_in*>(&socketName);
int nameLength = sizeof(SocketAddress);
if(AquireSocket())
{
getsockname(_socket, (struct sockaddr*)name, &nameLength);
ReleaseSocket();
}
int32_t res = SetTrafficControl(serviceType, -1, name);
if (res == -1)
{
OSVERSIONINFO OsVersion;
OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx (&OsVersion);
if ((OsVersion.dwMajorVersion == 4)) // NT 4.0
{
if(SetSockopt(IPPROTO_IP,IP_TOS ,
(int8_t*)&serviceType, 4) != 0)
{
return -1;
}
}
}
return res;
}
int32_t UdpSocket2Windows::SetPCP(int32_t pcp)
{
SocketAddress socketName;
struct sockaddr_in* name =
reinterpret_cast<struct sockaddr_in*>(&socketName);
int nameLength = sizeof(SocketAddress);
if(AquireSocket())
{
getsockname(_socket, (struct sockaddr*)name, &nameLength);
ReleaseSocket();
}
return SetTrafficControl(-1, pcp, name);
}
int32_t UdpSocket2Windows::SetTrafficControl(
int32_t dscp,
int32_t pcp,
const struct sockaddr_in* name,
FLOWSPEC* send, FLOWSPEC* recv)
{
if (pcp == _pcp)
{
// No change.
pcp = -1;
}
if ((-1 == pcp) && (-1 == dscp))
{
return 0;
}
if (!_gtc)
{
_gtc = TrafficControlWindows::GetInstance(_id);
}
if (!_gtc)
{
return -1;
}
if(_filterHandle)
{
_gtc->TcDeleteFilter(_filterHandle);
_filterHandle = NULL;
}
if(_flowHandle)
{
_gtc->TcDeleteFlow(_flowHandle);
_flowHandle = NULL;
}
if(_clientHandle)
{
_gtc->TcDeregisterClient(_clientHandle);
_clientHandle = NULL;
}
if ((0 == dscp) && (-2 == _pcp) && (-1 == pcp))
{
// TODO (pwestin): why is this not done before deleting old filter and
// flow? This scenario should probably be documented in
// the function declaration.
return 0;
}
TCI_CLIENT_FUNC_LIST QoSFunctions;
QoSFunctions.ClAddFlowCompleteHandler = NULL;
QoSFunctions.ClDeleteFlowCompleteHandler = NULL;
QoSFunctions.ClModifyFlowCompleteHandler = NULL;
QoSFunctions.ClNotifyHandler = (TCI_NOTIFY_HANDLER)MyClNotifyHandler;
// Register the client with Traffic control interface.
HANDLE ClientHandle;
ULONG result = _gtc->TcRegisterClient(CURRENT_TCI_VERSION, NULL,
&QoSFunctions,&ClientHandle);
if(result != NO_ERROR)
{
// This is likely caused by the application not being run as
// administrator.
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"TcRegisterClient returned %d", result);
return result;
}
// Find traffic control-enabled network interfaces that matches this
// socket's IP address.
ULONG BufferSize = 0;
result = _gtc->TcEnumerateInterfaces(ClientHandle, &BufferSize, NULL);
if(result != NO_ERROR && result != ERROR_INSUFFICIENT_BUFFER)
{
_gtc->TcDeregisterClient(ClientHandle);
return result;
}
if(result != ERROR_INSUFFICIENT_BUFFER)
{
// Empty buffer contains all control-enabled network interfaces. I.e.
// QoS is not enabled.
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"QOS faild since QOS is not installed on the interface");
_gtc->TcDeregisterClient(ClientHandle);
return -1;
}
PTC_IFC_DESCRIPTOR pInterfaceBuffer =
(PTC_IFC_DESCRIPTOR)malloc(BufferSize);
if(pInterfaceBuffer == NULL)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Out ot memory failure");
_gtc->TcDeregisterClient(ClientHandle);
return ERROR_NOT_ENOUGH_MEMORY;
}
result = _gtc->TcEnumerateInterfaces(ClientHandle, &BufferSize,
pInterfaceBuffer);
if(result != NO_ERROR)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"Critical: error enumerating interfaces when passing in correct\
buffer size: %d", result);
_gtc->TcDeregisterClient(ClientHandle);
free(pInterfaceBuffer);
return result;
}
PTC_IFC_DESCRIPTOR oneinterface;
HANDLE ifcHandle, iFilterHandle, iflowHandle;
bool addrFound = false;
ULONG filterSourceAddress = ULONG_MAX;
// Find the interface corresponding to the local address.
for(oneinterface = pInterfaceBuffer;
oneinterface != (PTC_IFC_DESCRIPTOR)
(((int8_t*)pInterfaceBuffer) + BufferSize);
oneinterface = (PTC_IFC_DESCRIPTOR)
((int8_t *)oneinterface + oneinterface->Length))
{
char interfaceName[500];
WideCharToMultiByte(CP_ACP, 0, oneinterface->pInterfaceName, -1,
interfaceName, sizeof(interfaceName), 0, 0 );
PNETWORK_ADDRESS_LIST addresses =
&(oneinterface->AddressListDesc.AddressList);
for(LONG i = 0; i < addresses->AddressCount ; i++)
{
// Only look at TCP/IP addresses.
if(addresses->Address[i].AddressType != NDIS_PROTOCOL_ID_TCP_IP)
{
continue;
}
NETWORK_ADDRESS_IP* pIpAddr =
(NETWORK_ADDRESS_IP*)&(addresses->Address[i].Address);
struct in_addr in;
in.S_un.S_addr = pIpAddr->in_addr;
if(pIpAddr->in_addr == name->sin_addr.S_un.S_addr)
{
filterSourceAddress = pIpAddr->in_addr;
addrFound = true;
}
}
if(!addrFound)
{
continue;
} else
{
break;
}
}
if(!addrFound)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"QOS faild since address is not found");
_gtc->TcDeregisterClient(ClientHandle);
free(pInterfaceBuffer);
return -1;
}
result = _gtc->TcOpenInterfaceW(oneinterface->pInterfaceName, ClientHandle,
NULL, &ifcHandle);
if(result != NO_ERROR)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Error opening interface: %d", result);
_gtc->TcDeregisterClient(ClientHandle);
free(pInterfaceBuffer);
return result;
}
// Create flow if one doesn't exist.
if (!_flow)
{
bool addPCP = ((pcp >= 0) || ((-1 == pcp) && (_pcp >= 0)));
int allocSize = sizeof(TC_GEN_FLOW) + sizeof(QOS_DS_CLASS) +
(addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0);
_flow = (PTC_GEN_FLOW)malloc(allocSize);
_flow->SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED;
_flow->SendingFlowspec.Latency = QOS_NOT_SPECIFIED;
_flow->SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED;
_flow->SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
_flow->SendingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED;
_flow->SendingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT;
_flow->SendingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED;
_flow->SendingFlowspec.TokenRate = QOS_NOT_SPECIFIED;
_flow->ReceivingFlowspec.DelayVariation = QOS_NOT_SPECIFIED;
_flow->ReceivingFlowspec.Latency = QOS_NOT_SPECIFIED;
_flow->ReceivingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED;
_flow->ReceivingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
_flow->ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED;
_flow->ReceivingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT;
_flow->ReceivingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED;
_flow->ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED;
QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects;
dsClass->DSField = 0;
dsClass->ObjectHdr.ObjectType = QOS_OBJECT_DS_CLASS;
dsClass->ObjectHdr.ObjectLength = sizeof(QOS_DS_CLASS);
if (addPCP)
{
QOS_TRAFFIC_CLASS* trafficClass = (QOS_TRAFFIC_CLASS*)(dsClass + 1);
trafficClass->TrafficClass = 0;
trafficClass->ObjectHdr.ObjectType = QOS_OBJECT_TRAFFIC_CLASS;
trafficClass->ObjectHdr.ObjectLength = sizeof(QOS_TRAFFIC_CLASS);
}
_flow->TcObjectsLength = sizeof(QOS_DS_CLASS) +
(addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0);
} else if (-1 != pcp) {
// Reallocate memory since pcp has changed.
PTC_GEN_FLOW oldFlow = _flow;
bool addPCP = (pcp >= 0);
int allocSize = sizeof(TC_GEN_FLOW) + sizeof(QOS_DS_CLASS) +
(addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0);
_flow = (PTC_GEN_FLOW)malloc(allocSize);
// Copy old flow.
_flow->ReceivingFlowspec = oldFlow->ReceivingFlowspec;
_flow->SendingFlowspec = oldFlow->SendingFlowspec;
// The DS info is always the first object.
QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects;
QOS_DS_CLASS* oldDsClass = (QOS_DS_CLASS*)oldFlow->TcObjects;
dsClass->DSField = oldDsClass->DSField;
dsClass->ObjectHdr.ObjectType = oldDsClass->ObjectHdr.ObjectType;
dsClass->ObjectHdr.ObjectLength = oldDsClass->ObjectHdr.ObjectLength;
if (addPCP)
{
QOS_TRAFFIC_CLASS* trafficClass = (QOS_TRAFFIC_CLASS*)(dsClass + 1);
trafficClass->TrafficClass = 0;
trafficClass->ObjectHdr.ObjectType = QOS_OBJECT_TRAFFIC_CLASS;
trafficClass->ObjectHdr.ObjectLength = sizeof(QOS_TRAFFIC_CLASS);
}
_flow->TcObjectsLength = sizeof(QOS_DS_CLASS) +
(addPCP ? sizeof(QOS_TRAFFIC_CLASS) : 0);
free(oldFlow);
}
// Setup send and receive flow and DS object.
if (dscp >= 0)
{
if (!send || (0 == dscp))
{
_flow->SendingFlowspec.DelayVariation = QOS_NOT_SPECIFIED;
_flow->SendingFlowspec.Latency = QOS_NOT_SPECIFIED;
_flow->SendingFlowspec.MaxSduSize = QOS_NOT_SPECIFIED;
_flow->SendingFlowspec.MinimumPolicedSize = QOS_NOT_SPECIFIED;
_flow->SendingFlowspec.PeakBandwidth =
(0 == dscp ? QOS_NOT_SPECIFIED : POSITIVE_INFINITY_RATE);
_flow->SendingFlowspec.ServiceType = SERVICETYPE_BESTEFFORT;
_flow->SendingFlowspec.TokenBucketSize = QOS_NOT_SPECIFIED;
// 128000 * 10 is 10mbit/s.
_flow->SendingFlowspec.TokenRate =
(0 == dscp ? QOS_NOT_SPECIFIED : 128000 * 10);
}
else
{
_flow->SendingFlowspec.DelayVariation = send->DelayVariation;
_flow->SendingFlowspec.Latency = send->Latency;
_flow->SendingFlowspec.MaxSduSize = send->MaxSduSize;
_flow->SendingFlowspec.MinimumPolicedSize =
send->MinimumPolicedSize;
_flow->SendingFlowspec.PeakBandwidth = send->PeakBandwidth;
_flow->SendingFlowspec.PeakBandwidth = POSITIVE_INFINITY_RATE;
_flow->SendingFlowspec.ServiceType = send->ServiceType;
_flow->SendingFlowspec.TokenBucketSize = send->TokenBucketSize;
_flow->SendingFlowspec.TokenRate = send->TokenRate;
}
if (!recv || (0 == dscp))
{
_flow->ReceivingFlowspec.DelayVariation =
_flow->SendingFlowspec.DelayVariation;
_flow->ReceivingFlowspec.Latency = _flow->SendingFlowspec.Latency;
_flow->ReceivingFlowspec.MaxSduSize =
_flow->SendingFlowspec.MaxSduSize;
_flow->ReceivingFlowspec.MinimumPolicedSize =
_flow->SendingFlowspec.MinimumPolicedSize;
_flow->ReceivingFlowspec.PeakBandwidth = QOS_NOT_SPECIFIED;
_flow->ReceivingFlowspec.ServiceType =
0 == dscp ? SERVICETYPE_BESTEFFORT : SERVICETYPE_CONTROLLEDLOAD;
_flow->ReceivingFlowspec.TokenBucketSize =
_flow->SendingFlowspec.TokenBucketSize;
_flow->ReceivingFlowspec.TokenRate =
_flow->SendingFlowspec.TokenRate;
} else {
_flow->ReceivingFlowspec.DelayVariation = recv->DelayVariation;
_flow->ReceivingFlowspec.Latency = recv->Latency;
_flow->ReceivingFlowspec.MaxSduSize = recv->MaxSduSize;
_flow->ReceivingFlowspec.MinimumPolicedSize =
recv->MinimumPolicedSize;
_flow->ReceivingFlowspec.PeakBandwidth = recv->PeakBandwidth;
_flow->ReceivingFlowspec.ServiceType = recv->ServiceType;
_flow->ReceivingFlowspec.TokenBucketSize = recv->TokenBucketSize;
_flow->ReceivingFlowspec.TokenRate = QOS_NOT_SPECIFIED;
}
// Setup DS (for DSCP value).
// DS is always the first object.
QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects;
dsClass->DSField = dscp;
}
// Setup PCP (802.1p priority in 802.1Q/VLAN tagging)
if (pcp >= 0)
{
// DS is always first object.
QOS_DS_CLASS* dsClass = (QOS_DS_CLASS*)_flow->TcObjects;
QOS_TRAFFIC_CLASS* trafficClass = (QOS_TRAFFIC_CLASS*)(dsClass + 1);
trafficClass->TrafficClass = pcp;
}
result = _gtc->TcAddFlow(ifcHandle, NULL, 0, _flow, &iflowHandle);
if(result != NO_ERROR)
{
_gtc->TcCloseInterface(ifcHandle);
_gtc->TcDeregisterClient(ClientHandle);
free(pInterfaceBuffer);
return -1;
}
IP_PATTERN filterPattern, mask;
ZeroMemory((int8_t*)&filterPattern, sizeof(IP_PATTERN));
ZeroMemory((int8_t*)&mask, sizeof(IP_PATTERN));
filterPattern.ProtocolId = IPPROTO_UDP;
// "name" fields already in network order.
filterPattern.S_un.S_un_ports.s_srcport = name->sin_port;
filterPattern.SrcAddr = filterSourceAddress;
// Unsigned max of a type corresponds to a bitmask with all bits set to 1.
// I.e. the filter should allow all ProtocolIds, any source port and any
// IP address
mask.ProtocolId = UCHAR_MAX;
mask.S_un.S_un_ports.s_srcport = USHRT_MAX;
mask.SrcAddr = ULONG_MAX;
TC_GEN_FILTER filter;
filter.AddressType = NDIS_PROTOCOL_ID_TCP_IP;
filter.Mask = (LPVOID)&mask;
filter.Pattern = (LPVOID)&filterPattern;
filter.PatternSize = sizeof(IP_PATTERN);
result = _gtc->TcAddFilter(iflowHandle, &filter, &iFilterHandle);
if(result != NO_ERROR)
{
_gtc->TcDeleteFlow(iflowHandle);
_gtc->TcCloseInterface(ifcHandle);
_gtc->TcDeregisterClient(ClientHandle);
free(pInterfaceBuffer);
return result;
}
_flowHandle = iflowHandle;
_filterHandle = iFilterHandle;
_clientHandle = ClientHandle;
if (-1 != pcp)
{
_pcp = pcp;
}
_gtc->TcCloseInterface(ifcHandle);
free(pInterfaceBuffer);
return 0;
}
int32_t UdpSocket2Windows::CreateFlowSpec(int32_t serviceType,
int32_t tokenRate,
int32_t bucketSize,
int32_t peekBandwith,
int32_t minPolicedSize,
int32_t maxSduSize,
FLOWSPEC* f)
{
if (!f)
{
return -1;
}
f->ServiceType = serviceType;
f->TokenRate = tokenRate;
f->TokenBucketSize = QOS_NOT_SPECIFIED;
f->PeakBandwidth = QOS_NOT_SPECIFIED;
f->DelayVariation = QOS_NOT_SPECIFIED;
f->Latency = QOS_NOT_SPECIFIED;
f->MaxSduSize = QOS_NOT_SPECIFIED;
f->MinimumPolicedSize = QOS_NOT_SPECIFIED;
return 0;
}
bool UdpSocket2Windows::NewOutstandingCall()
{
assert(!_outstandingCallsDisabled);
++_outstandingCalls;
return true;
}
void UdpSocket2Windows::OutstandingCallCompleted()
{
_ptrDestRWLock->AcquireLockShared();
++_outstandingCallComplete;
if((--_outstandingCalls == 0) && _outstandingCallsDisabled)
{
// When there are no outstanding calls and new outstanding calls are
// disabled it is time to terminate.
_terminate = true;
}
_ptrDestRWLock->ReleaseLockShared();
if((--_outstandingCallComplete == 0) &&
(_terminate))
{
// Only one thread will enter here. The thread with the last outstanding
// call.
delete_event_.Set();
}
}
void UdpSocket2Windows::DisableNewOutstandingCalls()
{
_ptrDestRWLock->AcquireLockExclusive();
if(_outstandingCallsDisabled)
{
// Outstandning calls are already disabled.
_ptrDestRWLock->ReleaseLockExclusive();
return;
}
_outstandingCallsDisabled = true;
const bool noOutstandingCalls = (_outstandingCalls.Value() == 0);
_ptrDestRWLock->ReleaseLockExclusive();
RemoveSocketFromManager();
if(noOutstandingCalls)
{
delete_event_.Set();
}
}
void UdpSocket2Windows::RemoveSocketFromManager()
{
// New outstanding calls should be disabled at this point.
assert(_outstandingCallsDisabled);
if(_addedToMgr)
{
WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
"calling UdpSocketManager::RemoveSocket()");
if(_mgr->RemoveSocket(this))
{
_addedToMgr=false;
}
}
}
bool UdpSocket2Windows::AquireSocket()
{
_ptrSocketRWLock->AcquireLockShared();
const bool returnValue = _socket != INVALID_SOCKET;
if(!returnValue)
{
_ptrSocketRWLock->ReleaseLockShared();
}
return returnValue;
}
void UdpSocket2Windows::ReleaseSocket()
{
_ptrSocketRWLock->ReleaseLockShared();
}
bool UdpSocket2Windows::InvalidateSocket()
{
_ptrSocketRWLock->AcquireLockExclusive();
if(_socket == INVALID_SOCKET)
{
_ptrSocketRWLock->ReleaseLockExclusive();
return true;
}
// Give the socket back to the system. All socket calls will fail from now
// on.
if(closesocket(_socket) == SOCKET_ERROR)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"UdpSocket2Windows(%d)::InvalidateSocket() WSAerror: %d",
(int32_t)this, WSAGetLastError());
}
_socket = INVALID_SOCKET;
_ptrSocketRWLock->ReleaseLockExclusive();
return true;
}
} // namespace test
} // namespace webrtc