face: elevate privileges before calling setsockopt(SO_BINDTODEVICE)

Change-Id: I33db8eb59ffd17dab087c0a6af5faf273f1c5987
Refs: #4507
diff --git a/daemon/face/multicast-udp-transport.cpp b/daemon/face/multicast-udp-transport.cpp
index 8912759..80f86ae 100644
--- a/daemon/face/multicast-udp-transport.cpp
+++ b/daemon/face/multicast-udp-transport.cpp
@@ -27,6 +27,8 @@
 #include "socket-utils.hpp"
 #include "udp-protocol.hpp"
 
+#include "core/privilege-helper.hpp"
+
 #include <boost/functional/hash.hpp>
 
 #ifdef __linux__
@@ -108,6 +110,26 @@
   DatagramTransport::doClose();
 }
 
+static void
+bindToDevice(int fd, const std::string& ifname)
+{
+  // On Linux, if there is more than one MulticastUdpTransport for the same multicast
+  // group but they are on different network interfaces, each socket needs to be bound
+  // to the corresponding interface using SO_BINDTODEVICE, otherwise the transport will
+  // receive all packets sent to the other interfaces as well.
+  // This is needed only on Linux. On macOS, the boost::asio::ip::multicast::join_group
+  // option is sufficient to obtain the desired behavior.
+
+#ifdef __linux__
+  PrivilegeHelper::runElevated([=] {
+    if (::setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname.data(), ifname.size() + 1) < 0) {
+      BOOST_THROW_EXCEPTION(MulticastUdpTransport::Error("Cannot bind multicast rx socket to " +
+                                                         ifname + ": " + std::strerror(errno)));
+    }
+  });
+#endif // __linux__
+}
+
 void
 MulticastUdpTransport::openRxSocket(protocol::socket& sock,
                                     const protocol::endpoint& multicastGroup,
@@ -139,21 +161,8 @@
     sock.set_option(boost::asio::ip::multicast::join_group(multicastGroup.address().to_v6()));
   }
 
-#ifdef __linux__
-  if (netif) {
-    // On Linux, if there is more than one MulticastUdpTransport for the same multicast
-    // group but they are on different network interfaces, each socket needs to be bound
-    // to the corresponding interface using SO_BINDTODEVICE, otherwise the transport will
-    // receive all packets sent to the other interfaces as well.
-    // This is needed only on Linux. On macOS, the boost::asio::ip::multicast::join_group
-    // option is sufficient to obtain the desired behavior.
-    if (::setsockopt(sock.native_handle(), SOL_SOCKET, SO_BINDTODEVICE,
-                     netif->getName().data(), netif->getName().size() + 1) < 0) {
-      BOOST_THROW_EXCEPTION(Error("Cannot bind multicast rx socket to " + netif->getName() +
-                                  ": " + std::strerror(errno)));
-    }
-  }
-#endif // __linux__
+  if (netif)
+    bindToDevice(sock.native_handle(), netif->getName());
 }
 
 void