blob: 897c8085226261bd0d9e61f15acc12568a8b4a1a [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_transport_impl.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#if defined(_WIN32)
#include <winsock2.h>
#include <ws2tcpip.h>
#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
#include <arpa/inet.h>
#include <ctype.h>
#include <fcntl.h>
#include <net/if.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#ifndef WEBRTC_IOS
#include <net/if_arp.h>
#endif
#endif // defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
#if defined(WEBRTC_MAC)
#include <ifaddrs.h>
#include <machine/types.h>
#endif
#if defined(WEBRTC_LINUX)
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#endif
#include "webrtc/common_types.h"
#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
#include "webrtc/system_wrappers/include/rw_lock_wrapper.h"
#include "webrtc/system_wrappers/include/trace.h"
#include "webrtc/test/channel_transport/udp_socket_manager_wrapper.h"
#include "webrtc/typedefs.h"
#if defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
#define GetLastError() errno
#define IFRSIZE ((int)(size * sizeof (struct ifreq)))
#define NLMSG_OK_NO_WARNING(nlh,len) \
((len) >= (int)sizeof(struct nlmsghdr) && \
(int)(nlh)->nlmsg_len >= (int)sizeof(struct nlmsghdr) && \
(int)(nlh)->nlmsg_len <= (len))
#endif // defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
namespace webrtc {
namespace test {
class SocketFactory : public UdpTransportImpl::SocketFactoryInterface {
public:
UdpSocketWrapper* CreateSocket(const int32_t id,
UdpSocketManager* mgr,
CallbackObj obj,
IncomingSocketCallback cb,
bool ipV6Enable,
bool disableGQOS) override {
return UdpSocketWrapper::CreateSocket(id, mgr, obj, cb, ipV6Enable,
disableGQOS);
}
};
// Creates an UdpTransport using the definition of SocketFactory above,
// and passes (creating if needed) a pointer to the static singleton
// UdpSocketManager.
UdpTransport* UdpTransport::Create(const int32_t id,
uint8_t& numSocketThreads)
{
return new UdpTransportImpl(id,
new SocketFactory(),
UdpSocketManager::Create(id, numSocketThreads));
}
// Deletes the UdpTransport and decrements the refcount of the
// static singleton UdpSocketManager, possibly destroying it.
// Should only be used on UdpTransports that are created using Create.
void UdpTransport::Destroy(UdpTransport* module)
{
if(module)
{
delete module;
UdpSocketManager::Return();
}
}
UdpTransportImpl::UdpTransportImpl(const int32_t id,
SocketFactoryInterface* maker,
UdpSocketManager* socket_manager)
: _id(id),
_socket_creator(maker),
_crit(CriticalSectionWrapper::CreateCriticalSection()),
_critFilter(CriticalSectionWrapper::CreateCriticalSection()),
_critPacketCallback(CriticalSectionWrapper::CreateCriticalSection()),
_mgr(socket_manager),
_lastError(kNoSocketError),
_destPort(0),
_destPortRTCP(0),
_localPort(0),
_localPortRTCP(0),
_srcPort(0),
_srcPortRTCP(0),
_fromPort(0),
_fromPortRTCP(0),
_fromIP(),
_destIP(),
_localIP(),
_localMulticastIP(),
_ptrRtpSocket(NULL),
_ptrRtcpSocket(NULL),
_ptrSendRtpSocket(NULL),
_ptrSendRtcpSocket(NULL),
_remoteRTPAddr(),
_remoteRTCPAddr(),
_localRTPAddr(),
_localRTCPAddr(),
_tos(0),
_receiving(false),
_useSetSockOpt(false),
_qos(false),
_pcp(0),
_ipV6Enabled(false),
_serviceType(0),
_overrideDSCP(0),
_maxBitrate(0),
_cachLock(RWLockWrapper::CreateRWLock()),
_previousAddress(),
_previousIP(),
_previousIPSize(0),
_previousSourcePort(0),
_filterIPAddress(),
_rtpFilterPort(0),
_rtcpFilterPort(0),
_packetCallback(0)
{
memset(&_remoteRTPAddr, 0, sizeof(_remoteRTPAddr));
memset(&_remoteRTCPAddr, 0, sizeof(_remoteRTCPAddr));
memset(&_localRTPAddr, 0, sizeof(_localRTPAddr));
memset(&_localRTCPAddr, 0, sizeof(_localRTCPAddr));
memset(_fromIP, 0, sizeof(_fromIP));
memset(_destIP, 0, sizeof(_destIP));
memset(_localIP, 0, sizeof(_localIP));
memset(_localMulticastIP, 0, sizeof(_localMulticastIP));
memset(&_filterIPAddress, 0, sizeof(_filterIPAddress));
WEBRTC_TRACE(kTraceMemory, kTraceTransport, id, "%s created", __FUNCTION__);
}
UdpTransportImpl::~UdpTransportImpl()
{
CloseSendSockets();
CloseReceiveSockets();
delete _crit;
delete _critFilter;
delete _critPacketCallback;
delete _cachLock;
delete _socket_creator;
WEBRTC_TRACE(kTraceMemory, kTraceTransport, _id, "%s deleted",
__FUNCTION__);
}
UdpTransport::ErrorCode UdpTransportImpl::LastError() const
{
return _lastError;
}
bool SameAddress(const SocketAddress& address1, const SocketAddress& address2)
{
return (memcmp(&address1,&address2,sizeof(address1)) == 0);
}
void UdpTransportImpl::GetCachedAddress(char* ip,
uint32_t& ipSize,
uint16_t& sourcePort)
{
const uint32_t originalIPSize = ipSize;
// If the incoming string is too small, fill it as much as there is room
// for. Make sure that there is room for the '\0' character.
ipSize = (ipSize - 1 < _previousIPSize) ? ipSize - 1 : _previousIPSize;
memcpy(ip,_previousIP,sizeof(int8_t)*(ipSize + 1));
ip[originalIPSize - 1] = '\0';
sourcePort = _previousSourcePort;
}
int32_t UdpTransportImpl::IPAddressCached(const SocketAddress& address,
char* ip,
uint32_t& ipSize,
uint16_t& sourcePort)
{
{
ReadLockScoped rl(*_cachLock);
// Check if the old address can be re-used (is the same).
if(SameAddress(address,_previousAddress))
{
GetCachedAddress(ip,ipSize,sourcePort);
return 0;
}
}
// Get the new address and store it.
WriteLockScoped wl(*_cachLock);
ipSize = kIpAddressVersion6Length;
if(IPAddress(address,_previousIP,ipSize,_previousSourcePort) != 0)
{
return -1;
}
_previousIPSize = ipSize;
memcpy(&_previousAddress, &address, sizeof(address));
// Address has been cached at this point.
GetCachedAddress(ip,ipSize,sourcePort);
return 0;
}
int32_t UdpTransportImpl::InitializeReceiveSockets(
UdpTransportData* const packetCallback,
const uint16_t portnr,
const char* ip,
const char* multicastIpAddr,
const uint16_t rtcpPort)
{
{
CriticalSectionScoped cs(_critPacketCallback);
_packetCallback = packetCallback;
if(packetCallback == NULL)
{
WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id,
"Closing down receive sockets");
return 0;
}
}
CriticalSectionScoped cs(_crit);
CloseReceiveSockets();
if(portnr == 0)
{
// TODO (hellner): why not just fail here?
if(_destPort == 0)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"InitializeReceiveSockets port 0 not allowed");
_lastError = kPortInvalid;
return -1;
}
_localPort = _destPort;
} else {
_localPort = portnr;
}
if(rtcpPort)
{
_localPortRTCP = rtcpPort;
}else {
_localPortRTCP = _localPort + 1;
WEBRTC_TRACE(
kTraceStateInfo,
kTraceTransport,
_id,
"InitializeReceiveSockets RTCP port not configured using RTP\
port+1=%d",
_localPortRTCP);
}
if(ip)
{
if(IsIpAddressValid(ip,IpV6Enabled()))
{
strncpy(_localIP, ip,kIpAddressVersion6Length);
} else
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"InitializeReceiveSockets invalid IP address");
_lastError = kIpAddressInvalid;
return -1;
}
}else
{
// Don't bind to a specific IP address.
if(! IpV6Enabled())
{
strncpy(_localIP, "0.0.0.0",16);
} else
{
strncpy(_localIP, "0000:0000:0000:0000:0000:0000:0000:0000",
kIpAddressVersion6Length);
}
}
if(multicastIpAddr && !IpV6Enabled())
{
if(IsIpAddressValid(multicastIpAddr,IpV6Enabled()))
{
strncpy(_localMulticastIP, multicastIpAddr,
kIpAddressVersion6Length);
} else
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"InitializeReceiveSockets invalid IP address");
_lastError = kIpAddressInvalid;
return -1;
}
}
if(_mgr == NULL)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"InitializeReceiveSockets no socket manager");
return -1;
}
_useSetSockOpt=false;
_tos=0;
_pcp=0;
_ptrRtpSocket = _socket_creator->CreateSocket(_id, _mgr, this,
IncomingRTPCallback,
IpV6Enabled(), false);
_ptrRtcpSocket = _socket_creator->CreateSocket(_id, _mgr, this,
IncomingRTCPCallback,
IpV6Enabled(), false);
ErrorCode retVal = BindLocalRTPSocket();
if(retVal != kNoSocketError)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"InitializeReceiveSockets faild to bind RTP socket");
_lastError = retVal;
CloseReceiveSockets();
return -1;
}
retVal = BindLocalRTCPSocket();
if(retVal != kNoSocketError)
{
_lastError = retVal;
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"InitializeReceiveSockets faild to bind RTCP socket");
CloseReceiveSockets();
return -1;
}
return 0;
}
int32_t UdpTransportImpl::ReceiveSocketInformation(
char ipAddr[kIpAddressVersion6Length],
uint16_t& rtpPort,
uint16_t& rtcpPort,
char multicastIpAddr[kIpAddressVersion6Length]) const
{
CriticalSectionScoped cs(_crit);
rtpPort = _localPort;
rtcpPort = _localPortRTCP;
if (ipAddr)
{
strncpy(ipAddr, _localIP, IpV6Enabled() ?
UdpTransport::kIpAddressVersion6Length :
UdpTransport::kIpAddressVersion4Length);
}
if (multicastIpAddr)
{
strncpy(multicastIpAddr, _localMulticastIP, IpV6Enabled() ?
UdpTransport::kIpAddressVersion6Length :
UdpTransport::kIpAddressVersion4Length);
}
return 0;
}
int32_t UdpTransportImpl::SendSocketInformation(
char ipAddr[kIpAddressVersion6Length],
uint16_t& rtpPort,
uint16_t& rtcpPort) const
{
CriticalSectionScoped cs(_crit);
rtpPort = _destPort;
rtcpPort = _destPortRTCP;
strncpy(ipAddr, _destIP, IpV6Enabled() ?
UdpTransport::kIpAddressVersion6Length :
UdpTransport::kIpAddressVersion4Length);
return 0;
}
int32_t UdpTransportImpl::RemoteSocketInformation(
char ipAddr[kIpAddressVersion6Length],
uint16_t& rtpPort,
uint16_t& rtcpPort) const
{
CriticalSectionScoped cs(_crit);
rtpPort = _fromPort;
rtcpPort = _fromPortRTCP;
if(ipAddr)
{
strncpy(ipAddr, _fromIP, IpV6Enabled() ?
kIpAddressVersion6Length :
kIpAddressVersion4Length);
}
return 0;
}
int32_t UdpTransportImpl::FilterPorts(
uint16_t& rtpFilterPort,
uint16_t& rtcpFilterPort) const
{
CriticalSectionScoped cs(_critFilter);
rtpFilterPort = _rtpFilterPort;
rtcpFilterPort = _rtcpFilterPort;
return 0;
}
int32_t UdpTransportImpl::SetQoS(bool QoS, int32_t serviceType,
uint32_t maxBitrate,
int32_t overrideDSCP, bool audio)
{
if(QoS)
{
return EnableQoS(serviceType, audio, maxBitrate, overrideDSCP);
}else
{
return DisableQoS();
}
}
int32_t UdpTransportImpl::EnableQoS(int32_t serviceType,
bool audio, uint32_t maxBitrate,
int32_t overrideDSCP)
{
if (_ipV6Enabled)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"QOS is enabled but will be ignored since IPv6 is enabled");
_lastError = kQosError;
return -1;
}
if (_tos)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"TOS already enabled, can't use TOS and QoS at the same time");
_lastError = kQosError;
return -1;
}
if (_pcp)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"PCP already enabled, can't use PCP and QoS at the same time");
_lastError = kQosError;
return -1;
}
if(_destPort == 0)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"QOS is enabled but not started since we have not yet configured\
the send destination");
return -1;
}
if(_qos)
{
if(_overrideDSCP == 0 && overrideDSCP != 0)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"QOS is already enabled and overrideDSCP differs, not allowed");
return -1;
}
}
CriticalSectionScoped cs(_crit);
UdpSocketWrapper* rtpSock = _ptrSendRtpSocket ?
_ptrSendRtpSocket :
_ptrRtpSocket;
if (!rtpSock || !rtpSock->ValidHandle())
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"QOS is enabled but not started since we have not yet created the\
RTP socket");
return -1;
}
UdpSocketWrapper* rtcpSock = _ptrSendRtcpSocket ?
_ptrSendRtcpSocket :
_ptrRtcpSocket;
if (!rtcpSock || !rtcpSock->ValidHandle())
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"QOS is enabled but not started since we have not yet created the\
RTCP socket");
return -1;
}
// Minimum packet size in bytes for which the requested quality of service
// will be provided. The smallest RTP header is 12 byte.
const int32_t min_policed_size = 12;
// Max SDU, maximum packet size permitted or used in the traffic flow, in
// bytes.
const int32_t max_sdu_size = 1500;
// Enable QoS for RTP sockets.
if(maxBitrate)
{
// Note: 1 kbit is 125 bytes.
// Token Rate is typically set to the average bit rate from peak to
// peak.
// Bucket size is normally set to the largest average frame size.
if(audio)
{
WEBRTC_TRACE(kTraceStateInfo,
kTraceTransport,
_id,
"Enable QOS for audio with max bitrate:%d",
maxBitrate);
const int32_t token_rate = maxBitrate*125;
// The largest audio packets are 60ms frames. This is a fraction
// more than 16 packets/second. These 16 frames are sent, at max,
// at a bitrate of maxBitrate*125 -> 1 frame is maxBitrate*125/16 ~
// maxBitrate * 8.
const int32_t bucket_size = maxBitrate * 8;
const int32_t peek_bandwith = maxBitrate * 125;
if (!rtpSock->SetQos(serviceType, token_rate, bucket_size,
peek_bandwith, min_policed_size,
max_sdu_size, _remoteRTPAddr, overrideDSCP))
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"QOS failed on the RTP socket");
_lastError = kQosError;
return -1;
}
}else
{
WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id,
"Enable QOS for video with max bitrate:%d",
maxBitrate);
// Allow for a token rate that is twice that of the maximum bitrate
// (in bytes).
const int32_t token_rate = maxBitrate*250;
// largest average frame size (key frame size). Assuming that a
// keyframe is 25% of the bitrate during the second its sent
// Assume that a key frame is 25% of the bitrate the second that it
// is sent. The largest frame size is then maxBitrate* 125 * 0.25 ~
// 31.
const int32_t bucket_size = maxBitrate*31;
const int32_t peek_bandwith = maxBitrate*125;
if (!rtpSock->SetQos(serviceType, token_rate, bucket_size,
peek_bandwith, min_policed_size, max_sdu_size,
_remoteRTPAddr, overrideDSCP))
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"QOS failed on the RTP socket");
_lastError = kQosError;
return -1;
}
}
} else if(audio)
{
// No max bitrate set. Audio.
WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id,
"Enable QOS for audio with default max bitrate");
// Let max bitrate be 240kbit/s.
const int32_t token_rate = 30000;
const int32_t bucket_size = 2000;
const int32_t peek_bandwith = 30000;
if (!rtpSock->SetQos(serviceType, token_rate, bucket_size,
peek_bandwith, min_policed_size, max_sdu_size,
_remoteRTPAddr, overrideDSCP))
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"QOS failed on the RTP socket");
_lastError = kQosError;
return -1;
}
}else
{
// No max bitrate set. Video.
WEBRTC_TRACE(kTraceStateInfo, kTraceTransport, _id,
"Enable QOS for video with default max bitrate");
// Let max bitrate be 10mbit/s.
const int32_t token_rate = 128000*10;
const int32_t bucket_size = 32000;
const int32_t peek_bandwith = 256000;
if (!rtpSock->SetQos(serviceType, token_rate, bucket_size,
peek_bandwith, min_policed_size, max_sdu_size,
_remoteRTPAddr, overrideDSCP))
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"QOS failed on the RTP socket");
_lastError = kQosError;
return -1;
}
}
// Enable QoS for RTCP sockets.
// TODO (hellner): shouldn't RTCP be based on 5% of the maximum bandwidth?
if(audio)
{
const int32_t token_rate = 200;
const int32_t bucket_size = 200;
const int32_t peek_bandwith = 400;
if (!rtcpSock->SetQos(serviceType, token_rate, bucket_size,
peek_bandwith, min_policed_size, max_sdu_size,
_remoteRTCPAddr, overrideDSCP))
{
WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
"QOS failed on the RTCP socket");
_lastError = kQosError;
}
}else
{
const int32_t token_rate = 5000;
const int32_t bucket_size = 100;
const int32_t peek_bandwith = 10000;
if (!rtcpSock->SetQos(serviceType, token_rate, bucket_size,
peek_bandwith, min_policed_size, max_sdu_size,
_remoteRTCPAddr, _overrideDSCP))
{
WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
"QOS failed on the RTCP socket");
_lastError = kQosError;
}
}
_qos = true;
_serviceType = serviceType;
_maxBitrate = maxBitrate;
_overrideDSCP = overrideDSCP;
return 0;
}
int32_t UdpTransportImpl::DisableQoS()
{
if(_qos == false)
{
return 0;
}
CriticalSectionScoped cs(_crit);
UdpSocketWrapper* rtpSock = (_ptrSendRtpSocket ?
_ptrSendRtpSocket : _ptrRtpSocket);
if (!rtpSock || !rtpSock->ValidHandle())
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"QOS is enabled but not started since we have not yet created the\
RTP socket");
return -1;
}
UdpSocketWrapper* rtcpSock = (_ptrSendRtcpSocket ?
_ptrSendRtcpSocket : _ptrRtcpSocket);
if (!rtcpSock || !rtcpSock->ValidHandle())
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"QOS is enabled but not started since we have not yet created the\
RTCP socket");
return -1;
}
const int32_t service_type = 0; // = SERVICETYPE_NOTRAFFIC
const int32_t not_specified = -1;
if (!rtpSock->SetQos(service_type, not_specified, not_specified,
not_specified, not_specified, not_specified,
_remoteRTPAddr, _overrideDSCP))
{
_lastError = kQosError;
return -1;
}
if (!rtcpSock->SetQos(service_type, not_specified, not_specified,
not_specified, not_specified, not_specified,
_remoteRTCPAddr,_overrideDSCP))
{
_lastError = kQosError;
}
_qos = false;
return 0;
}
int32_t UdpTransportImpl::QoS(bool& QoS, int32_t& serviceType,
int32_t& overrideDSCP) const
{
CriticalSectionScoped cs(_crit);
QoS = _qos;
serviceType = _serviceType;
overrideDSCP = _overrideDSCP;
return 0;
}
int32_t UdpTransportImpl::SetToS(int32_t DSCP, bool useSetSockOpt)
{
if (_qos)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "QoS already enabled");
_lastError = kQosError;
return -1;
}
if (DSCP < 0 || DSCP > 63)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Invalid DSCP");
_lastError = kTosInvalid;
return -1;
}
if(_tos)
{
if(useSetSockOpt != _useSetSockOpt)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"Can't switch SetSockOpt method without disabling TOS first");
_lastError = kTosInvalid;
return -1;
}
}
CriticalSectionScoped cs(_crit);
UdpSocketWrapper* rtpSock = NULL;
UdpSocketWrapper* rtcpSock = NULL;
if(_ptrSendRtpSocket)
{
rtpSock = _ptrSendRtpSocket;
}else
{
rtpSock = _ptrRtpSocket;
}
if (rtpSock == NULL)
{
_lastError = kSocketInvalid;
return -1;
}
if(!rtpSock->ValidHandle())
{
_lastError = kSocketInvalid;
return -1;
}
if(_ptrSendRtcpSocket)
{
rtcpSock = _ptrSendRtcpSocket;
}else
{
rtcpSock = _ptrRtcpSocket;
}
if (rtcpSock == NULL)
{
_lastError = kSocketInvalid;
return -1;
}
if(!rtcpSock->ValidHandle())
{
_lastError = kSocketInvalid;
return -1;
}
if (useSetSockOpt)
{
#ifdef _WIN32
OSVERSIONINFO OsVersion;
OsVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&OsVersion);
// Disable QoS before setting ToS on Windows XP. This is done by closing
// and re-opening the sockets.
// TODO (hellner): why not just fail here and force the user to
// re-initialize sockets? Doing this may trick the user
// into thinking that the sockets are in a state which
// they aren't.
if (OsVersion.dwMajorVersion == 5 &&
OsVersion.dwMinorVersion == 1)
{
if(!_useSetSockOpt)
{
if(_ptrSendRtpSocket)
{
CloseSendSockets();
_ptrSendRtpSocket =
_socket_creator->CreateSocket(_id, _mgr, NULL,
NULL, IpV6Enabled(),
true);
_ptrSendRtcpSocket =
_socket_creator->CreateSocket(_id, _mgr, NULL,
NULL, IpV6Enabled(),
true);
rtpSock=_ptrSendRtpSocket;
rtcpSock=_ptrSendRtcpSocket;
ErrorCode retVal = BindRTPSendSocket();
if(retVal != kNoSocketError)
{
_lastError = retVal;
return -1;
}
retVal = BindRTCPSendSocket();
if(retVal != kNoSocketError)
{
_lastError = retVal;
return -1;
}
}
else
{
bool receiving=_receiving;
uint32_t noOfReceiveBuffers = 0;
if(receiving)
{
noOfReceiveBuffers=_ptrRtpSocket->ReceiveBuffers();
if(StopReceiving()!=0)
{
return -1;
}
}
CloseReceiveSockets();
_ptrRtpSocket = _socket_creator->CreateSocket(
_id, _mgr, this, IncomingRTPCallback, IpV6Enabled(),
true);
_ptrRtcpSocket = _socket_creator->CreateSocket(
_id, _mgr, this, IncomingRTCPCallback, IpV6Enabled(),
true);
rtpSock=_ptrRtpSocket;
rtcpSock=_ptrRtcpSocket;
ErrorCode retVal = BindLocalRTPSocket();
if(retVal != kNoSocketError)
{
_lastError = retVal;
return -1;
}
retVal = BindLocalRTCPSocket();
if(retVal != kNoSocketError)
{
_lastError = retVal;
return -1;
}
if(receiving)
{
if(StartReceiving(noOfReceiveBuffers) !=
kNoSocketError)
{
return -1;
}
}
}
}
}
#endif // #ifdef _WIN32
WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
"Setting TOS using SetSockopt");
int32_t TOSShifted = DSCP << 2;
if (!rtpSock->SetSockopt(IPPROTO_IP, IP_TOS,
(int8_t*) &TOSShifted, 4))
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Could not SetSockopt tos value on RTP socket");
_lastError = kTosInvalid;
return -1;
}
if (!rtcpSock->SetSockopt(IPPROTO_IP, IP_TOS,
(int8_t*) &TOSShifted, 4))
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Could not sSetSockopt tos value on RTCP socket");
_lastError = kTosInvalid;
return -1;
}
} else
{
WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id,
"Setting TOS NOT using SetSockopt");
if (rtpSock->SetTOS(DSCP) != 0)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Could not set tos value on RTP socket");
_lastError = kTosError;
return -1;
}
if (rtcpSock->SetTOS(DSCP) != 0)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Could not set tos value on RTCP socket");
_lastError = kTosError;
return -1;
}
}
_useSetSockOpt = useSetSockOpt;
_tos = DSCP;
return 0;
}
int32_t UdpTransportImpl::ToS(int32_t& DSCP,
bool& useSetSockOpt) const
{
CriticalSectionScoped cs(_crit);
DSCP = _tos;
useSetSockOpt = _useSetSockOpt;
return 0;
}
int32_t UdpTransportImpl::SetPCP(int32_t PCP)
{
if (_qos)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "QoS already enabled");
_lastError = kQosError;
return -1;
}
if ((PCP < 0) || (PCP > 7))
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Invalid PCP");
_lastError = kPcpError;
return -1;
}
CriticalSectionScoped cs(_crit);
UdpSocketWrapper* rtpSock = NULL;
UdpSocketWrapper* rtcpSock = NULL;
if(_ptrSendRtpSocket)
{
rtpSock = _ptrSendRtpSocket;
}else
{
rtpSock = _ptrRtpSocket;
}
if (rtpSock == NULL)
{
_lastError = kSocketInvalid;
return -1;
}
if(!rtpSock->ValidHandle())
{
_lastError = kSocketInvalid;
return -1;
}
if(_ptrSendRtcpSocket)
{
rtcpSock = _ptrSendRtcpSocket;
}else
{
rtcpSock = _ptrRtcpSocket;
}
if (rtcpSock == NULL)
{
_lastError = kSocketInvalid;
return -1;
}
if(!rtcpSock->ValidHandle())
{
_lastError = kSocketInvalid;
return -1;
}
#if defined(_WIN32)
if (rtpSock->SetPCP(PCP) != 0)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Could not set PCP value on RTP socket");
_lastError = kPcpError;
return -1;
}
if (rtcpSock->SetPCP(PCP) != 0)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Could not set PCP value on RTCP socket");
_lastError = kPcpError;
return -1;
}
#elif defined(WEBRTC_LINUX)
if (!rtpSock->SetSockopt(SOL_SOCKET, SO_PRIORITY, (int8_t*) &PCP,
sizeof(PCP)))
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Could not SetSockopt PCP value on RTP socket");
_lastError = kPcpError;
return -1;
}
if (!rtcpSock->SetSockopt(SOL_SOCKET, SO_PRIORITY, (int8_t*) &PCP,
sizeof(PCP)))
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Could not SetSockopt PCP value on RTCP socket");
_lastError = kPcpError;
return -1;
}
#else
// Not supported on other platforms (WEBRTC_MAC)
_lastError = kPcpError;
return -1;
#endif
_pcp = PCP;
return 0;
}
int32_t UdpTransportImpl::PCP(int32_t& PCP) const
{
CriticalSectionScoped cs(_crit);
PCP = _pcp;
return 0;
}
bool UdpTransportImpl::SetSockOptUsed()
{
return _useSetSockOpt;
}
int32_t UdpTransportImpl::EnableIpV6() {
CriticalSectionScoped cs(_crit);
const bool initialized = (_ptrSendRtpSocket || _ptrRtpSocket);
if (_ipV6Enabled) {
return 0;
}
if (initialized) {
_lastError = kIpVersion6Error;
return -1;
}
_ipV6Enabled = true;
return 0;
}
int32_t UdpTransportImpl::FilterIP(
char filterIPAddress[kIpAddressVersion6Length]) const
{
if(filterIPAddress == NULL)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"FilterIP: Invalid argument");
return -1;
}
if(_filterIPAddress._sockaddr_storage.sin_family == 0)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "No Filter configured");
return -1;
}
CriticalSectionScoped cs(_critFilter);
uint32_t ipSize = kIpAddressVersion6Length;
uint16_t sourcePort;
return IPAddress(_filterIPAddress, filterIPAddress, ipSize, sourcePort);
}
int32_t UdpTransportImpl::SetFilterIP(
const char filterIPAddress[kIpAddressVersion6Length])
{
if(filterIPAddress == NULL)
{
memset(&_filterIPAddress, 0, sizeof(_filterIPAddress));
WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "Filter IP reset");
return 0;
}
CriticalSectionScoped cs(_critFilter);
if (_ipV6Enabled)
{
_filterIPAddress._sockaddr_storage.sin_family = AF_INET6;
if (InetPresentationToNumeric(
AF_INET6,
filterIPAddress,
&_filterIPAddress._sockaddr_in6.sin6_addr) < 0)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id, "Failed to set\
filter IP for IPv6");
_lastError = FILTER_ERROR;
return -1;
}
}
else
{
_filterIPAddress._sockaddr_storage.sin_family = AF_INET;
if(InetPresentationToNumeric(
AF_INET,
filterIPAddress,
&_filterIPAddress._sockaddr_in.sin_addr) < 0)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Failed to set filter IP for IPv4");
_lastError = FILTER_ERROR;
return -1;
}
}
WEBRTC_TRACE(kTraceDebug, kTraceTransport, _id, "Filter IP set");
return 0;
}
int32_t UdpTransportImpl::SetFilterPorts(uint16_t rtpFilterPort,
uint16_t rtcpFilterPort)
{
CriticalSectionScoped cs(_critFilter);
_rtpFilterPort = rtpFilterPort;
_rtcpFilterPort = rtcpFilterPort;
return 0;
}
bool UdpTransportImpl::SendSocketsInitialized() const
{
CriticalSectionScoped cs(_crit);
if(_ptrSendRtpSocket)
{
return true;
}
if(_destPort !=0)
{
return true;
}
return false;
}
bool UdpTransportImpl::ReceiveSocketsInitialized() const
{
if(_ptrRtpSocket)
{
return true;
}
return false;
}
bool UdpTransportImpl::SourcePortsInitialized() const
{
if(_ptrSendRtpSocket)
{
return true;
}
return false;
}
bool UdpTransportImpl::IpV6Enabled() const
{
WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, "%s", __FUNCTION__);
return _ipV6Enabled;
}
void UdpTransportImpl::BuildRemoteRTPAddr()
{
if(_ipV6Enabled)
{
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
_remoteRTPAddr.sin_length = 0;
_remoteRTPAddr.sin_family = PF_INET6;
#else
_remoteRTPAddr._sockaddr_storage.sin_family = PF_INET6;
#endif
_remoteRTPAddr._sockaddr_in6.sin6_flowinfo=0;
_remoteRTPAddr._sockaddr_in6.sin6_scope_id=0;
_remoteRTPAddr._sockaddr_in6.sin6_port = Htons(_destPort);
InetPresentationToNumeric(AF_INET6,_destIP,
&_remoteRTPAddr._sockaddr_in6.sin6_addr);
} else
{
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
_remoteRTPAddr.sin_length = 0;
_remoteRTPAddr.sin_family = PF_INET;
#else
_remoteRTPAddr._sockaddr_storage.sin_family = PF_INET;
#endif
_remoteRTPAddr._sockaddr_in.sin_port = Htons(_destPort);
_remoteRTPAddr._sockaddr_in.sin_addr = InetAddrIPV4(_destIP);
}
}
void UdpTransportImpl::BuildRemoteRTCPAddr()
{
if(_ipV6Enabled)
{
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
_remoteRTCPAddr.sin_length = 0;
_remoteRTCPAddr.sin_family = PF_INET6;
#else
_remoteRTCPAddr._sockaddr_storage.sin_family = PF_INET6;
#endif
_remoteRTCPAddr._sockaddr_in6.sin6_flowinfo=0;
_remoteRTCPAddr._sockaddr_in6.sin6_scope_id=0;
_remoteRTCPAddr._sockaddr_in6.sin6_port = Htons(_destPortRTCP);
InetPresentationToNumeric(AF_INET6,_destIP,
&_remoteRTCPAddr._sockaddr_in6.sin6_addr);
} else
{
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
_remoteRTCPAddr.sin_length = 0;
_remoteRTCPAddr.sin_family = PF_INET;
#else
_remoteRTCPAddr._sockaddr_storage.sin_family = PF_INET;
#endif
_remoteRTCPAddr._sockaddr_in.sin_port = Htons(_destPortRTCP);
_remoteRTCPAddr._sockaddr_in.sin_addr= InetAddrIPV4(_destIP);
}
}
UdpTransportImpl::ErrorCode UdpTransportImpl::BindRTPSendSocket()
{
if(!_ptrSendRtpSocket)
{
return kSocketInvalid;
}
if(!_ptrSendRtpSocket->ValidHandle())
{
return kIpAddressInvalid;
}
if(_ipV6Enabled)
{
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
_localRTPAddr.sin_length = 0;
_localRTPAddr.sin_family = PF_INET6;
#else
_localRTPAddr._sockaddr_storage.sin_family = PF_INET6;
#endif
_localRTPAddr._sockaddr_in6.sin6_flowinfo=0;
_localRTPAddr._sockaddr_in6.sin6_scope_id=0;
_localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[0] =
0; // = INADDR_ANY
_localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[1] =
0;
_localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[2] =
0;
_localRTPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[3] =
0;
_localRTPAddr._sockaddr_in6.sin6_port = Htons(_srcPort);
if(_ptrSendRtpSocket->Bind(_localRTPAddr) == false)
{
WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
"Failed to bind to port:%d ", _srcPort);
return kFailedToBindPort;
}
} else {
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
_localRTPAddr.sin_length = 0;
_localRTPAddr.sin_family = PF_INET;
#else
_localRTPAddr._sockaddr_storage.sin_family = PF_INET;
#endif
_localRTPAddr._sockaddr_in.sin_addr = 0;
_localRTPAddr._sockaddr_in.sin_port = Htons(_srcPort);
if(_ptrSendRtpSocket->Bind(_localRTPAddr) == false)
{
WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
"Failed to bind to port:%d ", _srcPort);
return kFailedToBindPort;
}
}
return kNoSocketError;
}
UdpTransportImpl::ErrorCode UdpTransportImpl::BindRTCPSendSocket()
{
if(!_ptrSendRtcpSocket)
{
return kSocketInvalid;
}
if(_ipV6Enabled)
{
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
_localRTCPAddr.sin_length = 0;
_localRTCPAddr.sin_family = PF_INET6;
#else
_localRTCPAddr._sockaddr_storage.sin_family = PF_INET6;
#endif
_localRTCPAddr._sockaddr_in6.sin6_flowinfo=0;
_localRTCPAddr._sockaddr_in6.sin6_scope_id=0;
_localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[0] =
0; // = INADDR_ANY
_localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[1] =
0;
_localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[2] =
0;
_localRTCPAddr._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[3] =
0;
_localRTCPAddr._sockaddr_in6.sin6_port = Htons(_srcPortRTCP);
if(_ptrSendRtcpSocket->Bind(_localRTCPAddr) == false)
{
WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
"Failed to bind to port:%d ", _srcPortRTCP);
return kFailedToBindPort;
}
} else {
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
_localRTCPAddr.sin_length = 0;
_localRTCPAddr.sin_family = PF_INET;
#else
_localRTCPAddr._sockaddr_storage.sin_family = PF_INET;
#endif
_localRTCPAddr._sockaddr_in.sin_addr= 0;
_localRTCPAddr._sockaddr_in.sin_port = Htons(_srcPortRTCP);
if(_ptrSendRtcpSocket->Bind(_localRTCPAddr) == false)
{
WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
"Failed to bind to port:%d ", _srcPortRTCP);
return kFailedToBindPort;
}
}
return kNoSocketError;
}
UdpTransportImpl::ErrorCode UdpTransportImpl::BindLocalRTPSocket()
{
if(!_ptrRtpSocket)
{
return kSocketInvalid;
}
if(!IpV6Enabled())
{
SocketAddress recAddr;
memset(&recAddr, 0, sizeof(SocketAddress));
recAddr._sockaddr_storage.sin_family = AF_INET;
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
recAddr.sin_length = 0;
recAddr.sin_family = PF_INET;
#else
recAddr._sockaddr_storage.sin_family = PF_INET;
#endif
recAddr._sockaddr_in.sin_addr = InetAddrIPV4(_localIP);
recAddr._sockaddr_in.sin_port = Htons(_localPort);
if (!_ptrRtpSocket->Bind(recAddr))
{
WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
"Failed to bind to port:%d ", _localPort);
return kFailedToBindPort;
}
}
else
{
SocketAddress stLclName;
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
stLclName.sin_lenght = 0;
stLclName.sin_family = PF_INET6;
#else
stLclName._sockaddr_storage.sin_family = PF_INET6;
#endif
InetPresentationToNumeric(AF_INET6,_localIP,
&stLclName._sockaddr_in6.sin6_addr);
stLclName._sockaddr_in6.sin6_port = Htons(_localPort);
stLclName._sockaddr_in6.sin6_flowinfo = 0;
stLclName._sockaddr_in6.sin6_scope_id = 0;
if (!_ptrRtpSocket->Bind(stLclName))
{
WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
"Failed to bind to port:%d ", _localPort);
return kFailedToBindPort;
}
}
if(_localMulticastIP[0] != 0)
{
// Join the multicast group from which to receive datagrams.
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = InetAddrIPV4(_localMulticastIP);
mreq.imr_interface.s_addr = INADDR_ANY;
if (!_ptrRtpSocket->SetSockopt(IPPROTO_IP,IP_ADD_MEMBERSHIP,
(int8_t*)&mreq,sizeof (mreq)))
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"setsockopt() for multicast failed, not closing socket");
}else
{
WEBRTC_TRACE(kTraceInfo, kTraceTransport, _id,
"multicast group successfully joined");
}
}
return kNoSocketError;
}
UdpTransportImpl::ErrorCode UdpTransportImpl::BindLocalRTCPSocket()
{
if(!_ptrRtcpSocket)
{
return kSocketInvalid;
}
if(! IpV6Enabled())
{
SocketAddress recAddr;
memset(&recAddr, 0, sizeof(SocketAddress));
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
recAddr.sin_length = 0;
recAddr.sin_family = AF_INET;
#else
recAddr._sockaddr_storage.sin_family = AF_INET;
#endif
recAddr._sockaddr_in.sin_addr = InetAddrIPV4(_localIP);
recAddr._sockaddr_in.sin_port = Htons(_localPortRTCP);
if (!_ptrRtcpSocket->Bind(recAddr))
{
WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
"Failed to bind to port:%d ", _localPortRTCP);
return kFailedToBindPort;
}
}
else
{
SocketAddress stLclName;
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
stLclName.sin_length = 0;
stLclName.sin_family = PF_INET6;
#else
stLclName._sockaddr_storage.sin_family = PF_INET6;
#endif
stLclName._sockaddr_in6.sin6_flowinfo = 0;
stLclName._sockaddr_in6.sin6_scope_id = 0;
stLclName._sockaddr_in6.sin6_port = Htons(_localPortRTCP);
InetPresentationToNumeric(AF_INET6,_localIP,
&stLclName._sockaddr_in6.sin6_addr);
if (!_ptrRtcpSocket->Bind(stLclName))
{
WEBRTC_TRACE(kTraceWarning, kTraceTransport, _id,
"Failed to bind to port:%d ", _localPortRTCP);
return kFailedToBindPort;
}
}
if(_localMulticastIP[0] != 0)
{
// Join the multicast group from which to receive datagrams.
struct ip_mreq mreq;
mreq.imr_multiaddr.s_addr = InetAddrIPV4(_localMulticastIP);
mreq.imr_interface.s_addr = INADDR_ANY;
if (!_ptrRtcpSocket->SetSockopt(IPPROTO_IP,IP_ADD_MEMBERSHIP,
(int8_t*)&mreq,sizeof (mreq)))
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"setsockopt() for multicast failed, not closing socket");
}else
{
WEBRTC_TRACE(kTraceInfo, kTraceTransport, _id,
"multicast group successfully joined");
}
}
return kNoSocketError;
}
int32_t UdpTransportImpl::InitializeSourcePorts(uint16_t rtpPort,
uint16_t rtcpPort)
{
if(rtpPort == 0)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"InitializeSourcePorts port 0 not allowed");
_lastError = kPortInvalid;
return -1;
}
CriticalSectionScoped cs(_crit);
CloseSendSockets();
if(_mgr == NULL)
{
return -1;
}
_srcPort = rtpPort;
if(rtcpPort == 0)
{
_srcPortRTCP = rtpPort+1;
} else
{
_srcPortRTCP = rtcpPort;
}
_useSetSockOpt =false;
_tos=0;
_pcp=0;
_ptrSendRtpSocket = _socket_creator->CreateSocket(_id, _mgr, NULL, NULL,
IpV6Enabled(), false);
_ptrSendRtcpSocket = _socket_creator->CreateSocket(_id, _mgr, NULL, NULL,
IpV6Enabled(), false);
ErrorCode retVal = BindRTPSendSocket();
if(retVal != kNoSocketError)
{
_lastError = retVal;
return -1;
}
retVal = BindRTCPSendSocket();
if(retVal != kNoSocketError)
{
_lastError = retVal;
return -1;
}
return 0;
}
int32_t UdpTransportImpl::SourcePorts(uint16_t& rtpPort,
uint16_t& rtcpPort) const
{
CriticalSectionScoped cs(_crit);
rtpPort = (_srcPort != 0) ? _srcPort : _localPort;
rtcpPort = (_srcPortRTCP != 0) ? _srcPortRTCP : _localPortRTCP;
return 0;
}
#ifdef _WIN32
int32_t UdpTransportImpl::StartReceiving(uint32_t numberOfSocketBuffers)
#else
int32_t UdpTransportImpl::StartReceiving(uint32_t /*numberOfSocketBuffers*/)
#endif
{
CriticalSectionScoped cs(_crit);
if(_receiving)
{
return 0;
}
if(_ptrRtpSocket)
{
#ifdef _WIN32
if(!_ptrRtpSocket->StartReceiving(numberOfSocketBuffers))
#else
if(!_ptrRtpSocket->StartReceiving())
#endif
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Failed to start receive on RTP socket");
_lastError = kStartReceiveError;
return -1;
}
}
if(_ptrRtcpSocket)
{
if(!_ptrRtcpSocket->StartReceiving())
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Failed to start receive on RTCP socket");
_lastError = kStartReceiveError;
return -1;
}
}
if( _ptrRtpSocket == NULL &&
_ptrRtcpSocket == NULL)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Failed to StartReceiving, no socket initialized");
_lastError = kStartReceiveError;
return -1;
}
_receiving = true;
return 0;
}
bool UdpTransportImpl::Receiving() const
{
return _receiving;
}
int32_t UdpTransportImpl::StopReceiving()
{
CriticalSectionScoped cs(_crit);
_receiving = false;
if (_ptrRtpSocket)
{
if (!_ptrRtpSocket->StopReceiving())
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Failed to stop receiving on RTP socket");
_lastError = kStopReceiveError;
return -1;
}
}
if (_ptrRtcpSocket)
{
if (!_ptrRtcpSocket->StopReceiving())
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"Failed to stop receiving on RTCP socket");
_lastError = kStopReceiveError;
return -1;
}
}
return 0;
}
int32_t UdpTransportImpl::InitializeSendSockets(
const char* ipaddr,
const uint16_t rtpPort,
const uint16_t rtcpPort)
{
{
CriticalSectionScoped cs(_crit);
_destPort = rtpPort;
if(rtcpPort == 0)
{
_destPortRTCP = _destPort+1;
} else
{
_destPortRTCP = rtcpPort;
}
if(ipaddr == NULL)
{
if (!IsIpAddressValid(_destIP, IpV6Enabled()))
{
_destPort = 0;
_destPortRTCP = 0;
_lastError = kIpAddressInvalid;
return -1;
}
} else
{
if (IsIpAddressValid(ipaddr, IpV6Enabled()))
{
strncpy(
_destIP,
ipaddr,
IpV6Enabled() ? kIpAddressVersion6Length :
kIpAddressVersion4Length);
} else {
_destPort = 0;
_destPortRTCP = 0;
_lastError = kIpAddressInvalid;
return -1;
}
}
BuildRemoteRTPAddr();
BuildRemoteRTCPAddr();
}
if (_ipV6Enabled)
{
if (_qos)
{
WEBRTC_TRACE(
kTraceWarning,
kTraceTransport,
_id,
"QOS is enabled but will be ignored since IPv6 is enabled");
}
}else
{
// TODO (grunell): Multicast support is experimantal.
// Put the first digit of the remote address in val.
int32_t val = ntohl(_remoteRTPAddr._sockaddr_in.sin_addr)>> 24;
if((val > 223) && (val < 240))
{
// Multicast address.
CriticalSectionScoped cs(_crit);
UdpSocketWrapper* rtpSock = (_ptrSendRtpSocket ?
_ptrSendRtpSocket : _ptrRtpSocket);
if (!rtpSock || !rtpSock->ValidHandle())
{
_lastError = kSocketInvalid;
return -1;
}
UdpSocketWrapper* rtcpSock = (_ptrSendRtcpSocket ?
_ptrSendRtcpSocket : _ptrRtcpSocket);
if (!rtcpSock || !rtcpSock->ValidHandle())
{
_lastError = kSocketInvalid;
return -1;
}
// Set Time To Live to same region
int32_t iOptVal = 64;
if (!rtpSock->SetSockopt(IPPROTO_IP, IP_MULTICAST_TTL,
(int8_t*)&iOptVal,
sizeof (int32_t)))
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"setsockopt for multicast error on RTP socket");
_ptrRtpSocket->CloseBlocking();
_ptrRtpSocket = NULL;
_lastError = kMulticastAddressInvalid;
return -1;
}
if (!rtcpSock->SetSockopt(IPPROTO_IP, IP_MULTICAST_TTL,
(int8_t*)&iOptVal,
sizeof (int32_t)))
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"setsockopt for multicast error on RTCP socket");
_ptrRtpSocket->CloseBlocking();
_ptrRtpSocket = NULL;
_lastError = kMulticastAddressInvalid;
return -1;
}
}
}
return 0;
}
void UdpTransportImpl::BuildSockaddrIn(uint16_t portnr,
const char* ip,
SocketAddress& remoteAddr) const
{
if(_ipV6Enabled)
{
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
remoteAddr.sin_length = 0;
remoteAddr.sin_family = PF_INET6;
#else
remoteAddr._sockaddr_storage.sin_family = PF_INET6;
#endif
remoteAddr._sockaddr_in6.sin6_port = Htons(portnr);
InetPresentationToNumeric(AF_INET6, ip,
&remoteAddr._sockaddr_in6.sin6_addr);
remoteAddr._sockaddr_in6.sin6_flowinfo=0;
remoteAddr._sockaddr_in6.sin6_scope_id=0;
} else
{
#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
remoteAddr.sin_length = 0;
remoteAddr.sin_family = PF_INET;
#else
remoteAddr._sockaddr_storage.sin_family = PF_INET;
#endif
remoteAddr._sockaddr_in.sin_port = Htons(portnr);
remoteAddr._sockaddr_in.sin_addr= InetAddrIPV4(
const_cast<char*>(ip));
}
}
int32_t UdpTransportImpl::SendRaw(const int8_t *data,
size_t length,
int32_t isRTCP,
uint16_t portnr,
const char* ip)
{
CriticalSectionScoped cs(_crit);
if(isRTCP)
{
UdpSocketWrapper* rtcpSock = NULL;
if(_ptrSendRtcpSocket)
{
rtcpSock = _ptrSendRtcpSocket;
} else if(_ptrRtcpSocket)
{
rtcpSock = _ptrRtcpSocket;
} else
{
return -1;
}
if(portnr == 0 && ip == NULL)
{
return rtcpSock->SendTo(data,length,_remoteRTCPAddr);
} else if(portnr != 0 && ip != NULL)
{
SocketAddress remoteAddr;
BuildSockaddrIn(portnr, ip, remoteAddr);
return rtcpSock->SendTo(data,length,remoteAddr);
} else if(ip != NULL)
{
SocketAddress remoteAddr;
BuildSockaddrIn(_destPortRTCP, ip, remoteAddr);
return rtcpSock->SendTo(data,length,remoteAddr);
} else
{
SocketAddress remoteAddr;
BuildSockaddrIn(portnr, _destIP, remoteAddr);
return rtcpSock->SendTo(data,length,remoteAddr);
}
} else {
UdpSocketWrapper* rtpSock = NULL;
if(_ptrSendRtpSocket)
{
rtpSock = _ptrSendRtpSocket;
} else if(_ptrRtpSocket)
{
rtpSock = _ptrRtpSocket;
} else
{
return -1;
}
if(portnr == 0 && ip == NULL)
{
return rtpSock->SendTo(data,length,_remoteRTPAddr);
} else if(portnr != 0 && ip != NULL)
{
SocketAddress remoteAddr;
BuildSockaddrIn(portnr, ip, remoteAddr);
return rtpSock->SendTo(data,length,remoteAddr);
} else if(ip != NULL)
{
SocketAddress remoteAddr;
BuildSockaddrIn(_destPort, ip, remoteAddr);
return rtpSock->SendTo(data,length,remoteAddr);
} else
{
SocketAddress remoteAddr;
BuildSockaddrIn(portnr, _destIP, remoteAddr);
return rtpSock->SendTo(data,length,remoteAddr);
}
}
}
int32_t UdpTransportImpl::SendRTPPacketTo(const int8_t* data,
size_t length,
const SocketAddress& to)
{
CriticalSectionScoped cs(_crit);
if(_ptrSendRtpSocket)
{
return _ptrSendRtpSocket->SendTo(data,length,to);
} else if(_ptrRtpSocket)
{
return _ptrRtpSocket->SendTo(data,length,to);
}
return -1;
}
int32_t UdpTransportImpl::SendRTCPPacketTo(const int8_t* data,
size_t length,
const SocketAddress& to)
{
CriticalSectionScoped cs(_crit);
if(_ptrSendRtcpSocket)
{
return _ptrSendRtcpSocket->SendTo(data,length,to);
} else if(_ptrRtcpSocket)
{
return _ptrRtcpSocket->SendTo(data,length,to);
}
return -1;
}
int32_t UdpTransportImpl::SendRTPPacketTo(const int8_t* data,
size_t length,
const uint16_t rtpPort)
{
CriticalSectionScoped cs(_crit);
// Use the current SocketAdress but update it with rtpPort.
SocketAddress to;
memcpy(&to, &_remoteRTPAddr, sizeof(SocketAddress));
if(_ipV6Enabled)
{
to._sockaddr_in6.sin6_port = Htons(rtpPort);
} else
{
to._sockaddr_in.sin_port = Htons(rtpPort);
}
if(_ptrSendRtpSocket)
{
return _ptrSendRtpSocket->SendTo(data,length,to);
} else if(_ptrRtpSocket)
{
return _ptrRtpSocket->SendTo(data,length,to);
}
return -1;
}
int32_t UdpTransportImpl::SendRTCPPacketTo(const int8_t* data,
size_t length,
const uint16_t rtcpPort)
{
CriticalSectionScoped cs(_crit);
// Use the current SocketAdress but update it with rtcpPort.
SocketAddress to;
memcpy(&to, &_remoteRTCPAddr, sizeof(SocketAddress));
if(_ipV6Enabled)
{
to._sockaddr_in6.sin6_port = Htons(rtcpPort);
} else
{
to._sockaddr_in.sin_port = Htons(rtcpPort);
}
if(_ptrSendRtcpSocket)
{
return _ptrSendRtcpSocket->SendTo(data,length,to);
} else if(_ptrRtcpSocket)
{
return _ptrRtcpSocket->SendTo(data,length,to);
}
return -1;
}
bool UdpTransportImpl::SendRtp(const uint8_t* data,
size_t length,
const PacketOptions& packet_options) {
WEBRTC_TRACE(kTraceStream, kTraceTransport, _id, "%s", __FUNCTION__);
CriticalSectionScoped cs(_crit);
if(_destIP[0] == 0)
{
return false;
}
if(_destPort == 0)
{
return false;
}
// Create socket if it hasn't been set up already.
// TODO (hellner): why not fail here instead. Sockets not being initialized
// indicates that there is a problem somewhere.
if( _ptrSendRtpSocket == NULL &&
_ptrRtpSocket == NULL)
{
WEBRTC_TRACE(
kTraceStateInfo,
kTraceTransport,
_id,
"Creating RTP socket since no receive or source socket is\
configured");
_ptrRtpSocket = _socket_creator->CreateSocket(_id, _mgr, this,
IncomingRTPCallback,
IpV6Enabled(), false);
// Don't bind to a specific IP address.
if(! IpV6Enabled())
{
strncpy(_localIP, "0.0.0.0",16);
} else
{
strncpy(_localIP, "0000:0000:0000:0000:0000:0000:0000:0000",
kIpAddressVersion6Length);
}
_localPort = _destPort;
ErrorCode retVal = BindLocalRTPSocket();
if(retVal != kNoSocketError)
{
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"SendPacket() failed to bind RTP socket");
_lastError = retVal;
CloseReceiveSockets();
return false;
}
}
if(_ptrSendRtpSocket)
{
return _ptrSendRtpSocket->SendTo((const int8_t*)data, length,
_remoteRTPAddr) >= 0;
} else if(_ptrRtpSocket)
{
return _ptrRtpSocket->SendTo((const int8_t*)data, length,
_remoteRTPAddr) >= 0;
}
return false;
}
bool UdpTransportImpl::SendRtcp(const uint8_t* data, size_t length) {
CriticalSectionScoped cs(_crit);
if(_destIP[0] == 0)
{
return false;
}
if(_destPortRTCP == 0)
{
return false;
}
// Create socket if it hasn't been set up already.
// TODO (hellner): why not fail here instead. Sockets not being initialized
// indicates that there is a problem somewhere.
if( _ptrSendRtcpSocket == NULL &&
_ptrRtcpSocket == NULL)
{
WEBRTC_TRACE(
kTraceStateInfo,
kTraceTransport,
_id,
"Creating RTCP socket since no receive or source socket is\
configured");
_ptrRtcpSocket = _socket_creator->CreateSocket(_id, _mgr, this,
IncomingRTCPCallback,
IpV6Enabled(), false);
// Don't bind to a specific IP address.
if(! IpV6Enabled())
{
strncpy(_localIP, "0.0.0.0",16);
} else
{
strncpy(_localIP, "0000:0000:0000:0000:0000:0000:0000:0000",
kIpAddressVersion6Length);
}
_localPortRTCP = _destPortRTCP;
ErrorCode retVal = BindLocalRTCPSocket();
if(retVal != kNoSocketError)
{
_lastError = retVal;
WEBRTC_TRACE(kTraceError, kTraceTransport, _id,
"SendRtcp() failed to bind RTCP socket");
CloseReceiveSockets();
return false;
}
}
if(_ptrSendRtcpSocket)
{
return _ptrSendRtcpSocket->SendTo((const int8_t*)data, length,
_remoteRTCPAddr) >= 0;
} else if(_ptrRtcpSocket)
{
return _ptrRtcpSocket->SendTo((const int8_t*)data, length,
_remoteRTCPAddr) >= 0;
}
return false;
}
int32_t UdpTransportImpl::SetSendIP(const char* ipaddr)
{
if(!IsIpAddressValid(ipaddr,IpV6Enabled()))
{
return kIpAddressInvalid;
}
CriticalSectionScoped cs(_crit);
strncpy(_destIP, ipaddr,kIpAddressVersion6Length);
BuildRemoteRTPAddr();
BuildRemoteRTCPAddr();
return 0;
}
int32_t UdpTransportImpl::SetSendPorts(uint16_t rtpPort, uint16_t rtcpPort)
{
CriticalSectionScoped cs(_crit);
_destPort = rtpPort;
if(rtcpPort == 0)
{
_destPortRTCP = _destPort+1;
} else
{
_destPortRTCP = rtcpPort;
}
BuildRemoteRTPAddr();
BuildRemoteRTCPAddr();
return 0;
}
void UdpTransportImpl::IncomingRTPCallback(CallbackObj obj,
const int8_t* rtpPacket,
size_t rtpPacketLength,
const SocketAddress* from)
{
if (rtpPacket && rtpPacketLength > 0)
{
UdpTransportImpl* socketTransport = (UdpTransportImpl*) obj;
socketTransport->IncomingRTPFunction(rtpPacket, rtpPacketLength, from);
}
}
void UdpTransportImpl::IncomingRTCPCallback(CallbackObj obj,
const int8_t* rtcpPacket,
size_t rtcpPacketLength,
const SocketAddress* from)
{
if (rtcpPacket && rtcpPacketLength > 0)
{
UdpTransportImpl* socketTransport = (UdpTransportImpl*) obj;
socketTransport->IncomingRTCPFunction(rtcpPacket, rtcpPacketLength,
from);
}
}
void UdpTransportImpl::IncomingRTPFunction(const int8_t* rtpPacket,
size_t rtpPacketLength,
const SocketAddress* fromSocket)
{
char ipAddress[kIpAddressVersion6Length];
uint32_t ipAddressLength = kIpAddressVersion6Length;
uint16_t portNr = 0;
{
CriticalSectionScoped cs(_critFilter);
if (FilterIPAddress(fromSocket) == false)
{
// Packet should be filtered out. Drop it.
WEBRTC_TRACE(kTraceStream, kTraceTransport, _id,
"Incoming RTP packet blocked by IP filter");
return;
}
if (IPAddressCached(*fromSocket, ipAddress, ipAddressLength, portNr) <
0)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"UdpTransportImpl::IncomingRTPFunction - Cannot get sender\
information");
}else
{
// Make sure ipAddress is null terminated.
ipAddress[kIpAddressVersion6Length - 1] = 0;
strncpy(_fromIP, ipAddress, kIpAddressVersion6Length - 1);
}
// Filter based on port.
if (_rtpFilterPort != 0 &&
_rtpFilterPort != portNr)
{
// Drop packet.
memset(_fromIP, 0, sizeof(_fromIP));
WEBRTC_TRACE(
kTraceStream,
kTraceTransport,
_id,
"Incoming RTP packet blocked by filter incoming from port:%d\
allowed port:%d",
portNr,
_rtpFilterPort);
return;
}
_fromPort = portNr;
}
CriticalSectionScoped cs(_critPacketCallback);
if (_packetCallback)
{
WEBRTC_TRACE(kTraceStream, kTraceTransport, _id,
"Incoming RTP packet from ip:%s port:%d", ipAddress, portNr);
_packetCallback->IncomingRTPPacket(rtpPacket, rtpPacketLength,
ipAddress, portNr);
}
}
void UdpTransportImpl::IncomingRTCPFunction(const int8_t* rtcpPacket,
size_t rtcpPacketLength,
const SocketAddress* fromSocket)
{
char ipAddress[kIpAddressVersion6Length];
uint32_t ipAddressLength = kIpAddressVersion6Length;
uint16_t portNr = 0;
{
CriticalSectionScoped cs(_critFilter);
if (FilterIPAddress(fromSocket) == false)
{
// Packet should be filtered out. Drop it.
WEBRTC_TRACE(kTraceStream, kTraceTransport, _id,
"Incoming RTCP packet blocked by IP filter");
return;
}
if (IPAddress(*fromSocket, ipAddress, ipAddressLength, portNr) < 0)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"UdpTransportImpl::IncomingRTCPFunction - Cannot get sender\
information");
}else {
// Make sure ipAddress is null terminated.
ipAddress[kIpAddressVersion6Length - 1] = 0;
strncpy(_fromIP, ipAddress, kIpAddressVersion6Length - 1);
}
// Filter based on port.
if (_rtcpFilterPort != 0 &&
_rtcpFilterPort != portNr)
{
// Drop packet.
WEBRTC_TRACE(
kTraceStream,
kTraceTransport,
_id,
"Incoming RTCP packet blocked by filter incoming from port:%d\
allowed port:%d",
portNr,
_rtpFilterPort);
return;
}
_fromPortRTCP = portNr;
}
CriticalSectionScoped cs(_critPacketCallback);
if (_packetCallback)
{
WEBRTC_TRACE(kTraceStream, kTraceTransport, _id,
"Incoming RTCP packet from ip:%s port:%d", ipAddress,
portNr);
_packetCallback->IncomingRTCPPacket(rtcpPacket, rtcpPacketLength,
ipAddress, portNr);
}
}
bool UdpTransportImpl::FilterIPAddress(const SocketAddress* fromAddress)
{
if(fromAddress->_sockaddr_storage.sin_family == AF_INET)
{
if (_filterIPAddress._sockaddr_storage.sin_family == AF_INET)
{
// IP is stored in sin_addr.
if (_filterIPAddress._sockaddr_in.sin_addr != 0 &&
(_filterIPAddress._sockaddr_in.sin_addr !=
fromAddress->_sockaddr_in.sin_addr))
{
return false;
}
}
}
else if(fromAddress->_sockaddr_storage.sin_family == AF_INET6)
{
if (_filterIPAddress._sockaddr_storage.sin_family == AF_INET6)
{
// IP is stored in sin_6addr.
for (int32_t i = 0; i < 4; i++)
{
if (_filterIPAddress._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[i] != 0 &&
_filterIPAddress._sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[i] != fromAddress->_sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u32[i])
{
return false;
}
}
}
}
else
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
_id,
"UdpTransportImpl::FilterIPAddress() unknown address family");
return false;
}
return true;
}
void UdpTransportImpl::CloseReceiveSockets()
{
if(_ptrRtpSocket)
{
_ptrRtpSocket->CloseBlocking();
_ptrRtpSocket = NULL;
}
if(_ptrRtcpSocket)
{
_ptrRtcpSocket->CloseBlocking();
_ptrRtcpSocket = NULL;
}
_receiving = false;
}
void UdpTransportImpl::CloseSendSockets()
{
if(_ptrSendRtpSocket)
{
_ptrSendRtpSocket->CloseBlocking();
_ptrSendRtpSocket = 0;
}
if(_ptrSendRtcpSocket)
{
_ptrSendRtcpSocket->CloseBlocking();
_ptrSendRtcpSocket = 0;
}
}
uint16_t UdpTransport::Htons(const uint16_t port)
{
return htons(port);
}
uint32_t UdpTransport::Htonl(const uint32_t a)
{
return htonl(a);
}
uint32_t UdpTransport::InetAddrIPV4(const char* ip)
{
return ::inet_addr(ip);
}
int32_t UdpTransport::InetPresentationToNumeric(int32_t af,
const char* src,
void* dst)
{
#if defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
const int32_t result = inet_pton(af, src, dst);
return result > 0 ? 0 : -1;
#elif defined(_WIN32)
SocketAddress temp;
int length=sizeof(SocketAddress);
if(af == AF_INET)
{
int32_t result = WSAStringToAddressA(
(const LPSTR)src,
af,
0,
reinterpret_cast<struct sockaddr*>(&temp),
&length);
if(result != 0)
{
return -1;
}
memcpy(dst,&(temp._sockaddr_in.sin_addr),
sizeof(temp._sockaddr_in.sin_addr));
return 0;
}
else if(af == AF_INET6)
{
int32_t result = WSAStringToAddressA(
(const LPSTR)src,
af,
0,
reinterpret_cast<struct sockaddr*>(&temp),
&length);
if(result !=0)
{
return -1;
}
memcpy(dst,&(temp._sockaddr_in6.sin6_addr),
sizeof(temp._sockaddr_in6.sin6_addr));
return 0;
}else
{
return -1;
}
#else
return -1;
#endif
}
int32_t UdpTransport::LocalHostAddressIPV6(char n_localIP[16])
{
#if defined(_WIN32)
struct addrinfo *result = NULL;
struct addrinfo *ptr = NULL;
struct addrinfo hints;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET6;
char szHostName[256] = "";
if(::gethostname(szHostName, sizeof(szHostName) - 1))
{
WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, "gethostname failed");
return -1;
}
DWORD dwRetval = getaddrinfo(szHostName, NULL, &hints, &result);
if ( dwRetval != 0 )
{
WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1,
"getaddrinfo failed, error:%d", dwRetval);
return -1;
}
for(ptr=result; ptr != NULL ;ptr=ptr->ai_next)
{
switch (ptr->ai_family)
{
case AF_INET6:
{
for(int i = 0; i< 16; i++)
{
n_localIP[i] = (*(SocketAddress*)ptr->ai_addr).
_sockaddr_in6.sin6_addr.Version6AddressUnion._s6_u8[i];
}
bool islocalIP = true;
for(int n = 0; n< 15; n++)
{
if(n_localIP[n] != 0)
{
islocalIP = false;
break;
}
}
if(islocalIP && n_localIP[15] != 1)
{
islocalIP = false;
}
if(islocalIP && ptr->ai_next)
{
continue;
}
if(n_localIP[0] == 0xfe &&
n_localIP[1] == 0x80 && ptr->ai_next)
{
continue;
}
freeaddrinfo(result);
}
return 0;
default:
break;
};
}
freeaddrinfo(result);
WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1,
"getaddrinfo failed to find address");
return -1;
#elif defined(WEBRTC_MAC)
struct ifaddrs* ptrIfAddrs = NULL;
struct ifaddrs* ptrIfAddrsStart = NULL;
getifaddrs(&ptrIfAddrsStart);
ptrIfAddrs = ptrIfAddrsStart;
while(ptrIfAddrs)
{
if(ptrIfAddrs->ifa_addr->sa_family == AF_INET6)
{
const struct sockaddr_in6* sock_in6 =
reinterpret_cast<struct sockaddr_in6*>(ptrIfAddrs->ifa_addr);
const struct in6_addr* sin6_addr = &sock_in6->sin6_addr;
if (IN6_IS_ADDR_LOOPBACK(sin6_addr) ||
IN6_IS_ADDR_LINKLOCAL(sin6_addr)) {
ptrIfAddrs = ptrIfAddrs->ifa_next;
continue;
}
memcpy(n_localIP, sin6_addr->s6_addr, sizeof(sin6_addr->s6_addr));
freeifaddrs(ptrIfAddrsStart);
return 0;
}
ptrIfAddrs = ptrIfAddrs->ifa_next;
}
freeifaddrs(ptrIfAddrsStart);
return -1;
#elif defined(WEBRTC_ANDROID)
return -1;
#else // WEBRTC_LINUX
struct
{
struct nlmsghdr n;
struct ifaddrmsg r;
} req;
struct rtattr* rta = NULL;
int status;
char buf[16384]; // = 16 * 1024 (16 kB)
struct nlmsghdr* nlmp;
struct ifaddrmsg* rtmp;
struct rtattr* rtatp;
int rtattrlen;
struct in6_addr* in6p;
int fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
if (fd == -1)
{
return -1;
}
// RTM_GETADDR is used to fetch the ip address from the kernel interface
// table. Populate the msg structure (req) the size of the message buffer
// is specified to netlinkmessage header, and flags values are set as
// NLM_F_ROOT | NLM_F_REQUEST.
// The request flag must be set for all messages requesting the data from
// kernel. The root flag is used to notify the kernel to return the full
// tabel. Another flag (not used) is NLM_F_MATCH. This is used to get only
// specified entries in the table. At the time of writing this program this
// flag is not implemented in kernel
memset(&req, 0, sizeof(req));
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
req.n.nlmsg_type = RTM_GETADDR;
req.r.ifa_family = AF_INET6;
// Fill up all the attributes for the rtnetlink header.
// The lenght is very important. 16 signifies the ipv6 address.
rta = (struct rtattr*)(((char*)&req) + NLMSG_ALIGN(req.n.nlmsg_len));
rta->rta_len = RTA_LENGTH(16);
status = send(fd, &req, req.n.nlmsg_len, 0);
if (status < 0)
{
close(fd);
return -1;
}
status = recv(fd, buf, sizeof(buf), 0);
if (status < 0)
{
close(fd);
return -1;
}
if(status == 0)
{
close(fd);
return -1;
}
close(fd);
// The message is stored in buff. Parse the message to get the requested
// data.
{
nlmp = (struct nlmsghdr*)buf;
int len = nlmp->nlmsg_len;
int req_len = len - sizeof(*nlmp);
if (req_len < 0 || len > status)
{
return -1;
}
if (!NLMSG_OK_NO_WARNING(nlmp, status))
{
return -1;
}
rtmp = (struct ifaddrmsg*)NLMSG_DATA(nlmp);
rtatp = (struct rtattr*)IFA_RTA(rtmp);
rtattrlen = IFA_PAYLOAD(nlmp);
for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen))
{
// Here we hit the fist chunk of the message. Time to validate the
// type. For more info on the different types see
// "man(7) rtnetlink" The table below is taken from man pages.
// Attributes
// rta_type value type description
// -------------------------------------------------------------
// IFA_UNSPEC - unspecified.
// IFA_ADDRESS raw protocol address interface address
// IFA_LOCAL raw protocol address local address
// IFA_LABEL asciiz string name of the interface
// IFA_BROADCAST raw protocol address broadcast address.
// IFA_ANYCAST raw protocol address anycast address
// IFA_CACHEINFO struct ifa_cacheinfo Address information.
if(rtatp->rta_type == IFA_ADDRESS)
{
bool islocalIP = true;
in6p = (struct in6_addr*)RTA_DATA(rtatp);
for(int n = 0; n< 15; n++)
{
if(in6p->s6_addr[n] != 0)
{
islocalIP = false;
break;
}
}
if(islocalIP && in6p->s6_addr[15] != 1)
{
islocalIP = false;
}
if(!islocalIP)
{
for(int i = 0; i< 16; i++)
{
n_localIP[i] = in6p->s6_addr[i];
}
if(n_localIP[0] == static_cast<char> (0xfe)
&& n_localIP[1] == static_cast<char>(0x80) )
{
// Auto configured IP.
continue;
}
break;
}
}
}
}
return 0;
#endif
}
int32_t UdpTransport::LocalHostAddress(uint32_t& localIP)
{
#if defined(_WIN32)
hostent* localHost;
localHost = gethostbyname( "" );
if(localHost)
{
if(localHost->h_addrtype != AF_INET)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
-1,
"LocalHostAddress can only get local IP for IP Version 4");
return -1;
}
localIP= Htonl(
(*(struct in_addr *)localHost->h_addr_list[0]).S_un.S_addr);
return 0;
}
else
{
int32_t error = WSAGetLastError();
WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1,
"gethostbyname failed, error:%d", error);
return -1;
}
#elif (defined(WEBRTC_MAC))
char localname[255];
if (gethostname(localname, 255) != -1)
{
hostent* localHost;
localHost = gethostbyname(localname);
if(localHost)
{
if(localHost->h_addrtype != AF_INET)
{
WEBRTC_TRACE(
kTraceError,
kTraceTransport,
-1,
"LocalHostAddress can only get local IP for IP Version 4");
return -1;
}
localIP = Htonl((*(struct in_addr*)*localHost->h_addr_list).s_addr);
return 0;
}
}
WEBRTC_TRACE(kTraceWarning, kTraceTransport, -1, "gethostname failed");
return -1;
#else // WEBRTC_LINUX
int sockfd, size = 1;
struct ifreq* ifr;
struct ifconf ifc;
if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)))
{
return -1;
}
ifc.ifc_len = IFRSIZE;
ifc.ifc_req = NULL;
do
{
++size;
// Buffer size needed is unknown. Try increasing it until no overflow
// occurs.
if (NULL == (ifc.ifc_req = (ifreq*)realloc(ifc.ifc_req, IFRSIZE))) {
fprintf(stderr, "Out of memory.\n");
exit(EXIT_FAILURE);
}
ifc.ifc_len = IFRSIZE;
if (ioctl(sockfd, SIOCGIFCONF, &ifc))
{
free(ifc.ifc_req);
close(sockfd);
return -1;
}
} while (IFRSIZE <= ifc.ifc_len);
ifr = ifc.ifc_req;
for (;(char *) ifr < (char *) ifc.ifc_req + ifc.ifc_len; ++ifr)
{
if (ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data)
{
continue; // duplicate, skip it
}
if (ioctl(sockfd, SIOCGIFFLAGS, ifr))
{
continue; // failed to get flags, skip it
}
if(strncmp(ifr->ifr_name, "lo",3) == 0)
{
continue;
}else
{
struct sockaddr* saddr = &(ifr->ifr_addr);
SocketAddress* socket_addess = reinterpret_cast<SocketAddress*>(
saddr);
localIP = Htonl(socket_addess->_sockaddr_in.sin_addr);
close(sockfd);
free(ifc.ifc_req);
return 0;
}
}
free(ifc.ifc_req);
close(sockfd);
return -1;
#endif
}
int32_t UdpTransport::IPAddress(const SocketAddress& address,
char* ip,
uint32_t& ipSize,
uint16_t& sourcePort)
{
#if defined(_WIN32)
DWORD dwIPSize = ipSize;
int32_t returnvalue = WSAAddressToStringA((LPSOCKADDR)(&address),
sizeof(SocketAddress),
NULL,
ip,
&dwIPSize);
if(returnvalue == -1)
{
return -1;
}
uint16_t source_port = 0;
if(address._sockaddr_storage.sin_family == AF_INET)
{
// Parse IP assuming format "a.b.c.d:port".
char* ipEnd = strchr(ip,':');
if(ipEnd != NULL)
{
*ipEnd = '\0';
}
ipSize = (int32_t)strlen(ip);
if(ipSize == 0)
{
return -1;
}
source_port = address._sockaddr_in.sin_port;
}
else
{
// Parse IP assuming format "[address]:port".
char* ipEnd = strchr(ip,']');
if(ipEnd != NULL)
{
// Calculate length
int32_t adrSize = int32_t(ipEnd - ip) - 1;
memmove(ip, &ip[1], adrSize); // Remove '['
*(ipEnd - 1) = '\0';
}
ipSize = (int32_t)strlen(ip);
if(ipSize == 0)
{
return -1;
}
source_port = address._sockaddr_in6.sin6_port;
}
// Convert port number to network byte order.
sourcePort = htons(source_port);
return 0;
#elif defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
int32_t ipFamily = address._sockaddr_storage.sin_family;
const void* ptrNumericIP = NULL;
if(ipFamily == AF_INET)
{
ptrNumericIP = &(address._sockaddr_in.sin_addr);
}
else if(ipFamily == AF_INET6)
{
ptrNumericIP = &(address._sockaddr_in6.sin6_addr);
}
else
{
return -1;
}
if(inet_ntop(ipFamily, ptrNumericIP, ip, ipSize) == NULL)
{
return -1;
}
uint16_t source_port;
if(ipFamily == AF_INET)
{
source_port = address._sockaddr_in.sin_port;
} else
{
source_port = address._sockaddr_in6.sin6_port;
}
// Convert port number to network byte order.
sourcePort = htons(source_port);
return 0;
#else
return -1;
#endif
}
bool UdpTransport::IsIpAddressValid(const char* ipadr, const bool ipV6)
{
if(ipV6)
{
int32_t len = (int32_t)strlen(ipadr);
if( len>39 || len == 0)
{
return false;
}
int32_t i;
int32_t colonPos[7] = {0,0,0,0,0,0,0};
int32_t lastColonPos = -2;
int32_t nColons = 0;
int32_t nDubbleColons = 0;
int32_t nDots = 0;
int32_t error = 0;
char c;
for(i = 0; i < len ; i++)
{
c=ipadr[i];
if(isxdigit(c))
;
else if(c == ':')
{
if(nColons < 7)
colonPos[nColons] = i;
if((i-lastColonPos)==1)
nDubbleColons++;
lastColonPos=i;
if(nDots != 0)
{
error = 1;
}
nColons++;
}
else if(c == '.')
{
nDots++;
}
else
{
error = 1;
}
}
if(error)
{
return false;
}
if(nDubbleColons > 1)
{
return false;
}
if(nColons > 7 || nColons < 2)
{
return false;
}
if(!(nDots == 3 || nDots == 0))
{
return false;
}
lastColonPos = -1;
int32_t charsBeforeColon = 0;
for(i = 0; i < nColons; i++)
{
charsBeforeColon=colonPos[i]-lastColonPos-1;
if(charsBeforeColon > 4)
{
return false;
}
lastColonPos=colonPos[i];
}
int32_t lengthAfterLastColon = len - lastColonPos - 1;
if(nDots == 0)
{
if(lengthAfterLastColon > 4)
return false;
}
if(nDots == 3 && lengthAfterLastColon > 0)
{
return IsIpAddressValid((ipadr+lastColonPos+1),false);
}
}
else
{
int32_t len = (int32_t)strlen(ipadr);
if((len>15)||(len==0))
{
return false;
}
// IPv4 should be [0-255].[0-255].[0-255].[0-255]
int32_t i;
int32_t nDots = 0;
int32_t iDotPos[4] = {0,0,0,0};
for (i = 0; (i < len) && (nDots < 4); i++)
{
if (ipadr[i] == (char)'.')
{
// Store index of dots and count number of dots.
iDotPos[nDots++] = i;
}
else if (isdigit(ipadr[i]) == 0)
{
return false;
}
}
bool allUnder256 = false;
// TODO (hellner): while loop seems to be abused here to get
// label like functionality. Fix later to avoid introducing bugs now.
// Check that all numbers are smaller than 256.
do
{
if (nDots != 3 )
{
break;
}
if (iDotPos[0] <= 3)
{
char nr[4];
memset(nr,0,4);
strncpy(nr,&ipadr[0],iDotPos[0]);
int32_t num = atoi(nr);
if (num > 255 || num < 0)
{
break;
}
} else {
break;
}
if (iDotPos[1] - iDotPos[0] <= 4)
{
char nr[4];
memset(nr,0,4);
strncpy(nr,&ipadr[iDotPos[0]+1], iDotPos[1] - iDotPos[0] - 1);
int32_t num = atoi(nr);
if (num > 255 || num < 0)
break;
} else {
break;
}
if (iDotPos[2] - iDotPos[1] <= 4)
{
char nr[4];
memset(nr,0,4);
strncpy(nr,&ipadr[iDotPos[1]+1], iDotPos[2] - iDotPos[1] - 1);
int32_t num = atoi(nr);
if (num > 255 || num < 0)
break;
} else {
break;
}
if (len - iDotPos[2] <= 4)
{
char nr[4];
memset(nr,0,4);
strncpy(nr,&ipadr[iDotPos[2]+1], len - iDotPos[2] -1);
int32_t num = atoi(nr);
if (num > 255 || num < 0)
break;
else
allUnder256 = true;
} else {
break;
}
} while(false);
if (nDots != 3 || !allUnder256)
{
return false;
}
}
return true;
}
} // namespace test
} // namespace webrtc