Skip to content

Commit ca9c27e

Browse files
committed
Transport: Add TPTAG_SOCKET_BIND_IFC to optionally force socket binding
1 parent 43d6d21 commit ca9c27e

File tree

6 files changed

+107
-38
lines changed

6 files changed

+107
-38
lines changed

libsofia-sip-ua/tport/sofia-sip/tport_tag.h

+8
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,14 @@ TPORT_DLL extern tag_typedef_t tptag_socket_keepalive;
148148
TPORT_DLL extern tag_typedef_t tptag_socket_keepalive_ref;
149149
#define TPTAG_SOCKET_KEEPALIVE_REF(x) tptag_socket_keepalive_ref, tag_uint_vr(&(x))
150150

151+
#if defined (__linux__)
152+
TPORT_DLL extern tag_typedef_t tptag_socket_bind_ifc;
153+
#define TPTAG_SOCKET_BIND_IFC(x) tptag_socket_bind_ifc, tag_bool_v((x))
154+
155+
TPORT_DLL extern tag_typedef_t tptag_socket_bind_ifc_ref;
156+
#define TPTAG_SOCKET_BIND_IFC_REF(x) tptag_socket_bind_ifc_ref, tag_bool_vr(&(x))
157+
#endif
158+
151159
TPORT_DLL extern tag_typedef_t tptag_keepalive;
152160
#define TPTAG_KEEPALIVE(x) tptag_keepalive, tag_uint_v((x))
153161

libsofia-sip-ua/tport/tport.c

+45-34
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ typedef struct tport_nat_s tport_nat_t;
7171
#include <sofia-sip/rbtree.h>
7272

7373
#include "tport_internal.h"
74-
#if defined (__linux__)
74+
#if HAVE_GETIFADDRS && defined (__linux__)
7575
#include <ifaddrs.h>
7676
#if HAVE_NET_IF_H
7777
#include <net/if.h>
@@ -520,6 +520,9 @@ tport_t *tport_tcreate(tp_stack_t *stack,
520520
tpp->tpp_timeout = UINT_MAX;
521521
tpp->tpp_sigcomp_lifetime = UINT_MAX;
522522
tpp->tpp_socket_keepalive = 30;
523+
#if defined (__linux__)
524+
tpp->tpp_socket_bind_ifc = 0;
525+
#endif
523526
tpp->tpp_keepalive = 0;
524527
tpp->tpp_pingpong = 0;
525528
tpp->tpp_pong2ping = 0;
@@ -803,53 +806,51 @@ int tport_bind_socket(int socket,
803806
}
804807
}
805808
#endif
806-
#if defined(__linux__)
807-
if (tport_bind_socket_iface(socket, su, ai) < 0) {
808-
return -1;
809-
}
810-
#endif
809+
811810
return 0;
812811
}
813812

814-
#if defined(__linux__)
813+
#if HAVE_GETIFADDRS && defined (__linux__)
815814
int tport_bind_socket_iface(int s,
816-
su_sockaddr_t *su,
817-
su_addrinfo_t *ai)
815+
su_addrinfo_t *ai,
816+
char const **return_culprit)
818817
{
818+
su_sockaddr_t *su = (su_sockaddr_t *)ai->ai_addr;
819819
struct ifaddrs *addrs, *iap;
820820
struct sockaddr_in *sa;
821821
struct ifreq ifr;
822822
char ipaddr[SU_ADDRSIZE + 2];
823823

824-
getifaddrs(&addrs);
825-
for (iap = addrs; iap != NULL; iap = iap->ifa_next) {
826-
if (iap->ifa_addr && (iap->ifa_flags & IFF_UP) && iap->ifa_addr->sa_family == su->su_family) {
827-
sa = (struct sockaddr_in *)(iap->ifa_addr);
828-
if(sa->sin_addr.s_addr == su->su_sin.sin_addr.s_addr) {
829-
memset(&ifr, 0, sizeof(struct ifreq));
830-
strncpy(ifr.ifr_name, (char const *) iap->ifa_name, IFNAMSIZ);
831-
832-
/* Assign socket to an already active access point (interface) */
833-
ioctl(s, SIOCSIFNAME, &ifr);
834-
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
835-
SU_DEBUG_3(("socket: %d setsockopt(SO_BINDTODEVICE) error binding to ifc %s: %s\n",
836-
s, ifr.ifr_name, su_strerror(su_errno())));
837-
freeifaddrs(addrs);
838-
return -1;
824+
if (getifaddrs(&addrs) == 0) {
825+
for (iap = addrs; iap != NULL; iap = iap->ifa_next) {
826+
if (iap->ifa_addr && (iap->ifa_flags & IFF_UP) && iap->ifa_addr->sa_family == su->su_family) {
827+
sa = (struct sockaddr_in *)(iap->ifa_addr);
828+
if(sa->sin_addr.s_addr == su->su_sin.sin_addr.s_addr) {
829+
memset(&ifr, 0, sizeof(struct ifreq));
830+
strncpy(ifr.ifr_name, (char const *) iap->ifa_name, IFNAMSIZ);
831+
832+
/* Assign socket to an already active access point (interface) */
833+
ioctl(s, SIOCSIFNAME, &ifr);
834+
if (setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
835+
SU_DEBUG_3(("socket: %d setsockopt(SO_BINDTODEVICE) error binding to ifc %s: %s\n",
836+
s, ifr.ifr_name, su_strerror(su_errno())));
837+
freeifaddrs(addrs);
838+
return *return_culprit = "setsockopt", -1;
839+
}
840+
SU_DEBUG_9(("socket: %d, bound %s to ifc: %s\n", s,
841+
su_inet_ntop(su->su_family, SU_ADDR(su), ipaddr, sizeof(ipaddr)),
842+
ifr.ifr_name));
843+
freeifaddrs(addrs);
844+
return 0;
839845
}
840-
SU_DEBUG_9(("socket: %d, bound %s to ifc: %s\n", s,
841-
su_inet_ntop(su->su_family, SU_ADDR(su), ipaddr, sizeof(ipaddr)),
842-
ifr.ifr_name));
843-
freeifaddrs(addrs);
844-
return 0;
845846
}
846847
}
847-
}
848-
freeifaddrs(addrs);
848+
freeifaddrs(addrs);
849849

850-
SU_DEBUG_3(("socket: %d: did not find ifc to bind %s\n",
851-
s, su_inet_ntop(su->su_family, SU_ADDR(su), ipaddr, sizeof(ipaddr))));
852-
/* Technically it's not a "failure" */
850+
SU_DEBUG_3(("socket: %d: did not find ifc to bind %s\n",
851+
s, su_inet_ntop(su->su_family, SU_ADDR(su), ipaddr, sizeof(ipaddr))));
852+
/* Technically it's not a "failure" */
853+
}
853854
return 0;
854855
}
855856
#endif
@@ -1276,6 +1277,9 @@ int tport_get_params(tport_t const *self,
12761277
TPTAG_IDLE(tpp->tpp_idle),
12771278
TPTAG_TIMEOUT(tpp->tpp_timeout),
12781279
TPTAG_SOCKET_KEEPALIVE(tpp->tpp_socket_keepalive),
1280+
#if defined (__linux__)
1281+
TPTAG_SOCKET_BIND_IFC(tpp->tpp_socket_bind_ifc),
1282+
#endif
12791283
TPTAG_KEEPALIVE(tpp->tpp_keepalive),
12801284
TPTAG_PINGPONG(tpp->tpp_pingpong),
12811285
TPTAG_PONG2PING(tpp->tpp_pong2ping),
@@ -1321,6 +1325,9 @@ int tport_set_params(tport_t *self,
13211325

13221326
usize_t mtu;
13231327
int connect, sdwn_error, reusable, stun_server, pong2ping;
1328+
#if defined (__linux__)
1329+
int socket_ifc;
1330+
#endif
13241331

13251332
if (self == NULL)
13261333
return su_seterrno(EINVAL);
@@ -1333,6 +1340,7 @@ int tport_set_params(tport_t *self,
13331340
reusable = self->tp_reusable;
13341341
stun_server = tpp->tpp_stun_server;
13351342
pong2ping = tpp->tpp_pong2ping;
1343+
socket_ifc = tpp->tpp_socket_bind_ifc;
13361344

13371345
ta_start(ta, tag, value);
13381346

@@ -1342,6 +1350,9 @@ int tport_set_params(tport_t *self,
13421350
TPTAG_IDLE_REF(tpp->tpp_idle),
13431351
TPTAG_TIMEOUT_REF(tpp->tpp_timeout),
13441352
TPTAG_SOCKET_KEEPALIVE_REF(tpp->tpp_socket_keepalive),
1353+
#if defined (__linux__)
1354+
TPTAG_SOCKET_BIND_IFC_REF(socket_ifc),
1355+
#endif
13451356
TPTAG_KEEPALIVE_REF(tpp->tpp_keepalive),
13461357
TPTAG_PINGPONG_REF(tpp->tpp_pingpong),
13471358
TPTAG_PONG2PING_REF(pong2ping),

libsofia-sip-ua/tport/tport_internal.h

+7-4
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ typedef struct {
123123
unsigned tpp_sdwn_error:1; /**< If true, shutdown is error. */
124124
unsigned tpp_stun_server:1; /**< If true, use stun server */
125125
unsigned tpp_pong2ping:1; /**< If true, respond with pong to ping */
126+
#if defined (__linux__)
127+
unsigned tpp_socket_bind_ifc:1; /**< If true, force socket bind to the interface */
128+
#endif
126129

127130
unsigned :0;
128131

@@ -440,10 +443,10 @@ void tport_base_timer(tport_t *self, su_time_t now);
440443
int tport_bind_socket(int socket,
441444
su_addrinfo_t *ai,
442445
char const **return_culprit);
443-
#if defined(__linux__)
444-
int tport_bind_socket_iface(int s,
445-
su_sockaddr_t *su,
446-
su_addrinfo_t *ai);
446+
#if HAVE_GETIFADDRS && defined (__linux__)
447+
int tport_bind_socket_iface(int socket,
448+
su_addrinfo_t *ai,
449+
char const **return_culprit);
447450
#endif
448451
void tport_close(tport_t *self);
449452
int tport_shutdown0(tport_t *self, int how);

libsofia-sip-ua/tport/tport_tag.c

+15
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,21 @@ tag_typedef_t tptag_pingpong = UINTTAG_TYPEDEF(pingpong);
247247
*/
248248
tag_typedef_t tptag_pong2ping = BOOLTAG_TYPEDEF(pong2ping);
249249

250+
#if defined (__linux__)
251+
/**@def TPTAG_SOCKET_BIND_IFC(x)
252+
*
253+
* Explicit socket binding to network interface (Linux only).
254+
*
255+
* If true, perform a setsockopt() SO_BINDTODEVICE.
256+
*
257+
* This is to get around an issue in certain Linux kernel whereas in a
258+
* multi-homed environment, a socket might bind to the wrong (primary)
259+
* network interface rather than the intended ifc.
260+
*
261+
*/
262+
tag_typedef_t tptag_socket_bind_ifc = BOOLTAG_TYPEDEF(socket_bind_ifc);
263+
#endif
264+
250265
/**@def TPTAG_SIGCOMP_LIFETIME(x)
251266
*
252267
* Default SigComp lifetime in seconds.

libsofia-sip-ua/tport/tport_type_tcp.c

+16
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ int tport_stream_init_primary(tport_primary_t *pri,
138138
tagi_t const *tags,
139139
char const **return_culprit)
140140
{
141+
#if defined (__linux__)
142+
int socket_bind_ifc = 0;
143+
#endif
141144
pri->pri_primary->tp_socket = socket;
142145

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

158+
#if HAVE_GETIFADDRS && defined (__linux__)
159+
tl_gets(tags,
160+
TPTAG_SOCKET_BIND_IFC_REF(socket_bind_ifc),
161+
TAG_END());
162+
/* Force socket binding
163+
*
164+
* On Linux 5.x kernel for multi-homed environment
165+
*/
166+
if (socket_bind_ifc == 1 && tport_bind_socket_iface(socket, ai, return_culprit) == -1) {
167+
return -1;
168+
}
169+
#endif
170+
155171
if (listen(socket, pri->pri_params->tpp_qsize) == SOCKET_ERROR)
156172
return *return_culprit = "listen", -1;
157173

libsofia-sip-ua/tport/tport_type_udp.c

+16
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ int tport_udp_init_primary(tport_primary_t *pri,
132132
su_sockaddr_t *su = (su_sockaddr_t *)ai->ai_addr;
133133
#endif
134134
int const one = 1; (void)one;
135+
#if defined (__linux__)
136+
int socket_bind_ifc = 0;
137+
#endif
135138

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

148+
#if HAVE_GETIFADDRS && defined (__linux__)
149+
tl_gets(tags,
150+
TPTAG_SOCKET_BIND_IFC_REF(socket_bind_ifc),
151+
TAG_END());
152+
/* Force socket binding
153+
*
154+
* On Linux 5.x kernel for multi-homed environment
155+
*/
156+
if (socket_bind_ifc == 1 && tport_bind_socket_iface(s, ai, return_culprit) == -1) {
157+
return -1;
158+
}
159+
#endif
160+
145161
tport_set_tos(s, ai, pri->pri_params->tpp_tos);
146162

147163
#if HAVE_IP_ADD_MEMBERSHIP

0 commit comments

Comments
 (0)