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:
@@ -69,3 +69,8 @@ bool nl_e131_parse(const uint8_t *buf, size_t len, nl_e131_packet_t *out)
|
|||||||
out->slots = buf + NL_E131_MIN_PACKET;
|
out->slots = buf + NL_E131_MIN_PACKET;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t nl_e131_group_addr(uint16_t universe)
|
||||||
|
{
|
||||||
|
return 0xefff0000u | ((uint32_t)(universe >> 8) << 8) | (uint32_t)(universe & 0xffu);
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,4 +23,10 @@ typedef struct {
|
|||||||
* `out` wird nur bei true beschrieben. */
|
* `out` wird nur bei true beschrieben. */
|
||||||
bool nl_e131_parse(const uint8_t *buf, size_t len, nl_e131_packet_t *out);
|
bool nl_e131_parse(const uint8_t *buf, size_t len, nl_e131_packet_t *out);
|
||||||
|
|
||||||
|
/* Die Multicast-Gruppe eines Universe nach E1.31 Anhang A: 239.255.<hi>.<lo>, als Zahl in
|
||||||
|
* HOST-Byte-Reihenfolge. Steht hier und nicht im Empfangs-Task, damit sie im Host-Test
|
||||||
|
* nachgerechnet werden kann - der Wert war schon einmal falsch, und man sieht es einer
|
||||||
|
* Lampe nicht an: sie tritt dann sauber einer Gruppe bei, auf die niemand sendet. */
|
||||||
|
uint32_t nl_e131_group_addr(uint16_t universe);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+15
-2
@@ -57,6 +57,7 @@ static int open_socket(uint16_t universe, char ip_seen[16])
|
|||||||
struct ip_mreq mreq;
|
struct ip_mreq mreq;
|
||||||
struct timeval tv;
|
struct timeval tv;
|
||||||
nl_wifi_status_t w;
|
nl_wifi_status_t w;
|
||||||
|
uint32_t group;
|
||||||
int on = 1;
|
int on = 1;
|
||||||
int fd = socket(AF_INET, SOCK_DGRAM, 0);
|
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
|
* nicht beitritt oder das WLAN die Gruppe nicht weiterreicht. Genau diese Frage
|
||||||
* beantwortet das Feld. */
|
* beantwortet das Feld. */
|
||||||
memset(&mreq, 0, sizeof mreq);
|
memset(&mreq, 0, sizeof mreq);
|
||||||
mreq.imr_multiaddr.s_addr =
|
/* Die Gruppenadresse wird ZUERST in eine Variable gerechnet und erst dann umgedreht.
|
||||||
htonl(0xefff0000u | ((uint32_t)(universe >> 8) << 8) | (universe & 0xffu));
|
* 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);
|
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;
|
s_joined = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof mreq) == 0;
|
||||||
printf("sacn: universe %u, multicast join %s\r\n", (unsigned)universe,
|
printf("sacn: universe %u, multicast join %s\r\n", (unsigned)universe,
|
||||||
|
|||||||
@@ -192,9 +192,23 @@ static void test_universe_too_high_rejected(void)
|
|||||||
TEST_ASSERT_FALSE(nl_e131_parse(pkt, pkt_len, &p));
|
TEST_ASSERT_FALSE(nl_e131_parse(pkt, pkt_len, &p));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_group_address_per_annex_a(void)
|
||||||
|
{
|
||||||
|
/* 239.255.<hi>.<lo>. Der Fehler, den dieser Test festhaelt: stand die Rechnung direkt
|
||||||
|
* im htonl()-Aufruf, zerriss das nicht geklammerte SDK-Makro sie, und aus Universe 1
|
||||||
|
* wurde 239.255.239.255 - eine Gruppe, auf die niemand sendet. */
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(0xefff0001u, nl_e131_group_addr(1));
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(0xefff000cu, nl_e131_group_addr(12));
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(0xefff012cu, nl_e131_group_addr(300));
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(0xefff00ffu, nl_e131_group_addr(255));
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(0xefff0100u, nl_e131_group_addr(256));
|
||||||
|
TEST_ASSERT_EQUAL_HEX32(0xeffff9ffu, nl_e131_group_addr(63999));
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
|
RUN_TEST(test_group_address_per_annex_a);
|
||||||
RUN_TEST(test_valid_packet);
|
RUN_TEST(test_valid_packet);
|
||||||
RUN_TEST(test_full_universe);
|
RUN_TEST(test_full_universe);
|
||||||
RUN_TEST(test_empty_slot_set);
|
RUN_TEST(test_empty_slot_set);
|
||||||
|
|||||||
Reference in New Issue
Block a user