mcproxy: add igmpV2 reply support
authorJohn Crispin <john@phrozen.org>
Wed, 5 Jun 2019 18:33:42 +0000 (20:33 +0200)
committerJohn Crispin <john@phrozen.org>
Sun, 30 Jun 2019 11:03:18 +0000 (13:03 +0200)
mcproxy has no way to send an IGMPv2 query today. If you force IGMPv2 (by
setting the protocol in the mcproxy config and setting the force_igmp_version
flag on all interfaces) the bridge will send v2 queries but if mcproxy takes
over as the querier it will send v3 queries. The patch below adds support for
sending v2 queries so everyone stays in sync:

Signed-off-by: Sukru Senli <sukru.senli@iopsys.eu>
Signed-off-by: Chad Monroe <chad.monroe@smartrg.com>
Signed-off-by: John Crispin <john@phrozen.org>
mcproxy/files/mcproxy.config
mcproxy/patches/0006-block-ingress.patch
mcproxy/patches/0007-igmpv2-queries.patch [new file with mode: 0644]

index e80b6023739ff289847346763c78fdbac9867efd..f0287950cfd74eae3d8de576109bff2155ab9b39 100644 (file)
@@ -230,7 +230,7 @@ config behaviour
        option whitelist '1'
        option table '{(*|*)}'
 
-config blocks
+config blocks blocks
        # mDNS
        list entries '(*|239.255.255.0/24)'
        # SSDP
index c8bcdb30f9f5ec6cd89313db541516bc5626c3d8..59987419836b3e11664643d405ebdb0d1d2aeaa6 100644 (file)
@@ -1,25 +1,23 @@
---- a/mcproxy/src/proxy/proxy_instance.cpp
-+++ b/mcproxy/src/proxy/proxy_instance.cpp
+Index: mcproxy-2017-08-24-93b5ace42268160ebbfff4c61818fb15fa2d9b99/mcproxy/src/proxy/proxy_instance.cpp
+===================================================================
+--- mcproxy-2017-08-24-93b5ace42268160ebbfff4c61818fb15fa2d9b99.orig/mcproxy/src/proxy/proxy_instance.cpp
++++ mcproxy-2017-08-24-93b5ace42268160ebbfff4c61818fb15fa2d9b99/mcproxy/src/proxy/proxy_instance.cpp
 @@ -171,6 +171,9 @@ void proxy_instance::worker_thread()
      HC_LOG_TRACE("");
      while (m_running) {
          auto msg = m_job_queue.dequeue();
 +
-+              HC_LOG_DEBUG("Proxy Message: " << msg->get_message_type_name(msg->get_type()) );
++        HC_LOG_DEBUG("Proxy Message: " << msg->get_message_type_name(msg->get_type()) );
 +
          switch (msg->get_type()) {
          case proxy_msg::TEST_MSG:
              (*msg)();
-@@ -190,28 +193,80 @@ void proxy_instance::worker_thread()
-             } else {
-                 HC_LOG_DEBUG("failed to find querier of interface: " << interfaces::get_if_name(std::static_pointer_cast<timer_msg>(msg)->get_if_index()));
-             }
--        }
-+              }
+@@ -193,25 +196,66 @@ void proxy_instance::worker_thread()
+         }
          break;
          case proxy_msg::GROUP_RECORD_MSG: {
 -            auto r =  std::static_pointer_cast<group_record_msg>(msg);
-+            auto gr =  std::static_pointer_cast<group_record_msg>(msg);
++            auto gr = std::static_pointer_cast<group_record_msg>(msg);
  
              if (m_in_debug_testing_mode) {
                  std::cout << "!!--ACTION: receive record" << std::endl;
              }
  
 -            auto it = m_downstreams.find(r->get_if_index());
-+                      auto slist = gr->get_slist();
-+                      addr_storage saddr;
-+                      if ( slist.empty() )
-+                      {
-+                              saddr = "0.0.0.0";
-+                      }
-+                      else
-+                      {
-+                              saddr = slist.begin()->saddr;
-+                      }
++            auto slist = gr->get_slist();
++            addr_storage saddr;
++            if (slist.empty()) {
++                saddr = "0.0.0.0";
++            } else {
++                saddr = slist.begin()->saddr;
++            }
 +            auto it = m_downstreams.find(gr->get_if_index());
              if (it != std::end(m_downstreams)) {
 -                it->second.m_querier->receive_record(msg);
-+                              // Check for input filters
-+                              if ( ! it->second.m_interface->match_input_filter( interfaces::get_if_name( gr->get_if_index() ),
-+                                                                                                         saddr,
-+                                                                                                         gr->get_gaddr() )
-+                                       )
-+                              {
-+                                      HC_LOG_DEBUG("group report " << gr->get_gaddr() << " filtered");
-+                              }
-+                              else
-+                              {
-+                                      it->second.m_querier->receive_record(msg);
-+                              }
++                // Check for input filters
++                if (!it->second.m_interface->match_input_filter(interfaces::get_if_name(gr->get_if_index()), saddr, gr->get_gaddr()))
++                {
++                    HC_LOG_DEBUG("group report " << gr->get_gaddr() << " filtered");
++                }
++                else
++                {
++                    it->second.m_querier->receive_record(msg);
++                }
              } else {
 -                HC_LOG_DEBUG("failed to find querier of interface: " << interfaces::get_if_name(std::static_pointer_cast<timer_msg>(msg)->get_if_index()));
 +                HC_LOG_DEBUG("failed to find querier of interface: " << interfaces::get_if_name( gr->get_if_index() ));
 +              }
 +        break;
 +        case proxy_msg::NEW_SOURCE_MSG: {
-+                      auto sm = std::static_pointer_cast<new_source_msg>(msg);
-+                      // Find the interface
-+                      std::shared_ptr<interface> interf;
-+                      auto it = m_downstreams.find(sm->get_if_index());
-+                      if (it != std::end(m_downstreams)) {
-+                              interf = it->second.m_interface;
-+                      } else {
-+                              for (auto & e : m_upstreams) {
-+                                      if (e.m_if_index == sm->get_if_index()) {
-+                                              interf = e.m_interface;
-+                                              break;
-+                                      }
-+                              }
-+                      }
-+                      if ( !interf )
-+                      {
-+                              HC_LOG_DEBUG("failed to find interface: " << interfaces::get_if_name( sm->get_if_index() ) << " for Source message " << sm->get_saddr()  << " | " << sm->get_gaddr() );
-+                              break;
-+                      }
-+                      // Check for input filters
-+                      if ( ! interf->match_input_filter( interfaces::get_if_name( sm->get_if_index() ),
-+                                                                                                         sm->get_saddr(),
-+                                                                                                         sm->get_gaddr() )
-+                                       )
-+                      {
-+                              HC_LOG_DEBUG("source " << sm->get_saddr()  << " | " << sm->get_gaddr() << " filtered");
-+                      }
-+                      else
-+                      {
-+                              m_routing_management->event_new_source(msg);
-+                      }
++            auto sm = std::static_pointer_cast<new_source_msg>(msg);
++            // Find the interface
++            std::shared_ptr<interface> interf;
++            auto it = m_downstreams.find(sm->get_if_index());
++            if (it != std::end(m_downstreams)) {
++                interf = it->second.m_interface;
++            } else {
++                for (auto & e : m_upstreams) {
++                    if (e.m_if_index == sm->get_if_index()) {
++                        interf = e.m_interface;
++                        break;
++                    }
++                }
++            }
++            if ( !interf )
++            {
++                HC_LOG_DEBUG("failed to find interface: " << interfaces::get_if_name( sm->get_if_index() ) << " for Source message " << sm->get_saddr()  << " | " << sm->get_gaddr() );
++                break;
++            }
++            // Check for input filters
++            if (!interf->match_input_filter(interfaces::get_if_name(sm->get_if_index()), sm->get_saddr(), sm->get_gaddr()))
++            {
++                HC_LOG_DEBUG("source " << sm->get_saddr()  << " | " << sm->get_gaddr() << " filtered");
++            } else {
++                m_routing_management->event_new_source(msg);
++            }
 +              }
          break;
 -        case proxy_msg::NEW_SOURCE_MSG:
          case proxy_msg::NEW_SOURCE_TIMER_MSG:
              m_routing_management->timer_triggerd_maintain_routing_table(msg);
              break;
-             return false;
diff --git a/mcproxy/patches/0007-igmpv2-queries.patch b/mcproxy/patches/0007-igmpv2-queries.patch
new file mode 100644 (file)
index 0000000..005b56d
--- /dev/null
@@ -0,0 +1,116 @@
+--- a/mcproxy/include/proxy/igmp_sender.hpp
++++ b/mcproxy/include/proxy/igmp_sender.hpp
+@@ -37,9 +37,10 @@ class igmp_sender : public sender
+ {
+ private:
+     bool send_igmpv3_query(unsigned int if_index, const timers_values& tv, const addr_storage& gaddr, bool s_flag, const source_list<source>& slist) const;
++    bool send_igmpv2_query(unsigned int if_index, const timers_values& tv, const addr_storage& gaddr ) const;
+ public:
+-    igmp_sender(const std::shared_ptr<const interfaces>& interfaces);
++    igmp_sender(const std::shared_ptr<const interfaces>& interfaces, const group_mem_protocol gmp);
+     bool send_record(unsigned int if_index, mc_filter filter_mode, const addr_storage& gaddr, const source_list<source>& slist) const override;
+--- a/mcproxy/src/proxy/igmp_sender.cpp
++++ b/mcproxy/src/proxy/igmp_sender.cpp
+@@ -32,7 +32,7 @@
+ #include <memory>
+-igmp_sender::igmp_sender(const std::shared_ptr<const interfaces>& interfaces): sender(interfaces, IGMPv3)
++igmp_sender::igmp_sender(const std::shared_ptr<const interfaces>& interfaces, const group_mem_protocol gmp): sender(interfaces, gmp)
+ {
+     HC_LOG_TRACE("");
+@@ -119,10 +119,79 @@ bool igmp_sender::send_mc_addr_and_src_s
+     return rc;
+ }
++bool igmp_sender::send_igmpv2_query(unsigned int if_index, const timers_values& tv, const addr_storage& gaddr ) const
++{
++    HC_LOG_TRACE("");
++
++    std::unique_ptr<unsigned char[]> packet;
++    unsigned int size;
++
++      size = sizeof(ip) + sizeof(router_alert_option) + sizeof(igmp);
++      packet.reset(new unsigned char[size]);
++
++    addr_storage dst_addr;
++
++    if (gaddr == addr_storage(AF_INET)) { //is general query
++        dst_addr = IPV4_ALL_HOST_ADDR;
++    } else {
++        dst_addr = gaddr;
++    }
++
++    //-------------------------------------------------------------------
++    //fill ip header
++    ip* ip_hdr = reinterpret_cast<ip*>(packet.get());
++
++    ip_hdr->ip_v = 4;
++    ip_hdr->ip_hl = (sizeof(ip) + sizeof(router_alert_option)) / 4;
++    ip_hdr->ip_tos = 0;
++    ip_hdr->ip_len = htons(size);
++    ip_hdr->ip_id = 0;
++    ip_hdr->ip_off = htons(0 | IP_DF); //dont fragment flag
++    ip_hdr->ip_ttl = 1;
++    ip_hdr->ip_p = IPPROTO_IGMP;
++    ip_hdr->ip_sum = 0;
++    ip_hdr->ip_src = m_interfaces->get_saddr(interfaces::get_if_name(if_index)).get_in_addr();
++    ip_hdr->ip_dst = dst_addr.get_in_addr();
++
++    //-------------------------------------------------------------------
++    //fill router_alert_option header
++    router_alert_option* ra_hdr = reinterpret_cast<router_alert_option*>(reinterpret_cast<unsigned char*>(ip_hdr) + sizeof(ip));
++    *ra_hdr = router_alert_option();
++
++    ip_hdr->ip_sum = m_sock.calc_checksum(reinterpret_cast<unsigned char*>(ip_hdr), sizeof(ip) + sizeof(router_alert_option));
++
++    //-------------------------------------------------------------------
++    //fill igmpv3 query
++    igmp* query = reinterpret_cast<igmp*>(reinterpret_cast<unsigned char*>(ra_hdr) + sizeof(router_alert_option));
++
++    query->igmp_type = IGMP_MEMBERSHIP_QUERY;
++
++    if (gaddr == addr_storage(AF_INET)) { //general query
++        query->igmp_code = tv.maxrespi_to_maxrespc_igmpv3(tv.get_query_response_interval());
++    } else {
++        query->igmp_code = tv.maxrespi_to_maxrespc_igmpv3(tv.get_last_listener_query_time());
++    }
++
++    query->igmp_cksum = 0;
++    query->igmp_group = gaddr.get_in_addr();
++
++    query->igmp_cksum = m_sock.calc_checksum(reinterpret_cast<unsigned char*>(query), (sizeof(igmp) ));
++
++    if (!m_sock.choose_if(if_index)) {
++        return false;
++    }
++
++    return m_sock.send_packet(dst_addr, reinterpret_cast<unsigned char*>(ip_hdr), size);
++}
++
+ bool igmp_sender::send_igmpv3_query(unsigned int if_index, const timers_values& tv, const addr_storage& gaddr, bool s_flag, const source_list<source>& slist) const
+ {
+     HC_LOG_TRACE("");
++    if ( (m_group_mem_protocol & IGMPv3) == 0 ) {
++              return send_igmpv2_query( if_index, tv, gaddr );
++      }
++
+     std::unique_ptr<unsigned char[]> packet;
+     unsigned int size;
+--- a/mcproxy/src/proxy/proxy_instance.cpp
++++ b/mcproxy/src/proxy/proxy_instance.cpp
+@@ -119,7 +119,7 @@ bool proxy_instance::init_sender()
+ {
+     HC_LOG_TRACE("");
+     if (is_IPv4(m_group_mem_protocol)) {
+-        m_sender = std::make_shared<igmp_sender>(m_interfaces);
++        m_sender = std::make_shared<igmp_sender>(m_interfaces, m_group_mem_protocol );
+     } else if (is_IPv6(m_group_mem_protocol)) {
+         m_sender = std::make_shared<mld_sender>(m_interfaces);
+     } else {