fix(sacn): Multicast kam nie an - htonl() zerriss die Gruppenadresse

Die Lampe trat seit jeher der falschen Gruppe bei. Aus Universe 1 wurde
239.255.239.255 statt 239.255.0.1, und damit lauschte sie auf einer Adresse,
auf die niemand sendet: Multicast kam nie an, Unicast lief, und der Beitritt
meldete trotzdem Erfolg - er gelang ja, nur eben fuer die falsche Gruppe.

Ursache ist ein nicht geklammertes Makro im SDK:

  #define htonl(n) (((n & 0xff) << 24) | ((n & 0xff00) << 8) | ...)

Stand die Adressrechnung direkt im Aufruf, traf die Maske nach der
Textersetzung nur den letzten Term, weil & staerker bindet als |. Nachgerechnet
ergibt das fuer Universe 1 exakt die 0xefffefff, die am EVK auf der seriellen
Konsole stand.

Die Rechnung liegt jetzt in nl_e131_group_addr() - im reinen, host-testbaren
Teil, mit Testfaellen fuer beide Byte-Haelften. Der Aufrufer reicht eine
Variable an htonl(), keinen Ausdruck mehr.

Gefunden am EVK ueber die serielle Konsole, nachdem vier Netz-Konfigurationen
und fuenf andere Theorien nichts ergeben hatten.
This commit is contained in:
Michael Sauer
2026-09-21 23:06:01 +02:00
parent 4400ea5b42
commit fdded43ab9
4 changed files with 40 additions and 2 deletions
+15 -2
View File
@@ -57,6 +57,7 @@ static int open_socket(uint16_t universe, char ip_seen[16])
struct ip_mreq mreq;
struct timeval tv;
nl_wifi_status_t w;
uint32_t group;
int on = 1;
int fd = socket(AF_INET, SOCK_DGRAM, 0);
@@ -90,8 +91,20 @@ static int open_socket(uint16_t universe, char ip_seen[16])
* nicht beitritt oder das WLAN die Gruppe nicht weiterreicht. Genau diese Frage
* beantwortet das Feld. */
memset(&mreq, 0, sizeof mreq);
mreq.imr_multiaddr.s_addr =
htonl(0xefff0000u | ((uint32_t)(universe >> 8) << 8) | (universe & 0xffu));
/* Die Gruppenadresse wird ZUERST in eine Variable gerechnet und erst dann umgedreht.
* Das ist kein Schoenheitsfehler: htonl() ist in diesem SDK ein Makro, das sein
* Argument NICHT klammert (sdk/Include/Net/wm_sockets2.0.3.h):
*
* #define htonl(n) (((n & 0xff) << 24) | ((n & 0xff00) << 8) | ...)
*
* Ein Ausdruck mit | wird dabei zerrissen, weil & staerker bindet als | - die Maske
* trifft nur den letzten Term. Frueher stand der ganze Ausdruck direkt im Aufruf, und
* fuer Universe 1 kam 239.255.239.255 statt 239.255.0.1 heraus. Die Lampe trat also
* sauber einer Gruppe bei, auf die niemand sendete: Multicast kam nie an, waehrend
* Unicast lief, und der Beitritt meldete trotzdem Erfolg. Gefunden am 2026-09-21 am
* EVK ueber die serielle Konsole. */
group = nl_e131_group_addr(universe);
mreq.imr_multiaddr.s_addr = htonl(group);
mreq.imr_interface.s_addr = ip_seen[0] ? inet_addr(ip_seen) : htonl(INADDR_ANY);
s_joined = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof mreq) == 0;
printf("sacn: universe %u, multicast join %s\r\n", (unsigned)universe,