Skip to content

Commit

Permalink
Allow SIP plugin to bind media to a specific address (see #3248) (#3263)
Browse files Browse the repository at this point in the history
  • Loading branch information
pawnnail authored Nov 16, 2023
1 parent aed755a commit d9a5853
Showing 1 changed file with 92 additions and 50 deletions.
142 changes: 92 additions & 50 deletions src/plugins/janus_sip.c
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,7 @@ static gboolean ipv6_disabled = FALSE;
static janus_callbacks *gateway = NULL;

static char *local_ip = NULL, *sdp_ip = NULL, *local_media_ip = NULL;
static janus_network_address janus_network_local_media_ip = { 0 };
static int keepalive_interval = 120;
static gboolean behind_nat = FALSE;
static char *user_agent;
Expand Down Expand Up @@ -1882,11 +1883,11 @@ int janus_sip_init(janus_callbacks *callback, const char *config_path) {
}

item = janus_config_get(config, config_general, janus_config_type_item, "local_media_ip");
if(item && item->value)
if(item && item->value && strlen(item->value) > 0)
local_media_ip = g_strdup(item->value);

item = janus_config_get(config, config_general, janus_config_type_item, "sdp_ip");
if(item && item->value) {
if(item && item->value && strlen(item->value) > 0) {
sdp_ip = g_strdup(item->value);
JANUS_LOG(LOG_VERB, "IP to advertise in SDP: %s\n", sdp_ip);
}
Expand Down Expand Up @@ -2003,6 +2004,25 @@ int janus_sip_init(janus_callbacks *callback, const char *config_path) {
}
JANUS_LOG(LOG_VERB, "Local IP set to %s\n", local_ip);

/* Since we might have to derive SDP connection address from local_media_ip make sure it has a meaningful value
* for the purpose of using it in the SDP c= header */
janus_network_address_nullify(&janus_network_local_media_ip);
if(local_media_ip) {
if(janus_network_string_to_address(janus_network_query_options_any_ip, local_media_ip, &janus_network_local_media_ip) != 0) {
JANUS_LOG(LOG_ERR, "Invalid local media IP address [%s]...\n", local_media_ip);
return -1;
}
if((janus_network_local_media_ip.family == AF_INET && janus_network_local_media_ip.ipv4.s_addr == INADDR_ANY) ||
(janus_network_local_media_ip.family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED(&janus_network_local_media_ip.ipv6))) {
janus_network_address_nullify(&janus_network_local_media_ip);
}
}
JANUS_LOG(LOG_VERB, "Binding media address set to [%s]...\n", janus_network_address_is_null(&janus_network_local_media_ip) ? "any" : local_media_ip);
if(!sdp_ip) {
sdp_ip = janus_network_address_is_null(&janus_network_local_media_ip) ? local_ip : local_media_ip;
JANUS_LOG(LOG_VERB, "IP to advertise in SDP: %s\n", sdp_ip);
}

/* Setup sofia */
su_init();
if(notify_events && callback->events_is_enabled()) {
Expand All @@ -2022,19 +2042,29 @@ int janus_sip_init(janus_callbacks *callback, const char *config_path) {
/* This is the callback we'll need to invoke to contact the Janus core */
gateway = callback;

/* Finally, let's check if IPv6 is disabled, as we may need to know for RTP/RTCP sockets */
int fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if(fd <= 0) {
ipv6_disabled = TRUE;
} else {
int v6only = 0;
if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)) != 0)
if(janus_network_address_is_null(&janus_network_local_media_ip) ||
janus_network_local_media_ip.family == AF_INET6) {
/* Finally, let's check if IPv6 is disabled, as we may need to know for RTP/RTCP sockets */
int fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if(fd <= 0) {
ipv6_disabled = TRUE;
}
if(fd > 0)
close(fd);
if(ipv6_disabled) {
JANUS_LOG(LOG_WARN, "IPv6 disabled, will only use IPv4 for RTP/RTCP sockets (SIP)\n");
} else {
int v6only = 0;
if(setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)) != 0)
ipv6_disabled = TRUE;
}
if(fd > 0)
close(fd);
if(ipv6_disabled) {
if(!janus_network_address_is_null(&janus_network_local_media_ip)) {
JANUS_LOG(LOG_ERR, "IPv6 disabled and local media address is IPv6...\n");
return -1;
}
JANUS_LOG(LOG_WARN, "IPv6 disabled, will only use IPv4 for RTP/RTCP sockets (SIP)\n");
}
} else if(janus_network_local_media_ip.family == AF_INET) {
/* Disable if we have a specified IPv4 address for RTP/RTCP sockets */
ipv6_disabled = TRUE;
}

g_atomic_int_set(&initialized, 1);
Expand Down Expand Up @@ -6489,7 +6519,7 @@ char *janus_sip_sdp_manipulate(janus_sip_session *session, janus_sdp *sdp, gbool
JANUS_LOG(LOG_VERB, "Setting protocol to %s\n", session->media.require_srtp ? "RTP/SAVP" : "RTP/AVP");
if(sdp->c_addr) {
g_free(sdp->c_addr);
sdp->c_addr = g_strdup(sdp_ip ? sdp_ip : (local_media_ip ? local_media_ip : local_ip));
sdp->c_addr = g_strdup(sdp_ip);
}
int opusred_pt = answer ? janus_sdp_get_opusred_pt(sdp, -1) : -1;
GList *temp = sdp->m_lines;
Expand Down Expand Up @@ -6523,7 +6553,7 @@ char *janus_sip_sdp_manipulate(janus_sip_session *session, janus_sdp *sdp, gbool
}
}
g_free(m->c_addr);
m->c_addr = g_strdup(sdp_ip ? sdp_ip : (local_media_ip ? local_media_ip : local_ip));
m->c_addr = g_strdup(sdp_ip);
if(answer && (m->type == JANUS_SDP_AUDIO || m->type == JANUS_SDP_VIDEO)) {
/* Check which codec was negotiated eventually */
int pt = -1;
Expand Down Expand Up @@ -6625,18 +6655,24 @@ static int janus_sip_allocate_local_ports(janus_sip_session *session, gboolean u
session->media.pipefd[1] = -1;
}
}
gboolean use_ipv6_address_family = !ipv6_disabled &&
(janus_network_address_is_null(&janus_network_local_media_ip) || janus_network_local_media_ip.family == AF_INET6);
socklen_t addrlen = use_ipv6_address_family? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
/* Start */
int attempts = 100; /* FIXME Don't retry forever */
if(session->media.has_audio) {
JANUS_LOG(LOG_VERB, "Allocating audio ports:\n");
JANUS_LOG(LOG_VERB, "Allocating audio ports using address [%s]\n",
janus_network_address_is_null(&janus_network_local_media_ip) ? "any" : local_media_ip);
struct sockaddr_storage audio_rtp_address, audio_rtcp_address;
while(session->media.local_audio_rtp_port == 0 || session->media.local_audio_rtcp_port == 0) {
if(attempts == 0) /* Too many failures */
return -1;
memset(&audio_rtp_address, 0, sizeof(audio_rtp_address));
memset(&audio_rtcp_address, 0, sizeof(audio_rtcp_address));
if(session->media.audio_rtp_fd == -1) {
session->media.audio_rtp_fd = socket(!ipv6_disabled ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
session->media.audio_rtp_fd = socket(use_ipv6_address_family ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
int v6only = 0;
if(!ipv6_disabled && session->media.audio_rtp_fd != -1 &&
if(use_ipv6_address_family && session->media.audio_rtp_fd != -1 &&
setsockopt(session->media.audio_rtp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)) != 0) {
JANUS_LOG(LOG_WARN, "Error setting v6only to false on audio RTP socket (error=%s)\n",
g_strerror(errno));
Expand All @@ -6652,9 +6688,9 @@ static int janus_sip_allocate_local_ports(janus_sip_session *session, gboolean u
}
}
if(session->media.audio_rtcp_fd == -1) {
session->media.audio_rtcp_fd = socket(!ipv6_disabled ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
session->media.audio_rtcp_fd = socket(use_ipv6_address_family ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
int v6only = 0;
if(!ipv6_disabled && session->media.audio_rtcp_fd != -1 &&
if(use_ipv6_address_family && session->media.audio_rtcp_fd != -1 &&
setsockopt(session->media.audio_rtcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)) != 0) {
JANUS_LOG(LOG_WARN, "Error setting v6only to false on audio RTCP socket (error=%s)\n",
g_strerror(errno));
Expand All @@ -6667,39 +6703,40 @@ static int janus_sip_allocate_local_ports(janus_sip_session *session, gboolean u
int rtp_port = g_random_int_range(rtp_range_min, rtp_range_max);
if(rtp_port % 2)
rtp_port++; /* Pick an even port for RTP */
if(!ipv6_disabled) {
if(use_ipv6_address_family) {
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&audio_rtp_address;
addr->sin6_family = AF_INET6;
addr->sin6_port = htons(rtp_port);
addr->sin6_addr = in6addr_any;
addr->sin6_addr = janus_network_address_is_null(&janus_network_local_media_ip) ? in6addr_any : janus_network_local_media_ip.ipv6;
} else {
struct sockaddr_in *addr = (struct sockaddr_in *)&audio_rtp_address;
addr->sin_family = AF_INET;
addr->sin_port = htons(rtp_port);
addr->sin_addr.s_addr = INADDR_ANY;
addr->sin_addr.s_addr = janus_network_address_is_null(&janus_network_local_media_ip) ? INADDR_ANY : janus_network_local_media_ip.ipv4.s_addr;
}
if(bind(session->media.audio_rtp_fd, (struct sockaddr *)(&audio_rtp_address), sizeof(audio_rtp_address)) < 0) {
JANUS_LOG(LOG_ERR, "Bind failed for audio RTP (port %d), trying a different one...\n", rtp_port);
if(bind(session->media.audio_rtp_fd, (struct sockaddr *)(&audio_rtp_address), addrlen) < 0) {
JANUS_LOG(LOG_ERR, "Bind failed for audio RTP (port %d), error (%s), trying a different one...\n", rtp_port, g_strerror(errno));
close(session->media.audio_rtp_fd);
session->media.audio_rtp_fd = -1;
attempts--;
continue;
}
JANUS_LOG(LOG_VERB, "Audio RTP listener bound to %s:%d(%d)\n", (local_media_ip ? local_media_ip : local_ip), rtp_port, session->media.audio_rtp_fd);
JANUS_LOG(LOG_VERB, "Audio RTP listener bound to [%s]:%d(%d)\n",
janus_network_address_is_null(&janus_network_local_media_ip) ? "any" : local_media_ip, rtp_port, session->media.audio_rtp_fd);
int rtcp_port = rtp_port+1;
if(!ipv6_disabled) {
if(use_ipv6_address_family) {
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&audio_rtcp_address;
addr->sin6_family = AF_INET6;
addr->sin6_port = htons(rtcp_port);
addr->sin6_addr = in6addr_any;
addr->sin6_addr = janus_network_address_is_null(&janus_network_local_media_ip) ? in6addr_any : janus_network_local_media_ip.ipv6;
} else {
struct sockaddr_in *addr = (struct sockaddr_in *)&audio_rtcp_address;
addr->sin_family = AF_INET;
addr->sin_port = htons(rtcp_port);
addr->sin_addr.s_addr = INADDR_ANY;
addr->sin_addr.s_addr = janus_network_address_is_null(&janus_network_local_media_ip) ? INADDR_ANY : janus_network_local_media_ip.ipv4.s_addr;
}
if(bind(session->media.audio_rtcp_fd, (struct sockaddr *)(&audio_rtcp_address), sizeof(audio_rtcp_address)) < 0) {
JANUS_LOG(LOG_ERR, "Bind failed for audio RTCP (port %d), trying a different one...\n", rtcp_port);
if(bind(session->media.audio_rtcp_fd, (struct sockaddr *)(&audio_rtcp_address), addrlen) < 0) {
JANUS_LOG(LOG_ERR, "Bind failed for audio RTCP (port %d), error (%s), trying a different one...\n", rtcp_port, g_strerror(errno));
/* RTP socket is not valid anymore, reset it */
close(session->media.audio_rtp_fd);
session->media.audio_rtp_fd = -1;
Expand All @@ -6708,21 +6745,24 @@ static int janus_sip_allocate_local_ports(janus_sip_session *session, gboolean u
attempts--;
continue;
}
JANUS_LOG(LOG_VERB, "Audio RTCP listener bound to %s:%d(%d)\n", (local_media_ip ? local_media_ip : local_ip), rtcp_port, session->media.audio_rtcp_fd);
JANUS_LOG(LOG_VERB, "Audio RTCP listener bound to [%s]:%d(%d)\n", janus_network_address_is_null(&janus_network_local_media_ip) ?"any":local_media_ip, rtcp_port, session->media.audio_rtcp_fd);
session->media.local_audio_rtp_port = rtp_port;
session->media.local_audio_rtcp_port = rtcp_port;
}
}
if(session->media.has_video) {
JANUS_LOG(LOG_VERB, "Allocating video ports:\n");
JANUS_LOG(LOG_VERB, "Allocating video ports using address [%s]\n",
janus_network_address_is_null(&janus_network_local_media_ip ) ?"any" : local_media_ip);
struct sockaddr_storage video_rtp_address, video_rtcp_address;
while(session->media.local_video_rtp_port == 0 || session->media.local_video_rtcp_port == 0) {
if(attempts == 0) /* Too many failures */
return -1;
memset(&video_rtp_address, 0, sizeof(video_rtp_address));
memset(&video_rtcp_address, 0, sizeof(video_rtcp_address));
if(session->media.video_rtp_fd == -1) {
session->media.video_rtp_fd = socket(!ipv6_disabled ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
session->media.video_rtp_fd = socket(use_ipv6_address_family ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
int v6only = 0;
if(!ipv6_disabled && session->media.video_rtp_fd != -1 &&
if(use_ipv6_address_family && session->media.video_rtp_fd != -1 &&
setsockopt(session->media.video_rtp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)) != 0) {
JANUS_LOG(LOG_WARN, "Error setting v6only to false on video RTP socket (error=%s)\n",
g_strerror(errno));
Expand All @@ -6738,9 +6778,9 @@ static int janus_sip_allocate_local_ports(janus_sip_session *session, gboolean u
}
}
if(session->media.video_rtcp_fd == -1) {
session->media.video_rtcp_fd = socket(!ipv6_disabled ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
session->media.video_rtcp_fd = socket(use_ipv6_address_family ? AF_INET6 : AF_INET, SOCK_DGRAM, 0);
int v6only = 0;
if(!ipv6_disabled && session->media.video_rtcp_fd != -1 &&
if(use_ipv6_address_family && session->media.video_rtcp_fd != -1 &&
setsockopt(session->media.video_rtcp_fd, IPPROTO_IPV6, IPV6_V6ONLY, &v6only, sizeof(v6only)) != 0) {
JANUS_LOG(LOG_WARN, "Error setting v6only to false on video RTCP socket (error=%s)\n",
g_strerror(errno));
Expand All @@ -6753,39 +6793,40 @@ static int janus_sip_allocate_local_ports(janus_sip_session *session, gboolean u
int rtp_port = g_random_int_range(rtp_range_min, rtp_range_max);
if(rtp_port % 2)
rtp_port++; /* Pick an even port for RTP */
if(!ipv6_disabled) {
if(use_ipv6_address_family) {
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&video_rtp_address;
addr->sin6_family = AF_INET6;
addr->sin6_port = htons(rtp_port);
addr->sin6_addr = in6addr_any;
addr->sin6_addr = janus_network_address_is_null(&janus_network_local_media_ip) ? in6addr_any : janus_network_local_media_ip.ipv6;
} else {
struct sockaddr_in *addr = (struct sockaddr_in *)&video_rtp_address;
addr->sin_family = AF_INET;
addr->sin_port = htons(rtp_port);
addr->sin_addr.s_addr = INADDR_ANY;
addr->sin_addr.s_addr = janus_network_address_is_null(&janus_network_local_media_ip) ? INADDR_ANY : janus_network_local_media_ip.ipv4.s_addr;
}
if(bind(session->media.video_rtp_fd, (struct sockaddr *)(&video_rtp_address), sizeof(video_rtp_address)) < 0) {
JANUS_LOG(LOG_ERR, "Bind failed for video RTP (port %d), trying a different one...\n", rtp_port);
if(bind(session->media.video_rtp_fd, (struct sockaddr *)(&video_rtp_address), addrlen) < 0) {
JANUS_LOG(LOG_ERR, "Bind failed for video RTP (port %d), error (%s), trying a different one...\n", rtp_port, g_strerror(errno));
close(session->media.video_rtp_fd);
session->media.video_rtp_fd = -1;
attempts--;
continue;
}
JANUS_LOG(LOG_VERB, "Video RTP listener bound to %s:%d(%d)\n", (local_media_ip ? local_media_ip : local_ip), rtp_port, session->media.video_rtp_fd);
JANUS_LOG(LOG_VERB, "Video RTP listener bound to [%s]:%d(%d)\n",
janus_network_address_is_null(&janus_network_local_media_ip) ? "any" : local_media_ip, rtp_port, session->media.video_rtp_fd);
int rtcp_port = rtp_port+1;
if(!ipv6_disabled) {
if(use_ipv6_address_family) {
struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&video_rtcp_address;
addr->sin6_family = AF_INET6;
addr->sin6_port = htons(rtcp_port);
addr->sin6_addr = in6addr_any;
addr->sin6_addr = janus_network_address_is_null(&janus_network_local_media_ip) ? in6addr_any : janus_network_local_media_ip.ipv6;
} else {
struct sockaddr_in *addr = (struct sockaddr_in *)&video_rtcp_address;
addr->sin_family = AF_INET;
addr->sin_port = htons(rtcp_port);
addr->sin_addr.s_addr = INADDR_ANY;
addr->sin_addr.s_addr = janus_network_address_is_null(&janus_network_local_media_ip) ? INADDR_ANY : janus_network_local_media_ip.ipv4.s_addr;
}
if(bind(session->media.video_rtcp_fd, (struct sockaddr *)(&video_rtcp_address), sizeof(video_rtcp_address)) < 0) {
JANUS_LOG(LOG_ERR, "Bind failed for video RTCP (port %d), trying a different one...\n", rtcp_port);
if(bind(session->media.video_rtcp_fd, (struct sockaddr *)(&video_rtcp_address), addrlen) < 0) {
JANUS_LOG(LOG_ERR, "Bind failed for video RTCP (port %d), error (%s), trying a different one...\n", rtcp_port, g_strerror(errno));
/* RTP socket is not valid anymore, reset it */
close(session->media.video_rtp_fd);
session->media.video_rtp_fd = -1;
Expand All @@ -6794,7 +6835,8 @@ static int janus_sip_allocate_local_ports(janus_sip_session *session, gboolean u
attempts--;
continue;
}
JANUS_LOG(LOG_VERB, "Video RTCP listener bound to %s:%d(%d)\n", (local_media_ip ? local_media_ip : local_ip), rtcp_port, session->media.video_rtcp_fd);
JANUS_LOG(LOG_VERB, "Video RTCP listener bound to [%s]:%d(%d)\n",
janus_network_address_is_null(&janus_network_local_media_ip) ? "any" : local_media_ip, rtcp_port, session->media.video_rtcp_fd);
session->media.local_video_rtp_port = rtp_port;
session->media.local_video_rtcp_port = rtcp_port;
}
Expand Down

0 comments on commit d9a5853

Please sign in to comment.