llmnr_responder.c
Go to the documentation of this file.
1 /**
2  * @file llmnr_responder.c
3  * @brief LLMNR responder (Link-Local Multicast Name Resolution)
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2024 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.4.0
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL LLMNR_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "llmnr/llmnr_responder.h"
37 #include "dns/dns_debug.h"
38 #include "debug.h"
39 
40 //Check TCP/IP stack configuration
41 #if (LLMNR_RESPONDER_SUPPORT == ENABLED)
42 
43 
44 /**
45  * @brief LLMNR responder initialization
46  * @param[in] interface Underlying network interface
47  * @return Error code
48  **/
49 
51 {
52  error_t error;
53 
54 #if (IPV4_SUPPORT == ENABLED)
55  //Join the LLMNR IPv4 multicast group
57  //Any error to report?
58  if(error)
59  return error;
60 #endif
61 
62 #if (IPV6_SUPPORT == ENABLED)
63  //Join the LLMNR IPv6 multicast group
65  //Any error to report?
66  if(error)
67  return error;
68 #endif
69 
70  //LLMNR responders must listen on UDP port 5355
72  NULL);
73  //Any error to report?
74  if(error)
75  return error;
76 
77  //Successful initialization
78  return NO_ERROR;
79 }
80 
81 
82 /**
83  * @brief Process LLMNR query message
84  * @param[in] interface Underlying network interface
85  * @param[in] pseudoHeader UDP pseudo header
86  * @param[in] udpHeader UDP header
87  * @param[in] buffer Multi-part buffer containing the incoming LLMNR message
88  * @param[in] offset Offset to the first byte of the LLMNR message
89  * @param[in] ancillary Additional options passed to the stack along with
90  * the packet
91  * @param[in] param Callback function parameter (not used)
92  **/
93 
95  const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader,
96  const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary,
97  void *param)
98 {
99  size_t n;
100  size_t pos;
101  size_t length;
102  uint16_t destPort;
105  DnsQuestion *question;
106 
107  //Retrieve the length of the LLMNR message
108  length = netBufferGetLength(buffer) - offset;
109 
110  //Ensure the LLMNR message is valid
111  if(length < sizeof(LlmnrHeader))
112  return;
114  return;
115 
116  //Point to the LLMNR message header
117  message = netBufferAt(buffer, offset);
118  //Sanity check
119  if(message == NULL)
120  return;
121 
122  //Debug message
123  TRACE_INFO("LLMNR message received (%" PRIuSIZE " bytes)...\r\n", length);
124  //Dump message
126 
127 #if (IPV4_SUPPORT == ENABLED)
128  //IPv4 query received?
129  if(pseudoHeader->length == sizeof(Ipv4PseudoHeader))
130  {
131  //Unicast UDP queries must be silently discarded (refer to RFC 4795,
132  //section 2.4)
133  if(!ipv4IsMulticastAddr(pseudoHeader->ipv4Data.destAddr))
134  return;
135 
136  //A responder responds to a multicast query by sending a unicast UDP
137  //response to the sender (refer to RFC 4795, section 2)
138  destIpAddr.length = sizeof(Ipv4Addr);
139  destIpAddr.ipv4Addr = pseudoHeader->ipv4Data.srcAddr;
140  }
141  else
142 #endif
143 #if (IPV6_SUPPORT == ENABLED)
144  //IPv6 query received?
145  if(pseudoHeader->length == sizeof(Ipv6PseudoHeader))
146  {
147  //Unicast UDP queries must be silently discarded (refer to RFC 4795,
148  //section 2.4)
149  if(!ipv6IsMulticastAddr(&pseudoHeader->ipv6Data.destAddr))
150  return;
151 
152  //A responder responds to a multicast query by sending a unicast UDP
153  //response to the sender (refer to RFC 4795, section 2)
154  destIpAddr.length = sizeof(Ipv6Addr);
155  destIpAddr.ipv6Addr = pseudoHeader->ipv6Data.srcAddr;
156  }
157  else
158 #endif
159  //Invalid query received?
160  {
161  //Discard the LLMNR query message
162  return;
163  }
164 
165  //Discard LLMNR responses received on port 5355
166  if(message->qr)
167  return;
168 
169  //LLMNR messages received with an opcode other than zero must be silently
170  //ignored
171  if(message->opcode != DNS_OPCODE_QUERY)
172  return;
173 
174  //LLMNR messages received with non-zero response codes must be silently
175  //ignored
176  if(message->rcode != DNS_RCODE_NOERROR)
177  return;
178 
179  //LLMNR responders must silently discard LLMNR queries with QDCOUNT not
180  //equal to one (refer to RFC 4795, section 2.1.1)
181  if(ntohs(message->qdcount) != 1)
182  return;
183 
184  //LLMNR responders must silently discard LLMNR queries with ANCOUNT or
185  //NSCOUNT not equal to zero
186  if(ntohs(message->ancount) != 0 || ntohs(message->nscount) != 0)
187  return;
188 
189  //Point to the first question
190  pos = sizeof(LlmnrHeader);
191 
192  //Parse resource record name
193  n = dnsParseName((DnsHeader *) message, length, pos, NULL, 0);
194  //Invalid name?
195  if(n == 0)
196  return;
197 
198  //Malformed LLMNR message?
199  if((n + sizeof(DnsQuestion)) > length)
200  return;
201 
202  //Point to the corresponding entry
203  question = DNS_GET_QUESTION(message, n);
204 
205  //Check the class of the query
206  if(ntohs(question->qclass) == DNS_RR_CLASS_IN ||
207  ntohs(question->qclass) == DNS_RR_CLASS_ANY)
208  {
209  //Responders must respond to LLMNR queries for names and addresses for
210  //which they are authoritative
211  if(!dnsCompareName((DnsHeader *) message, length, pos,
212  interface->hostname, 0))
213  {
214  //Responders must direct responses to the port from which the query
215  //was sent
216  destPort = ntohs(udpHeader->srcPort);
217 
218  //Send LLMNR response
219  llmnrSendResponse(interface, &destIpAddr, destPort, message->id,
220  ntohs(question->qtype), ntohs(question->qclass));
221  }
222  }
223 }
224 
225 
226 /**
227  * @brief Send LLMNR response message
228  * @param[in] interface Underlying network interface
229  * @param[in] destIpAddr Destination IP address
230  * @param[in] destPort destination port
231  * @param[in] id 16-bit identifier to be used when sending LLMNR query
232  * @param[in] qtype Resource record type
233  * @param[in] qclass Resource record class
234  **/
235 
237  uint16_t destPort, uint16_t id, uint16_t qtype, uint16_t qclass)
238 {
239  error_t error;
240  uint_t i;
241  uint_t j;
242  bool_t linkScope;
243  size_t length;
244  size_t offset;
245  NetBuffer *buffer;
247  DnsQuestion *question;
248  NetTxAncillary ancillary;
249 
250  //Initialize status code
251  error = NO_ERROR;
252 
253  //Allocate a memory buffer to hold the LLMNR response message
254  buffer = udpAllocBuffer(LLMNR_MESSAGE_MAX_SIZE, &offset);
255  //Failed to allocate buffer?
256  if(buffer == NULL)
257  return ERROR_OUT_OF_MEMORY;
258 
259  //Point to the LLMNR header
260  message = netBufferAt(buffer, offset);
261 
262  //Take the identifier from the query message
263  message->id = id;
264 
265  //Format LLMNR response header
266  message->qr = 1;
267  message->opcode = DNS_OPCODE_QUERY;
268  message->c = 0;
269  message->tc = 0;
270  message->t = 0;
271  message->z = 0;
272  message->rcode = DNS_RCODE_NOERROR;
273  message->qdcount = HTONS(1);
274  message->ancount = 0;
275  message->nscount = 0;
276  message->arcount = 0;
277 
278  //Set the length of the LLMNR response message
279  length = sizeof(DnsHeader);
280 
281  //Encode the requested host name using the DNS name notation
282  length += dnsEncodeName(interface->hostname,
283  (uint8_t *) message + length);
284 
285  //Point to the corresponding entry
286  question = DNS_GET_QUESTION(message, length);
287 
288  //Fill in resource record
289  question->qtype = htons(qtype);
290  question->qclass = htons(qclass);
291 
292  //Update the length of the response message
293  length += sizeof(DnsQuestion);
294 
295  //Check whether the source address of the query is a link-scope address
296  linkScope = ipIsLinkLocalAddr(destIpAddr);
297 
298 #if (IPV4_SUPPORT == ENABLED)
299  //A resource record requested?
300  if(qtype == DNS_RR_TYPE_A || qtype == DNS_RR_TYPE_ANY)
301  {
302  Ipv4AddrEntry *entry;
303 
304  //If the source address of the query is a routable address, then the
305  //responder must include a routable address first in the response, if
306  //available (refer to RFC 4795, section 2.6)
307  for(i = 0; i < 2; i++, linkScope = !linkScope)
308  {
309  //Loop through the list of IPv4 addresses assigned to the interface
310  for(j = 0; j < IPV4_ADDR_LIST_SIZE; j++)
311  {
312  //Point to the current entry
313  entry = &interface->ipv4Context.addrList[j];
314 
315  //Check the state of the address
316  if(entry->state == IPV4_ADDR_STATE_VALID)
317  {
318  //Link-scope or routable address?
319  if(ipv4IsLinkLocalAddr(entry->addr) == linkScope)
320  {
321  //Format A resource record
323  entry->addr);
324  }
325  }
326  }
327  }
328  }
329 #endif
330 
331 #if (IPV6_SUPPORT == ENABLED)
332  //AAAA resource record requested?
333  if(qtype == DNS_RR_TYPE_AAAA || qtype == DNS_RR_TYPE_ANY)
334  {
335  Ipv6AddrEntry *entry;
336 
337  //If the source address of the query is a routable address, then the
338  //responder must include a routable address first in the response, if
339  //available (refer to RFC 4795, section 2.6)
340  for(i = 0; i < 2; i++, linkScope = !linkScope)
341  {
342  //Loop through the list of IPv6 addresses assigned to the interface
343  for(j = 0; j < IPV6_ADDR_LIST_SIZE; j++)
344  {
345  //Point to the current entry
346  entry = &interface->ipv6Context.addrList[j];
347 
348  //Check the state of the address
349  if(entry->state == IPV6_ADDR_STATE_PREFERRED ||
351  {
352  //Link-scope or routable address?
353  if(ipv6IsLinkLocalUnicastAddr(&entry->addr) == linkScope)
354  {
355  //Format AAAA resource record
357  &entry->addr);
358  }
359  }
360  }
361  }
362  }
363 #endif
364 
365  //Valid LLMNR response?
366  if(message->ancount > 0)
367  {
368  //The ANCOUNT field specifies the number of resource records in the
369  //answer section
370  message->ancount = htons(message->ancount);
371 
372  //Adjust the length of the multi-part buffer
373  netBufferSetLength(buffer, offset + length);
374 
375  //Debug message
376  TRACE_INFO("Sending LLMNR message (%" PRIuSIZE " bytes)...\r\n", length);
377  //Dump message
379 
380  //Additional options can be passed to the stack along with the packet
381  ancillary = NET_DEFAULT_TX_ANCILLARY;
382 
383  //For UDP responses, the Hop Limit field in the IPv6 header and the TTL
384  //field in the IPV4 header MAY be set to any value. However, it is
385  //recommended that the value 255 be used for compatibility with early
386  //implementations (refer to RFC 4795, section 2.5)
387  ancillary.ttl = LLMNR_DEFAULT_RESPONSE_IP_TTL;
388 
389  //This flag tells the stack that the destination is on a locally attached
390  //network and not to perform a lookup of the routing table
391  ancillary.dontRoute = TRUE;
392 
393  //Send LLMNR response
394  error = udpSendBuffer(interface, NULL, LLMNR_PORT, destIpAddr, destPort,
395  buffer, offset, &ancillary);
396  }
397 
398  //Free previously allocated memory
399  netBufferFree(buffer);
400 
401  //Return status code
402  return error;
403 }
404 
405 
406 /**
407  * @brief Format A resource record
408  * @param[in] interface Underlying network interface
409  * @param[in] message Pointer to the LLMNR message
410  * @param[in,out] length Actual length of the LLMNR message, in bytes
411  * @param[in] ipv4Addr IPv4 address to be added
412  * @return Error code
413  **/
414 
416  LlmnrHeader *message, size_t *length, Ipv4Addr ipv4Addr)
417 {
418 #if (IPV4_SUPPORT == ENABLED)
419  size_t n;
420  size_t offset;
421  DnsResourceRecord *record;
422 
423  //Set the position to the end of the buffer
424  offset = *length;
425 
426  //The first pass calculates the length of the DNS encoded host name
427  n = dnsEncodeName(interface->hostname, NULL);
428 
429  //Check the length of the resulting LLMNR message
430  if((offset + n) > LLMNR_MESSAGE_MAX_SIZE)
431  return ERROR_MESSAGE_TOO_LONG;
432 
433  //The second pass encodes the host name using the DNS name notation
434  offset += dnsEncodeName(interface->hostname, (uint8_t *) message + offset);
435 
436  //Consider the length of the resource record itself
437  n = sizeof(DnsResourceRecord) + sizeof(Ipv4Addr);
438 
439  //Check the length of the resulting LLMNR message
440  if((offset + n) > LLMNR_MESSAGE_MAX_SIZE)
441  return ERROR_MESSAGE_TOO_LONG;
442 
443  //Point to the corresponding resource record
444  record = DNS_GET_RESOURCE_RECORD(message, offset);
445 
446  //Fill in resource record
447  record->rtype = HTONS(DNS_RR_TYPE_A);
448  record->rclass = HTONS(DNS_RR_CLASS_IN);
450  record->rdlength = HTONS(sizeof(Ipv4Addr));
451 
452  //Copy IPv4 address
453  ipv4CopyAddr(record->rdata, &ipv4Addr);
454 
455  //Number of resource records in the answer section
456  message->ancount++;
457  //Update the length of the LLMNR response message
458  *length = offset + n;
459 #endif
460 
461  //Successful processing
462  return NO_ERROR;
463 }
464 
465 
466 /**
467  * @brief Format AAAA resource record
468  * @param[in] interface Underlying network interface
469  * @param[in] message Pointer to the LLMNR message
470  * @param[in,out] length Actual length of the LLMNR message, in bytes
471  * @param[in] ipv6Addr IPv6 address to be added
472  * @return Error code
473  **/
474 
476  LlmnrHeader *message, size_t *length, const Ipv6Addr *ipv6Addr)
477 {
478 #if (IPV6_SUPPORT == ENABLED)
479  size_t n;
480  size_t offset;
481  DnsResourceRecord *record;
482 
483  //Set the position to the end of the buffer
484  offset = *length;
485 
486  //The first pass calculates the length of the DNS encoded host name
487  n = dnsEncodeName(interface->hostname, NULL);
488 
489  //Check the length of the resulting LLMNR message
490  if((offset + n) > LLMNR_MESSAGE_MAX_SIZE)
491  return ERROR_MESSAGE_TOO_LONG;
492 
493  //The second pass encodes the host name using the DNS name notation
494  offset += dnsEncodeName(interface->hostname, (uint8_t *) message + offset);
495 
496  //Consider the length of the resource record itself
497  n = sizeof(DnsResourceRecord) + sizeof(Ipv6Addr);
498 
499  //Check the length of the resulting LLMNR message
500  if((offset + n) > LLMNR_MESSAGE_MAX_SIZE)
501  return ERROR_MESSAGE_TOO_LONG;
502 
503  //Point to the corresponding resource record
504  record = DNS_GET_RESOURCE_RECORD(message, offset);
505 
506  //Fill in resource record
507  record->rtype = HTONS(DNS_RR_TYPE_AAAA);
508  record->rclass = HTONS(DNS_RR_CLASS_IN);
510  record->rdlength = HTONS(sizeof(Ipv6Addr));
511 
512  //Copy IPv6 address
513  ipv6CopyAddr(record->rdata, ipv6Addr);
514 
515  //Number of resource records in the answer section
516  message->ancount++;
517  //Update the length of the LLMNR response message
518  *length = offset + n;
519 #endif
520 
521  //Successful processing
522  return NO_ERROR;
523 }
524 
525 #endif
uint8_t message[]
Definition: chap.h:154
unsigned int uint_t
Definition: compiler_port.h:50
#define PRIuSIZE
int bool_t
Definition: compiler_port.h:53
#define HTONS(value)
Definition: cpu_endian.h:410
#define htons(value)
Definition: cpu_endian.h:413
#define ntohs(value)
Definition: cpu_endian.h:421
#define HTONL(value)
Definition: cpu_endian.h:411
Debugging facilities.
#define TRACE_INFO(...)
Definition: debug.h:95
uint8_t n
int_t dnsCompareName(const DnsHeader *message, size_t length, size_t pos, const char_t *name, uint_t level)
Compare domain names.
Definition: dns_common.c:242
size_t dnsParseName(const DnsHeader *message, size_t length, size_t pos, char_t *dest, uint_t level)
Decode a domain name that uses the DNS name encoding.
Definition: dns_common.c:132
size_t dnsEncodeName(const char_t *src, uint8_t *dest)
Encode a domain name using the DNS name notation.
Definition: dns_common.c:58
DnsHeader
Definition: dns_common.h:199
#define DNS_GET_RESOURCE_RECORD(message, offset)
Definition: dns_common.h:64
DnsResourceRecord
Definition: dns_common.h:224
#define DNS_GET_QUESTION(message, offset)
Definition: dns_common.h:63
@ DNS_RCODE_NOERROR
No error.
Definition: dns_common.h:95
@ DNS_OPCODE_QUERY
Query.
Definition: dns_common.h:81
@ DNS_RR_TYPE_A
Host address.
Definition: dns_common.h:137
@ DNS_RR_TYPE_AAAA
IPv6 address.
Definition: dns_common.h:147
@ DNS_RR_TYPE_ANY
A request for all records.
Definition: dns_common.h:155
@ DNS_RR_CLASS_ANY
Any class.
Definition: dns_common.h:127
@ DNS_RR_CLASS_IN
Internet.
Definition: dns_common.h:124
DnsQuestion
Definition: dns_common.h:210
uint16_t qclass
Definition: dns_common.h:209
void dnsDumpMessage(const DnsHeader *message, size_t length)
Dump DNS message for debugging purpose.
Definition: dns_debug.c:52
Data logging functions for debugging purpose (DNS)
error_t
Error codes.
Definition: error.h:43
@ ERROR_MESSAGE_TOO_LONG
Definition: error.h:136
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_OUT_OF_MEMORY
Definition: error.h:63
bool_t ipIsLinkLocalAddr(const IpAddr *ipAddr)
Determine whether an IP address is a link-local address.
Definition: ip.c:210
Ipv4Addr destIpAddr
Definition: ipcp.h:80
error_t ipv4JoinMulticastGroup(NetInterface *interface, Ipv4Addr groupAddr)
Join the specified host group.
Definition: ipv4.c:1362
#define Ipv4PseudoHeader
Definition: ipv4.h:39
#define IPV4_ADDR_LIST_SIZE
Definition: ipv4.h:69
#define ipv4CopyAddr(destIpAddr, srcIpAddr)
Definition: ipv4.h:148
uint32_t Ipv4Addr
IPv4 network address.
Definition: ipv4.h:267
@ IPV4_ADDR_STATE_VALID
An address assigned to an interface whose use is unrestricted.
Definition: ipv4.h:197
#define ipv4IsMulticastAddr(ipAddr)
Definition: ipv4.h:168
#define ipv4IsLinkLocalAddr(ipAddr)
Definition: ipv4.h:164
error_t ipv6JoinMulticastGroup(NetInterface *interface, const Ipv6Addr *groupAddr)
Join an IPv6 multicast group.
Definition: ipv6.c:2035
Ipv6Addr
Definition: ipv6.h:251
#define Ipv6PseudoHeader
Definition: ipv6.h:42
#define ipv6IsMulticastAddr(ipAddr)
Definition: ipv6.h:132
#define ipv6IsLinkLocalUnicastAddr(ipAddr)
Definition: ipv6.h:124
#define IPV6_ADDR_LIST_SIZE
Definition: ipv6.h:65
#define ipv6CopyAddr(destIpAddr, srcIpAddr)
Definition: ipv6.h:116
@ IPV6_ADDR_STATE_DEPRECATED
An address assigned to an interface whose use is discouraged.
Definition: ipv6.h:169
@ IPV6_ADDR_STATE_PREFERRED
An address assigned to an interface whose use is unrestricted.
Definition: ipv6.h:168
const Ipv6Addr LLMNR_IPV6_MULTICAST_ADDR
Definition: llmnr_common.c:45
#define LLMNR_PORT
Definition: llmnr_common.h:53
#define LLMNR_DEFAULT_RESOURCE_RECORD_TTL
Definition: llmnr_common.h:47
#define LLMNR_MESSAGE_MAX_SIZE
Definition: llmnr_common.h:40
LlmnrHeader
Definition: llmnr_common.h:105
#define LLMNR_IPV4_MULTICAST_ADDR
Definition: llmnr_common.h:60
#define LLMNR_DEFAULT_RESPONSE_IP_TTL
Definition: llmnr_common.h:57
error_t llmnrSendResponse(NetInterface *interface, const IpAddr *destIpAddr, uint16_t destPort, uint16_t id, uint16_t qtype, uint16_t qclass)
Send LLMNR response message.
error_t llmnrFormatIpv4AddrRecord(NetInterface *interface, LlmnrHeader *message, size_t *length, Ipv4Addr ipv4Addr)
Format A resource record.
void llmnrProcessQuery(NetInterface *interface, const IpPseudoHeader *pseudoHeader, const UdpHeader *udpHeader, const NetBuffer *buffer, size_t offset, const NetRxAncillary *ancillary, void *param)
Process LLMNR query message.
error_t llmnrFormatIpv6AddrRecord(NetInterface *interface, LlmnrHeader *message, size_t *length, const Ipv6Addr *ipv6Addr)
Format AAAA resource record.
error_t llmnrResponderInit(NetInterface *interface)
LLMNR responder initialization.
LLMNR responder (Link-Local Multicast Name Resolution)
TCP/IP stack core.
#define NetInterface
Definition: net.h:36
void * netBufferAt(const NetBuffer *buffer, size_t offset)
Returns a pointer to the data at the specified position.
Definition: net_mem.c:415
void netBufferFree(NetBuffer *buffer)
Dispose a multi-part buffer.
Definition: net_mem.c:282
error_t netBufferSetLength(NetBuffer *buffer, size_t length)
Adjust the length of a multi-part buffer.
Definition: net_mem.c:322
size_t netBufferGetLength(const NetBuffer *buffer)
Get the actual length of a multi-part buffer.
Definition: net_mem.c:297
const NetTxAncillary NET_DEFAULT_TX_ANCILLARY
Definition: net_misc.c:71
#define NetRxAncillary
Definition: net_misc.h:40
#define NetTxAncillary
Definition: net_misc.h:36
#define TRUE
Definition: os_port.h:50
IP network address.
Definition: ip.h:79
IP pseudo header.
Definition: ip.h:99
Ipv4PseudoHeader ipv4Data
Definition: ip.h:104
Ipv6PseudoHeader ipv6Data
Definition: ip.h:107
size_t length
Definition: ip.h:100
IPv4 address entry.
Definition: ipv4.h:343
Ipv4AddrState state
IPv4 address state.
Definition: ipv4.h:345
Ipv4Addr addr
IPv4 address.
Definition: ipv4.h:344
IPv6 address entry.
Definition: ipv6.h:399
Ipv6AddrState state
IPv6 address state.
Definition: ipv6.h:401
Ipv6Addr addr
IPv6 address.
Definition: ipv6.h:400
Structure describing a buffer that spans multiple chunks.
Definition: net_mem.h:89
uint8_t length
Definition: tcp.h:368
uint16_t destPort
Definition: tcp.h:340
NetBuffer * udpAllocBuffer(size_t length, size_t *offset)
Allocate a buffer to hold a UDP packet.
Definition: udp.c:905
error_t udpSendBuffer(NetInterface *interface, const IpAddr *srcIpAddr, uint16_t srcPort, const IpAddr *destIpAddr, uint16_t destPort, NetBuffer *buffer, size_t offset, NetTxAncillary *ancillary)
Send a UDP datagram.
Definition: udp.c:627
error_t udpAttachRxCallback(NetInterface *interface, uint16_t port, UdpRxCallback callback, void *param)
Register user callback.
Definition: udp.c:978
UdpHeader
Definition: udp.h:85