Ich habe auf meinem aktuellen Centos7 versucht, knxd (aktuelles github, commit 848a5cc6cc1f3f79116fbf61c825ca9cd47057cd) mit multicast, sowohl ip als auch server/ets_router zum Laufen zu bringen. Allerdings hat die (virtuelle) Maschine 3 Ethernet-interfaces, weil KNX auf einem sauberen VLAN laufen (eth2) soll, auf dem ansonsten kein Traffic ist.
Tunnelmode (mit IP-router/interface) kein Problem. Ausgehende multicast-messages ebenfalls kein Problem.
Hereinkommenden Pakete werden vom knxd nicht bemerkt (maximales logging). Nach Beseitigung der offensichtlichen Probleme (firewall, 2 Stunden, grr), Diagnose: der knxd tritt der multicast-Gruppe bei, allerdings mit dem falschen Interface:
Hier das config file (keine command line parameter):
Zur Behandlung des Themas ist schon reichlich code im knxd vorhanden, ich denke aber, das funktioniert (zumindest bei mir) nicht richtig.
Hier mein vorgeschlagener Patch, mit dem das Problem beseitigt wird. Ich denke, nicht, dass das der optimale Weg ist, aber es ist recht minimalistisch. Da ja das Versenden der Pakete über multicast funktioniert, geht setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,...) offensichtlich gut. Was nicht funktioniert ist setsockopt (fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, ...), dabei insbesondere die Festlegung des verwendeten physischen Interfaces. Dazu gibt es zwei Möglichkeiten: Angabe der Adresse des Interfaces oder des Index (>0). Nur gibt es leider ip_mreq (alt) und ip_mreqn (neu), die dafür verwendet werden können. ip_mreqn ist bereits bei IP_MULTICAST_IF im Einsatz. Wünschenswert wäre eine Vereinheitlichung von EIBNetIPSocket::SetMulticast() und EIBNetIPSocket::SetInterface() in dieser Beziehung, aber das war mir jetzt etwas kritisch. Also merke ich mir den Interface-Index bei SetInterface() und nutze ihn im Erfolgsfall bei SetMulticast().
Tunnelmode (mit IP-router/interface) kein Problem. Ausgehende multicast-messages ebenfalls kein Problem.
Hereinkommenden Pakete werden vom knxd nicht bemerkt (maximales logging). Nach Beseitigung der offensichtlichen Probleme (firewall, 2 Stunden, grr), Diagnose: der knxd tritt der multicast-Gruppe bei, allerdings mit dem falschen Interface:
Code:
% netstat -ng IPv6/IPv4 Group Memberships Interface RefCnt Group --------------- ------ --------------------- lo 1 224.0.0.1 eth0 1 224.0.23.12 eth0 1 224.0.0.1 eth1 1 224.0.0.1 eth2 1 224.0.0.1
Code:
[main] addr = 0.0.2 client-addrs = 0.0.5:9 connections = server,knxd_unix # logfile = /var/log/knx/knxd.log # background = true cache=gc [server] server = ets_router discover = true debug = debug-server router = router tunnel = tunnel interface = eth2 [debug-server] name = mcast:knxd error-level = 0x9 trace-mask = 0xffffff [knxd_unix] path = /usr/local/knxd/run/knxd.socket
Hier mein vorgeschlagener Patch, mit dem das Problem beseitigt wird. Ich denke, nicht, dass das der optimale Weg ist, aber es ist recht minimalistisch. Da ja das Versenden der Pakete über multicast funktioniert, geht setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,...) offensichtlich gut. Was nicht funktioniert ist setsockopt (fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, ...), dabei insbesondere die Festlegung des verwendeten physischen Interfaces. Dazu gibt es zwei Möglichkeiten: Angabe der Adresse des Interfaces oder des Index (>0). Nur gibt es leider ip_mreq (alt) und ip_mreqn (neu), die dafür verwendet werden können. ip_mreqn ist bereits bei IP_MULTICAST_IF im Einsatz. Wünschenswert wäre eine Vereinheitlichung von EIBNetIPSocket::SetMulticast() und EIBNetIPSocket::SetInterface() in dieser Beziehung, aber das war mir jetzt etwas kritisch. Also merke ich mir den Interface-Index bei SetInterface() und nutze ihn im Erfolgsfall bei SetMulticast().
Code:
diff --git a/src/libserver/eibnetip.cpp b/src/libserver/eibnetip.cpp index 7139d44..480faf2 100644 --- a/src/libserver/eibnetip.cpp +++ b/src/libserver/eibnetip.cpp @@ -74,6 +74,7 @@ EIBNetIPSocket::EIBNetIPSocket (struct sockaddr_in bindaddr, bool reuseaddr, t = tr; TRACEPRINTF (t, 0, "Open"); multicast = false; + mc_ifindex = 0; memset (&maddr, 0, sizeof (maddr)); memset (&sendaddr, 0, sizeof (sendaddr)); memset (&recvaddr, 0, sizeof (recvaddr)); @@ -201,10 +202,17 @@ EIBNetIPSocket::port () bool EIBNetIPSocket::SetMulticast (struct ip_mreq multicastaddr) { + + ip_mreqn mreqn; + + mreqn.imr_multiaddr = multicastaddr.imr_multiaddr; + mreqn.imr_address = multicastaddr.imr_interface; + mreqn.imr_ifindex = mc_ifindex; + if (multicast) return false; maddr = multicastaddr; - if (setsockopt (fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &maddr, sizeof (maddr)) + if (setsockopt (fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreqn, sizeof (mreqn)) == -1) return false; multicast = true; @@ -302,8 +310,12 @@ EIBNetIPSocket::SetInterface(std::string& iface) if (iface.size() == 0) return true; addr.imr_ifindex = if_nametoindex(iface.c_str()); - return - setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)) >= 0; + if(setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &addr, sizeof(addr)) >= 0) + { + mc_ifindex = addr.imr_ifindex; // save for SetMulticast + return true; + } + return false; } EIBnet_SearchRequest::EIBnet_SearchRequest () diff --git a/src/libserver/eibnetip.h b/src/libserver/eibnetip.h index b6fb92e..9bca6e5 100644 --- a/src/libserver/eibnetip.h +++ b/src/libserver/eibnetip.h @@ -512,6 +512,8 @@ private: int fd; /** multicast in use? */ bool multicast; + /** saved interface index **/ + int mc_ifindex; }; #endif