socket: add support for binding sockets to device

As a Linux-specific feature, allow sockets to be bound to a device using
the SO_BINDTODEVICE socket option. The CAP_NET_RAW capability is
required for setting the option.
This commit is contained in:
Miroslav Lichvar 2020-06-30 10:21:45 +02:00
parent 0f04baeb97
commit 4ef944b734
9 changed files with 45 additions and 20 deletions

View file

@ -222,7 +222,7 @@ open_socket(struct Address *addr)
switch (addr->type) {
case SCK_ADDR_IP:
sock_fd = SCK_OpenUdpSocket(&addr->addr.ip, NULL, 0);
sock_fd = SCK_OpenUdpSocket(&addr->addr.ip, NULL, NULL, 0);
break;
case SCK_ADDR_UNIX:
/* Construct path of our socket. Use the same directory as the server

View file

@ -173,7 +173,7 @@ open_socket(int family)
SCK_GetLoopbackIPAddress(family, &local_addr.ip_addr);
local_addr.port = port;
sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, SCK_FLAG_RX_DEST_ADDR);
sock_fd = SCK_OpenUdpSocket(NULL, &local_addr, NULL, SCK_FLAG_RX_DEST_ADDR);
if (sock_fd < 0) {
LOG(LOGS_ERR, "Could not open command socket on %s",
UTI_IPSockAddrToString(&local_addr));

View file

@ -103,7 +103,7 @@ open_socket(int family, int local_port, int client_only, IPSockAddr *remote_addr
if (!client_only)
sock_flags |= SCK_FLAG_BROADCAST;
sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, sock_flags);
sock_fd = SCK_OpenUdpSocket(remote_addr, &local_addr, NULL, sock_flags);
if (sock_fd < 0) {
if (!client_only)
LOG(LOGS_ERR, "Could not open NTP socket on %s", UTI_IPSockAddrToString(&local_addr));

View file

@ -125,7 +125,7 @@ add_interface(CNF_HwTsInterface *conf_iface)
return 1;
}
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
return 0;
@ -285,7 +285,7 @@ update_interface_speed(struct Interface *iface)
struct ifreq req;
int sock_fd, link_speed;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
return;
@ -320,7 +320,7 @@ check_timestamping_option(int option)
{
int sock_fd;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
return 0;
@ -341,7 +341,7 @@ open_dummy_socket(void)
{
int sock_fd, events = 0;
sock_fd = SCK_OpenUdpSocket(NULL, NULL, 0);
sock_fd = SCK_OpenUdpSocket(NULL, NULL, NULL, 0);
if (sock_fd < 0)
return INVALID_SOCK_FD;

View file

@ -325,7 +325,7 @@ NKC_Start(NKC_Instance inst)
local_addr.port = 0;
sock_fd = SCK_OpenTcpSocket(&inst->address, &local_addr, 0);
sock_fd = SCK_OpenTcpSocket(&inst->address, &local_addr, NULL, 0);
if (sock_fd < 0)
return 0;

View file

@ -268,7 +268,7 @@ open_socket(int family, int port)
local_addr.port = port;
sock_fd = SCK_OpenTcpSocket(NULL, &local_addr, 0);
sock_fd = SCK_OpenTcpSocket(NULL, &local_addr, NULL, 0);
if (sock_fd < 0) {
LOG(LOGS_ERR, "Could not open NTS-KE socket on %s", UTI_IPSockAddrToString(&local_addr));
return INVALID_SOCK_FD;

View file

@ -336,6 +336,23 @@ is_any_address(IPAddr *addr)
/* ================================================== */
static int
bind_device(int sock_fd, const char *iface)
{
#ifdef SO_BINDTODEVICE
if (setsockopt(sock_fd, SOL_SOCKET, SO_BINDTODEVICE, iface, strlen(iface) + 1) < 0) {
DEBUG_LOG("Could not bind socket to %s : %s", iface, strerror(errno));
return 0;
}
return 1;
#else
DEBUG_LOG("Could not bind socket to %s : %s", "Not supported");
return 0;
#endif
}
/* ================================================== */
static int
bind_ip_address(int sock_fd, IPSockAddr *addr, int flags)
{
@ -403,7 +420,8 @@ connect_ip_address(int sock_fd, IPSockAddr *addr)
/* ================================================== */
static int
open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int type, int flags)
open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface,
int type, int flags)
{
int domain, family, sock_fd;
@ -442,6 +460,9 @@ open_ip_socket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int type, int fl
if (!set_ip_options(sock_fd, family, flags))
goto error;
if (iface && !bind_device(sock_fd, iface))
goto error;
/* Bind the socket if a non-any local address/port was specified */
if (local_addr && local_addr->ip_addr.family != IPADDR_UNSPEC &&
(local_addr->port != 0 || !is_any_address(&local_addr->ip_addr)) &&
@ -1213,17 +1234,17 @@ SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address,
/* ================================================== */
int
SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags)
SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface, int flags)
{
return open_ip_socket(remote_addr, local_addr, SOCK_DGRAM, flags);
return open_ip_socket(remote_addr, local_addr, iface, SOCK_DGRAM, flags);
}
/* ================================================== */
int
SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags)
SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, const char *iface, int flags)
{
return open_ip_socket(remote_addr, local_addr, SOCK_STREAM, flags);
return open_ip_socket(remote_addr, local_addr, iface, SOCK_STREAM, flags);
}
/* ================================================== */

View file

@ -92,9 +92,11 @@ extern void SCK_GetLoopbackIPAddress(int family, IPAddr *local_addr);
extern void SCK_SetPrivBind(int (*function)(int sock_fd, struct sockaddr *address,
socklen_t address_len));
/* Open socket */
extern int SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags);
extern int SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr, int flags);
/* Open a socket (addresses and iface may be NULL) */
extern int SCK_OpenUdpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr,
const char *iface, int flags);
extern int SCK_OpenTcpSocket(IPSockAddr *remote_addr, IPSockAddr *local_addr,
const char *iface, int flags);
extern int SCK_OpenUnixDatagramSocket(const char *remote_addr, const char *local_addr,
int flags);
extern int SCK_OpenUnixStreamSocket(const char *remote_addr, const char *local_addr,

View file

@ -437,10 +437,12 @@ SYS_Linux_DropRoot(uid_t uid, gid_t gid, int clock_control)
UTI_DropRoot(uid, gid);
/* Keep CAP_NET_BIND_SERVICE only if a server NTP port can be opened
and keep CAP_SYS_TIME only if the clock control is enabled */
if (snprintf(cap_text, sizeof (cap_text), "%s %s",
/* Keep CAP_NET_BIND_SERVICE if the NTP server sockets may need to be bound.
Keep CAP_NET_RAW if an NTP socket may need to be bound to a device.
Keep CAP_SYS_TIME if the clock control is enabled. */
if (snprintf(cap_text, sizeof (cap_text), "%s %s %s",
CNF_GetNTPPort() ? "cap_net_bind_service=ep" : "",
0 ? "cap_net_raw=ep" : "",
clock_control ? "cap_sys_time=ep" : "") >= sizeof (cap_text))
assert(0);