Skip to content

Commit

Permalink
Transport: Add TPTAG_SOCKET_BIND_IFC to optionally force socket binding
Browse files Browse the repository at this point in the history
  • Loading branch information
pdepaulis committed Jul 18, 2023
1 parent 43d6d21 commit ca9c27e
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 38 deletions.
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
79 changes: 45 additions & 34 deletions libsofia-sip-ua/tport/tport.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ typedef struct tport_nat_s tport_nat_t;
#include <sofia-sip/rbtree.h>

#include "tport_internal.h"
#if defined (__linux__)
#if HAVE_GETIFADDRS && defined (__linux__)
#include <ifaddrs.h>
#if HAVE_NET_IF_H
#include <net/if.h>
Expand Down Expand Up @@ -520,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 @@ -803,53 +806,51 @@ int tport_bind_socket(int socket,
}
}
#endif
#if defined(__linux__)
if (tport_bind_socket_iface(socket, su, ai) < 0) {
return -1;
}
#endif

return 0;
}

#if defined(__linux__)
#if HAVE_GETIFADDRS && defined (__linux__)
int tport_bind_socket_iface(int s,
su_sockaddr_t *su,
su_addrinfo_t *ai)
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];

getifaddrs(&addrs);
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 -1;
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;
}
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);
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" */
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
Expand Down Expand Up @@ -1276,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 @@ -1321,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 @@ -1333,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 @@ -1342,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
11 changes: 7 additions & 4 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,10 +443,10 @@ 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 defined(__linux__)
int tport_bind_socket_iface(int s,
su_sockaddr_t *su,
su_addrinfo_t *ai);
#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

0 comments on commit ca9c27e

Please sign in to comment.