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 1 commit
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
130 changes: 88 additions & 42 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,27 @@ 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 prupose of using it in the SDP c= header.
*/
Copy link
Member

Choose a reason for hiding this comment

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

Please don't use this format for comments. Align to what we use in other parts of the code, so something the lines of

/* Since ...
 * for the ... */

As a side notem there's a typo in "purpose".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The point taken.

Copy link
Member

Choose a reason for hiding this comment

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

The format of the comment is still incorrect.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sorry about that. Fixed.

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 +2044,24 @@ 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)) {
/* 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) {
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 +6516,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 +6550,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,14 +6652,27 @@ static int janus_sip_allocate_local_ports(janus_sip_session *session, gboolean u
session->media.pipefd[1] = -1;
}
}
/* Determine the address family to use */
if(ipv6_disabled &&
!janus_network_address_is_null(&janus_network_local_media_ip) &&
janus_network_local_media_ip.family == AF_INET6) {
Copy link
Member

Choose a reason for hiding this comment

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

Code style: you need another tab of indent for the two additional checks above, otherwise it ends up lined with the action below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Point taken. The condition is removed as redundant.

JANUS_LOG(LOG_ERR, "Error setting address for media sockets, IPv6 is disabled and local media address is IPv6...\n");
Copy link
Member

Choose a reason for hiding this comment

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

I don't like this action failing at runtime. It looks like a check that can be done when thew plugin is loaded, that is after the janus_network_local_media_ip property is filled in and we know if ipv6_disabled is FALSE. Better to have the plugin not load at all, if we already know it will never allocate sockets for media.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. Moved the failure to the config loading time.

return -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);
Copy link
Member

@lminiero lminiero Aug 29, 2023

Choose a reason for hiding this comment

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

Did you forget to switch to use_ipv6_address_family here? (and when creating the RTCP socket too, a few lines below). There's other checks that now use use_ipv6_address_family in this code block that should probably move to the other boolean, since it's that one that now dictates the addrlen.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Essentially !ipv6_disabled and use_ipv6_address_family have the same effect, replaced one with the other for clarity.

int v6only = 0;
Expand Down Expand Up @@ -6667,39 +6707,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,17 +6749,20 @@ 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);
pawnnail marked this conversation as resolved.
Show resolved Hide resolved
int v6only = 0;
Expand Down Expand Up @@ -6753,39 +6797,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 +6839,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