Lorcan Smith
/
Enet_SPI
SNMP agent attached to SPI slave
Embed:
(wiki syntax)
Show/hide line numbers
udp.c
Go to the documentation of this file.
00001 /** 00002 * @file 00003 * User Datagram Protocol module 00004 v0.1: Debug statements in udp_connect 00005 * 00006 */ 00007 00008 /* 00009 * Copyright (c) 2001-2004 Swedish Institute of Computer Science. 00010 * All rights reserved. 00011 * 00012 * Redistribution and use in source and binary forms, with or without modification, 00013 * are permitted provided that the following conditions are met: 00014 * 00015 * 1. Redistributions of source code must retain the above copyright notice, 00016 * this list of conditions and the following disclaimer. 00017 * 2. Redistributions in binary form must reproduce the above copyright notice, 00018 * this list of conditions and the following disclaimer in the documentation 00019 * and/or other materials provided with the distribution. 00020 * 3. The name of the author may not be used to endorse or promote products 00021 * derived from this software without specific prior written permission. 00022 * 00023 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 00024 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 00025 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 00026 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00027 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 00028 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00029 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00030 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 00031 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY 00032 * OF SUCH DAMAGE. 00033 * 00034 * This file is part of the lwIP TCP/IP stack. 00035 * 00036 * Author: Adam Dunkels <adam@sics.se> 00037 * 00038 */ 00039 00040 00041 /* udp.c 00042 * 00043 * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828). 00044 * 00045 */ 00046 00047 /* @todo Check the use of '(struct udp_pcb).chksum_len_rx'! 00048 */ 00049 00050 #include "lwip/opt.h" 00051 00052 #if LWIP_UDP /* don't build if not configured for use in lwipopts.h */ 00053 00054 #include "lwip/udp.h" 00055 #include "lwip/def.h" 00056 #include "lwip/memp.h" 00057 #include "lwip/inet_chksum.h" 00058 #include "lwip/ip_addr.h" 00059 #include "lwip/netif.h" 00060 #include "lwip/icmp.h" 00061 #include "lwip/stats.h" 00062 #include "lwip/snmp.h" 00063 #include "arch/perf.h" 00064 #include "lwip/dhcp.h " 00065 00066 #include <string.h> 00067 #include <stdio.h> 00068 00069 /** 00070 * UDP_DEBUG: Enable debugging in UDP. 00071 */ 00072 #ifndef UDP_DEBUG 00073 #define UDP_DEBUG LWIP_DBG_ON 00074 #endif 00075 00076 /* The list of UDP PCBs */ 00077 /* exported in udp.h (was static) */ 00078 struct udp_pcb *udp_pcbs; 00079 00080 /** 00081 * Process an incoming UDP datagram. 00082 * 00083 * Given an incoming UDP datagram (as a chain of pbufs) this function 00084 * finds a corresponding UDP PCB and hands over the pbuf to the pcbs 00085 * recv function. If no pcb is found or the datagram is incorrect, the 00086 * pbuf is freed. 00087 * 00088 * @param p pbuf to be demultiplexed to a UDP PCB. 00089 * @param inp network interface on which the datagram was received. 00090 * 00091 */ 00092 void 00093 udp_input(struct pbuf *p, struct netif *inp) 00094 { 00095 struct udp_hdr *udphdr; 00096 struct udp_pcb *pcb, *prev; 00097 struct udp_pcb *uncon_pcb; 00098 struct ip_hdr *iphdr; 00099 u16_t src, dest; 00100 u8_t local_match; 00101 u8_t broadcast; 00102 00103 PERF_START; 00104 00105 UDP_STATS_INC(udp.recv); 00106 00107 iphdr = (struct ip_hdr *)p->payload; 00108 00109 /* Check minimum length (IP header + UDP header) 00110 * and move payload pointer to UDP header */ 00111 if (p->tot_len < (IPH_HL(iphdr) * 4 + UDP_HLEN) || pbuf_header(p, -(s16_t)(IPH_HL(iphdr) * 4))) { 00112 /* drop short packets */ 00113 LWIP_DEBUGF(UDP_DEBUG, 00114 ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len)); 00115 UDP_STATS_INC(udp.lenerr); 00116 UDP_STATS_INC(udp.drop); 00117 snmp_inc_udpinerrors(); 00118 pbuf_free(p); 00119 goto end; 00120 } 00121 00122 udphdr = (struct udp_hdr *)p->payload; 00123 00124 /* is broadcast packet ? */ 00125 broadcast = ip_addr_isbroadcast(¤t_iphdr_dest, inp); 00126 00127 LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len)); 00128 // printf("\r\nUDP_input: received datagram of length %"U16_F"\r\n", p->tot_len); 00129 00130 /* convert src and dest ports to host byte order */ 00131 src = ntohs(udphdr->src); 00132 dest = ntohs(udphdr->dest); 00133 00134 udp_debug_print(udphdr); 00135 00136 /* print the UDP source and destination */ 00137 LWIP_DEBUGF(UDP_DEBUG, 00138 ("udp (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") <-- " 00139 "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", 00140 ip4_addr1_16(&iphdr->dest), ip4_addr2_16(&iphdr->dest), 00141 ip4_addr3_16(&iphdr->dest), ip4_addr4_16(&iphdr->dest), ntohs(udphdr->dest), 00142 ip4_addr1_16(&iphdr->src), ip4_addr2_16(&iphdr->src), 00143 ip4_addr3_16(&iphdr->src), ip4_addr4_16(&iphdr->src), ntohs(udphdr->src))); 00144 00145 #if LWIP_DHCP 00146 pcb = NULL; 00147 /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by 00148 the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */ 00149 if (dest == DHCP_CLIENT_PORT) { 00150 /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */ 00151 if (src == DHCP_SERVER_PORT) { 00152 if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) { 00153 /* accept the packe if 00154 (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY! 00155 - inp->dhcp->pcb->remote == ANY or iphdr->src */ 00156 if ((ip_addr_isany(&inp->dhcp->pcb->remote_ip) || 00157 ip_addr_cmp(&(inp->dhcp->pcb->remote_ip), ¤t_iphdr_src))) { 00158 pcb = inp->dhcp->pcb; 00159 } 00160 } 00161 } 00162 } else 00163 #endif /* LWIP_DHCP */ 00164 { 00165 prev = NULL; 00166 local_match = 0; 00167 uncon_pcb = NULL; 00168 /* Iterate through the UDP pcb list for a matching pcb. 00169 * 'Perfect match' pcbs (connected to the remote port & ip address) are 00170 * preferred. If no perfect match is found, the first unconnected pcb that 00171 * matches the local port and ip address gets the datagram. */ 00172 for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) { 00173 local_match = 0; 00174 /* print the PCB local and remote address */ 00175 LWIP_DEBUGF(UDP_DEBUG, 00176 ("pcb (%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F") --- " 00177 "(%"U16_F".%"U16_F".%"U16_F".%"U16_F", %"U16_F")\n", 00178 ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), 00179 ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), pcb->local_port, 00180 ip4_addr1_16(&pcb->remote_ip), ip4_addr2_16(&pcb->remote_ip), 00181 ip4_addr3_16(&pcb->remote_ip), ip4_addr4_16(&pcb->remote_ip), pcb->remote_port)); 00182 00183 /* compare PCB local addr+port to UDP destination addr+port */ 00184 if ((pcb->local_port == dest) && 00185 ((!broadcast && ip_addr_isany(&pcb->local_ip)) || 00186 ip_addr_cmp(&(pcb->local_ip), ¤t_iphdr_dest) || 00187 #if LWIP_IGMP 00188 ip_addr_ismulticast(¤t_iphdr_dest) || 00189 #endif /* LWIP_IGMP */ 00190 #if IP_SOF_BROADCAST_RECV 00191 (broadcast && (pcb->so_options & SOF_BROADCAST)))) { 00192 #else /* IP_SOF_BROADCAST_RECV */ 00193 (broadcast))) { 00194 #endif /* IP_SOF_BROADCAST_RECV */ 00195 local_match = 1; 00196 if ((uncon_pcb == NULL) && 00197 ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { 00198 /* the first unconnected matching PCB */ 00199 uncon_pcb = pcb; 00200 } 00201 } 00202 /* compare PCB remote addr+port to UDP source addr+port */ 00203 if ((local_match != 0) && 00204 (pcb->remote_port == src) && 00205 (ip_addr_isany(&pcb->remote_ip) || 00206 ip_addr_cmp(&(pcb->remote_ip), ¤t_iphdr_src))) { 00207 /* the first fully matching PCB */ 00208 if (prev != NULL) { 00209 /* move the pcb to the front of udp_pcbs so that is 00210 found faster next time */ 00211 prev->next = pcb->next; 00212 pcb->next = udp_pcbs; 00213 udp_pcbs = pcb; 00214 } else { 00215 UDP_STATS_INC(udp.cachehit); 00216 } 00217 break; 00218 } 00219 prev = pcb; 00220 } 00221 /* no fully matching pcb found? then look for an unconnected pcb */ 00222 if (pcb == NULL) { 00223 pcb = uncon_pcb; 00224 } 00225 } 00226 00227 /* Check checksum if this is a match or if it was directed at us. */ 00228 if (pcb != NULL || ip_addr_cmp(&inp->ip_addr, ¤t_iphdr_dest)) { 00229 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\r\n")); 00230 #if LWIP_UDPLITE 00231 if (IPH_PROTO(iphdr) == IP_PROTO_UDPLITE) { 00232 /* Do the UDP Lite checksum */ 00233 #if CHECKSUM_CHECK_UDP 00234 u16_t chklen = ntohs(udphdr->len); 00235 if (chklen < sizeof(struct udp_hdr)) { 00236 if (chklen == 0) { 00237 /* For UDP-Lite, checksum length of 0 means checksum 00238 over the complete packet (See RFC 3828 chap. 3.1) */ 00239 chklen = p->tot_len; 00240 } else { 00241 /* At least the UDP-Lite header must be covered by the 00242 checksum! (Again, see RFC 3828 chap. 3.1) */ 00243 UDP_STATS_INC(udp.chkerr); 00244 UDP_STATS_INC(udp.drop); 00245 snmp_inc_udpinerrors(); 00246 pbuf_free(p); 00247 goto end; 00248 } 00249 } 00250 if (inet_chksum_pseudo_partial(p, ¤t_iphdr_src, ¤t_iphdr_dest, 00251 IP_PROTO_UDPLITE, p->tot_len, chklen) != 0) { 00252 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, 00253 ("udp_input: UDP Lite datagram discarded due to failing checksum\n")); 00254 UDP_STATS_INC(udp.chkerr); 00255 UDP_STATS_INC(udp.drop); 00256 snmp_inc_udpinerrors(); 00257 pbuf_free(p); 00258 goto end; 00259 } 00260 #endif /* CHECKSUM_CHECK_UDP */ 00261 } else 00262 #endif /* LWIP_UDPLITE */ 00263 { 00264 #if CHECKSUM_CHECK_UDP 00265 if (udphdr->chksum != 0) { 00266 if (inet_chksum_pseudo(p, ip_current_src_addr(), ip_current_dest_addr(), 00267 IP_PROTO_UDP, p->tot_len) != 0) { 00268 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, 00269 ("udp_input: UDP datagram discarded due to failing checksum\n")); 00270 UDP_STATS_INC(udp.chkerr); 00271 UDP_STATS_INC(udp.drop); 00272 snmp_inc_udpinerrors(); 00273 pbuf_free(p); 00274 goto end; 00275 } 00276 } 00277 #endif /* CHECKSUM_CHECK_UDP */ 00278 } 00279 if(pbuf_header(p, -UDP_HLEN)) { 00280 /* Can we cope with this failing? Just assert for now */ 00281 LWIP_ASSERT("pbuf_header failed\n", 0); 00282 UDP_STATS_INC(udp.drop); 00283 snmp_inc_udpinerrors(); 00284 pbuf_free(p); 00285 goto end; 00286 } 00287 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: OK 1\r\n")); 00288 if (pcb != NULL) { 00289 snmp_inc_udpindatagrams(); 00290 #if SO_REUSE && SO_REUSE_RXTOALL 00291 if ((broadcast || ip_addr_ismulticast(¤t_iphdr_dest)) && 00292 ((pcb->so_options & SOF_REUSEADDR) != 0)) { 00293 /* pass broadcast- or multicast packets to all multicast pcbs 00294 if SOF_REUSEADDR is set on the first match */ 00295 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: OK 2\r\n")); 00296 struct udp_pcb *mpcb; 00297 u8_t p_header_changed = 0; 00298 for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) { 00299 if (mpcb != pcb) { 00300 /* compare PCB local addr+port to UDP destination addr+port */ 00301 if ((mpcb->local_port == dest) && 00302 ((!broadcast && ip_addr_isany(&mpcb->local_ip)) || 00303 ip_addr_cmp(&(mpcb->local_ip), ¤t_iphdr_dest) || 00304 #if LWIP_IGMP 00305 ip_addr_ismulticast(¤t_iphdr_dest) || 00306 #endif /* LWIP_IGMP */ 00307 #if IP_SOF_BROADCAST_RECV 00308 (broadcast && (mpcb->so_options & SOF_BROADCAST)))) { 00309 #else /* IP_SOF_BROADCAST_RECV */ 00310 (broadcast))) { 00311 #endif /* IP_SOF_BROADCAST_RECV */ 00312 /* pass a copy of the packet to all local matches */ 00313 if (mpcb->recv != NULL) { 00314 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: OK 3\r\n")); 00315 struct pbuf *q; 00316 /* for that, move payload to IP header again */ 00317 if (p_header_changed == 0) { 00318 pbuf_header(p, (s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); 00319 p_header_changed = 1; 00320 } 00321 q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM); 00322 if (q != NULL) { 00323 err_t err = pbuf_copy(q, p); 00324 if (err == ERR_OK) { 00325 /* move payload to UDP data */ 00326 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: OK 4\r\n")); 00327 pbuf_header(q, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); 00328 mpcb->recv(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src); 00329 } 00330 } 00331 } 00332 } 00333 } 00334 } 00335 if (p_header_changed) { 00336 /* and move payload to UDP data again */ 00337 pbuf_header(p, -(s16_t)((IPH_HL(iphdr) * 4) + UDP_HLEN)); 00338 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: OK 5\r\n")); 00339 } 00340 } 00341 #endif /* SO_REUSE && SO_REUSE_RXTOALL */ 00342 /* callback */ 00343 if (pcb->recv != NULL) { 00344 /* now the recv function is responsible for freeing p */ 00345 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: OK 6: src %d\r\n", src)); 00346 pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr(), src); 00347 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: OK 6a\r\n")); 00348 } else { 00349 /* no recv function registered? then we have to free the pbuf! */ 00350 pbuf_free(p); 00351 goto end; 00352 } 00353 } else { 00354 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n")); 00355 00356 #if LWIP_ICMP 00357 /* No match was found, send ICMP destination port unreachable unless 00358 destination address was broadcast/multicast. */ 00359 if (!broadcast && 00360 !ip_addr_ismulticast(¤t_iphdr_dest)) { 00361 /* move payload pointer back to ip header */ 00362 pbuf_header(p, (IPH_HL(iphdr) * 4) + UDP_HLEN); 00363 LWIP_ASSERT("p->payload == iphdr", (p->payload == iphdr)); 00364 icmp_dest_unreach(p, ICMP_DUR_PORT); 00365 } 00366 #endif /* LWIP_ICMP */ 00367 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: OK 7\r\n")); 00368 UDP_STATS_INC(udp.proterr); 00369 UDP_STATS_INC(udp.drop); 00370 snmp_inc_udpnoports(); 00371 pbuf_free(p); 00372 } 00373 } else { 00374 pbuf_free(p); 00375 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: OK 8\r\n")); 00376 } 00377 end: 00378 PERF_STOP("udp_input"); 00379 } 00380 00381 /** 00382 * Send data using UDP. 00383 * 00384 * @param pcb UDP PCB used to send the data. 00385 * @param p chain of pbuf's to be sent. 00386 * 00387 * The datagram will be sent to the current remote_ip & remote_port 00388 * stored in pcb. If the pcb is not bound to a port, it will 00389 * automatically be bound to a random port. 00390 * 00391 * @return lwIP error code. 00392 * - ERR_OK. Successful. No error occured. 00393 * - ERR_MEM. Out of memory. 00394 * - ERR_RTE. Could not find route to destination address. 00395 * - More errors could be returned by lower protocol layers. 00396 * 00397 * @see udp_disconnect() udp_sendto() 00398 */ 00399 err_t 00400 udp_send(struct udp_pcb *pcb, struct pbuf *p) 00401 { 00402 /* send to the packet using remote ip and port stored in the pcb */ 00403 return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port); 00404 } 00405 00406 #if LWIP_CHECKSUM_ON_COPY 00407 /** Same as udp_send() but with checksum 00408 */ 00409 err_t 00410 udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p, 00411 u8_t have_chksum, u16_t chksum) 00412 { 00413 /* send to the packet using remote ip and port stored in the pcb */ 00414 return udp_sendto_chksum(pcb, p, &pcb->remote_ip, pcb->remote_port, 00415 have_chksum, chksum); 00416 } 00417 #endif /* LWIP_CHECKSUM_ON_COPY */ 00418 00419 /** 00420 * Send data to a specified address using UDP. 00421 * 00422 * @param pcb UDP PCB used to send the data. 00423 * @param p chain of pbuf's to be sent. 00424 * @param dst_ip Destination IP address. 00425 * @param dst_port Destination UDP port. 00426 * 00427 * dst_ip & dst_port are expected to be in the same byte order as in the pcb. 00428 * 00429 * If the PCB already has a remote address association, it will 00430 * be restored after the data is sent. 00431 * 00432 * @return lwIP error code (@see udp_send for possible error codes) 00433 * 00434 * @see udp_disconnect() udp_send() 00435 */ 00436 err_t 00437 udp_sendto(struct udp_pcb *pcb, struct pbuf *p, 00438 ip_addr_t *dst_ip, u16_t dst_port) 00439 { 00440 #if LWIP_CHECKSUM_ON_COPY 00441 return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0); 00442 } 00443 00444 /** Same as udp_sendto(), but with checksum */ 00445 err_t 00446 udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, 00447 u16_t dst_port, u8_t have_chksum, u16_t chksum) 00448 { 00449 #endif /* LWIP_CHECKSUM_ON_COPY */ 00450 struct netif *netif; 00451 00452 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n")); 00453 00454 /* find the outgoing network interface for this packet */ 00455 #if LWIP_IGMP 00456 netif = ip_route((ip_addr_ismulticast(dst_ip))?(&(pcb->multicast_ip)):(dst_ip)); 00457 #else 00458 netif = ip_route(dst_ip); 00459 #endif /* LWIP_IGMP */ 00460 00461 /* no outgoing network interface could be found? */ 00462 if (netif == NULL) { 00463 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n", 00464 ip4_addr1_16(dst_ip), ip4_addr2_16(dst_ip), ip4_addr3_16(dst_ip), ip4_addr4_16(dst_ip))); 00465 UDP_STATS_INC(udp.rterr); 00466 return ERR_RTE; 00467 } 00468 #if LWIP_CHECKSUM_ON_COPY 00469 return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum); 00470 #else /* LWIP_CHECKSUM_ON_COPY */ 00471 return udp_sendto_if(pcb, p, dst_ip, dst_port, netif); 00472 #endif /* LWIP_CHECKSUM_ON_COPY */ 00473 } 00474 00475 /** 00476 * Send data to a specified address using UDP. 00477 * The netif used for sending can be specified. 00478 * 00479 * This function exists mainly for DHCP, to be able to send UDP packets 00480 * on a netif that is still down. 00481 * 00482 * @param pcb UDP PCB used to send the data. 00483 * @param p chain of pbuf's to be sent. 00484 * @param dst_ip Destination IP address. 00485 * @param dst_port Destination UDP port. 00486 * @param netif the netif used for sending. 00487 * 00488 * dst_ip & dst_port are expected to be in the same byte order as in the pcb. 00489 * 00490 * @return lwIP error code (@see udp_send for possible error codes) 00491 * 00492 * @see udp_disconnect() udp_send() 00493 */ 00494 err_t 00495 udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p, 00496 ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif) 00497 { 00498 #if LWIP_CHECKSUM_ON_COPY 00499 return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0); 00500 } 00501 00502 /** Same as udp_sendto_if(), but with checksum */ 00503 err_t 00504 udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip, 00505 u16_t dst_port, struct netif *netif, u8_t have_chksum, 00506 u16_t chksum) 00507 { 00508 #endif /* LWIP_CHECKSUM_ON_COPY */ 00509 struct udp_hdr *udphdr; 00510 ip_addr_t *src_ip; 00511 err_t err; 00512 struct pbuf *q; /* q will be sent down the stack */ 00513 00514 #if IP_SOF_BROADCAST 00515 /* broadcast filter? */ 00516 if ( ((pcb->so_options & SOF_BROADCAST) == 0) && ip_addr_isbroadcast(dst_ip, netif) ) { 00517 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, 00518 ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb)); 00519 return ERR_VAL; 00520 } 00521 #endif /* IP_SOF_BROADCAST */ 00522 00523 /* if the PCB is not yet bound to a port, bind it here */ 00524 if (pcb->local_port == 0) { 00525 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n")); 00526 err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); 00527 printf("\r\nudp_send: not yet bound to a port, binding to %d\r\n", pcb->local_port); 00528 if (err != ERR_OK) { 00529 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n")); 00530 return err; 00531 } 00532 } 00533 00534 /* not enough space to add an UDP header to first pbuf in given p chain? */ 00535 if (pbuf_header(p, UDP_HLEN)) { 00536 /* allocate header in a separate new pbuf */ 00537 q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); 00538 /* new header pbuf could not be allocated? */ 00539 if (q == NULL) { 00540 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n")); 00541 return ERR_MEM; 00542 } 00543 /* chain header q in front of given pbuf p */ 00544 pbuf_chain(q, p); 00545 /* first pbuf q points to header pbuf */ 00546 LWIP_DEBUGF(UDP_DEBUG, 00547 ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p)); 00548 } else { 00549 /* adding space for header within p succeeded */ 00550 /* first pbuf q equals given pbuf */ 00551 q = p; 00552 LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p)); 00553 } 00554 LWIP_ASSERT("check that first pbuf can hold struct udp_hdr", 00555 (q->len >= sizeof(struct udp_hdr))); 00556 /* q now represents the packet to be sent */ 00557 udphdr = (struct udp_hdr *)q->payload; 00558 udphdr->src = htons(pcb->local_port); 00559 udphdr->dest = htons(dst_port); 00560 printf("udp_sendto_if: local %d dst %d q->len %d\r\n", pcb->local_port, dst_port, q->len); 00561 /* in UDP, 0 checksum means 'no checksum' */ 00562 udphdr->chksum = 0x0000; 00563 00564 /* Multicast Loop? */ 00565 #if LWIP_IGMP 00566 if (ip_addr_ismulticast(dst_ip) && ((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0)) { 00567 q->flags |= PBUF_FLAG_MCASTLOOP; 00568 } 00569 #endif /* LWIP_IGMP */ 00570 00571 00572 /* PCB local address is IP_ANY_ADDR? */ 00573 if (ip_addr_isany(&pcb->local_ip)) { 00574 /* use outgoing network interface IP address as source address */ 00575 src_ip = &(netif->ip_addr); 00576 } else { 00577 /* check if UDP PCB local IP address is correct 00578 * this could be an old address if netif->ip_addr has changed */ 00579 if (!ip_addr_cmp(&(pcb->local_ip), &(netif->ip_addr))) { 00580 /* local_ip doesn't match, drop the packet */ 00581 if (q != p) { 00582 /* free the header pbuf */ 00583 pbuf_free(q); 00584 q = NULL; 00585 /* p is still referenced by the caller, and will live on */ 00586 } 00587 return ERR_VAL; 00588 } 00589 /* use UDP PCB local IP address as source address */ 00590 src_ip = &(pcb->local_ip); 00591 } 00592 00593 LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len)); 00594 //printf("\r\nudp_sendto_if: sending datagram of length %"U16_F"\r\n", q->tot_len); 00595 00596 #if LWIP_UDPLITE 00597 /* UDP Lite protocol? */ 00598 if (pcb->flags & UDP_FLAGS_UDPLITE) { 00599 u16_t chklen, chklen_hdr; 00600 LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len)); 00601 /* set UDP message length in UDP header */ 00602 chklen_hdr = chklen = pcb->chksum_len_tx; 00603 printf("\r\nudp_sendto_if: LWIP_UDPLITE chklen %d"\r\n", chklen); 00604 if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) { 00605 if (chklen != 0) { 00606 LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen)); 00607 } 00608 /* For UDP-Lite, checksum length of 0 means checksum 00609 over the complete packet. (See RFC 3828 chap. 3.1) 00610 At least the UDP-Lite header must be covered by the 00611 checksum, therefore, if chksum_len has an illegal 00612 value, we generate the checksum over the complete 00613 packet to be safe. */ 00614 chklen_hdr = 0; 00615 chklen = q->tot_len; 00616 } 00617 udphdr->len = htons(chklen_hdr); 00618 /* calculate checksum */ 00619 #if CHECKSUM_GEN_UDP 00620 udphdr->chksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, 00621 IP_PROTO_UDPLITE, q->tot_len, 00622 #if !LWIP_CHECKSUM_ON_COPY 00623 chklen); 00624 #else /* !LWIP_CHECKSUM_ON_COPY */ 00625 (have_chksum ? UDP_HLEN : chklen)); 00626 if (have_chksum) { 00627 u32_t acc; 00628 acc = udphdr->chksum + (u16_t)~(chksum); 00629 udphdr->chksum = FOLD_U32T(acc); 00630 } 00631 #endif /* !LWIP_CHECKSUM_ON_COPY */ 00632 00633 /* chksum zero must become 0xffff, as zero means 'no checksum' */ 00634 if (udphdr->chksum == 0x0000) { 00635 udphdr->chksum = 0xffff; 00636 } 00637 #endif /* CHECKSUM_GEN_UDP */ 00638 /* output to IP */ 00639 LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDPLITE,)\n")); 00640 #if LWIP_NETIF_HWADDRHINT 00641 netif->addr_hint = &(pcb->addr_hint); 00642 #endif /* LWIP_NETIF_HWADDRHINT*/ 00643 err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDPLITE, netif); 00644 #if LWIP_NETIF_HWADDRHINT 00645 netif->addr_hint = NULL; 00646 #endif /* LWIP_NETIF_HWADDRHINT*/ 00647 } else 00648 #endif /* LWIP_UDPLITE */ 00649 { /* UDP */ 00650 LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len)); 00651 //printf("\r\nudp_sendto_if: UDP packet length %d\r\n", q->tot_len); 00652 udphdr->len = htons(q->tot_len); 00653 /* calculate checksum */ 00654 #if CHECKSUM_GEN_UDP 00655 if ((pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) { 00656 u16_t udpchksum; 00657 #if LWIP_CHECKSUM_ON_COPY 00658 if (have_chksum) { 00659 u32_t acc; 00660 udpchksum = inet_chksum_pseudo_partial(q, src_ip, dst_ip, IP_PROTO_UDP, 00661 q->tot_len, UDP_HLEN); 00662 acc = udpchksum + (u16_t)~(chksum); 00663 udpchksum = FOLD_U32T(acc); 00664 } else 00665 #endif /* LWIP_CHECKSUM_ON_COPY */ 00666 { 00667 udpchksum = inet_chksum_pseudo(q, src_ip, dst_ip, IP_PROTO_UDP, q->tot_len); 00668 } 00669 00670 /* chksum zero must become 0xffff, as zero means 'no checksum' */ 00671 if (udpchksum == 0x0000) { 00672 udpchksum = 0xffff; 00673 } 00674 udphdr->chksum = udpchksum; 00675 } 00676 #endif /* CHECKSUM_GEN_UDP */ 00677 LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum)); 00678 LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,IP_PROTO_UDP,)\n")); 00679 /* output to IP */ 00680 #if LWIP_NETIF_HWADDRHINT 00681 netif->addr_hint = &(pcb->addr_hint); 00682 #endif /* LWIP_NETIF_HWADDRHINT*/ 00683 err = ip_output_if(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP, netif); 00684 printf("udp_sendto_if: ip_output_if error %d\r\n", err); 00685 #if LWIP_NETIF_HWADDRHINT 00686 netif->addr_hint = NULL; 00687 #endif /* LWIP_NETIF_HWADDRHINT*/ 00688 } 00689 /* TODO: must this be increased even if error occured? */ 00690 snmp_inc_udpoutdatagrams(); 00691 00692 /* did we chain a separate header pbuf earlier? */ 00693 if (q != p) { 00694 /* free the header pbuf */ 00695 pbuf_free(q); 00696 q = NULL; 00697 /* p is still referenced by the caller, and will live on */ 00698 } 00699 00700 UDP_STATS_INC(udp.xmit); 00701 return err; 00702 } 00703 00704 /** 00705 * Bind an UDP PCB. 00706 * 00707 * @param pcb UDP PCB to be bound with a local address ipaddr and port. 00708 * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to 00709 * bind to all local interfaces. 00710 * @param port local UDP port to bind with. Use 0 to automatically bind 00711 * to a random port between UDP_LOCAL_PORT_RANGE_START and 00712 * UDP_LOCAL_PORT_RANGE_END. 00713 * 00714 * ipaddr & port are expected to be in the same byte order as in the pcb. 00715 * 00716 * @return lwIP error code. 00717 * - ERR_OK. Successful. No error occured. 00718 * - ERR_USE. The specified ipaddr and port are already bound to by 00719 * another UDP PCB. 00720 * 00721 * @see udp_disconnect() 00722 */ 00723 err_t 00724 udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) 00725 { 00726 struct udp_pcb *ipcb; 00727 u8_t rebind; 00728 00729 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = ")); 00730 ip_addr_debug_print(UDP_DEBUG, ipaddr); 00731 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port)); 00732 printf("\r\nUDP_bind: IP Address %d.%d.%d.%d\r\n", ipaddr[0], ipaddr[1], ipaddr[2], ipaddr[3]); 00733 printf("\r\nUDP_bind: port %"U16_F"\r\n", port); 00734 00735 rebind = 0; 00736 /* Check for double bind and rebind of the same pcb */ 00737 for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { 00738 /* is this UDP PCB already on active list? */ 00739 if (pcb == ipcb) { 00740 /* pcb may occur at most once in active list */ 00741 LWIP_ASSERT("rebind == 0", rebind == 0); 00742 /* pcb already in list, just rebind */ 00743 rebind = 1; 00744 } 00745 00746 /* By default, we don't allow to bind to a port that any other udp 00747 PCB is alread bound to, unless *all* PCBs with that port have tha 00748 REUSEADDR flag set. */ 00749 #if SO_REUSE 00750 else if (((pcb->so_options & SOF_REUSEADDR) == 0) && 00751 ((ipcb->so_options & SOF_REUSEADDR) == 0)) { 00752 #else /* SO_REUSE */ 00753 /* port matches that of PCB in list and REUSEADDR not set -> reject */ 00754 else { 00755 #endif /* SO_REUSE */ 00756 if ((ipcb->local_port == port) && 00757 /* IP address matches, or one is IP_ADDR_ANY? */ 00758 (ip_addr_isany(&(ipcb->local_ip)) || 00759 ip_addr_isany(ipaddr) || 00760 ip_addr_cmp(&(ipcb->local_ip), ipaddr))) { 00761 /* other PCB already binds to this local IP and port */ 00762 LWIP_DEBUGF(UDP_DEBUG, 00763 ("udp_bind: local port %"U16_F" already bound by another pcb\n", port)); 00764 return ERR_USE; 00765 } 00766 } 00767 } 00768 00769 ip_addr_set(&pcb->local_ip, ipaddr); 00770 00771 /* no port specified? */ 00772 if (port == 0) { 00773 #ifndef UDP_LOCAL_PORT_RANGE_START 00774 #define UDP_LOCAL_PORT_RANGE_START 4096 00775 #define UDP_LOCAL_PORT_RANGE_END 0x7fff 00776 #endif 00777 port = UDP_LOCAL_PORT_RANGE_START; 00778 ipcb = udp_pcbs; 00779 while ((ipcb != NULL) && (port != UDP_LOCAL_PORT_RANGE_END)) { 00780 if (ipcb->local_port == port) { 00781 /* port is already used by another udp_pcb */ 00782 port++; 00783 /* restart scanning all udp pcbs */ 00784 ipcb = udp_pcbs; 00785 } else { 00786 /* go on with next udp pcb */ 00787 ipcb = ipcb->next; 00788 } 00789 } 00790 if (ipcb != NULL) { 00791 /* no more ports available in local range */ 00792 LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n")); 00793 return ERR_USE; 00794 } 00795 } 00796 pcb->local_port = port; 00797 snmp_insert_udpidx_tree(pcb); 00798 /* pcb not active yet? */ 00799 if (rebind == 0) { 00800 /* place the PCB on the active list if not already there */ 00801 pcb->next = udp_pcbs; 00802 udp_pcbs = pcb; 00803 } 00804 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, 00805 ("udp_bind: bound to %"U16_F".%"U16_F".%"U16_F".%"U16_F", port %"U16_F"\n", 00806 ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), 00807 ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), 00808 pcb->local_port)); 00809 return ERR_OK; 00810 } 00811 /** 00812 * Connect an UDP PCB. 00813 * 00814 * This will associate the UDP PCB with the remote address. 00815 * 00816 * @param pcb UDP PCB to be connected with remote address ipaddr and port. 00817 * @param ipaddr remote IP address to connect with. 00818 * @param port remote UDP port to connect with. 00819 * 00820 * @return lwIP error code 00821 * 00822 * ipaddr & port are expected to be in the same byte order as in the pcb. 00823 * 00824 * The udp pcb is bound to a random local port if not already bound. 00825 * 00826 * @see udp_disconnect() 00827 */ 00828 err_t 00829 udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port) 00830 { 00831 struct udp_pcb *ipcb; 00832 00833 if (pcb->local_port == 0) { 00834 printf("Bind to local port: %d\r\n", pcb->local_port); // v0.1 DEBUG 00835 err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port); 00836 if (err != ERR_OK) { 00837 return err; 00838 } 00839 } 00840 00841 ip_addr_set(&pcb->remote_ip, ipaddr); 00842 pcb->remote_port = port; 00843 pcb->flags |= UDP_FLAGS_CONNECTED; 00844 printf("Remote port: %d and flags %d\r\n", pcb->remote_port, pcb->flags); // v0.1 DEBUG 00845 /** TODO: this functionality belongs in upper layers */ 00846 #ifdef LWIP_UDP_TODO 00847 /* Nail down local IP for netconn_addr()/getsockname() */ 00848 if (ip_addr_isany(&pcb->local_ip) && !ip_addr_isany(&pcb->remote_ip)) { 00849 struct netif *netif; 00850 00851 if ((netif = ip_route(&(pcb->remote_ip))) == NULL) { 00852 LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n", pcb->remote_ip.addr)); 00853 UDP_STATS_INC(udp.rterr); 00854 return ERR_RTE; 00855 } 00856 /** TODO: this will bind the udp pcb locally, to the interface which 00857 is used to route output packets to the remote address. However, we 00858 might want to accept incoming packets on any interface! */ 00859 pcb->local_ip = netif->ip_addr; 00860 } else if (ip_addr_isany(&pcb->remote_ip)) { 00861 pcb->local_ip.addr = 0; 00862 } 00863 #endif 00864 LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, 00865 ("udp_connect: connected to %"U16_F".%"U16_F".%"U16_F".%"U16_F",port %"U16_F"\n", 00866 ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), 00867 ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), 00868 pcb->local_port)); 00869 printf("udp_connect: connected to %d.%d.%d.%d, port %d\r\n", 00870 ip4_addr1_16(&pcb->local_ip), ip4_addr2_16(&pcb->local_ip), 00871 ip4_addr3_16(&pcb->local_ip), ip4_addr4_16(&pcb->local_ip), 00872 pcb->local_port); 00873 00874 /* Insert UDP PCB into the list of active UDP PCBs. */ 00875 for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { 00876 if (pcb == ipcb) { 00877 /* already on the list, just return */ 00878 return ERR_OK; 00879 } 00880 } 00881 /* PCB not yet on the list, add PCB now */ 00882 pcb->next = udp_pcbs; 00883 udp_pcbs = pcb; 00884 return ERR_OK; 00885 } 00886 00887 /** 00888 * Disconnect a UDP PCB 00889 * 00890 * @param pcb the udp pcb to disconnect. 00891 */ 00892 void 00893 udp_disconnect(struct udp_pcb *pcb) 00894 { 00895 /* reset remote address association */ 00896 ip_addr_set_any(&pcb->remote_ip); 00897 pcb->remote_port = 0; 00898 /* mark PCB as unconnected */ 00899 pcb->flags &= ~UDP_FLAGS_CONNECTED; 00900 } 00901 00902 /** 00903 * Set a receive callback for a UDP PCB 00904 * 00905 * This callback will be called when receiving a datagram for the pcb. 00906 * 00907 * @param pcb the pcb for wich to set the recv callback 00908 * @param recv function pointer of the callback function 00909 * @param recv_arg additional argument to pass to the callback function 00910 */ 00911 void 00912 udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg) 00913 { 00914 /* remember recv() callback and user data */ 00915 pcb->recv = recv; 00916 pcb->recv_arg = recv_arg; 00917 } 00918 00919 /** 00920 * Remove an UDP PCB. 00921 * 00922 * @param pcb UDP PCB to be removed. The PCB is removed from the list of 00923 * UDP PCB's and the data structure is freed from memory. 00924 * 00925 * @see udp_new() 00926 */ 00927 void 00928 udp_remove(struct udp_pcb *pcb) 00929 { 00930 struct udp_pcb *pcb2; 00931 00932 snmp_delete_udpidx_tree(pcb); 00933 /* pcb to be removed is first in list? */ 00934 if (udp_pcbs == pcb) { 00935 /* make list start at 2nd pcb */ 00936 udp_pcbs = udp_pcbs->next; 00937 /* pcb not 1st in list */ 00938 } else { 00939 for (pcb2 = udp_pcbs; pcb2 != NULL; pcb2 = pcb2->next) { 00940 /* find pcb in udp_pcbs list */ 00941 if (pcb2->next != NULL && pcb2->next == pcb) { 00942 /* remove pcb from list */ 00943 pcb2->next = pcb->next; 00944 } 00945 } 00946 } 00947 memp_free(MEMP_UDP_PCB, pcb); 00948 } 00949 00950 /** 00951 * Create a UDP PCB. 00952 * 00953 * @return The UDP PCB which was created. NULL if the PCB data structure 00954 * could not be allocated. 00955 * 00956 * @see udp_remove() 00957 */ 00958 struct udp_pcb * 00959 udp_new(void) 00960 { 00961 struct udp_pcb *pcb; 00962 pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB); 00963 /* could allocate UDP PCB? */ 00964 if (pcb != NULL) { 00965 /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0 00966 * which means checksum is generated over the whole datagram per default 00967 * (recommended as default by RFC 3828). */ 00968 /* initialize PCB to all zeroes */ 00969 memset(pcb, 0, sizeof(struct udp_pcb)); 00970 pcb->ttl = UDP_TTL; 00971 } 00972 return pcb; 00973 } 00974 00975 #if UDP_DEBUG 00976 /** 00977 * Print UDP header information for debug purposes. 00978 * 00979 * @param udphdr pointer to the udp header in memory. 00980 */ 00981 void 00982 udp_debug_print(struct udp_hdr *udphdr) 00983 { 00984 printf("UDP header:\r\n"); 00985 LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n")); 00986 LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); 00987 LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n", 00988 ntohs(udphdr->src), ntohs(udphdr->dest))); 00989 LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); 00990 LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n", 00991 ntohs(udphdr->len), ntohs(udphdr->chksum))); 00992 LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n")); 00993 } 00994 #endif /* UDP_DEBUG */ 00995 00996 #endif /* LWIP_UDP */
Generated on Tue Jul 12 2022 19:17:31 by 1.7.2