Skip to content

Commit 5dd780c

Browse files
authored
udp: restore correct address/port when parsing packet (#6011)
do interleave informations on addresses within reception pbuf chain: before: (data-pbuf) -> (data-pbuf) -> (data-pbuf) -> ... in the receiving order now: (address+port-info-pbuf -> data-pbuf) -> (address_port-info-pbuf -> data-pbuf) -> ... address/port informations are updated along with data exposed to user
1 parent f6dd826 commit 5dd780c

File tree

2 files changed

+91
-53
lines changed

2 files changed

+91
-53
lines changed

libraries/ESP8266WiFi/examples/Udp/Udp.ino

+9-15
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
unsigned int localPort = 8888; // local port to listen on
2727

2828
// buffers for receiving and sending data
29-
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet,
29+
char packetBuffer[UDP_TX_PACKET_MAX_SIZE + 1]; //buffer to hold incoming packet,
3030
char ReplyBuffer[] = "acknowledged\r\n"; // a string to send back
3131

3232
WiFiUDP Udp;
@@ -49,21 +49,15 @@ void loop() {
4949
// if there's data available, read a packet
5050
int packetSize = Udp.parsePacket();
5151
if (packetSize) {
52-
Serial.print("Received packet of size ");
53-
Serial.println(packetSize);
54-
Serial.print("From ");
55-
IPAddress remote = Udp.remoteIP();
56-
for (int i = 0; i < 4; i++) {
57-
Serial.print(remote[i], DEC);
58-
if (i < 3) {
59-
Serial.print(".");
60-
}
61-
}
62-
Serial.print(", port ");
63-
Serial.println(Udp.remotePort());
52+
Serial.printf("Received packet of size %d from %s:%d\n (to %s:%d, free heap = %d B)\n",
53+
packetSize,
54+
Udp.remoteIP().toString().c_str(), Udp.remotePort(),
55+
Udp.destinationIP().toString().c_str(), Udp.localPort(),
56+
ESP.getFreeHeap());
6457

6558
// read the packet into packetBufffer
66-
Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
59+
int n = Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
60+
packetBuffer[n] = 0;
6761
Serial.println("Contents:");
6862
Serial.println(packetBuffer);
6963

@@ -72,7 +66,7 @@ void loop() {
7266
Udp.write(ReplyBuffer);
7367
Udp.endPacket();
7468
}
75-
delay(10);
69+
7670
}
7771

7872
/*

libraries/ESP8266WiFi/src/include/UdpContext.h

+82-38
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ void esp_schedule();
3232

3333
#include <AddrList.h>
3434

35-
#define GET_UDP_HDR(pb) (reinterpret_cast<udp_hdr*>(((uint8_t*)((pb)->payload)) - UDP_HLEN))
35+
#define PBUF_ALIGNER_ADJUST 4
36+
#define PBUF_ALIGNER(x) ((void*)((((intptr_t)(x))+3)&~3))
3637

3738
class UdpContext
3839
{
@@ -207,21 +208,17 @@ class UdpContext
207208

208209
CONST IPAddress& getRemoteAddress() CONST
209210
{
210-
return _src_addr;
211+
return _currentAddr.srcaddr;
211212
}
212213

213214
uint16_t getRemotePort() const
214215
{
215-
if (!_rx_buf)
216-
return 0;
217-
218-
udp_hdr* udphdr = GET_UDP_HDR(_rx_buf);
219-
return lwip_ntohs(udphdr->src);
216+
return _currentAddr.srcport;
220217
}
221218

222219
const IPAddress& getDestAddress() const
223220
{
224-
return _dst_addr;
221+
return _currentAddr.dstaddr;
225222
}
226223

227224
uint16_t getLocalPort() const
@@ -235,23 +232,41 @@ class UdpContext
235232
{
236233
if (!_rx_buf)
237234
return false;
238-
239235
if (!_first_buf_taken)
240236
{
241237
_first_buf_taken = true;
242238
return true;
243239
}
244240

245-
auto head = _rx_buf;
241+
auto deleteme = _rx_buf;
246242
_rx_buf = _rx_buf->next;
247-
_rx_buf_offset = 0;
248243

249244
if (_rx_buf)
250245
{
246+
// we have interleaved informations on addresses within reception pbuf chain:
247+
// before: (data-pbuf) -> (data-pbuf) -> (data-pbuf) -> ... in the receiving order
248+
// now: (address-info-pbuf -> data-pbuf) -> (address-info-pbuf -> data-pbuf) -> ...
249+
250+
// so the first rx_buf contains an address helper,
251+
// copy it to "current address"
252+
auto helper = (AddrHelper*)PBUF_ALIGNER(_rx_buf->payload);
253+
_currentAddr = *helper;
254+
255+
// destroy the helper in the about-to-be-released pbuf
256+
helper->~AddrHelper();
257+
258+
// forward in rx_buf list, next one is effective data
259+
// current (not ref'ed) one will be pbuf_free'd with deleteme
260+
_rx_buf = _rx_buf->next;
261+
262+
// this rx_buf is not nullptr by construction,
263+
// ref'ing it to prevent release from the below pbuf_free(deleteme)
251264
pbuf_ref(_rx_buf);
252265
}
253-
pbuf_free(head);
254-
return _rx_buf != 0;
266+
pbuf_free(deleteme);
267+
268+
_rx_buf_offset = 0;
269+
return _rx_buf != nullptr;
255270
}
256271

257272
int read()
@@ -420,54 +435,74 @@ class UdpContext
420435
}
421436

422437
void _recv(udp_pcb *upcb, pbuf *pb,
423-
const ip_addr_t *addr, u16_t port)
438+
const ip_addr_t *srcaddr, u16_t srcport)
424439
{
425440
(void) upcb;
426-
(void) addr;
427-
(void) port;
441+
442+
#if LWIP_VERSION_MAJOR == 1
443+
#define TEMPDSTADDR (&current_iphdr_dest)
444+
#else
445+
#define TEMPDSTADDR (ip_current_dest_addr())
446+
#endif
447+
448+
// chain this helper pbuf first
428449
if (_rx_buf)
429450
{
430451
// there is some unread data
431-
// chain the new pbuf to the existing one
452+
// chain pbuf
453+
454+
// Addresses/ports are stored from this callback because lwIP's
455+
// macro are valid only now.
456+
//
457+
// When peeking data from before payload start (like it was done
458+
// before IPv6), there's no easy way to safely guess whether
459+
// packet is from v4 or v6.
460+
//
461+
// Now storing data in an intermediate chained pbuf containing
462+
// AddrHelper
463+
464+
// allocate new pbuf to store addresses/ports
465+
pbuf* pb_helper = pbuf_alloc(PBUF_RAW, sizeof(AddrHelper) + PBUF_ALIGNER_ADJUST, PBUF_RAM);
466+
if (!pb_helper)
467+
{
468+
// memory issue - discard received data
469+
pbuf_free(pb);
470+
return;
471+
}
472+
// construct in place
473+
new(PBUF_ALIGNER(pb_helper->payload)) AddrHelper(srcaddr, TEMPDSTADDR, srcport);
474+
// chain it
475+
pbuf_cat(_rx_buf, pb_helper);
476+
477+
// now chain the new data pbuf
432478
DEBUGV(":urch %d, %d\r\n", _rx_buf->tot_len, pb->tot_len);
433479
pbuf_cat(_rx_buf, pb);
434480
}
435481
else
436482
{
483+
_currentAddr.srcaddr = srcaddr;
484+
_currentAddr.dstaddr = TEMPDSTADDR;
485+
_currentAddr.srcport = srcport;
486+
437487
DEBUGV(":urn %d\r\n", pb->tot_len);
438488
_first_buf_taken = false;
439489
_rx_buf = pb;
440490
_rx_buf_offset = 0;
441491
}
442492

443-
// --> Arduino's UDP is a stream but UDP is not <--
444-
// When IPv6 is enabled, we store addresses from here
445-
// because lwIP's macro are valid only in this callback
446-
// (there's no easy way to safely guess whether packet
447-
// is from v4 or v6 when we have only access to payload)
448-
// Because of this stream-ed way this is inacurate when
449-
// user does not swallow data quickly enough (the former
450-
// IPv4-only way suffers from the exact same issue.
451-
452-
#if LWIP_VERSION_MAJOR == 1
453-
_src_addr = current_iphdr_src;
454-
_dst_addr = current_iphdr_dest;
455-
#else
456-
_src_addr = ip_data.current_iphdr_src;
457-
_dst_addr = ip_data.current_iphdr_dest;
458-
#endif
459-
460493
if (_on_rx) {
461494
_on_rx();
462495
}
463-
}
464496

497+
#undef TEMPDSTADDR
498+
499+
}
465500

466501
static void _s_recv(void *arg,
467502
udp_pcb *upcb, pbuf *p,
468-
CONST ip_addr_t *addr, u16_t port)
503+
CONST ip_addr_t *srcaddr, u16_t srcport)
469504
{
470-
reinterpret_cast<UdpContext*>(arg)->_recv(upcb, p, addr, port);
505+
reinterpret_cast<UdpContext*>(arg)->_recv(upcb, p, srcaddr, srcport);
471506
}
472507

473508
private:
@@ -483,7 +518,16 @@ class UdpContext
483518
#ifdef LWIP_MAYBE_XCC
484519
uint16_t _mcast_ttl;
485520
#endif
486-
IPAddress _src_addr, _dst_addr;
521+
struct AddrHelper
522+
{
523+
IPAddress srcaddr, dstaddr;
524+
int16_t srcport;
525+
526+
AddrHelper() { }
527+
AddrHelper(const ip_addr_t* src, const ip_addr_t* dst, uint16_t srcport):
528+
srcaddr(src), dstaddr(dst), srcport(srcport) { }
529+
};
530+
AddrHelper _currentAddr;
487531
};
488532

489533

0 commit comments

Comments
 (0)