Commit 2f7f75fa authored by Ming Rui Zhang's avatar Ming Rui Zhang Committed by Adrien Béraud

sip: add sip_bind_address parameter

- provide the user the ability to bind ip addresses for sip/IPtoIP account
  freely, IPtoIP will be given more freedom to switch between ipv4 and ipv6
  according to the parameter given

Change-Id: I364ad8a2acab4480a35a87baf3f7eb8e7ac72b36
parent c9470384
......@@ -38,6 +38,7 @@
<li>REALM</li>
<li>CONFIG_ACCOUNT_MAILBOX: Number to dial to access the voicemail box</li>
<li>CONFIG_ACCOUNT_REGISTRATION_EXPIRE: SIP header expiration value (Default: 1600)</li>
<li>BIND_ADDRESS: Custom bind SIP ip address</li>
<li>LOCAL_INTERFACE: The network interface (Default: eth0)</li>
<li>PUBLISHED_SAMEAS_LOCAL: If False, the published address equals the local address. This is the default.</li>
<li>PUBLISHED_ADDRESS: The SIP published address</li>
......
......@@ -60,6 +60,7 @@ static const char *const CONFIG_ACCOUNT_AUDIO_PORT_MAX = "Account.audio
static const char *const CONFIG_ACCOUNT_VIDEO_PORT_MIN = "Account.videoPortMin";
static const char *const CONFIG_ACCOUNT_VIDEO_PORT_MAX = "Account.videoPortMax";
static const char *const CONFIG_BIND_ADDRESS = "Account.bindAddress";
static const char *const CONFIG_LOCAL_INTERFACE = "Account.localInterface";
static const char *const CONFIG_PUBLISHED_SAMEAS_LOCAL = "Account.publishedSameAsLocal";
static const char *const CONFIG_LOCAL_PORT = "Account.localPort";
......
......@@ -118,6 +118,7 @@ constexpr static const char AUTOANSWER [] = "Account.autoAnswer";
constexpr static const char ACTIVE_CALL_LIMIT [] = "Account.activeCallLimit";
constexpr static const char HOSTNAME [] = "Account.hostname";
constexpr static const char USERNAME [] = "Account.username";
constexpr static const char BIND_ADDRESS [] = "Account.bindAddress";
constexpr static const char ROUTE [] = "Account.routeset";
constexpr static const char PASSWORD [] = "Account.password";
constexpr static const char REALM [] = "Account.realm";
......
......@@ -404,6 +404,7 @@ void SIPAccount::serialize(YAML::Emitter &out) const
out << YAML::BeginMap;
SIPAccountBase::serialize(out);
out << YAML::Key << Conf::BIND_ADDRESS_KEY << YAML::Value << bindAddress_;
out << YAML::Key << Conf::PORT_KEY << YAML::Value << localPort_;
out << YAML::Key << USERNAME_KEY << YAML::Value << username_;
......@@ -479,6 +480,8 @@ void SIPAccount::unserialize(const YAML::Node &node)
if (not publishedSameasLocal_)
usePublishedAddressPortInVIA();
parseValue(node, Conf::BIND_ADDRESS_KEY, bindAddress_);
int port = sip_utils::DEFAULT_SIP_PORT;
parseValue(node, Conf::PORT_KEY, port);
localPort_ = port;
......@@ -562,6 +565,7 @@ void SIPAccount::setAccountDetails(const std::map<std::string, std::string> &det
parseString(details, Conf::CONFIG_TLS_PASSWORD, tlsPassword_);
// SIP specific account settings
parseString(details, Conf::CONFIG_BIND_ADDRESS, bindAddress_);
parseString(details, Conf::CONFIG_ACCOUNT_ROUTESET, serviceRoute_);
if (not publishedSameasLocal_)
......@@ -629,6 +633,7 @@ SIPAccount::getAccountDetails() const
}
a.emplace(Conf::CONFIG_ACCOUNT_PASSWORD, std::move(password));
a.emplace(Conf::CONFIG_BIND_ADDRESS, bindAddress_);
a.emplace(Conf::CONFIG_LOCAL_PORT, std::to_string(localPort_));
a.emplace(Conf::CONFIG_ACCOUNT_ROUTESET, serviceRoute_);
a.emplace(Conf::CONFIG_ACCOUNT_REGISTRATION_EXPIRE, std::to_string(registrationExpire_));
......@@ -771,18 +776,22 @@ void SIPAccount::doRegister1_()
void SIPAccount::doRegister2_()
{
bool ipv6 = false;
if (isIP2IP()) {
JAMI_DBG("doRegister isIP2IP.");
ipv6 = ip_utils::getInterfaceAddr(interface_).isIpv6();
} else if (!hostIp_) {
if (not isIP2IP() and not hostIp_) {
setRegistrationState(RegistrationState::ERROR_GENERIC, PJSIP_SC_NOT_FOUND);
JAMI_ERR("Hostname not resolved.");
return;
} else {
ipv6 = hostIp_.isIpv6();
}
IpAddr bindAddress = createBindingAddress();
if (not bindAddress) {
setRegistrationState(RegistrationState::ERROR_GENERIC, PJSIP_SC_NOT_FOUND);
JAMI_ERR("Can't compute address to bind.");
return;
}
bool ipv6 = bindAddress.isIpv6();
transportType_ = tlsEnable_ ? (ipv6 ? PJSIP_TRANSPORT_TLS6 : PJSIP_TRANSPORT_TLS) : (ipv6 ? PJSIP_TRANSPORT_UDP6 : PJSIP_TRANSPORT_UDP);
// Init TLS settings if the user wants to use TLS
if (tlsEnable_) {
JAMI_DBG("TLS is enabled for account %s", accountID_.c_str());
......@@ -790,14 +799,10 @@ void SIPAccount::doRegister2_()
// Dropping current calls already using the transport is currently required
// with TLS.
freeAccount();
transportType_ = ipv6 ? PJSIP_TRANSPORT_TLS6 : PJSIP_TRANSPORT_TLS;
initTlsConfiguration();
if (!tlsListener_) {
tlsListener_ = link_->sipTransportBroker->getTlsListener(
SipTransportDescr {getTransportType(), getTlsListenerPort(), getLocalInterface()},
getTlsSetting());
tlsListener_ = link_->sipTransportBroker->getTlsListener(bindAddress, getTlsSetting());
if (!tlsListener_) {
setRegistrationState(RegistrationState::ERROR_GENERIC);
JAMI_ERR("Error creating TLS listener.");
......@@ -806,7 +811,6 @@ void SIPAccount::doRegister2_()
}
} else {
tlsListener_.reset();
transportType_ = ipv6 ? PJSIP_TRANSPORT_UDP6 : PJSIP_TRANSPORT_UDP;
}
// Init STUN settings for this account if the user selected it
......@@ -819,10 +823,9 @@ void SIPAccount::doRegister2_()
// no registration should be performed
if (isIP2IP()) {
// If we use Tls for IP2IP, transports will be created on connection.
if (!tlsEnable_)
setTransport(link_->sipTransportBroker->getUdpTransport(
SipTransportDescr { getTransportType(), getLocalPort(), getLocalInterface() }
));
if (!tlsEnable_){
setTransport(link_->sipTransportBroker->getUdpTransport(bindAddress));
}
setRegistrationState(RegistrationState::REGISTERED);
return;
}
......@@ -833,9 +836,7 @@ void SIPAccount::doRegister2_()
if (isTlsEnabled()) {
setTransport(link_->sipTransportBroker->getTlsTransport(tlsListener_, hostIp_, tlsServerName_.empty() ? hostname_ : tlsServerName_));
} else {
setTransport(link_->sipTransportBroker->getUdpTransport(
SipTransportDescr { getTransportType(), getLocalPort(), getLocalInterface() }
));
setTransport(link_->sipTransportBroker->getUdpTransport(bindAddress));
}
if (!transport_)
throw VoipLinkException("Can't create transport");
......@@ -2203,4 +2204,22 @@ SIPAccount::getUserUri() const
return getFromUri();
}
IpAddr
SIPAccount::createBindingAddress()
{
auto family = hostIp_ ? hostIp_.getFamily() : PJ_AF_INET;
IpAddr ret = bindAddress_.empty()
? (interface_ == ip_utils::DEFAULT_INTERFACE
? ip_utils::getAnyHostAddr(family)
: ip_utils::getInterfaceAddr(getLocalInterface(), family))
: IpAddr(bindAddress_, family);
if (ret.getPort() == 0) {
ret.setPort(tlsEnable_ ? getTlsListenerPort() : getLocalPort());
}
return ret;
}
} // namespace jami
......@@ -293,6 +293,24 @@ class SIPAccount : public SIPAccountBase {
localPort_ = port;
}
/**
* Get the bind ip address on which the account should use, or is
* actually using.
* Note: if it is NULL, this address should not be used
* @return std::string The bind ip address used for that account
*/
std::string getBindAddress() const {
return bindAddress_;
}
/**
* Set the new bind ip address on which this account is bind on.
* @pram address The bind ip address used by this account.
*/
void setBindAddress(const std::string &address) {
bindAddress_ = address;
}
/**
* @return pjsip_tls_setting structure, filled from the configuration
* file, that can be used directly by PJSIP to initialize
......@@ -509,6 +527,12 @@ class SIPAccount : public SIPAccountBase {
std::string getUserUri() const override;
/**
* Create the Ip address that the transport uses
* @return IpAddr created
*/
IpAddr createBindingAddress();
private:
void doRegister1_();
void doRegister2_();
......@@ -680,6 +704,11 @@ class SIPAccount : public SIPAccountBase {
*/
pj_uint16_t localPort_ {sip_utils::DEFAULT_SIP_PORT};
/**
* Potential ip addresss on which this account is bound
*/
std::string bindAddress_;
/**
* The TLS listener port
*/
......
......@@ -54,6 +54,7 @@ namespace jami {
namespace Conf {
// SIP specific configuration keys
const char *const BIND_ADDRESS_KEY = "bindAddress";
const char *const INTERFACE_KEY = "interface";
const char *const PORT_KEY = "port";
const char *const PUBLISH_ADDR_KEY = "publishAddr";
......
......@@ -55,14 +55,6 @@ constexpr const char* TRANSPORT_STATE_STR[] = {
};
constexpr const size_t TRANSPORT_STATE_SZ = arraySize(TRANSPORT_STATE_STR);
std::string
SipTransportDescr::toString() const
{
std::stringstream ss;
ss << "{" << pjsip_transport_get_type_desc(type) << " on " << interface << ":" << listenerPort << "}";
return ss.str();
}
void
SipTransport::deleteTransport(pjsip_transport* t)
{
......@@ -253,7 +245,7 @@ SipTransportBroker::transportStateChanged(pjsip_transport* tp,
if (type == PJSIP_TRANSPORT_UDP or type == PJSIP_TRANSPORT_UDP6) {
const auto updKey = std::find_if(
udpTransports_.cbegin(), udpTransports_.cend(),
[tp](const std::pair<SipTransportDescr, pjsip_transport*>& pair) {
[tp](const std::pair<IpAddr, pjsip_transport*>& pair) {
return pair.second == tp;
});
if (updKey != udpTransports_.cend())
......@@ -303,79 +295,66 @@ SipTransportBroker::shutdown()
}
std::shared_ptr<SipTransport>
SipTransportBroker::getUdpTransport(const SipTransportDescr& descr)
SipTransportBroker::getUdpTransport(const IpAddr& ipAddress)
{
std::lock_guard<std::mutex> lock(transportMapMutex_);
auto itp = udpTransports_.find(descr);
auto itp = udpTransports_.find(ipAddress);
if (itp != udpTransports_.end()) {
auto it = transports_.find(itp->second);
if (it != transports_.end()) {
if (auto spt = it->second.lock()) {
JAMI_DBG("Reusing transport %s", descr.toString().c_str());
JAMI_DBG("Reusing transport %s", ipAddress.toString().c_str());
return spt;
}
else {
// Transport still exists but have not been destroyed yet.
JAMI_WARN("Recycling transport %s", descr.toString().c_str());
JAMI_WARN("Recycling transport %s", ipAddress.toString().c_str());
auto ret = std::make_shared<SipTransport>(itp->second);
it->second = ret;
return ret;
}
} else {
JAMI_WARN("Cleaning up UDP transport %s", descr.toString().c_str());
JAMI_WARN("Cleaning up UDP transport %s", ipAddress.toString().c_str());
udpTransports_.erase(itp);
}
}
auto ret = createUdpTransport(descr);
auto ret = createUdpTransport(ipAddress);
if (ret) {
udpTransports_[descr] = ret->get();
udpTransports_[ipAddress] = ret->get();
transports_[ret->get()] = ret;
}
return ret;
}
std::shared_ptr<SipTransport>
SipTransportBroker::createUdpTransport(const SipTransportDescr& d)
SipTransportBroker::createUdpTransport(const IpAddr& ipAddress)
{
RETURN_IF_FAIL(d.listenerPort != 0, nullptr, "Could not determine port for this transport");
auto family = pjsip_transport_type_get_af(d.type);
IpAddr listeningAddress = (d.interface == ip_utils::DEFAULT_INTERFACE) ?
ip_utils::getAnyHostAddr(family) :
ip_utils::getInterfaceAddr(d.interface, family);
listeningAddress.setPort(d.listenerPort);
RETURN_IF_FAIL(listeningAddress, nullptr, "Could not determine IP address for this transport");
RETURN_IF_FAIL(ipAddress.getPort() != 0, nullptr, "Could not determine port for this transport");
RETURN_IF_FAIL(ipAddress, nullptr, "Could not determine IP address for this transport");
pjsip_udp_transport_cfg pj_cfg;
pjsip_udp_transport_cfg_default(&pj_cfg, family);
pj_cfg.bind_addr = listeningAddress;
pjsip_udp_transport_cfg_default(&pj_cfg, ipAddress.getFamily());
pj_cfg.bind_addr = ipAddress;
pjsip_transport *transport = nullptr;
if (pj_status_t status = pjsip_udp_transport_start2(endpt_, &pj_cfg, &transport)) {
JAMI_ERR("pjsip_udp_transport_start2 failed with error %d: %s", status,
sip_utils::sip_strerror(status).c_str());
JAMI_ERR("UDP IPv%s Transport did not start on %s",
listeningAddress.isIpv4() ? "4" : "6",
listeningAddress.toString(true).c_str());
ipAddress.isIpv4() ? "4" : "6",
ipAddress.toString(true).c_str());
return nullptr;
}
JAMI_DBG("Created UDP transport on %s : %s", d.interface.c_str(), listeningAddress.toString(true).c_str());
JAMI_DBG("Created UDP transport on address %s", ipAddress.toString(true).c_str());
return std::make_shared<SipTransport>(transport);
}
std::shared_ptr<TlsListener>
SipTransportBroker::getTlsListener(const SipTransportDescr& d, const pjsip_tls_setting* settings)
SipTransportBroker::getTlsListener(const IpAddr& ipAddress, const pjsip_tls_setting* settings)
{
RETURN_IF_FAIL(settings, nullptr, "TLS settings not specified");
auto family = pjsip_transport_type_get_af(d.type);
IpAddr listeningAddress = (d.interface == ip_utils::DEFAULT_INTERFACE) ?
ip_utils::getAnyHostAddr(family) :
ip_utils::getInterfaceAddr(d.interface, family);
listeningAddress.setPort(d.listenerPort);
RETURN_IF_FAIL(listeningAddress, nullptr, "Could not determine IP address for this transport");
JAMI_DBG("Creating TLS listener %s on %s...", d.toString().c_str(), listeningAddress.toString(true).c_str());
RETURN_IF_FAIL(ipAddress, nullptr, "Could not determine IP address for this transport");
JAMI_DBG("Creating TLS listener on %s...", ipAddress.toString(true).c_str());
#if 0
JAMI_DBG(" ca_list_file : %s", settings->ca_list_file.ptr);
JAMI_DBG(" cert_file : %s", settings->cert_file.ptr);
......@@ -385,7 +364,7 @@ SipTransportBroker::getTlsListener(const SipTransportDescr& d, const pjsip_tls_s
#endif
pjsip_tpfactory *listener = nullptr;
const pj_status_t status = pjsip_tls_transport_start2(endpt_, settings, listeningAddress.pjPtr(), nullptr, 1, &listener);
const pj_status_t status = pjsip_tls_transport_start2(endpt_, settings, ipAddress.pjPtr(), nullptr, 1, &listener);
if (status != PJ_SUCCESS) {
JAMI_ERR("TLS listener did not start: %s", sip_utils::sip_strerror(status).c_str());
return nullptr;
......@@ -406,6 +385,7 @@ SipTransportBroker::getTlsTransport(const std::shared_ptr<TlsListener>& l, const
pjsip_tpselector sel;
sel.type = PJSIP_TPSELECTOR_LISTENER;
sel.u.listener = l->get();
sel.disable_connection_reuse = PJ_FALSE;
pjsip_tx_data tx_data;
tx_data.dest_info.name = pj_str_t{(char*)remote_name.data(), (pj_ssize_t)remote_name.size()};
......
......@@ -50,37 +50,6 @@ struct Certificate;
namespace jami {
struct SipTransportDescr
{
SipTransportDescr() {}
SipTransportDescr(pjsip_transport_type_e t)
: type(t), listenerPort(pjsip_transport_get_default_port_for_type(t)) {}
SipTransportDescr(pjsip_transport_type_e t, pj_uint16_t port, const std::string& i)
: type(t), listenerPort(port), interface(i) {}
static inline pjsip_transport_type_e actualType(pjsip_transport_type_e t) {
return (t == PJSIP_TRANSPORT_START_OTHER) ? PJSIP_TRANSPORT_UDP : t;
}
inline bool operator==(SipTransportDescr const& o) const {
return actualType(type) == actualType(o.type)
&& listenerPort == o.listenerPort
&& interface == o.interface;
}
inline bool operator<(SipTransportDescr const& o) const {
return actualType(type) < actualType(o.type)
|| listenerPort < o.listenerPort
|| std::hash<std::string>()(interface) < std::hash<std::string>()(o.interface);
}
std::string toString() const;
pjsip_transport_type_e type {PJSIP_TRANSPORT_UNSPECIFIED};
pj_uint16_t listenerPort {sip_utils::DEFAULT_SIP_PORT};
std::string interface {"default"};
};
struct TlsListener
{
TlsListener() {}
......@@ -175,10 +144,10 @@ public:
SipTransportBroker(pjsip_endpoint *endpt, pj_caching_pool& cp, pj_pool_t& pool);
~SipTransportBroker();
std::shared_ptr<SipTransport> getUdpTransport(const SipTransportDescr&);
std::shared_ptr<SipTransport> getUdpTransport(const IpAddr&);
std::shared_ptr<TlsListener>
getTlsListener(const SipTransportDescr&, const pjsip_tls_setting*);
getTlsListener(const IpAddr&, const pjsip_tls_setting*);
std::shared_ptr<SipTransport>
getTlsTransport(const std::shared_ptr<TlsListener>&, const IpAddr& remote, const std::string& remote_name = {});
......@@ -205,7 +174,7 @@ private:
* @param IP protocol version to use, can be pj_AF_INET() or pj_AF_INET6()
* @return a pointer to the new transport
*/
std::shared_ptr<SipTransport> createUdpTransport(const SipTransportDescr&);
std::shared_ptr<SipTransport> createUdpTransport(const IpAddr&);
/**
* List of transports so we can bubble the events up.
......@@ -217,7 +186,7 @@ private:
* Transports are stored in this map in order to retrieve them in case
* several accounts would share the same port number.
*/
std::map<SipTransportDescr, pjsip_transport*> udpTransports_;
std::map<IpAddr, pjsip_transport*> udpTransports_;
/**
* Storage for SIP/ICE transport instances.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment