Rtos API example

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_raw.c Source File

lwip_raw.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  * Implementation of raw protocol PCBs for low-level handling of
00004  * different types of protocols besides (or overriding) those
00005  * already available in lwIP.\n
00006  * See also @ref raw_raw
00007  * 
00008  * @defgroup raw_raw RAW
00009  * @ingroup callbackstyle_api
00010  * Implementation of raw protocol PCBs for low-level handling of
00011  * different types of protocols besides (or overriding) those
00012  * already available in lwIP.\n
00013  * @see @ref raw_api
00014  */
00015 
00016 /*
00017  * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
00018  * All rights reserved.
00019  *
00020  * Redistribution and use in source and binary forms, with or without modification,
00021  * are permitted provided that the following conditions are met:
00022  *
00023  * 1. Redistributions of source code must retain the above copyright notice,
00024  *    this list of conditions and the following disclaimer.
00025  * 2. Redistributions in binary form must reproduce the above copyright notice,
00026  *    this list of conditions and the following disclaimer in the documentation
00027  *    and/or other materials provided with the distribution.
00028  * 3. The name of the author may not be used to endorse or promote products
00029  *    derived from this software without specific prior written permission.
00030  *
00031  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
00032  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00033  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
00034  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00035  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
00036  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00037  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00038  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
00039  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
00040  * OF SUCH DAMAGE.
00041  *
00042  * This file is part of the lwIP TCP/IP stack.
00043  *
00044  * Author: Adam Dunkels <adam@sics.se>
00045  *
00046  */
00047 
00048 #include "lwip/opt.h"
00049 
00050 #if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
00051 
00052 #include "lwip/def.h"
00053 #include "lwip/memp.h"
00054 #include "lwip/ip_addr.h"
00055 #include "lwip/netif.h"
00056 #include "lwip/raw.h"
00057 #include "lwip/stats.h"
00058 #include "lwip/ip6.h"
00059 #include "lwip/ip6_addr.h"
00060 #include "lwip/inet_chksum.h"
00061 
00062 #include <string.h>
00063 
00064 /** The list of RAW PCBs */
00065 static struct raw_pcb *raw_pcbs;
00066 
00067 static u8_t
00068 raw_input_match(struct raw_pcb *pcb, u8_t broadcast)
00069 {
00070   LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */
00071 
00072 #if LWIP_IPV4 && LWIP_IPV6
00073   /* Dual-stack: PCBs listening to any IP type also listen to any IP address */
00074   if (IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
00075 #if IP_SOF_BROADCAST_RECV
00076     if ((broadcast != 0) && !ip_get_option(pcb, SOF_BROADCAST)) {
00077       return 0;
00078     }
00079 #endif /* IP_SOF_BROADCAST_RECV */
00080     return 1;
00081   }
00082 #endif /* LWIP_IPV4 && LWIP_IPV6 */
00083 
00084   /* Only need to check PCB if incoming IP version matches PCB IP version */
00085   if (IP_ADDR_PCB_VERSION_MATCH_EXACT(pcb, ip_current_dest_addr())) {
00086 #if LWIP_IPV4
00087     /* Special case: IPv4 broadcast: receive all broadcasts
00088      * Note: broadcast variable can only be 1 if it is an IPv4 broadcast */
00089     if (broadcast != 0) {
00090 #if IP_SOF_BROADCAST_RECV
00091       if (ip_get_option(pcb, SOF_BROADCAST))
00092 #endif /* IP_SOF_BROADCAST_RECV */
00093       {
00094         if (ip4_addr_isany(ip_2_ip4(&pcb->local_ip))) {
00095           return 1;
00096         }
00097       }
00098     } else
00099 #endif /* LWIP_IPV4 */
00100     /* Handle IPv4 and IPv6: catch all or exact match */
00101     if (ip_addr_isany(&pcb->local_ip) ||
00102        ip_addr_cmp(&pcb->local_ip, ip_current_dest_addr())) {
00103       return 1;
00104     }
00105   }
00106 
00107   return 0;
00108 }
00109 
00110 /**
00111  * Determine if in incoming IP packet is covered by a RAW PCB
00112  * and if so, pass it to a user-provided receive callback function.
00113  *
00114  * Given an incoming IP datagram (as a chain of pbufs) this function
00115  * finds a corresponding RAW PCB and calls the corresponding receive
00116  * callback function.
00117  *
00118  * @param p pbuf to be demultiplexed to a RAW PCB.
00119  * @param inp network interface on which the datagram was received.
00120  * @return - 1 if the packet has been eaten by a RAW PCB receive
00121  *           callback function. The caller MAY NOT not reference the
00122  *           packet any longer, and MAY NOT call pbuf_free().
00123  * @return - 0 if packet is not eaten (pbuf is still referenced by the
00124  *           caller).
00125  *
00126  */
00127 u8_t
00128 raw_input(struct pbuf *p, struct netif *inp)
00129 {
00130   struct raw_pcb *pcb, *prev;
00131   s16_t proto;
00132   u8_t eaten = 0;
00133   u8_t broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
00134 
00135   LWIP_UNUSED_ARG(inp);
00136 
00137 #if LWIP_IPV6
00138 #if LWIP_IPV4
00139   if (IP_HDR_GET_VERSION(p->payload) == 6)
00140 #endif /* LWIP_IPV4 */
00141   {
00142     struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload;
00143     proto = IP6H_NEXTH(ip6hdr);
00144   }
00145 #if LWIP_IPV4
00146   else
00147 #endif /* LWIP_IPV4 */
00148 #endif /* LWIP_IPV6 */
00149 #if LWIP_IPV4
00150   {
00151     proto = IPH_PROTO((struct ip_hdr *)p->payload);
00152   }
00153 #endif /* LWIP_IPV4 */
00154 
00155   prev = NULL;
00156   pcb = raw_pcbs;
00157   /* loop through all raw pcbs until the packet is eaten by one */
00158   /* this allows multiple pcbs to match against the packet by design */
00159   while ((eaten == 0) && (pcb != NULL)) {
00160     if ((pcb->protocol == proto) && raw_input_match(pcb, broadcast)) {
00161       /* receive callback function available? */
00162       if (pcb->recv != NULL) {
00163 #ifndef LWIP_NOASSERT
00164         void* old_payload = p->payload;
00165 #endif
00166         /* the receive callback function did not eat the packet? */
00167         eaten = pcb->recv(pcb->recv_arg, pcb, p, ip_current_src_addr());
00168         if (eaten != 0) {
00169           /* receive function ate the packet */
00170           p = NULL;
00171           eaten = 1;
00172           if (prev != NULL) {
00173           /* move the pcb to the front of raw_pcbs so that is
00174              found faster next time */
00175             prev->next = pcb->next;
00176             pcb->next = raw_pcbs;
00177             raw_pcbs = pcb;
00178           }
00179         } else {
00180           /* sanity-check that the receive callback did not alter the pbuf */
00181           LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet",
00182             p->payload == old_payload);
00183         }
00184       }
00185       /* no receive callback function was set for this raw PCB */
00186     }
00187     /* drop the packet */
00188     prev = pcb;
00189     pcb = pcb->next;
00190   }
00191   return eaten;
00192 }
00193 
00194 /**
00195  * @ingroup raw_raw
00196  * Bind a RAW PCB.
00197  *
00198  * @param pcb RAW PCB to be bound with a local address ipaddr.
00199  * @param ipaddr local IP address to bind with. Use IP4_ADDR_ANY to
00200  * bind to all local interfaces.
00201  *
00202  * @return lwIP error code.
00203  * - ERR_OK. Successful. No error occurred.
00204  * - ERR_USE. The specified IP address is already bound to by
00205  * another RAW PCB.
00206  *
00207  * @see raw_disconnect()
00208  */
00209 err_t
00210 raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
00211 {
00212   if ((pcb == NULL) || (ipaddr == NULL)) {
00213     return ERR_VAL;
00214   }
00215   ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
00216   return ERR_OK;
00217 }
00218 
00219 /**
00220  * @ingroup raw_raw
00221  * Connect an RAW PCB. This function is required by upper layers
00222  * of lwip. Using the raw api you could use raw_sendto() instead
00223  *
00224  * This will associate the RAW PCB with the remote address.
00225  *
00226  * @param pcb RAW PCB to be connected with remote address ipaddr and port.
00227  * @param ipaddr remote IP address to connect with.
00228  *
00229  * @return lwIP error code
00230  *
00231  * @see raw_disconnect() and raw_sendto()
00232  */
00233 err_t
00234 raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
00235 {
00236   if ((pcb == NULL) || (ipaddr == NULL)) {
00237     return ERR_VAL;
00238   }
00239   ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
00240   return ERR_OK;
00241 }
00242 
00243 /**
00244  * @ingroup raw_raw
00245  * Set the callback function for received packets that match the
00246  * raw PCB's protocol and binding.
00247  *
00248  * The callback function MUST either
00249  * - eat the packet by calling pbuf_free() and returning non-zero. The
00250  *   packet will not be passed to other raw PCBs or other protocol layers.
00251  * - not free the packet, and return zero. The packet will be matched
00252  *   against further PCBs and/or forwarded to another protocol layers.
00253  */
00254 void
00255 raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
00256 {
00257   /* remember recv() callback and user data */
00258   pcb->recv = recv;
00259   pcb->recv_arg = recv_arg;
00260 }
00261 
00262 /**
00263  * @ingroup raw_raw
00264  * Send the raw IP packet to the given address. Note that actually you cannot
00265  * modify the IP headers (this is inconsistent with the receive callback where
00266  * you actually get the IP headers), you can only specify the IP payload here.
00267  * It requires some more changes in lwIP. (there will be a raw_send() function
00268  * then.)
00269  *
00270  * @param pcb the raw pcb which to send
00271  * @param p the IP payload to send
00272  * @param ipaddr the destination address of the IP packet
00273  *
00274  */
00275 err_t
00276 raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr)
00277 {
00278   err_t err;
00279   struct netif *netif;
00280   const ip_addr_t *src_ip;
00281   struct pbuf *q; /* q will be sent down the stack */
00282   s16_t header_size;
00283 
00284   if ((pcb == NULL) || (ipaddr == NULL) || !IP_ADDR_PCB_VERSION_MATCH(pcb, ipaddr)) {
00285     return ERR_VAL;
00286   }
00287 
00288   LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n"));
00289 
00290   header_size = (
00291 #if LWIP_IPV4 && LWIP_IPV6
00292     IP_IS_V6(ipaddr) ? IP6_HLEN : IP_HLEN);
00293 #elif LWIP_IPV4
00294     IP_HLEN);
00295 #else
00296     IP6_HLEN);
00297 #endif
00298 
00299   /* not enough space to add an IP header to first pbuf in given p chain? */
00300   if (pbuf_header(p, header_size)) {
00301     /* allocate header in new pbuf */
00302     q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM);
00303     /* new header pbuf could not be allocated? */
00304     if (q == NULL) {
00305       LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n"));
00306       return ERR_MEM;
00307     }
00308     if (p->tot_len != 0) {
00309       /* chain header q in front of given pbuf p */
00310       pbuf_chain(q, p);
00311     }
00312     /* { first pbuf q points to header pbuf } */
00313     LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
00314   } else {
00315     /* first pbuf q equals given pbuf */
00316     q = p;
00317     if (pbuf_header(q, -header_size)) {
00318       LWIP_ASSERT("Can't restore header we just removed!", 0);
00319       return ERR_MEM;
00320     }
00321   }
00322 
00323   if(IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
00324     /* Don't call ip_route() with IP_ANY_TYPE */
00325     netif = ip_route(IP46_ADDR_ANY(IP_GET_TYPE(ipaddr)), ipaddr);
00326   } else {
00327     netif = ip_route(&pcb->local_ip, ipaddr);
00328   }
00329 
00330   if (netif == NULL) {
00331     LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to "));
00332     ip_addr_debug_print(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ipaddr);
00333     /* free any temporary header pbuf allocated by pbuf_header() */
00334     if (q != p) {
00335       pbuf_free(q);
00336     }
00337     return ERR_RTE;
00338   }
00339 
00340 #if IP_SOF_BROADCAST
00341   if (IP_IS_V4(ipaddr))
00342   {
00343     /* broadcast filter? */
00344     if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) {
00345       LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
00346       /* free any temporary header pbuf allocated by pbuf_header() */
00347       if (q != p) {
00348         pbuf_free(q);
00349       }
00350       return ERR_VAL;
00351     }
00352   }
00353 #endif /* IP_SOF_BROADCAST */
00354 
00355   if (ip_addr_isany(&pcb->local_ip)) {
00356     /* use outgoing network interface IP address as source address */
00357     src_ip = ip_netif_get_local_ip(netif, ipaddr);
00358 #if LWIP_IPV6
00359     if (src_ip == NULL) {
00360       if (q != p) {
00361         pbuf_free(q);
00362       }
00363       return ERR_RTE;
00364     }
00365 #endif /* LWIP_IPV6 */
00366   } else {
00367     /* use RAW PCB local IP address as source address */
00368     src_ip = &pcb->local_ip;
00369   }
00370 
00371 #if LWIP_IPV6
00372   /* If requested, based on the IPV6_CHECKSUM socket option per RFC3542,
00373      compute the checksum and update the checksum in the payload. */
00374   if (IP_IS_V6(ipaddr) && pcb->chksum_reqd) {
00375     u16_t chksum = ip6_chksum_pseudo(p, pcb->protocol, p->tot_len, ip_2_ip6(src_ip), ip_2_ip6(ipaddr));
00376     LWIP_ASSERT("Checksum must fit into first pbuf", p->len >= (pcb->chksum_offset + 2));
00377     SMEMCPY(((u8_t *)p->payload) + pcb->chksum_offset, &chksum, sizeof(u16_t));
00378   }
00379 #endif
00380 
00381   NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint);
00382   err = ip_output_if(q, src_ip, ipaddr, pcb->ttl, pcb->tos, pcb->protocol, netif);
00383   NETIF_SET_HWADDRHINT(netif, NULL);
00384 
00385   /* did we chain a header earlier? */
00386   if (q != p) {
00387     /* free the header */
00388     pbuf_free(q);
00389   }
00390   return err;
00391 }
00392 
00393 /**
00394  * @ingroup raw_raw
00395  * Send the raw IP packet to the address given by raw_connect()
00396  *
00397  * @param pcb the raw pcb which to send
00398  * @param p the IP payload to send
00399  *
00400  */
00401 err_t
00402 raw_send(struct raw_pcb *pcb, struct pbuf *p)
00403 {
00404   return raw_sendto(pcb, p, &pcb->remote_ip);
00405 }
00406 
00407 /**
00408  * @ingroup raw_raw
00409  * Remove an RAW PCB.
00410  *
00411  * @param pcb RAW PCB to be removed. The PCB is removed from the list of
00412  * RAW PCB's and the data structure is freed from memory.
00413  *
00414  * @see raw_new()
00415  */
00416 void
00417 raw_remove(struct raw_pcb *pcb)
00418 {
00419   struct raw_pcb *pcb2;
00420   /* pcb to be removed is first in list? */
00421   if (raw_pcbs == pcb) {
00422     /* make list start at 2nd pcb */
00423     raw_pcbs = raw_pcbs->next;
00424     /* pcb not 1st in list */
00425   } else {
00426     for (pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
00427       /* find pcb in raw_pcbs list */
00428       if (pcb2->next != NULL && pcb2->next == pcb) {
00429         /* remove pcb from list */
00430         pcb2->next = pcb->next;
00431         break;
00432       }
00433     }
00434   }
00435   memp_free(MEMP_RAW_PCB, pcb);
00436 }
00437 
00438 /**
00439  * @ingroup raw_raw
00440  * Create a RAW PCB.
00441  *
00442  * @return The RAW PCB which was created. NULL if the PCB data structure
00443  * could not be allocated.
00444  *
00445  * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP)
00446  *
00447  * @see raw_remove()
00448  */
00449 struct raw_pcb *
00450 raw_new(u8_t proto)
00451 {
00452   struct raw_pcb *pcb;
00453 
00454   LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n"));
00455 
00456   pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB);
00457   /* could allocate RAW PCB? */
00458   if (pcb != NULL) {
00459     /* initialize PCB to all zeroes */
00460     memset(pcb, 0, sizeof(struct raw_pcb));
00461     pcb->protocol = proto;
00462     pcb->ttl = RAW_TTL;
00463     pcb->next = raw_pcbs;
00464     raw_pcbs = pcb;
00465   }
00466   return pcb;
00467 }
00468 
00469 /**
00470  * @ingroup raw_raw
00471  * Create a RAW PCB for specific IP type.
00472  *
00473  * @return The RAW PCB which was created. NULL if the PCB data structure
00474  * could not be allocated.
00475  *
00476  * @param type IP address type, see @ref lwip_ip_addr_type definitions.
00477  * If you want to listen to IPv4 and IPv6 (dual-stack) packets,
00478  * supply @ref IPADDR_TYPE_ANY as argument and bind to @ref IP_ANY_TYPE.
00479  * @param proto the protocol number (next header) of the IPv6 packet payload
00480  *              (e.g. IP6_NEXTH_ICMP6)
00481  *
00482  * @see raw_remove()
00483  */
00484 struct raw_pcb *
00485 raw_new_ip_type(u8_t type, u8_t proto)
00486 {
00487   struct raw_pcb *pcb;
00488   pcb = raw_new(proto);
00489 #if LWIP_IPV4 && LWIP_IPV6
00490   if (pcb != NULL) {
00491     IP_SET_TYPE_VAL(pcb->local_ip,  type);
00492     IP_SET_TYPE_VAL(pcb->remote_ip, type);
00493   }
00494 #else /* LWIP_IPV4 && LWIP_IPV6 */
00495   LWIP_UNUSED_ARG(type);
00496 #endif /* LWIP_IPV4 && LWIP_IPV6 */
00497   return pcb;
00498 }
00499 
00500 /** This function is called from netif.c when address is changed
00501  *
00502  * @param old_addr IP address of the netif before change
00503  * @param new_addr IP address of the netif after change
00504  */
00505 void raw_netif_ip_addr_changed(const ip_addr_t* old_addr, const ip_addr_t* new_addr)
00506 {
00507   struct raw_pcb* rpcb;
00508 
00509   if (!ip_addr_isany(old_addr) && !ip_addr_isany(new_addr)) {
00510     for (rpcb = raw_pcbs; rpcb != NULL; rpcb = rpcb->next) {
00511       /* PCB bound to current local interface address? */
00512       if (ip_addr_cmp(&rpcb->local_ip, old_addr)) {
00513         /* The PCB is bound to the old ipaddr and
00514          * is set to bound to the new one instead */
00515         ip_addr_copy(rpcb->local_ip, *new_addr);
00516       }
00517     }
00518   }
00519 }
00520 
00521 #endif /* LWIP_RAW */