Skip to content

Transport interface: force socket binding to network interface #217

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
8 changes: 8 additions & 0 deletions libsofia-sip-ua/tport/sofia-sip/tport_tag.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ TPORT_DLL extern tag_typedef_t tptag_socket_keepalive;
TPORT_DLL extern tag_typedef_t tptag_socket_keepalive_ref;
#define TPTAG_SOCKET_KEEPALIVE_REF(x) tptag_socket_keepalive_ref, tag_uint_vr(&(x))

#if defined (__linux__)
TPORT_DLL extern tag_typedef_t tptag_socket_bind_ifc;
#define TPTAG_SOCKET_BIND_IFC(x) tptag_socket_bind_ifc, tag_bool_v((x))

TPORT_DLL extern tag_typedef_t tptag_socket_bind_ifc_ref;
#define TPTAG_SOCKET_BIND_IFC_REF(x) tptag_socket_bind_ifc_ref, tag_bool_vr(&(x))
#endif

TPORT_DLL extern tag_typedef_t tptag_keepalive;
#define TPTAG_KEEPALIVE(x) tptag_keepalive, tag_uint_v((x))

Expand Down
64 changes: 64 additions & 0 deletions libsofia-sip-ua/tport/tport.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ typedef struct tport_nat_s tport_nat_t;
#include <sofia-sip/rbtree.h>

#include "tport_internal.h"
#if HAVE_GETIFADDRS && defined (__linux__)
#include <ifaddrs.h>
#if HAVE_NET_IF_H
#include <net/if.h>
#endif
#include <sys/ioctl.h>
#endif

#if HAVE_FUNC
#elif HAVE_FUNCTION
Expand Down Expand Up @@ -513,6 +520,9 @@ tport_t *tport_tcreate(tp_stack_t *stack,
tpp->tpp_timeout = UINT_MAX;
tpp->tpp_sigcomp_lifetime = UINT_MAX;
tpp->tpp_socket_keepalive = 30;
#if defined (__linux__)
tpp->tpp_socket_bind_ifc = 0;
#endif
tpp->tpp_keepalive = 0;
tpp->tpp_pingpong = 0;
tpp->tpp_pong2ping = 0;
Expand Down Expand Up @@ -800,6 +810,50 @@ int tport_bind_socket(int socket,
return 0;
}

#if HAVE_GETIFADDRS && defined (__linux__)
int tport_bind_socket_iface(int s,
su_addrinfo_t *ai,
char const **return_culprit)
{
su_sockaddr_t *su = (su_sockaddr_t *)ai->ai_addr;
struct ifaddrs *addrs, *iap;
struct sockaddr_in *sa;
struct ifreq ifr;
char ipaddr[SU_ADDRSIZE + 2];

if (getifaddrs(&addrs) == 0) {
for (iap = addrs; iap != NULL; iap = iap->ifa_next) {
if (iap->ifa_addr && (iap->ifa_flags & IFF_UP) && iap->ifa_addr->sa_family == su->su_family) {
sa = (struct sockaddr_in *)(iap->ifa_addr);
if(sa->sin_addr.s_addr == su->su_sin.sin_addr.s_addr) {
memset(&ifr, 0, sizeof(struct ifreq));
strncpy(ifr.ifr_name, (char const *) iap->ifa_name, IFNAMSIZ);

/* Assign socket to an already active access point (interface) */
ioctl(s, SIOCSIFNAME, &ifr);
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
SU_DEBUG_3(("socket: %d setsockopt(SO_BINDTODEVICE) error binding to ifc %s: %s\n",
s, ifr.ifr_name, su_strerror(su_errno())));
freeifaddrs(addrs);
return *return_culprit = "setsockopt", -1;
}
SU_DEBUG_9(("socket: %d, bound %s to ifc: %s\n", s,
su_inet_ntop(su->su_family, SU_ADDR(su), ipaddr, sizeof(ipaddr)),
ifr.ifr_name));
freeifaddrs(addrs);
return 0;
}
}
}
freeifaddrs(addrs);

SU_DEBUG_3(("socket: %d: did not find ifc to bind %s\n",
s, su_inet_ntop(su->su_family, SU_ADDR(su), ipaddr, sizeof(ipaddr))));
/* Technically it's not a "failure" */
}
return 0;
}
#endif

/** Indicate stack that a transport has been updated */
void tport_has_been_updated(tport_t *self)
Expand Down Expand Up @@ -1223,6 +1277,9 @@ int tport_get_params(tport_t const *self,
TPTAG_IDLE(tpp->tpp_idle),
TPTAG_TIMEOUT(tpp->tpp_timeout),
TPTAG_SOCKET_KEEPALIVE(tpp->tpp_socket_keepalive),
#if defined (__linux__)
TPTAG_SOCKET_BIND_IFC(tpp->tpp_socket_bind_ifc),
#endif
TPTAG_KEEPALIVE(tpp->tpp_keepalive),
TPTAG_PINGPONG(tpp->tpp_pingpong),
TPTAG_PONG2PING(tpp->tpp_pong2ping),
Expand Down Expand Up @@ -1268,6 +1325,9 @@ int tport_set_params(tport_t *self,

usize_t mtu;
int connect, sdwn_error, reusable, stun_server, pong2ping;
#if defined (__linux__)
int socket_ifc;
#endif

if (self == NULL)
return su_seterrno(EINVAL);
Expand All @@ -1280,6 +1340,7 @@ int tport_set_params(tport_t *self,
reusable = self->tp_reusable;
stun_server = tpp->tpp_stun_server;
pong2ping = tpp->tpp_pong2ping;
socket_ifc = tpp->tpp_socket_bind_ifc;

ta_start(ta, tag, value);

Expand All @@ -1289,6 +1350,9 @@ int tport_set_params(tport_t *self,
TPTAG_IDLE_REF(tpp->tpp_idle),
TPTAG_TIMEOUT_REF(tpp->tpp_timeout),
TPTAG_SOCKET_KEEPALIVE_REF(tpp->tpp_socket_keepalive),
#if defined (__linux__)
TPTAG_SOCKET_BIND_IFC_REF(socket_ifc),
#endif
TPTAG_KEEPALIVE_REF(tpp->tpp_keepalive),
TPTAG_PINGPONG_REF(tpp->tpp_pingpong),
TPTAG_PONG2PING_REF(pong2ping),
Expand Down
8 changes: 8 additions & 0 deletions libsofia-sip-ua/tport/tport_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ typedef struct {
unsigned tpp_sdwn_error:1; /**< If true, shutdown is error. */
unsigned tpp_stun_server:1; /**< If true, use stun server */
unsigned tpp_pong2ping:1; /**< If true, respond with pong to ping */
#if defined (__linux__)
unsigned tpp_socket_bind_ifc:1; /**< If true, force socket bind to the interface */
#endif

unsigned :0;

Expand Down Expand Up @@ -440,6 +443,11 @@ void tport_base_timer(tport_t *self, su_time_t now);
int tport_bind_socket(int socket,
su_addrinfo_t *ai,
char const **return_culprit);
#if HAVE_GETIFADDRS && defined (__linux__)
int tport_bind_socket_iface(int socket,
su_addrinfo_t *ai,
char const **return_culprit);
#endif
void tport_close(tport_t *self);
int tport_shutdown0(tport_t *self, int how);

Expand Down
15 changes: 15 additions & 0 deletions libsofia-sip-ua/tport/tport_tag.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,21 @@ tag_typedef_t tptag_pingpong = UINTTAG_TYPEDEF(pingpong);
*/
tag_typedef_t tptag_pong2ping = BOOLTAG_TYPEDEF(pong2ping);

#if defined (__linux__)
/**@def TPTAG_SOCKET_BIND_IFC(x)
*
* Explicit socket binding to network interface (Linux only).
*
* If true, perform a setsockopt() SO_BINDTODEVICE.
*
* This is to get around an issue in certain Linux kernel whereas in a
* multi-homed environment, a socket might bind to the wrong (primary)
* network interface rather than the intended ifc.
*
*/
tag_typedef_t tptag_socket_bind_ifc = BOOLTAG_TYPEDEF(socket_bind_ifc);
#endif

/**@def TPTAG_SIGCOMP_LIFETIME(x)
*
* Default SigComp lifetime in seconds.
Expand Down
16 changes: 16 additions & 0 deletions libsofia-sip-ua/tport/tport_type_tcp.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ int tport_stream_init_primary(tport_primary_t *pri,
tagi_t const *tags,
char const **return_culprit)
{
#if defined (__linux__)
int socket_bind_ifc = 0;
#endif
pri->pri_primary->tp_socket = socket;

/* Set IP TOS if set */
Expand All @@ -152,6 +155,19 @@ int tport_stream_init_primary(tport_primary_t *pri,
if (tport_bind_socket(socket, ai, return_culprit) == -1)
return -1;

#if HAVE_GETIFADDRS && defined (__linux__)
tl_gets(tags,
TPTAG_SOCKET_BIND_IFC_REF(socket_bind_ifc),
TAG_END());
/* Force socket binding
*
* On Linux 5.x kernel for multi-homed environment
*/
if (socket_bind_ifc == 1 && tport_bind_socket_iface(socket, ai, return_culprit) == -1) {
return -1;
}
#endif

if (listen(socket, pri->pri_params->tpp_qsize) == SOCKET_ERROR)
return *return_culprit = "listen", -1;

Expand Down
16 changes: 16 additions & 0 deletions libsofia-sip-ua/tport/tport_type_udp.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ int tport_udp_init_primary(tport_primary_t *pri,
su_sockaddr_t *su = (su_sockaddr_t *)ai->ai_addr;
#endif
int const one = 1; (void)one;
#if defined (__linux__)
int socket_bind_ifc = 0;
#endif

s = su_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (s == INVALID_SOCKET)
Expand All @@ -142,6 +145,19 @@ int tport_udp_init_primary(tport_primary_t *pri,
if (tport_bind_socket(s, ai, return_culprit) < 0)
return -1;

#if HAVE_GETIFADDRS && defined (__linux__)
tl_gets(tags,
TPTAG_SOCKET_BIND_IFC_REF(socket_bind_ifc),
TAG_END());
/* Force socket binding
*
* On Linux 5.x kernel for multi-homed environment
*/
if (socket_bind_ifc == 1 && tport_bind_socket_iface(s, ai, return_culprit) == -1) {
return -1;
}
#endif

tport_set_tos(s, ai, pri->pri_params->tpp_tos);

#if HAVE_IP_ADD_MEMBERSHIP
Expand Down