/* * 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 #include #include #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), _safeTodelete(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(mgr); _obj = NULL; _incomingCb = NULL; _socket = INVALID_SOCKET; _pCrit = CriticalSectionWrapper::CreateCriticalSection(); _ptrCbRWLock = RWLockWrapper::CreateRWLock(); _ptrDestRWLock = RWLockWrapper::CreateRWLock(); _ptrSocketRWLock = RWLockWrapper::CreateRWLock(); _ptrDeleteCrit = CriticalSectionWrapper::CreateCriticalSection(); _ptrDeleteCond = ConditionVariableWrapper::CreateConditionVariable(); // 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; iTcDeleteFilter(_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(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(&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(len); pIoContext->fromLen=sizeof(SocketAddress); pIoContext->ioOperation = OP_WRITE; pIoContext->nTotalBytes = len; pIoContext->nSentBytes=0; DWORD numOfbytesSent = 0; const struct sockaddr* addr = reinterpret_cast(&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(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( 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(&(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(&lingerStruct), sizeof(lingerStruct)); ReleaseSocket(); } _wantsIncoming = false; // Reclaims the socket and prevents it from being used again. InvalidateSocket(); DisableNewOutstandingCalls(); WaitForOutstandingCalls(); 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(&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(&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(&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. CriticalSectionScoped cs(_ptrDeleteCrit); _safeTodelete = true; _ptrDeleteCond->Wake(); } } 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) { CriticalSectionScoped cs(_ptrDeleteCrit); _safeTodelete = true; _ptrDeleteCond->Wake(); } } void UdpSocket2Windows::WaitForOutstandingCalls() { CriticalSectionScoped cs(_ptrDeleteCrit); while(!_safeTodelete) { _ptrDeleteCond->SleepCS(*_ptrDeleteCrit); } } 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