From 1236931eea3714fe27b392fe0f320e5ef95bfc6d Mon Sep 17 00:00:00 2001
From: g0dil <g0dil@wiback.org>
Date: Wed, 8 Oct 2008 10:44:17 +0000
Subject: [PATCH] Socket/Protocols/INet: Implement SSM Multicast API

---
 Scheduler/Console/Mainpage.dox                |  2 +-
 .../Protocols/INet/MulticastSocketProtocol.cc | 70 +++++++++++++++++++
 .../Protocols/INet/MulticastSocketProtocol.hh | 46 +++++++++++-
 .../INet/MulticastSocketProtocol.test.cc      | 26 ++++++-
 Utils/Exception.hh                            |  2 +-
 5 files changed, 141 insertions(+), 5 deletions(-)

diff --git a/Scheduler/Console/Mainpage.dox b/Scheduler/Console/Mainpage.dox
index b1636e361..ce4982003 100644
--- a/Scheduler/Console/Mainpage.dox
+++ b/Scheduler/Console/Mainpage.dox
@@ -118,7 +118,7 @@
     \see \ref console_testserver for a complete example application
 
 
-    \section intro_usage Using the Console: Configuration files, Network console, ...
+    \section intro_usage Using the Console: Configuration files, Network console
 
     There are several ways to access the node tree:
     \li By parsing configuration files
diff --git a/Socket/Protocols/INet/MulticastSocketProtocol.cc b/Socket/Protocols/INet/MulticastSocketProtocol.cc
index dca071e24..42f2126b8 100644
--- a/Socket/Protocols/INet/MulticastSocketProtocol.cc
+++ b/Socket/Protocols/INet/MulticastSocketProtocol.cc
@@ -183,6 +183,40 @@ prefix_ void senf::INet4MulticastSocketProtocol::mcDropMembership(INet4Address c
         SENF_THROW_SYSTEM_EXCEPTION("");
 }
 
+namespace {
+    void mc4SSMSourceRequest(int operation, int fd, senf::INet4Address const & group,
+                              senf::INet4Address const & source, std::string const & iface)
+    {
+        struct group_source_req req;
+        ::memset(&req, 0, sizeof(req));
+        req.gsr_interface = if_nametoindex(iface.c_str());
+        if (req.gsr_interface == 0)
+            throw senf::SystemException("::if_nametoindex()", ENOENT SENF_EXC_DEBUGINFO);
+        req.gsr_group.ss_family = AF_INET;
+        reinterpret_cast<struct sockaddr_in&>(req.gsr_group).sin_addr.s_addr = group.inaddr();
+        req.gsr_source.ss_family = AF_INET;
+        reinterpret_cast<struct sockaddr_in&>(req.gsr_source).sin_addr.s_addr = source.inaddr();
+        if (::setsockopt(fd, SOL_IP, MCAST_JOIN_SOURCE_GROUP, &req, sizeof(req)) < 0)
+            SENF_THROW_SYSTEM_EXCEPTION("::setsockopt()");
+    }
+}
+
+prefix_ void senf::INet4MulticastSocketProtocol::mcJoinSSMSource(INet4Address const & group,
+                                                                 INet4Address const & source,
+                                                                 std::string const & iface)
+    const
+{
+    mc4SSMSourceRequest(MCAST_JOIN_SOURCE_GROUP, fd(), group, source, iface);
+}
+
+prefix_ void senf::INet4MulticastSocketProtocol::mcLeaveSSMSource(INet4Address const & group,
+                                                                  INet4Address const & source,
+                                                                  std::string const & iface)
+    const
+{
+    mc4SSMSourceRequest(MCAST_LEAVE_SOURCE_GROUP, fd(), group, source, iface);
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // senf::INet6MulticastSocketProtocol
 
@@ -276,6 +310,42 @@ senf::INet6MulticastSocketProtocol::mcDropMembership(INet6Address const & mcAddr
     }
 }
 
+namespace {
+    void mc6SSMSourceRequest(int operation, int fd, senf::INet6Address const & group,
+                             senf::INet6Address const & source, std::string const & iface)
+    {
+        struct group_source_req req;
+        ::memset(&req, 0, sizeof(req));
+        req.gsr_interface = if_nametoindex(iface.c_str());
+        if (req.gsr_interface == 0)
+            throw senf::SystemException("::if_nametoindex()", ENOENT SENF_EXC_DEBUGINFO);
+        req.gsr_group.ss_family = AF_INET6;
+        std::copy(group.begin(), group.end(),
+                  reinterpret_cast<struct sockaddr_in6&>(req.gsr_group).sin6_addr.s6_addr);
+        req.gsr_source.ss_family = AF_INET6;
+        std::copy(source.begin(), source.end(),
+                  reinterpret_cast<struct sockaddr_in6&>(req.gsr_source).sin6_addr.s6_addr);
+        if (::setsockopt(fd, SOL_IPV6, MCAST_JOIN_SOURCE_GROUP, &req, sizeof(req)) < 0)
+            SENF_THROW_SYSTEM_EXCEPTION("::setsockopt()");
+    }
+}
+
+prefix_ void senf::INet6MulticastSocketProtocol::mcJoinSSMSource(INet6Address const & group,
+                                                                 INet6Address const & source,
+                                                                 std::string const & iface)
+    const
+{
+    mc6SSMSourceRequest(MCAST_JOIN_SOURCE_GROUP, fd(), group, source, iface);
+}
+
+prefix_ void senf::INet6MulticastSocketProtocol::mcLeaveSSMSource(INet6Address const & group,
+                                                                  INet6Address const & source,
+                                                                  std::string const & iface)
+    const
+{
+    mc6SSMSourceRequest(MCAST_LEAVE_SOURCE_GROUP, fd(), group, source, iface);
+}
+
 ///////////////////////////////cc.e////////////////////////////////////////
 #undef prefix_
 //#include "MulticastSocketProtocol.mpp"
diff --git a/Socket/Protocols/INet/MulticastSocketProtocol.hh b/Socket/Protocols/INet/MulticastSocketProtocol.hh
index 0de6997b5..cf8074075 100644
--- a/Socket/Protocols/INet/MulticastSocketProtocol.hh
+++ b/Socket/Protocols/INet/MulticastSocketProtocol.hh
@@ -40,6 +40,9 @@ namespace senf {
     ///\{
    
     /** \brief Generic addressing type independent multicast protocol facet
+
+        \todo implement complete new multicast API from RFC3678 (as far as supported by linux)
+        \bug mcLeaveSSMSource fails with EADDRNOTAVAIL
      */
     class MulticastSocketProtocol 
         : public virtual SocketProtocol
@@ -125,6 +128,26 @@ namespace senf {
                                              interface with the given local address.
                                              \param[in] mcAddr address of group to leave
                                              \param[in] iface interface name */
+
+        void mcJoinSSMSource(INet4Address const & group, INet4Address const & source, 
+                             std::string const & iface) const;
+                                        ///< join SSM multicast group
+                                        /**< This call will join the multicast group \a group for
+                                             traffic from \a source. A single group may be joined
+                                             multiple times on different sources.
+                                             \param[in] group multicast group to join
+                                             \param[in] source SSM multicast source to join the
+                                                 group on
+                                             \param[in] iface interface to join the group on */
+        void mcLeaveSSMSource(INet4Address const & group, INet4Address const & source,
+                              std::string const & iface) const;
+                                        ///< leave SSM multicast group
+                                        /**< This call will leave the multicast group \a group for
+                                             traffic from \a source.
+                                             \param[in] group multicast group to leave
+                                             \param[in] source SSM multicast source to leave the
+                                                 group from
+                                             \param[in] iface interface to leave the group on */
     };
 
     /** \brief Multicast protocol facet for INet6 addressable multicast enabled sockets
@@ -156,14 +179,33 @@ namespace senf {
                                              multicast groups received. The group is left from the
                                              default interface.
                                              \param[in] mcAddr address of group to leave */
-        void mcDropMembership(INet6Address const & mcAddr, std::string const & iface) 
-            const;
+        void mcDropMembership(INet6Address const & mcAddr, std::string const & iface) const;
                                         ///< leave multicast group on a specific interface
                                         /**< This member will remove \a mcAddr from the list of
                                              multicast groups received. The group is left from the
                                              interface with the given local address.
                                              \param[in] mcAddr address of group to leave
                                              \param[in] iface interface name */
+
+        void mcJoinSSMSource(INet6Address const & group, INet6Address const & source, 
+                             std::string const & iface) const;
+                                        ///< join SSM multicast group
+                                        /**< This call will join the multicast group \a group for
+                                             traffic from \a source. A single group may be joined
+                                             multiple times on different sources.
+                                             \param[in] group multicast group to join
+                                             \param[in] source SSM multicast source to join the
+                                                 group on
+                                             \param[in] iface interface to join the group on */
+        void mcLeaveSSMSource(INet6Address const & group, INet6Address const & source,
+                              std::string const & iface) const;
+                                        ///< leave SSM multicast group
+                                        /**< This call will leave the multicast group \a group for
+                                             traffic from \a source.
+                                             \param[in] group multicast group to leave
+                                             \param[in] source SSM multicast source to leave the
+                                                 group from
+                                             \param[in] iface interface to leave the group on */
     };
 
     ///\}
diff --git a/Socket/Protocols/INet/MulticastSocketProtocol.test.cc b/Socket/Protocols/INet/MulticastSocketProtocol.test.cc
index 8a02f0bea..2212645f7 100644
--- a/Socket/Protocols/INet/MulticastSocketProtocol.test.cc
+++ b/Socket/Protocols/INet/MulticastSocketProtocol.test.cc
@@ -53,8 +53,32 @@ BOOST_AUTO_UNIT_TEST(multicastSocketProtocol)
     BOOST_CHECK( ! sock.protocol().mcLoop() );
     sock.protocol().mcLoop(true);
     BOOST_CHECK( sock.protocol().mcLoop() );
-    
+
     sock.protocol().mcIface("lo");
+
+    SENF_CHECK_NO_THROW( sock.protocol().mcJoinSSMSource(
+                             senf::INet4Address(0xE0000001u),
+                             senf::INet4Address(0x7F000001u),
+                             "lo") );
+    // This fails with EADDRNOTAVAIL .. no idea why. I tried with 'eth' interface
+    // and a real address (not loopback) to no avail.
+//     SENF_CHECK_NO_THROW( sock.protocol().mcLeaveSSMSource(
+//                              senf::INet4Address(0xE0000001u),
+//                              senf::INet4Address(0x7F000001u),
+//                              "lo") );
+
+    senf::UDPv6ClientSocketHandle sock6;
+
+    SENF_CHECK_NO_THROW( sock6.protocol().mcJoinSSMSource(
+                             senf::INet6Address(0xFF00u, 0, 0, 0, 0, 0, 0, 1),
+                             senf::INet6Address::Loopback,
+                             "lo") );
+    // This fails with EADDRNOTAVAIL .. no idea why. I tried with 'eth' interface
+    // and a real address (not loopback) to no avail.
+//     SENF_CHECK_NO_THROW( sock6.protocol().mcLeaveSSMSource(
+//                              senf::INet6Address(0xFF00u, 0, 0, 0, 0, 0, 0, 1),
+//                              senf::INet6Address::Loopback,
+//                              "lo") );
 }
 
 ///////////////////////////////cc.e////////////////////////////////////////
diff --git a/Utils/Exception.hh b/Utils/Exception.hh
index bb947e2c4..b151b883d 100644
--- a/Utils/Exception.hh
+++ b/Utils/Exception.hh
@@ -308,7 +308,7 @@ namespace senf {
 #       define SENF_EXC_DEBUGINFO
 #   endif
 
-#   define SENF_THROW_SYSTEM_EXCEPTION(desc) throw SystemException(desc SENF_EXC_DEBUGINFO)
+#   define SENF_THROW_SYSTEM_EXCEPTION(desc) throw senf::SystemException(desc SENF_EXC_DEBUGINFO)
 
 }
 
-- 
GitLab