Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow SIP plugin to bind media to a specific address (see #3248) #3263

Merged
merged 6 commits into from
Nov 16, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
pawnnail marked this conversation as resolved.
Show resolved Hide resolved
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some weird spaces in here. Per the code style, it should be

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