DDP input: reject packets with unsupported data type or non-display destination (#5390)

* Fix: reject invalid DDP packets with wrong destination or unsupported data type

Co-authored-by: DedeHai <6280424+DedeHai@users.noreply.github.com>
This commit is contained in:
Copilot
2026-03-29 18:01:11 +02:00
committed by Frank
parent a233e99909
commit d9e8203276
5 changed files with 30 additions and 27 deletions

View File

@@ -187,7 +187,7 @@ function sendDDP(ws, start, len, colors) {
pkt[0] = 0x02; // DDP protocol indicator for WLED websocket. Note: below DDP protocol bytes are offset by 1
pkt[1] = 0x40; // flags: 0x40 = no push, 0x41 = push (i.e. render), note: this is DDP protocol byte 0
pkt[2] = 0x00; // reserved
pkt[3] = 0x01; // 1 = RGB (currently only supported mode)
pkt[3] = 0x0B; // RGB, 8bit per channel
pkt[4] = 0x01; // destination id (not used but 0x01 is default output)
pkt[5] = (off >> 24) & 255; // DDP protocol 4-7 is offset
pkt[6] = (off >> 16) & 255;

View File

@@ -15,6 +15,14 @@ void handleDDPPacket(e131_packet_t* p) {
static bool ddpSeenPush = false; // have we seen a push yet?
[[maybe_unused]] int lastPushSeq = e131LastSequenceNumber[0];
// reject unsupported color data types (only RGB and RGBW are supported)
// WLEDMM allow legacy "undefined" datatype, and legacy (but wrong) datatype=0x01
if ( p->dataType != 0 && p->dataType != 0x01 &&
p->dataType != DDP_TYPE_RGB24 && p->dataType != DDP_TYPE_RGBW32) return;
// reject status and config packets (not implemented)
if (p->destination == DDP_ID_STATUS || p->destination == DDP_ID_CONFIG) return;
//reject late packets belonging to previous frame (assuming 4 packets max. before push)
#if 0 // WLEDMM fixme - we definitely have more than 5-10 packets per frame !!!
if (e131SkipOutOfSequence && lastPushSeq) {
@@ -41,8 +49,8 @@ void handleDDPPacket(e131_packet_t* p) {
uint16_t dataLen = htons(p->dataLen);
unsigned stop = start + dataLen / ddpChannelsPerLed;
uint8_t* data = p->data;
uint16_t c = 0;
if (p->flags & DDP_TIMECODE_FLAG) c = 4; //packet has timecode flag, we do not support it, but data starts 4 bytes later
unsigned c = 0;
if (p->flags & DDP_FLAGS_TIME) c = 4; //packet has timecode flag, we do not support it, but data starts 4 bytes later
unsigned numLeds = stop - start; // stop >= start is guaranteed
unsigned maxDataIndex = c + numLeds * ddpChannelsPerLed; // validate bounds before accessing data array
@@ -67,7 +75,7 @@ void handleDDPPacket(e131_packet_t* p) {
}
}
bool push = p->flags & DDP_PUSH_FLAG;
bool push = p->flags & DDP_FLAGS_PUSH;
ddpSeenPush |= push;
if (!ddpSeenPush || push) { // if we've never seen a push, or this is one, render display
#ifdef WLED_DEBUG

View File

@@ -49,13 +49,26 @@ typedef struct ip_addr ip4_addr_t;
#define E131_DEFAULT_PORT 5568
#define ARTNET_DEFAULT_PORT 6454
#define DDP_DEFAULT_PORT 4048
#define DDP_HEADER_LEN 10
#define DDP_SYNCPACKET_LEN 10
#define DDP_PUSH_FLAG 0x01
#define DDP_TIMECODE_FLAG 0x10
#define DDP_FLAGS_VER 0xc0 // version mask
#define DDP_FLAGS_VER1 0x40 // version=1
#define DDP_FLAGS_PUSH 0x01
#define DDP_FLAGS_QUERY 0x02
#define DDP_FLAGS_REPLY 0x04
#define DDP_FLAGS_STORAGE 0x08
#define DDP_FLAGS_TIME 0x10
#define DDP_CHANNELS_PER_PACKET 1440 // 480 leds
#define DDP_TYPE_RGB24 0x0B // 00 001 011 (RGB , 8 bits per channel, 3 channels)
#define DDP_TYPE_RGBW32 0x1B // 00 011 011 (RGBW, 8 bits per channel, 4 channels)
#define DDP_ID_DISPLAY 1
#define DDP_ID_CONFIG 250
#define DDP_ID_STATUS 251
#define ARTNET_OPCODE_OPDMX 0x5000
#define ARTNET_OPCODE_OPPOLL 0x2000
#define ARTNET_OPCODE_OPPOLLREPLY 0x2100

View File

@@ -799,24 +799,6 @@ void sendSysInfoUDP()
* Art-Net, DDP, E131 output - work in progress
\*********************************************************************************************/
#define DDP_HEADER_LEN 10
#define DDP_SYNCPACKET_LEN 10
#define DDP_FLAGS1_VER 0xc0 // version mask
#define DDP_FLAGS1_VER1 0x40 // version=1
#define DDP_FLAGS1_PUSH 0x01
#define DDP_FLAGS1_QUERY 0x02
#define DDP_FLAGS1_REPLY 0x04
#define DDP_FLAGS1_STORAGE 0x08
#define DDP_FLAGS1_TIME 0x10
#define DDP_ID_DISPLAY 1
#define DDP_ID_CONFIG 250
#define DDP_ID_STATUS 251
// 1440 channels per packet
#define DDP_CHANNELS_PER_PACKET 1440 // 480 leds
//
// Send real time UDP updates to the specified client
//
@@ -885,11 +867,11 @@ uint8_t IRAM_ATTR_YN realtimeBroadcast(uint8_t type, IPAddress client, uint16_t
// the amount of data is AFTER the header in the current packet
size_t packetSize = DDP_CHANNELS_PER_PACKET;
uint8_t flags = DDP_FLAGS1_VER1;
uint8_t flags = DDP_FLAGS_VER1;
if (currentPacket == (packetCount - 1U)) {
// last packet, set the push flag
// TODO: determine if we want to send an empty push packet to each destination after sending the pixel data
flags = DDP_FLAGS1_VER1 | DDP_FLAGS1_PUSH;
flags = DDP_FLAGS_VER1 | DDP_FLAGS_PUSH;
if (channelCount % DDP_CHANNELS_PER_PACKET) {
packetSize = channelCount % DDP_CHANNELS_PER_PACKET;
}

View File

@@ -100,7 +100,7 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
if (len < unsigned(10 + offset)) return; // DDP header is 10 bytes (+1 protocol byte)
size_t ddpDataLen = (data[8+offset] << 8) | data[9+offset]; // data length in bytes from DDP header
uint8_t flags = data[0+offset];
if ((flags & DDP_TIMECODE_FLAG) ) ddpDataLen += 4; // timecode flag adds 4 bytes to data length
if ((flags & DDP_FLAGS_TIME) ) ddpDataLen += 4; // timecode flag adds 4 bytes to data length
if (len < (10 + offset + ddpDataLen)) return; // not enough data, prevent out of bounds read
// could be a valid DDP packet, forward to handler
handleE131Packet((e131_packet_t*)&data[offset], client->remoteIP(), P_DDP);