From 4ef944b73436fc3e1047ec1586c0872e3c581df0 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Tue, 30 Jun 2020 10:21:45 +0200 Subject: [PATCH] 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. --- client.c | 2 +- cmdmon.c | 2 +- ntp_io.c | 2 +- ntp_io_linux.c | 8 ++++---- nts_ke_client.c | 2 +- nts_ke_server.c | 2 +- socket.c | 31 ++++++++++++++++++++++++++----- socket.h | 8 +++++--- sys_linux.c | 8 +++++--- 9 files changed, 45 insertions(+), 20 deletions(-) diff --git a/client.c b/client.c index 41527e7..3ac0b86 100644 --- a/client.c +++ b/client.c @@ -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 diff --git a/cmdmon.c b/cmdmon.c index 8e9764a..d5dbe71 100644 --- a/cmdmon.c +++ b/cmdmon.c @@ -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)); diff --git a/ntp_io.c b/ntp_io.c index ed55687..9c1b686 100644 --- a/ntp_io.c +++ b/ntp_io.c @@ -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)); diff --git a/ntp_io_linux.c b/ntp_io_linux.c index 634ccb0..bfe9f99 100644 --- a/ntp_io_linux.c +++ b/ntp_io_linux.c @@ -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; diff --git a/nts_ke_client.c b/nts_ke_client.c index f37d768..967e014 100644 --- a/nts_ke_client.c +++ b/nts_ke_client.c @@ -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; diff --git a/nts_ke_server.c b/nts_ke_server.c index a4b1e2d..2b94b9b 100644 --- a/nts_ke_server.c +++ b/nts_ke_server.c @@ -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; diff --git a/socket.c b/socket.c index 325ef19..9eaf9fd 100644 --- a/socket.c +++ b/socket.c @@ -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); } /* ================================================== */ diff --git a/socket.h b/socket.h index a51a67c..9207a43 100644 --- a/socket.h +++ b/socket.h @@ -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, diff --git a/sys_linux.c b/sys_linux.c index f74e323..8c41259 100644 --- a/sys_linux.c +++ b/sys_linux.c @@ -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);