From mboxrd@z Thu Jan 1 00:00:00 1970 Return-path: Received: from mail-la0-x22f.google.com ([2a00:1450:4010:c03::22f]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1ZGusV-0007m3-TE for barebox@lists.infradead.org; Sun, 19 Jul 2015 20:08:43 +0000 Received: by lahh5 with SMTP id h5so86540778lah.2 for ; Sun, 19 Jul 2015 13:07:42 -0700 (PDT) From: Antony Pavlov Date: Sun, 19 Jul 2015 23:07:15 +0300 Message-Id: <1437336443-8076-9-git-send-email-antonynpavlov@gmail.com> In-Reply-To: <1437336443-8076-1-git-send-email-antonynpavlov@gmail.com> References: <1437336443-8076-1-git-send-email-antonynpavlov@gmail.com> List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "barebox" Errors-To: barebox-bounces+u.kleine-koenig=pengutronix.de@lists.infradead.org Subject: [RFC v2 08/16] net: import picotcp from github To: barebox@lists.infradead.org Original repo: https://github.com/tass-belgium/picotcp The last original repo commit is v1.5.0 commit f6d139147d86d74f9a53ba3c02b2a5b276858d95 Author: laurens Date: Thu Jun 25 12:54:38 2015 +0100 Remove old check from fragments module from development branch Import mini-howto: $ cd barebox.git $ rm -rf net/picotcp $ git clone --branch ${branch_name} ${PICOTCP_REPO} net/picotcp $ rm -rf net/picotcp/* $ ( cd net/picotcp; git checkout .gitignore COPYING LICENSE README.md ) $ ( cd net/picotcp; mkdir include modules stack ) $ ( cd net/picotcp/include; git checkout heap.h pico_addressing.h pico_config.h pico_constants.h pico_device.h pico_eth.h pico_frame.h pico_module_eth.h pico_protocol.h pico_queue.h pico_socket.h pico_socket_multicast.h pico_stack.h pico_tree.h ) $ ( cd net/picotcp/modules; git checkout pico_aodv.h pico_arp.c pico_arp.h pico_dev_loop.c pico_dev_loop.h pico_dev_mock.h pico_dev_null.c pico_dev_null.h pico_dhcp_client.c pico_dhcp_client.h pico_dhcp_common.c pico_dhcp_common.h pico_dns_client.h pico_dns_common.h pico_dns_sd.h pico_fragments.c pico_fragments.h pico_icmp4.c pico_icmp4.h pico_icmp6.h pico_igmp.h pico_ipfilter.h pico_ipv4.c pico_ipv4.h pico_ipv6.h pico_ipv6_nd.h pico_mm.h pico_nat.h pico_olsr.h pico_socket_tcp.h pico_socket_udp.c pico_socket_udp.h pico_tcp.h pico_udp.c pico_udp.h ) $ ( cd net/picotcp/stack; git checkout pico_device.c pico_frame.c pico_protocol.c pico_socket.c pico_socket_multicast.c pico_stack.c pico_tree.c ) $ rm -rf net/picotcp/.git $ git add net/picotcp/ $ git commit -s Signed-off-by: Antony Pavlov --- net/picotcp/.gitignore | 24 + net/picotcp/COPYING | 8 + net/picotcp/LICENSE | 339 ++++ net/picotcp/README.md | 33 + net/picotcp/include/heap.h | 83 + net/picotcp/include/pico_addressing.h | 52 + net/picotcp/include/pico_config.h | 229 +++ net/picotcp/include/pico_constants.h | 54 + net/picotcp/include/pico_device.h | 54 + net/picotcp/include/pico_eth.h | 21 + net/picotcp/include/pico_frame.h | 122 ++ net/picotcp/include/pico_module_eth.h | 33 + net/picotcp/include/pico_protocol.h | 95 ++ net/picotcp/include/pico_queue.h | 166 ++ net/picotcp/include/pico_socket.h | 268 ++++ net/picotcp/include/pico_socket_multicast.h | 10 + net/picotcp/include/pico_stack.h | 87 ++ net/picotcp/include/pico_tree.h | 93 ++ net/picotcp/modules/pico_aodv.h | 131 ++ net/picotcp/modules/pico_arp.c | 531 +++++++ net/picotcp/modules/pico_arp.h | 32 + net/picotcp/modules/pico_dev_loop.c | 66 + net/picotcp/modules/pico_dev_loop.h | 15 + net/picotcp/modules/pico_dev_mock.h | 47 + net/picotcp/modules/pico_dev_null.c | 60 + net/picotcp/modules/pico_dev_null.h | 15 + net/picotcp/modules/pico_dhcp_client.c | 960 ++++++++++++ net/picotcp/modules/pico_dhcp_client.h | 30 + net/picotcp/modules/pico_dhcp_common.c | 189 +++ net/picotcp/modules/pico_dhcp_common.h | 186 +++ net/picotcp/modules/pico_dns_client.h | 46 + net/picotcp/modules/pico_dns_common.h | 521 +++++++ net/picotcp/modules/pico_dns_sd.h | 90 ++ net/picotcp/modules/pico_fragments.c | 345 +++++ net/picotcp/modules/pico_fragments.h | 11 + net/picotcp/modules/pico_icmp4.c | 395 +++++ net/picotcp/modules/pico_icmp4.h | 162 ++ net/picotcp/modules/pico_icmp6.h | 259 ++++ net/picotcp/modules/pico_igmp.h | 26 + net/picotcp/modules/pico_ipfilter.h | 29 + net/picotcp/modules/pico_ipv4.c | 1574 +++++++++++++++++++ net/picotcp/modules/pico_ipv4.h | 124 ++ net/picotcp/modules/pico_ipv6.h | 153 ++ net/picotcp/modules/pico_ipv6_nd.h | 26 + net/picotcp/modules/pico_mm.h | 98 ++ net/picotcp/modules/pico_nat.h | 90 ++ net/picotcp/modules/pico_olsr.h | 32 + net/picotcp/modules/pico_socket_tcp.h | 33 + net/picotcp/modules/pico_socket_udp.c | 250 +++ net/picotcp/modules/pico_socket_udp.h | 19 + net/picotcp/modules/pico_tcp.h | 102 ++ net/picotcp/modules/pico_udp.c | 216 +++ net/picotcp/modules/pico_udp.h | 45 + net/picotcp/stack/pico_device.c | 408 +++++ net/picotcp/stack/pico_frame.c | 286 ++++ net/picotcp/stack/pico_protocol.c | 214 +++ net/picotcp/stack/pico_socket.c | 2213 +++++++++++++++++++++++++++ net/picotcp/stack/pico_socket_multicast.c | 956 ++++++++++++ net/picotcp/stack/pico_stack.c | 1157 ++++++++++++++ net/picotcp/stack/pico_tree.c | 575 +++++++ 60 files changed, 14488 insertions(+) diff --git a/net/picotcp/.gitignore b/net/picotcp/.gitignore new file mode 100644 index 0000000..88593da --- /dev/null +++ b/net/picotcp/.gitignore @@ -0,0 +1,24 @@ +*.d +*.o +*.a +*.out +*.swp +tags +build +UNIT_* +core +core.* +.DS_Store +cscope.files +cscope.out +*.so +*.aux +*.pdf +*.toc +*.gz +*.log +*.pyc +*.elf +*.gcov +*.gcda +*.gcno diff --git a/net/picotcp/COPYING b/net/picotcp/COPYING new file mode 100644 index 0000000..8a5fe4b --- /dev/null +++ b/net/picotcp/COPYING @@ -0,0 +1,8 @@ +PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. + +Released under the GNU General Public License, version 2. +See LICENSE for details. + +Different licensing models may exist, at the sole discretion of +the Copyright holders. + diff --git a/net/picotcp/LICENSE b/net/picotcp/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/net/picotcp/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/net/picotcp/README.md b/net/picotcp/README.md new file mode 100644 index 0000000..9e32eb7 --- /dev/null +++ b/net/picotcp/README.md @@ -0,0 +1,33 @@ +picoTCP + +--------------- + +Welcome to the one and only picoTCP repository. + +picoTCP is a small-footprint, modular TCP/IP stack designed for embedded systems and the Internet of Things. It's actively being developed by *[Altran Intelligent Systems](http://intelligent-systems.altran.com/)*. + +This code is released under the terms of GNU GPL v2 only. Some rights reserved. +Other licenses may apply at the sole discretion of the copyright holders. + +Learn how to use picoTCP in your project by going through the Getting Started guide on our [GitHub wiki](https://github.com/tass-belgium/picotcp/wiki). + +For more information visit the [picoTCP website](http://www.picotcp.com), send us an email or contact us on [Twitter](https://twitter.com/picotcp), [Facebook](https://www.facebook.com/picoTCP) or [Reddit](http://www.reddit.com/r/picotcp/). + +Wondering about picoTCP's code quality? Check [our TiCS score](http://tics.picotcp.com:42506/TIOBEPortal/TICS/treeviewer?) + + +--------------- + +Continuous integration + +Jenkins Functional tests: +[![Jenkins autotest](http://jenkins.picotcp.com:8080/buildStatus/icon?job=PicoTCP_rel_autotest)](http://jenkins.picotcp.com:8080/job/PicoTCP_rel_autotest) + +Jenkins Unit tests : +[![Jenkins unit tests](http://jenkins.picotcp.com:8080/buildStatus/icon?job=PicoTCP_rel_unit_tests)](http://jenkins.picotcp.com:8080/job/PicoTCP_rel_unit_tests) + +Jenkins RFC compliance : +[![Jenkins RFC Compliance](http://jenkins.picotcp.com:8080/buildStatus/icon?job=PicoTCP_rel_RF_mbed)](http://jenkins.picotcp.com:8080/job/PicoTCP_rel_RF_mbed) + +Jenkins TICS quality : +[![Jenkins TICS](http://jenkins.picotcp.com:8080/buildStatus/icon?job=PicoTCP_rel_TICS)](http://jenkins.picotcp.com:8080/job/PicoTCP_rel_TICS/) diff --git a/net/picotcp/include/heap.h b/net/picotcp/include/heap.h new file mode 100644 index 0000000..88f495d --- /dev/null +++ b/net/picotcp/include/heap.h @@ -0,0 +1,83 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ + +#define DECLARE_HEAP(type, orderby) \ + struct heap_ ## type { \ + uint32_t size; \ + uint32_t n; \ + type *top; \ + }; \ + typedef struct heap_ ## type heap_ ## type; \ + static inline int heap_insert(struct heap_ ## type *heap, type * el) \ + { \ + uint32_t i; \ + type *newTop; \ + if (++heap->n >= heap->size) { \ + newTop = PICO_ZALLOC((heap->n + 1) * sizeof(type)); \ + if(!newTop) { \ + heap->n--; \ + return -1; \ + } \ + if (heap->top) { \ + memcpy(newTop, heap->top, heap->n * sizeof(type)); \ + PICO_FREE(heap->top); \ + } \ + heap->top = newTop; \ + heap->size++; \ + } \ + if (heap->n == 1) { \ + memcpy(&heap->top[1], el, sizeof(type)); \ + return 0; \ + } \ + for (i = heap->n; ((i > 1) && (heap->top[i / 2].orderby > el->orderby)); i /= 2) { \ + memcpy(&heap->top[i], &heap->top[i / 2], sizeof(type)); \ + } \ + memcpy(&heap->top[i], el, sizeof(type)); \ + return 0; \ + } \ + static inline int heap_peek(struct heap_ ## type *heap, type * first) \ + { \ + type *last; \ + uint32_t i, child; \ + if(heap->n == 0) { \ + return -1; \ + } \ + memcpy(first, &heap->top[1], sizeof(type)); \ + last = &heap->top[heap->n--]; \ + for(i = 1; (i * 2u) <= heap->n; i = child) { \ + child = 2u * i; \ + if ((child != heap->n) && \ + (heap->top[child + 1]).orderby \ + < (heap->top[child]).orderby) \ + child++; \ + if (last->orderby > \ + heap->top[child].orderby) \ + memcpy(&heap->top[i], &heap->top[child], \ + sizeof(type)); \ + else \ + break; \ + } \ + memcpy(&heap->top[i], last, sizeof(type)); \ + return 0; \ + } \ + static inline type *heap_first(heap_ ## type * heap) \ + { \ + if (heap->n == 0) \ + return NULL; \ + return &heap->top[1]; \ + } \ + static inline heap_ ## type *heap_init(void) \ + { \ + heap_ ## type * p = (heap_ ## type *)PICO_ZALLOC(sizeof(heap_ ## type)); \ + return p; \ + } \ + /*static inline void heap_destroy(heap_ ## type * h) \ + { \ + PICO_FREE(h->top); \ + PICO_FREE(h); \ + } \*/ + + diff --git a/net/picotcp/include/pico_addressing.h b/net/picotcp/include/pico_addressing.h new file mode 100644 index 0000000..de62281 --- /dev/null +++ b/net/picotcp/include/pico_addressing.h @@ -0,0 +1,52 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef INCLUDE_PICO_ADDRESSING +#define INCLUDE_PICO_ADDRESSING + +#include "pico_config.h" + +PACKED_STRUCT_DEF pico_ip4 +{ + uint32_t addr; +}; + +PACKED_STRUCT_DEF pico_ip6 +{ + uint8_t addr[16]; +}; + +union pico_address +{ + struct pico_ip4 ip4; + struct pico_ip6 ip6; +}; + +PACKED_STRUCT_DEF pico_eth +{ + uint8_t addr[6]; + uint8_t padding[2]; +}; + +extern const uint8_t PICO_ETHADDR_ALL[]; + + +PACKED_STRUCT_DEF pico_trans +{ + uint16_t sport; + uint16_t dport; + +}; + +/* Here are some protocols. */ +#define PICO_PROTO_IPV4 0 +#define PICO_PROTO_ICMP4 1 +#define PICO_PROTO_IGMP 2 +#define PICO_PROTO_TCP 6 +#define PICO_PROTO_UDP 17 +#define PICO_PROTO_IPV6 41 +#define PICO_PROTO_ICMP6 58 + +#endif diff --git a/net/picotcp/include/pico_config.h b/net/picotcp/include/pico_config.h new file mode 100644 index 0000000..6b9c040 --- /dev/null +++ b/net/picotcp/include/pico_config.h @@ -0,0 +1,229 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#include "pico_defines.h" +#ifndef INCLUDE_PICO_CONFIG +#define INCLUDE_PICO_CONFIG +#ifndef __KERNEL__ +#include +#include +#include +#else +#include +#endif + +#if defined __IAR_SYSTEMS_ICC__ || defined ATOP +# define PACKED_STRUCT_DEF __packed struct +# define PEDANTIC_STRUCT_DEF __packed struct +# define PACKED_UNION_DEF __packed union +# define WEAK +#else +# define PACKED_STRUCT_DEF struct __attribute__((packed)) +# define PEDANTIC_STRUCT_DEF struct +# define PACKED_UNION_DEF union /* Sane compilers do not require packed unions */ +# define WEAK __attribute__((weak)) +# ifdef __GNUC__ +# define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) +# if ((GCC_VERSION >= 40800)) +# define BYTESWAP_GCC +# endif +# endif +#endif + +#ifdef PICO_BIGENDIAN + +# define PICO_IDETH_IPV4 0x0800 +# define PICO_IDETH_ARP 0x0806 +# define PICO_IDETH_IPV6 0x86DD + +# define PICO_ARP_REQUEST 0x0001 +# define PICO_ARP_REPLY 0x0002 +# define PICO_ARP_HTYPE_ETH 0x0001 + +#define short_be(x) (x) +#define long_be(x) (x) +#define long_long_be(x) (x) + +static inline uint16_t short_from(void *_p) +{ + unsigned char *p = (unsigned char *)_p; + uint16_t r, p0, p1; + p0 = p[0]; + p1 = p[1]; + r = (p0 << 8) + p1; + return r; +} + +static inline uint32_t long_from(void *_p) +{ + unsigned char *p = (unsigned char *)_p; + uint32_t r, p0, p1, p2, p3; + p0 = p[0]; + p1 = p[1]; + p2 = p[2]; + p3 = p[3]; + r = (p0 << 24) + (p1 << 16) + (p2 << 8) + p3; + return r; +} + +#else + +static inline uint16_t short_from(void *_p) +{ + unsigned char *p = (unsigned char *)_p; + uint16_t r, _p0, _p1; + _p0 = p[0]; + _p1 = p[1]; + r = (uint16_t)((_p1 << 8u) + _p0); + return r; +} + +static inline uint32_t long_from(void *_p) +{ + unsigned char *p = (unsigned char *)_p; + uint32_t r, _p0, _p1, _p2, _p3; + _p0 = p[0]; + _p1 = p[1]; + _p2 = p[2]; + _p3 = p[3]; + r = (_p3 << 24) + (_p2 << 16) + (_p1 << 8) + _p0; + return r; +} + + +# define PICO_IDETH_IPV4 0x0008 +# define PICO_IDETH_ARP 0x0608 +# define PICO_IDETH_IPV6 0xDD86 + +# define PICO_ARP_REQUEST 0x0100 +# define PICO_ARP_REPLY 0x0200 +# define PICO_ARP_HTYPE_ETH 0x0100 + +# ifndef BYTESWAP_GCC +static inline uint16_t short_be(uint16_t le) +{ + return (uint16_t)(((le & 0xFFu) << 8) | ((le >> 8u) & 0xFFu)); +} + +static inline uint32_t long_be(uint32_t le) +{ + uint8_t *b = (uint8_t *)≤ + uint32_t be = 0; + uint32_t b0, b1, b2; + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + be = b[3] + (b2 << 8) + (b1 << 16) + (b0 << 24); + return be; +} +static inline uint64_t long_long_be(uint64_t le) +{ + uint8_t *b = (uint8_t *)≤ + uint64_t be = 0; + uint64_t b0, b1, b2, b3, b4, b5, b6; + b0 = b[0]; + b1 = b[1]; + b2 = b[2]; + b3 = b[3]; + b4 = b[4]; + b5 = b[5]; + b6 = b[6]; + be = b[7] + (b6 << 8) + (b5 << 16) + (b4 << 24) + (b3 << 32) + (b2 << 40) + (b1 << 48) + (b0 << 56); + return be; +} +# else +/* + extern uint32_t __builtin_bswap32(uint32_t); + extern uint16_t __builtin_bswap16(uint16_t); + extern uint64_t __builtin_bswap64(uint64_t); + */ + +static inline uint32_t long_be(uint32_t le) +{ + return (uint32_t)__builtin_bswap32(le); +} + +static inline uint16_t short_be(uint16_t le) +{ + return (uint16_t)__builtin_bswap16(le); +} + +static inline uint64_t long_long_be(uint64_t le) +{ + return (uint64_t)__builtin_bswap64(le); +} + +# endif /* BYTESWAP_GCC */ +#endif + + +/* Mockables */ +#if defined UNIT_TEST +# define MOCKABLE __attribute__((weak)) +#else +# define MOCKABLE +#endif + +#include "pico_constants.h" +#include "pico_mm.h" + +#define IGNORE_PARAMETER(x) ((void)x) + +#define PICO_MEM_DEFAULT_SLAB_SIZE 1600 +#define PICO_MEM_PAGE_SIZE 4096 +#define PICO_MEM_PAGE_LIFETIME 100 +#define PICO_MIN_HEAP_SIZE 600 +#define PICO_MIN_SLAB_SIZE 1200 +#define PICO_MAX_SLAB_SIZE 1600 +#define PICO_MEM_MINIMUM_OBJECT_SIZE 4 + + +/*** *** *** *** *** *** *** + *** PLATFORM SPECIFIC *** + *** *** *** *** *** *** ***/ +#if defined PICO_PORT_CUSTOM +# include "pico_port.h" +#elif defined CORTEX_M4_HARDFLOAT +# include "arch/pico_cortex_m.h" +#elif defined CORTEX_M4_SOFTFLOAT +# include "arch/pico_cortex_m.h" +#elif defined CORTEX_M3 +# include "arch/pico_cortex_m.h" +#elif defined PIC24 +# include "arch/pico_pic24.h" +#elif defined MSP430 +# include "arch/pico_msp430.h" +#elif defined MBED_TEST +# include "arch/pico_mbed.h" +#elif defined AVR +# include "arch/pico_avr.h" +#elif defined ARM9 +# include "arch/pico_arm9.h" +#elif defined ESP8266 +# include "arch/pico_esp8266.h" +#elif defined MT7681 +# include "arch/pico_generic_gcc.h" +#elif defined FAULTY +# include "../test/pico_faulty.h" +#elif defined ARCHNONE +# include "arch/pico_none.h" +#elif defined GENERIC +# include "arch/pico_generic_gcc.h" +#elif defined __KERNEL__ +# include "arch/pico_linux.h" +/* #elif defined ... */ +#else +# include "arch/pico_posix.h" +#endif + +#ifdef PICO_SUPPORT_MM +#define PICO_ZALLOC(x) pico_mem_zalloc(x) +#define PICO_FREE(x) pico_mem_free(x) +#else +#define PICO_ZALLOC(x) pico_zalloc(x) +#define PICO_FREE(x) pico_free(x) +#endif /* PICO_SUPPORT_MM */ + +#endif diff --git a/net/picotcp/include/pico_constants.h b/net/picotcp/include/pico_constants.h new file mode 100644 index 0000000..f043408 --- /dev/null +++ b/net/picotcp/include/pico_constants.h @@ -0,0 +1,54 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef INCLUDE_PICO_CONST +#define INCLUDE_PICO_CONST +/* Included from pico_config.h */ + +/** Non-endian dependant constants */ +#define PICO_SIZE_IP4 4 +#define PICO_SIZE_IP6 16 +#define PICO_SIZE_ETH 6 +#define PICO_SIZE_TRANS 8 + +/** Endian-dependant constants **/ +typedef uint64_t pico_time; +extern volatile uint64_t pico_tick; + + +/*** *** *** *** *** *** *** + *** ARP CONFIG *** + *** *** *** *** *** *** ***/ + +#include "pico_addressing.h" + +/* Maximum amount of accepted ARP requests per burst interval */ +#define PICO_ARP_MAX_RATE 1 +/* Duration of the burst interval in milliseconds */ +#define PICO_ARP_INTERVAL 1000 + +/* Add well-known host numbers here. (bigendian constants only beyond this point) */ +#define PICO_IP4_ANY (0x00000000U) +#define PICO_IP4_BCAST (0xffffffffU) + +/* defined in modules/pico_ipv6.c */ +#ifdef PICO_SUPPORT_IPV6 +extern const uint8_t PICO_IPV6_ANY[PICO_SIZE_IP6]; +#endif + +static inline uint32_t pico_hash(const void *buf, uint32_t size) +{ + uint32_t hash = 5381; + uint32_t i; + const uint8_t *ptr = (const uint8_t *)buf; + for(i = 0; i < size; i++) + hash = ((hash << 5) + hash) + ptr[i]; /* hash * 33 + char */ + return hash; +} + +/* Debug */ +/* #define PICO_SUPPORT_DEBUG_MEMORY */ +/* #define PICO_SUPPORT_DEBUG_TOOLS */ +#endif diff --git a/net/picotcp/include/pico_device.h b/net/picotcp/include/pico_device.h new file mode 100644 index 0000000..a2d03ec --- /dev/null +++ b/net/picotcp/include/pico_device.h @@ -0,0 +1,54 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef INCLUDE_PICO_DEVICE +#define INCLUDE_PICO_DEVICE +#include "pico_queue.h" +#include "pico_frame.h" +#include "pico_addressing.h" +#include "pico_tree.h" +extern struct pico_tree Device_tree; +#include "pico_ipv6_nd.h" +#define MAX_DEVICE_NAME 16 + + +struct pico_ethdev { + struct pico_eth mac; +}; + +struct pico_device { + char name[MAX_DEVICE_NAME]; + uint32_t hash; + uint32_t overhead; + uint32_t mtu; + struct pico_ethdev *eth; /* Null if non-ethernet */ + struct pico_queue *q_in; + struct pico_queue *q_out; + int (*link_state)(struct pico_device *self); + int (*send)(struct pico_device *self, void *buf, int len); /* Send function. Return 0 if busy */ + int (*poll)(struct pico_device *self, int loop_score); + void (*destroy)(struct pico_device *self); + int (*dsr)(struct pico_device *self, int loop_score); + int __serving_interrupt; + /* used to signal the upper layer the number of events arrived since the last processing */ + volatile int eventCnt; + #ifdef PICO_SUPPORT_IPV6 + struct pico_nd_hostvars hostvars; + #endif +}; + + +int pico_device_init(struct pico_device *dev, const char *name, uint8_t *mac); +void pico_device_destroy(struct pico_device *dev); +int pico_devices_loop(int loop_score, int direction); +struct pico_device*pico_get_device(const char*name); +int32_t pico_device_broadcast(struct pico_frame *f); +int pico_device_link_state(struct pico_device *dev); +int pico_device_ipv6_random_ll(struct pico_device *dev); +#ifdef PICO_SUPPORT_IPV6 +struct pico_ipv6_link *pico_ipv6_link_add_local(struct pico_device *dev, const struct pico_ip6 *prefix); +#endif + +#endif diff --git a/net/picotcp/include/pico_eth.h b/net/picotcp/include/pico_eth.h new file mode 100644 index 0000000..89846c7 --- /dev/null +++ b/net/picotcp/include/pico_eth.h @@ -0,0 +1,21 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef INCLUDE_PICO_ETH +#define INCLUDE_PICO_ETH +#include "pico_addressing.h" +#include "pico_ipv4.h" +#include "pico_ipv6.h" + + +PACKED_STRUCT_DEF pico_eth_hdr { + uint8_t daddr[6]; + uint8_t saddr[6]; + uint16_t proto; +}; + +#define PICO_SIZE_ETHHDR 14 + +#endif diff --git a/net/picotcp/include/pico_frame.h b/net/picotcp/include/pico_frame.h new file mode 100644 index 0000000..feb8f78 --- /dev/null +++ b/net/picotcp/include/pico_frame.h @@ -0,0 +1,122 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef INCLUDE_PICO_FRAME +#define INCLUDE_PICO_FRAME +#include "pico_config.h" + + +#define PICO_FRAME_FLAG_BCAST (0x01) +#define PICO_FRAME_FLAG_EXT_BUFFER (0x02) +#define PICO_FRAME_FLAG_EXT_USAGE_COUNTER (0x04) +#define PICO_FRAME_FLAG_SACKED (0x80) +#define IS_BCAST(f) ((f->flags & PICO_FRAME_FLAG_BCAST) == PICO_FRAME_FLAG_BCAST) + + +struct pico_socket; + + +struct pico_frame { + + /* Connector for queues */ + struct pico_frame *next; + + /* Start of the whole buffer, total frame length. */ + unsigned char *buffer; + uint32_t buffer_len; + + /* For outgoing packets: this is the meaningful buffer. */ + unsigned char *start; + uint32_t len; + + /* Pointer to usage counter */ + uint32_t *usage_count; + + /* Pointer to protocol headers */ + uint8_t *datalink_hdr; + + uint8_t *net_hdr; + uint16_t net_len; + uint8_t *transport_hdr; + uint16_t transport_len; + uint8_t *app_hdr; + uint16_t app_len; + + /* Pointer to the phisical device this packet belongs to. + * Should be valid in both routing directions + */ + struct pico_device *dev; + + pico_time timestamp; + + /* Failures due to bad datalink addressing. */ + uint16_t failure_count; + + /* Protocol over IP */ + uint8_t proto; + + /* PICO_FRAME_FLAG_* */ + uint8_t flags; + + /* Pointer to payload */ + unsigned char *payload; + uint16_t payload_len; + +#ifdef PICO_SUPPORT_IPFRAG + /* Payload fragmentation info */ + uint16_t frag; +#endif + + /* Pointer to socket */ + struct pico_socket *sock; + + /* Pointer to transport info, used to store remote UDP endpoint (IP + port) */ + void *info; + + /*Priority. "best-effort" priority, the default value is 0. Priority can be in between -10 and +10*/ + int8_t priority; + uint8_t transport_flags_saved; + + /* Callback to notify listener when the buffer has been discarded */ + void (*notify_free)(uint8_t *); + + uint8_t send_ttl; /* Special TTL/HOPS value, 0 = auto assign */ + uint8_t send_tos; /* Type of service */ +}; + +/** frame alloc/dealloc/copy **/ +void pico_frame_discard(struct pico_frame *f); +struct pico_frame *pico_frame_copy(struct pico_frame *f); +struct pico_frame *pico_frame_deepcopy(struct pico_frame *f); +struct pico_frame *pico_frame_alloc(uint32_t size); +int pico_frame_grow(struct pico_frame *f, uint32_t size); +struct pico_frame *pico_frame_alloc_skeleton(uint32_t size, int ext_buffer); +int pico_frame_skeleton_set_buffer(struct pico_frame *f, void *buf); +uint16_t pico_checksum(void *inbuf, uint32_t len); +uint16_t pico_dualbuffer_checksum(void *b1, uint32_t len1, void *b2, uint32_t len2); + +static inline int pico_is_digit(char c) +{ + if (c < '0' || c > '9') + return 0; + + return 1; +} + +static inline int pico_is_hex(char c) +{ + if (c >= '0' && c <= '9') + return 1; + + if (c >= 'a' && c <= 'f') + return 1; + + if (c >= 'A' && c <= 'F') + return 1; + + return 0; +} + +#endif diff --git a/net/picotcp/include/pico_module_eth.h b/net/picotcp/include/pico_module_eth.h new file mode 100644 index 0000000..c86680d --- /dev/null +++ b/net/picotcp/include/pico_module_eth.h @@ -0,0 +1,33 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef PICO_MODULE_IPV4_H +#define PICO_MODULE_IPV4_H + +struct pico_arp_entry { + struct eth dest; +#ifdef PICO_CONFIG_IPV4 + struct ipv4 addr_ipv4; +#endif + RB_ENTRY(pico_arp_entry) node; +}; + +/* Configured device */ +struct pico_eth_link { + struct pico_device *dev; + struct eth address; + struct eth netmask; + RB_ENTRY(pico_eth_link) node; +}; + +#ifndef IS_MODULE_ETH +# define _mod extern +#else +# define _mod +#endif +_mod struct pico_module pico_module_eth; +#undef _mod + +#endif diff --git a/net/picotcp/include/pico_protocol.h b/net/picotcp/include/pico_protocol.h new file mode 100644 index 0000000..fd7c122 --- /dev/null +++ b/net/picotcp/include/pico_protocol.h @@ -0,0 +1,95 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef INCLUDE_PICO_PROTOCOL +#define INCLUDE_PICO_PROTOCOL +#include "pico_config.h" +#include "pico_queue.h" + +#define PICO_LOOP_DIR_IN 1 +#define PICO_LOOP_DIR_OUT 2 + +enum pico_layer { + PICO_LAYER_DATALINK = 2, /* Ethernet only. */ + PICO_LAYER_NETWORK = 3, /* IPv4, IPv6, ARP. Arp is there because it communicates with L2 */ + PICO_LAYER_TRANSPORT = 4, /* UDP, TCP, ICMP */ + PICO_LAYER_SOCKET = 5 /* Socket management */ +}; + +enum pico_err_e { + PICO_ERR_NOERR = 0, + PICO_ERR_EPERM = 1, + PICO_ERR_ENOENT = 2, + /* ... */ + PICO_ERR_EINTR = 4, + PICO_ERR_EIO = 5, + PICO_ERR_ENXIO = 6, + /* ... */ + PICO_ERR_EAGAIN = 11, + PICO_ERR_ENOMEM = 12, + PICO_ERR_EACCESS = 13, + PICO_ERR_EFAULT = 14, + /* ... */ + PICO_ERR_EBUSY = 16, + PICO_ERR_EEXIST = 17, + /* ... */ + PICO_ERR_EINVAL = 22, + /* ... */ + PICO_ERR_ENONET = 64, + /* ... */ + PICO_ERR_EPROTO = 71, + /* ... */ + PICO_ERR_ENOPROTOOPT = 92, + PICO_ERR_EPROTONOSUPPORT = 93, + /* ... */ + PICO_ERR_EOPNOTSUPP = 95, + PICO_ERR_EADDRINUSE = 98, + PICO_ERR_EADDRNOTAVAIL = 99, + PICO_ERR_ENETDOWN = 100, + PICO_ERR_ENETUNREACH = 101, + /* ... */ + PICO_ERR_ECONNRESET = 104, + /* ... */ + PICO_ERR_EISCONN = 106, + PICO_ERR_ENOTCONN = 107, + PICO_ERR_ESHUTDOWN = 108, + /* ... */ + PICO_ERR_ETIMEDOUT = 110, + PICO_ERR_ECONNREFUSED = 111, + PICO_ERR_EHOSTDOWN = 112, + PICO_ERR_EHOSTUNREACH = 113, +}; + +typedef enum pico_err_e pico_err_t; +extern volatile pico_err_t pico_err; + +#define IS_IPV6(f) (f && f->net_hdr && ((((uint8_t *)(f->net_hdr))[0] & 0xf0) == 0x60)) +#define IS_IPV4(f) (f && f->net_hdr && ((((uint8_t *)(f->net_hdr))[0] & 0xf0) == 0x40)) + +#define MAX_PROTOCOL_NAME 16 + +struct pico_protocol { + char name[MAX_PROTOCOL_NAME]; + uint32_t hash; + enum pico_layer layer; + uint16_t proto_number; + struct pico_queue *q_in; + struct pico_queue *q_out; + struct pico_frame *(*alloc)(struct pico_protocol *self, uint16_t size); /* Frame allocation. */ + int (*push)(struct pico_protocol *self, struct pico_frame *p); /* Push function, for active outgoing pkts from above */ + int (*process_out)(struct pico_protocol *self, struct pico_frame *p); /* Send loop. */ + int (*process_in)(struct pico_protocol *self, struct pico_frame *p); /* Recv loop. */ + uint16_t (*get_mtu)(struct pico_protocol *self); +}; + +int pico_protocols_loop(int loop_score); +void pico_protocol_init(struct pico_protocol *p); + +int pico_protocol_datalink_loop(int loop_score, int direction); +int pico_protocol_network_loop(int loop_score, int direction); +int pico_protocol_transport_loop(int loop_score, int direction); +int pico_protocol_socket_loop(int loop_score, int direction); + +#endif diff --git a/net/picotcp/include/pico_queue.h b/net/picotcp/include/pico_queue.h new file mode 100644 index 0000000..6cbb979 --- /dev/null +++ b/net/picotcp/include/pico_queue.h @@ -0,0 +1,166 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef INCLUDE_PICO_QUEUE +#define INCLUDE_PICO_QUEUE +#include "pico_config.h" +#include "pico_frame.h" + +#define Q_LIMIT 0 + +#ifndef NULL +#define NULL ((void *)0) +#endif + +void *pico_mutex_init(void); +void pico_mutex_deinit(void *mutex); +void pico_mutex_lock(void *mutex); +int pico_mutex_lock_timeout(void *mutex, int timeout); +void pico_mutex_unlock(void *mutex); +void pico_mutex_unlock_ISR(void *mutex); + +struct pico_queue { + uint32_t frames; + uint32_t size; + uint32_t max_frames; + uint32_t max_size; + struct pico_frame *head; + struct pico_frame *tail; +#ifdef PICO_SUPPORT_MUTEX + void *mutex; +#endif + uint8_t shared; + uint16_t overhead; +}; + +#ifdef PICO_SUPPORT_MUTEX +#define PICOTCP_MUTEX_LOCK(x) { \ + if (x == NULL) \ + x = pico_mutex_init(); \ + pico_mutex_lock(x); \ +} +#define PICOTCP_MUTEX_UNLOCK(x) pico_mutex_unlock(x) +#define PICOTCP_MUTEX_DEL(x) pico_mutex_deinit(x) + +#else +#define PICOTCP_MUTEX_LOCK(x) do {} while(0) +#define PICOTCP_MUTEX_UNLOCK(x) do {} while(0) +#define PICOTCP_MUTEX_DEL(x) do {} while(0) +#endif + +#ifdef PICO_SUPPORT_DEBUG_TOOLS +static void debug_q(struct pico_queue *q) +{ + struct pico_frame *p = q->head; + dbg("%d: ", q->frames); + while(p) { + dbg("(%p)-->", p); + p = p->next; + } + dbg("X\n"); +} + +#else + +#define debug_q(x) do {} while(0) +#endif + +static inline int32_t pico_enqueue(struct pico_queue *q, struct pico_frame *p) +{ + if ((q->max_frames) && (q->max_frames <= q->frames)) + return -1; + +#if (Q_LIMIT != 0) + if ((Q_LIMIT < p->buffer_len + q->size)) + return -1; + +#endif + + if ((q->max_size) && (q->max_size < (p->buffer_len + q->size))) + return -1; + + if (q->shared) + PICOTCP_MUTEX_LOCK(q->mutex); + + p->next = NULL; + if (!q->head) { + q->head = p; + q->tail = p; + q->size = 0; + q->frames = 0; + } else { + q->tail->next = p; + q->tail = p; + } + + q->size += p->buffer_len + q->overhead; + q->frames++; + debug_q(q); + + if (q->shared) + PICOTCP_MUTEX_UNLOCK(q->mutex); + + return (int32_t)q->size; +} + +static inline struct pico_frame *pico_dequeue(struct pico_queue *q) +{ + struct pico_frame *p = q->head; + if (!p) + return NULL; + + if (q->frames < 1) + return NULL; + + if (q->shared) + PICOTCP_MUTEX_LOCK(q->mutex); + + q->head = p->next; + q->frames--; + q->size -= p->buffer_len - q->overhead; + if (q->head == NULL) + q->tail = NULL; + + debug_q(q); + + p->next = NULL; + if (q->shared) + PICOTCP_MUTEX_UNLOCK(q->mutex); + + return p; +} + +static inline struct pico_frame *pico_queue_peek(struct pico_queue *q) +{ + struct pico_frame *p = q->head; + if (q->frames < 1) + return NULL; + + debug_q(q); + return p; +} + +static inline void pico_queue_deinit(struct pico_queue *q) +{ + if (q->shared) { + PICOTCP_MUTEX_DEL(q->mutex); + } +} + +static inline void pico_queue_empty(struct pico_queue *q) +{ + struct pico_frame *p = pico_dequeue(q); + while(p) { + pico_frame_discard(p); + p = pico_dequeue(q); + } +} + +static inline void pico_queue_protect(struct pico_queue *q) +{ + q->shared = 1; +} + +#endif diff --git a/net/picotcp/include/pico_socket.h b/net/picotcp/include/pico_socket.h new file mode 100644 index 0000000..a076654 --- /dev/null +++ b/net/picotcp/include/pico_socket.h @@ -0,0 +1,268 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef INCLUDE_PICO_SOCKET +#define INCLUDE_PICO_SOCKET +#include "pico_queue.h" +#include "pico_addressing.h" +#include "pico_config.h" +#include "pico_protocol.h" +#include "pico_tree.h" + +#ifdef __linux__ + #define PICO_DEFAULT_SOCKETQ (16 * 1024) /* Linux host, so we want full throttle */ +#else + #define PICO_DEFAULT_SOCKETQ (6 * 1024) /* seems like an acceptable default for small embedded systems */ +#endif + +#define PICO_SHUT_RD 1 +#define PICO_SHUT_WR 2 +#define PICO_SHUT_RDWR 3 + +#ifdef PICO_SUPPORT_IPV4 +# define IS_SOCK_IPV4(s) ((s->net == &pico_proto_ipv4)) +#else +# define IS_SOCK_IPV4(s) (0) +#endif + +#ifdef PICO_SUPPORT_IPV6 +# define IS_SOCK_IPV6(s) ((s->net == &pico_proto_ipv6)) +#else +# define IS_SOCK_IPV6(s) (0) +#endif + + +struct pico_sockport +{ + struct pico_tree socks; /* how you make the connection ? */ + uint16_t number; + uint16_t proto; +}; + + +struct pico_socket { + struct pico_protocol *proto; + struct pico_protocol *net; + + union pico_address local_addr; + union pico_address remote_addr; + + uint16_t local_port; + uint16_t remote_port; + + struct pico_queue q_in; + struct pico_queue q_out; + + void (*wakeup)(uint16_t ev, struct pico_socket *s); + + +#ifdef PICO_SUPPORT_TCP + /* For the TCP backlog queue */ + struct pico_socket *backlog; + struct pico_socket *next; + struct pico_socket *parent; + uint16_t max_backlog; + uint16_t number_of_pending_conn; +#endif +#ifdef PICO_SUPPORT_MCAST + struct pico_tree *MCASTListen; +#endif + uint16_t ev_pending; + + struct pico_device *dev; + + /* Private field. */ + int id; + uint16_t state; + uint16_t opt_flags; + pico_time timestamp; + void *priv; +}; + +struct pico_remote_endpoint { + union pico_address remote_addr; + uint16_t remote_port; +}; + + +/* request struct for multicast socket opt */ +struct pico_ip_mreq { + struct pico_ip4 mcast_group_addr; + struct pico_ip4 mcast_link_addr; +}; + +struct pico_ip_mreq_source { + struct pico_ip4 mcast_group_addr; + struct pico_ip4 mcast_source_addr; + struct pico_ip4 mcast_link_addr; +}; + +#ifdef PICO_SUPPORT_IPV6 + +/* same as above, but ipv6 */ +struct pico_ipv6_mreq { + struct pico_ip6 mcast_group_addr; + struct pico_ip6 mcast_link_addr; +}; + +struct pico_ipv6_mreq_source { + struct pico_ip6 mcast_group_addr; + struct pico_ip6 mcast_source_addr; + struct pico_ip6 mcast_link_addr; +}; + +#endif + +#define PICO_SOCKET_STATE_UNDEFINED 0x0000u +#define PICO_SOCKET_STATE_SHUT_LOCAL 0x0001u +#define PICO_SOCKET_STATE_SHUT_REMOTE 0x0002u +#define PICO_SOCKET_STATE_BOUND 0x0004u +#define PICO_SOCKET_STATE_CONNECTED 0x0008u +#define PICO_SOCKET_STATE_CLOSING 0x0010u +#define PICO_SOCKET_STATE_CLOSED 0x0020u + +# define PICO_SOCKET_STATE_TCP 0xFF00u +# define PICO_SOCKET_STATE_TCP_UNDEF 0x00FFu +# define PICO_SOCKET_STATE_TCP_CLOSED 0x0100u +# define PICO_SOCKET_STATE_TCP_LISTEN 0x0200u +# define PICO_SOCKET_STATE_TCP_SYN_SENT 0x0300u +# define PICO_SOCKET_STATE_TCP_SYN_RECV 0x0400u +# define PICO_SOCKET_STATE_TCP_ESTABLISHED 0x0500u +# define PICO_SOCKET_STATE_TCP_CLOSE_WAIT 0x0600u +# define PICO_SOCKET_STATE_TCP_LAST_ACK 0x0700u +# define PICO_SOCKET_STATE_TCP_FIN_WAIT1 0x0800u +# define PICO_SOCKET_STATE_TCP_FIN_WAIT2 0x0900u +# define PICO_SOCKET_STATE_TCP_CLOSING 0x0a00u +# define PICO_SOCKET_STATE_TCP_TIME_WAIT 0x0b00u +# define PICO_SOCKET_STATE_TCP_ARRAYSIZ 0x0cu + + +/* Socket options */ +# define PICO_TCP_NODELAY 1 +# define PICO_SOCKET_OPT_TCPNODELAY 0x0000u + +# define PICO_IP_MULTICAST_EXCLUDE 0 +# define PICO_IP_MULTICAST_INCLUDE 1 +# define PICO_IP_MULTICAST_IF 32 +# define PICO_IP_MULTICAST_TTL 33 +# define PICO_IP_MULTICAST_LOOP 34 +# define PICO_IP_ADD_MEMBERSHIP 35 +# define PICO_IP_DROP_MEMBERSHIP 36 +# define PICO_IP_UNBLOCK_SOURCE 37 +# define PICO_IP_BLOCK_SOURCE 38 +# define PICO_IP_ADD_SOURCE_MEMBERSHIP 39 +# define PICO_IP_DROP_SOURCE_MEMBERSHIP 40 + +# define PICO_SOCKET_OPT_MULTICAST_LOOP 1 + +# define PICO_SOCKET_OPT_RCVBUF 52 +# define PICO_SOCKET_OPT_SNDBUF 53 + +/* Constants */ +# define PICO_IP_DEFAULT_MULTICAST_TTL 1 +# define PICO_IP_DEFAULT_MULTICAST_LOOP 1 + +#define PICO_SOCKET_TIMEOUT 90000u /* 90 seconds */ +#define PICO_SOCKET_BOUND_TIMEOUT 30000u /* 30 seconds */ + +#define PICO_SOCKET_SHUTDOWN_WRITE 0x01u +#define PICO_SOCKET_SHUTDOWN_READ 0x02u +#define TCPSTATE(s) ((s)->state & PICO_SOCKET_STATE_TCP) + +#define PICO_SOCK_EV_RD 1u +#define PICO_SOCK_EV_WR 2u +#define PICO_SOCK_EV_CONN 4u +#define PICO_SOCK_EV_CLOSE 8u +#define PICO_SOCK_EV_FIN 0x10u +#define PICO_SOCK_EV_ERR 0x80u + +struct pico_msginfo { + struct pico_device *dev; + uint8_t ttl; + uint8_t tos; +}; + +struct pico_socket *pico_socket_open(uint16_t net, uint16_t proto, void (*wakeup)(uint16_t ev, struct pico_socket *s)); + +int pico_socket_read(struct pico_socket *s, void *buf, int len); +int pico_socket_write(struct pico_socket *s, const void *buf, int len); + +int pico_socket_sendto(struct pico_socket *s, const void *buf, int len, void *dst, uint16_t remote_port); +int pico_socket_sendto_extended(struct pico_socket *s, const void *buf, const int len, + void *dst, uint16_t remote_port, struct pico_msginfo *msginfo); + +int pico_socket_recvfrom(struct pico_socket *s, void *buf, int len, void *orig, uint16_t *local_port); +int pico_socket_recvfrom_extended(struct pico_socket *s, void *buf, int len, void *orig, + uint16_t *remote_port, struct pico_msginfo *msginfo); + +int pico_socket_send(struct pico_socket *s, const void *buf, int len); +int pico_socket_recv(struct pico_socket *s, void *buf, int len); + +int pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t *port); +int pico_socket_getname(struct pico_socket *s, void *local_addr, uint16_t *port, uint16_t *proto); +int pico_socket_getpeername(struct pico_socket *s, void *remote_addr, uint16_t *port, uint16_t *proto); + +int pico_socket_connect(struct pico_socket *s, const void *srv_addr, uint16_t remote_port); +int pico_socket_listen(struct pico_socket *s, const int backlog); +struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *port); +int8_t pico_socket_del(struct pico_socket *s); + +int pico_socket_setoption(struct pico_socket *s, int option, void *value); +int pico_socket_getoption(struct pico_socket *s, int option, void *value); + +int pico_socket_shutdown(struct pico_socket *s, int mode); +int pico_socket_close(struct pico_socket *s); + +struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, uint16_t len); + +#ifdef PICO_SUPPORT_IPV4 +# define is_sock_ipv4(x) (x->net == &pico_proto_ipv4) +#else +# define is_sock_ipv4(x) (0) +#endif + +#ifdef PICO_SUPPORT_IPV6 +# define is_sock_ipv6(x) (x->net == &pico_proto_ipv6) +#else +# define is_sock_ipv6(x) (0) +#endif + +#ifdef PICO_SUPPORT_UDP +# define is_sock_udp(x) (x->proto == &pico_proto_udp) +#else +# define is_sock_udp(x) (0) +#endif + +#ifdef PICO_SUPPORT_TCP +# define is_sock_tcp(x) (x->proto == &pico_proto_tcp) +#else +# define is_sock_tcp(x) (0) +#endif + +/* Interface towards transport protocol */ +int pico_transport_process_in(struct pico_protocol *self, struct pico_frame *f); +struct pico_socket *pico_socket_clone(struct pico_socket *facsimile); +int8_t pico_socket_add(struct pico_socket *s); +int pico_transport_error(struct pico_frame *f, uint8_t proto, int code); + +/* Socket loop */ +int pico_sockets_loop(int loop_score); +struct pico_socket*pico_sockets_find(uint16_t local, uint16_t remote); +/* Port check */ +int pico_is_port_free(uint16_t proto, uint16_t port, void *addr, void *net); + +struct pico_sockport *pico_get_sockport(uint16_t proto, uint16_t port); + +uint32_t pico_socket_get_mss(struct pico_socket *s); +int pico_socket_set_family(struct pico_socket *s, uint16_t family); + +int pico_count_sockets(uint8_t proto); + +#define PICO_SOCKET_SETOPT_EN(socket, index) (socket->opt_flags |= (1 << index)) +#define PICO_SOCKET_SETOPT_DIS(socket, index) (socket->opt_flags &= (uint16_t) ~(1 << index)) +#define PICO_SOCKET_GETOPT(socket, index) ((socket->opt_flags & (1u << index)) != 0) + + +#endif diff --git a/net/picotcp/include/pico_socket_multicast.h b/net/picotcp/include/pico_socket_multicast.h new file mode 100644 index 0000000..44c30c5 --- /dev/null +++ b/net/picotcp/include/pico_socket_multicast.h @@ -0,0 +1,10 @@ +#ifndef PICO_SOCKET_MULTICAST_H +#define PICO_SOCKET_MULTICAST_H +int pico_socket_mcast_filter(struct pico_socket *s, union pico_address *mcast_group, union pico_address *src); +void pico_multicast_delete(struct pico_socket *s); +int pico_setsockopt_mcast(struct pico_socket *s, int option, void *value); +int pico_getsockopt_mcast(struct pico_socket *s, int option, void *value); +int pico_udp_get_mc_ttl(struct pico_socket *s, uint8_t *ttl); +int pico_udp_set_mc_ttl(struct pico_socket *s, void *_ttl); + +#endif diff --git a/net/picotcp/include/pico_stack.h b/net/picotcp/include/pico_stack.h new file mode 100644 index 0000000..69b0bf1 --- /dev/null +++ b/net/picotcp/include/pico_stack.h @@ -0,0 +1,87 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef INCLUDE_PICO_STACK +#define INCLUDE_PICO_STACK +#include "pico_config.h" +#include "pico_frame.h" + +#define PICO_MAX_TIMERS 20 + +#define PICO_ETH_MRU (1514u) +#define PICO_IP_MRU (1500u) + +/* ===== RECEIVING FUNCTIONS (from dev up to socket) ===== */ + +/* TRANSPORT LEVEL */ +/* interface towards network */ +int32_t pico_transport_receive(struct pico_frame *f, uint8_t proto); + +/* NETWORK LEVEL */ +/* interface towards ethernet */ +int32_t pico_network_receive(struct pico_frame *f); + + +/* LOWEST LEVEL: interface towards devices. */ +/* Device driver will call this function which returns immediately. + * Incoming packet will be processed later on in the dev loop. + * The zerocopy version will associate the current buffer to the newly created frame. + * Warning: the buffer used in the zerocopy version MUST have been allocated using PICO_ZALLOC() + */ +int32_t pico_stack_recv(struct pico_device *dev, uint8_t *buffer, uint32_t len); +int32_t pico_stack_recv_zerocopy(struct pico_device *dev, uint8_t *buffer, uint32_t len); +int32_t pico_stack_recv_zerocopy_ext_buffer(struct pico_device *dev, uint8_t *buffer, uint32_t len); +int32_t pico_stack_recv_zerocopy_ext_buffer_notify(struct pico_device *dev, uint8_t *buffer, uint32_t len, void (*notify_free)(uint8_t *buffer)); + +/* ===== SENDING FUNCTIONS (from socket down to dev) ===== */ + +int32_t pico_network_send(struct pico_frame *f); +int32_t pico_sendto_dev(struct pico_frame *f); + +#ifdef PICO_SUPPORT_ETH +int32_t pico_ethernet_send(struct pico_frame *f); + +/* The pico_ethernet_receive() function is used by + * those devices supporting ETH in order to push packets up + * into the stack. + */ +/* DATALINK LEVEL */ +int32_t pico_ethernet_receive(struct pico_frame *f); +#else +/* When ETH is not supported by the stack... */ +# define pico_ethernet_send(f) (-1) +# define pico_ethernet_receive(f) (-1) +#endif + +/* ----- Initialization ----- */ +int pico_stack_init(void); + +/* ----- Loop Function. ----- */ +void pico_stack_tick(void); +void pico_stack_loop(void); + +/* ---- Notifications for stack errors */ +int pico_notify_socket_unreachable(struct pico_frame *f); +int pico_notify_proto_unreachable(struct pico_frame *f); +int pico_notify_dest_unreachable(struct pico_frame *f); +int pico_notify_ttl_expired(struct pico_frame *f); +int pico_notify_frag_expired(struct pico_frame *f); +int pico_notify_pkt_too_big(struct pico_frame *f); + +/* Various. */ +struct pico_timer; +int pico_source_is_local(struct pico_frame *f); +int pico_frame_dst_is_unicast(struct pico_frame *f); +void pico_store_network_origin(void *src, struct pico_frame *f); +struct pico_timer *pico_timer_add(pico_time expire, void (*timer)(pico_time, void *), void *arg); +void pico_timer_cancel(struct pico_timer *t); +pico_time pico_timer_get_expire(struct pico_timer *t); +uint32_t pico_rand(void); +void pico_rand_feed(uint32_t feed); +void pico_to_lowercase(char *str); +int pico_address_compare(union pico_address *a, union pico_address *b, uint16_t proto); +int32_t pico_seq_compare(uint32_t a, uint32_t b); + +#endif diff --git a/net/picotcp/include/pico_tree.h b/net/picotcp/include/pico_tree.h new file mode 100644 index 0000000..8667a1f --- /dev/null +++ b/net/picotcp/include/pico_tree.h @@ -0,0 +1,93 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + Author: Andrei Carp + *********************************************************************/ + +#ifndef PICO_RBTREE_H +#define PICO_RBTREE_H + +#include "pico_config.h" + +/* This is used to declare a new tree, leaf root by default */ +#define PICO_TREE_DECLARE(name, compareFunction) \ + struct pico_tree name = \ + { \ + &LEAF, \ + compareFunction \ + } + +#define USE_PICO_PAGE0_ZALLOC (1) +#define USE_PICO_ZALLOC (2) + +struct pico_tree_node +{ + void*keyValue; /* generic key */ + struct pico_tree_node*parent; + struct pico_tree_node*leftChild; + struct pico_tree_node*rightChild; + uint8_t color; +}; + +struct pico_tree +{ + struct pico_tree_node *root; /* root of the tree */ + + /* this function directly provides the keys as parameters not the nodes. */ + int (*compare)(void*keyA, void*keyB); +}; + +extern struct pico_tree_node LEAF; /* generic leaf node */ + +#ifdef PICO_SUPPORT_MM +void *pico_tree_insert_implementation(struct pico_tree *tree, void *key, uint8_t allocator); +void *pico_tree_delete_implementation(struct pico_tree *tree, void *key, uint8_t allocator); +#endif + + +/* + * Manipulation functions + */ +void *pico_tree_insert(struct pico_tree *tree, void *key); +void *pico_tree_delete(struct pico_tree *tree, void *key); +void *pico_tree_findKey(struct pico_tree *tree, void *key); +void pico_tree_drop(struct pico_tree *tree); +int pico_tree_empty(struct pico_tree *tree); +struct pico_tree_node *pico_tree_findNode(struct pico_tree *tree, void *key); + +void *pico_tree_first(struct pico_tree *tree); +void *pico_tree_last(struct pico_tree *tree); +/* + * Traverse functions + */ +struct pico_tree_node *pico_tree_lastNode(struct pico_tree_node *node); +struct pico_tree_node *pico_tree_firstNode(struct pico_tree_node *node); +struct pico_tree_node *pico_tree_next(struct pico_tree_node *node); +struct pico_tree_node *pico_tree_prev(struct pico_tree_node *node); + +/* + * For each macros + */ + +#define pico_tree_foreach(idx, tree) \ + for ((idx) = pico_tree_firstNode((tree)->root); \ + (idx) != &LEAF; \ + (idx) = pico_tree_next(idx)) + +#define pico_tree_foreach_reverse(idx, tree) \ + for ((idx) = pico_tree_lastNode((tree)->root); \ + (idx) != &LEAF; \ + (idx) = pico_tree_prev(idx)) + +#define pico_tree_foreach_safe(idx, tree, idx2) \ + for ((idx) = pico_tree_firstNode((tree)->root); \ + ((idx) != &LEAF) && ((idx2) = pico_tree_next(idx), 1); \ + (idx) = (idx2)) + +#define pico_tree_foreach_reverse_safe(idx, tree, idx2) \ + for ((idx) = pico_tree_lastNode((tree)->root); \ + ((idx) != &LEAF) && ((idx2) = pico_tree_prev(idx), 1); \ + (idx) = (idx2)) + +#endif diff --git a/net/picotcp/modules/pico_aodv.h b/net/picotcp/modules/pico_aodv.h new file mode 100644 index 0000000..a3e9b66 --- /dev/null +++ b/net/picotcp/modules/pico_aodv.h @@ -0,0 +1,131 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + Author: Daniele Lacamera + *********************************************************************/ +#ifndef PICO_AODV_H_ +#define PICO_AODV_H_ +#include + +/* RFC3561 */ +#define PICO_AODV_PORT (654) + +/* RFC3561 $10 */ +#define AODV_ACTIVE_ROUTE_TIMEOUT (8000u) /* Conservative value for link breakage detection */ +#define AODV_DELETE_PERIOD (5 * AODV_ACTIVE_ROUTE_TIMEOUT) /* Recommended value K = 5 */ +#define AODV_ALLOWED_HELLO_LOSS (4) /* conservative */ +#define AODV_NET_DIAMETER ((uint8_t)(35)) +#define AODV_RREQ_RETRIES (2) +#define AODV_NODE_TRAVERSAL_TIME (40) +#define AODV_HELLO_INTERVAL (1000) +#define AODV_LOCAL_ADD_TTL 2 +#define AODV_RREQ_RATELIMIT (10) +#define AODV_TIMEOUT_BUFFER (2) +#define AODV_TTL_START ((uint8_t)(1)) +#define AODV_TTL_INCREMENT 2 +#define AODV_TTL_THRESHOLD ((uint8_t)(7)) +#define AODV_RERR_RATELIMIT (10) +#define AODV_MAX_REPAIR_TTL ((uint8_t)(AODV_NET_DIAMETER / 3)) +#define AODV_MY_ROUTE_TIMEOUT (2 * AODV_ACTIVE_ROUTE_TIMEOUT) +#define AODV_NET_TRAVERSAL_TIME (2 * AODV_NODE_TRAVERSAL_TIME * AODV_NET_DIAMETER) +#define AODV_BLACKLIST_TIMEOUT (AODV_RREQ_RETRIES * AODV_NET_TRAVERSAL_TIME) +#define AODV_NEXT_HOP_WAIT (AODV_NODE_TRAVERSAL_TIME + 10) +#define AODV_PATH_DISCOVERY_TIME (2 * AODV_NET_TRAVERSAL_TIME) +#define AODV_RING_TRAVERSAL_TIME(ttl) (2 * AODV_NODE_TRAVERSAL_TIME * (ttl + AODV_TIMEOUT_BUFFER)) +/* End section RFC3561 $10 */ + + +#define AODV_TYPE_RREQ 1 +#define AODV_TYPE_RREP 2 +#define AODV_TYPE_RERR 3 +#define AODV_TYPE_RACK 4 + +PACKED_STRUCT_DEF pico_aodv_rreq +{ + uint8_t type; + uint16_t req_flags; + uint8_t hop_count; + uint32_t rreq_id; + uint32_t dest; + uint32_t dseq; + uint32_t orig; + uint32_t oseq; +}; + +#define AODV_RREQ_FLAG_J 0x8000 +#define AODV_RREQ_FLAG_R 0x4000 +#define AODV_RREQ_FLAG_G 0x2000 +#define AODV_RREQ_FLAG_D 0x1000 +#define AODV_RREQ_FLAG_U 0x0800 +#define AODV_RREQ_FLAG_RESERVED 0x07FF + +PACKED_STRUCT_DEF pico_aodv_rrep +{ + uint8_t type; + uint8_t rep_flags; + uint8_t prefix_sz; + uint8_t hop_count; + uint32_t dest; + uint32_t dseq; + uint32_t orig; + uint32_t lifetime; +}; + +#define AODV_RREP_MAX_PREFIX 0x1F +#define AODV_RREP_FLAG_R 0x80 +#define AODV_RREP_FLAG_A 0x40 +#define AODV_RREP_FLAG_RESERVED 0x3F + +#define PICO_AODV_NODE_NEW 0x0000 +#define PICO_AODV_NODE_SYNC 0x0001 +#define PICO_AODV_NODE_REQUESTING 0x0002 +#define PICO_AODV_NODE_ROUTE_UP 0x0004 +#define PICO_AODV_NODE_ROUTE_DOWN 0x0008 +#define PICO_AODV_NODE_IDLING 0x0010 +#define PICO_AODV_NODE_UNREACH 0x0020 + +#define PICO_AODV_ACTIVE(node) ((node->flags & PICO_AODV_NODE_ROUTE_UP) && (node->flags & PICO_AODV_NODE_ROUTE_DOWN)) + + +struct pico_aodv_node +{ + union pico_address dest; + pico_time last_seen; + pico_time fwd_time; + uint32_t dseq; + uint16_t flags; + uint8_t metric; + uint8_t ring_ttl; + uint8_t rreq_retry; +}; + +PACKED_STRUCT_DEF pico_aodv_unreachable +{ + uint32_t addr; + uint32_t dseq; +}; + +PACKED_STRUCT_DEF pico_aodv_rerr +{ + uint8_t type; + uint16_t rerr_flags; + uint8_t dst_count; + uint32_t unreach_addr; + uint32_t unreach_dseq; + struct pico_aodv_unreachable unreach[1]; /* unrechable nodes: must be at least 1. See dst_count field above */ +}; + +PACKED_STRUCT_DEF pico_aodv_rack +{ + uint8_t type; + uint8_t reserved; +}; + +int pico_aodv_init(void); +int pico_aodv_add(struct pico_device *dev); +int pico_aodv_lookup(const union pico_address *addr); +void pico_aodv_refresh(const union pico_address *addr); +#endif diff --git a/net/picotcp/modules/pico_arp.c b/net/picotcp/modules/pico_arp.c new file mode 100644 index 0000000..f205bfe --- /dev/null +++ b/net/picotcp/modules/pico_arp.c @@ -0,0 +1,531 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + Authors: Daniele Lacamera + *********************************************************************/ + + +#include "pico_config.h" +#include "pico_arp.h" +#include "pico_tree.h" +#include "pico_ipv4.h" +#include "pico_device.h" +#include "pico_stack.h" + +extern const uint8_t PICO_ETHADDR_ALL[6]; +#define PICO_ARP_TIMEOUT 600000llu +#define PICO_ARP_RETRY 300lu +#define PICO_ARP_MAX_PENDING 5 + +#ifdef DEBUG_ARP + #define arp_dbg dbg +#else + #define arp_dbg(...) do {} while(0) +#endif + +static int max_arp_reqs = PICO_ARP_MAX_RATE; +static struct pico_frame *frames_queued[PICO_ARP_MAX_PENDING] = { }; + +static void pico_arp_queued_trigger(void) +{ + int i; + struct pico_frame *f; + for (i = 0; i < PICO_ARP_MAX_PENDING; i++) + { + f = frames_queued[i]; + if (f) { + pico_ethernet_send(f); + frames_queued[i] = NULL; + } + } +} + +static void update_max_arp_reqs(pico_time now, void *unused) +{ + IGNORE_PARAMETER(now); + IGNORE_PARAMETER(unused); + if (max_arp_reqs < PICO_ARP_MAX_RATE) + max_arp_reqs++; + + pico_timer_add(PICO_ARP_INTERVAL / PICO_ARP_MAX_RATE, &update_max_arp_reqs, NULL); +} + +void pico_arp_init(void) +{ + pico_timer_add(PICO_ARP_INTERVAL / PICO_ARP_MAX_RATE, &update_max_arp_reqs, NULL); +} + +PACKED_STRUCT_DEF pico_arp_hdr +{ + uint16_t htype; + uint16_t ptype; + uint8_t hsize; + uint8_t psize; + uint16_t opcode; + uint8_t s_mac[PICO_SIZE_ETH]; + struct pico_ip4 src; + uint8_t d_mac[PICO_SIZE_ETH]; + struct pico_ip4 dst; +}; + + + +/* Callback handler for ip conflict service (e.g. IPv4 SLAAC) + * Whenever the IP address registered here is seen in the network, + * the callback is awaken to take countermeasures against IP collisions. + * + */ + +struct arp_service_ipconflict { + struct pico_eth mac; + struct pico_ip4 ip; + void (*conflict)(void); +}; + +static struct arp_service_ipconflict conflict_ipv4; + + + +#define PICO_SIZE_ARPHDR ((sizeof(struct pico_arp_hdr))) + +/* Arp Entries for the tables. */ +struct pico_arp { +/* CAREFUL MAN! ARP entry MUST begin with a pico_eth structure, + * due to in-place casting!!! */ + struct pico_eth eth; + struct pico_ip4 ipv4; + int arp_status; + pico_time timestamp; + struct pico_device *dev; + struct pico_timer *timer; +}; + + + +/*****************/ +/** ARP TREE **/ +/*****************/ + +/* Routing destination */ + +static int arp_compare(void *ka, void *kb) +{ + struct pico_arp *a = ka, *b = kb; + return pico_ipv4_compare(&a->ipv4, &b->ipv4); +} + +PICO_TREE_DECLARE(arp_tree, arp_compare); + +/*********************/ +/** END ARP TREE **/ +/*********************/ + +struct pico_eth *pico_arp_lookup(struct pico_ip4 *dst) +{ + struct pico_arp search, *found; + search.ipv4.addr = dst->addr; + found = pico_tree_findKey(&arp_tree, &search); + if (found && (found->arp_status != PICO_ARP_STATUS_STALE)) + return &found->eth; + + return NULL; +} + +struct pico_ip4 *pico_arp_reverse_lookup(struct pico_eth *dst) +{ + struct pico_arp*search; + struct pico_tree_node *index; + pico_tree_foreach(index, &arp_tree){ + search = index->keyValue; + if(memcmp(&(search->eth.addr), &dst->addr, 6) == 0) + return &search->ipv4; + } + return NULL; +} + +static void pico_arp_unreachable(struct pico_ip4 *a) +{ + int i; + struct pico_frame *f; + struct pico_ipv4_hdr *hdr; + struct pico_ip4 dst; + for (i = 0; i < PICO_ARP_MAX_PENDING; i++) + { + f = frames_queued[i]; + if (f) { + hdr = (struct pico_ipv4_hdr *) f->net_hdr; + dst = pico_ipv4_route_get_gateway(&hdr->dst); + if (!dst.addr) + dst.addr = hdr->dst.addr; + + if (dst.addr == a->addr) { + if (!pico_source_is_local(f)) { + pico_notify_dest_unreachable(f); + } + + pico_frame_discard(f); + frames_queued[i] = NULL; + } + } + } +} + +static void pico_arp_retry(struct pico_frame *f, struct pico_ip4 *where) +{ + if (++f->failure_count < 4) { + arp_dbg ("================= ARP REQUIRED: %d =============\n\n", f->failure_count); + /* check if dst is local (gateway = 0), or if to use gateway */ + pico_arp_request(f->dev, where, PICO_ARP_QUERY); + } else { + pico_arp_unreachable(where); + } +} + +struct pico_eth *pico_arp_get(struct pico_frame *f) +{ + struct pico_eth *a4; + struct pico_ip4 gateway; + struct pico_ip4 *where; + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + struct pico_ipv4_link *l; + if (!hdr) + return NULL; + + l = pico_ipv4_link_get(&hdr->dst); + if(l) { + /* address belongs to ourself */ + return &l->dev->eth->mac; + } + + gateway = pico_ipv4_route_get_gateway(&hdr->dst); + /* check if dst is local (gateway = 0), or if to use gateway */ + if (gateway.addr != 0) + where = &gateway; + else + where = &hdr->dst; + + a4 = pico_arp_lookup(where); /* check if dst ip mac in cache */ + + if (!a4) + pico_arp_retry(f, where); + + return a4; +} + + +void pico_arp_postpone(struct pico_frame *f) +{ + int i; + for (i = 0; i < PICO_ARP_MAX_PENDING; i++) + { + if (!frames_queued[i]) { + frames_queued[i] = pico_frame_copy(f); + return; + } + } + /* Not possible to enqueue: caller will discard packet */ +} + + +#ifdef DEBUG_ARP +void dbg_arp(void) +{ + struct pico_arp *a; + struct pico_tree_node *index; + + pico_tree_foreach(index, &arp_tree) { + a = index->keyValue; + arp_dbg("ARP to %08x, mac: %02x:%02x:%02x:%02x:%02x:%02x\n", a->ipv4.addr, a->eth.addr[0], a->eth.addr[1], a->eth.addr[2], a->eth.addr[3], a->eth.addr[4], a->eth.addr[5] ); + } +} +#endif + +static void arp_expire(pico_time now, void *_stale) +{ + struct pico_arp *stale = (struct pico_arp *) _stale; + if (now >= (stale->timestamp + PICO_ARP_TIMEOUT)) { + stale->arp_status = PICO_ARP_STATUS_STALE; + arp_dbg("ARP: Setting arp_status to STALE\n"); + pico_arp_request(stale->dev, &stale->ipv4, PICO_ARP_QUERY); + } else { + /* Timer must be rescheduled, ARP entry has been renewed lately. + * No action required to refresh the entry, will check on the next timeout */ + pico_timer_add(PICO_ARP_TIMEOUT + stale->timestamp - now, arp_expire, stale); + } +} + +static void pico_arp_add_entry(struct pico_arp *entry) +{ + entry->arp_status = PICO_ARP_STATUS_REACHABLE; + entry->timestamp = PICO_TIME(); + + pico_tree_insert(&arp_tree, entry); + arp_dbg("ARP ## reachable.\n"); + pico_arp_queued_trigger(); + pico_timer_add(PICO_ARP_TIMEOUT, arp_expire, entry); +} + +int pico_arp_create_entry(uint8_t *hwaddr, struct pico_ip4 ipv4, struct pico_device *dev) +{ + struct pico_arp*arp = PICO_ZALLOC(sizeof(struct pico_arp)); + if(!arp) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + memcpy(arp->eth.addr, hwaddr, 6); + arp->ipv4.addr = ipv4.addr; + arp->dev = dev; + + pico_arp_add_entry(arp); + + return 0; +} + +static void pico_arp_check_conflict(struct pico_arp_hdr *hdr) +{ + + if ((conflict_ipv4.conflict) && + ((conflict_ipv4.ip.addr == hdr->src.addr) && + (memcmp(hdr->s_mac, conflict_ipv4.mac.addr, PICO_SIZE_ETH) != 0))) + conflict_ipv4.conflict(); +} + +static struct pico_arp *pico_arp_lookup_entry(struct pico_frame *f) +{ + struct pico_arp search; + struct pico_arp *found = NULL; + struct pico_arp_hdr *hdr = (struct pico_arp_hdr *) f->net_hdr; + /* Populate a new arp entry */ + search.ipv4.addr = hdr->src.addr; + + /* Search for already existing entry */ + found = pico_tree_findKey(&arp_tree, &search); + if (found) { + if (found->arp_status == PICO_ARP_STATUS_STALE) { + /* Replace if stale */ + pico_tree_delete(&arp_tree, found); + pico_arp_add_entry(found); + } else { + /* Update mac address */ + memcpy(found->eth.addr, hdr->s_mac, PICO_SIZE_ETH); + arp_dbg("ARP entry updated!\n"); + + /* Refresh timestamp, this will force a reschedule on the next timeout*/ + found->timestamp = PICO_TIME(); + } + } + + return found; +} + + +static int pico_arp_check_incoming_hdr_type(struct pico_arp_hdr *h) +{ + /* Check the hardware type and protocol */ + if ((h->htype != PICO_ARP_HTYPE_ETH) || (h->ptype != PICO_IDETH_IPV4)) + return -1; + + return 0; +} + +static int pico_arp_check_incoming_hdr(struct pico_frame *f, struct pico_ip4 *dst_addr) +{ + struct pico_arp_hdr *hdr = (struct pico_arp_hdr *) f->net_hdr; + if (!hdr) + return -1; + + dst_addr->addr = hdr->dst.addr; + if (pico_arp_check_incoming_hdr_type(hdr) < 0) + return -1; + + /* The source mac address must not be a multicast or broadcast address */ + if (hdr->s_mac[0] & 0x01) + return -1; + + return 0; +} + +static void pico_arp_reply_on_request(struct pico_frame *f, struct pico_ip4 me) +{ + struct pico_arp_hdr *hdr; + struct pico_eth_hdr *eh; + + hdr = (struct pico_arp_hdr *) f->net_hdr; + eh = (struct pico_eth_hdr *)f->datalink_hdr; + if (hdr->opcode != PICO_ARP_REQUEST) + return; + + hdr->opcode = PICO_ARP_REPLY; + memcpy(hdr->d_mac, hdr->s_mac, PICO_SIZE_ETH); + memcpy(hdr->s_mac, f->dev->eth->mac.addr, PICO_SIZE_ETH); + hdr->dst.addr = hdr->src.addr; + hdr->src.addr = me.addr; + + /* Prepare eth header for arp reply */ + memcpy(eh->daddr, eh->saddr, PICO_SIZE_ETH); + memcpy(eh->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH); + f->start = f->datalink_hdr; + f->len = PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR; + f->dev->send(f->dev, f->start, (int)f->len); +} + +static int pico_arp_check_flooding(struct pico_frame *f, struct pico_ip4 me) +{ + struct pico_device *link_dev; + struct pico_arp_hdr *hdr; + hdr = (struct pico_arp_hdr *) f->net_hdr; + + /* Prevent ARP flooding */ + link_dev = pico_ipv4_link_find(&me); + if ((link_dev == f->dev) && (hdr->opcode == PICO_ARP_REQUEST)) { + if (max_arp_reqs == 0) + return -1; + else + max_arp_reqs--; + } + + /* Check if we are the target IP address */ + if (link_dev != f->dev) + return -1; + + return 0; +} + +static int pico_arp_process_in(struct pico_frame *f, struct pico_arp_hdr *hdr, struct pico_arp *found) +{ + struct pico_ip4 me; + if (pico_arp_check_incoming_hdr(f, &me) < 0) { + pico_frame_discard(f); + return -1; + } + + if (pico_arp_check_flooding(f, me) < 0) { + pico_frame_discard(f); + return -1; + } + + /* If no existing entry was found, create a new entry, or fail trying. */ + if ((!found) && (pico_arp_create_entry(hdr->s_mac, hdr->src, f->dev) < 0)) { + pico_frame_discard(f); + return -1; + } + + /* If the packet is a request, send a reply */ + pico_arp_reply_on_request(f, me); + +#ifdef DEBUG_ARP + dbg_arp(); +#endif + pico_frame_discard(f); + return 0; +} + +int pico_arp_receive(struct pico_frame *f) +{ + struct pico_arp_hdr *hdr; + struct pico_arp *found = NULL; + + hdr = (struct pico_arp_hdr *) f->net_hdr; + if (!hdr) + return -1; + + pico_arp_check_conflict(hdr); + found = pico_arp_lookup_entry(f); + return pico_arp_process_in(f, hdr, found); + +} + +static int32_t pico_arp_request_xmit(struct pico_device *dev, struct pico_frame *f, struct pico_ip4 *src, struct pico_ip4 *dst, uint8_t type) +{ + struct pico_arp_hdr *ah = (struct pico_arp_hdr *) (f->start + PICO_SIZE_ETHHDR); + int ret; + + /* Fill arp header */ + ah->htype = PICO_ARP_HTYPE_ETH; + ah->ptype = PICO_IDETH_IPV4; + ah->hsize = PICO_SIZE_ETH; + ah->psize = PICO_SIZE_IP4; + ah->opcode = PICO_ARP_REQUEST; + memcpy(ah->s_mac, dev->eth->mac.addr, PICO_SIZE_ETH); + + switch (type) { + case PICO_ARP_ANNOUNCE: + ah->src.addr = dst->addr; + ah->dst.addr = dst->addr; + break; + case PICO_ARP_PROBE: + ah->src.addr = 0; + ah->dst.addr = dst->addr; + break; + case PICO_ARP_QUERY: + ah->src.addr = src->addr; + ah->dst.addr = dst->addr; + break; + default: + pico_frame_discard(f); + return -1; + } + arp_dbg("Sending arp request.\n"); + ret = dev->send(dev, f->start, (int) f->len); + pico_frame_discard(f); + return ret; +} + +int32_t pico_arp_request(struct pico_device *dev, struct pico_ip4 *dst, uint8_t type) +{ + struct pico_frame *q = pico_frame_alloc(PICO_SIZE_ETHHDR + PICO_SIZE_ARPHDR); + struct pico_eth_hdr *eh; + struct pico_ip4 *src = NULL; + + if (!q) + return -1; + + if (type == PICO_ARP_QUERY) + { + src = pico_ipv4_source_find(dst); + if (!src) { + pico_frame_discard(q); + return -1; + } + } + + arp_dbg("QUERY: %08x\n", dst->addr); + + eh = (struct pico_eth_hdr *)q->start; + + /* Fill eth header */ + memcpy(eh->saddr, dev->eth->mac.addr, PICO_SIZE_ETH); + memcpy(eh->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH); + eh->proto = PICO_IDETH_ARP; + + return pico_arp_request_xmit(dev, q, src, dst, type); +} + +int pico_arp_get_neighbors(struct pico_device *dev, struct pico_ip4 *neighbors, int maxlen) +{ + struct pico_arp*search; + struct pico_tree_node *index; + int i = 0; + pico_tree_foreach(index, &arp_tree){ + search = index->keyValue; + if (search->dev == dev) { + neighbors[i++].addr = search->ipv4.addr; + if (i >= maxlen) + return i; + } + } + return i; +} + +void pico_arp_register_ipconflict(struct pico_ip4 *ip, struct pico_eth *mac, void (*cb)(void)) +{ + conflict_ipv4.conflict = cb; + conflict_ipv4.ip.addr = ip->addr; + if (mac != NULL) + memcpy(conflict_ipv4.mac.addr, mac, 6); +} + diff --git a/net/picotcp/modules/pico_arp.h b/net/picotcp/modules/pico_arp.h new file mode 100644 index 0000000..18e1f69 --- /dev/null +++ b/net/picotcp/modules/pico_arp.h @@ -0,0 +1,32 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef INCLUDE_PICO_ARP +#define INCLUDE_PICO_ARP +#include "pico_eth.h" +#include "pico_device.h" + +int pico_arp_receive(struct pico_frame *); + + +struct pico_eth *pico_arp_get(struct pico_frame *f); +int32_t pico_arp_request(struct pico_device *dev, struct pico_ip4 *dst, uint8_t type); + +#define PICO_ARP_STATUS_REACHABLE 0x00 +#define PICO_ARP_STATUS_PERMANENT 0x01 +#define PICO_ARP_STATUS_STALE 0x02 + +#define PICO_ARP_QUERY 0x00 +#define PICO_ARP_PROBE 0x01 +#define PICO_ARP_ANNOUNCE 0x02 + +struct pico_eth *pico_arp_lookup(struct pico_ip4 *dst); +struct pico_ip4 *pico_arp_reverse_lookup(struct pico_eth *dst); +int pico_arp_create_entry(uint8_t*hwaddr, struct pico_ip4 ipv4, struct pico_device*dev); +int pico_arp_get_neighbors(struct pico_device *dev, struct pico_ip4 *neighbors, int maxlen); +void pico_arp_register_ipconflict(struct pico_ip4 *ip, struct pico_eth *mac, void (*cb)(void)); +void pico_arp_postpone(struct pico_frame *f); +void pico_arp_init(void); +#endif diff --git a/net/picotcp/modules/pico_dev_loop.c b/net/picotcp/modules/pico_dev_loop.c new file mode 100644 index 0000000..4416d0b --- /dev/null +++ b/net/picotcp/modules/pico_dev_loop.c @@ -0,0 +1,66 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + Authors: Daniele Lacamera + *********************************************************************/ + + +#include "pico_device.h" +#include "pico_dev_loop.h" +#include "pico_stack.h" + + +#define LOOP_MTU 1500 +static uint8_t l_buf[LOOP_MTU]; +static int l_bufsize = 0; + + +static int pico_loop_send(struct pico_device *dev, void *buf, int len) +{ + IGNORE_PARAMETER(dev); + if (len > LOOP_MTU) + return 0; + + if (l_bufsize == 0) { + memcpy(l_buf, buf, (size_t)len); + l_bufsize += len; + return len; + } + + return 0; +} + +static int pico_loop_poll(struct pico_device *dev, int loop_score) +{ + if (loop_score <= 0) + return 0; + + if (l_bufsize > 0) { + pico_stack_recv(dev, l_buf, (uint32_t)l_bufsize); + l_bufsize = 0; + loop_score--; + } + + return loop_score; +} + + +struct pico_device *pico_loop_create(void) +{ + struct pico_device *loop = PICO_ZALLOC(sizeof(struct pico_device)); + if (!loop) + return NULL; + + if( 0 != pico_device_init(loop, "loop", NULL)) { + dbg ("Loop init failed.\n"); + pico_device_destroy(loop); + return NULL; + } + + loop->send = pico_loop_send; + loop->poll = pico_loop_poll; + dbg("Device %s created.\n", loop->name); + return loop; +} + diff --git a/net/picotcp/modules/pico_dev_loop.h b/net/picotcp/modules/pico_dev_loop.h new file mode 100644 index 0000000..6cb8de1 --- /dev/null +++ b/net/picotcp/modules/pico_dev_loop.h @@ -0,0 +1,15 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef INCLUDE_PICO_LOOP +#define INCLUDE_PICO_LOOP +#include "pico_config.h" +#include "pico_device.h" + +void pico_loop_destroy(struct pico_device *loop); +struct pico_device *pico_loop_create(void); + +#endif + diff --git a/net/picotcp/modules/pico_dev_mock.h b/net/picotcp/modules/pico_dev_mock.h new file mode 100644 index 0000000..dfc2cfc --- /dev/null +++ b/net/picotcp/modules/pico_dev_mock.h @@ -0,0 +1,47 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef INCLUDE_PICO_MOCK +#define INCLUDE_PICO_MOCK +#include "pico_config.h" +#include "pico_device.h" + + +struct mock_frame { + uint8_t*buffer; + int len; + int read; + + struct mock_frame*next; +}; + +struct mock_device { + struct pico_device*dev; + struct mock_frame*in_head; + struct mock_frame*in_tail; + struct mock_frame*out_head; + struct mock_frame*out_tail; + + uint8_t*mac; + +}; + +struct mock_device; +/* A mockup-device for the purpose of testing. It provides a couple of extra "network"-functions, which represent the network-side of the device. A network_send will result in mock_poll reading something, a network_read will see if the stack has sent anything through our mock-device. */ +void pico_mock_destroy(struct pico_device *dev); +struct mock_device *pico_mock_create(uint8_t*mac); + +int pico_mock_network_read(struct mock_device*mock, void *buf, int len); +int pico_mock_network_write(struct mock_device*mock, const void *buf, int len); + +/* TODO */ +/* we could use a few checking functions, e.g. one to see if it's a valid IP packet, if it's TCP, if the IP-address matches,... */ +/* That would be useful to avoid having to manually create buffers of what you expect, probably with masks for things that are random,... */ +uint32_t mock_get_sender_ip4(struct mock_device*mock, void*buf, int len); + +int mock_ip_protocol(struct mock_device*mock, void*buf, int len); +int mock_icmp_type(struct mock_device*mock, void*buf, int len); +int mock_icmp_code(struct mock_device*mock, void*buf, int len); +#endif diff --git a/net/picotcp/modules/pico_dev_null.c b/net/picotcp/modules/pico_dev_null.c new file mode 100644 index 0000000..5fed494 --- /dev/null +++ b/net/picotcp/modules/pico_dev_null.c @@ -0,0 +1,60 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + Authors: Daniele Lacamera + *********************************************************************/ + + +#include "pico_device.h" +#include "pico_dev_null.h" +#include "pico_stack.h" + +struct pico_device_null { + struct pico_device dev; + int statistics_frames_out; +}; + +#define NULL_MTU 0 + +static int pico_null_send(struct pico_device *dev, void *buf, int len) +{ + struct pico_device_null *null = (struct pico_device_null *) dev; + IGNORE_PARAMETER(buf); + + /* Increase the statistic count */ + null->statistics_frames_out++; + + /* Discard the frame content silently. */ + return len; +} + +static int pico_null_poll(struct pico_device *dev, int loop_score) +{ + /* We never have packet to receive, no score is used. */ + IGNORE_PARAMETER(dev); + return loop_score; +} + +/* Public interface: create/destroy. */ + + +struct pico_device *pico_null_create(char *name) +{ + struct pico_device_null *null = PICO_ZALLOC(sizeof(struct pico_device_null)); + + if (!null) + return NULL; + + if( 0 != pico_device_init((struct pico_device *)null, name, NULL)) { + return NULL; + } + + null->dev.overhead = 0; + null->statistics_frames_out = 0; + null->dev.send = pico_null_send; + null->dev.poll = pico_null_poll; + dbg("Device %s created.\n", null->dev.name); + return (struct pico_device *)null; +} + diff --git a/net/picotcp/modules/pico_dev_null.h b/net/picotcp/modules/pico_dev_null.h new file mode 100644 index 0000000..a0eb98e --- /dev/null +++ b/net/picotcp/modules/pico_dev_null.h @@ -0,0 +1,15 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef INCLUDE_PICO_NULL +#define INCLUDE_PICO_NULL +#include "pico_config.h" +#include "pico_device.h" + +void pico_null_destroy(struct pico_device *null); +struct pico_device *pico_null_create(char *name); + +#endif + diff --git a/net/picotcp/modules/pico_dhcp_client.c b/net/picotcp/modules/pico_dhcp_client.c new file mode 100644 index 0000000..7f8df7f --- /dev/null +++ b/net/picotcp/modules/pico_dhcp_client.c @@ -0,0 +1,960 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + Authors: Kristof Roelants, Frederik Van Slycken + *********************************************************************/ + + +#include "pico_dhcp_client.h" +#include "pico_stack.h" +#include "pico_config.h" +#include "pico_device.h" +#include "pico_ipv4.h" +#include "pico_socket.h" +#include "pico_eth.h" + +#if (defined PICO_SUPPORT_DHCPC && defined PICO_SUPPORT_UDP) +#define dhcpc_dbg(...) do {} while(0) +/* #define dhcpc_dbg dbg */ + +/* timer values */ +#define DHCP_CLIENT_REINIT 6000 /* msec */ +#define DHCP_CLIENT_RETRANS 4 /* sec */ +#define DHCP_CLIENT_RETRIES 3 + +#define DHCP_CLIENT_TIMER_STOPPED 0 +#define DHCP_CLIENT_TIMER_STARTED 1 + +/* maximum size of a DHCP message */ +#define DHCP_CLIENT_MAXMSGZISE (PICO_IP_MRU - PICO_SIZE_IP4HDR) + +enum dhcp_client_state { + DHCP_CLIENT_STATE_INIT_REBOOT = 0, + DHCP_CLIENT_STATE_REBOOTING, + DHCP_CLIENT_STATE_INIT, + DHCP_CLIENT_STATE_SELECTING, + DHCP_CLIENT_STATE_REQUESTING, + DHCP_CLIENT_STATE_BOUND, + DHCP_CLIENT_STATE_RENEWING, + DHCP_CLIENT_STATE_REBINDING +}; + + +#define PICO_DHCPC_TIMER_INIT 0 +#define PICO_DHCPC_TIMER_REQUEST 1 +#define PICO_DHCPC_TIMER_RENEW 2 +#define PICO_DHCPC_TIMER_REBIND 3 +#define PICO_DHCPC_TIMER_T1 4 +#define PICO_DHCPC_TIMER_T2 5 +#define PICO_DHCPC_TIMER_LEASE 6 +#define PICO_DHCPC_TIMER_ARRAY_SIZE 7 + +struct dhcp_client_timer +{ + uint8_t state; + unsigned int type; + uint32_t xid; +}; + +struct pico_dhcp_client_cookie +{ + uint8_t event; + uint8_t retry; + uint32_t xid; + uint32_t *uid; + enum dhcp_client_state state; + void (*cb)(void*dhcpc, int code); + pico_time init_timestamp; + struct pico_socket *s; + struct pico_ip4 address; + struct pico_ip4 netmask; + struct pico_ip4 gateway; + struct pico_ip4 nameserver[2]; + struct pico_ip4 server_id; + struct pico_device *dev; + struct dhcp_client_timer *timer[PICO_DHCPC_TIMER_ARRAY_SIZE]; + uint32_t t1_time; + uint32_t t2_time; + uint32_t lease_time; + uint32_t renew_time; + uint32_t rebind_time; +}; + +static int pico_dhcp_client_init(struct pico_dhcp_client_cookie *dhcpc); +static int reset(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); +static int8_t pico_dhcp_client_msg(struct pico_dhcp_client_cookie *dhcpc, uint8_t msg_type); +static void pico_dhcp_client_wakeup(uint16_t ev, struct pico_socket *s); +static void pico_dhcp_state_machine(uint8_t event, struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); + +static const struct pico_ip4 bcast = { + .addr = 0xFFFFFFFF +}; + +static const struct pico_ip4 bcast_netmask = { + .addr = 0xFFFFFFFF +}; + +static struct pico_ip4 inaddr_any = { + 0 +}; + + +static int dhcp_cookies_cmp(void *ka, void *kb) +{ + struct pico_dhcp_client_cookie *a = ka, *b = kb; + if (a->xid == b->xid) + return 0; + + return (a->xid < b->xid) ? (-1) : (1); +} +PICO_TREE_DECLARE(DHCPCookies, dhcp_cookies_cmp); + +static struct pico_dhcp_client_cookie *pico_dhcp_client_add_cookie(uint32_t xid, struct pico_device *dev, void (*cb)(void *dhcpc, int code), uint32_t *uid) +{ + struct pico_dhcp_client_cookie *dhcpc = NULL, *found = NULL, test = { + 0 + }; + + test.xid = xid; + found = pico_tree_findKey(&DHCPCookies, &test); + if (found) { + pico_err = PICO_ERR_EAGAIN; + return NULL; + } + + dhcpc = PICO_ZALLOC(sizeof(struct pico_dhcp_client_cookie)); + if (!dhcpc) { + pico_err = PICO_ERR_ENOMEM; + return NULL; + } + + dhcpc->state = DHCP_CLIENT_STATE_INIT; + dhcpc->xid = xid; + dhcpc->uid = uid; + *(dhcpc->uid) = 0; + dhcpc->cb = cb; + dhcpc->dev = dev; + + pico_tree_insert(&DHCPCookies, dhcpc); + return dhcpc; +} + +static void pico_dhcp_client_stop_timers(struct pico_dhcp_client_cookie *dhcpc); +static int pico_dhcp_client_del_cookie(uint32_t xid) +{ + struct pico_dhcp_client_cookie test = { + 0 + }, *found = NULL; + + test.xid = xid; + found = pico_tree_findKey(&DHCPCookies, &test); + if (!found) + return -1; + + pico_dhcp_client_stop_timers(found); + pico_socket_close(found->s); + found->s = NULL; + pico_ipv4_link_del(found->dev, found->address); + pico_tree_delete(&DHCPCookies, found); + PICO_FREE(found); + return 0; +} + +static struct pico_dhcp_client_cookie *pico_dhcp_client_find_cookie(uint32_t xid) +{ + struct pico_dhcp_client_cookie test = { + 0 + }, *found = NULL; + + test.xid = xid; + found = pico_tree_findKey(&DHCPCookies, &test); + if (found) + return found; + else + return NULL; +} + +static void pico_dhcp_client_timer_handler(pico_time now, void *arg); +static void pico_dhcp_client_reinit(pico_time now, void *arg); +static struct dhcp_client_timer *pico_dhcp_timer_add(uint8_t type, uint32_t time, struct pico_dhcp_client_cookie *ck) +{ + struct dhcp_client_timer *t; + + t = PICO_ZALLOC(sizeof(struct dhcp_client_timer)); + if (!t) + return NULL; + + t->state = DHCP_CLIENT_TIMER_STARTED; + t->xid = ck->xid; + t->type = type; + pico_timer_add(time, pico_dhcp_client_timer_handler, t); + if (ck->timer[type]) { + ck->timer[type]->state = DHCP_CLIENT_TIMER_STOPPED; + } + + ck->timer[type] = t; + return t; +} + +static int dhcp_get_timer_event(struct pico_dhcp_client_cookie *dhcpc, unsigned int type) +{ + const int events[PICO_DHCPC_TIMER_ARRAY_SIZE] = + { + PICO_DHCP_EVENT_RETRANSMIT, + PICO_DHCP_EVENT_RETRANSMIT, + PICO_DHCP_EVENT_RETRANSMIT, + PICO_DHCP_EVENT_RETRANSMIT, + PICO_DHCP_EVENT_T1, + PICO_DHCP_EVENT_T2, + PICO_DHCP_EVENT_LEASE + }; + + if (type == PICO_DHCPC_TIMER_REQUEST) { + if (++dhcpc->retry > DHCP_CLIENT_RETRIES) { + reset(dhcpc, NULL); + return PICO_DHCP_EVENT_NONE; + } + } else if (type < PICO_DHCPC_TIMER_T1) { + dhcpc->retry++; + } + + return events[type]; +} + +static void pico_dhcp_client_timer_handler(pico_time now, void *arg) +{ + struct dhcp_client_timer *t = (struct dhcp_client_timer *)arg; + struct pico_dhcp_client_cookie *dhcpc; + + if (!t) + return; + + (void) now; + if (t->state != DHCP_CLIENT_TIMER_STOPPED) { + dhcpc = pico_dhcp_client_find_cookie(t->xid); + if (dhcpc && dhcpc->timer) { + t->state = DHCP_CLIENT_TIMER_STOPPED; + if ((t->type == PICO_DHCPC_TIMER_INIT) && (dhcpc->state < DHCP_CLIENT_STATE_SELECTING)) { + pico_dhcp_client_reinit(now, dhcpc); + } else if (t->type != PICO_DHCPC_TIMER_INIT) { + dhcpc->event = (uint8_t)dhcp_get_timer_event(dhcpc, t->type); + if (dhcpc->event != PICO_DHCP_EVENT_NONE) + pico_dhcp_state_machine(dhcpc->event, dhcpc, NULL); + } + } + } +} + +static void pico_dhcp_client_timer_stop(struct pico_dhcp_client_cookie *dhcpc, int type) +{ + if (dhcpc->timer[type]) { + dhcpc->timer[type]->state = DHCP_CLIENT_TIMER_STOPPED; + } + +} + + +static void pico_dhcp_client_reinit(pico_time now, void *arg) +{ + struct pico_dhcp_client_cookie *dhcpc = (struct pico_dhcp_client_cookie *)arg; + (void) now; + + if (dhcpc->s) { + pico_socket_close(dhcpc->s); + dhcpc->s = NULL; + } + + if (++dhcpc->retry > DHCP_CLIENT_RETRIES) { + pico_err = PICO_ERR_EAGAIN; + if (dhcpc->cb) + dhcpc->cb(dhcpc, PICO_DHCP_ERROR); + + pico_dhcp_client_del_cookie(dhcpc->xid); + return; + } + + pico_dhcp_client_init(dhcpc); + return; +} + + +static void pico_dhcp_client_stop_timers(struct pico_dhcp_client_cookie *dhcpc) +{ + int i; + dhcpc->retry = 0; + for (i = 0; i < PICO_DHCPC_TIMER_ARRAY_SIZE; i++) + pico_dhcp_client_timer_stop(dhcpc, i); +} + +static void pico_dhcp_client_start_init_timer(struct pico_dhcp_client_cookie *dhcpc) +{ + uint32_t time = 0; + /* timer value is doubled with every retry (exponential backoff) */ + time = (uint32_t) (DHCP_CLIENT_RETRANS << dhcpc->retry); + pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, time * 1000, dhcpc); +} + +static void pico_dhcp_client_start_requesting_timer(struct pico_dhcp_client_cookie *dhcpc) +{ + uint32_t time = 0; + + /* timer value is doubled with every retry (exponential backoff) */ + time = (uint32_t)(DHCP_CLIENT_RETRANS << dhcpc->retry); + pico_dhcp_timer_add(PICO_DHCPC_TIMER_REQUEST, time * 1000, dhcpc); +} + +static void pico_dhcp_client_start_renewing_timer(struct pico_dhcp_client_cookie *dhcpc) +{ + uint32_t halftime = 0; + + /* wait one-half of the remaining time until T2, down to a minimum of 60 seconds */ + /* (dhcpc->retry + 1): initial -> divide by 2, 1st retry -> divide by 4, 2nd retry -> divide by 8, etc */ + pico_dhcp_client_stop_timers(dhcpc); + halftime = dhcpc->renew_time >> (dhcpc->retry + 1); + if (halftime < 60) + halftime = 60; + + pico_dhcp_timer_add(PICO_DHCPC_TIMER_RENEW, halftime * 1000, dhcpc); + + return; +} + +static void pico_dhcp_client_start_rebinding_timer(struct pico_dhcp_client_cookie *dhcpc) +{ + uint32_t halftime = 0; + + pico_dhcp_client_stop_timers(dhcpc); + halftime = dhcpc->rebind_time >> (dhcpc->retry + 1); + if (halftime < 60) + halftime = 60; + + pico_dhcp_timer_add(PICO_DHCPC_TIMER_REBIND, halftime * 1000, dhcpc); + + return; +} + +static void pico_dhcp_client_start_reacquisition_timers(struct pico_dhcp_client_cookie *dhcpc) +{ + + pico_dhcp_client_stop_timers(dhcpc); + pico_dhcp_timer_add(PICO_DHCPC_TIMER_T1, dhcpc->t1_time * 1000, dhcpc); + pico_dhcp_timer_add(PICO_DHCPC_TIMER_T2, dhcpc->t2_time * 1000, dhcpc); + pico_dhcp_timer_add(PICO_DHCPC_TIMER_LEASE, dhcpc->lease_time * 1000, dhcpc); +} + +static int pico_dhcp_client_init(struct pico_dhcp_client_cookie *dhcpc) +{ + uint16_t port = PICO_DHCP_CLIENT_PORT; + if (!dhcpc) + return -1; + + /* adding a link with address 0.0.0.0 and netmask 0.0.0.0, + * automatically adds a route for a global broadcast */ + pico_ipv4_link_add(dhcpc->dev, inaddr_any, bcast_netmask); + if (!dhcpc->s) + dhcpc->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_client_wakeup); + + if (!dhcpc->s) { + pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc); + return 0; + } + + dhcpc->s->dev = dhcpc->dev; + if (pico_socket_bind(dhcpc->s, &inaddr_any, &port) < 0) { + pico_socket_close(dhcpc->s); + dhcpc->s = NULL; + pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc); + return 0; + } + + if (pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER) < 0) { + pico_socket_close(dhcpc->s); + dhcpc->s = NULL; + pico_dhcp_timer_add(PICO_DHCPC_TIMER_INIT, DHCP_CLIENT_REINIT, dhcpc); + return 0; + } + + dhcpc->retry = 0; + dhcpc->init_timestamp = PICO_TIME_MS(); + pico_dhcp_client_start_init_timer(dhcpc); + return 0; +} + +int pico_dhcp_initiate_negotiation(struct pico_device *dev, void (*cb)(void *dhcpc, int code), uint32_t *uid) +{ + uint8_t retry = 32; + uint32_t xid = 0; + struct pico_dhcp_client_cookie *dhcpc = NULL; + + if (!dev || !cb || !uid) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + if (!dev->eth) { + pico_err = PICO_ERR_EOPNOTSUPP; + return -1; + } + + /* attempt to generate a correct xid, else fail */ + do { + xid = pico_rand(); + } while (!xid && --retry); + + if (!xid) { + pico_err = PICO_ERR_EAGAIN; + return -1; + } + + dhcpc = pico_dhcp_client_add_cookie(xid, dev, cb, uid); + if (!dhcpc) + return -1; + + dhcpc_dbg("DHCP client: cookie with xid %u\n", dhcpc->xid); + *uid = xid; + return pico_dhcp_client_init(dhcpc); +} + +static void pico_dhcp_client_recv_params(struct pico_dhcp_client_cookie *dhcpc, struct pico_dhcp_opt *opt) +{ + do { + switch (opt->code) + { + case PICO_DHCP_OPT_PAD: + break; + + case PICO_DHCP_OPT_END: + break; + + case PICO_DHCP_OPT_MSGTYPE: + dhcpc->event = opt->ext.msg_type.type; + dhcpc_dbg("DHCP client: message type %u\n", dhcpc->event); + break; + + case PICO_DHCP_OPT_LEASETIME: + dhcpc->lease_time = long_be(opt->ext.lease_time.time); + dhcpc_dbg("DHCP client: lease time %u\n", dhcpc->lease_time); + break; + + case PICO_DHCP_OPT_RENEWALTIME: + dhcpc->t1_time = long_be(opt->ext.renewal_time.time); + dhcpc_dbg("DHCP client: renewal time %u\n", dhcpc->t1_time); + break; + + case PICO_DHCP_OPT_REBINDINGTIME: + dhcpc->t2_time = long_be(opt->ext.rebinding_time.time); + dhcpc_dbg("DHCP client: rebinding time %u\n", dhcpc->t2_time); + break; + + case PICO_DHCP_OPT_ROUTER: + dhcpc->gateway = opt->ext.router.ip; + dhcpc_dbg("DHCP client: router %08X\n", dhcpc->gateway.addr); + break; + + case PICO_DHCP_OPT_DNS: + dhcpc->nameserver[0] = opt->ext.dns1.ip; + dhcpc_dbg("DHCP client: dns1 %08X\n", dhcpc->nameserver[0].addr); + if (opt->len >= 8) { + dhcpc->nameserver[1] = opt->ext.dns2.ip; + dhcpc_dbg("DHCP client: dns1 %08X\n", dhcpc->nameserver[1].addr); + } + + break; + + case PICO_DHCP_OPT_NETMASK: + dhcpc->netmask = opt->ext.netmask.ip; + dhcpc_dbg("DHCP client: netmask %08X\n", dhcpc->netmask.addr); + break; + + case PICO_DHCP_OPT_SERVERID: + dhcpc->server_id = opt->ext.server_id.ip; + dhcpc_dbg("DHCP client: server ID %08X\n", dhcpc->server_id.addr); + break; + + case PICO_DHCP_OPT_OPTOVERLOAD: + dhcpc_dbg("DHCP client: WARNING option overload present (not processed)"); + break; + + default: + dhcpc_dbg("DHCP client: WARNING unsupported option %u\n", opt->code); + break; + } + } while (pico_dhcp_next_option(&opt)); + + /* default values for T1 and T2 when not provided */ + if (!dhcpc->t1_time) + dhcpc->t1_time = dhcpc->lease_time >> 1; + + if (!dhcpc->t2_time) + dhcpc->t2_time = (dhcpc->lease_time * 875) / 1000; + + return; +} + +static int recv_offer(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) +{ + struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf; + struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)hdr->options; + + pico_dhcp_client_recv_params(dhcpc, opt); + if ((dhcpc->event != PICO_DHCP_MSG_OFFER) || !dhcpc->server_id.addr || !dhcpc->netmask.addr || !dhcpc->lease_time) + return -1; + + dhcpc->address.addr = hdr->yiaddr; + + /* we skip state SELECTING, process first offer received */ + dhcpc->state = DHCP_CLIENT_STATE_REQUESTING; + dhcpc->retry = 0; + pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST); + pico_dhcp_client_start_requesting_timer(dhcpc); + return 0; +} + +static int recv_ack(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) +{ + struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)buf; + struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)hdr->options; + struct pico_ip4 address = { + 0 + }; + struct pico_ip4 any_address = { + 0 + }; + + struct pico_ipv4_link *l; + + pico_dhcp_client_recv_params(dhcpc, opt); + if ((dhcpc->event != PICO_DHCP_MSG_ACK) || !dhcpc->server_id.addr || !dhcpc->netmask.addr || !dhcpc->lease_time) + return -1; + + /* Issue #20 the server can transmit on ACK a different IP than the one in OFFER */ + /* RFC2131 ch 4.3.2 ... The client SHOULD use the parameters in the DHCPACK message for configuration */ + if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING) + dhcpc->address.addr = hdr->yiaddr; + + + /* close the socket used for address (re)acquisition */ + pico_socket_close(dhcpc->s); + dhcpc->s = NULL; + + /* Delete all the links before adding the address */ + pico_ipv4_link_del(dhcpc->dev, address); + l = pico_ipv4_link_by_dev(dhcpc->dev); + while(l) { + pico_ipv4_link_del(dhcpc->dev, l->address); + l = pico_ipv4_link_by_dev_next(dhcpc->dev, l); + } + pico_ipv4_link_add(dhcpc->dev, dhcpc->address, dhcpc->netmask); + + dbg("DHCP client: renewal time (T1) %u\n", (unsigned int)dhcpc->t1_time); + dbg("DHCP client: rebinding time (T2) %u\n", (unsigned int)dhcpc->t2_time); + dbg("DHCP client: lease time %u\n", (unsigned int)dhcpc->lease_time); + + /* If router option is received, use it as default gateway */ + if (dhcpc->gateway.addr != 0U) { + pico_ipv4_route_add(any_address, any_address, dhcpc->gateway, 1, NULL); + } + + dhcpc->retry = 0; + dhcpc->renew_time = dhcpc->t2_time - dhcpc->t1_time; + dhcpc->rebind_time = dhcpc->lease_time - dhcpc->t2_time; + pico_dhcp_client_start_reacquisition_timers(dhcpc); + + *(dhcpc->uid) = dhcpc->xid; + if (dhcpc->cb) + dhcpc->cb(dhcpc, PICO_DHCP_SUCCESS); + + dhcpc->state = DHCP_CLIENT_STATE_BOUND; + return 0; +} + +static int renew(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) +{ + uint16_t port = PICO_DHCP_CLIENT_PORT; + (void) buf; + dhcpc->state = DHCP_CLIENT_STATE_RENEWING; + dhcpc->s = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &pico_dhcp_client_wakeup); + if (!dhcpc->s) { + dhcpc_dbg("DHCP client ERROR: failure opening socket on renew, aborting DHCP! (%s)\n", strerror(pico_err)); + if (dhcpc->cb) + dhcpc->cb(dhcpc, PICO_DHCP_ERROR); + + return -1; + } + + if (pico_socket_bind(dhcpc->s, &dhcpc->address, &port) != 0) { + dhcpc_dbg("DHCP client ERROR: failure binding socket on renew, aborting DHCP! (%s)\n", strerror(pico_err)); + pico_socket_close(dhcpc->s); + dhcpc->s = NULL; + if (dhcpc->cb) + dhcpc->cb(dhcpc, PICO_DHCP_ERROR); + + return -1; + } + + dhcpc->retry = 0; + pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST); + pico_dhcp_client_start_renewing_timer(dhcpc); + + return 0; +} + +static int rebind(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) +{ + (void) buf; + + dhcpc->state = DHCP_CLIENT_STATE_REBINDING; + dhcpc->retry = 0; + pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST); + pico_dhcp_client_start_rebinding_timer(dhcpc); + + return 0; +} + +static int reset(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) +{ + struct pico_ip4 address = { + 0 + }; + (void) buf; + + if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING) + address.addr = PICO_IP4_ANY; + else + address.addr = dhcpc->address.addr; + + /* close the socket used for address (re)acquisition */ + pico_socket_close(dhcpc->s); + dhcpc->s = NULL; + /* delete the link with the currently in use address */ + pico_ipv4_link_del(dhcpc->dev, address); + + if (dhcpc->cb) + dhcpc->cb(dhcpc, PICO_DHCP_RESET); + + if (dhcpc->state < DHCP_CLIENT_STATE_BOUND) + { + /* pico_dhcp_client_timer_stop(dhcpc, PICO_DHCPC_TIMER_INIT); */ + } + + + dhcpc->state = DHCP_CLIENT_STATE_INIT; + pico_dhcp_client_stop_timers(dhcpc); + pico_dhcp_client_init(dhcpc); + return 0; +} + +static int retransmit(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) +{ + (void) buf; + switch (dhcpc->state) + { + case DHCP_CLIENT_STATE_INIT: + pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER); + pico_dhcp_client_start_init_timer(dhcpc); + break; + + case DHCP_CLIENT_STATE_REQUESTING: + pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST); + pico_dhcp_client_start_requesting_timer(dhcpc); + break; + + case DHCP_CLIENT_STATE_RENEWING: + pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_REQUEST); + pico_dhcp_client_start_renewing_timer(dhcpc); + break; + + case DHCP_CLIENT_STATE_REBINDING: + pico_dhcp_client_msg(dhcpc, PICO_DHCP_MSG_DISCOVER); + pico_dhcp_client_start_rebinding_timer(dhcpc); + break; + + default: + dhcpc_dbg("DHCP client WARNING: retransmit in incorrect state (%u)!\n", dhcpc->state); + return -1; + } + return 0; +} + +struct dhcp_action_entry { + int (*offer)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); + int (*ack)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); + int (*nak)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); + int (*timer1)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); + int (*timer2)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); + int (*timer_lease)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); + int (*timer_retransmit)(struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf); +}; + +static struct dhcp_action_entry dhcp_fsm[] = +{ /* event |offer |ack |nak |T1 |T2 |lease |retransmit */ +/* state init-reboot */ + { NULL, NULL, NULL, NULL, NULL, NULL, NULL }, +/* state rebooting */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL }, +/* state init */ { recv_offer, NULL, NULL, NULL, NULL, NULL, retransmit }, +/* state selecting */ { NULL, NULL, NULL, NULL, NULL, NULL, NULL }, +/* state requesting */ { NULL, recv_ack, reset, NULL, NULL, NULL, retransmit }, +/* state bound */ { NULL, NULL, NULL, renew, NULL, NULL, NULL }, +/* state renewing */ { NULL, recv_ack, reset, NULL, rebind, NULL, retransmit }, +/* state rebinding */ { NULL, recv_ack, reset, NULL, NULL, reset, retransmit }, +}; + +/* TIMERS REMARK: + * In state bound we have T1, T2 and the lease timer running. If T1 goes off, we attempt to renew. + * If the renew succeeds a new T1, T2 and lease timer is started. The former T2 and lease timer is + * still running though. This poses no concerns as the T2 and lease event in state bound have a NULL + * pointer in the fsm. If the former T2 or lease timer goes off, nothing happens. Same situation + * applies for T2 and a succesfull rebind. */ + +static void pico_dhcp_state_machine(uint8_t event, struct pico_dhcp_client_cookie *dhcpc, uint8_t *buf) +{ + switch (event) + { + case PICO_DHCP_MSG_OFFER: + dhcpc_dbg("DHCP client: received OFFER\n"); + if (dhcp_fsm[dhcpc->state].offer) + dhcp_fsm[dhcpc->state].offer(dhcpc, buf); + + break; + + case PICO_DHCP_MSG_ACK: + dhcpc_dbg("DHCP client: received ACK\n"); + if (dhcp_fsm[dhcpc->state].ack) + dhcp_fsm[dhcpc->state].ack(dhcpc, buf); + + break; + + case PICO_DHCP_MSG_NAK: + dhcpc_dbg("DHCP client: received NAK\n"); + if (dhcp_fsm[dhcpc->state].nak) + dhcp_fsm[dhcpc->state].nak(dhcpc, buf); + + break; + + case PICO_DHCP_EVENT_T1: + dhcpc_dbg("DHCP client: received T1 timeout\n"); + if (dhcp_fsm[dhcpc->state].timer1) + dhcp_fsm[dhcpc->state].timer1(dhcpc, NULL); + + break; + + case PICO_DHCP_EVENT_T2: + dhcpc_dbg("DHCP client: received T2 timeout\n"); + if (dhcp_fsm[dhcpc->state].timer2) + dhcp_fsm[dhcpc->state].timer2(dhcpc, NULL); + + break; + + case PICO_DHCP_EVENT_LEASE: + dhcpc_dbg("DHCP client: received LEASE timeout\n"); + if (dhcp_fsm[dhcpc->state].timer_lease) + dhcp_fsm[dhcpc->state].timer_lease(dhcpc, NULL); + + break; + + case PICO_DHCP_EVENT_RETRANSMIT: + dhcpc_dbg("DHCP client: received RETRANSMIT timeout\n"); + if (dhcp_fsm[dhcpc->state].timer_retransmit) + dhcp_fsm[dhcpc->state].timer_retransmit(dhcpc, NULL); + + break; + + default: + dhcpc_dbg("DHCP client WARNING: unrecognized event (%u)!\n", dhcpc->event); + return; + } + return; +} + +static int16_t pico_dhcp_client_opt_parse(void *ptr, uint16_t len) +{ + uint32_t optlen = len - (uint32_t)sizeof(struct pico_dhcp_hdr); + struct pico_dhcp_hdr *hdr = (struct pico_dhcp_hdr *)ptr; + struct pico_dhcp_opt *opt = NULL; + + if (hdr->dhcp_magic != PICO_DHCPD_MAGIC_COOKIE) + return -1; + + if (!pico_dhcp_are_options_valid(hdr->options, (int32_t)optlen)) + return -1; + + opt = (struct pico_dhcp_opt *)hdr->options; + do { + if (opt->code == PICO_DHCP_OPT_MSGTYPE) + return opt->ext.msg_type.type; + } while (pico_dhcp_next_option(&opt)); + + return -1; +} + +static int8_t pico_dhcp_client_msg(struct pico_dhcp_client_cookie *dhcpc, uint8_t msg_type) +{ + int32_t r = 0; + uint16_t optlen = 0, offset = 0; + struct pico_ip4 destination = { + .addr = 0xFFFFFFFF + }; + struct pico_dhcp_hdr *hdr = NULL; + + + /* RFC 2131 3.1.3: Request is always BROADCAST */ + + /* Set again default route for the bcast request */ + pico_ipv4_route_set_bcast_link(pico_ipv4_link_by_dev(dhcpc->dev)); + + switch (msg_type) + { + case PICO_DHCP_MSG_DISCOVER: + dhcpc_dbg("DHCP client: sent DHCPDISCOVER\n"); + optlen = PICO_DHCP_OPTLEN_MSGTYPE + PICO_DHCP_OPTLEN_MAXMSGSIZE + PICO_DHCP_OPTLEN_PARAMLIST + PICO_DHCP_OPTLEN_END; + hdr = PICO_ZALLOC((size_t)(sizeof(struct pico_dhcp_hdr) + optlen)); + if (!hdr) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + /* specific options */ + offset = (uint16_t)(offset + pico_dhcp_opt_maxmsgsize(&hdr->options[offset], DHCP_CLIENT_MAXMSGZISE)); + break; + + case PICO_DHCP_MSG_REQUEST: + optlen = PICO_DHCP_OPTLEN_MSGTYPE + PICO_DHCP_OPTLEN_MAXMSGSIZE + PICO_DHCP_OPTLEN_PARAMLIST + PICO_DHCP_OPTLEN_REQIP + PICO_DHCP_OPTLEN_SERVERID + + PICO_DHCP_OPTLEN_END; + hdr = PICO_ZALLOC(sizeof(struct pico_dhcp_hdr) + optlen); + if (!hdr) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + /* specific options */ + offset = (uint16_t)(offset + pico_dhcp_opt_maxmsgsize(&hdr->options[offset], DHCP_CLIENT_MAXMSGZISE)); + if (dhcpc->state == DHCP_CLIENT_STATE_REQUESTING) { + offset = (uint16_t)(offset + pico_dhcp_opt_reqip(&hdr->options[offset], &dhcpc->address)); + offset = (uint16_t)(offset + pico_dhcp_opt_serverid(&hdr->options[offset], &dhcpc->server_id)); + } + + break; + + default: + return -1; + } + + /* common options */ + offset = (uint16_t)(offset + pico_dhcp_opt_msgtype(&hdr->options[offset], msg_type)); + offset = (uint16_t)(offset + pico_dhcp_opt_paramlist(&hdr->options[offset])); + offset = (uint16_t)(offset + pico_dhcp_opt_end(&hdr->options[offset])); + + switch (dhcpc->state) + { + case DHCP_CLIENT_STATE_BOUND: + destination.addr = dhcpc->server_id.addr; + hdr->ciaddr = dhcpc->address.addr; + break; + + case DHCP_CLIENT_STATE_RENEWING: + destination.addr = dhcpc->server_id.addr; + hdr->ciaddr = dhcpc->address.addr; + break; + + case DHCP_CLIENT_STATE_REBINDING: + hdr->ciaddr = dhcpc->address.addr; + break; + + default: + /* do nothing */ + break; + } + + /* header information */ + hdr->op = PICO_DHCP_OP_REQUEST; + hdr->htype = PICO_DHCP_HTYPE_ETH; + hdr->hlen = PICO_SIZE_ETH; + hdr->xid = dhcpc->xid; + /* hdr->flags = short_be(PICO_DHCP_FLAG_BROADCAST); / * Nope: see bug #96! * / */ + hdr->dhcp_magic = PICO_DHCPD_MAGIC_COOKIE; + /* copy client hardware address */ + memcpy(hdr->hwaddr, &dhcpc->dev->eth->mac, PICO_SIZE_ETH); + + if (destination.addr == PICO_IP4_BCAST) + pico_ipv4_route_set_bcast_link(pico_ipv4_link_get(&dhcpc->address)); + + r = pico_socket_sendto(dhcpc->s, hdr, (int)(sizeof(struct pico_dhcp_hdr) + optlen), &destination, PICO_DHCPD_PORT); + PICO_FREE(hdr); + if (r < 0) + return -1; + + return 0; +} + +static void pico_dhcp_client_wakeup(uint16_t ev, struct pico_socket *s) +{ + + uint8_t *buf; + int r = 0; + struct pico_dhcp_hdr *hdr = NULL; + struct pico_dhcp_client_cookie *dhcpc = NULL; + + if ((ev & PICO_SOCK_EV_RD) == 0) + return; + + buf = PICO_ZALLOC(DHCP_CLIENT_MAXMSGZISE); + if (!buf) { + return; + } + + r = pico_socket_recvfrom(s, buf, DHCP_CLIENT_MAXMSGZISE, NULL, NULL); + if (r < 0) + goto out_discard_buf; + + /* If the 'xid' of an arriving message does not match the 'xid' + * of the most recent transmitted message, the message must be + * silently discarded. */ + hdr = (struct pico_dhcp_hdr *)buf; + dhcpc = pico_dhcp_client_find_cookie(hdr->xid); + if (!dhcpc) + goto out_discard_buf; + + dhcpc->event = (uint8_t)pico_dhcp_client_opt_parse(buf, (uint16_t)r); + pico_dhcp_state_machine(dhcpc->event, dhcpc, buf); + +out_discard_buf: + PICO_FREE(buf); +} + +void *pico_dhcp_get_identifier(uint32_t xid) +{ + return (void *)pico_dhcp_client_find_cookie(xid); +} + +struct pico_ip4 pico_dhcp_get_address(void*dhcpc) +{ + return ((struct pico_dhcp_client_cookie*)dhcpc)->address; +} + +struct pico_ip4 pico_dhcp_get_gateway(void*dhcpc) +{ + return ((struct pico_dhcp_client_cookie*)dhcpc)->gateway; +} + +struct pico_ip4 pico_dhcp_get_netmask(void *dhcpc) +{ + return ((struct pico_dhcp_client_cookie*)dhcpc)->netmask; +} + +struct pico_ip4 pico_dhcp_get_nameserver(void*dhcpc, int index) +{ + struct pico_ip4 fault = { + .addr = 0xFFFFFFFFU + }; + if ((index != 0) && (index != 1)) + return fault; + + return ((struct pico_dhcp_client_cookie*)dhcpc)->nameserver[index]; +} + +int pico_dhcp_client_abort(uint32_t xid) +{ + return pico_dhcp_client_del_cookie(xid); +} +#endif diff --git a/net/picotcp/modules/pico_dhcp_client.h b/net/picotcp/modules/pico_dhcp_client.h new file mode 100644 index 0000000..2b5bbf9 --- /dev/null +++ b/net/picotcp/modules/pico_dhcp_client.h @@ -0,0 +1,30 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + *********************************************************************/ +#ifndef INCLUDE_PICO_DHCP_CLIENT +#define INCLUDE_PICO_DHCP_CLIENT +#include "pico_defines.h" +#ifdef PICO_SUPPORT_UDP +#include "pico_dhcp_common.h" +#include "pico_addressing.h" +#include "pico_protocol.h" + +int pico_dhcp_initiate_negotiation(struct pico_device *device, void (*callback)(void*cli, int code), uint32_t *xid); +void *pico_dhcp_get_identifier(uint32_t xid); +struct pico_ip4 pico_dhcp_get_address(void *cli); +struct pico_ip4 pico_dhcp_get_gateway(void *cli); +struct pico_ip4 pico_dhcp_get_netmask(void *cli); +struct pico_ip4 pico_dhcp_get_nameserver(void*cli, int index); +int pico_dhcp_client_abort(uint32_t xid); + +/* possible codes for the callback */ +#define PICO_DHCP_SUCCESS 0 +#define PICO_DHCP_ERROR 1 +#define PICO_DHCP_RESET 2 + +#endif +#endif diff --git a/net/picotcp/modules/pico_dhcp_common.c b/net/picotcp/modules/pico_dhcp_common.c new file mode 100644 index 0000000..99701ba --- /dev/null +++ b/net/picotcp/modules/pico_dhcp_common.c @@ -0,0 +1,189 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + Authors: Frederik Van Slycken + *********************************************************************/ + +#include "pico_config.h" +#include "pico_stack.h" +#include "pico_dhcp_common.h" + +#if defined (PICO_SUPPORT_DHCPC) || defined (PICO_SUPPORT_DHCPD) +/* pico_dhcp_are_options_valid needs to be called first to prevent illegal memory access */ +/* The argument pointer is moved forward to the next option */ +struct pico_dhcp_opt *pico_dhcp_next_option(struct pico_dhcp_opt **ptr) +{ + uint8_t **p = (uint8_t **)ptr; + struct pico_dhcp_opt *opt = *ptr; + + if (opt->code == PICO_DHCP_OPT_END) + return NULL; + + if (opt->code == PICO_DHCP_OPT_PAD) { + *p += 1; + return *ptr; + } + + *p += (opt->len + 2); /* (len + 2) to account for code and len octet */ + return *ptr; +} + +uint8_t pico_dhcp_are_options_valid(void *ptr, int32_t len) +{ + uint8_t optlen = 0, *p = ptr; + + while (len > 0) { + switch (*p) + { + case PICO_DHCP_OPT_END: + return 1; + + case PICO_DHCP_OPT_PAD: + p++; + len--; + break; + + default: + p++; /* move pointer from code octet to len octet */ + len--; + if ((len <= 0) || (len - (*p + 1) < 0)) /* (*p + 1) to account for len octet */ + return 0; + + optlen = *p; + p += optlen + 1; + len -= optlen; + break; + } + } + return 0; +} + +uint8_t pico_dhcp_opt_netmask(void *ptr, struct pico_ip4 *ip) +{ + struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; + + /* option: netmask */ + opt->code = PICO_DHCP_OPT_NETMASK; + opt->len = PICO_DHCP_OPTLEN_NETMASK - PICO_DHCP_OPTLEN_HDR; + opt->ext.netmask.ip = *ip; + return PICO_DHCP_OPTLEN_NETMASK; +} + +uint8_t pico_dhcp_opt_router(void *ptr, struct pico_ip4 *ip) +{ + struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; + + /* option: router */ + opt->code = PICO_DHCP_OPT_ROUTER; + opt->len = PICO_DHCP_OPTLEN_ROUTER - PICO_DHCP_OPTLEN_HDR; + opt->ext.router.ip = *ip; + return PICO_DHCP_OPTLEN_ROUTER; +} + +uint8_t pico_dhcp_opt_dns(void *ptr, struct pico_ip4 *ip) +{ + struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; + + /* option: dns */ + opt->code = PICO_DHCP_OPT_DNS; + opt->len = PICO_DHCP_OPTLEN_DNS - PICO_DHCP_OPTLEN_HDR; + opt->ext.dns1.ip = *ip; + return PICO_DHCP_OPTLEN_DNS; +} + +uint8_t pico_dhcp_opt_broadcast(void *ptr, struct pico_ip4 *ip) +{ + struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; + + /* option: broadcast */ + opt->code = PICO_DHCP_OPT_BROADCAST; + opt->len = PICO_DHCP_OPTLEN_BROADCAST - PICO_DHCP_OPTLEN_HDR; + opt->ext.broadcast.ip = *ip; + return PICO_DHCP_OPTLEN_BROADCAST; +} + +uint8_t pico_dhcp_opt_reqip(void *ptr, struct pico_ip4 *ip) +{ + struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; + + /* option: request IP address */ + opt->code = PICO_DHCP_OPT_REQIP; + opt->len = PICO_DHCP_OPTLEN_REQIP - PICO_DHCP_OPTLEN_HDR; + opt->ext.req_ip.ip = *ip; + return PICO_DHCP_OPTLEN_REQIP; +} + +uint8_t pico_dhcp_opt_leasetime(void *ptr, uint32_t time) +{ + struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; + + /* option: lease time */ + opt->code = PICO_DHCP_OPT_LEASETIME; + opt->len = PICO_DHCP_OPTLEN_LEASETIME - PICO_DHCP_OPTLEN_HDR; + opt->ext.lease_time.time = time; + return PICO_DHCP_OPTLEN_LEASETIME; +} + +uint8_t pico_dhcp_opt_msgtype(void *ptr, uint8_t type) +{ + struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; + + /* option: message type */ + opt->code = PICO_DHCP_OPT_MSGTYPE; + opt->len = PICO_DHCP_OPTLEN_MSGTYPE - PICO_DHCP_OPTLEN_HDR; + opt->ext.msg_type.type = type; + return PICO_DHCP_OPTLEN_MSGTYPE; +} + +uint8_t pico_dhcp_opt_serverid(void *ptr, struct pico_ip4 *ip) +{ + struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; + + /* option: server identifier */ + opt->code = PICO_DHCP_OPT_SERVERID; + opt->len = PICO_DHCP_OPTLEN_SERVERID - PICO_DHCP_OPTLEN_HDR; + opt->ext.server_id.ip = *ip; + return PICO_DHCP_OPTLEN_SERVERID; +} + +uint8_t pico_dhcp_opt_paramlist(void *ptr) +{ + struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; + + /* option: parameter list */ + opt->code = PICO_DHCP_OPT_PARAMLIST; + opt->len = PICO_DHCP_OPTLEN_PARAMLIST - PICO_DHCP_OPTLEN_HDR; + opt->ext.param_list.code[0] = PICO_DHCP_OPT_NETMASK; + opt->ext.param_list.code[1] = PICO_DHCP_OPT_TIME; + opt->ext.param_list.code[2] = PICO_DHCP_OPT_ROUTER; + opt->ext.param_list.code[3] = PICO_DHCP_OPT_HOSTNAME; + opt->ext.param_list.code[4] = PICO_DHCP_OPT_RENEWALTIME; + opt->ext.param_list.code[5] = PICO_DHCP_OPT_REBINDINGTIME; + opt->ext.param_list.code[6] = PICO_DHCP_OPT_DNS; + return PICO_DHCP_OPTLEN_PARAMLIST; +} + +uint8_t pico_dhcp_opt_maxmsgsize(void *ptr, uint16_t size) +{ + struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr; + + /* option: maximum message size */ + opt->code = PICO_DHCP_OPT_MAXMSGSIZE; + opt->len = PICO_DHCP_OPTLEN_MAXMSGSIZE - PICO_DHCP_OPTLEN_HDR; + opt->ext.max_msg_size.size = short_be(size); + return PICO_DHCP_OPTLEN_MAXMSGSIZE; +} + +uint8_t pico_dhcp_opt_end(void *ptr) +{ + uint8_t *opt = (uint8_t *)ptr; + + /* option: end of options */ + *opt = PICO_DHCP_OPT_END; + return PICO_DHCP_OPTLEN_END; +} + +#endif diff --git a/net/picotcp/modules/pico_dhcp_common.h b/net/picotcp/modules/pico_dhcp_common.h new file mode 100644 index 0000000..cdf1bd1 --- /dev/null +++ b/net/picotcp/modules/pico_dhcp_common.h @@ -0,0 +1,186 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + *********************************************************************/ +#ifndef INCLUDE_PICO_DHCP_COMMON +#define INCLUDE_PICO_DHCP_COMMON +#include "pico_config.h" +#include "pico_addressing.h" + +#define PICO_DHCPD_PORT (short_be(67)) +#define PICO_DHCP_CLIENT_PORT (short_be(68)) +#define PICO_DHCPD_MAGIC_COOKIE (long_be(0x63825363)) +#define PICO_DHCP_HTYPE_ETH 1 + +/* flags */ +#define PICO_DHCP_FLAG_BROADCAST 0x8000 + +/* options */ +#define PICO_DHCP_OPT_PAD 0x00 +#define PICO_DHCP_OPT_NETMASK 0x01 +#define PICO_DHCP_OPT_TIME 0x02 +#define PICO_DHCP_OPT_ROUTER 0x03 +#define PICO_DHCP_OPT_DNS 0x06 +#define PICO_DHCP_OPT_HOSTNAME 0x0c +#define PICO_DHCP_OPT_DOMAINNAME 0x0f +#define PICO_DHCP_OPT_MTU 0x1a +#define PICO_DHCP_OPT_BROADCAST 0x1c +#define PICO_DHCP_OPT_NETBIOSNS 0x2c +#define PICO_DHCP_OPT_NETBIOSSCOPE 0x2f +#define PICO_DHCP_OPT_REQIP 0x32 +#define PICO_DHCP_OPT_LEASETIME 0x33 +#define PICO_DHCP_OPT_OPTOVERLOAD 0x34 +#define PICO_DHCP_OPT_MSGTYPE 0x35 +#define PICO_DHCP_OPT_SERVERID 0x36 +#define PICO_DHCP_OPT_PARAMLIST 0x37 +#define PICO_DHCP_OPT_MESSAGE 0x38 +#define PICO_DHCP_OPT_MAXMSGSIZE 0x39 +#define PICO_DHCP_OPT_RENEWALTIME 0x3a +#define PICO_DHCP_OPT_REBINDINGTIME 0x3b +#define PICO_DHCP_OPT_VENDORID 0x3c +#define PICO_DHCP_OPT_CLIENTID 0x3d +#define PICO_DHCP_OPT_DOMAINSEARCH 0x77 +#define PICO_DHCP_OPT_STATICROUTE 0x79 +#define PICO_DHCP_OPT_END 0xFF + +/* options len */ +#define PICO_DHCP_OPTLEN_HDR 2 /* account for code and len field */ +#define PICO_DHCP_OPTLEN_NETMASK 6 +#define PICO_DHCP_OPTLEN_ROUTER 6 +#define PICO_DHCP_OPTLEN_DNS 6 +#define PICO_DHCP_OPTLEN_BROADCAST 6 +#define PICO_DHCP_OPTLEN_REQIP 6 +#define PICO_DHCP_OPTLEN_LEASETIME 6 +#define PICO_DHCP_OPTLEN_OPTOVERLOAD 3 +#define PICO_DHCP_OPTLEN_MSGTYPE 3 +#define PICO_DHCP_OPTLEN_SERVERID 6 +#define PICO_DHCP_OPTLEN_PARAMLIST 9 /* PicoTCP specific */ +#define PICO_DHCP_OPTLEN_MAXMSGSIZE 4 +#define PICO_DHCP_OPTLEN_RENEWALTIME 6 +#define PICO_DHCP_OPTLEN_REBINDINGTIME 6 +#define PICO_DHCP_OPTLEN_END 1 + +/* op codes */ +#define PICO_DHCP_OP_REQUEST 1 +#define PICO_DHCP_OP_REPLY 2 + +/* rfc message types */ +#define PICO_DHCP_MSG_DISCOVER 1 +#define PICO_DHCP_MSG_OFFER 2 +#define PICO_DHCP_MSG_REQUEST 3 +#define PICO_DHCP_MSG_DECLINE 4 +#define PICO_DHCP_MSG_ACK 5 +#define PICO_DHCP_MSG_NAK 6 +#define PICO_DHCP_MSG_RELEASE 7 +#define PICO_DHCP_MSG_INFORM 8 + +/* custom message types */ +#define PICO_DHCP_EVENT_T1 9 +#define PICO_DHCP_EVENT_T2 10 +#define PICO_DHCP_EVENT_LEASE 11 +#define PICO_DHCP_EVENT_RETRANSMIT 12 +#define PICO_DHCP_EVENT_NONE 0xff + +PACKED_STRUCT_DEF pico_dhcp_hdr +{ + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; /* zero */ + uint32_t xid; /* store this in the request */ + uint16_t secs; /* ignore */ + uint16_t flags; + uint32_t ciaddr; /* client address - if asking for renewal */ + uint32_t yiaddr; /* your address (client) */ + uint32_t siaddr; /* dhcp offered address */ + uint32_t giaddr; /* relay agent, bootp. */ + uint8_t hwaddr[6]; + uint8_t hwaddr_padding[10]; + char hostname[64]; + char bootp_filename[128]; + uint32_t dhcp_magic; + uint8_t options[0]; +}; + +PACKED_STRUCT_DEF pico_dhcp_opt +{ + uint8_t code; + uint8_t len; + PACKED_UNION_DEF dhcp_opt_ext_u { + PEDANTIC_STRUCT_DEF netmask_s { + struct pico_ip4 ip; + } netmask; + PEDANTIC_STRUCT_DEF router_s { + struct pico_ip4 ip; + } router; + PEDANTIC_STRUCT_DEF dns_s { + struct pico_ip4 ip; + } dns1; + struct dns_s dns2; + PEDANTIC_STRUCT_DEF broadcast_s { + struct pico_ip4 ip; + } broadcast; + PEDANTIC_STRUCT_DEF req_ip_s { + struct pico_ip4 ip; + } req_ip; + PEDANTIC_STRUCT_DEF lease_time_s { + uint32_t time; + } lease_time; + PEDANTIC_STRUCT_DEF opt_overload_s { + uint8_t value; + } opt_overload; + PEDANTIC_STRUCT_DEF tftp_server_s { + char name[1]; + } tftp_server; + PEDANTIC_STRUCT_DEF bootfile_s { + char name[1]; + } bootfile; + PEDANTIC_STRUCT_DEF msg_type_s { + uint8_t type; + } msg_type; + PEDANTIC_STRUCT_DEF server_id_s { + struct pico_ip4 ip; + } server_id; + PEDANTIC_STRUCT_DEF param_list_s { + uint8_t code[1]; + } param_list; + PEDANTIC_STRUCT_DEF message_s { + char error[1]; + } message; + PEDANTIC_STRUCT_DEF max_msg_size_s { + uint16_t size; + } max_msg_size; + PEDANTIC_STRUCT_DEF renewal_time_s { + uint32_t time; + } renewal_time; + PEDANTIC_STRUCT_DEF rebinding_time_s { + uint32_t time; + } rebinding_time; + PEDANTIC_STRUCT_DEF vendor_id_s { + uint8_t id[1]; + } vendor_id; + PEDANTIC_STRUCT_DEF client_id_s { + uint8_t id[1]; + } client_id; + } ext; +}; + +uint8_t dhcp_get_next_option(uint8_t *begin, uint8_t *data, int *len, uint8_t **nextopt); +struct pico_dhcp_opt *pico_dhcp_next_option(struct pico_dhcp_opt **ptr); +uint8_t pico_dhcp_are_options_valid(void *ptr, int32_t len); + +uint8_t pico_dhcp_opt_netmask(void *ptr, struct pico_ip4 *ip); +uint8_t pico_dhcp_opt_router(void *ptr, struct pico_ip4 *ip); +uint8_t pico_dhcp_opt_dns(void *ptr, struct pico_ip4 *ip); +uint8_t pico_dhcp_opt_broadcast(void *ptr, struct pico_ip4 *ip); +uint8_t pico_dhcp_opt_reqip(void *ptr, struct pico_ip4 *ip); +uint8_t pico_dhcp_opt_leasetime(void *ptr, uint32_t time); +uint8_t pico_dhcp_opt_msgtype(void *ptr, uint8_t type); +uint8_t pico_dhcp_opt_serverid(void *ptr, struct pico_ip4 *ip); +uint8_t pico_dhcp_opt_paramlist(void *ptr); +uint8_t pico_dhcp_opt_maxmsgsize(void *ptr, uint16_t size); +uint8_t pico_dhcp_opt_end(void *ptr); +#endif diff --git a/net/picotcp/modules/pico_dns_client.h b/net/picotcp/modules/pico_dns_client.h new file mode 100644 index 0000000..910cc92 --- /dev/null +++ b/net/picotcp/modules/pico_dns_client.h @@ -0,0 +1,46 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + Authors: Kristof Roelants + *********************************************************************/ + +#ifndef INCLUDE_PICO_DNS_CLIENT +#define INCLUDE_PICO_DNS_CLIENT + +#define PICO_DNS_NS_DEL 0 +#define PICO_DNS_NS_ADD 1 +#include "pico_config.h" + +/* Compression values */ +#define PICO_DNS_LABEL 0 +#define PICO_DNS_POINTER 3 + +/* Label len */ +#define PICO_DNS_LABEL_INITIAL 1u +#define PICO_DNS_LABEL_ROOT 1 + +/* TTL values */ +#define PICO_DNS_MAX_TTL 604800 /* one week */ + +/* Len of an IPv4 address string */ +#define PICO_DNS_IPV4_ADDR_LEN 16 +#define PICO_DNS_IPV6_ADDR_LEN 54 + +/* Default nameservers + port */ +#define PICO_DNS_NS_DEFAULT "208.67.222.222" +#define PICO_DNS_NS_PORT 53 + +int pico_dns_client_init(void); +/* flag is PICO_DNS_NS_DEL or PICO_DNS_NS_ADD */ +int pico_dns_client_nameserver(struct pico_ip4 *ns, uint8_t flag); +int pico_dns_client_getaddr(const char *url, void (*callback)(char *ip, void *arg), void *arg); +int pico_dns_client_getname(const char *ip, void (*callback)(char *url, void *arg), void *arg); +#ifdef PICO_SUPPORT_IPV6 +int pico_dns_client_getaddr6(const char *url, void (*callback)(char *, void *), void *arg); +int pico_dns_client_getname6(const char *url, void (*callback)(char *, void *), void *arg); +#endif + +#endif /* _INCLUDE_PICO_DNS_CLIENT */ diff --git a/net/picotcp/modules/pico_dns_common.h b/net/picotcp/modules/pico_dns_common.h new file mode 100644 index 0000000..9172696 --- /dev/null +++ b/net/picotcp/modules/pico_dns_common.h @@ -0,0 +1,521 @@ + +/********************************************************************* + PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. + See LICENSE and COPYING for usage. + . + Authors: Toon Stegen, Jelle De Vleeschouwer + *********************************************************************/ + +#ifndef INCLUDE_PICO_DNS_COMMON +#define INCLUDE_PICO_DNS_COMMON + +#include "pico_config.h" +#include "pico_tree.h" + +/* TYPE values */ +#define PICO_DNS_TYPE_A 1 +#define PICO_DNS_TYPE_CNAME 5 +#define PICO_DNS_TYPE_PTR 12 +#define PICO_DNS_TYPE_TXT 16 +#define PICO_DNS_TYPE_AAAA 28 +#define PICO_DNS_TYPE_SRV 33 +#define PICO_DNS_TYPE_NSEC 47 +#define PICO_DNS_TYPE_ANY 255 + +/* CLASS values */ +#define PICO_DNS_CLASS_IN 1 + +/* FLAG values */ +#define PICO_DNS_QR_QUERY 0 +#define PICO_DNS_QR_RESPONSE 1 +#define PICO_DNS_OPCODE_QUERY 0 +#define PICO_DNS_OPCODE_IQUERY 1 +#define PICO_DNS_OPCODE_STATUS 2 +#define PICO_DNS_AA_NO_AUTHORITY 0 +#define PICO_DNS_AA_IS_AUTHORITY 1 +#define PICO_DNS_TC_NO_TRUNCATION 0 +#define PICO_DNS_TC_IS_TRUNCATED 1 +#define PICO_DNS_RD_NO_DESIRE 0 +#define PICO_DNS_RD_IS_DESIRED 1 +#define PICO_DNS_RA_NO_SUPPORT 0 +#define PICO_DNS_RA_IS_SUPPORTED 1 +#define PICO_DNS_RCODE_NO_ERROR 0 +#define PICO_DNS_RCODE_EFORMAT 1 +#define PICO_DNS_RCODE_ESERVER 2 +#define PICO_DNS_RCODE_ENAME 3 +#define PICO_DNS_RCODE_ENOIMP 4 +#define PICO_DNS_RCODE_EREFUSED 5 + +#define PICO_ARPA_IPV4_SUFFIX ".in-addr.arpa" + +#ifdef PICO_SUPPORT_IPV6 +#define STRLEN_PTR_IP6 63 +#define PICO_ARPA_IPV6_SUFFIX ".IP6.ARPA" +#endif + +#define PICO_DNS_NAMEBUF_SIZE (256) + +enum pico_dns_arpa +{ + PICO_DNS_ARPA4, + PICO_DNS_ARPA6, + PICO_DNS_NO_ARPA, +}; + +/* flags split in 2x uint8 due to endianness */ +PACKED_STRUCT_DEF pico_dns_header +{ + uint16_t id; /* Packet id */ + uint8_t rd : 1; /* Recursion Desired */ + uint8_t tc : 1; /* TrunCation */ + uint8_t aa : 1; /* Authoritative Answer */ + uint8_t opcode : 4; /* Opcode */ + uint8_t qr : 1; /* Query/Response */ + uint8_t rcode : 4; /* Response code */ + uint8_t z : 3; /* Zero */ + uint8_t ra : 1; /* Recursion Available */ + uint16_t qdcount; /* Question count */ + uint16_t ancount; /* Answer count */ + uint16_t nscount; /* Authority count */ + uint16_t arcount; /* Additional count */ +}; +typedef struct pico_dns_header pico_dns_packet; + +/* Question fixed-sized fields */ +PACKED_STRUCT_DEF pico_dns_question_suffix +{ + uint16_t qtype; + uint16_t qclass; +}; + +/* Resource record fixed-sized fields */ +PACKED_STRUCT_DEF pico_dns_record_suffix +{ + uint16_t rtype; + uint16_t rclass; + uint32_t rttl; + uint16_t rdlength; +}; + +/* DNS QUESTION */ +struct pico_dns_question +{ + char *qname; + struct pico_dns_question_suffix *qsuffix; + uint16_t qname_length; + uint8_t proto; +}; + +/* DNS RECORD */ +struct pico_dns_record +{ + char *rname; + struct pico_dns_record_suffix *rsuffix; + uint8_t *rdata; + uint16_t rname_length; +}; + +/* MARK: v NAME & IP FUNCTIONS */ + +/* **************************************************************************** + * Checks if the DNS name doesn't exceed 256 bytes including zero-byte. + * + * @param namelen Length of the DNS name-string including zero-byte + * @return 0 when the length is correct + * ****************************************************************************/ +int +pico_dns_check_namelen( uint16_t namelen ); + +/* **************************************************************************** + * Returns the length of a name in a DNS-packet as if DNS name compression + * would be applied to the packet. If there's no compression present this + * returns the strlen. If there's compression present this returns the length + * until the compression-pointer + 1. + * + * @param name Compressed name you want the calculate the strlen from + * @return Returns strlen of a compressed name, takes the first byte of compr- + * ession pointer into account but not the second byte, which acts + * like a trailing zero-byte. + * ****************************************************************************/ +uint16_t +pico_dns_namelen_comp( char *name ); + +/* **************************************************************************** + * Returns the uncompressed name in DNS name format when DNS name compression + * is applied to the packet-buffer. + * + * @param name Compressed name, should be in the bounds of the actual packet + * @param packet Packet that contains the compressed name + * @return Returns the decompressed name, NULL on failure. + * ****************************************************************************/ +char * +pico_dns_decompress_name( char *name, pico_dns_packet *packet ); + +/* **************************************************************************** + * Converts a DNS name in DNS name format to a name in URL format. Provides + * space for the name in URL format as well. PICO_FREE() should be called on + * the returned string buffer that contains the name in URL format. + * + * @param qname DNS name in DNS name format to convert + * @return Returns a pointer to a string-buffer with the URL name on success. + * ****************************************************************************/ +char * +pico_dns_qname_to_url( const char *qname ); + +/* **************************************************************************** + * Converts a DNS name in URL format to name in DNS name format. Provides + * space for the DNS name as well. PICO_FREE() should be called on the returned + * string buffer that contains the DNS name. + * + * @param url DNS name in URL format to convert + * @return Returns a pointer to a string-buffer with the DNS name on success. + * ****************************************************************************/ +char * +pico_dns_url_to_qname( const char *url ); + +/* **************************************************************************** + * @param url String-buffer + * @return Length of string-buffer in an uint16_t + * ****************************************************************************/ +uint16_t +pico_dns_strlen( const char *url ); + +/* **************************************************************************** + * Replaces .'s in a DNS name in URL format by the label lengths. So it + * actually converts a name in URL format to a name in DNS name format. + * f.e. "*www.google.be" => "3www6google2be0" + * + * @param url Location to buffer with name in URL format. The URL needs to + * be +1 byte offset in the actual buffer. Size is should be + * strlen(url) + 2. + * @param maxlen Maximum length of buffer so it doesn't cause a buffer overflow + * @return 0 on success, something else on failure. + * ****************************************************************************/ +int pico_dns_name_to_dns_notation( char *url, unsigned int maxlen ); + +/* **************************************************************************** + * Replaces the label lengths in a DNS-name by .'s. So it actually converts a + * name in DNS format to a name in URL format. + * f.e. 3www6google2be0 => .www.google.be + * + * @param ptr Location to buffer with name in DNS name format + * @param maxlen Maximum length of buffer so it doesn't cause a buffer overflow + * @return 0 on success, something else on failure. + * ****************************************************************************/ +int pico_dns_notation_to_name( char *ptr, unsigned int maxlen ); + +/* **************************************************************************** + * Determines the length of the first label of a DNS name in URL-format + * + * @param url DNS name in URL-format + * @return Length of the first label of DNS name in URL-format + * ****************************************************************************/ +uint16_t +pico_dns_first_label_length( const char *url ); + +/* **************************************************************************** + * Mirrors a dotted IPv4-address string. + * f.e. 192.168.0.1 => 1.0.168.192 + * + * @param ptr + * @return 0 on success, something else on failure. + * ****************************************************************************/ +int +pico_dns_mirror_addr( char *ptr ); + +/* **************************************************************************** + * Convert an IPv6-address in string-format to a IPv6-address in nibble-format. + * Doesn't add a IPv6 ARPA-suffix though. + * + * @param ip IPv6-address stored as a string + * @param dst Destination to store IPv6-address in nibble-format + * ****************************************************************************/ +void +pico_dns_ipv6_set_ptr( const char *ip, char *dst ); + +/* MARK: QUESTION FUNCTIONS */ + +/* **************************************************************************** + * Deletes a single DNS Question. + * + * @param question Void-pointer to DNS Question. Can be used with pico_tree_- + * destroy. + * @return Returns 0 on success, something else on failure. + * ****************************************************************************/ +int +pico_dns_question_delete( void **question); + +/* **************************************************************************** + * Fills in the DNS question suffix-fields with the correct values. + * + * todo: Update pico_dns_client to make the same mechanism possible as with + * filling DNS Resource Record-suffixes. This function shouldn't be an + * API-function. + * + * @param suf Pointer to the suffix member of the DNS question. + * @param qtype DNS type of the DNS question to be. + * @param qclass DNS class of the DNS question to be. + * @return Returns 0 on success, something else on failure. + * ****************************************************************************/ +int +pico_dns_question_fill_suffix( struct pico_dns_question_suffix *suf, + uint16_t qtype, + uint16_t qclass ); + +/* **************************************************************************** + * Creates a standalone DNS Question with a given name and type. + * + * @param url DNS question name in URL format. Will be converted to DNS + * name notation format. + * @param len Will be filled with the total length of the DNS question. + * @param proto Protocol for which you want to create a question. Can be + * either PICO_PROTO_IPV4 or PICO_PROTO_IPV6. + * @param qtype DNS type of the question to be. + * @param qclass DNS class of the question to be. + * @param reverse When this is true, a reverse resolution name will be gene- + * from the URL + * @return Returns pointer to the created DNS Question on success, NULL on + * failure. + * ****************************************************************************/ +struct pico_dns_question * +pico_dns_question_create( const char *url, + uint16_t *len, + uint8_t proto, + uint16_t qtype, + uint16_t qclass, + uint8_t reverse ); + +/* **************************************************************************** + * Decompresses the name of a single DNS question. + * + * @param question Question you want to decompress the name of + * @param packet Packet in which the DNS question is contained. + * @return Pointer to original name of the DNS question before decompressing. + * ****************************************************************************/ +char * +pico_dns_question_decompress( struct pico_dns_question *question, + pico_dns_packet *packet ); + +/* MARK: RESOURCE RECORD FUNCTIONS */ + +/* **************************************************************************** + * Deletes a single DNS resource record. + * + * @param record Void-pointer to DNS record. Can be used with pico_tree_destroy + * @return Returns 0 on success, something else on failure. + * ****************************************************************************/ +int +pico_dns_record_delete( void **record ); + +/* **************************************************************************** + * Just makes a hardcopy from a single DNS Resource Record + * + * @param record DNS record you want to copy + * @return Pointer to copy of DNS record. + * ****************************************************************************/ +struct pico_dns_record * +pico_dns_record_copy( struct pico_dns_record *record ); + +/* **************************************************************************** + * Create a standalone DNS Resource Record with given name, type and data. + * + * @param url DNS rrecord name in URL format. Will be converted to DNS + * name notation format. + * @param _rdata Memory buffer with data to insert in the resource record. If + * data of record should contain a DNS name, the name in the + * databuffer needs to be in URL-format. + * @param datalen The exact length in bytes of the _rdata-buffer. If data of + * record should contain a DNS name, datalen needs to be + * pico_dns_strlen(_rdata). + * @param len Will be filled with the total length of the DNS rrecord. + * @param rtype DNS type of the resource record to be. + * @param rclass DNS class of the resource record to be. + * @param rttl DNS ttl of the resource record to be. + * @return Returns pointer to the created DNS Resource Record + * ****************************************************************************/ +struct pico_dns_record * +pico_dns_record_create( const char *url, + void *_rdata, + uint16_t datalen, + uint16_t *len, + uint16_t rtype, + uint16_t rclass, + uint32_t rttl ); + +/* **************************************************************************** + * Decompresses the name of single DNS record. + * + * @param record DNS record to decompress the name of. + * @param packet Packet in which is DNS record is present + * @return Pointer to original name of the DNS record before decompressing. + * ****************************************************************************/ +char * +pico_dns_record_decompress( struct pico_dns_record *record, + pico_dns_packet *packet ); + +/* MARK: COMPARING */ + +/* **************************************************************************** + * Compares two databuffers against each other. + * + * @param a 1st Memory buffer to compare + * @param b 2nd Memory buffer to compare + * @param rdlength_a Length of 1st memory buffer + * @param rdlength_b Length of 2nd memory buffer + * @return 0 when the buffers are equal, returns difference when they're not. + * ****************************************************************************/ +int +pico_dns_rdata_cmp( uint8_t *a, uint8_t *b, + uint16_t rdlength_a, uint16_t rdlength_b ); + +/* **************************************************************************** + * Compares 2 DNS questions + * + * @param qa DNS question A as a void-pointer (for pico_tree) + * @param qb DNS question A as a void-pointer (for pico_tree) + * @return 0 when questions are equal, returns difference when they're not. + * ****************************************************************************/ +int +pico_dns_question_cmp( void *qa, + void *qb ); + +/* **************************************************************************** + * Compares 2 DNS records by type and name only + * + * @param ra DNS record A as a void-pointer (for pico_tree) + * @param rb DNS record B as a void-pointer (for pico_tree) + * @return 0 when name and type of records are equal, returns difference when + * they're not. + * ****************************************************************************/ +int +pico_dns_record_cmp_name_type( void *ra, + void *rb ); + +/* **************************************************************************** + * Compares 2 DNS records by type, name AND rdata for a truly unique result + * + * @param ra DNS record A as a void-pointer (for pico_tree) + * @param rb DNS record B as a void-pointer (for pico_tree) + * @return 0 when records are equal, returns difference when they're not + * ****************************************************************************/ +int +pico_dns_record_cmp( void *ra, + void *rb ); + +/* MARK: PICO_TREE */ + +/* **************************************************************************** + * Erases a pico_tree entirely. + * + * @param tree Pointer to a pico_tree-instance + * @param node_delete Helper-function for type-specific deleting. + * @return Returns 0 on success, something else on failure. + * ****************************************************************************/ +int +pico_tree_destroy( struct pico_tree *tree, int (*node_delete)(void **)); + +/* **************************************************************************** + * Determines the amount of nodes in a pico_tree + * + * @param tree Pointer to pico_tree-instance + * @return Amount of items in the tree. + * ****************************************************************************/ +uint16_t +pico_tree_count( struct pico_tree *tree ); + +/* **************************************************************************** + * Definition of DNS question tree + * ****************************************************************************/ +typedef struct pico_tree pico_dns_qtree; +#define PICO_DNS_QTREE_DECLARE(name) \ + pico_dns_qtree (name) = {&LEAF, pico_dns_question_cmp} +#define PICO_DNS_QTREE_DESTROY(qtree) \ + pico_tree_destroy(qtree, pico_dns_question_delete) + +/* **************************************************************************** + * Deletes all the questions with given DNS name from a pico_tree + * + * @param qtree Pointer to pico_tree-instance which contains DNS questions + * @param name Name of the questions you want to delete + * @return Returns 0 on success, something else on failure. + * ****************************************************************************/ +int +pico_dns_qtree_del_name( struct pico_tree *qtree, + const char *name ); + +/* **************************************************************************** + * Checks whether a question with given name is in the tree or not. + * + * @param qtree Pointer to pico_tree-instance which contains DNS questions + * @param name Name you want to check for + * @return 1 when the name is present in the qtree, 0 when it's not. + * ****************************************************************************/ +int +pico_dns_qtree_find_name( struct pico_tree *qtree, + const char *name ); + +/* **************************************************************************** + * Definition of DNS record tree + * ****************************************************************************/ +typedef struct pico_tree pico_dns_rtree; +#define PICO_DNS_RTREE_DECLARE(name) \ + pico_dns_rtree (name) = {&LEAF, pico_dns_record_cmp} +#define PICO_DNS_RTREE_DESTROY(rtree) \ + pico_tree_destroy((rtree), pico_dns_record_delete) + +/* MARK: DNS PACKET FUNCTIONS */ + +/* **************************************************************************** + * Fills the header section of a DNS packet with the correct flags and section + * -counts. + * + * @param hdr Header to fill in. + * @param qdcount Amount of questions added to the packet + * @param ancount Amount of answer records added to the packet + * @param nscount Amount of authority records added to the packet + * @param arcount Amount of additional records added to the packet + * ****************************************************************************/ +void +pico_dns_fill_packet_header( struct pico_dns_header *hdr, + uint16_t qdcount, + uint16_t ancount, + uint16_t authcount, + uint16_t addcount ); + +/* **************************************************************************** + * Creates a DNS Query packet with given question and resource records to put + * the Resource Record Sections. If a NULL-pointer is provided for a certain + * tree, no records will be added to that particular section of the packet. + * + * @param qtree DNS Questions to put in the Question Section + * @param antree DNS Records to put in the Answer Section + * @param nstree DNS Records to put in the Authority Section + * @param artree DNS Records to put in the Additional Section + * @param len Will get filled with the entire size of the packet + * @return Pointer to created DNS packet + * ****************************************************************************/ +pico_dns_packet * +pico_dns_query_create( struct pico_tree *qtree, + struct pico_tree *antree, + struct pico_tree *nstree, + struct pico_tree *artree, + uint16_t *len ); + +/* **************************************************************************** + * Creates a DNS Answer packet with given resource records to put in the + * Resource Record Sections. If a NULL-pointer is provided for a certain tree, + * no records will be added to that particular section of the packet. + * + * @param antree DNS Records to put in the Answer Section + * @param nstree DNS Records to put in the Authority Section + * @param artree DNS Records to put in the Additional Section + * @param len Will get filled with the entire size of the packet + * @return Pointer to created DNS packet. + * ****************************************************************************/ +pico_dns_packet * +pico_dns_answer_create( struct pico_tree *antree, + struct pico_tree *nstree, + struct pico_tree *artree, + uint16_t *len ); + +#endif /* _INCLUDE_PICO_DNS_COMMON */ diff --git a/net/picotcp/modules/pico_dns_sd.h b/net/picotcp/modules/pico_dns_sd.h new file mode 100644 index 0000000..77f8ef2 --- /dev/null +++ b/net/picotcp/modules/pico_dns_sd.h @@ -0,0 +1,90 @@ +/* **************************************************************************** + * PicoTCP. Copyright (c) 2014 TASS Belgium NV. Some rights reserved. + * See LICENSE and COPYING for usage. + * . + * Author: Jelle De Vleeschouwer + * ****************************************************************************/ +#ifndef INCLUDE_PICO_DNS_SD +#define INCLUDE_PICO_DNS_SD + +#include "pico_mdns.h" + +typedef struct +{ + char *key; + char *value; +} key_value_pair_t; + +typedef struct +{ + key_value_pair_t **pairs; + uint16_t count; +} kv_vector; + +#define PICO_DNS_SD_KV_VECTOR_DECLARE(name) \ + kv_vector (name) = {0} + +/* **************************************************************************** + * Just calls pico_mdns_init in it's turn to initialise the mDNS-module. + * See pico_mdns.h for description. + * ****************************************************************************/ +int +pico_dns_sd_init( const char *_hostname, + struct pico_ip4 address, + void (*callback)(pico_mdns_rtree *, + char *, + void *), + void *arg ); + +/* **************************************************************************** + * Register a DNS-SD service via Multicast DNS on the local network. + * + * @param name Instance Name of the service, f.e. "Printer 2nd Floor". + * @param type ServiceType of the service, f.e. "_http._tcp". + * @param port Port number on which the service runs. + * @param txt_data TXT data to create TXT record with, need kv_vector-type, + * Declare such a type with PICO_DNS_SD_KV_VECTOR_DECLARE(*) & + * add key-value pairs with pico_dns_sd_kv_vector_add(). + * @param ttl TTL + * @param callback Callback-function to call when the service is registered. + * @return + * ****************************************************************************/ +int +pico_dns_sd_register_service( const char *name, + const char *type, + uint16_t port, + kv_vector *txt_data, + uint16_t ttl, + void (*callback)(pico_mdns_rtree *, + char *, + void *), + void *arg); + +/* **************************************************************************** + * Does nothing for now. + * + * @param type Type to browse for. + * @param callback Callback to call when something particular happens. + * @return When the module successfully started browsing the servicetype. + * ****************************************************************************/ +int +pico_dns_sd_browse_service( const char *type, + void (*callback)(pico_mdns_rtree *, + char *, + void *), + void *arg ); + +/* **************************************************************************** + * Add a key-value pair the a key-value pair vector. + * + * @param vector Vector to add the pair to. + * @param key Key of the pair, cannot be NULL. + * @param value Value of the pair, can be NULL, empty ("") or filled ("qkejq") + * @return Returns 0 when the pair is added successfully, something else on + * failure. + * ****************************************************************************/ +int +pico_dns_sd_kv_vector_add( kv_vector *vector, char *key, char *value ); + + +#endif /* _INCLUDE_PICO_DNS_SD */ \ No newline at end of file diff --git a/net/picotcp/modules/pico_fragments.c b/net/picotcp/modules/pico_fragments.c new file mode 100644 index 0000000..e6fe44b --- /dev/null +++ b/net/picotcp/modules/pico_fragments.c @@ -0,0 +1,345 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012 TASS Belgium NV. Some rights reserved. + See LICENSE and COPYING for usage. + + Authors: Laurens Miers, Daniele Lacamera + *********************************************************************/ + + +#include "pico_config.h" +#ifdef PICO_SUPPORT_IPV6 +#include "pico_ipv6.h" +#include "pico_icmp6.h" +#endif +#ifdef PICO_SUPPORT_IPV4 +#include "pico_ipv4.h" +#include "pico_icmp4.h" +#endif +#include "pico_stack.h" +#include "pico_eth.h" +#include "pico_udp.h" +#include "pico_tcp.h" +#include "pico_socket.h" +#include "pico_device.h" +#include "pico_tree.h" +#include "pico_constants.h" +#include "pico_fragments.h" +#define frag_dbg(...) do {} while(0) + +#define IP6_FRAG_OFF(x) ((x & 0xFFF8u)) +#define IP6_FRAG_MORE(x) ((x & 0x0001)) +#define IP6_FRAG_ID(x) ((uint32_t)((x->ext.frag.id[0] << 24) + (x->ext.frag.id[1] << 16) + \ + (x->ext.frag.id[2] << 8) + x->ext.frag.id[3])) + +#define IP4_FRAG_OFF(frag) (((uint32_t)frag & PICO_IPV4_FRAG_MASK) << 3ul) +#define IP4_FRAG_MORE(frag) ((frag & PICO_IPV4_MOREFRAG) ? 1 : 0) +#define IP4_FRAG_ID(hdr) (hdr->id) + +#define FRAG_OFF(net, frag) ((net == PICO_PROTO_IPV4) ? (IP4_FRAG_OFF(frag)) : (IP6_FRAG_OFF(frag))) +#define FRAG_MORE(net, frag) ((net == PICO_PROTO_IPV4) ? (IP4_FRAG_MORE(frag)) : (IP6_FRAG_MORE(frag))) + +#define PICO_IPV6_FRAG_TIMEOUT 60000 +#define PICO_IPV4_FRAG_TIMEOUT 15000 + +static int pico_ipv6_frag_compare(void *ka, void *kb); +static int pico_ipv4_frag_compare(void *ka, void *kb); +static void pico_ipv6_fragments_complete(unsigned int len, uint8_t proto); +static void pico_ipv4_fragments_complete(unsigned int len, uint8_t proto); +static void pico_fragments_complete(unsigned int bookmark, uint8_t proto, uint8_t net); +static int pico_fragments_check_complete(uint8_t proto, uint8_t net); +static void pico_frag_expire(pico_time now, void *arg); +static void pico_ipv6_frag_timer_on(void); +static void pico_ipv4_frag_timer_on(void); +static int pico_ipv6_frag_match(struct pico_frame *a, struct pico_frame *b); +static int pico_ipv4_frag_match(struct pico_frame *a, struct pico_frame *b); + +static uint32_t ipv6_cur_frag_id = 0u; +static uint32_t ipv4_cur_frag_id = 0u; + +static int pico_ipv6_frag_compare(void *ka, void *kb) +{ + struct pico_frame *a = ka, *b = kb; + if (IP6_FRAG_OFF(a->frag) > IP6_FRAG_OFF(b->frag)) + return 1; + + if (IP6_FRAG_OFF(a->frag) < IP6_FRAG_OFF(b->frag)) + return -1; + + return 0; +} +PICO_TREE_DECLARE(ipv6_fragments, pico_ipv6_frag_compare); + +struct pico_timer *ipv6_fragments_timer = NULL; + +static int pico_ipv4_frag_compare(void *ka, void *kb) +{ + struct pico_frame *a = ka, *b = kb; + if (IP4_FRAG_OFF(a->frag) > IP4_FRAG_OFF(b->frag)) + return 1; + + if (IP4_FRAG_OFF(a->frag) < IP4_FRAG_OFF(b->frag)) + return -1; + + return 0; +} +PICO_TREE_DECLARE(ipv4_fragments, pico_ipv4_frag_compare); + +struct pico_timer *ipv4_fragments_timer = NULL; + +static void pico_ipv6_fragments_complete(unsigned int len, uint8_t proto) +{ + struct pico_tree_node *index, *tmp; + struct pico_frame *f; + unsigned int bookmark = 0; + struct pico_frame *full = NULL; + struct pico_frame *first = pico_tree_first(&ipv6_fragments); + + full = pico_frame_alloc((uint16_t)(PICO_SIZE_IP6HDR + len)); + if (full) { + full->net_hdr = full->buffer; + full->net_len = PICO_SIZE_IP6HDR; + memcpy(full->net_hdr, first->net_hdr, full->net_len); + full->transport_hdr = full->net_hdr + full->net_len; + full->transport_len = (uint16_t)len; + full->dev = first->dev; + pico_tree_foreach_safe(index, &ipv6_fragments, tmp) { + f = index->keyValue; + memcpy(full->transport_hdr + bookmark, f->transport_hdr, f->transport_len); + bookmark += f->transport_len; + pico_tree_delete(&ipv6_fragments, f); + pico_frame_discard(f); + } + pico_transport_receive(full, proto); + if (ipv6_fragments_timer) { + pico_timer_cancel(ipv6_fragments_timer); + ipv6_fragments_timer = NULL; + } + } +} + +static void pico_ipv4_fragments_complete(unsigned int len, uint8_t proto) +{ + struct pico_tree_node *index, *tmp; + struct pico_frame *f; + unsigned int bookmark = 0; + struct pico_frame *full = NULL; + struct pico_frame *first = pico_tree_first(&ipv4_fragments); + + full = pico_frame_alloc((uint16_t)(PICO_SIZE_IP4HDR + len)); + if (full) { + full->net_hdr = full->buffer; + full->net_len = PICO_SIZE_IP4HDR; + memcpy(full->net_hdr, first->net_hdr, full->net_len); + full->transport_hdr = full->net_hdr + full->net_len; + full->transport_len = (uint16_t)len; + full->dev = first->dev; + pico_tree_foreach_safe(index, &ipv4_fragments, tmp) { + f = index->keyValue; + memcpy(full->transport_hdr + bookmark, f->transport_hdr, f->transport_len); + bookmark += f->transport_len; + pico_tree_delete(&ipv4_fragments, f); + pico_frame_discard(f); + } + pico_transport_receive(full, proto); + if (ipv4_fragments_timer) { + pico_timer_cancel(ipv4_fragments_timer); + ipv4_fragments_timer = NULL; + } + } +} + +static void pico_fragments_complete(unsigned int bookmark, uint8_t proto, uint8_t net) +{ + if (net == PICO_PROTO_IPV4) + { + pico_ipv4_fragments_complete(bookmark, proto); + } + else + { + pico_ipv6_fragments_complete(bookmark, proto); + } +} + +static int pico_fragments_check_complete(uint8_t proto, uint8_t net) +{ + struct pico_tree_node *index, *temp; + struct pico_frame *cur; + unsigned int bookmark = 0; + struct pico_tree *tree = NULL; + + if (net == PICO_PROTO_IPV4) + { + tree = &ipv4_fragments; + } + else + { + tree = &ipv6_fragments; + } + + + pico_tree_foreach_safe(index, tree, temp) { + cur = index->keyValue; + if (FRAG_OFF(net, cur->frag) != bookmark) + return 0; + + bookmark += cur->transport_len; + if (!FRAG_MORE(net, cur->frag)) { + pico_fragments_complete(bookmark, proto, net); + return 1; + } + } + return 0; +} + +static void pico_frag_expire(pico_time now, void *arg) +{ + struct pico_tree_node *index, *tmp; + struct pico_frame *f = NULL; + struct pico_tree *tree = (struct pico_tree *) arg; + struct pico_frame *first = NULL; + uint8_t net = 0; + (void)now; + + if (!tree) + { + frag_dbg("Expired packet but no tree supplied!\n"); + return; + } + + first = pico_tree_first(tree); + + if (!first) { + frag_dbg("not first - not sending notify\n"); + return; + } + + if (IS_IPV4(first)) + { + net = PICO_PROTO_IPV4; + frag_dbg("Packet expired! ID:%hu\n", ipv4_cur_frag_id); + } + else + { + net = PICO_PROTO_IPV6; + frag_dbg("Packet expired! ID:%hu\n", ipv6_cur_frag_id); + } + + /* Empty the tree */ + pico_tree_foreach_safe(index, tree, tmp) { + f = index->keyValue; + pico_tree_delete(tree, f); + if (f != first) + pico_frame_discard(f); /* Later, after ICMP notification...*/ + + } + + if (((FRAG_OFF(net, first->frag) == 0) && (pico_frame_dst_is_unicast(first)))) + { + frag_dbg("sending notify\n"); + pico_notify_frag_expired(first); + } + + if (f) + pico_tree_delete(tree, f); + + pico_frame_discard(first); +} + +static void pico_ipv6_frag_timer_on(void) +{ + ipv6_fragments_timer = pico_timer_add(PICO_IPV6_FRAG_TIMEOUT, pico_frag_expire, &ipv6_fragments); +} + +static void pico_ipv4_frag_timer_on(void) +{ + ipv4_fragments_timer = pico_timer_add( PICO_IPV4_FRAG_TIMEOUT, pico_frag_expire, &ipv4_fragments); +} + + +static int pico_ipv6_frag_match(struct pico_frame *a, struct pico_frame *b) +{ + struct pico_ipv6_hdr *ha, *hb; + if (!a || !b) + return 0; + + ha = (struct pico_ipv6_hdr *)a->net_hdr; + hb = (struct pico_ipv6_hdr *)b->net_hdr; + if (!ha || !hb) + return 0; + + if (memcmp(ha->src.addr, hb->src.addr, PICO_SIZE_IP6) != 0) + return 0; + + if (memcmp(ha->dst.addr, hb->dst.addr, PICO_SIZE_IP6) != 0) + return 0; + + return 1; +} + +static int pico_ipv4_frag_match(struct pico_frame *a, struct pico_frame *b) +{ + struct pico_ipv4_hdr *ha, *hb; + if (!a || !b) + return 0; + + ha = (struct pico_ipv4_hdr *)a->net_hdr; + hb = (struct pico_ipv4_hdr *)b->net_hdr; + if (!ha || !hb) + return 0; + + if (memcmp(&(ha->src.addr), &(hb->src.addr), PICO_SIZE_IP4) != 0) + return 0; + + if (memcmp(&(ha->dst.addr), &(hb->dst.addr), PICO_SIZE_IP4) != 0) + return 0; + + return 1; +} + + +void pico_ipv6_process_frag(struct pico_ipv6_exthdr *frag, struct pico_frame *f, uint8_t proto) +{ + struct pico_frame *first = pico_tree_first(&ipv6_fragments); + + if (!first) { + if (ipv6_cur_frag_id && (IP6_FRAG_ID(frag) == ipv6_cur_frag_id)) { + /* Discard late arrivals, without firing the timer. + */ + frag_dbg("discarded late arrival, exp:%hu found:%hu\n", ipv6_cur_frag_id, IP6_FRAG_ID(frag)); + return; + } + + pico_ipv6_frag_timer_on(); + ipv6_cur_frag_id = IP6_FRAG_ID(frag); + frag_dbg("Started new reassembly, ID:%hu\n", ipv6_cur_frag_id); + } + + if (!first || (pico_ipv6_frag_match(f, first) && (IP6_FRAG_ID(frag) == ipv6_cur_frag_id))) { + pico_tree_insert(&ipv6_fragments, pico_frame_copy(f)); + } + + pico_fragments_check_complete(proto, PICO_PROTO_IPV6); +} + +void pico_ipv4_process_frag(struct pico_ipv4_hdr *hdr, struct pico_frame *f, uint8_t proto) +{ + struct pico_frame *first = pico_tree_first(&ipv4_fragments); + + f->frag = short_be(hdr->frag); + if (!first) { + if (ipv4_cur_frag_id && (IP4_FRAG_ID(hdr) == ipv4_cur_frag_id)) { + /* Discard late arrivals, without firing the timer, + */ + return; + } + + pico_ipv4_frag_timer_on(); + ipv4_cur_frag_id = IP4_FRAG_ID(hdr); + } + + if (!first || (pico_ipv4_frag_match(f, first) && (IP4_FRAG_ID(hdr) == ipv4_cur_frag_id))) { + pico_tree_insert(&ipv4_fragments, pico_frame_copy(f)); + } + + pico_fragments_check_complete(proto, PICO_PROTO_IPV4); +} diff --git a/net/picotcp/modules/pico_fragments.h b/net/picotcp/modules/pico_fragments.h new file mode 100644 index 0000000..e51ec44 --- /dev/null +++ b/net/picotcp/modules/pico_fragments.h @@ -0,0 +1,11 @@ +#ifndef PICO_FRAGMENTS_H +#define PICO_FRAGMENTS_H +#include "pico_ipv4.h" +#include "pico_ipv6.h" +#include "pico_addressing.h" +#include "pico_frame.h" + +void pico_ipv6_process_frag(struct pico_ipv6_exthdr *frag, struct pico_frame *f, uint8_t proto); +void pico_ipv4_process_frag(struct pico_ipv4_hdr *hdr, struct pico_frame *f, uint8_t proto); + +#endif diff --git a/net/picotcp/modules/pico_icmp4.c b/net/picotcp/modules/pico_icmp4.c new file mode 100644 index 0000000..da3d89c --- /dev/null +++ b/net/picotcp/modules/pico_icmp4.c @@ -0,0 +1,395 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + Authors: Daniele Lacamera + *********************************************************************/ + + +#include "pico_icmp4.h" +#include "pico_config.h" +#include "pico_ipv4.h" +#include "pico_eth.h" +#include "pico_device.h" +#include "pico_stack.h" +#include "pico_tree.h" + +/* Queues */ +static struct pico_queue icmp_in = { + 0 +}; +static struct pico_queue icmp_out = { + 0 +}; + + +/* Functions */ + +static int pico_icmp4_checksum(struct pico_frame *f) +{ + struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr; + if (!hdr) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + hdr->crc = 0; + hdr->crc = short_be(pico_checksum(hdr, f->transport_len)); + return 0; +} + +#ifdef PICO_SUPPORT_PING +static void ping_recv_reply(struct pico_frame *f); +#endif + +static int pico_icmp4_process_in(struct pico_protocol *self, struct pico_frame *f) +{ + struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr; + static int firstpkt = 1; + static uint16_t last_id = 0; + static uint16_t last_seq = 0; + IGNORE_PARAMETER(self); + + if (hdr->type == PICO_ICMP_ECHO) { + hdr->type = PICO_ICMP_ECHOREPLY; + /* outgoing frames require a f->len without the ethernet header len */ + if (f->dev && f->dev->eth) + f->len -= PICO_SIZE_ETHHDR; + + if (!firstpkt && (hdr->hun.ih_idseq.idseq_id == last_id) && (last_seq == hdr->hun.ih_idseq.idseq_seq)) { + /* The network duplicated the echo. Do not reply. */ + pico_frame_discard(f); + return 0; + } + + firstpkt = 0; + last_id = hdr->hun.ih_idseq.idseq_id; + last_seq = hdr->hun.ih_idseq.idseq_seq; + pico_icmp4_checksum(f); + pico_ipv4_rebound(f); + } else if (hdr->type == PICO_ICMP_UNREACH) { + f->net_hdr = f->transport_hdr + PICO_ICMPHDR_UN_SIZE; + pico_ipv4_unreachable(f, hdr->code); + } else if (hdr->type == PICO_ICMP_ECHOREPLY) { +#ifdef PICO_SUPPORT_PING + ping_recv_reply(f); +#endif + pico_frame_discard(f); + } else { + pico_frame_discard(f); + } + + return 0; +} + +static int pico_icmp4_process_out(struct pico_protocol *self, struct pico_frame *f) +{ + IGNORE_PARAMETER(self); + IGNORE_PARAMETER(f); + dbg("Called %s\n", __FUNCTION__); + return 0; +} + +/* Interface: protocol definition */ +struct pico_protocol pico_proto_icmp4 = { + .name = "icmp4", + .proto_number = PICO_PROTO_ICMP4, + .layer = PICO_LAYER_TRANSPORT, + .process_in = pico_icmp4_process_in, + .process_out = pico_icmp4_process_out, + .q_in = &icmp_in, + .q_out = &icmp_out, +}; + +static int pico_icmp4_notify(struct pico_frame *f, uint8_t type, uint8_t code) +{ + struct pico_frame *reply; + struct pico_icmp4_hdr *hdr; + struct pico_ipv4_hdr *info; + uint16_t f_tot_len; + + f_tot_len = short_be(((struct pico_ipv4_hdr *)f->net_hdr)->len); + + if (f_tot_len < (sizeof(struct pico_ipv4_hdr))) + return -1; + + /* Truncate tot len to be at most 8 bytes + iphdr */ + if (f_tot_len > (sizeof(struct pico_ipv4_hdr) + 8u)) { + f_tot_len = (sizeof(struct pico_ipv4_hdr) + 8u); + } + + if (f == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + reply = pico_proto_ipv4.alloc(&pico_proto_ipv4, (uint16_t) (f_tot_len + PICO_ICMPHDR_UN_SIZE)); + info = (struct pico_ipv4_hdr*)(f->net_hdr); + hdr = (struct pico_icmp4_hdr *) reply->transport_hdr; + hdr->type = type; + hdr->code = code; + hdr->hun.ih_pmtu.ipm_nmtu = short_be(1500); + hdr->hun.ih_pmtu.ipm_void = 0; + reply->transport_len = (uint16_t)(f_tot_len + PICO_ICMPHDR_UN_SIZE); + reply->payload = reply->transport_hdr + PICO_ICMPHDR_UN_SIZE; + memcpy(reply->payload, f->net_hdr, f_tot_len); + pico_icmp4_checksum(reply); + pico_ipv4_frame_push(reply, &info->src, PICO_PROTO_ICMP4); + return 0; +} + +int pico_icmp4_port_unreachable(struct pico_frame *f) +{ + /*Parameter check executed in pico_icmp4_notify*/ + return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PORT); +} + +int pico_icmp4_proto_unreachable(struct pico_frame *f) +{ + /*Parameter check executed in pico_icmp4_notify*/ + return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PROTOCOL); +} + +int pico_icmp4_dest_unreachable(struct pico_frame *f) +{ + /*Parameter check executed in pico_icmp4_notify*/ + return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_HOST); +} + +int pico_icmp4_ttl_expired(struct pico_frame *f) +{ + /*Parameter check executed in pico_icmp4_notify*/ + return pico_icmp4_notify(f, PICO_ICMP_TIME_EXCEEDED, PICO_ICMP_TIMXCEED_INTRANS); +} + +MOCKABLE int pico_icmp4_frag_expired(struct pico_frame *f) +{ + /*Parameter check executed in pico_icmp4_notify*/ + return pico_icmp4_notify(f, PICO_ICMP_TIME_EXCEEDED, PICO_ICMP_TIMXCEED_REASS); +} + +int pico_icmp4_mtu_exceeded(struct pico_frame *f) +{ + /*Parameter check executed in pico_icmp4_notify*/ + return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_NEEDFRAG); +} + +int pico_icmp4_packet_filtered(struct pico_frame *f) +{ + /*Parameter check executed in pico_icmp4_notify*/ + /*Packet Filtered: type 3, code 13 (Communication Administratively Prohibited)*/ + return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_FILTER_PROHIB); +} + +int pico_icmp4_param_problem(struct pico_frame *f, uint8_t code) +{ + return pico_icmp4_notify(f, PICO_ICMP_PARAMPROB, code); +} + +/***********************/ +/* Ping implementation */ +/***********************/ +/***********************/ +/***********************/ +/***********************/ + + +#ifdef PICO_SUPPORT_PING + + +struct pico_icmp4_ping_cookie +{ + struct pico_ip4 dst; + uint16_t err; + uint16_t id; + uint16_t seq; + uint16_t size; + int count; + pico_time timestamp; + int interval; + int timeout; + void (*cb)(struct pico_icmp4_stats*); +}; + +static int cookie_compare(void *ka, void *kb) +{ + struct pico_icmp4_ping_cookie *a = ka, *b = kb; + if (a->id < b->id) + return -1; + + if (a->id > b->id) + return 1; + + return (a->seq - b->seq); +} + +PICO_TREE_DECLARE(Pings, cookie_compare); + +static int8_t pico_icmp4_send_echo(struct pico_icmp4_ping_cookie *cookie) +{ + struct pico_frame *echo = pico_proto_ipv4.alloc(&pico_proto_ipv4, (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size)); + struct pico_icmp4_hdr *hdr; + if (!echo) { + return -1; + } + + hdr = (struct pico_icmp4_hdr *) echo->transport_hdr; + + hdr->type = PICO_ICMP_ECHO; + hdr->code = 0; + hdr->hun.ih_idseq.idseq_id = short_be(cookie->id); + hdr->hun.ih_idseq.idseq_seq = short_be(cookie->seq); + echo->transport_len = (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size); + echo->payload = echo->transport_hdr + PICO_ICMPHDR_UN_SIZE; + echo->payload_len = cookie->size; + /* XXX: Fill payload */ + pico_icmp4_checksum(echo); + pico_ipv4_frame_push(echo, &cookie->dst, PICO_PROTO_ICMP4); + return 0; +} + + +static void ping_timeout(pico_time now, void *arg) +{ + struct pico_icmp4_ping_cookie *cookie = (struct pico_icmp4_ping_cookie *)arg; + IGNORE_PARAMETER(now); + + if(pico_tree_findKey(&Pings, cookie)) { + if (cookie->err == PICO_PING_ERR_PENDING) { + struct pico_icmp4_stats stats; + stats.dst = cookie->dst; + stats.seq = cookie->seq; + stats.time = 0; + stats.size = cookie->size; + stats.err = PICO_PING_ERR_TIMEOUT; + dbg(" ---- Ping timeout!!!\n"); + cookie->cb(&stats); + } + + pico_tree_delete(&Pings, cookie); + PICO_FREE(cookie); + } +} + +static void next_ping(pico_time now, void *arg); +static inline void send_ping(struct pico_icmp4_ping_cookie *cookie) +{ + pico_icmp4_send_echo(cookie); + cookie->timestamp = pico_tick; + pico_timer_add((uint32_t)cookie->timeout, ping_timeout, cookie); + if (cookie->seq < (uint16_t)cookie->count) + pico_timer_add((uint32_t)cookie->interval, next_ping, cookie); +} + +static void next_ping(pico_time now, void *arg) +{ + struct pico_icmp4_ping_cookie *newcookie, *cookie = (struct pico_icmp4_ping_cookie *)arg; + IGNORE_PARAMETER(now); + + if(pico_tree_findKey(&Pings, cookie)) { + if (cookie->err == PICO_PING_ERR_ABORTED) + return; + + if (cookie->seq < (uint16_t)cookie->count) { + newcookie = PICO_ZALLOC(sizeof(struct pico_icmp4_ping_cookie)); + if (!newcookie) + return; + + memcpy(newcookie, cookie, sizeof(struct pico_icmp4_ping_cookie)); + newcookie->seq++; + + pico_tree_insert(&Pings, newcookie); + send_ping(newcookie); + } + } +} + + +static void ping_recv_reply(struct pico_frame *f) +{ + struct pico_icmp4_ping_cookie test, *cookie; + struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr; + test.id = short_be(hdr->hun.ih_idseq.idseq_id ); + test.seq = short_be(hdr->hun.ih_idseq.idseq_seq); + + cookie = pico_tree_findKey(&Pings, &test); + if (cookie) { + struct pico_icmp4_stats stats; + if (cookie->err == PICO_PING_ERR_ABORTED) + return; + + cookie->err = PICO_PING_ERR_REPLIED; + stats.dst = ((struct pico_ipv4_hdr *)f->net_hdr)->src; + stats.seq = cookie->seq; + stats.size = cookie->size; + stats.time = pico_tick - cookie->timestamp; + stats.err = cookie->err; + stats.ttl = ((struct pico_ipv4_hdr *)f->net_hdr)->ttl; + if(cookie->cb != NULL) + cookie->cb(&stats); + } else { + dbg("Reply for seq=%d, not found.\n", test.seq); + } +} + +int pico_icmp4_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp4_stats *)) +{ + static uint16_t next_id = 0x91c0; + struct pico_icmp4_ping_cookie *cookie; + + if((dst == NULL) || (interval == 0) || (timeout == 0) || (count == 0)) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + cookie = PICO_ZALLOC(sizeof(struct pico_icmp4_ping_cookie)); + if (!cookie) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + if (pico_string_to_ipv4(dst, (uint32_t *)&cookie->dst.addr) < 0) { + pico_err = PICO_ERR_EINVAL; + PICO_FREE(cookie); + return -1; + } + + cookie->seq = 1; + cookie->id = next_id++; + cookie->err = PICO_PING_ERR_PENDING; + cookie->size = (uint16_t)size; + cookie->interval = interval; + cookie->timeout = timeout; + cookie->cb = cb; + cookie->count = count; + + pico_tree_insert(&Pings, cookie); + send_ping(cookie); + + return cookie->id; + +} + +int pico_icmp4_ping_abort(int id) +{ + struct pico_tree_node *node; + int found = 0; + pico_tree_foreach(node, &Pings) + { + struct pico_icmp4_ping_cookie *ck = + (struct pico_icmp4_ping_cookie *) node->keyValue; + if (ck->id == (uint16_t)id) { + ck->err = PICO_PING_ERR_ABORTED; + found++; + } + } + if (found > 0) + return 0; /* OK if at least one pending ping has been canceled */ + + pico_err = PICO_ERR_ENOENT; + return -1; +} + +#endif diff --git a/net/picotcp/modules/pico_icmp4.h b/net/picotcp/modules/pico_icmp4.h new file mode 100644 index 0000000..45f0a62 --- /dev/null +++ b/net/picotcp/modules/pico_icmp4.h @@ -0,0 +1,162 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + *********************************************************************/ +#ifndef INCLUDE_PICO_ICMP4 +#define INCLUDE_PICO_ICMP4 +#include "pico_defines.h" +#include "pico_addressing.h" +#include "pico_protocol.h" + + +extern struct pico_protocol pico_proto_icmp4; + +PACKED_STRUCT_DEF pico_icmp4_hdr { + uint8_t type; + uint8_t code; + uint16_t crc; + + /* hun */ + PACKED_UNION_DEF hun_u { + uint8_t ih_pptr; + struct pico_ip4 ih_gwaddr; + PEDANTIC_STRUCT_DEF ih_idseq_s { + uint16_t idseq_id; + uint16_t idseq_seq; + } ih_idseq; + uint32_t ih_void; + PEDANTIC_STRUCT_DEF ih_pmtu_s { + uint16_t ipm_void; + uint16_t ipm_nmtu; + } ih_pmtu; + PEDANTIC_STRUCT_DEF ih_rta_s { + uint8_t rta_numgw; + uint8_t rta_wpa; + uint16_t rta_lifetime; + } ih_rta; + } hun; + + /* dun */ + PACKED_UNION_DEF dun_u { + PEDANTIC_STRUCT_DEF id_ts_s { + uint32_t ts_otime; + uint32_t ts_rtime; + uint32_t ts_ttime; + } id_ts; + PEDANTIC_STRUCT_DEF id_ip_s { + uint32_t ip_options; + uint32_t ip_data_hi; + uint32_t ip_data_lo; + } id_ip; + PEDANTIC_STRUCT_DEF id_ra_s { + uint32_t ira_addr; + uint32_t ira_pref; + } id_ra; + uint32_t id_mask; + uint8_t id_data[1]; + } dun; +}; + +#define PICO_ICMPHDR_DRY_SIZE 4 +#define PICO_ICMPHDR_UN_SIZE 8u + +#define PICO_ICMP_ECHOREPLY 0 +#define PICO_ICMP_DEST_UNREACH 3 +#define PICO_ICMP_SOURCE_QUENCH 4 +#define PICO_ICMP_REDIRECT 5 +#define PICO_ICMP_ECHO 8 +#define PICO_ICMP_TIME_EXCEEDED 11 +#define PICO_ICMP_PARAMETERPROB 12 +#define PICO_ICMP_TIMESTAMP 13 +#define PICO_ICMP_TIMESTAMPREPLY 14 +#define PICO_ICMP_INFO_REQUEST 15 +#define PICO_ICMP_INFO_REPLY 16 +#define PICO_ICMP_ADDRESS 17 +#define PICO_ICMP_ADDRESSREPLY 18 + + +#define PICO_ICMP_UNREACH 3 +#define PICO_ICMP_SOURCEQUENCH 4 +#define PICO_ICMP_ROUTERADVERT 9 +#define PICO_ICMP_ROUTERSOLICIT 10 +#define PICO_ICMP_TIMXCEED 11 +#define PICO_ICMP_PARAMPROB 12 +#define PICO_ICMP_TSTAMP 13 +#define PICO_ICMP_TSTAMPREPLY 14 +#define PICO_ICMP_IREQ 15 +#define PICO_ICMP_IREQREPLY 16 +#define PICO_ICMP_MASKREQ 17 +#define PICO_ICMP_MASKREPLY 18 + +#define PICO_ICMP_MAXTYPE 18 + + +#define PICO_ICMP_UNREACH_NET 0 +#define PICO_ICMP_UNREACH_HOST 1 +#define PICO_ICMP_UNREACH_PROTOCOL 2 +#define PICO_ICMP_UNREACH_PORT 3 +#define PICO_ICMP_UNREACH_NEEDFRAG 4 +#define PICO_ICMP_UNREACH_SRCFAIL 5 +#define PICO_ICMP_UNREACH_NET_UNKNOWN 6 +#define PICO_ICMP_UNREACH_HOST_UNKNOWN 7 +#define PICO_ICMP_UNREACH_ISOLATED 8 +#define PICO_ICMP_UNREACH_NET_PROHIB 9 +#define PICO_ICMP_UNREACH_HOST_PROHIB 10 +#define PICO_ICMP_UNREACH_TOSNET 11 +#define PICO_ICMP_UNREACH_TOSHOST 12 +#define PICO_ICMP_UNREACH_FILTER_PROHIB 13 +#define PICO_ICMP_UNREACH_HOST_PRECEDENCE 14 +#define PICO_ICMP_UNREACH_PRECEDENCE_CUTOFF 15 + + +#define PICO_ICMP_REDIRECT_NET 0 +#define PICO_ICMP_REDIRECT_HOST 1 +#define PICO_ICMP_REDIRECT_TOSNET 2 +#define PICO_ICMP_REDIRECT_TOSHOST 3 + + +#define PICO_ICMP_TIMXCEED_INTRANS 0 +#define PICO_ICMP_TIMXCEED_REASS 1 + + +#define PICO_ICMP_PARAMPROB_OPTABSENT 1 + +#define PICO_SIZE_ICMP4HDR ((sizeof(struct pico_icmp4_hdr))) + +struct pico_icmp4_stats +{ + struct pico_ip4 dst; + unsigned long size; + unsigned long seq; + pico_time time; + unsigned long ttl; + int err; +}; + +int pico_icmp4_port_unreachable(struct pico_frame *f); +int pico_icmp4_proto_unreachable(struct pico_frame *f); +int pico_icmp4_dest_unreachable(struct pico_frame *f); +int pico_icmp4_mtu_exceeded(struct pico_frame *f); +int pico_icmp4_ttl_expired(struct pico_frame *f); +int pico_icmp4_frag_expired(struct pico_frame *f); +int pico_icmp4_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp4_stats *)); +int pico_icmp4_ping_abort(int id); + +#ifdef PICO_SUPPORT_ICMP4 +int pico_icmp4_packet_filtered(struct pico_frame *f); +int pico_icmp4_param_problem(struct pico_frame *f, uint8_t code); +#else +# define pico_icmp4_packet_filtered(f) (-1) +# define pico_icmp4_param_problem(f, c) (-1) +#endif /* PICO_SUPPORT_ICMP4 */ + +#define PICO_PING_ERR_REPLIED 0 +#define PICO_PING_ERR_TIMEOUT 1 +#define PICO_PING_ERR_UNREACH 2 +#define PICO_PING_ERR_ABORTED 3 +#define PICO_PING_ERR_PENDING 0xFFFF + +#endif diff --git a/net/picotcp/modules/pico_icmp6.h b/net/picotcp/modules/pico_icmp6.h new file mode 100644 index 0000000..90e8223 --- /dev/null +++ b/net/picotcp/modules/pico_icmp6.h @@ -0,0 +1,259 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + *********************************************************************/ +#ifndef _INCLUDE_PICO_ICMP6 +#define _INCLUDE_PICO_ICMP6 +#include "pico_addressing.h" +#include "pico_protocol.h" + +/* ICMP header sizes */ +#define PICO_ICMP6HDR_DRY_SIZE 4 +#define PICO_ICMP6HDR_ECHO_REQUEST_SIZE 8 +#define PICO_ICMP6HDR_DEST_UNREACH_SIZE 8 +#define PICO_ICMP6HDR_TIME_XCEEDED_SIZE 8 +#define PICO_ICMP6HDR_PARAM_PROBLEM_SIZE 8 +#define PICO_ICMP6HDR_NEIGH_SOL_SIZE 24 +#define PICO_ICMP6HDR_NEIGH_ADV_SIZE 24 +#define PICO_ICMP6HDR_ROUTER_SOL_SIZE 8 +#define PICO_ICMP6HDR_ROUTER_ADV_SIZE 16 +#define PICO_ICMP6HDR_REDIRECT_SIZE 40 + +/* ICMP types */ +#define PICO_ICMP6_DEST_UNREACH 1 +#define PICO_ICMP6_PKT_TOO_BIG 2 +#define PICO_ICMP6_TIME_EXCEEDED 3 +#define PICO_ICMP6_PARAM_PROBLEM 4 +#define PICO_ICMP6_ECHO_REQUEST 128 +#define PICO_ICMP6_ECHO_REPLY 129 +#define PICO_ICMP6_ROUTER_SOL 133 +#define PICO_ICMP6_ROUTER_ADV 134 +#define PICO_ICMP6_NEIGH_SOL 135 +#define PICO_ICMP6_NEIGH_ADV 136 +#define PICO_ICMP6_REDIRECT 137 + +/* destination unreachable codes */ +#define PICO_ICMP6_UNREACH_NOROUTE 0 +#define PICO_ICMP6_UNREACH_ADMIN 1 +#define PICO_ICMP6_UNREACH_SRCSCOPE 2 +#define PICO_ICMP6_UNREACH_ADDR 3 +#define PICO_ICMP6_UNREACH_PORT 4 +#define PICO_ICMP6_UNREACH_SRCFILTER 5 +#define PICO_ICMP6_UNREACH_REJROUTE 6 + +/* time exceeded codes */ +#define PICO_ICMP6_TIMXCEED_INTRANS 0 +#define PICO_ICMP6_TIMXCEED_REASS 1 + +/* parameter problem codes */ +#define PICO_ICMP6_PARAMPROB_HDRFIELD 0 +#define PICO_ICMP6_PARAMPROB_NXTHDR 1 +#define PICO_ICMP6_PARAMPROB_IPV6OPT 2 + +/* ping error codes */ +#define PICO_PING6_ERR_REPLIED 0 +#define PICO_PING6_ERR_TIMEOUT 1 +#define PICO_PING6_ERR_UNREACH 2 +#define PICO_PING6_ERR_ABORTED 3 +#define PICO_PING6_ERR_PENDING 0xFFFF + +/* ND configuration */ +#define PICO_ND_MAX_FRAMES_QUEUED 4 /* max frames queued while awaiting address resolution */ + +/* ND RFC constants */ +#define PICO_ND_MAX_SOLICIT 3 +#define PICO_ND_MAX_NEIGHBOR_ADVERT 3 +#define PICO_ND_DELAY_INCOMPLETE 1000 /* msec */ +#define PICO_ND_DELAY_FIRST_PROBE_TIME 5000 /* msec */ + +/* neighbor discovery options */ +#define PICO_ND_OPT_LLADDR_SRC 1 +#define PICO_ND_OPT_LLADDR_TGT 2 +#define PICO_ND_OPT_PREFIX 3 +#define PICO_ND_OPT_REDIRECT 4 +#define PICO_ND_OPT_MTU 5 +#define PICO_ND_OPT_RDNSS 25 /* RFC 5006 */ + +/* ND advertisement flags */ +#define PICO_ND_ROUTER 0x80000000 +#define PICO_ND_SOLICITED 0x40000000 +#define PICO_ND_OVERRIDE 0x20000000 +#define IS_ROUTER(x) (long_be(x->msg.info.neigh_adv.rsor) & (PICO_ND_ROUTER)) /* router flag set? */ +#define IS_SOLICITED(x) (long_be(x->msg.info.neigh_adv.rsor) & (PICO_ND_SOLICITED)) /* solicited flag set? */ +#define IS_OVERRIDE(x) (long_be(x->msg.info.neigh_adv.rsor) & (PICO_ND_OVERRIDE)) /* override flag set? */ + +#define PICO_ND_PREFIX_LIFETIME_INF 0xFFFFFFFFu +/* #define PICO_ND_DESTINATION_LRU_TIME 600000u / * msecs (10min) * / */ + +/* custom defines */ +#define PICO_ICMP6_ND_UNICAST 0 +#define PICO_ICMP6_ND_ANYCAST 1 +#define PICO_ICMP6_ND_SOLICITED 2 +#define PICO_ICMP6_ND_DAD 3 + +#define PICO_ICMP6_MAX_RTR_SOL_DELAY 1000 + +#define PICO_SIZE_ICMP6HDR ((sizeof(struct pico_icmp6_hdr))) +#define PICO_ICMP6_OPT_LLADDR_SIZE (8) + +extern struct pico_protocol pico_proto_icmp6; + +PACKED_STRUCT_DEF pico_icmp6_hdr { + uint8_t type; + uint8_t code; + uint16_t crc; + + PACKED_UNION_DEF icmp6_msg_u { + /* error messages */ + PACKED_UNION_DEF icmp6_err_u { + PEDANTIC_STRUCT_DEF dest_unreach_s { + uint32_t unused; + uint8_t data[0]; + } dest_unreach; + PEDANTIC_STRUCT_DEF pkt_too_big_s { + uint32_t mtu; + uint8_t data[0]; + } pkt_too_big; + PEDANTIC_STRUCT_DEF time_exceeded_s { + uint32_t unused; + uint8_t data[0]; + } time_exceeded; + PEDANTIC_STRUCT_DEF param_problem_s { + uint32_t ptr; + uint8_t data[0]; + } param_problem; + } err; + + /* informational messages */ + PACKED_UNION_DEF icmp6_info_u { + PEDANTIC_STRUCT_DEF echo_request_s { + uint16_t id; + uint16_t seq; + uint8_t data[0]; + } echo_request; + PEDANTIC_STRUCT_DEF echo_reply_s { + uint16_t id; + uint16_t seq; + uint8_t data[0]; + } echo_reply; + PEDANTIC_STRUCT_DEF router_sol_s { + uint32_t unused; + uint8_t options[0]; + } router_sol; + PEDANTIC_STRUCT_DEF router_adv_s { + uint8_t hop; + uint8_t mor; + uint16_t life_time; + uint32_t reachable_time; + uint32_t retrans_time; + uint8_t options[0]; + } router_adv; + PEDANTIC_STRUCT_DEF neigh_sol_s { + uint32_t unused; + struct pico_ip6 target; + uint8_t options[0]; + } neigh_sol; + PEDANTIC_STRUCT_DEF neigh_adv_s { + uint32_t rsor; + struct pico_ip6 target; + uint8_t options[0]; + } neigh_adv; + PEDANTIC_STRUCT_DEF redirect_s { + uint32_t reserved; + struct pico_ip6 target; + struct pico_ip6 dest; + uint8_t options[0]; + } redirect; + } info; + } msg; +}; + +PACKED_STRUCT_DEF pico_icmp6_opt_lladdr +{ + uint8_t type; + uint8_t len; + PACKED_UNION_DEF icmp6_opt_hw_addr_u { + struct pico_eth mac; + } addr; +}; + +PACKED_STRUCT_DEF pico_icmp6_opt_prefix +{ + uint8_t type; + uint8_t len; + uint8_t prefix_len; + uint8_t res : 6; + uint8_t aac : 1; + uint8_t onlink : 1; + uint32_t val_lifetime; + uint32_t pref_lifetime; + uint32_t reserved; + struct pico_ip6 prefix; +}; + +PACKED_STRUCT_DEF pico_icmp6_opt_mtu +{ + uint8_t type; + uint8_t len; + uint16_t res; + uint32_t mtu; +}; + +PACKED_STRUCT_DEF pico_icmp6_opt_redirect +{ + uint8_t type; + uint8_t len; + uint16_t res0; + uint32_t res1; + uint8_t data[0]; +}; + +PACKED_STRUCT_DEF pico_icmp6_opt_rdnss +{ + uint8_t type; + uint8_t len; + uint16_t res0; + uint32_t lifetime; + struct pico_ip6 addr[]; +}; + +PACKED_STRUCT_DEF pico_icmp6_opt_na +{ + uint8_t type; + uint8_t len; + uint8_t options[0]; +}; + +struct pico_icmp6_stats +{ + unsigned long size; + unsigned long seq; + pico_time time; + unsigned long ttl; + int err; + struct pico_ip6 dst; +}; + +int pico_icmp6_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp6_stats *), struct pico_device *dev); +int pico_icmp6_ping_abort(int id); + +int pico_icmp6_neighbor_solicitation(struct pico_device *dev, struct pico_ip6 *dst, uint8_t type); +int pico_icmp6_neighbor_advertisement(struct pico_frame *f, struct pico_ip6 *target); +int pico_icmp6_router_solicitation(struct pico_device *dev, struct pico_ip6 *src); + +int pico_icmp6_port_unreachable(struct pico_frame *f); +int pico_icmp6_proto_unreachable(struct pico_frame *f); +int pico_icmp6_dest_unreachable(struct pico_frame *f); +int pico_icmp6_ttl_expired(struct pico_frame *f); +int pico_icmp6_packet_filtered(struct pico_frame *f); +int pico_icmp6_parameter_problem(struct pico_frame *f, uint8_t problem, uint32_t ptr); +int pico_icmp6_pkt_too_big(struct pico_frame *f); +int pico_icmp6_frag_expired(struct pico_frame *f); + +uint16_t pico_icmp6_checksum(struct pico_frame *f); +int pico_icmp6_router_advertisement(struct pico_device *dev, struct pico_ip6 *dst); + +#endif diff --git a/net/picotcp/modules/pico_igmp.h b/net/picotcp/modules/pico_igmp.h new file mode 100644 index 0000000..03d6102 --- /dev/null +++ b/net/picotcp/modules/pico_igmp.h @@ -0,0 +1,26 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + Authors: Kristof Roelants, Simon Maes, Brecht Van Cauwenberghe + *********************************************************************/ + +#ifndef INCLUDE_PICO_IGMP +#define INCLUDE_PICO_IGMP + +#define PICO_IGMPV1 1 +#define PICO_IGMPV2 2 +#define PICO_IGMPV3 3 + +#define PICO_IGMP_STATE_CREATE 1 +#define PICO_IGMP_STATE_UPDATE 2 +#define PICO_IGMP_STATE_DELETE 3 + +#define PICO_IGMP_QUERY_INTERVAL 125 + +extern struct pico_protocol pico_proto_igmp; + +int pico_igmp_state_change(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t filter_mode, struct pico_tree *MCASTFilter, uint8_t state); +#endif /* _INCLUDE_PICO_IGMP */ diff --git a/net/picotcp/modules/pico_ipfilter.h b/net/picotcp/modules/pico_ipfilter.h new file mode 100644 index 0000000..fb92e67 --- /dev/null +++ b/net/picotcp/modules/pico_ipfilter.h @@ -0,0 +1,29 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + Authors: Simon Maes + *********************************************************************/ +#ifndef INCLUDE_PICO_IPFILTER +#define INCLUDE_PICO_IPFILTER + +#include "pico_device.h" + +enum filter_action { + FILTER_PRIORITY = 0, + FILTER_REJECT, + FILTER_DROP, + FILTER_COUNT +}; + +uint32_t pico_ipv4_filter_add(struct pico_device *dev, uint8_t proto, + struct pico_ip4 *out_addr, struct pico_ip4 *out_addr_netmask, struct pico_ip4 *in_addr, + struct pico_ip4 *in_addr_netmask, uint16_t out_port, uint16_t in_port, + int8_t priority, uint8_t tos, enum filter_action action); + +int pico_ipv4_filter_del(uint32_t filter_id); + +int ipfilter(struct pico_frame *f); + +#endif /* _INCLUDE_PICO_IPFILTER */ + diff --git a/net/picotcp/modules/pico_ipv4.c b/net/picotcp/modules/pico_ipv4.c new file mode 100644 index 0000000..f94d6e6 --- /dev/null +++ b/net/picotcp/modules/pico_ipv4.c @@ -0,0 +1,1574 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + Authors: Daniele Lacamera, Markian Yskout + *********************************************************************/ + + +#include "pico_config.h" +#include "pico_ipfilter.h" +#include "pico_ipv4.h" +#include "pico_icmp4.h" +#include "pico_stack.h" +#include "pico_eth.h" +#include "pico_udp.h" +#include "pico_tcp.h" +#include "pico_socket.h" +#include "pico_device.h" +#include "pico_nat.h" +#include "pico_igmp.h" +#include "pico_tree.h" +#include "pico_aodv.h" +#include "pico_socket_multicast.h" +#include "pico_fragments.h" + +#ifdef PICO_SUPPORT_IPV4 + +#ifdef PICO_SUPPORT_MCAST +# define ip_mcast_dbg(...) do {} while(0) /* so_mcast_dbg in pico_socket.c */ +/* #define ip_mcast_dbg dbg */ +# define PICO_MCAST_ALL_HOSTS long_be(0xE0000001) /* 224.0.0.1 */ +/* Default network interface for multicast transmission */ +static struct pico_ipv4_link *mcast_default_link = NULL; +#endif +#ifdef PICO_SUPPORT_IPFRAG +/* # define reassembly_dbg dbg */ +# define reassembly_dbg(...) do {} while(0) +#endif + +/* Queues */ +static struct pico_queue in = { + 0 +}; +static struct pico_queue out = { + 0 +}; + +/* Functions */ +static int ipv4_route_compare(void *ka, void *kb); +static struct pico_frame *pico_ipv4_alloc(struct pico_protocol *self, uint16_t size); + + +int pico_ipv4_compare(struct pico_ip4 *a, struct pico_ip4 *b) +{ + if (a->addr < b->addr) + return -1; + + if (a->addr > b->addr) + return 1; + + return 0; +} + +int pico_ipv4_to_string(char *ipbuf, const uint32_t ip) +{ + const unsigned char *addr = (const unsigned char *) &ip; + int i; + + if (!ipbuf) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + for(i = 0; i < 4; i++) + { + if (addr[i] > 99) { + *ipbuf++ = (char)('0' + (addr[i] / 100)); + *ipbuf++ = (char)('0' + ((addr[i] % 100) / 10)); + *ipbuf++ = (char)('0' + ((addr[i] % 100) % 10)); + } else if (addr[i] > 9) { + *ipbuf++ = (char)('0' + (addr[i] / 10)); + *ipbuf++ = (char)('0' + (addr[i] % 10)); + } else { + *ipbuf++ = (char)('0' + addr[i]); + } + + if (i < 3) + *ipbuf++ = '.'; + } + *ipbuf = '\0'; + + return 0; +} + +static int pico_string_check_null_args(const char *ipstr, uint32_t *ip) +{ + + if (!ipstr || !ip) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + return 0; + +} + +int pico_string_to_ipv4(const char *ipstr, uint32_t *ip) +{ + unsigned char buf[PICO_SIZE_IP4] = { + 0 + }; + int cnt = 0; + char p; + + if (pico_string_check_null_args(ipstr, ip) < 0) + return -1; + + while((p = *ipstr++) != 0 && cnt < PICO_SIZE_IP4) + { + if (pico_is_digit(p)) { + buf[cnt] = (uint8_t)((10 * buf[cnt]) + (p - '0')); + } else if (p == '.') { + cnt++; + } else { + return -1; + } + } + /* Handle short notation */ + if (cnt == 1) { + buf[3] = buf[1]; + buf[1] = 0; + buf[2] = 0; + } else if (cnt == 2) { + buf[3] = buf[2]; + buf[2] = 0; + } else if (cnt != 3) { + /* String could not be parsed, return error */ + return -1; + } + + *ip = long_from(buf); + + return 0; +} + +int pico_ipv4_valid_netmask(uint32_t mask) +{ + int cnt = 0; + int end = 0; + int i; + uint32_t mask_swap = long_be(mask); + + /* + * Swap bytes for convenient parsing + * e.g. 0x..f8ff will become 0xfff8.. + * Then, we count the consecutive bits + * + * */ + + for(i = 0; i < 32; i++) { + if ((mask_swap << i) & 0x80000000) { + if (end) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + cnt++; + } else { + end = 1; + } + } + return cnt; +} + +int pico_ipv4_is_unicast(uint32_t address) +{ + const unsigned char *addr = (unsigned char *) &address; + if ((addr[0] & 0xe0) == 0xe0) + return 0; /* multicast */ + + return 1; +} + +int pico_ipv4_is_multicast(uint32_t address) +{ + const unsigned char *addr = (unsigned char *) &address; + if ((addr[0] != 0xff) && ((addr[0] & 0xe0) == 0xe0)) + return 1; /* multicast */ + + return 0; +} + +int pico_ipv4_is_loopback(uint32_t address) +{ + const unsigned char *addr = (unsigned char *) &address; + if (addr[0] == 0x7f) + return 1; + + return 0; +} + +static int pico_ipv4_is_invalid_loopback(uint32_t address, struct pico_device *dev) +{ + return pico_ipv4_is_loopback(address) && ((!dev) || strcmp(dev->name, "loop")); +} + +int pico_ipv4_is_valid_src(uint32_t address, struct pico_device *dev) +{ + if (pico_ipv4_is_broadcast(address)) { + dbg("Source is a broadcast address, discard packet\n"); + return 0; + } else if ( pico_ipv4_is_multicast(address)) { + dbg("Source is a multicast address, discard packet\n"); + return 0; + } else if (pico_ipv4_is_invalid_loopback(address, dev)) { + dbg("Source is a loopback address, discard packet\n"); + return 0; + } else { +#ifdef PICO_SUPPORT_AODV + union pico_address src; + src.ip4.addr = address; + pico_aodv_refresh(&src); +#endif + return 1; + } +} + +static int pico_ipv4_checksum(struct pico_frame *f) +{ + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + if (!hdr) + return -1; + + hdr->crc = 0; + hdr->crc = short_be(pico_checksum(hdr, f->net_len)); + return 0; +} + + +#ifdef PICO_SUPPORT_CRC +static inline int pico_ipv4_crc_check(struct pico_frame *f) +{ + uint16_t checksum_invalid = 1; + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + + checksum_invalid = short_be(pico_checksum(hdr, f->net_len)); + if (checksum_invalid) { + dbg("IP: checksum failed!\n"); + pico_frame_discard(f); + return 0; + } + + return 1; +} +#else +static inline int pico_ipv4_crc_check(struct pico_frame *f) +{ + IGNORE_PARAMETER(f); + return 1; +} +#endif /* PICO_SUPPORT_CRC */ + +static int pico_ipv4_forward(struct pico_frame *f); +#ifdef PICO_SUPPORT_MCAST +static int pico_ipv4_mcast_filter(struct pico_frame *f); +#endif + +static int ipv4_link_compare(void *ka, void *kb) +{ + struct pico_ipv4_link *a = ka, *b = kb; + int cmp = pico_ipv4_compare(&a->address, &b->address); + if (cmp) + return cmp; + + /* zero can be assigned multiple times (e.g. for DHCP) */ + if (a->dev != NULL && b->dev != NULL && a->address.addr == PICO_IP4_ANY && b->address.addr == PICO_IP4_ANY) { + if (a->dev < b->dev) + return -1; + + if (a->dev > b->dev) + return 1; + } + + return 0; +} + +PICO_TREE_DECLARE(Tree_dev_link, ipv4_link_compare); + +static int pico_ipv4_process_bcast_in(struct pico_frame *f) +{ + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; +#ifdef PICO_SUPPORT_UDP + if (pico_ipv4_is_broadcast(hdr->dst.addr) && (hdr->proto == PICO_PROTO_UDP)) { + /* Receiving UDP broadcast datagram */ + f->flags |= PICO_FRAME_FLAG_BCAST; + pico_enqueue(pico_proto_udp.q_in, f); + return 1; + } + +#endif + +#ifdef PICO_SUPPORT_ICMP4 + if (pico_ipv4_is_broadcast(hdr->dst.addr) && (hdr->proto == PICO_PROTO_ICMP4)) { + /* Receiving ICMP4 bcast packet */ + f->flags |= PICO_FRAME_FLAG_BCAST; + pico_enqueue(pico_proto_icmp4.q_in, f); + return 1; + } + +#endif + return 0; +} + +static int pico_ipv4_process_mcast_in(struct pico_frame *f) +{ + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + if (pico_ipv4_is_multicast(hdr->dst.addr)) { +#ifdef PICO_SUPPORT_MCAST + /* Receiving UDP multicast datagram TODO set f->flags? */ + if (hdr->proto == PICO_PROTO_IGMP) { + ip_mcast_dbg("MCAST: received IGMP message\n"); + pico_transport_receive(f, PICO_PROTO_IGMP); + return 1; + } else if ((pico_ipv4_mcast_filter(f) == 0) && (hdr->proto == PICO_PROTO_UDP)) { + pico_enqueue(pico_proto_udp.q_in, f); + return 1; + } + +#endif + pico_frame_discard(f); + return 1; + } + + return 0; +} + +static int pico_ipv4_process_local_unicast_in(struct pico_frame *f) +{ + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + struct pico_ipv4_link test = { + .address = {.addr = PICO_IP4_ANY}, .dev = NULL + }; + if (pico_ipv4_link_find(&hdr->dst)) { + if (pico_ipv4_nat_inbound(f, &hdr->dst) == 0) + pico_enqueue(pico_proto_ipv4.q_in, f); /* dst changed, reprocess */ + else + pico_transport_receive(f, hdr->proto); + + return 1; + } else if (pico_tree_findKey(&Tree_dev_link, &test)) { +#ifdef PICO_SUPPORT_UDP + /* address of this device is apparently 0.0.0.0; might be a DHCP packet */ + /* XXX KRO: is obsolete. Broadcast flag is set on outgoing DHCP messages. + * incomming DHCP messages are to be broadcasted. Our current DHCP server + * implementation does not take this flag into account yet though ... */ + pico_enqueue(pico_proto_udp.q_in, f); + return 1; +#endif + } + + return 0; +} + +static void pico_ipv4_process_finally_try_forward(struct pico_frame *f) +{ + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + if ((pico_ipv4_is_broadcast(hdr->dst.addr))) { + /* don't forward broadcast frame, discard! */ + pico_frame_discard(f); + } else if (pico_ipv4_forward(f) != 0) { + pico_frame_discard(f); + /* dbg("Forward failed.\n"); */ + } +} + + + +static int pico_ipv4_process_in(struct pico_protocol *self, struct pico_frame *f) +{ + uint8_t option_len = 0; + int ret = 0; + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + uint16_t max_allowed = (uint16_t) ((int)f->buffer_len - (f->net_hdr - f->buffer) - (int)PICO_SIZE_IP4HDR); + uint16_t flag = short_be(hdr->frag); + + (void)self; + /* NAT needs transport header information */ + if (((hdr->vhl) & 0x0F) > 5) { + option_len = (uint8_t)(4 * (((hdr->vhl) & 0x0F) - 5)); + } + + f->transport_hdr = ((uint8_t *)f->net_hdr) + PICO_SIZE_IP4HDR + option_len; + f->transport_len = (uint16_t)(short_be(hdr->len) - PICO_SIZE_IP4HDR - option_len); + f->net_len = (uint16_t)(PICO_SIZE_IP4HDR + option_len); + + if (f->transport_len > max_allowed) { + pico_frame_discard(f); + return 0; /* Packet is discarded due to unfeasible length */ + } + +#ifdef PICO_SUPPORT_IPFILTER + if (ipfilter(f)) { + /*pico_frame is discarded as result of the filtering*/ + return 0; + } + +#endif + + + /* ret == 1 indicates to continue the function */ + ret = pico_ipv4_crc_check(f); + if (ret < 1) + return ret; + + /* Validate source IP address. Discard quietly if invalid */ + if (!pico_ipv4_is_valid_src(hdr->src.addr, f->dev)) { + pico_frame_discard(f); + return 0; + } + + if (hdr->frag & short_be(PICO_IPV4_EVIL)) { + (void)pico_icmp4_param_problem(f, 0); + pico_frame_discard(f); /* RFC 3514 */ + return 0; + } + + if ((hdr->vhl & 0x0f) < 5) { + /* RFC 791: IHL minimum value is 5 */ + (void)pico_icmp4_param_problem(f, 0); + pico_frame_discard(f); + return 0; + } + + if ((flag & PICO_IPV4_MOREFRAG) || (flag & PICO_IPV4_FRAG_MASK)) + { +#ifdef PICO_SUPPORT_IPFRAG + pico_ipv4_process_frag(hdr, f, hdr ? hdr->proto : 0 ); + /* Frame can be discarded, frag will handle its own copy */ +#endif + /* We do not support fragmentation, discard frame quietly */ + pico_frame_discard(f); + return 0; + } + + if (pico_ipv4_process_bcast_in(f) > 0) + return 0; + + if (pico_ipv4_process_mcast_in(f) > 0) + return 0; + + if (pico_ipv4_process_local_unicast_in(f) > 0) + return 0; + + pico_ipv4_process_finally_try_forward(f); + + return 0; +} + +PICO_TREE_DECLARE(Routes, ipv4_route_compare); + + +static int pico_ipv4_process_out(struct pico_protocol *self, struct pico_frame *f) +{ + IGNORE_PARAMETER(self); + f->start = (uint8_t*) f->net_hdr; +#ifdef PICO_SUPPORT_IPFILTER + if (ipfilter(f)) { + /*pico_frame is discarded as result of the filtering*/ + return 0; + } + +#endif + return pico_sendto_dev(f); +} + + +static struct pico_frame *pico_ipv4_alloc(struct pico_protocol *self, uint16_t size) +{ + struct pico_frame *f = pico_frame_alloc(size + PICO_SIZE_IP4HDR + PICO_SIZE_ETHHDR); + IGNORE_PARAMETER(self); + + if (!f) + return NULL; + + f->datalink_hdr = f->buffer; + f->net_hdr = f->buffer + PICO_SIZE_ETHHDR; + f->net_len = PICO_SIZE_IP4HDR; + f->transport_hdr = f->net_hdr + PICO_SIZE_IP4HDR; + f->transport_len = size; + f->len = size + PICO_SIZE_IP4HDR; + return f; +} + +static int pico_ipv4_frame_sock_push(struct pico_protocol *self, struct pico_frame *f); + +/* Interface: protocol definition */ +struct pico_protocol pico_proto_ipv4 = { + .name = "ipv4", + .proto_number = PICO_PROTO_IPV4, + .layer = PICO_LAYER_NETWORK, + .alloc = pico_ipv4_alloc, + .process_in = pico_ipv4_process_in, + .process_out = pico_ipv4_process_out, + .push = pico_ipv4_frame_sock_push, + .q_in = &in, + .q_out = &out, +}; + + +static int ipv4_route_compare(void *ka, void *kb) +{ + struct pico_ipv4_route *a = ka, *b = kb; + uint32_t a_nm, b_nm; + int cmp; + + a_nm = long_be(a->netmask.addr); + b_nm = long_be(b->netmask.addr); + + /* Routes are sorted by (host side) netmask len, then by addr, then by metric. */ + if (a_nm < b_nm) + return -1; + + if (b_nm < a_nm) + return 1; + + cmp = pico_ipv4_compare(&a->dest, &b->dest); + if (cmp) + return cmp; + + if (a->metric < b->metric) + return -1; + + if (a->metric > b->metric) + return 1; + + return 0; +} + + +static struct pico_ipv4_route default_bcast_route = { + .dest = {PICO_IP4_BCAST}, + .netmask = {PICO_IP4_BCAST}, + .gateway = { 0 }, + .link = NULL, + .metric = 1000 +}; + +static struct pico_ipv4_route *route_find_default_bcast(void) +{ + return &default_bcast_route; +} + + +static struct pico_ipv4_route *route_find(const struct pico_ip4 *addr) +{ + struct pico_ipv4_route *r; + struct pico_tree_node *index; + + if (addr->addr != PICO_IP4_BCAST) { + pico_tree_foreach_reverse(index, &Routes) { + r = index->keyValue; + if ((addr->addr & (r->netmask.addr)) == (r->dest.addr)) { + return r; + } + } + return NULL; + } + + return route_find_default_bcast(); +} + +struct pico_ip4 pico_ipv4_route_get_gateway(struct pico_ip4 *addr) +{ + struct pico_ip4 nullip; + struct pico_ipv4_route *route; + nullip.addr = 0U; + + if (!addr) { + pico_err = PICO_ERR_EINVAL; + return nullip; + } + + route = route_find(addr); + if (!route) { + pico_err = PICO_ERR_EHOSTUNREACH; + return nullip; + } + else + return route->gateway; +} + +struct pico_ip4 *pico_ipv4_source_find(const struct pico_ip4 *dst) +{ + struct pico_ip4 *myself = NULL; + struct pico_ipv4_route *rt; +#ifdef PICO_SUPPORT_AODV + union pico_address node_address; + node_address.ip4.addr = dst->addr; + if (dst->addr && pico_ipv4_is_unicast(dst->addr)) + pico_aodv_lookup(&node_address); + +#endif + + if (!dst) { + pico_err = PICO_ERR_EINVAL; + return NULL; + } + + rt = route_find(dst); + if (rt && rt->link) { + myself = &rt->link->address; + } else { + pico_err = PICO_ERR_EHOSTUNREACH; + } + + return myself; +} + +struct pico_device *pico_ipv4_source_dev_find(const struct pico_ip4 *dst) +{ + struct pico_device *dev = NULL; + struct pico_ipv4_route *rt; + + if (!dst) { + pico_err = PICO_ERR_EINVAL; + return NULL; + } + + rt = route_find(dst); + if (rt && rt->link) { + dev = rt->link->dev; + } else { + pico_err = PICO_ERR_EHOSTUNREACH; + } + + return dev; +} + + +#ifdef PICO_SUPPORT_MCAST +/* link + * | + * MCASTGroups + * | | | + * ------------ | ------------ + * | | | + * MCASTSources MCASTSources MCASTSources + * | | | | | | | | | | | | + * S S S S S S S S S S S S + * + * MCASTGroups: RBTree(mcast_group) + * MCASTSources: RBTree(source) + */ +static int ipv4_mcast_groups_cmp(void *ka, void *kb) +{ + struct pico_mcast_group *a = ka, *b = kb; + return pico_ipv4_compare(&a->mcast_addr, &b->mcast_addr); +} + +static int ipv4_mcast_sources_cmp(void *ka, void *kb) +{ + struct pico_ip4 *a = ka, *b = kb; + return pico_ipv4_compare(a, b); +} + +static void pico_ipv4_mcast_print_groups(struct pico_ipv4_link *mcast_link) +{ + uint16_t i = 0; + struct pico_mcast_group *g = NULL; + struct pico_ip4 *source = NULL; + struct pico_tree_node *index = NULL, *index2 = NULL; + (void) source; + + ip_mcast_dbg("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); + ip_mcast_dbg("+ MULTICAST list interface %-16s +\n", mcast_link->dev->name); + ip_mcast_dbg("+---------------------------------------------------------------------------------+\n"); + ip_mcast_dbg("+ nr | interface | host group | reference count | filter mode | source +\n"); + ip_mcast_dbg("+---------------------------------------------------------------------------------+\n"); + + pico_tree_foreach(index, mcast_link->MCASTGroups) { + g = index->keyValue; + ip_mcast_dbg("+ %04d | %16s | %08X | %05u | %u | %8s +\n", i, mcast_link->dev->name, g->mcast_addr.addr, g->reference_count, g->filter_mode, ""); + pico_tree_foreach(index2, &g->MCASTSources) { + source = index2->keyValue; + ip_mcast_dbg("+ %4s | %16s | %8s | %5s | %s | %08X +\n", "", "", "", "", "", source->addr); + } + i++; + } + ip_mcast_dbg("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); +} + +static int mcast_group_update(struct pico_mcast_group *g, struct pico_tree *MCASTFilter, uint8_t filter_mode) +{ + struct pico_tree_node *index = NULL, *_tmp = NULL; + struct pico_ip4 *source = NULL; + /* cleanup filter */ + pico_tree_foreach_safe(index, &g->MCASTSources, _tmp) { + source = index->keyValue; + pico_tree_delete(&g->MCASTSources, source); + PICO_FREE(source); + } + /* insert new filter */ + if (MCASTFilter) { + pico_tree_foreach(index, MCASTFilter) { + if (index->keyValue) { + source = PICO_ZALLOC(sizeof(struct pico_ip4)); + if (!source) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + source->addr = ((struct pico_ip4 *)index->keyValue)->addr; + pico_tree_insert(&g->MCASTSources, source); + } + } + } + + g->filter_mode = filter_mode; + return 0; +} + +int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter) +{ + struct pico_mcast_group *g = NULL, test = { + 0 + }; + struct pico_ipv4_link *link = NULL; + + if (mcast_link) + link = pico_ipv4_link_get(mcast_link); + + if (!link) + link = mcast_default_link; + + test.mcast_addr = *mcast_group; + g = pico_tree_findKey(link->MCASTGroups, &test); + if (g) { + if (reference_count) + g->reference_count++; + + pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_UPDATE); + } else { + g = PICO_ZALLOC(sizeof(struct pico_mcast_group)); + if (!g) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + /* "non-existent" state of filter mode INCLUDE and empty source list */ + g->filter_mode = PICO_IP_MULTICAST_INCLUDE; + g->reference_count = 1; + g->mcast_addr = *mcast_group; + g->MCASTSources.root = &LEAF; + g->MCASTSources.compare = ipv4_mcast_sources_cmp; + pico_tree_insert(link->MCASTGroups, g); + pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_CREATE); + } + + if (mcast_group_update(g, MCASTFilter, filter_mode) < 0) { + dbg("Error in mcast_group update\n"); + return -1; + } + + pico_ipv4_mcast_print_groups(link); + return 0; +} + +int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter) +{ + + struct pico_mcast_group *g = NULL, test = { + 0 + }; + struct pico_ipv4_link *link = NULL; + struct pico_tree_node *index = NULL, *_tmp = NULL; + struct pico_ip4 *source = NULL; + + if (mcast_link) + link = pico_ipv4_link_get(mcast_link); + + if (!link) + link = mcast_default_link; + + test.mcast_addr = *mcast_group; + g = pico_tree_findKey(link->MCASTGroups, &test); + if (!g) { + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + if (reference_count && (--(g->reference_count) < 1)) { + pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_DELETE); + /* cleanup filter */ + pico_tree_foreach_safe(index, &g->MCASTSources, _tmp) { + source = index->keyValue; + pico_tree_delete(&g->MCASTSources, source); + PICO_FREE(source); + } + pico_tree_delete(link->MCASTGroups, g); + PICO_FREE(g); + } else { + pico_igmp_state_change(mcast_link, mcast_group, filter_mode, MCASTFilter, PICO_IGMP_STATE_UPDATE); + if (mcast_group_update(g, MCASTFilter, filter_mode) < 0) + return -1; + } + } + + pico_ipv4_mcast_print_groups(link); + return 0; +} + +struct pico_ipv4_link *pico_ipv4_get_default_mcastlink(void) +{ + return mcast_default_link; +} + +static int pico_ipv4_mcast_filter(struct pico_frame *f) +{ + struct pico_ipv4_link *link = NULL; + struct pico_tree_node *index = NULL, *index2 = NULL; + struct pico_mcast_group *g = NULL, test = { + 0 + }; + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + + test.mcast_addr = hdr->dst; + + pico_tree_foreach(index, &Tree_dev_link) { + link = index->keyValue; + g = pico_tree_findKey(link->MCASTGroups, &test); + if (g) { + if (f->dev == link->dev) { + ip_mcast_dbg("MCAST: IP %08X is group member of current link %s\n", hdr->dst.addr, f->dev->name); + /* perform source filtering */ + switch (g->filter_mode) { + case PICO_IP_MULTICAST_INCLUDE: + pico_tree_foreach(index2, &g->MCASTSources) { + if (hdr->src.addr == ((struct pico_ip4 *)index2->keyValue)->addr) { + ip_mcast_dbg("MCAST: IP %08X in included interface source list\n", hdr->src.addr); + return 0; + } + } + ip_mcast_dbg("MCAST: IP %08X NOT in included interface source list\n", hdr->src.addr); + return -1; + + case PICO_IP_MULTICAST_EXCLUDE: + pico_tree_foreach(index2, &g->MCASTSources) { + if (hdr->src.addr == ((struct pico_ip4 *)index2->keyValue)->addr) { + ip_mcast_dbg("MCAST: IP %08X in excluded interface source list\n", hdr->src.addr); + return -1; + } + } + ip_mcast_dbg("MCAST: IP %08X NOT in excluded interface source list\n", hdr->src.addr); + return 0; + + default: + return -1; + } + } else { + ip_mcast_dbg("MCAST: IP %08X is group member of different link %s\n", hdr->dst.addr, link->dev->name); + } + } else { + ip_mcast_dbg("MCAST: IP %08X is not a group member of link %s\n", hdr->dst.addr, f->dev->name); + } + } + return -1; +} + +#else + +int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter) +{ + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + +int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter) +{ + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + +struct pico_ipv4_link *pico_ipv4_get_default_mcastlink(void) +{ + pico_err = PICO_ERR_EPROTONOSUPPORT; + return NULL; +} +#endif /* PICO_SUPPORT_MCAST */ + +/* #define DEBUG_ROUTE */ +#ifdef DEBUG_ROUTE +void dbg_route(void) +{ + struct pico_ipv4_route *r; + struct pico_tree_node *index; + int count_hosts = 0; + dbg("==== ROUTING TABLE =====\n"); + pico_tree_foreach(index, &Routes) { + r = index->keyValue; + dbg("Route to %08x/%08x, gw %08x, dev: %s, metric: %d\n", r->dest.addr, r->netmask.addr, r->gateway.addr, r->link->dev->name, r->metric); + if (r->netmask.addr == 0xFFFFFFFF) + count_hosts++; + } + dbg("================ total HOST nodes: %d ======\n\n\n", count_hosts); +} +#else +#define dbg_route() do { } while(0) +#endif + +int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t proto) +{ + + struct pico_ipv4_route *route; + struct pico_ipv4_link *link; + struct pico_ipv4_hdr *hdr; + uint8_t ttl = PICO_IPV4_DEFAULT_TTL; + uint8_t vhl = 0x45; /* version 4, header length 20 */ + static uint16_t ipv4_progressive_id = 0x91c0; +#ifdef PICO_SUPPORT_MCAST + struct pico_tree_node *index; +#endif + + if (!f || !dst) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + + hdr = (struct pico_ipv4_hdr *) f->net_hdr; + if (!hdr) { + dbg("IP header error\n"); + pico_err = PICO_ERR_EINVAL; + goto drop; + } + + if (dst->addr == 0) { + dbg("IP destination addr error\n"); + pico_err = PICO_ERR_EINVAL; + goto drop; + } + + route = route_find(dst); + if (!route) { + /* dbg("Route to %08x not found.\n", long_be(dst->addr)); */ + + + pico_err = PICO_ERR_EHOSTUNREACH; + goto drop; + } else { + link = route->link; +#ifdef PICO_SUPPORT_MCAST + if (pico_ipv4_is_multicast(dst->addr)) { /* if multicast */ + switch (proto) { + case PICO_PROTO_UDP: + if (pico_udp_get_mc_ttl(f->sock, &ttl) < 0) + ttl = PICO_IP_DEFAULT_MULTICAST_TTL; + + break; + case PICO_PROTO_IGMP: + vhl = 0x46; /* header length 24 */ + ttl = 1; + /* router alert (RFC 2113) */ + hdr->options[0] = 0x94; + hdr->options[1] = 0x04; + hdr->options[2] = 0x00; + hdr->options[3] = 0x00; + if (f->dev && link->dev != f->dev) { /* default link is not requested link */ + pico_tree_foreach(index, &Tree_dev_link) { + link = index->keyValue; + if (link->dev == f->dev) + break; + } + } + + break; + default: + ttl = PICO_IPV4_DEFAULT_TTL; + } + } + +#endif + } + + hdr->vhl = vhl; + hdr->len = short_be((uint16_t)(f->transport_len + f->net_len)); + if ((f->transport_hdr != f->payload) && +#ifdef PICO_SUPPORT_IPFRAG + (0 == (f->frag & PICO_IPV4_MOREFRAG)) && + (0 == (f->frag & PICO_IPV4_FRAG_MASK)) && +#endif + 1 ) + ipv4_progressive_id++; + + if (f->send_ttl > 0) { + ttl = f->send_ttl; + } + + hdr->id = short_be(ipv4_progressive_id); + hdr->dst.addr = dst->addr; + hdr->src.addr = link->address.addr; + hdr->ttl = ttl; + hdr->tos = f->send_tos; + hdr->proto = proto; + hdr->frag = short_be(PICO_IPV4_DONTFRAG); + +#ifdef PICO_SUPPORT_IPFRAG +# ifdef PICO_SUPPORT_UDP + if (proto == PICO_PROTO_UDP) { + /* first fragment, can not use transport_len to calculate IP length */ + if (f->transport_hdr != f->payload) + hdr->len = short_be((uint16_t)(f->payload_len + sizeof(struct pico_udp_hdr) + f->net_len)); + + /* set fragmentation flags and offset calculated in socket layer */ + hdr->frag = short_be(f->frag); + } + + if (proto == PICO_PROTO_ICMP4) + { + hdr->frag = short_be(f->frag); + } + +# endif +#endif /* PICO_SUPPORT_IPFRAG */ + pico_ipv4_checksum(f); + + if (f->sock && f->sock->dev) { + /* if the socket has its device set, use that (currently used for DHCP) */ + f->dev = f->sock->dev; + } else { + f->dev = link->dev; + if (f->sock) + f->sock->dev = f->dev; + } + +#ifdef PICO_SUPPORT_MCAST + if (pico_ipv4_is_multicast(hdr->dst.addr)) { + struct pico_frame *cpy; + /* Sending UDP multicast datagram, am I member? If so, loopback copy */ + if ((proto != PICO_PROTO_IGMP) && (pico_ipv4_mcast_filter(f) == 0)) { + ip_mcast_dbg("MCAST: sender is member of group, loopback copy\n"); + cpy = pico_frame_copy(f); + pico_enqueue(&in, cpy); + } + } + +#endif + +/* #ifdef PICO_SUPPORT_AODV */ +#if 0 + { + union pico_address node_address; + node_address.ip4.addr = hdr->dst.addr; + if(hdr->dst.addr && pico_ipv4_is_unicast(hdr->dst.addr)) + pico_aodv_lookup(&node_address); + } +#endif + + if (pico_ipv4_link_get(&hdr->dst)) { + /* it's our own IP */ + return pico_enqueue(&in, f); + } else{ + /* TODO: Check if there are members subscribed here */ + return pico_enqueue(&out, f); + } + +drop: + pico_frame_discard(f); + return -1; +} + + +static int pico_ipv4_frame_sock_push(struct pico_protocol *self, struct pico_frame *f) +{ + struct pico_ip4 *dst; + struct pico_remote_endpoint *remote_endpoint = (struct pico_remote_endpoint *) f->info; + IGNORE_PARAMETER(self); + + if (!f->sock) { + pico_frame_discard(f); + return -1; + } + + if (remote_endpoint) { + dst = &remote_endpoint->remote_addr.ip4; + } else { + dst = &f->sock->remote_addr.ip4; + } + + return pico_ipv4_frame_push(f, dst, (uint8_t)f->sock->proto->proto_number); +} + + +int MOCKABLE pico_ipv4_route_add(struct pico_ip4 address, struct pico_ip4 netmask, struct pico_ip4 gateway, int metric, struct pico_ipv4_link *link) +{ + struct pico_ipv4_route test, *new; + test.dest.addr = address.addr; + test.netmask.addr = netmask.addr; + test.metric = (uint32_t)metric; + + if (pico_tree_findKey(&Routes, &test)) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + new = PICO_ZALLOC(sizeof(struct pico_ipv4_route)); + if (!new) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + new->dest.addr = address.addr; + new->netmask.addr = netmask.addr; + new->gateway.addr = gateway.addr; + new->metric = (uint32_t)metric; + if (gateway.addr == 0) { + /* No gateway provided, use the link */ + new->link = link; + } else { + struct pico_ipv4_route *r = route_find(&gateway); + if (!r ) { /* Specified Gateway is unreachable */ + pico_err = PICO_ERR_EHOSTUNREACH; + PICO_FREE(new); + return -1; + } + + if (r->gateway.addr) { /* Specified Gateway is not a neighbor */ + pico_err = PICO_ERR_ENETUNREACH; + PICO_FREE(new); + return -1; + } + + new->link = r->link; + } + + if (!new->link) { + pico_err = PICO_ERR_EINVAL; + PICO_FREE(new); + return -1; + } + + pico_tree_insert(&Routes, new); + dbg_route(); + return 0; +} + +int pico_ipv4_route_del(struct pico_ip4 address, struct pico_ip4 netmask, int metric) +{ + struct pico_ipv4_route test, *found; + + test.dest.addr = address.addr; + test.netmask.addr = netmask.addr; + test.metric = (uint32_t)metric; + + found = pico_tree_findKey(&Routes, &test); + if (found) { + + pico_tree_delete(&Routes, found); + PICO_FREE(found); + + dbg_route(); + return 0; + } + + pico_err = PICO_ERR_EINVAL; + return -1; +} + + +int pico_ipv4_link_add(struct pico_device *dev, struct pico_ip4 address, struct pico_ip4 netmask) +{ + struct pico_ipv4_link test, *new; + struct pico_ip4 network, gateway; + char ipstr[30]; + + if (!dev) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + test.address.addr = address.addr; + test.netmask.addr = netmask.addr; + test.dev = dev; + /** XXX: Valid netmask / unicast address test **/ + + if (pico_tree_findKey(&Tree_dev_link, &test)) { + pico_err = PICO_ERR_EADDRINUSE; + return -1; + } + + /** XXX: Check for network already in use (e.g. trying to assign 10.0.0.1/24 where 10.1.0.1/8 is in use) **/ + new = PICO_ZALLOC(sizeof(struct pico_ipv4_link)); + if (!new) { + dbg("IPv4: Out of memory!\n"); + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + new->address.addr = address.addr; + new->netmask.addr = netmask.addr; + new->dev = dev; +#ifdef PICO_SUPPORT_MCAST + new->MCASTGroups = PICO_ZALLOC(sizeof(struct pico_tree)); + if (!new->MCASTGroups) { + PICO_FREE(new); + dbg("IPv4: Out of memory!\n"); + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + new->MCASTGroups->root = &LEAF; + new->MCASTGroups->compare = ipv4_mcast_groups_cmp; + new->mcast_compatibility = PICO_IGMPV3; /* default RFC 3376 $7.2.1 */ + new->mcast_last_query_interval = PICO_IGMP_QUERY_INTERVAL; +#endif + + pico_tree_insert(&Tree_dev_link, new); +#ifdef PICO_SUPPORT_MCAST + do { + struct pico_ip4 mcast_all_hosts, mcast_addr, mcast_nm, mcast_gw; + if (!mcast_default_link) { + mcast_addr.addr = long_be(0xE0000000); /* 224.0.0.0 */ + mcast_nm.addr = long_be(0xF0000000); /* 15.0.0.0 */ + mcast_gw.addr = long_be(0x00000000); + mcast_default_link = new; + pico_ipv4_route_add(mcast_addr, mcast_nm, mcast_gw, 1, new); + } + + mcast_all_hosts.addr = PICO_MCAST_ALL_HOSTS; + pico_ipv4_mcast_join(&address, &mcast_all_hosts, 1, PICO_IP_MULTICAST_EXCLUDE, NULL); + } while(0); +#endif + + network.addr = address.addr & netmask.addr; + gateway.addr = 0U; + pico_ipv4_route_add(network, netmask, gateway, 1, new); + pico_ipv4_to_string(ipstr, new->address.addr); + dbg("Assigned ipv4 %s to device %s\n", ipstr, new->dev->name); + if (default_bcast_route.link == NULL) + default_bcast_route.link = new; + + return 0; +} + +static int pico_ipv4_cleanup_routes(struct pico_ipv4_link *link) +{ + struct pico_tree_node *index = NULL, *tmp = NULL; + struct pico_ipv4_route *route = NULL; + + pico_tree_foreach_safe(index, &Routes, tmp) { + route = index->keyValue; + if (link == route->link) + pico_ipv4_route_del(route->dest, route->netmask, (int)route->metric); + } + return 0; +} + +void MOCKABLE pico_ipv4_route_set_bcast_link(struct pico_ipv4_link *link) +{ + if (link) + default_bcast_route.link = link; +} + +int pico_ipv4_link_del(struct pico_device *dev, struct pico_ip4 address) +{ + struct pico_ipv4_link test, *found; + + if (!dev) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + test.address.addr = address.addr; + test.dev = dev; + found = pico_tree_findKey(&Tree_dev_link, &test); + if (!found) { + pico_err = PICO_ERR_ENXIO; + return -1; + } + +#ifdef PICO_SUPPORT_MCAST + do { + struct pico_ip4 mcast_all_hosts, mcast_addr, mcast_nm; + struct pico_mcast_group *g = NULL; + struct pico_tree_node *index, *_tmp; + if (found == mcast_default_link) { + mcast_addr.addr = long_be(0xE0000000); /* 224.0.0.0 */ + mcast_nm.addr = long_be(0xF0000000); /* 15.0.0.0 */ + mcast_default_link = NULL; + pico_ipv4_route_del(mcast_addr, mcast_nm, 1); + } + + mcast_all_hosts.addr = PICO_MCAST_ALL_HOSTS; + pico_ipv4_mcast_leave(&address, &mcast_all_hosts, 1, PICO_IP_MULTICAST_EXCLUDE, NULL); + pico_tree_foreach_safe(index, found->MCASTGroups, _tmp) { + g = index->keyValue; + pico_tree_delete(found->MCASTGroups, g); + PICO_FREE(g); + } + } while(0); +#endif + + pico_ipv4_cleanup_routes(found); + pico_tree_delete(&Tree_dev_link, found); + if (default_bcast_route.link == found) + default_bcast_route.link = NULL; + + PICO_FREE(found); + + return 0; +} + + +struct pico_ipv4_link *pico_ipv4_link_get(struct pico_ip4 *address) +{ + struct pico_ipv4_link test = { + 0 + }, *found = NULL; + test.address.addr = address->addr; + + found = pico_tree_findKey(&Tree_dev_link, &test); + if (!found) + return NULL; + else + return found; +} + +struct pico_ipv4_link *MOCKABLE pico_ipv4_link_by_dev(struct pico_device *dev) +{ + struct pico_tree_node *index = NULL; + struct pico_ipv4_link *link = NULL; + + pico_tree_foreach(index, &Tree_dev_link) { + link = index->keyValue; + if (link->dev == dev) + return link; + } + return NULL; +} + +struct pico_ipv4_link *pico_ipv4_link_by_dev_next(struct pico_device *dev, struct pico_ipv4_link *last) +{ + struct pico_tree_node *index = NULL; + struct pico_ipv4_link *link = NULL; + int valid = 0; + + if (last == NULL) + valid = 1; + + pico_tree_foreach(index, &Tree_dev_link) { + link = index->keyValue; + if (link->dev == dev) { + if (last == link) + valid = 1; + else if (valid > 0) + return link; + } + } + return NULL; +} + +struct pico_device *MOCKABLE pico_ipv4_link_find(struct pico_ip4 *address) +{ + struct pico_ipv4_link test, *found; + if (!address) { + pico_err = PICO_ERR_EINVAL; + return NULL; + } + + test.dev = NULL; + test.address.addr = address->addr; + found = pico_tree_findKey(&Tree_dev_link, &test); + if (!found) { + pico_err = PICO_ERR_ENXIO; + return NULL; + } + + return found->dev; +} + + +static int pico_ipv4_rebound_large(struct pico_frame *f) +{ +#ifdef PICO_SUPPORT_IPFRAG + uint16_t total_payload_written = 0; + uint32_t len = f->transport_len; + struct pico_frame *fr; + struct pico_ip4 dst; + struct pico_ipv4_hdr *hdr; + hdr = (struct pico_ipv4_hdr *) f->net_hdr; + dst.addr = hdr->src.addr; + + while(total_payload_written < len) { + uint32_t space = (uint32_t)len - total_payload_written; + if (space > PICO_IPV4_MAXPAYLOAD) + space = PICO_IPV4_MAXPAYLOAD; + + fr = pico_ipv4_alloc(&pico_proto_ipv4, (uint16_t)space); + if (!fr) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + if (space + total_payload_written < len) + { + fr->frag |= PICO_IPV4_MOREFRAG; + } + else + { + fr->frag &= PICO_IPV4_FRAG_MASK; + } + + fr->frag = (((total_payload_written) >> 3u) & 0xffffu) | fr->frag; + + memcpy(fr->transport_hdr, f->transport_hdr + total_payload_written, fr->transport_len); + if (pico_ipv4_frame_push(fr, &dst, hdr->proto) > 0) { + total_payload_written = (uint16_t)((uint16_t)fr->transport_len + total_payload_written); + } else { + pico_frame_discard(fr); + break; + } + } /* while() */ + return (int)total_payload_written; +#else + (void)f; + return -1; +#endif +} + +int pico_ipv4_rebound(struct pico_frame *f) +{ + struct pico_ip4 dst; + struct pico_ipv4_hdr *hdr; + if (!f) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + hdr = (struct pico_ipv4_hdr *) f->net_hdr; + if (!hdr) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + dst.addr = hdr->src.addr; + if (f->transport_len > PICO_IPV4_MAXPAYLOAD) { + return pico_ipv4_rebound_large(f); + } + + return pico_ipv4_frame_push(f, &dst, hdr->proto); +} + +static int pico_ipv4_pre_forward_checks(struct pico_frame *f) +{ + static uint16_t last_id = 0; + static uint16_t last_proto = 0; + static struct pico_ip4 last_src = { + 0 + }; + static struct pico_ip4 last_dst = { + 0 + }; + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)f->net_hdr; + + /* Decrease TTL, check if expired */ + hdr->ttl = (uint8_t)(hdr->ttl - 1); + if (hdr->ttl < 1) { + pico_notify_ttl_expired(f); + dbg(" ------------------- TTL EXPIRED\n"); + return -1; + } + + /* HACK: increase crc to compensate decreased TTL */ + hdr->crc++; + + /* If source is local, discard anyway (packets bouncing back and forth) */ + if (pico_ipv4_link_get(&hdr->src)) + return -1; + + /* If this was the last forwarded packet, silently discard to prevent duplications */ + if ((last_src.addr == hdr->src.addr) && (last_id == hdr->id) + && (last_dst.addr == hdr->dst.addr) && (last_proto == hdr->proto)) { + return -1; + } else { + last_src.addr = hdr->src.addr; + last_dst.addr = hdr->dst.addr; + last_id = hdr->id; + last_proto = hdr->proto; + } + + return 0; +} + +static int pico_ipv4_forward_check_dev(struct pico_frame *f) +{ + if (f->dev->eth != NULL) + f->len -= PICO_SIZE_ETHHDR; + + if (f->len > f->dev->mtu) { + pico_notify_pkt_too_big(f); + return -1; + } + + return 0; +} + +static int pico_ipv4_forward(struct pico_frame *f) +{ + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)f->net_hdr; + struct pico_ipv4_route *rt; + if (!hdr) { + return -1; + } + + rt = route_find(&hdr->dst); + if (!rt) { + pico_notify_dest_unreachable(f); + return -1; + } + + f->dev = rt->link->dev; + + if (pico_ipv4_pre_forward_checks(f) < 0) + return -1; + + pico_ipv4_nat_outbound(f, &rt->link->address); + + f->start = f->net_hdr; + + if (pico_ipv4_forward_check_dev(f) < 0) + return -1; + + pico_sendto_dev(f); + return 0; + +} + +int pico_ipv4_is_broadcast(uint32_t addr) +{ + struct pico_ipv4_link *link; + struct pico_tree_node *index; + if (addr == PICO_IP4_BCAST) + return 1; + + pico_tree_foreach(index, &Tree_dev_link) { + link = index->keyValue; + if ((link->address.addr | (~link->netmask.addr)) == addr) + return 1; + } + return 0; +} + +void pico_ipv4_unreachable(struct pico_frame *f, int err) +{ + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; +#if defined PICO_SUPPORT_TCP || defined PICO_SUPPORT_UDP + f->transport_hdr = ((uint8_t *)f->net_hdr) + PICO_SIZE_IP4HDR; + pico_transport_error(f, hdr->proto, err); +#endif +} + +int pico_ipv4_cleanup_links(struct pico_device *dev) +{ + struct pico_tree_node *index = NULL, *_tmp = NULL; + struct pico_ipv4_link *link = NULL; + + pico_tree_foreach_safe(index, &Tree_dev_link, _tmp) { + link = index->keyValue; + if (dev == link->dev) + pico_ipv4_link_del(dev, link->address); + } + return 0; +} + + +#endif diff --git a/net/picotcp/modules/pico_ipv4.h b/net/picotcp/modules/pico_ipv4.h new file mode 100644 index 0000000..2429815 --- /dev/null +++ b/net/picotcp/modules/pico_ipv4.h @@ -0,0 +1,124 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + *********************************************************************/ +#ifndef INCLUDE_PICO_IPV4 +#define INCLUDE_PICO_IPV4 +#include "pico_addressing.h" +#include "pico_protocol.h" +#include "pico_tree.h" +#include "pico_device.h" + +#define PICO_IPV4_INADDR_ANY 0x00000000U + +#define PICO_IPV4_MTU (1500u) +#define PICO_SIZE_IP4HDR (uint32_t)((sizeof(struct pico_ipv4_hdr))) +#define PICO_IPV4_MAXPAYLOAD (PICO_IPV4_MTU - PICO_SIZE_IP4HDR) +#define PICO_IPV4_DONTFRAG 0x4000U +#define PICO_IPV4_MOREFRAG 0x2000U +#define PICO_IPV4_EVIL 0x8000U +#define PICO_IPV4_FRAG_MASK 0x1FFFU +#define PICO_IPV4_DEFAULT_TTL 64 +#ifndef MBED + #define PICO_IPV4_FRAG_MAX_SIZE (uint32_t)(63 * 1024) +#else + #define PICO_IPV4_FRAG_MAX_SIZE PICO_DEFAULT_SOCKETQ +#endif + +extern struct pico_protocol pico_proto_ipv4; + +PACKED_STRUCT_DEF pico_ipv4_hdr { + uint8_t vhl; + uint8_t tos; + uint16_t len; + uint16_t id; + uint16_t frag; + uint8_t ttl; + uint8_t proto; + uint16_t crc; + struct pico_ip4 src; + struct pico_ip4 dst; + uint8_t options[]; +}; + +PACKED_STRUCT_DEF pico_ipv4_pseudo_hdr +{ + struct pico_ip4 src; + struct pico_ip4 dst; + uint8_t zeros; + uint8_t proto; + uint16_t len; +}; + +/* Interface: link to device */ +struct pico_mcast_list; + +struct pico_ipv4_link +{ + struct pico_device *dev; + struct pico_ip4 address; + struct pico_ip4 netmask; +#ifdef PICO_SUPPORT_MCAST + struct pico_tree *MCASTGroups; + uint8_t mcast_compatibility; + uint8_t mcast_last_query_interval; +#endif +}; + +#ifdef PICO_SUPPORT_MCAST +struct pico_mcast_group { + uint8_t filter_mode; + uint16_t reference_count; + struct pico_ip4 mcast_addr; + struct pico_tree MCASTSources; +}; +#endif + +struct pico_ipv4_route +{ + struct pico_ip4 dest; + struct pico_ip4 netmask; + struct pico_ip4 gateway; + struct pico_ipv4_link *link; + uint32_t metric; +}; + +extern struct pico_tree Routes; + + +int pico_ipv4_compare(struct pico_ip4 *a, struct pico_ip4 *b); +int pico_ipv4_to_string(char *ipbuf, const uint32_t ip); +int pico_string_to_ipv4(const char *ipstr, uint32_t *ip); +int pico_ipv4_valid_netmask(uint32_t mask); +int pico_ipv4_is_unicast(uint32_t address); +int pico_ipv4_is_multicast(uint32_t address); +int pico_ipv4_is_broadcast(uint32_t addr); +int pico_ipv4_is_loopback(uint32_t addr); +int pico_ipv4_is_valid_src(uint32_t addr, struct pico_device *dev); + +int pico_ipv4_link_add(struct pico_device *dev, struct pico_ip4 address, struct pico_ip4 netmask); +int pico_ipv4_link_del(struct pico_device *dev, struct pico_ip4 address); +int pico_ipv4_rebound(struct pico_frame *f); + +int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t proto); +struct pico_ipv4_link *pico_ipv4_link_get(struct pico_ip4 *address); +struct pico_ipv4_link *pico_ipv4_link_by_dev(struct pico_device *dev); +struct pico_ipv4_link *pico_ipv4_link_by_dev_next(struct pico_device *dev, struct pico_ipv4_link *last); +struct pico_device *pico_ipv4_link_find(struct pico_ip4 *address); +struct pico_ip4 *pico_ipv4_source_find(const struct pico_ip4 *dst); +struct pico_device *pico_ipv4_source_dev_find(const struct pico_ip4 *dst); +int pico_ipv4_route_add(struct pico_ip4 address, struct pico_ip4 netmask, struct pico_ip4 gateway, int metric, struct pico_ipv4_link *link); +int pico_ipv4_route_del(struct pico_ip4 address, struct pico_ip4 netmask, int metric); +struct pico_ip4 pico_ipv4_route_get_gateway(struct pico_ip4 *addr); +void pico_ipv4_route_set_bcast_link(struct pico_ipv4_link *link); +void pico_ipv4_unreachable(struct pico_frame *f, int err); + +int pico_ipv4_mcast_join(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter); +int pico_ipv4_mcast_leave(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t reference_count, uint8_t filter_mode, struct pico_tree *MCASTFilter); +struct pico_ipv4_link *pico_ipv4_get_default_mcastlink(void); +int pico_ipv4_cleanup_links(struct pico_device *dev); + +#endif /* _INCLUDE_PICO_IPV4 */ diff --git a/net/picotcp/modules/pico_ipv6.h b/net/picotcp/modules/pico_ipv6.h new file mode 100644 index 0000000..a753531 --- /dev/null +++ b/net/picotcp/modules/pico_ipv6.h @@ -0,0 +1,153 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + *********************************************************************/ +#ifndef _INCLUDE_PICO_IPV6 +#define _INCLUDE_PICO_IPV6 +#include "pico_addressing.h" +#include "pico_protocol.h" + +#define PICO_SIZE_IP6HDR ((uint32_t)(sizeof(struct pico_ipv6_hdr))) +#define PICO_IPV6_DEFAULT_HOP 64 +#define PICO_IPV6_MIN_MTU 1280 + + +#define PICO_IPV6_EXTHDR_HOPBYHOP 0 +#define PICO_IPV6_EXTHDR_ROUTING 43 +#define PICO_IPV6_EXTHDR_FRAG 44 +#define PICO_IPV6_EXTHDR_ESP 50 +#define PICO_IPV6_EXTHDR_AUTH 51 +#define PICO_IPV6_EXTHDR_NONE 59 +#define PICO_IPV6_EXTHDR_DESTOPT 60 + + + + +extern const uint8_t PICO_IP6_ANY[PICO_SIZE_IP6]; +extern struct pico_protocol pico_proto_ipv6; +extern struct pico_tree IPV6Routes; + +PACKED_STRUCT_DEF pico_ipv6_hdr { + uint32_t vtf; + uint16_t len; + uint8_t nxthdr; + uint8_t hop; + struct pico_ip6 src; + struct pico_ip6 dst; + uint8_t extensions[0]; +}; + +PACKED_STRUCT_DEF pico_ipv6_pseudo_hdr +{ + struct pico_ip6 src; + struct pico_ip6 dst; + uint32_t len; + uint8_t zero[3]; + uint8_t nxthdr; +}; + +struct pico_ipv6_link +{ + struct pico_device *dev; + struct pico_ip6 address; + struct pico_ip6 netmask; + uint8_t istentative : 1; + uint8_t isduplicate : 1; + struct pico_timer *dad_timer; + uint16_t dup_detect_retrans; + pico_time expire_time; +}; + +struct pico_ipv6_hbhoption { + uint8_t type; + uint8_t len; + uint8_t options[0]; +}; + +struct pico_ipv6_destoption { + uint8_t type; + uint8_t len; + uint8_t options[0]; +}; + +struct pico_ipv6_route +{ + struct pico_ip6 dest; + struct pico_ip6 netmask; + struct pico_ip6 gateway; + struct pico_ipv6_link *link; + uint32_t metric; +}; + +PACKED_STRUCT_DEF pico_ipv6_exthdr { + uint8_t nxthdr; + + PACKED_UNION_DEF ipv6_ext_u { + PEDANTIC_STRUCT_DEF hopbyhop_s { + uint8_t len; + uint8_t options[0]; + } hopbyhop; + + PEDANTIC_STRUCT_DEF destopt_s { + uint8_t len; + uint8_t options[0]; + } destopt; + + PEDANTIC_STRUCT_DEF routing_s { + uint8_t len; + uint8_t routtype; + uint8_t segleft; + } routing; + + PEDANTIC_STRUCT_DEF fragmentation_s { + uint8_t res; + uint8_t om[2]; + uint8_t id[4]; + } frag; + } ext; +}; + +int pico_ipv6_compare(struct pico_ip6 *a, struct pico_ip6 *b); +int pico_string_to_ipv6(const char *ipstr, uint8_t *ip); +int pico_ipv6_to_string(char *ipbuf, const uint8_t ip[PICO_SIZE_IP6]); +int pico_ipv6_is_unicast(struct pico_ip6 *a); +int pico_ipv6_is_multicast(const uint8_t addr[PICO_SIZE_IP6]); +int pico_ipv6_is_allhosts_multicast(const uint8_t addr[PICO_SIZE_IP6]); +int pico_ipv6_is_solnode_multicast(const uint8_t addr[PICO_SIZE_IP6], struct pico_device *dev); +int pico_ipv6_is_global(const uint8_t addr[PICO_SIZE_IP6]); +int pico_ipv6_is_uniquelocal(const uint8_t addr[PICO_SIZE_IP6]); +int pico_ipv6_is_sitelocal(const uint8_t addr[PICO_SIZE_IP6]); +int pico_ipv6_is_linklocal(const uint8_t addr[PICO_SIZE_IP6]); +int pico_ipv6_is_solicited(const uint8_t addr[PICO_SIZE_IP6]); +int pico_ipv6_is_unspecified(const uint8_t addr[PICO_SIZE_IP6]); +int pico_ipv6_is_localhost(const uint8_t addr[PICO_SIZE_IP6]); + +int pico_ipv6_frame_push(struct pico_frame *f, struct pico_ip6 *src, struct pico_ip6 *dst, uint8_t proto, int is_dad); +int pico_ipv6_route_add(struct pico_ip6 address, struct pico_ip6 netmask, struct pico_ip6 gateway, int metric, struct pico_ipv6_link *link); +int pico_ipv6_route_del(struct pico_ip6 address, struct pico_ip6 netmask, struct pico_ip6 gateway, int metric, struct pico_ipv6_link *link); +void pico_ipv6_unreachable(struct pico_frame *f, uint8_t code); + +struct pico_ipv6_link *pico_ipv6_link_add(struct pico_device *dev, struct pico_ip6 address, struct pico_ip6 netmask); +int pico_ipv6_link_del(struct pico_device *dev, struct pico_ip6 address); +int pico_ipv6_cleanup_links(struct pico_device *dev); +struct pico_ipv6_link *pico_ipv6_link_istentative(struct pico_ip6 *address); +struct pico_ipv6_link *pico_ipv6_link_get(struct pico_ip6 *address); +struct pico_device *pico_ipv6_link_find(struct pico_ip6 *address); +struct pico_ip6 pico_ipv6_route_get_gateway(struct pico_ip6 *addr); +struct pico_ip6 *pico_ipv6_source_find(const struct pico_ip6 *dst); +struct pico_device *pico_ipv6_source_dev_find(const struct pico_ip6 *dst); +struct pico_ipv6_link *pico_ipv6_link_by_dev(struct pico_device *dev); +struct pico_ipv6_link *pico_ipv6_link_by_dev_next(struct pico_device *dev, struct pico_ipv6_link *last); +struct pico_ipv6_link *pico_ipv6_global_get(struct pico_device *dev); +struct pico_ipv6_link *pico_ipv6_linklocal_get(struct pico_device *dev); +struct pico_ipv6_link *pico_ipv6_sitelocal_get(struct pico_device *dev); +struct pico_ipv6_link *pico_ipv6_prefix_configured(struct pico_ip6 *prefix); +int pico_ipv6_lifetime_set(struct pico_ipv6_link *l, pico_time expire); +void pico_ipv6_check_lifetime_expired(pico_time now, void *arg); +int pico_ipv6_dev_routing_enable(struct pico_device *dev); +int pico_ipv6_dev_routing_disable(struct pico_device *dev); +void pico_ipv6_router_down(struct pico_ip6 *address); +#endif diff --git a/net/picotcp/modules/pico_ipv6_nd.h b/net/picotcp/modules/pico_ipv6_nd.h new file mode 100644 index 0000000..f709b1e --- /dev/null +++ b/net/picotcp/modules/pico_ipv6_nd.h @@ -0,0 +1,26 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + *********************************************************************/ +#ifndef _INCLUDE_PICO_ND +#define _INCLUDE_PICO_ND +#include "pico_frame.h" + +/* RFC constants */ +#define PICO_ND_REACHABLE_TIME 30000 /* msec */ +#define PICO_ND_RETRANS_TIMER 1000 /* msec */ + +struct pico_nd_hostvars { + uint8_t routing; + uint8_t hoplimit; + pico_time basetime; + pico_time reachabletime; + pico_time retranstime; +}; + +void pico_ipv6_nd_init(void); +struct pico_eth *pico_ipv6_get_neighbor(struct pico_frame *f); +void pico_ipv6_nd_postpone(struct pico_frame *f); +int pico_ipv6_nd_recv(struct pico_frame *f); +#endif diff --git a/net/picotcp/modules/pico_mm.h b/net/picotcp/modules/pico_mm.h new file mode 100644 index 0000000..29366d0 --- /dev/null +++ b/net/picotcp/modules/pico_mm.h @@ -0,0 +1,98 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + Authors: Gustav Janssens, Jonas Van Nieuwenberg, Sam Van Den Berge + *********************************************************************/ + + +#ifndef _INCLUDE_PICO_MM +#define _INCLUDE_PICO_MM + +#include "pico_config.h" + +/* + * Memory init function, this will create a memory manager instance + * A memory_manager page will be created, along with one page of memory + * Memory can be asked for via the pico_mem_zalloc function + * More memory will be allocated to the memory manager according to its needs + * A maximum amount of memory of uint32_t memsize can be allocated + */ +void pico_mem_init(uint32_t memsize); +/* + * Memory deinit function, this will free all memory occupied by the current + * memory manager instance. + */ +void pico_mem_deinit(void); +/* + * Zero-initialized malloc function, will reserve a memory segment of length uint32_t len + * This memory will be quickly allocated in a slab of fixed size if possible + * or less optimally in the heap for a small variable size + * The fixed size of the slabs can be changed dynamically via a statistics engine + */ +void*pico_mem_zalloc(size_t len); +/* + * Free function, free a block of memory pointed to by ptr. + * Unused memory is only returned to the system's control by pico_mem_cleanup + */ +void pico_mem_free(void*ptr); +/* + * This cleanup function will be provided by the memory manager + * It can be called during processor downtime + * This function will return unused pages to the system's control + * Pages are unused if they no longer contain slabs or heap, and they have been idle for a longer time + */ +void pico_mem_cleanup(uint32_t timestamp); + + + +#ifdef PICO_SUPPORT_MM_PROFILING +/*********************************************************************************************************************** + *********************************************************************************************************************** + MEMORY PROFILING FUNCTIONS + *********************************************************************************************************************** + ***********************************************************************************************************************/ +/* General info struct */ +struct profiling_data +{ + uint32_t free_heap_space; + uint32_t free_slab_space; + uint32_t used_heap_space; + uint32_t used_slab_space; +}; + +/* + * This function fills up a struct with used and free slab and heap space in the memory manager + * The user is responsible for resource managment + */ +void pico_mem_profile_collect_data(struct profiling_data*profiling_page_struct); + +/* + * This function prints the general structure of the memory manager + * Printf in this function can be rerouted to send this data over a serial port, or to write it away to memory + */ +void pico_mem_profile_scan_data(void); + +/* + * This function returns the total size that the manager has received from the system + * This can give an indication of the total system resource commitment, but keep in mind that + * there can be many free blocks in this "used" size + * Together with pico_mem_profile_collect_data, this can give a good estimation of the total + * resource commitment + */ +uint32_t pico_mem_profile_used_size(void); + +/* + * This function returns a pointer to page 0, the main memory manager housekeeping (struct pico_mem_manager). + * This can be used to collect data about the memory in user defined functions. + * Use with care! + */ +void*pico_mem_profile_manager(void); + +/* + * paramter manager is a pointer to a struct pico_mem_manager + */ +void pico_mem_init_profiling(void*manager, uint32_t memsize); +#endif /* PICO_SUPPORT_MM_PROFILING */ + +#endif /* _INCLUDE_PICO_MM */ diff --git a/net/picotcp/modules/pico_nat.h b/net/picotcp/modules/pico_nat.h new file mode 100644 index 0000000..5237501 --- /dev/null +++ b/net/picotcp/modules/pico_nat.h @@ -0,0 +1,90 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + Authors: Kristof Roelants, Simon Maes, Brecht Van Cauwenberghe + *********************************************************************/ + +#ifndef INCLUDE_PICO_NAT +#define INCLUDE_PICO_NAT +#include "pico_frame.h" + +#define PICO_NAT_PORT_FORWARD_DEL 0 +#define PICO_NAT_PORT_FORWARD_ADD 1 + +#ifdef PICO_SUPPORT_NAT +void pico_ipv4_nat_print_table(void); +int pico_ipv4_nat_find(uint16_t nat_port, struct pico_ip4 *src_addr, uint16_t src_port, uint8_t proto); +int pico_ipv4_port_forward(struct pico_ip4 nat_addr, uint16_t nat_port, struct pico_ip4 src_addr, uint16_t src_port, uint8_t proto, uint8_t flag); + +int pico_ipv4_nat_inbound(struct pico_frame *f, struct pico_ip4 *link_addr); +int pico_ipv4_nat_outbound(struct pico_frame *f, struct pico_ip4 *link_addr); +int pico_ipv4_nat_enable(struct pico_ipv4_link *link); +int pico_ipv4_nat_disable(void); +int pico_ipv4_nat_is_enabled(struct pico_ip4 *link_addr); +#else + +#define pico_ipv4_nat_print_table() do {} while(0) +static inline int pico_ipv4_nat_inbound(struct pico_frame *f, struct pico_ip4 *link_addr) +{ + (void)f; + (void)link_addr; + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + +static inline int pico_ipv4_nat_outbound(struct pico_frame *f, struct pico_ip4 *link_addr) +{ + (void)f; + (void)link_addr; + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + +static inline int pico_ipv4_nat_enable(struct pico_ipv4_link *link) +{ + (void)link; + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + +static inline int pico_ipv4_nat_disable(void) +{ + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + +static inline int pico_ipv4_nat_is_enabled(struct pico_ip4 *link_addr) +{ + (void)link_addr; + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + +static inline int pico_ipv4_nat_find(uint16_t nat_port, struct pico_ip4 *src_addr, uint16_t src_port, uint8_t proto) +{ + (void)nat_port; + (void)src_addr; + (void)src_port; + (void)proto; + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + +static inline int pico_ipv4_port_forward(struct pico_ip4 nat_addr, uint16_t nat_port, struct pico_ip4 src_addr, uint16_t src_port, uint8_t proto, uint8_t flag) +{ + (void)nat_addr; + (void)nat_port; + (void)src_addr; + (void)src_port; + (void)proto; + (void)flag; + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} +#endif + +#endif /* _INCLUDE_PICO_NAT */ + diff --git a/net/picotcp/modules/pico_olsr.h b/net/picotcp/modules/pico_olsr.h new file mode 100644 index 0000000..11c3bf0 --- /dev/null +++ b/net/picotcp/modules/pico_olsr.h @@ -0,0 +1,32 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + Authors: Daniele Lacamera + *********************************************************************/ +#ifndef PICO_OLSR_H +#define PICO_OLSR_H + + +/* Objects */ +struct olsr_route_entry +{ + struct olsr_route_entry *next; + uint32_t time_left; + struct pico_ip4 destination; + struct olsr_route_entry *gateway; + struct pico_device *iface; + uint16_t metric; + uint8_t link_type; + struct olsr_route_entry *children; + uint16_t ansn; + uint16_t seq; + uint8_t lq, nlq; + uint8_t *advertised_tc; +}; + + +void pico_olsr_init(void); +int pico_olsr_add(struct pico_device *dev); +struct olsr_route_entry *olsr_get_ethentry(struct pico_device *vif); +#endif diff --git a/net/picotcp/modules/pico_socket_tcp.h b/net/picotcp/modules/pico_socket_tcp.h new file mode 100644 index 0000000..6479103 --- /dev/null +++ b/net/picotcp/modules/pico_socket_tcp.h @@ -0,0 +1,33 @@ +#ifndef PICO_SOCKET_TCP_H +#define PICO_SOCKET_TCP_H +#include "pico_socket.h" + +#ifdef PICO_SUPPORT_TCP + +/* Functions/macros: conditional! */ + +# define IS_NAGLE_ENABLED(s) (!(!(!(s->opt_flags & (1 << PICO_SOCKET_OPT_TCPNODELAY))))) +int pico_setsockopt_tcp(struct pico_socket *s, int option, void *value); +int pico_getsockopt_tcp(struct pico_socket *s, int option, void *value); +int pico_socket_tcp_deliver(struct pico_sockport *sp, struct pico_frame *f); +void pico_socket_tcp_delete(struct pico_socket *s); +void pico_socket_tcp_cleanup(struct pico_socket *sock); +struct pico_socket *pico_socket_tcp_open(uint16_t family); +int pico_socket_tcp_read(struct pico_socket *s, void *buf, uint32_t len); +void transport_flags_update(struct pico_frame *, struct pico_socket *); + +#else +# define pico_getsockopt_tcp(...) (-1) +# define pico_setsockopt_tcp(...) (-1) +# define pico_socket_tcp_deliver(...) (-1) +# define IS_NAGLE_ENABLED(s) (0) +# define pico_socket_tcp_delete(...) do {} while(0) +# define pico_socket_tcp_cleanup(...) do {} while(0) +# define pico_socket_tcp_open(f) (NULL) +# define pico_socket_tcp_read(...) (-1) +# define transport_flags_update(...) do {} while(0) + +#endif + + +#endif diff --git a/net/picotcp/modules/pico_socket_udp.c b/net/picotcp/modules/pico_socket_udp.c new file mode 100644 index 0000000..ea7e9e1 --- /dev/null +++ b/net/picotcp/modules/pico_socket_udp.c @@ -0,0 +1,250 @@ +#include "pico_config.h" +#include "pico_socket.h" +#include "pico_udp.h" +#include "pico_socket_multicast.h" +#include "pico_ipv4.h" +#include "pico_ipv6.h" +#include "pico_socket_udp.h" + +#define UDP_FRAME_OVERHEAD (sizeof(struct pico_frame)) + + +struct pico_socket *pico_socket_udp_open(void) +{ + struct pico_socket *s = NULL; +#ifdef PICO_SUPPORT_UDP + s = pico_udp_open(); + if (!s) { + pico_err = PICO_ERR_ENOMEM; + return NULL; + } + + s->proto = &pico_proto_udp; + s->q_in.overhead = UDP_FRAME_OVERHEAD; + s->q_out.overhead = UDP_FRAME_OVERHEAD; +#endif + return s; +} + + +#ifdef PICO_SUPPORT_IPV4 +static inline int pico_socket_udp_deliver_ipv4_mcast_initial_checks(struct pico_socket *s, struct pico_frame *f) +{ + struct pico_ip4 p_dst; + struct pico_ipv4_hdr *ip4hdr; + + ip4hdr = (struct pico_ipv4_hdr*)(f->net_hdr); + p_dst.addr = ip4hdr->dst.addr; + if (pico_ipv4_is_multicast(p_dst.addr) && (pico_socket_mcast_filter(s, (union pico_address *)&ip4hdr->dst, (union pico_address *)&ip4hdr->src) < 0)) + return -1; + + + if ((pico_ipv4_link_get(&ip4hdr->src)) && (PICO_SOCKET_GETOPT(s, PICO_SOCKET_OPT_MULTICAST_LOOP) == 0u)) { + /* Datagram from ourselves, Loop disabled, discarding. */ + return -1; + } + + return 0; +} + + +static int pico_socket_udp_deliver_ipv4_mcast(struct pico_socket *s, struct pico_frame *f) +{ + struct pico_ip4 s_local; + struct pico_frame *cpy; + struct pico_device *dev = pico_ipv4_link_find(&s->local_addr.ip4); + + s_local.addr = s->local_addr.ip4.addr; + + if (pico_socket_udp_deliver_ipv4_mcast_initial_checks(s, f) < 0) + return 0; + + + + if ((s_local.addr == PICO_IPV4_INADDR_ANY) || /* If our local ip is ANY, or.. */ + (dev == f->dev)) { /* the source of the bcast packet is a neighbor... */ + cpy = pico_frame_copy(f); + if (!cpy) + return -1; + + if (pico_enqueue(&s->q_in, cpy) > 0) { + if (s->wakeup) + s->wakeup(PICO_SOCK_EV_RD, s); + } + else + pico_frame_discard(cpy); + } + + return 0; +} + +static int pico_socket_udp_deliver_ipv4_unicast(struct pico_socket *s, struct pico_frame *f) +{ + struct pico_frame *cpy; + /* Either local socket is ANY, or matches dst */ + cpy = pico_frame_copy(f); + if (!cpy) + return -1; + + if (pico_enqueue(&s->q_in, cpy) > 0) { + if (s->wakeup) + s->wakeup(PICO_SOCK_EV_RD, s); + } else { + pico_frame_discard(cpy); + } + + return 0; +} + + +static int pico_socket_udp_deliver_ipv4(struct pico_socket *s, struct pico_frame *f) +{ + int ret = 0; + struct pico_ip4 s_local, p_dst; + struct pico_ipv4_hdr *ip4hdr; + ip4hdr = (struct pico_ipv4_hdr*)(f->net_hdr); + s_local.addr = s->local_addr.ip4.addr; + p_dst.addr = ip4hdr->dst.addr; + if ((pico_ipv4_is_broadcast(p_dst.addr)) || pico_ipv4_is_multicast(p_dst.addr)) { + ret = pico_socket_udp_deliver_ipv4_mcast(s, f); + } else if ((s_local.addr == PICO_IPV4_INADDR_ANY) || (s_local.addr == p_dst.addr)) { + ret = pico_socket_udp_deliver_ipv4_unicast(s, f); + } + + pico_frame_discard(f); + return ret; +} +#endif + +#ifdef PICO_SUPPORT_IPV6 +static inline int pico_socket_udp_deliver_ipv6_mcast(struct pico_socket *s, struct pico_frame *f) +{ + struct pico_ipv6_hdr *ip6hdr; + struct pico_frame *cpy; + struct pico_device *dev = pico_ipv6_link_find(&s->local_addr.ip6); + + ip6hdr = (struct pico_ipv6_hdr*)(f->net_hdr); + + if ((pico_ipv6_link_get(&ip6hdr->src)) && (PICO_SOCKET_GETOPT(s, PICO_SOCKET_OPT_MULTICAST_LOOP) == 0u)) { + /* Datagram from ourselves, Loop disabled, discarding. */ + return 0; + } + + + if (pico_ipv6_is_unspecified(s->local_addr.ip6.addr) || /* If our local ip is ANY, or.. */ + (dev == f->dev)) { /* the source of the bcast packet is a neighbor... */ + cpy = pico_frame_copy(f); + if (!cpy) + return -1; + + if (pico_enqueue(&s->q_in, cpy) > 0) { + if (s->wakeup) + s->wakeup(PICO_SOCK_EV_RD, s); + } + else + pico_frame_discard(cpy); + } + + return 0; +} + +static int pico_socket_udp_deliver_ipv6(struct pico_socket *s, struct pico_frame *f) +{ + struct pico_ip6 s_local, p_dst; + struct pico_ipv6_hdr *ip6hdr; + struct pico_frame *cpy; + ip6hdr = (struct pico_ipv6_hdr*)(f->net_hdr); + s_local = s->local_addr.ip6; + p_dst = ip6hdr->dst; + if ((pico_ipv6_is_multicast(p_dst.addr))) { + return pico_socket_udp_deliver_ipv6_mcast(s, f); + } else if (pico_ipv6_is_unspecified(s->local_addr.ip6.addr) || (pico_ipv6_compare(&s_local, &p_dst) == 0)) + { /* Either local socket is ANY, or matches dst */ + cpy = pico_frame_copy(f); + if (!cpy) + return -1; + + if (pico_enqueue(&s->q_in, cpy) > 0) { + if (s->wakeup) + s->wakeup(PICO_SOCK_EV_RD, s); + } + } + + pico_frame_discard(f); + return 0; +} +#endif + + +int pico_socket_udp_deliver(struct pico_sockport *sp, struct pico_frame *f) +{ + struct pico_tree_node *index = NULL; + struct pico_tree_node *_tmp; + struct pico_socket *s = NULL; + pico_err = PICO_ERR_EPROTONOSUPPORT; + #ifdef PICO_SUPPORT_UDP + pico_err = PICO_ERR_NOERR; + pico_tree_foreach_safe(index, &sp->socks, _tmp){ + s = index->keyValue; + if (IS_IPV4(f)) { /* IPV4 */ +#ifdef PICO_SUPPORT_IPV4 + return pico_socket_udp_deliver_ipv4(s, f); +#endif + } else if (IS_IPV6(f)) { +#ifdef PICO_SUPPORT_IPV6 + return pico_socket_udp_deliver_ipv6(s, f); +#endif + } else { + /* something wrong in the packet header*/ + } + } /* FOREACH */ + pico_frame_discard(f); + if (s) + return 0; + + pico_err = PICO_ERR_ENXIO; + #endif + return -1; +} + +int pico_setsockopt_udp(struct pico_socket *s, int option, void *value) +{ + switch(option) { + case PICO_SOCKET_OPT_RCVBUF: + s->q_in.max_size = (*(uint32_t*)value); + return 0; + case PICO_SOCKET_OPT_SNDBUF: + s->q_out.max_size = (*(uint32_t*)value); + return 0; + } + + /* switch's default */ +#ifdef PICO_SUPPORT_MCAST + return pico_setsockopt_mcast(s, option, value); +#else + pico_err = PICO_ERR_EINVAL; + return -1; +#endif +} + +int pico_getsockopt_udp(struct pico_socket *s, int option, void *value) +{ + uint32_t *val = (uint32_t *)value; + switch(option) { + case PICO_SOCKET_OPT_RCVBUF: + *val = s->q_in.max_size; + return 0; + case PICO_SOCKET_OPT_SNDBUF: + *val = s->q_out.max_size; + return 0; + } + + /* switch's default */ +#ifdef PICO_SUPPORT_MCAST + return pico_getsockopt_mcast(s, option, value); +#else + pico_err = PICO_ERR_EINVAL; + return -1; +#endif +} + diff --git a/net/picotcp/modules/pico_socket_udp.h b/net/picotcp/modules/pico_socket_udp.h new file mode 100644 index 0000000..6b3a4c9 --- /dev/null +++ b/net/picotcp/modules/pico_socket_udp.h @@ -0,0 +1,19 @@ +#ifndef PICO_SOCKET_UDP_H +#define PICO_SOCKET_UDP_H + +struct pico_socket *pico_socket_udp_open(void); +int pico_socket_udp_deliver(struct pico_sockport *sp, struct pico_frame *f); + + +#ifdef PICO_SUPPORT_UDP +int pico_setsockopt_udp(struct pico_socket *s, int option, void *value); +int pico_getsockopt_udp(struct pico_socket *s, int option, void *value); +# define pico_socket_udp_recv(s, buf, len, addr, port) pico_udp_recv(s, buf, len, addr, port, NULL) +#else +# define pico_socket_udp_recv(...) (0) +# define pico_getsockopt_udp(...) (-1) +# define pico_setsockopt_udp(...) (-1) +#endif + + +#endif diff --git a/net/picotcp/modules/pico_tcp.h b/net/picotcp/modules/pico_tcp.h new file mode 100644 index 0000000..d5003a8 --- /dev/null +++ b/net/picotcp/modules/pico_tcp.h @@ -0,0 +1,102 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + *********************************************************************/ +#ifndef INCLUDE_PICO_TCP +#define INCLUDE_PICO_TCP +#include "pico_addressing.h" +#include "pico_protocol.h" +#include "pico_socket.h" + +extern struct pico_protocol pico_proto_tcp; + +PACKED_STRUCT_DEF pico_tcp_hdr { + struct pico_trans trans; + uint32_t seq; + uint32_t ack; + uint8_t len; + uint8_t flags; + uint16_t rwnd; + uint16_t crc; + uint16_t urgent; +}; + +PACKED_STRUCT_DEF tcp_pseudo_hdr_ipv4 +{ + struct pico_ip4 src; + struct pico_ip4 dst; + uint16_t tcp_len; + uint8_t res; + uint8_t proto; +}; + +#define PICO_TCPHDR_SIZE 20 +#define PICO_SIZE_TCPOPT_SYN 20 +#define PICO_SIZE_TCPHDR (uint32_t)(sizeof(struct pico_tcp_hdr)) + +/* TCP options */ +#define PICO_TCP_OPTION_END 0x00 +#define PICO_TCPOPTLEN_END 1u +#define PICO_TCP_OPTION_NOOP 0x01 +#define PICO_TCPOPTLEN_NOOP 1 +#define PICO_TCP_OPTION_MSS 0x02 +#define PICO_TCPOPTLEN_MSS 4 +#define PICO_TCP_OPTION_WS 0x03 +#define PICO_TCPOPTLEN_WS 3u +#define PICO_TCP_OPTION_SACK_OK 0x04 +#define PICO_TCPOPTLEN_SACK_OK 2 +#define PICO_TCP_OPTION_SACK 0x05 +#define PICO_TCPOPTLEN_SACK 2 /* Plus the block */ +#define PICO_TCP_OPTION_TIMESTAMP 0x08 +#define PICO_TCPOPTLEN_TIMESTAMP 10u + +/* TCP flags */ +#define PICO_TCP_FIN 0x01u +#define PICO_TCP_SYN 0x02u +#define PICO_TCP_RST 0x04u +#define PICO_TCP_PSH 0x08u +#define PICO_TCP_ACK 0x10u +#define PICO_TCP_URG 0x20u +#define PICO_TCP_ECN 0x40u +#define PICO_TCP_CWR 0x80u + +#define PICO_TCP_SYNACK (PICO_TCP_SYN | PICO_TCP_ACK) +#define PICO_TCP_PSHACK (PICO_TCP_PSH | PICO_TCP_ACK) +#define PICO_TCP_FINACK (PICO_TCP_FIN | PICO_TCP_ACK) +#define PICO_TCP_FINPSHACK (PICO_TCP_FIN | PICO_TCP_PSH | PICO_TCP_ACK) +#define PICO_TCP_RSTACK (PICO_TCP_RST | PICO_TCP_ACK) + + +PACKED_STRUCT_DEF pico_tcp_option +{ + uint8_t kind; + uint8_t len; +}; + +struct pico_socket *pico_tcp_open(uint16_t family); +uint32_t pico_tcp_read(struct pico_socket *s, void *buf, uint32_t len); +int pico_tcp_initconn(struct pico_socket *s); +int pico_tcp_input(struct pico_socket *s, struct pico_frame *f); +uint16_t pico_tcp_checksum(struct pico_frame *f); +uint16_t pico_tcp_checksum_ipv4(struct pico_frame *f); +#ifdef PICO_SUPPORT_IPV6 +uint16_t pico_tcp_checksum_ipv6(struct pico_frame *f); +#endif +uint16_t pico_tcp_overhead(struct pico_socket *s); +int pico_tcp_output(struct pico_socket *s, int loop_score); +int pico_tcp_queue_in_is_empty(struct pico_socket *s); +int pico_tcp_reply_rst(struct pico_frame *f); +void pico_tcp_cleanup_queues(struct pico_socket *sck); +void pico_tcp_notify_closing(struct pico_socket *sck); +void pico_tcp_flags_update(struct pico_frame *f, struct pico_socket *s); +int pico_tcp_set_bufsize_in(struct pico_socket *s, uint32_t value); +int pico_tcp_set_bufsize_out(struct pico_socket *s, uint32_t value); +int pico_tcp_get_bufsize_in(struct pico_socket *s, uint32_t *value); +int pico_tcp_get_bufsize_out(struct pico_socket *s, uint32_t *value); +uint16_t pico_tcp_get_socket_mss(struct pico_socket *s); +int pico_tcp_check_listen_close(struct pico_socket *s); + +#endif diff --git a/net/picotcp/modules/pico_udp.c b/net/picotcp/modules/pico_udp.c new file mode 100644 index 0000000..b7c61e4 --- /dev/null +++ b/net/picotcp/modules/pico_udp.c @@ -0,0 +1,216 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + Authors: Daniele Lacamera + *********************************************************************/ + + +#include "pico_udp.h" +#include "pico_config.h" +#include "pico_eth.h" +#include "pico_socket.h" +#include "pico_stack.h" + +#define UDP_FRAME_OVERHEAD (sizeof(struct pico_frame)) +#define udp_dbg(...) do {} while(0) + +/* Queues */ +static struct pico_queue udp_in = { + 0 +}; +static struct pico_queue udp_out = { + 0 +}; + + +/* Functions */ + +uint16_t pico_udp_checksum_ipv4(struct pico_frame *f) +{ + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + struct pico_udp_hdr *udp_hdr = (struct pico_udp_hdr *) f->transport_hdr; + struct pico_socket *s = f->sock; + struct pico_ipv4_pseudo_hdr pseudo; + + if (s) { + /* Case of outgoing frame */ + udp_dbg("UDP CRC: on outgoing frame\n"); + pseudo.src.addr = s->local_addr.ip4.addr; + pseudo.dst.addr = s->remote_addr.ip4.addr; + } else { + /* Case of incomming frame */ + udp_dbg("UDP CRC: on incomming frame\n"); + pseudo.src.addr = hdr->src.addr; + pseudo.dst.addr = hdr->dst.addr; + } + + pseudo.zeros = 0; + pseudo.proto = PICO_PROTO_UDP; + pseudo.len = short_be(f->transport_len); + + return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv4_pseudo_hdr), udp_hdr, f->transport_len); +} + +#ifdef PICO_SUPPORT_IPV6 +uint16_t pico_udp_checksum_ipv6(struct pico_frame *f) +{ + struct pico_ipv6_hdr *ipv6_hdr = (struct pico_ipv6_hdr *)f->net_hdr; + struct pico_udp_hdr *udp_hdr = (struct pico_udp_hdr *)f->transport_hdr; + struct pico_ipv6_pseudo_hdr pseudo = { + .src = {{0}}, .dst = {{0}}, .len = 0, .zero = {0}, .nxthdr = 0 + }; + struct pico_socket *s = f->sock; + struct pico_remote_endpoint *remote_endpoint = (struct pico_remote_endpoint *)f->info; + + /* XXX If the IPv6 packet contains a Routing header, the Destination + * Address used in the pseudo-header is that of the final destination */ + if (s) { + /* Case of outgoing frame */ + pseudo.src = s->local_addr.ip6; + if (remote_endpoint) + pseudo.dst = remote_endpoint->remote_addr.ip6; + else + pseudo.dst = s->remote_addr.ip6; + } else { + /* Case of incomming frame */ + pseudo.src = ipv6_hdr->src; + pseudo.dst = ipv6_hdr->dst; + } + + pseudo.len = long_be(f->transport_len); + pseudo.nxthdr = PICO_PROTO_UDP; + + return pico_dualbuffer_checksum(&pseudo, sizeof(struct pico_ipv6_pseudo_hdr), udp_hdr, f->transport_len); +} +#endif + + + +static int pico_udp_process_out(struct pico_protocol *self, struct pico_frame *f) +{ + IGNORE_PARAMETER(self); + return (int)pico_network_send(f); +} + +static int pico_udp_push(struct pico_protocol *self, struct pico_frame *f) +{ + struct pico_udp_hdr *hdr = (struct pico_udp_hdr *) f->transport_hdr; + struct pico_remote_endpoint *remote_endpoint = (struct pico_remote_endpoint *) f->info; + + /* this (fragmented) frame should contain a transport header */ + if (f->transport_hdr != f->payload) { + hdr->trans.sport = f->sock->local_port; + if (remote_endpoint) { + hdr->trans.dport = remote_endpoint->remote_port; + } else { + hdr->trans.dport = f->sock->remote_port; + } + + hdr->len = short_be(f->transport_len); + + /* do not perform CRC validation. If you want to, a system needs to be + implemented to calculate the CRC over the total payload of a + fragmented payload + */ + hdr->crc = 0; + } + + if (pico_enqueue(self->q_out, f) > 0) { + return f->payload_len; + } else { + return 0; + } +} + +/* Interface: protocol definition */ +struct pico_protocol pico_proto_udp = { + .name = "udp", + .proto_number = PICO_PROTO_UDP, + .layer = PICO_LAYER_TRANSPORT, + .process_in = pico_transport_process_in, + .process_out = pico_udp_process_out, + .push = pico_udp_push, + .q_in = &udp_in, + .q_out = &udp_out, +}; + + + +struct pico_socket *pico_udp_open(void) +{ + struct pico_socket_udp *u = PICO_ZALLOC(sizeof(struct pico_socket_udp)); + if (!u) + return NULL; + + u->mode = PICO_UDP_MODE_UNICAST; + +#ifdef PICO_SUPPORT_MCAST + u->mc_ttl = PICO_IP_DEFAULT_MULTICAST_TTL; + /* enable multicast loopback by default */ + u->sock.opt_flags |= (1 << PICO_SOCKET_OPT_MULTICAST_LOOP); +#endif + + return &u->sock; +} + +static void pico_udp_get_msginfo(struct pico_frame *f, struct pico_msginfo *msginfo) +{ + msginfo->dev = f->dev; + if (!msginfo || !f->net_hdr) + return; + + if (IS_IPV4(f)) { /* IPV4 */ +#ifdef PICO_SUPPORT_IPV4 + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)(f->net_hdr); + msginfo->ttl = hdr->ttl; + msginfo->tos = hdr->tos; +#endif + } else { +#ifdef PICO_SUPPORT_IPV6 + struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)(f->net_hdr); + msginfo->ttl = hdr->hop; + msginfo->tos = (hdr->vtf >> 20) & 0xFF; /* IPv6 traffic class */ +#endif + } +} + +uint16_t pico_udp_recv(struct pico_socket *s, void *buf, uint16_t len, void *src, uint16_t *port, struct pico_msginfo *msginfo) +{ + struct pico_frame *f = pico_queue_peek(&s->q_in); + if (f) { + if(!f->payload_len) { + f->payload = f->transport_hdr + sizeof(struct pico_udp_hdr); + f->payload_len = (uint16_t)(f->transport_len - sizeof(struct pico_udp_hdr)); + } + + udp_dbg("expected: %d, got: %d\n", len, f->payload_len); + if (src) + pico_store_network_origin(src, f); + + if (port) { + struct pico_trans *hdr = (struct pico_trans *)f->transport_hdr; + *port = hdr->sport; + } + + if (msginfo) { + pico_udp_get_msginfo(f, msginfo); + } + + if (f->payload_len > len) { + memcpy(buf, f->payload, len); + f->payload += len; + f->payload_len = (uint16_t)(f->payload_len - len); + return len; + } else { + uint16_t ret = f->payload_len; + memcpy(buf, f->payload, f->payload_len); + f = pico_dequeue(&s->q_in); + pico_frame_discard(f); + return ret; + } + } else return 0; +} + diff --git a/net/picotcp/modules/pico_udp.h b/net/picotcp/modules/pico_udp.h new file mode 100644 index 0000000..80b97a9 --- /dev/null +++ b/net/picotcp/modules/pico_udp.h @@ -0,0 +1,45 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + *********************************************************************/ +#ifndef INCLUDE_PICO_UDP +#define INCLUDE_PICO_UDP +#include "pico_addressing.h" +#include "pico_protocol.h" +#include "pico_socket.h" +#define PICO_UDP_MODE_UNICAST 0x01 +#define PICO_UDP_MODE_MULTICAST 0x02 +#define PICO_UDP_MODE_BROADCAST 0xFF + +struct pico_socket_udp +{ + struct pico_socket sock; + int mode; + uint8_t mc_ttl; /* Multicasting TTL */ +}; + + +extern struct pico_protocol pico_proto_udp; + +PACKED_STRUCT_DEF pico_udp_hdr { + struct pico_trans trans; + uint16_t len; + uint16_t crc; +}; +#define PICO_UDPHDR_SIZE 8 + +struct pico_socket *pico_udp_open(void); +uint16_t pico_udp_recv(struct pico_socket *s, void *buf, uint16_t len, void *src, uint16_t *port, struct pico_msginfo *msginfo); +uint16_t pico_udp_checksum_ipv4(struct pico_frame *f); + +#ifdef PICO_SUPPORT_IPV6 +uint16_t pico_udp_checksum_ipv6(struct pico_frame *f); +#endif + + +int pico_udp_setsockopt(struct pico_socket *s, int option, void *value); + +#endif diff --git a/net/picotcp/stack/pico_device.c b/net/picotcp/stack/pico_device.c new file mode 100644 index 0000000..224b37f --- /dev/null +++ b/net/picotcp/stack/pico_device.c @@ -0,0 +1,408 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + Authors: Daniele Lacamera + *********************************************************************/ + +#include "pico_config.h" +#include "pico_device.h" +#include "pico_stack.h" +#include "pico_protocol.h" +#include "pico_tree.h" +#include "pico_ipv6.h" +#include "pico_ipv4.h" +#include "pico_icmp6.h" +#include "pico_eth.h" +#define PICO_DEVICE_DEFAULT_MTU (1500) + +struct pico_devices_rr_info { + struct pico_tree_node *node_in, *node_out; +}; + +static struct pico_devices_rr_info Devices_rr_info = { + NULL, NULL +}; + +static int pico_dev_cmp(void *ka, void *kb) +{ + struct pico_device *a = ka, *b = kb; + if (a->hash < b->hash) + return -1; + + if (a->hash > b->hash) + return 1; + + return 0; +} + +PICO_TREE_DECLARE(Device_tree, pico_dev_cmp); + +#ifdef PICO_SUPPORT_IPV6 +static void device_init_ipv6_final(struct pico_device *dev, struct pico_ip6 *linklocal) +{ + dev->hostvars.basetime = PICO_ND_REACHABLE_TIME; + /* RFC 4861 $6.3.2 value between 0.5 and 1.5 times basetime */ + dev->hostvars.reachabletime = ((5 + (pico_rand() % 10)) * PICO_ND_REACHABLE_TIME) / 10; + dev->hostvars.retranstime = PICO_ND_RETRANS_TIMER; + pico_icmp6_router_solicitation(dev, linklocal); + dev->hostvars.hoplimit = PICO_IPV6_DEFAULT_HOP; +} + +struct pico_ipv6_link *pico_ipv6_link_add_local(struct pico_device *dev, const struct pico_ip6 *prefix) +{ + struct pico_ip6 newaddr; + struct pico_ip6 netmask64 = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + struct pico_ipv6_link *link; + memcpy(newaddr.addr, prefix->addr, PICO_SIZE_IP6); + /* modified EUI-64 + invert universal/local bit */ + newaddr.addr[8] = (dev->eth->mac.addr[0] ^ 0x02); + newaddr.addr[9] = dev->eth->mac.addr[1]; + newaddr.addr[10] = dev->eth->mac.addr[2]; + newaddr.addr[11] = 0xff; + newaddr.addr[12] = 0xfe; + newaddr.addr[13] = dev->eth->mac.addr[3]; + newaddr.addr[14] = dev->eth->mac.addr[4]; + newaddr.addr[15] = dev->eth->mac.addr[5]; + link = pico_ipv6_link_add(dev, newaddr, netmask64); + if (link) { + device_init_ipv6_final(dev, &newaddr); + } + + return link; +} +#endif + +static int device_init_mac(struct pico_device *dev, uint8_t *mac) +{ + #ifdef PICO_SUPPORT_IPV6 + struct pico_ip6 linklocal = {{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xff, 0xfe, 0xaa, 0xaa, 0xaa}}; + #endif + dev->eth = PICO_ZALLOC(sizeof(struct pico_ethdev)); + if (dev->eth) { + memcpy(dev->eth->mac.addr, mac, PICO_SIZE_ETH); + #ifdef PICO_SUPPORT_IPV6 + if (pico_ipv6_link_add_local(dev, &linklocal) == NULL) { + PICO_FREE(dev->q_in); + PICO_FREE(dev->q_out); + PICO_FREE(dev->eth); + return -1; + } + + #endif + } else { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + return 0; +} + +int pico_device_ipv6_random_ll(struct pico_device *dev) +{ + #ifdef PICO_SUPPORT_IPV6 + struct pico_ip6 linklocal = {{0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xff, 0xfe, 0xaa, 0xaa, 0xaa}}; + struct pico_ip6 netmask6 = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; + uint32_t len = (uint32_t)strlen(dev->name); + if (strcmp(dev->name, "loop")) { + do { + /* privacy extension + unset universal/local and individual/group bit */ + len = pico_rand(); + linklocal.addr[8] = (uint8_t)((len & 0xffu) & (uint8_t)(~0x03)); + linklocal.addr[9] = (uint8_t)(len >> 8); + linklocal.addr[10] = (uint8_t)(len >> 16); + linklocal.addr[11] = (uint8_t)(len >> 24); + len = pico_rand(); + linklocal.addr[12] = (uint8_t)len; + linklocal.addr[13] = (uint8_t)(len >> 8); + linklocal.addr[14] = (uint8_t)(len >> 16); + linklocal.addr[15] = (uint8_t)(len >> 24); + pico_rand_feed(dev->hash); + } while (pico_ipv6_link_get(&linklocal)); + + if (pico_ipv6_link_add(dev, linklocal, netmask6) == NULL) { + return -1; + } + } + + #endif + return 0; +} + +static int device_init_nomac(struct pico_device *dev) +{ + if (pico_device_ipv6_random_ll(dev) < 0) { + PICO_FREE(dev->q_in); + PICO_FREE(dev->q_out); + return -1; + } + + dev->eth = NULL; + return 0; +} + +int pico_device_init(struct pico_device *dev, const char *name, uint8_t *mac) +{ + + uint32_t len = (uint32_t)strlen(name); + int ret = 0; + if(len > MAX_DEVICE_NAME) + len = MAX_DEVICE_NAME; + + memcpy(dev->name, name, len); + dev->hash = pico_hash(dev->name, len); + + Devices_rr_info.node_in = NULL; + Devices_rr_info.node_out = NULL; + dev->q_in = PICO_ZALLOC(sizeof(struct pico_queue)); + if (!dev->q_in) + return -1; + + dev->q_out = PICO_ZALLOC(sizeof(struct pico_queue)); + if (!dev->q_out) { + PICO_FREE(dev->q_in); + return -1; + } + + pico_tree_insert(&Device_tree, dev); + if (!dev->mtu) + dev->mtu = PICO_DEVICE_DEFAULT_MTU; + + if (mac) { + ret = device_init_mac(dev, mac); + } else { + ret = device_init_nomac(dev); + } + + return ret; +} + +static void pico_queue_destroy(struct pico_queue *q) +{ + if (q) { + pico_queue_empty(q); + PICO_FREE(q); + } +} + +void pico_device_destroy(struct pico_device *dev) +{ + if (dev->destroy) + dev->destroy(dev); + + pico_queue_destroy(dev->q_in); + pico_queue_destroy(dev->q_out); + + if (dev->eth) + PICO_FREE(dev->eth); + +#ifdef PICO_SUPPORT_IPV4 + pico_ipv4_cleanup_links(dev); +#endif +#ifdef PICO_SUPPORT_IPV6 + pico_ipv6_cleanup_links(dev); +#endif + pico_tree_delete(&Device_tree, dev); + + Devices_rr_info.node_in = NULL; + Devices_rr_info.node_out = NULL; + PICO_FREE(dev); +} + +static int check_dev_serve_interrupt(struct pico_device *dev, int loop_score) +{ + if ((dev->__serving_interrupt) && (dev->dsr)) { + /* call dsr routine */ + loop_score = dev->dsr(dev, loop_score); + } + + return loop_score; +} + +static int check_dev_serve_polling(struct pico_device *dev, int loop_score) +{ + if (dev->poll) { + loop_score = dev->poll(dev, loop_score); + } + + return loop_score; +} + +static int devloop_in(struct pico_device *dev, int loop_score) +{ + struct pico_frame *f; + while(loop_score > 0) { + if (dev->q_in->frames <= 0) + break; + + /* Receive */ + f = pico_dequeue(dev->q_in); + if (f) { + if (dev->eth) { + f->datalink_hdr = f->buffer; + (void)pico_ethernet_receive(f); + } else { + f->net_hdr = f->buffer; + pico_network_receive(f); + } + + loop_score--; + } + } + return loop_score; +} + +static int devloop_sendto_dev(struct pico_device *dev, struct pico_frame *f) +{ + + if (dev->eth) { + /* Ethernet: pass management of the frame to the pico_ethernet_send() rdv function */ + return pico_ethernet_send(f); + } else { + /* non-ethernet: no post-processing needed */ + return (dev->send(dev, f->start, (int)f->len) <= 0); /* Return 0 upon success, which is dev->send() > 0 */ + } +} + +static int devloop_out(struct pico_device *dev, int loop_score) +{ + struct pico_frame *f; + while(loop_score > 0) { + if (dev->q_out->frames <= 0) + break; + + /* Device dequeue + send */ + f = pico_queue_peek(dev->q_out); + if (!f) + break; + + if (devloop_sendto_dev(dev, f) == 0) { /* success. */ + f = pico_dequeue(dev->q_out); + pico_frame_discard(f); /* SINGLE POINT OF DISCARD for OUTGOING FRAMES */ + loop_score--; + } else + break; /* Don't discard */ + + } + return loop_score; +} + +static int devloop(struct pico_device *dev, int loop_score, int direction) +{ + /* If device supports interrupts, read the value of the condition and trigger the dsr */ + loop_score = check_dev_serve_interrupt(dev, loop_score); + + /* If device supports polling, give control. Loop score is managed internally, + * remaining loop points are returned. */ + loop_score = check_dev_serve_polling(dev, loop_score); + + if (direction == PICO_LOOP_DIR_OUT) + loop_score = devloop_out(dev, loop_score); + else + loop_score = devloop_in(dev, loop_score); + + return loop_score; +} + + +static struct pico_tree_node *pico_dev_roundrobin_start(int direction) +{ + if (Devices_rr_info.node_in == NULL) + Devices_rr_info.node_in = pico_tree_firstNode(Device_tree.root); + + if (Devices_rr_info.node_out == NULL) + Devices_rr_info.node_out = pico_tree_firstNode(Device_tree.root); + + if (direction == PICO_LOOP_DIR_IN) + return Devices_rr_info.node_in; + else + return Devices_rr_info.node_out; +} + +static void pico_dev_roundrobin_end(int direction, struct pico_tree_node *last) +{ + if (direction == PICO_LOOP_DIR_IN) + Devices_rr_info.node_in = last; + else + Devices_rr_info.node_out = last; +} + +#define DEV_LOOP_MIN 16 + +int pico_devices_loop(int loop_score, int direction) +{ + struct pico_device *start, *next; + struct pico_tree_node *next_node = pico_dev_roundrobin_start(direction); + + if (!next_node) + return loop_score; + + next = next_node->keyValue; + start = next; + + /* round-robin all devices, break if traversed all devices */ + while ((loop_score > DEV_LOOP_MIN) && (next != NULL)) { + loop_score = devloop(next, loop_score, direction); + next_node = pico_tree_next(next_node); + next = next_node->keyValue; + if (next == NULL) + { + next_node = pico_tree_firstNode(Device_tree.root); + next = next_node->keyValue; + } + + if (next == start) + break; + } + pico_dev_roundrobin_end(direction, next_node); + return loop_score; +} + +struct pico_device *pico_get_device(const char*name) +{ + struct pico_device *dev; + struct pico_tree_node *index; + pico_tree_foreach(index, &Device_tree){ + dev = index->keyValue; + if(strcmp(name, dev->name) == 0) + return dev; + } + return NULL; +} + +int32_t pico_device_broadcast(struct pico_frame *f) +{ + struct pico_tree_node *index; + int32_t ret = -1; + + pico_tree_foreach(index, &Device_tree) + { + struct pico_device *dev = index->keyValue; + if(dev != f->dev) + { + struct pico_frame *copy = pico_frame_copy(f); + + if(!copy) + break; + + copy->dev = dev; + copy->dev->send(copy->dev, copy->start, (int)copy->len); + pico_frame_discard(copy); + } + else + { + ret = f->dev->send(f->dev, f->start, (int)f->len); + } + } + return ret; +} + +int pico_device_link_state(struct pico_device *dev) +{ + if (!dev->link_state) + return 1; /* Not supported, assuming link is always up */ + + return dev->link_state(dev); +} diff --git a/net/picotcp/stack/pico_frame.c b/net/picotcp/stack/pico_frame.c new file mode 100644 index 0000000..482edfe --- /dev/null +++ b/net/picotcp/stack/pico_frame.c @@ -0,0 +1,286 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + Authors: Daniele Lacamera + *********************************************************************/ + + +#include "pico_config.h" +#include "pico_frame.h" +#include "pico_protocol.h" +#include "pico_stack.h" + +#ifdef PICO_SUPPORT_DEBUG_MEMORY +static int n_frames_allocated; +#endif + +/** frame alloc/dealloc/copy **/ +void pico_frame_discard(struct pico_frame *f) +{ + if (!f) + return; + + (*f->usage_count)--; + if (*f->usage_count <= 0) { + if (f->flags & PICO_FRAME_FLAG_EXT_USAGE_COUNTER) + PICO_FREE(f->usage_count); + +#ifdef PICO_SUPPORT_DEBUG_MEMORY + dbg("Discarded buffer @%p, caller: %p\n", f->buffer, __builtin_return_address(3)); + dbg("DEBUG MEMORY: %d frames in use.\n", --n_frames_allocated); +#endif + if (!(f->flags & PICO_FRAME_FLAG_EXT_BUFFER)) + PICO_FREE(f->buffer); + else if (f->notify_free) + f->notify_free(f->buffer); + + if (f->info) + PICO_FREE(f->info); + } + +#ifdef PICO_SUPPORT_DEBUG_MEMORY + else { + dbg("Removed frame @%p(copy), usage count now: %d\n", f, *f->usage_count); + } +#endif + PICO_FREE(f); +} + +struct pico_frame *pico_frame_copy(struct pico_frame *f) +{ + struct pico_frame *new = PICO_ZALLOC(sizeof(struct pico_frame)); + if (!new) + return NULL; + + memcpy(new, f, sizeof(struct pico_frame)); + *(new->usage_count) += 1; +#ifdef PICO_SUPPORT_DEBUG_MEMORY + dbg("Copied frame @%p, into %p, usage count now: %d\n", f, new, *new->usage_count); +#endif + new->next = NULL; + return new; +} + + +static struct pico_frame *pico_frame_do_alloc(uint32_t size, int zerocopy, int ext_buffer) +{ + struct pico_frame *p = PICO_ZALLOC(sizeof(struct pico_frame)); + uint32_t frame_buffer_size = size; + if (!p) + return NULL; + + if (ext_buffer && !zerocopy) { + /* external buffer implies zerocopy flag! */ + PICO_FREE(p); + return NULL; + } + + if (!zerocopy) { + unsigned int align = size % sizeof(uint32_t); + /* Ensure that usage_count starts on an aligned address */ + if (align) { + frame_buffer_size += (uint32_t)sizeof(uint32_t) - align; + } + + p->buffer = PICO_ZALLOC(frame_buffer_size + sizeof(uint32_t)); + if (!p->buffer) { + PICO_FREE(p); + return NULL; + } + + p->usage_count = (uint32_t *)(((uint8_t*)p->buffer) + frame_buffer_size); + } else { + p->buffer = NULL; + p->flags |= PICO_FRAME_FLAG_EXT_USAGE_COUNTER; + p->usage_count = PICO_ZALLOC(sizeof(uint32_t)); + if (!p->usage_count) { + PICO_FREE(p); + return NULL; + } + } + + + p->buffer_len = size; + + /* By default, frame content is the full buffer. */ + p->start = p->buffer; + p->len = p->buffer_len; + *p->usage_count = 1; + + if (ext_buffer) + p->flags |= PICO_FRAME_FLAG_EXT_BUFFER; + +#ifdef PICO_SUPPORT_DEBUG_MEMORY + dbg("Allocated buffer @%p, len= %d caller: %p\n", p->buffer, p->buffer_len, __builtin_return_address(2)); + dbg("DEBUG MEMORY: %d frames in use.\n", ++n_frames_allocated); +#endif + return p; +} + +struct pico_frame *pico_frame_alloc(uint32_t size) +{ + return pico_frame_do_alloc(size, 0, 0); +} + +int pico_frame_grow(struct pico_frame *f, uint32_t size) +{ + uint8_t *oldbuf; + uint32_t usage_count, *p_old_usage; + uint32_t frame_buffer_size; + uint32_t oldsize; + unsigned int align; + int addr_diff = 0; + + if (!f || (size < f->buffer_len)) { + return -1; + } + + align = size % sizeof(uint32_t); + frame_buffer_size = size; + if (align) { + frame_buffer_size += (uint32_t)sizeof(uint32_t) - align; + } + + oldbuf = f->buffer; + oldsize = f->buffer_len; + usage_count = *(f->usage_count); + p_old_usage = f->usage_count; + f->buffer = PICO_ZALLOC(frame_buffer_size + sizeof(uint32_t)); + if (!f->buffer) { + f->buffer = oldbuf; + return -1; + } + + f->usage_count = (uint32_t *)(((uint8_t*)f->buffer) + frame_buffer_size); + *f->usage_count = usage_count; + f->buffer_len = size; + memcpy(f->buffer, oldbuf, oldsize); + + /* Update hdr fields to new buffer*/ + addr_diff = (int)(f->buffer - oldbuf); + f->net_hdr += addr_diff; + f->datalink_hdr += addr_diff; + f->transport_hdr += addr_diff; + f->app_hdr += addr_diff; + f->start += addr_diff; + f->payload += addr_diff; + + if (f->flags & PICO_FRAME_FLAG_EXT_USAGE_COUNTER) + PICO_FREE(p_old_usage); + + if (!(f->flags & PICO_FRAME_FLAG_EXT_BUFFER)) + PICO_FREE(oldbuf); + else if (f->notify_free) + f->notify_free(oldbuf); + + f->flags = 0; + /* Now, the frame is not zerocopy anymore, and the usage counter has been moved within it */ + return 0; +} + +struct pico_frame *pico_frame_alloc_skeleton(uint32_t size, int ext_buffer) +{ + return pico_frame_do_alloc(size, 1, ext_buffer); +} + +int pico_frame_skeleton_set_buffer(struct pico_frame *f, void *buf) +{ + if (!buf) + return -1; + + f->buffer = (uint8_t *) buf; + f->start = f->buffer; + return 0; +} + +struct pico_frame *pico_frame_deepcopy(struct pico_frame *f) +{ + struct pico_frame *new = pico_frame_alloc(f->buffer_len); + int addr_diff; + unsigned char *buf; + uint32_t *uc; + if (!new) + return NULL; + + /* Save the two key pointers... */ + buf = new->buffer; + uc = new->usage_count; + + /* Overwrite all fields with originals */ + memcpy(new, f, sizeof(struct pico_frame)); + + /* ...restore the two key pointers */ + new->buffer = buf; + new->usage_count = uc; + + /* Update in-buffer pointers with offset */ + addr_diff = (int)(new->buffer - f->buffer); + new->datalink_hdr += addr_diff; + new->net_hdr += addr_diff; + new->transport_hdr += addr_diff; + new->app_hdr += addr_diff; + new->start += addr_diff; + new->payload += addr_diff; + +#ifdef PICO_SUPPORT_DEBUG_MEMORY + dbg("Deep-Copied frame @%p, into %p, usage count now: %d\n", f, new, *new->usage_count); +#endif + new->next = NULL; + return new; +} + + +static inline uint32_t pico_checksum_adder(uint32_t sum, void *data, uint32_t len) +{ + uint16_t *buf = (uint16_t *)data; + uint16_t *stop; + + if (len & 0x01) { + --len; +#ifdef PICO_BIGENDIAN + sum += (((uint8_t *)data)[len]) << 8; +#else + sum += ((uint8_t *)data)[len]; +#endif + } + + stop = (uint16_t *)(((uint8_t *)data) + len); + + while (buf < stop) { + sum += *buf++; + } + return sum; +} + +static inline uint16_t pico_checksum_finalize(uint32_t sum) +{ + while (sum >> 16) { /* a second carry is possible! */ + sum = (sum & 0x0000FFFF) + (sum >> 16); + } + return short_be((uint16_t) ~sum); +} + +/** + * Calculate checksum of a given string + */ +uint16_t pico_checksum(void *inbuf, uint32_t len) +{ + uint32_t sum; + + sum = pico_checksum_adder(0, inbuf, len); + return pico_checksum_finalize(sum); +} + +/* WARNING: len1 MUST be an EVEN number */ +uint16_t pico_dualbuffer_checksum(void *inbuf1, uint32_t len1, void *inbuf2, uint32_t len2) +{ + uint32_t sum; + + sum = pico_checksum_adder(0, inbuf1, len1); + sum = pico_checksum_adder(sum, inbuf2, len2); + return pico_checksum_finalize(sum); +} + diff --git a/net/picotcp/stack/pico_protocol.c b/net/picotcp/stack/pico_protocol.c new file mode 100644 index 0000000..98d2a20 --- /dev/null +++ b/net/picotcp/stack/pico_protocol.c @@ -0,0 +1,214 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + Authors: Daniele Lacamera + *********************************************************************/ + + +#include "pico_protocol.h" +#include "pico_tree.h" + +struct pico_proto_rr +{ + struct pico_tree *t; + struct pico_tree_node *node_in, *node_out; +}; + + +static int pico_proto_cmp(void *ka, void *kb) +{ + struct pico_protocol *a = ka, *b = kb; + if (a->hash < b->hash) + return -1; + + if (a->hash > b->hash) + return 1; + + return 0; +} + +PICO_TREE_DECLARE(Datalink_proto_tree, pico_proto_cmp); +PICO_TREE_DECLARE(Network_proto_tree, pico_proto_cmp); +PICO_TREE_DECLARE(Transport_proto_tree, pico_proto_cmp); +PICO_TREE_DECLARE(Socket_proto_tree, pico_proto_cmp); + +/* Static variables to keep track of the round robin loop */ +static struct pico_proto_rr proto_rr_datalink = { + &Datalink_proto_tree, NULL, NULL +}; +static struct pico_proto_rr proto_rr_network = { + &Network_proto_tree, NULL, NULL +}; +static struct pico_proto_rr proto_rr_transport = { + &Transport_proto_tree, NULL, NULL +}; +static struct pico_proto_rr proto_rr_socket = { + &Socket_proto_tree, NULL, NULL +}; + +static int proto_loop_in(struct pico_protocol *proto, int loop_score) +{ + struct pico_frame *f; + while(loop_score > 0) { + if (proto->q_in->frames <= 0) + break; + + f = pico_dequeue(proto->q_in); + if ((f) && (proto->process_in(proto, f) > 0)) { + loop_score--; + } + } + return loop_score; +} + +static int proto_loop_out(struct pico_protocol *proto, int loop_score) +{ + struct pico_frame *f; + while(loop_score > 0) { + if (proto->q_out->frames <= 0) + break; + + f = pico_dequeue(proto->q_out); + if ((f) && (proto->process_out(proto, f) > 0)) { + loop_score--; + } + } + return loop_score; +} + +static int proto_loop(struct pico_protocol *proto, int loop_score, int direction) +{ + + if (direction == PICO_LOOP_DIR_IN) + loop_score = proto_loop_in(proto, loop_score); + else if (direction == PICO_LOOP_DIR_OUT) + loop_score = proto_loop_out(proto, loop_score); + + return loop_score; +} + +static struct pico_tree_node *roundrobin_init(struct pico_proto_rr *rr, int direction) +{ + struct pico_tree_node *next_node = NULL; + /* Initialization (takes place only once) */ + if (rr->node_in == NULL) + rr->node_in = pico_tree_firstNode(rr->t->root); + + if (rr->node_out == NULL) + rr->node_out = pico_tree_firstNode(rr->t->root); + + if (direction == PICO_LOOP_DIR_IN) + next_node = rr->node_in; + else + next_node = rr->node_out; + + return next_node; +} + +static void roundrobin_end(struct pico_proto_rr *rr, int direction, struct pico_tree_node *last) +{ + if (direction == PICO_LOOP_DIR_IN) + rr->node_in = last; + else + rr->node_out = last; +} + +static int pico_protocol_generic_loop(struct pico_proto_rr *rr, int loop_score, int direction) +{ + struct pico_protocol *start, *next; + struct pico_tree_node *next_node = roundrobin_init(rr, direction); + + if (!next_node) + return loop_score; + + next = next_node->keyValue; + + /* init start node */ + start = next; + + /* round-robin all layer protocols, break if traversed all protocols */ + while (loop_score > 1 && next != NULL) { + loop_score = proto_loop(next, loop_score, direction); + next_node = pico_tree_next(next_node); + next = next_node->keyValue; + if (next == NULL) + { + next_node = pico_tree_firstNode(rr->t->root); + next = next_node->keyValue; + } + + if (next == start) + break; + } + roundrobin_end(rr, direction, next_node); + return loop_score; +} + +int pico_protocol_datalink_loop(int loop_score, int direction) +{ + return pico_protocol_generic_loop(&proto_rr_datalink, loop_score, direction); +} + +int pico_protocol_network_loop(int loop_score, int direction) +{ + return pico_protocol_generic_loop(&proto_rr_network, loop_score, direction); +} + +int pico_protocol_transport_loop(int loop_score, int direction) +{ + return pico_protocol_generic_loop(&proto_rr_transport, loop_score, direction); +} + +int pico_protocol_socket_loop(int loop_score, int direction) +{ + return pico_protocol_generic_loop(&proto_rr_socket, loop_score, direction); +} + +int pico_protocols_loop(int loop_score) +{ +/* + loop_score = pico_protocol_datalink_loop(loop_score); + loop_score = pico_protocol_network_loop(loop_score); + loop_score = pico_protocol_transport_loop(loop_score); + loop_score = pico_protocol_socket_loop(loop_score); + */ + return loop_score; +} + +static void proto_layer_rr_reset(struct pico_proto_rr *rr) +{ + rr->node_in = NULL; + rr->node_out = NULL; +} + +void pico_protocol_init(struct pico_protocol *p) +{ + if (!p) + return; + + p->hash = pico_hash(p->name, (uint32_t)strlen(p->name)); + switch (p->layer) { + case PICO_LAYER_DATALINK: + pico_tree_insert(&Datalink_proto_tree, p); + proto_layer_rr_reset(&proto_rr_datalink); + break; + case PICO_LAYER_NETWORK: + pico_tree_insert(&Network_proto_tree, p); + proto_layer_rr_reset(&proto_rr_network); + break; + case PICO_LAYER_TRANSPORT: + pico_tree_insert(&Transport_proto_tree, p); + proto_layer_rr_reset(&proto_rr_transport); + break; + case PICO_LAYER_SOCKET: + pico_tree_insert(&Socket_proto_tree, p); + proto_layer_rr_reset(&proto_rr_socket); + break; + } + dbg("Protocol %s registered (layer: %d).\n", p->name, p->layer); + +} + diff --git a/net/picotcp/stack/pico_socket.c b/net/picotcp/stack/pico_socket.c new file mode 100644 index 0000000..cd0be1e --- /dev/null +++ b/net/picotcp/stack/pico_socket.c @@ -0,0 +1,2213 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + + Authors: Daniele Lacamera + *********************************************************************/ + + +#include "pico_config.h" +#include "pico_queue.h" +#include "pico_socket.h" +#include "pico_ipv4.h" +#include "pico_ipv6.h" +#include "pico_udp.h" +#include "pico_tcp.h" +#include "pico_stack.h" +#include "pico_icmp4.h" +#include "pico_nat.h" +#include "pico_tree.h" +#include "pico_device.h" +#include "pico_socket_multicast.h" +#include "pico_socket_tcp.h" +#include "pico_socket_udp.h" + +#if defined (PICO_SUPPORT_IPV4) || defined (PICO_SUPPORT_IPV6) +#if defined (PICO_SUPPORT_TCP) || defined (PICO_SUPPORT_UDP) + + +#define PROTO(s) ((s)->proto->proto_number) +#define PICO_MIN_MSS (1280) +#define TCP_STATE(s) (s->state & PICO_SOCKET_STATE_TCP) + +#ifdef PICO_SUPPORT_MUTEX +static void *Mutex = NULL; +#endif + + +#define PROTO(s) ((s)->proto->proto_number) + +#define PICO_SOCKET_MTU 1480 /* Ethernet MTU(1500) - IP header size(20) */ + +# define frag_dbg(...) do {} while(0) + + +static struct pico_sockport *sp_udp = NULL, *sp_tcp = NULL; + +struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, uint16_t len); + +static int socket_cmp_family(struct pico_socket *a, struct pico_socket *b) +{ + uint32_t a_is_ip6 = is_sock_ipv6(a); + uint32_t b_is_ip6 = is_sock_ipv6(b); + (void)a; + (void)b; + if (a_is_ip6 < b_is_ip6) + return -1; + + if (a_is_ip6 > b_is_ip6) + return 1; + + return 0; +} + + +static int socket_cmp_ipv6(struct pico_socket *a, struct pico_socket *b) +{ + int ret = 0; + (void)a; + (void)b; +#ifdef PICO_SUPPORT_IPV6 + if (!is_sock_ipv6(a) || !is_sock_ipv6(b)) + return 0; + + if ((memcmp(a->local_addr.ip6.addr, PICO_IP6_ANY, PICO_SIZE_IP6) == 0) || (memcmp(b->local_addr.ip6.addr, PICO_IP6_ANY, PICO_SIZE_IP6) == 0)) + ret = 0; + else + ret = memcmp(a->local_addr.ip6.addr, b->local_addr.ip6.addr, PICO_SIZE_IP6); + +#endif + return ret; +} + +static int socket_cmp_ipv4(struct pico_socket *a, struct pico_socket *b) +{ + int ret = 0; + (void)a; + (void)b; + if (!is_sock_ipv4(a) || !is_sock_ipv4(b)) + return 0; + +#ifdef PICO_SUPPORT_IPV4 + if ((a->local_addr.ip4.addr == PICO_IP4_ANY) || (b->local_addr.ip4.addr == PICO_IP4_ANY)) + ret = 0; + else + ret = (int)(a->local_addr.ip4.addr - b->local_addr.ip4.addr); + +#endif + return ret; +} + +static int socket_cmp_remotehost(struct pico_socket *a, struct pico_socket *b) +{ + int ret = 0; + if (is_sock_ipv6(a)) + ret = memcmp(a->remote_addr.ip6.addr, b->remote_addr.ip6.addr, PICO_SIZE_IP6); + else + ret = (int)(a->remote_addr.ip4.addr - b->remote_addr.ip4.addr); + + return ret; +} + +static int socket_cmp_addresses(struct pico_socket *a, struct pico_socket *b) +{ + int ret = 0; + /* At this point, sort by local host */ + ret = socket_cmp_ipv6(a, b); + + if (ret == 0) + ret = socket_cmp_ipv4(a, b); + + /* Sort by remote host */ + if (ret == 0) + ret = socket_cmp_remotehost(a, b); + + return 0; +} + +static int socket_cmp(void *ka, void *kb) +{ + struct pico_socket *a = ka, *b = kb; + int ret = 0; + + /* First, order by network family */ + ret = socket_cmp_family(a, b); + + /* Then, compare by source/destination addresses */ + if (ret == 0) + ret = socket_cmp_addresses(a, b); + + /* And finally by remote port. The two sockets are coincident if the quad is the same. */ + if (ret == 0) + ret = b->remote_port - a->remote_port; + + return ret; +} + + +#define INIT_SOCKPORT { {&LEAF, socket_cmp}, 0, 0 } + +static int sockport_cmp(void *ka, void *kb) +{ + struct pico_sockport *a = ka, *b = kb; + if (a->number < b->number) + return -1; + + if (a->number > b->number) + return 1; + + return 0; +} + +PICO_TREE_DECLARE(UDPTable, sockport_cmp); +PICO_TREE_DECLARE(TCPTable, sockport_cmp); + +struct pico_sockport *pico_get_sockport(uint16_t proto, uint16_t port) +{ + struct pico_sockport test = INIT_SOCKPORT; + test.number = port; + + if (proto == PICO_PROTO_UDP) + return pico_tree_findKey(&UDPTable, &test); + + else if (proto == PICO_PROTO_TCP) + return pico_tree_findKey(&TCPTable, &test); + + else return NULL; +} + +#ifdef PICO_SUPPORT_IPV4 + +static int pico_port_in_use_by_nat(uint16_t proto, uint16_t port) +{ + int ret = 0; + (void) proto; + (void) port; +#ifdef PICO_SUPPORT_NAT + if (pico_ipv4_nat_find(port, NULL, 0, (uint8_t)proto)) { + dbg("In use by nat....\n"); + ret = 1; + } + +#endif + return ret; +} + +static int pico_port_in_use_with_this_ipv4_address(struct pico_sockport *sp, struct pico_ip4 ip) +{ + if (sp) { + struct pico_ip4 *s_local; + struct pico_tree_node *idx; + struct pico_socket *s; + pico_tree_foreach(idx, &sp->socks) { + s = idx->keyValue; + if (s->net == &pico_proto_ipv4) { + s_local = (struct pico_ip4*) &s->local_addr; + if ((s_local->addr == PICO_IPV4_INADDR_ANY) || (s_local->addr == ip.addr)) { + return 1; + } + } + } + } + + return 0; +} + + +static int pico_port_in_use_ipv4(struct pico_sockport *sp, void *addr) +{ + struct pico_ip4 ip; + /* IPv4 */ + if (addr) + ip.addr = ((struct pico_ip4 *)addr)->addr; + else + ip.addr = PICO_IPV4_INADDR_ANY; + + if (ip.addr == PICO_IPV4_INADDR_ANY) { + if (!sp) + return 0; + else { + dbg("In use, and asked for ANY\n"); + return 1; + } + } + + return pico_port_in_use_with_this_ipv4_address(sp, ip); +} +#endif + +#ifdef PICO_SUPPORT_IPV6 +static int pico_port_in_use_with_this_ipv6_address(struct pico_sockport *sp, struct pico_ip6 ip) +{ + if (sp) { + struct pico_ip6 *s_local; + struct pico_tree_node *idx; + struct pico_socket *s; + pico_tree_foreach(idx, &sp->socks) { + s = idx->keyValue; + if (s->net == &pico_proto_ipv6) { + s_local = (struct pico_ip6*) &s->local_addr; + if ((pico_ipv6_is_unspecified(s_local->addr)) || (!memcmp(s_local->addr, ip.addr, PICO_SIZE_IP6))) { + return 1; + } + } + } + } + + return 0; +} + +static int pico_port_in_use_ipv6(struct pico_sockport *sp, void *addr) +{ + struct pico_ip6 ip; + /* IPv6 */ + if (addr) + memcpy(ip.addr, ((struct pico_ip6 *)addr)->addr, sizeof(struct pico_ip6)); + else + memcpy(ip.addr, PICO_IP6_ANY, sizeof(struct pico_ip6)); + + if (memcmp(ip.addr, PICO_IP6_ANY, sizeof(struct pico_ip6)) == 0) { + if (!sp) + return 0; + else { + dbg("In use, and asked for ANY\n"); + return 1; + } + } + + return pico_port_in_use_with_this_ipv6_address(sp, ip); +} +#endif + + + +static int pico_generic_port_in_use(uint16_t proto, uint16_t port, struct pico_sockport *sp, void *addr, void *net) +{ +#ifdef PICO_SUPPORT_IPV4 + if (net == &pico_proto_ipv4) + { + if (pico_port_in_use_by_nat(proto, port)) { + return 1; + } + + if (pico_port_in_use_ipv4(sp, addr)) { + return 1; + } + } + +#endif + +#ifdef PICO_SUPPORT_IPV6 + if (net == &pico_proto_ipv6) + { + if (pico_port_in_use_ipv6(sp, addr)) { + return 1; + } + } + +#endif + + return 0; +} + +int pico_is_port_free(uint16_t proto, uint16_t port, void *addr, void *net) +{ + struct pico_sockport *sp; + sp = pico_get_sockport(proto, port); + + if (pico_generic_port_in_use(proto, port, sp, addr, net)) + return 0; + + return 1; +} + +static int pico_check_socket(struct pico_socket *s) +{ + struct pico_sockport *test; + struct pico_socket *found; + struct pico_tree_node *index; + + test = pico_get_sockport(PROTO(s), s->local_port); + + if (!test) { + return -1; + } + + pico_tree_foreach(index, &test->socks){ + found = index->keyValue; + if (s == found) { + return 0; + } + } + + return -1; +} + +struct pico_socket *pico_sockets_find(uint16_t local, uint16_t remote) +{ + struct pico_socket *sock = NULL; + struct pico_tree_node *index = NULL; + struct pico_sockport *sp = NULL; + + sp = pico_get_sockport(PICO_PROTO_TCP, local); + if(sp) + { + pico_tree_foreach(index, &sp->socks) + { + if(((struct pico_socket *)index->keyValue)->remote_port == remote) + { + sock = (struct pico_socket *)index->keyValue; + break; + } + } + } + + return sock; +} + + +int8_t pico_socket_add(struct pico_socket *s) +{ + struct pico_sockport *sp = pico_get_sockport(PROTO(s), s->local_port); + PICOTCP_MUTEX_LOCK(Mutex); + if (!sp) { + /* dbg("Creating sockport..%04x\n", s->local_port); / * In comment due to spam during test * / */ + sp = PICO_ZALLOC(sizeof(struct pico_sockport)); + + if (!sp) { + pico_err = PICO_ERR_ENOMEM; + PICOTCP_MUTEX_UNLOCK(Mutex); + return -1; + } + + sp->proto = PROTO(s); + sp->number = s->local_port; + sp->socks.root = &LEAF; + sp->socks.compare = socket_cmp; + + if (PROTO(s) == PICO_PROTO_UDP) + { + pico_tree_insert(&UDPTable, sp); + } + else if (PROTO(s) == PICO_PROTO_TCP) + { + pico_tree_insert(&TCPTable, sp); + } + } + + pico_tree_insert(&sp->socks, s); + s->state |= PICO_SOCKET_STATE_BOUND; + PICOTCP_MUTEX_UNLOCK(Mutex); +#ifdef DEBUG_SOCKET_TREE + { + struct pico_tree_node *index; + /* RB_FOREACH(s, socket_tree, &sp->socks) { */ + pico_tree_foreach(index, &sp->socks){ + s = index->keyValue; + dbg(">>>> List Socket lc=%hu rm=%hu\n", short_be(s->local_port), short_be(s->remote_port)); + } + + } +#endif + return 0; +} + + +static void socket_clean_queues(struct pico_socket *sock) +{ + struct pico_frame *f_in = pico_dequeue(&sock->q_in); + struct pico_frame *f_out = pico_dequeue(&sock->q_out); + while(f_in || f_out) + { + if(f_in) + { + pico_frame_discard(f_in); + f_in = pico_dequeue(&sock->q_in); + } + + if(f_out) + { + pico_frame_discard(f_out); + f_out = pico_dequeue(&sock->q_out); + } + } + pico_queue_deinit(&sock->q_in); + pico_queue_deinit(&sock->q_out); + pico_socket_tcp_cleanup(sock); +} + +static void socket_garbage_collect(pico_time now, void *arg) +{ + struct pico_socket *s = (struct pico_socket *) arg; + IGNORE_PARAMETER(now); + + socket_clean_queues(s); + PICO_FREE(s); +} + + +static void pico_socket_check_empty_sockport(struct pico_socket *s, struct pico_sockport *sp) +{ + if(pico_tree_empty(&sp->socks)) { + if (PROTO(s) == PICO_PROTO_UDP) + { + pico_tree_delete(&UDPTable, sp); + } + else if (PROTO(s) == PICO_PROTO_TCP) + { + pico_tree_delete(&TCPTable, sp); + } + + if(sp_tcp == sp) + sp_tcp = NULL; + + if(sp_udp == sp) + sp_udp = NULL; + + PICO_FREE(sp); + } +} + +int8_t pico_socket_del(struct pico_socket *s) +{ + struct pico_sockport *sp = pico_get_sockport(PROTO(s), s->local_port); + if (!sp) { + pico_err = PICO_ERR_ENXIO; + return -1; + } + + PICOTCP_MUTEX_LOCK(Mutex); + pico_tree_delete(&sp->socks, s); + pico_socket_check_empty_sockport(s, sp); + pico_multicast_delete(s); + pico_socket_tcp_delete(s); + s->state = PICO_SOCKET_STATE_CLOSED; + pico_timer_add(3000, socket_garbage_collect, s); + PICOTCP_MUTEX_UNLOCK(Mutex); + return 0; +} + +static void pico_socket_update_tcp_state(struct pico_socket *s, uint16_t tcp_state) +{ + if (tcp_state) { + s->state &= 0x00FF; + s->state |= tcp_state; + } +} + +static int8_t pico_socket_alter_state(struct pico_socket *s, uint16_t more_states, uint16_t less_states, uint16_t tcp_state) +{ + struct pico_sockport *sp; + if (more_states & PICO_SOCKET_STATE_BOUND) + return pico_socket_add(s); + + if (less_states & PICO_SOCKET_STATE_BOUND) + return pico_socket_del(s); + + sp = pico_get_sockport(PROTO(s), s->local_port); + if (!sp) { + pico_err = PICO_ERR_ENXIO; + return -1; + } + + s->state |= more_states; + s->state = (uint16_t)(s->state & (~less_states)); + pico_socket_update_tcp_state(s, tcp_state); + return 0; +} + + +static int pico_socket_transport_deliver(struct pico_protocol *p, struct pico_sockport *sp, struct pico_frame *f) +{ +#ifdef PICO_SUPPORT_TCP + if (p->proto_number == PICO_PROTO_TCP) + return pico_socket_tcp_deliver(sp, f); + +#endif + +#ifdef PICO_SUPPORT_UDP + if (p->proto_number == PICO_PROTO_UDP) + return pico_socket_udp_deliver(sp, f); + +#endif + + return -1; +} + + +static int pico_socket_deliver(struct pico_protocol *p, struct pico_frame *f, uint16_t localport) +{ + struct pico_sockport *sp = NULL; + struct pico_trans *tr = (struct pico_trans *) f->transport_hdr; + + if (!tr) + return -1; + + sp = pico_get_sockport(p->proto_number, localport); + if (!sp) { + dbg("No such port %d\n", short_be(localport)); + return -1; + } + + return pico_socket_transport_deliver(p, sp, f); +} + +int pico_socket_set_family(struct pico_socket *s, uint16_t family) +{ + (void) family; + + #ifdef PICO_SUPPORT_IPV4 + if (family == PICO_PROTO_IPV4) + s->net = &pico_proto_ipv4; + + #endif + + #ifdef PICO_SUPPORT_IPV6 + if (family == PICO_PROTO_IPV6) + s->net = &pico_proto_ipv6; + + #endif + + if (s->net == NULL) + return -1; + + return 0; +} + +static struct pico_socket *pico_socket_transport_open(uint16_t proto, uint16_t family) +{ + struct pico_socket *s = NULL; + (void)family; +#ifdef PICO_SUPPORT_UDP + if (proto == PICO_PROTO_UDP) + s = pico_socket_udp_open(); + +#endif + +#ifdef PICO_SUPPORT_TCP + if (proto == PICO_PROTO_TCP) + s = pico_socket_tcp_open(family); + +#endif + + return s; + +} + +struct pico_socket *pico_socket_open(uint16_t net, uint16_t proto, void (*wakeup)(uint16_t ev, struct pico_socket *)) +{ + + struct pico_socket *s = NULL; + + s = pico_socket_transport_open(proto, net); + + if (!s) { + pico_err = PICO_ERR_EPROTONOSUPPORT; + return NULL; + } + + if (pico_socket_set_family(s, net) != 0) { + PICO_FREE(s); + pico_err = PICO_ERR_ENETUNREACH; + return NULL; + } + + s->q_in.max_size = PICO_DEFAULT_SOCKETQ; + s->q_out.max_size = PICO_DEFAULT_SOCKETQ; + + s->wakeup = wakeup; + return s; +} + + +static void pico_socket_clone_assign_address(struct pico_socket *s, struct pico_socket *facsimile) +{ + +#ifdef PICO_SUPPORT_IPV4 + if (facsimile->net == &pico_proto_ipv4) { + s->net = &pico_proto_ipv4; + memcpy(&s->local_addr, &facsimile->local_addr, sizeof(struct pico_ip4)); + memcpy(&s->remote_addr, &facsimile->remote_addr, sizeof(struct pico_ip4)); + } + +#endif + +#ifdef PICO_SUPPORT_IPV6 + if (facsimile->net == &pico_proto_ipv6) { + s->net = &pico_proto_ipv6; + memcpy(&s->local_addr, &facsimile->local_addr, sizeof(struct pico_ip6)); + memcpy(&s->remote_addr, &facsimile->remote_addr, sizeof(struct pico_ip6)); + } + +#endif + +} + +struct pico_socket *pico_socket_clone(struct pico_socket *facsimile) +{ + struct pico_socket *s = NULL; + + s = pico_socket_transport_open(facsimile->proto->proto_number, facsimile->net->proto_number); + if (!s) { + pico_err = PICO_ERR_EPROTONOSUPPORT; + return NULL; + } + + s->local_port = facsimile->local_port; + s->remote_port = facsimile->remote_port; + s->state = facsimile->state; + pico_socket_clone_assign_address(s, facsimile); + if (!s->net) { + PICO_FREE(s); + pico_err = PICO_ERR_ENETUNREACH; + return NULL; + } + + s->q_in.max_size = PICO_DEFAULT_SOCKETQ; + s->q_out.max_size = PICO_DEFAULT_SOCKETQ; + s->wakeup = NULL; + return s; +} + +static int pico_socket_transport_read(struct pico_socket *s, void *buf, int len) +{ + if (PROTO(s) == PICO_PROTO_UDP) + { + /* make sure cast to uint16_t doesn't give unexpected results */ + if(len > 0xFFFF) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + return pico_socket_udp_recv(s, buf, (uint16_t)len, NULL, NULL); + } + else if (PROTO(s) == PICO_PROTO_TCP) + return pico_socket_tcp_read(s, buf, (uint32_t)len); + else return 0; +} + +int pico_socket_read(struct pico_socket *s, void *buf, int len) +{ + if (!s || buf == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + /* check if exists in tree */ + /* See task #178 */ + if (pico_check_socket(s) != 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { + pico_err = PICO_ERR_EIO; + return -1; + } + + return pico_socket_transport_read(s, buf, len); +} + +static int pico_socket_write_check_state(struct pico_socket *s) +{ + if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { + pico_err = PICO_ERR_EIO; + return -1; + } + + if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) { + pico_err = PICO_ERR_ENOTCONN; + return -1; + } + + if (s->state & PICO_SOCKET_STATE_SHUT_LOCAL) { /* check if in shutdown state */ + pico_err = PICO_ERR_ESHUTDOWN; + return -1; + } + + return 0; +} + +static int pico_socket_write_attempt(struct pico_socket *s, const void *buf, int len) +{ + if (pico_socket_write_check_state(s) < 0) { + return -1; + } else { + return pico_socket_sendto(s, buf, len, &s->remote_addr, s->remote_port); + } +} + +int pico_socket_write(struct pico_socket *s, const void *buf, int len) +{ + if (!s || buf == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + /* check if exists in tree */ + /* See task #178 */ + if (pico_check_socket(s) != 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + return pico_socket_write_attempt(s, buf, len); +} + +static uint16_t pico_socket_high_port(uint16_t proto) +{ + uint16_t port; + if (0 || +#ifdef PICO_SUPPORT_TCP + (proto == PICO_PROTO_TCP) || +#endif +#ifdef PICO_SUPPORT_UDP + (proto == PICO_PROTO_UDP) || +#endif + 0) { + do { + uint32_t rand = pico_rand(); + port = (uint16_t) (rand & 0xFFFFU); + port = (uint16_t)((port % (65535 - 1024)) + 1024U); + if (pico_is_port_free(proto, port, NULL, NULL)) { + return short_be(port); + } + } while(1); + } + else return 0U; +} + +static void *pico_socket_sendto_get_ip4_src(struct pico_socket *s, struct pico_ip4 *dst) +{ + struct pico_ip4 *src4 = NULL; + +#ifdef PICO_SUPPORT_IPV4 + /* Check if socket is connected: destination address MUST match the + * current connected endpoint + */ + if ((s->state & PICO_SOCKET_STATE_CONNECTED)) { + src4 = &s->local_addr.ip4; + if (s->remote_addr.ip4.addr != ((struct pico_ip4 *)dst)->addr ) { + pico_err = PICO_ERR_EADDRNOTAVAIL; + return NULL; + } + } else { + + src4 = pico_ipv4_source_find(dst); + if (!src4) { + pico_err = PICO_ERR_EHOSTUNREACH; + return NULL; + } + + } + + if (src4->addr != PICO_IPV4_INADDR_ANY) + s->local_addr.ip4.addr = src4->addr; + +#else + pico_err = PICO_ERR_EPROTONOSUPPORT; +#endif + return src4; +} + +static void *pico_socket_sendto_get_ip6_src(struct pico_socket *s, struct pico_ip6 *dst) +{ + struct pico_ip6 *src6 = NULL; + (void)s; + (void)dst; + +#ifdef PICO_SUPPORT_IPV6 + + /* Check if socket is connected: destination address MUST match the + * current connected endpoint + */ + if ((s->state & PICO_SOCKET_STATE_CONNECTED)) { + src6 = &s->local_addr.ip6; + if (memcmp(&s->remote_addr, dst, PICO_SIZE_IP6)) { + pico_err = PICO_ERR_EADDRNOTAVAIL; + return NULL; + } + } else { + src6 = pico_ipv6_source_find(dst); + if (!src6) { + pico_err = PICO_ERR_EHOSTUNREACH; + return NULL; + } + + if (!pico_ipv6_is_unspecified(src6->addr)) + s->local_addr.ip6 = *src6; + } + +#else + pico_err = PICO_ERR_EPROTONOSUPPORT; +#endif + return src6; +} + + +static int pico_socket_sendto_dest_check(struct pico_socket *s, void *dst, uint16_t port) +{ + + /* For the sendto call to be valid, + * dst and remote_port should be always populated. + */ + if (!dst || !port) { + pico_err = PICO_ERR_EADDRNOTAVAIL; + return -1; + } + + /* When coming from pico_socket_send (or _write), + * the destination is automatically assigned to the currently connected endpoint. + * This check will ensure that there is no mismatch when sendto() is called directly + * on a connected socket + */ + if ((s->state & PICO_SOCKET_STATE_CONNECTED) != 0) { + if (port != s->remote_port) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + return 0; +} + +static int pico_socket_sendto_initial_checks(struct pico_socket *s, const void *buf, const int len, void *dst, uint16_t remote_port) +{ + if (len < 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + if (buf == NULL || s == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + return pico_socket_sendto_dest_check(s, dst, remote_port); +} + +static void *pico_socket_sendto_get_src(struct pico_socket *s, void *dst) +{ + void *src = NULL; + if (is_sock_ipv4(s)) + src = pico_socket_sendto_get_ip4_src(s, (struct pico_ip4 *)dst); + + if (is_sock_ipv6(s)) + src = pico_socket_sendto_get_ip6_src(s, (struct pico_ip6 *)dst); + + return src; +} + +static struct pico_remote_endpoint *pico_socket_sendto_destination_ipv4(struct pico_socket *s, struct pico_ip4 *dst, uint16_t port) +{ + struct pico_remote_endpoint *ep = NULL; + (void)s; + ep = PICO_ZALLOC(sizeof(struct pico_remote_endpoint)); + if (!ep) { + pico_err = PICO_ERR_ENOMEM; + return NULL; + } + + ep->remote_addr.ip4.addr = ((struct pico_ip4 *)dst)->addr; + ep->remote_port = port; + return ep; +} + +static void pico_endpoint_free(struct pico_remote_endpoint *ep) +{ + if (ep) + PICO_FREE(ep); +} + +static struct pico_remote_endpoint *pico_socket_sendto_destination_ipv6(struct pico_socket *s, struct pico_ip6 *dst, uint16_t port) +{ + struct pico_remote_endpoint *ep = NULL; + (void)s; + (void)dst; + (void)port; +#ifdef PICO_SUPPORT_IPV6 + ep = PICO_ZALLOC(sizeof(struct pico_remote_endpoint)); + if (!ep) { + pico_err = PICO_ERR_ENOMEM; + return NULL; + } + + memcpy(&ep->remote_addr.ip6, dst, sizeof(struct pico_ip6)); + ep->remote_port = port; +#endif + return ep; +} + + +static struct pico_remote_endpoint *pico_socket_sendto_destination(struct pico_socket *s, void *dst, uint16_t port) +{ + struct pico_remote_endpoint *ep = NULL; + (void)pico_socket_sendto_destination_ipv6; + /* socket remote info could change in a consecutive call, make persistent */ +# ifdef PICO_SUPPORT_UDP + if (PROTO(s) == PICO_PROTO_UDP) { +# ifdef PICO_SUPPORT_IPV6 + if (is_sock_ipv6(s)) + ep = pico_socket_sendto_destination_ipv6(s, (struct pico_ip6 *)dst, port); + +# endif +# ifdef PICO_SUPPORT_IPV4 + if (is_sock_ipv4(s)) + ep = pico_socket_sendto_destination_ipv4(s, (struct pico_ip4 *)dst, port); + +# endif + } + +# endif + return ep; +} + +static int32_t pico_socket_sendto_set_localport(struct pico_socket *s) +{ + + if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { + s->local_port = pico_socket_high_port(s->proto->proto_number); + if (s->local_port == 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + s->state |= PICO_SOCKET_STATE_BOUND; + } + + return s->local_port; +} + +static int pico_socket_sendto_transport_offset(struct pico_socket *s) +{ + int header_offset = -1; + #ifdef PICO_SUPPORT_TCP + if (PROTO(s) == PICO_PROTO_TCP) + header_offset = pico_tcp_overhead(s); + + #endif + + #ifdef PICO_SUPPORT_UDP + if (PROTO(s) == PICO_PROTO_UDP) + header_offset = sizeof(struct pico_udp_hdr); + + #endif + return header_offset; +} + + +static struct pico_remote_endpoint *pico_socket_set_info(struct pico_remote_endpoint *ep) +{ + struct pico_remote_endpoint *info; + info = PICO_ZALLOC(sizeof(struct pico_remote_endpoint)); + if (!info) { + pico_err = PICO_ERR_ENOMEM; + return NULL; + } + + memcpy(info, ep, sizeof(struct pico_remote_endpoint)); + return info; +} + +static void pico_xmit_frame_set_nofrag(struct pico_frame *f) +{ +#ifdef PICO_SUPPORT_IPFRAG + f->frag = PICO_IPV4_DONTFRAG; +#else + (void)f; +#endif +} + +static int pico_socket_final_xmit(struct pico_socket *s, struct pico_frame *f) +{ + if (s->proto->push(s->proto, f) > 0) { + return f->payload_len; + } else { + pico_frame_discard(f); + return 0; + } +} + +static int pico_socket_xmit_one(struct pico_socket *s, const void *buf, const int len, void *src, + struct pico_remote_endpoint *ep, struct pico_msginfo *msginfo) +{ + struct pico_frame *f; + uint16_t hdr_offset = (uint16_t)pico_socket_sendto_transport_offset(s); + int ret = 0; + (void)src; + + f = pico_socket_frame_alloc(s, (uint16_t)(len + hdr_offset)); + if (!f) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + f->payload += hdr_offset; + f->payload_len = (uint16_t)(len); + f->sock = s; + transport_flags_update(f, s); + pico_xmit_frame_set_nofrag(f); + if (ep && !f->info) { + f->info = pico_socket_set_info(ep); + if (!f->info) { + pico_frame_discard(f); + return -1; + } + } + + if (msginfo) { + f->send_ttl = (uint8_t)msginfo->ttl; + f->send_tos = (uint8_t)msginfo->tos; + f->dev = msginfo->dev; + } + + memcpy(f->payload, (const uint8_t *)buf, f->payload_len); + /* dbg("Pushing segment, hdr len: %d, payload_len: %d\n", header_offset, f->payload_len); */ + ret = pico_socket_final_xmit(s, f); + return ret; +} + +static int pico_socket_xmit_avail_space(struct pico_socket *s); + +#ifdef PICO_SUPPORT_IPFRAG +static void pico_socket_xmit_first_fragment_setup(struct pico_frame *f, int space, int hdr_offset) +{ + frag_dbg("FRAG: first fragmented frame %p | len = %u offset = 0\n", f, f->payload_len); + /* transport header length field contains total length + header length */ + f->transport_len = (uint16_t)(space); + f->frag = PICO_IPV4_MOREFRAG; + f->payload += hdr_offset; + f->payload_len = (uint16_t) space; +} + +static void pico_socket_xmit_next_fragment_setup(struct pico_frame *f, int hdr_offset, int total_payload_written, int len) +{ + /* no transport header in fragmented IP */ + f->payload = f->transport_hdr; + f->payload_len = (uint16_t)(f->payload_len - hdr_offset); + /* set offset in octets */ + f->frag = (uint16_t)((total_payload_written + (uint16_t)hdr_offset) >> 3u); + if (total_payload_written + f->payload_len < len) { + frag_dbg("FRAG: intermediate fragmented frame %p | len = %u offset = %u\n", f, f->payload_len, short_be(f->frag)); + f->frag |= PICO_IPV4_MOREFRAG; + } else { + frag_dbg("FRAG: last fragmented frame %p | len = %u offset = %u\n", f, f->payload_len, short_be(f->frag)); + f->frag &= PICO_IPV4_FRAG_MASK; + } +} +#endif + +static int pico_socket_xmit_fragments(struct pico_socket *s, const void *buf, const int len, + void *src, struct pico_remote_endpoint *ep, struct pico_msginfo *msginfo) +{ + int space = pico_socket_xmit_avail_space(s); + int hdr_offset = pico_socket_sendto_transport_offset(s); + int total_payload_written = 0; + struct pico_frame *f = NULL; + + if (space > len) { + return pico_socket_xmit_one(s, buf, len, src, ep, msginfo); + } + +#ifdef PICO_SUPPORT_IPV6 + /* Can't fragment IPv6 */ + if (is_sock_ipv6(s)) { + return pico_socket_xmit_one(s, buf, space, src, ep, msginfo); + } + +#endif + +#ifdef PICO_SUPPORT_IPFRAG + while(total_payload_written < len) { + /* Always allocate the max space available: space + offset */ + if (len < space) + space = len; + + if (space > len - total_payload_written) + space = len - total_payload_written; + + f = pico_socket_frame_alloc(s, (uint16_t)(space + hdr_offset)); + if (!f) { + pico_err = PICO_ERR_ENOMEM; + pico_endpoint_free(ep); + return -1; + } + + f->sock = s; + if (ep) { + f->info = pico_socket_set_info(ep); + if (!f->info) { + pico_frame_discard(f); + pico_endpoint_free(ep); + return -1; + } + } + + if (total_payload_written == 0) { + /* First fragment: no payload written yet! */ + pico_socket_xmit_first_fragment_setup(f, space, hdr_offset); + } else { + /* Next fragment */ + pico_socket_xmit_next_fragment_setup(f, hdr_offset, total_payload_written, len); + } + + memcpy(f->payload, (const uint8_t *)buf + total_payload_written, f->payload_len); + transport_flags_update(f, s); + if (s->proto->push(s->proto, f) > 0) { + total_payload_written += f->payload_len; + } else { + pico_frame_discard(f); + break; + } + } /* while() */ + pico_endpoint_free(ep); + return total_payload_written; + +#else + /* Careful with that axe, Eugene! + * + * cropping down datagrams to the MTU value. + */ + (void) f; + (void) hdr_offset; + (void) total_payload_written; + return pico_socket_xmit_one(s, buf, space, src, ep, msginfo); + +#endif +} + +static void get_sock_dev(struct pico_socket *s) +{ + if (0) {} + +#ifdef PICO_SUPPORT_IPV6 + else if (is_sock_ipv6(s)) + s->dev = pico_ipv6_source_dev_find(&s->remote_addr.ip6); +#endif +#ifdef PICO_SUPPORT_IPV4 + else if (is_sock_ipv4(s)) + s->dev = pico_ipv4_source_dev_find(&s->remote_addr.ip4); +#endif + +} + + +static uint32_t pico_socket_adapt_mss_to_proto(struct pico_socket *s, uint32_t mss) +{ +#ifdef PICO_SUPPORT_IPV6 + if (is_sock_ipv6(s)) + mss -= PICO_SIZE_IP6HDR; + else +#endif + mss -= PICO_SIZE_IP4HDR; + return mss; +} + +uint32_t pico_socket_get_mss(struct pico_socket *s) +{ + uint32_t mss = PICO_MIN_MSS; + if (!s) + return mss; + + if (!s->dev) + get_sock_dev(s); + + if (!s->dev) { + mss = PICO_MIN_MSS; + } else { + mss = s->dev->mtu; + } + + return pico_socket_adapt_mss_to_proto(s, mss); +} + + +static int pico_socket_xmit_avail_space(struct pico_socket *s) +{ + int transport_len; + int header_offset; + +#ifdef PICO_SUPPORT_TCP + if (PROTO(s) == PICO_PROTO_TCP) { + transport_len = (uint16_t)pico_tcp_get_socket_mss(s); + } else +#endif + transport_len = (uint16_t)pico_socket_get_mss(s); + header_offset = pico_socket_sendto_transport_offset(s); + if (header_offset < 0) { + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; + } + + transport_len -= pico_socket_sendto_transport_offset(s); + return transport_len; +} + + +static int pico_socket_xmit(struct pico_socket *s, const void *buf, const int len, void *src, + struct pico_remote_endpoint *ep, struct pico_msginfo *msginfo) +{ + int space = pico_socket_xmit_avail_space(s); + int total_payload_written = 0; + + if (space < 0) { + pico_err = PICO_ERR_EPROTONOSUPPORT; + pico_endpoint_free(ep); + return -1; + } + + if ((PROTO(s) == PICO_PROTO_UDP) && (len > space)) { + total_payload_written = pico_socket_xmit_fragments(s, buf, len, src, ep, msginfo); + /* Implies ep discarding */ + return total_payload_written; + } + + while (total_payload_written < len) { + int w, chunk_len = len - total_payload_written; + if (chunk_len > space) + chunk_len = space; + + w = pico_socket_xmit_one(s, (const void *)((const uint8_t *)buf + total_payload_written), chunk_len, src, ep, msginfo); + if (w <= 0) { + break; + } + + total_payload_written += w; + if (PROTO(s) == PICO_PROTO_UDP) { + /* Break after the first datagram sent with at most MTU bytes. */ + break; + } + } + pico_endpoint_free(ep); + return total_payload_written; +} + +static void pico_socket_sendto_set_dport(struct pico_socket *s, uint16_t port) +{ + if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) { + s->remote_port = port; + } +} + + +int MOCKABLE pico_socket_sendto_extended(struct pico_socket *s, const void *buf, const int len, + void *dst, uint16_t remote_port, struct pico_msginfo *msginfo) +{ + struct pico_remote_endpoint *remote_endpoint = NULL; + void *src = NULL; + + if(len == 0) + return 0; + + if (pico_socket_sendto_initial_checks(s, buf, len, dst, remote_port) < 0) + return -1; + + + src = pico_socket_sendto_get_src(s, dst); + if (!src) { +#ifdef PICO_SUPPORT_IPV6 + if((s->net->proto_number == PICO_PROTO_IPV6) + && msginfo && msginfo->dev + && pico_ipv6_is_linklocal(((struct pico_ip6 *)dst)->addr)) + { + src = &(pico_ipv6_linklocal_get(msginfo->dev)->address); + if(!src) + return -1; + } + else +#endif + return -1; + } + + remote_endpoint = pico_socket_sendto_destination(s, dst, remote_port); + if (pico_socket_sendto_set_localport(s) < 0) { + pico_endpoint_free(remote_endpoint); + return -1; + } + + pico_socket_sendto_set_dport(s, remote_port); + return pico_socket_xmit(s, buf, len, src, remote_endpoint, msginfo); /* Implies discarding the endpoint */ +} + +int MOCKABLE pico_socket_sendto(struct pico_socket *s, const void *buf, const int len, void *dst, uint16_t remote_port) +{ + return pico_socket_sendto_extended(s, buf, len, dst, remote_port, NULL); +} + +int pico_socket_send(struct pico_socket *s, const void *buf, int len) +{ + if (!s || buf == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + /* check if exists in tree */ + /* See task #178 */ + if (pico_check_socket(s) != 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) { + pico_err = PICO_ERR_ENOTCONN; + return -1; + } + + return pico_socket_sendto(s, buf, len, &s->remote_addr, s->remote_port); +} + +int pico_socket_recvfrom_extended(struct pico_socket *s, void *buf, int len, void *orig, + uint16_t *remote_port, struct pico_msginfo *msginfo) +{ + if (!s || buf == NULL) { /* / || orig == NULL || remote_port == NULL) { */ + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + /* check if exists in tree */ + if (pico_check_socket(s) != 0) { + pico_err = PICO_ERR_EINVAL; + /* See task #178 */ + return -1; + } + } + + if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { + pico_err = PICO_ERR_EADDRNOTAVAIL; + return -1; + } + +#ifdef PICO_SUPPORT_UDP + if (PROTO(s) == PICO_PROTO_UDP) { + /* make sure cast to uint16_t doesn't give unexpected results */ + if(len > 0xFFFF) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + return pico_udp_recv(s, buf, (uint16_t)len, orig, remote_port, msginfo); + } + +#endif +#ifdef PICO_SUPPORT_TCP + if (PROTO(s) == PICO_PROTO_TCP) { + /* check if in shutdown state and if tcpq_in empty */ + if ((s->state & PICO_SOCKET_STATE_SHUT_REMOTE) && pico_tcp_queue_in_is_empty(s)) { + pico_err = PICO_ERR_ESHUTDOWN; + return -1; + } else { + /* dbg("socket tcp recv\n"); */ + return (int)pico_tcp_read(s, buf, (uint32_t)len); + } + } + +#endif + /* dbg("socket return 0\n"); */ + return 0; +} + +int pico_socket_recvfrom(struct pico_socket *s, void *buf, int len, void *orig, + uint16_t *remote_port) +{ + return pico_socket_recvfrom_extended(s, buf, len, orig, remote_port, NULL); + +} + +int pico_socket_recv(struct pico_socket *s, void *buf, int len) +{ + return pico_socket_recvfrom(s, buf, len, NULL, NULL); +} + + +int pico_socket_getname(struct pico_socket *s, void *local_addr, uint16_t *port, uint16_t *proto) +{ + + if (!s || !local_addr || !port || !proto) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + if (is_sock_ipv4(s)) { + #ifdef PICO_SUPPORT_IPV4 + struct pico_ip4 *ip = (struct pico_ip4 *)local_addr; + ip->addr = s->local_addr.ip4.addr; + *proto = PICO_PROTO_IPV4; + #endif + } else if (is_sock_ipv6(s)) { + #ifdef PICO_SUPPORT_IPV6 + struct pico_ip6 *ip = (struct pico_ip6 *)local_addr; + memcpy(ip->addr, s->local_addr.ip6.addr, PICO_SIZE_IP6); + *proto = PICO_PROTO_IPV6; + #endif + } else { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + *port = s->local_port; + return 0; +} + +int pico_socket_getpeername(struct pico_socket *s, void *remote_addr, uint16_t *port, uint16_t *proto) +{ + if (!s || !remote_addr || !port || !proto) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + if ((s->state & PICO_SOCKET_STATE_CONNECTED) == 0) { + pico_err = PICO_ERR_ENOTCONN; + return -1; + } + + if (is_sock_ipv4(s)) { + #ifdef PICO_SUPPORT_IPV4 + struct pico_ip4 *ip = (struct pico_ip4 *)remote_addr; + ip->addr = s->remote_addr.ip4.addr; + *proto = PICO_PROTO_IPV4; + #endif + } else if (is_sock_ipv6(s)) { + #ifdef PICO_SUPPORT_IPV6 + struct pico_ip6 *ip = (struct pico_ip6 *)remote_addr; + memcpy(ip->addr, s->remote_addr.ip6.addr, PICO_SIZE_IP6); + *proto = PICO_PROTO_IPV6; + #endif + } else { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + *port = s->remote_port; + return 0; + +} + +int pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t *port) +{ + if (!s || !local_addr || !port) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + if (is_sock_ipv4(s)) { + #ifdef PICO_SUPPORT_IPV4 + struct pico_ip4 *ip = (struct pico_ip4 *)local_addr; + if (ip->addr != PICO_IPV4_INADDR_ANY) { + if (!pico_ipv4_link_find(local_addr)) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + #endif + } else if (is_sock_ipv6(s)) { + #ifdef PICO_SUPPORT_IPV6 + struct pico_ip6 *ip = (struct pico_ip6 *)local_addr; + if (!pico_ipv6_is_unspecified(ip->addr)) { + if (!pico_ipv6_link_find(local_addr)) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + #endif + } else { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + + /* When given port = 0, get a random high port to bind to. */ + if (*port == 0) { + *port = pico_socket_high_port(PROTO(s)); + if (*port == 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + if (pico_is_port_free(PROTO(s), *port, local_addr, s->net) == 0) { + pico_err = PICO_ERR_EADDRINUSE; + return -1; + } + + s->local_port = *port; + + if (is_sock_ipv4(s)) { + #ifdef PICO_SUPPORT_IPV4 + struct pico_ip4 *ip = (struct pico_ip4 *)local_addr; + s->local_addr.ip4 = *ip; + #endif + } else if (is_sock_ipv6(s)) { + #ifdef PICO_SUPPORT_IPV6 + struct pico_ip6 *ip = (struct pico_ip6 *)local_addr; + s->local_addr.ip6 = *ip; + #endif + } else { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + return pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0); +} + + +int pico_socket_connect(struct pico_socket *s, const void *remote_addr, uint16_t remote_port) +{ + int ret = -1; + pico_err = PICO_ERR_EPROTONOSUPPORT; + if (!s || remote_addr == NULL || remote_port == 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + s->remote_port = remote_port; + + if (s->local_port == 0) { + s->local_port = pico_socket_high_port(PROTO(s)); + if (!s->local_port) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + if (is_sock_ipv4(s)) { + #ifdef PICO_SUPPORT_IPV4 + struct pico_ip4 *local = NULL; + const struct pico_ip4 *ip = (const struct pico_ip4 *)remote_addr; + s->remote_addr.ip4 = *ip; + local = pico_ipv4_source_find(ip); + if (local) { + get_sock_dev(s); + s->local_addr.ip4 = *local; + } else { + pico_err = PICO_ERR_EHOSTUNREACH; + return -1; + } + + #endif + } else if (is_sock_ipv6(s)) { + #ifdef PICO_SUPPORT_IPV6 + struct pico_ip6 *local = NULL; + const struct pico_ip6 *ip = (const struct pico_ip6 *)remote_addr; + s->remote_addr.ip6 = *ip; + local = pico_ipv6_source_find(ip); + if (local) { + get_sock_dev(s); + s->local_addr.ip6 = *local; + } else { + pico_err = PICO_ERR_EHOSTUNREACH; + return -1; + } + + #endif + } else { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0); + +#ifdef PICO_SUPPORT_UDP + if (PROTO(s) == PICO_PROTO_UDP) { + pico_socket_alter_state(s, PICO_SOCKET_STATE_CONNECTED, 0, 0); + pico_err = PICO_ERR_NOERR; + ret = 0; + } + +#endif + +#ifdef PICO_SUPPORT_TCP + if (PROTO(s) == PICO_PROTO_TCP) { + if (pico_tcp_initconn(s) == 0) { + pico_socket_alter_state(s, PICO_SOCKET_STATE_CONNECTED | PICO_SOCKET_STATE_TCP_SYN_SENT, PICO_SOCKET_STATE_CLOSED, 0); + pico_err = PICO_ERR_NOERR; + ret = 0; + } else { + pico_err = PICO_ERR_EHOSTUNREACH; + } + } + +#endif + + return ret; +} + + +#ifdef PICO_SUPPORT_TCP + +int pico_socket_listen(struct pico_socket *s, int backlog) +{ + if (!s || backlog < 1) { + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + /* check if exists in tree */ + /* See task #178 */ + if (pico_check_socket(s) != 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + } + + if (PROTO(s) == PICO_PROTO_UDP) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { + pico_err = PICO_ERR_EISCONN; + return -1; + } + + if (PROTO(s) == PICO_PROTO_TCP) + pico_socket_alter_state(s, PICO_SOCKET_STATE_TCP_SYN_SENT, 0, PICO_SOCKET_STATE_TCP_LISTEN); + + s->max_backlog = (uint16_t)backlog; + + return 0; +} + +struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *port) +{ + if (!s || !orig || !port) { + pico_err = PICO_ERR_EINVAL; + return NULL; + } + + pico_err = PICO_ERR_EINVAL; + + if ((s->state & PICO_SOCKET_STATE_BOUND) == 0) { + return NULL; + } + + if (PROTO(s) == PICO_PROTO_UDP) { + return NULL; + } + + if (TCPSTATE(s) == PICO_SOCKET_STATE_TCP_LISTEN) { + struct pico_sockport *sp = pico_get_sockport(PICO_PROTO_TCP, s->local_port); + struct pico_socket *found; + uint32_t socklen = sizeof(struct pico_ip4); + /* If at this point no incoming connection socket is found, + * the accept call is valid, but no connection is established yet. + */ + pico_err = PICO_ERR_EAGAIN; + if (sp) { + struct pico_tree_node *index; + /* RB_FOREACH(found, socket_tree, &sp->socks) { */ + pico_tree_foreach(index, &sp->socks){ + found = index->keyValue; + if ((s == found->parent) && ((found->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) { + found->parent = NULL; + pico_err = PICO_ERR_NOERR; + #ifdef PICO_SUPPORT_IPV6 + if (is_sock_ipv6(s)) + socklen = sizeof(struct pico_ip6); + + #endif + memcpy(orig, &found->remote_addr, socklen); + *port = found->remote_port; + s->number_of_pending_conn--; + return found; + } + } + } + } + + return NULL; +} + +#else + +int pico_socket_listen(struct pico_socket *s, int backlog) +{ + IGNORE_PARAMETER(s); + IGNORE_PARAMETER(backlog); + pico_err = PICO_ERR_EINVAL; + return -1; +} + +struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16_t *local_port) +{ + IGNORE_PARAMETER(s); + IGNORE_PARAMETER(orig); + IGNORE_PARAMETER(local_port); + pico_err = PICO_ERR_EINVAL; + return NULL; +} + +#endif + + +int pico_socket_setoption(struct pico_socket *s, int option, void *value) +{ + + if (s == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + + if (PROTO(s) == PICO_PROTO_TCP) + return pico_setsockopt_tcp(s, option, value); + + if (PROTO(s) == PICO_PROTO_UDP) + return pico_setsockopt_udp(s, option, value); + + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + + +int pico_socket_getoption(struct pico_socket *s, int option, void *value) +{ + if (s == NULL) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + + if (PROTO(s) == PICO_PROTO_TCP) + return pico_getsockopt_tcp(s, option, value); + + if (PROTO(s) == PICO_PROTO_UDP) + return pico_getsockopt_udp(s, option, value); + + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + + +int pico_socket_shutdown(struct pico_socket *s, int mode) +{ + if (!s) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + /* Check if the socket has already been closed */ + if (s->state & PICO_SOCKET_STATE_CLOSED) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + +#ifdef PICO_SUPPORT_UDP + if (PROTO(s) == PICO_PROTO_UDP) { + if ((mode & PICO_SHUT_RDWR) == PICO_SHUT_RDWR) + pico_socket_alter_state(s, PICO_SOCKET_STATE_CLOSED, PICO_SOCKET_STATE_CLOSING | PICO_SOCKET_STATE_BOUND | PICO_SOCKET_STATE_CONNECTED, 0); + else if (mode & PICO_SHUT_RD) + pico_socket_alter_state(s, 0, PICO_SOCKET_STATE_BOUND, 0); + } + +#endif +#ifdef PICO_SUPPORT_TCP + if (PROTO(s) == PICO_PROTO_TCP) { + if ((mode & PICO_SHUT_RDWR) == PICO_SHUT_RDWR) + { + pico_socket_alter_state(s, PICO_SOCKET_STATE_SHUT_LOCAL | PICO_SOCKET_STATE_SHUT_REMOTE, 0, 0); + pico_tcp_notify_closing(s); + } + else if (mode & PICO_SHUT_WR) + pico_socket_alter_state(s, PICO_SOCKET_STATE_SHUT_LOCAL, 0, 0); + else if (mode & PICO_SHUT_RD) + pico_socket_alter_state(s, PICO_SOCKET_STATE_SHUT_REMOTE, 0, 0); + + } + +#endif + return 0; +} + +int MOCKABLE pico_socket_close(struct pico_socket *s) +{ + if (!s) + return -1; + +#ifdef PICO_SUPPORT_TCP + if (PROTO(s) == PICO_PROTO_TCP) { + if (pico_tcp_check_listen_close(s) == 0) + return 0; + } + +#endif + return pico_socket_shutdown(s, PICO_SHUT_RDWR); +} + +#ifdef PICO_SUPPORT_CRC +static inline int pico_transport_crc_check(struct pico_frame *f) +{ + struct pico_ipv4_hdr *net_hdr = (struct pico_ipv4_hdr *) f->net_hdr; + struct pico_udp_hdr *udp_hdr = NULL; + uint16_t checksum_invalid = 1; + + switch (net_hdr->proto) + { +#ifdef PICO_SUPPORT_TCP + case PICO_PROTO_TCP: + checksum_invalid = short_be(pico_tcp_checksum(f)); + /* dbg("TCP CRC validation == %u\n", checksum_invalid); */ + if (checksum_invalid) { + dbg("TCP CRC: validation failed!\n"); + pico_frame_discard(f); + return 0; + } + + break; +#endif /* PICO_SUPPORT_TCP */ + +#ifdef PICO_SUPPORT_UDP + case PICO_PROTO_UDP: + udp_hdr = (struct pico_udp_hdr *) f->transport_hdr; + if (short_be(udp_hdr->crc)) { +#ifdef PICO_SUPPORT_IPV4 + if (IS_IPV4(f)) + checksum_invalid = short_be(pico_udp_checksum_ipv4(f)); + +#endif +#ifdef PICO_SUPPORT_IPV6 + if (IS_IPV6(f)) + checksum_invalid = short_be(pico_udp_checksum_ipv6(f)); + +#endif + /* dbg("UDP CRC validation == %u\n", checksum_invalid); */ + if (checksum_invalid) { + /* dbg("UDP CRC: validation failed!\n"); */ + pico_frame_discard(f); + return 0; + } + } + + break; +#endif /* PICO_SUPPORT_UDP */ + + default: + /* Do nothing */ + break; + } + return 1; +} +#else +static inline int pico_transport_crc_check(struct pico_frame *f) +{ + IGNORE_PARAMETER(f); + return 1; +} +#endif /* PICO_SUPPORT_CRC */ + +int pico_transport_process_in(struct pico_protocol *self, struct pico_frame *f) +{ + struct pico_trans *hdr = (struct pico_trans *) f->transport_hdr; + int ret = 0; + + if (!hdr) { + pico_err = PICO_ERR_EFAULT; + return -1; + } + + ret = pico_transport_crc_check(f); + if (ret < 1) + return ret; + else + ret = 0; + + if ((hdr) && (pico_socket_deliver(self, f, hdr->dport) == 0)) + return ret; + + if (!IS_BCAST(f)) { + dbg("Socket not found... \n"); + pico_notify_socket_unreachable(f); + ret = -1; + pico_err = PICO_ERR_ENOENT; + } + + pico_frame_discard(f); + return ret; +} + +#define SL_LOOP_MIN 1 + +#ifdef PICO_SUPPORT_TCP +static int checkSocketSanity(struct pico_socket *s) +{ + + /* checking for pending connections */ + if(TCP_STATE(s) == PICO_SOCKET_STATE_TCP_SYN_RECV) { + if((PICO_TIME_MS() - s->timestamp) >= PICO_SOCKET_BOUND_TIMEOUT) + return -1; + } + + if((PICO_TIME_MS() - s->timestamp) >= PICO_SOCKET_TIMEOUT) { + /* checking for hanging sockets */ + if((TCP_STATE(s) != PICO_SOCKET_STATE_TCP_LISTEN) && (TCP_STATE(s) != PICO_SOCKET_STATE_TCP_ESTABLISHED)) + return -1; + } + + return 0; +} +#endif + + +static int pico_sockets_loop_udp(int loop_score) +{ + +#ifdef PICO_SUPPORT_UDP + static struct pico_tree_node *index_udp; + struct pico_sockport *start; + struct pico_socket *s; + struct pico_frame *f; + + if (sp_udp == NULL) + { + index_udp = pico_tree_firstNode(UDPTable.root); + sp_udp = index_udp->keyValue; + } + + /* init start node */ + start = sp_udp; + + /* round-robin all transport protocols, break if traversed all protocols */ + while (loop_score > SL_LOOP_MIN && sp_udp != NULL) { + struct pico_tree_node *index; + + pico_tree_foreach(index, &sp_udp->socks){ + s = index->keyValue; + f = pico_dequeue(&s->q_out); + while (f && (loop_score > 0)) { + pico_proto_udp.push(&pico_proto_udp, f); + loop_score -= 1; + f = pico_dequeue(&s->q_out); + } + } + + index_udp = pico_tree_next(index_udp); + sp_udp = index_udp->keyValue; + + if (sp_udp == NULL) + { + index_udp = pico_tree_firstNode(UDPTable.root); + sp_udp = index_udp->keyValue; + } + + if (sp_udp == start) + break; + } +#endif + return loop_score; +} + +static int pico_sockets_loop_tcp(int loop_score) +{ +#ifdef PICO_SUPPORT_TCP + struct pico_sockport *start; + struct pico_socket *s; + static struct pico_tree_node *index_tcp; + if (sp_tcp == NULL) + { + index_tcp = pico_tree_firstNode(TCPTable.root); + sp_tcp = index_tcp->keyValue; + } + + /* init start node */ + start = sp_tcp; + + while (loop_score > SL_LOOP_MIN && sp_tcp != NULL) { + struct pico_tree_node *index = NULL, *safe_index = NULL; + pico_tree_foreach_safe(index, &sp_tcp->socks, safe_index){ + s = index->keyValue; + loop_score = pico_tcp_output(s, loop_score); + if ((s->ev_pending) && s->wakeup) { + s->wakeup(s->ev_pending, s); + if(!s->parent) + s->ev_pending = 0; + } + + if (loop_score <= 0) { + loop_score = 0; + break; + } + + if(checkSocketSanity(s) < 0) + { + pico_socket_del(s); + index_tcp = NULL; /* forcing the restart of loop */ + sp_tcp = NULL; + break; + } + } + + /* check if RB_FOREACH ended, if not, break to keep the cur sp_tcp */ + if (!index_tcp || (index && index->keyValue)) + break; + + index_tcp = pico_tree_next(index_tcp); + sp_tcp = index_tcp->keyValue; + + if (sp_tcp == NULL) + { + index_tcp = pico_tree_firstNode(TCPTable.root); + sp_tcp = index_tcp->keyValue; + } + + if (sp_tcp == start) + break; + } +#endif + return loop_score; + + +} + +int pico_sockets_loop(int loop_score) +{ + loop_score = pico_sockets_loop_udp(loop_score); + loop_score = pico_sockets_loop_tcp(loop_score); + return loop_score; +} + +int pico_count_sockets(uint8_t proto) +{ + struct pico_sockport *sp; + struct pico_tree_node *idx_sp, *idx_s; + int count = 0; + + if ((proto == 0) || (proto == PICO_PROTO_TCP)) { + pico_tree_foreach(idx_sp, &TCPTable) { + sp = idx_sp->keyValue; + if (sp) { + pico_tree_foreach(idx_s, &sp->socks) + count++; + } + } + } + + if ((proto == 0) || (proto == PICO_PROTO_UDP)) { + pico_tree_foreach(idx_sp, &UDPTable) { + sp = idx_sp->keyValue; + if (sp) { + pico_tree_foreach(idx_s, &sp->socks) + count++; + } + } + } + + return count; +} + + +struct pico_frame *pico_socket_frame_alloc(struct pico_socket *s, uint16_t len) +{ + struct pico_frame *f = NULL; + +#ifdef PICO_SUPPORT_IPV6 + if (is_sock_ipv6(s)) + f = pico_proto_ipv6.alloc(&pico_proto_ipv6, len); + +#endif + +#ifdef PICO_SUPPORT_IPV4 + if (is_sock_ipv4(s)) + f = pico_proto_ipv4.alloc(&pico_proto_ipv4, len); + +#endif + if (!f) { + pico_err = PICO_ERR_ENOMEM; + return f; + } + + f->payload = f->transport_hdr; + f->payload_len = len; + f->sock = s; + return f; +} + +static void pico_transport_error_set_picoerr(int code) +{ + /* dbg("SOCKET ERROR FROM ICMP NOTIFICATION. (icmp code= %d)\n\n", code); */ + switch(code) { + case PICO_ICMP_UNREACH_NET: + pico_err = PICO_ERR_ENETUNREACH; + break; + + case PICO_ICMP_UNREACH_HOST: + pico_err = PICO_ERR_EHOSTUNREACH; + break; + + case PICO_ICMP_UNREACH_PROTOCOL: + pico_err = PICO_ERR_ENOPROTOOPT; + break; + + case PICO_ICMP_UNREACH_PORT: + pico_err = PICO_ERR_ECONNREFUSED; + break; + + case PICO_ICMP_UNREACH_NET_UNKNOWN: + pico_err = PICO_ERR_ENETUNREACH; + break; + + case PICO_ICMP_UNREACH_HOST_UNKNOWN: + pico_err = PICO_ERR_EHOSTDOWN; + break; + + case PICO_ICMP_UNREACH_ISOLATED: + pico_err = PICO_ERR_ENONET; + break; + + case PICO_ICMP_UNREACH_NET_PROHIB: + case PICO_ICMP_UNREACH_HOST_PROHIB: + pico_err = PICO_ERR_EHOSTUNREACH; + break; + + default: + pico_err = PICO_ERR_EOPNOTSUPP; + } +} + +int pico_transport_error(struct pico_frame *f, uint8_t proto, int code) +{ + int ret = -1; + struct pico_trans *trans = (struct pico_trans*) f->transport_hdr; + struct pico_sockport *port = NULL; + struct pico_socket *s = NULL; + switch (proto) { + + +#ifdef PICO_SUPPORT_UDP + case PICO_PROTO_UDP: + port = pico_get_sockport(proto, trans->sport); + break; +#endif + +#ifdef PICO_SUPPORT_TCP + case PICO_PROTO_TCP: + port = pico_get_sockport(proto, trans->sport); + break; +#endif + + default: + /* Protocol not available */ + ret = -1; + } + if (port) { + struct pico_tree_node *index; + ret = 0; + + pico_tree_foreach(index, &port->socks) { + s = index->keyValue; + if (trans->dport == s->remote_port) { + if (s->wakeup) { + pico_transport_error_set_picoerr(code); + s->state |= PICO_SOCKET_STATE_SHUT_REMOTE; + s->wakeup(PICO_SOCK_EV_ERR, s); + } + + break; + } + } + } + + pico_frame_discard(f); + return ret; +} +#endif +#endif diff --git a/net/picotcp/stack/pico_socket_multicast.c b/net/picotcp/stack/pico_socket_multicast.c new file mode 100644 index 0000000..f23327d --- /dev/null +++ b/net/picotcp/stack/pico_socket_multicast.c @@ -0,0 +1,956 @@ +#include "pico_config.h" +#include "pico_stack.h" +#include "pico_socket.h" +#include "pico_socket_multicast.h" +#include "pico_tree.h" +#include "pico_ipv4.h" +#include "pico_udp.h" + +#ifdef PICO_SUPPORT_MCAST +# define so_mcast_dbg(...) do {} while(0) /* ip_mcast_dbg in pico_ipv4.c */ +/* #define so_mcast_dbg dbg */ + +/* socket + * | + * MCASTListen + * | | | + * ------------ | ------------ + * | | | + * MCASTSources MCASTSources MCASTSources + * | | | | | | | | | | | | + * S S S S S S S S S S S S + * + * MCASTListen: RBTree(mcast_link, mcast_group) + * MCASTSources: RBTree(source) + */ +struct pico_mcast_listen +{ + uint8_t filter_mode; + union pico_address mcast_link; + union pico_address mcast_group; + struct pico_tree MCASTSources; + uint16_t proto; +}; + +static int mcast_listen_link_cmp(struct pico_mcast_listen *a, struct pico_mcast_listen *b) +{ + + if (a->proto < b->proto) + return -1; + + if (a->proto > b->proto) + return 1; + + return pico_address_compare(&a->mcast_link, &b->mcast_link, a->proto); +} + +static int mcast_listen_grp_cmp(struct pico_mcast_listen *a, struct pico_mcast_listen *b) +{ + if (a->mcast_group.ip4.addr < b->mcast_group.ip4.addr) + return -1; + + if (a->mcast_group.ip4.addr > b->mcast_group.ip4.addr) + return 1; + + return mcast_listen_link_cmp(a, b); +} + +static int mcast_listen_cmp(void *ka, void *kb) +{ + struct pico_mcast_listen *a = ka, *b = kb; + if (a->proto < b->proto) + return -1; + + if (a->proto > b->proto) + return 1; + + return mcast_listen_grp_cmp(a, b); +} + +static int mcast_sources_cmp(void *ka, void *kb) +{ + union pico_address *a = ka, *b = kb; + if (a->ip4.addr < b->ip4.addr) + return -1; + + if (a->ip4.addr > b->ip4.addr) + return 1; + + return 0; +} + +static int mcast_socket_cmp(void *ka, void *kb) +{ + struct pico_socket *a = ka, *b = kb; + if (a < b) + return -1; + + if (a > b) + return 1; + + return 0; +} + +/* gather all multicast sockets to hasten filter aggregation */ +PICO_TREE_DECLARE(MCASTSockets, mcast_socket_cmp); + +static int mcast_filter_cmp(void *ka, void *kb) +{ + union pico_address *a = ka, *b = kb; + if (a->ip4.addr < b->ip4.addr) + return -1; + + if (a->ip4.addr > b->ip4.addr) + return 1; + + return 0; +} +/* gather sources to be filtered */ +PICO_TREE_DECLARE(MCASTFilter, mcast_filter_cmp); + +static struct pico_mcast_listen *listen_find(struct pico_socket *s, union pico_address *lnk, union pico_address *grp) +{ + struct pico_mcast_listen ltest = { + 0 + }; + ltest.mcast_link.ip4.addr = lnk->ip4.addr; + ltest.mcast_group.ip4.addr = grp->ip4.addr; + return pico_tree_findKey(s->MCASTListen, <est); +} + +static uint8_t pico_mcast_filter_excl_excl(struct pico_mcast_listen *listen) +{ + /* filter = intersection of EXCLUDEs */ + /* any record with filter mode EXCLUDE, causes the interface mode to be EXCLUDE */ + /* remove from the interface EXCLUDE filter any source not in the socket EXCLUDE filter */ + struct pico_tree_node *index = NULL, *_tmp = NULL; + union pico_address *source = NULL; + pico_tree_foreach_safe(index, &MCASTFilter, _tmp) + { + source = pico_tree_findKey(&listen->MCASTSources, index->keyValue); + if (!source) + pico_tree_delete(&MCASTFilter, index->keyValue); + } + return PICO_IP_MULTICAST_EXCLUDE; +} + +static uint8_t pico_mcast_filter_excl_incl(struct pico_mcast_listen *listen) +{ + /* filter = EXCLUDE - INCLUDE */ + /* any record with filter mode EXCLUDE, causes the interface mode to be EXCLUDE */ + /* remove from the interface EXCLUDE filter any source in the socket INCLUDE filter */ + struct pico_tree_node *index = NULL, *_tmp = NULL; + union pico_address *source = NULL; + pico_tree_foreach_safe(index, &listen->MCASTSources, _tmp) + { + source = pico_tree_findKey(&MCASTFilter, index->keyValue); + if (source) + pico_tree_delete(&MCASTFilter, source); + } + return PICO_IP_MULTICAST_EXCLUDE; +} + +static uint8_t pico_mcast_filter_incl_excl(struct pico_mcast_listen *listen) +{ + /* filter = EXCLUDE - INCLUDE */ + /* delete from the interface INCLUDE filter any source NOT in the socket EXCLUDE filter */ + struct pico_tree_node *index = NULL, *_tmp = NULL, *index2 = NULL, *_tmp2 = NULL; + union pico_address *source = NULL; + pico_tree_foreach_safe(index2, &MCASTFilter, _tmp2) + { + source = pico_tree_findKey(&listen->MCASTSources, index2->keyValue); + if (!source) + pico_tree_delete(&MCASTFilter, index2->keyValue); + } + /* any record with filter mode EXCLUDE, causes the interface mode to be EXCLUDE */ + + /* add to the interface EXCLUDE filter any socket source NOT in the former interface INCLUDE filter */ + pico_tree_foreach_safe(index, &listen->MCASTSources, _tmp) + { + source = pico_tree_insert(&MCASTFilter, index->keyValue); + if (source) + pico_tree_delete(&MCASTFilter, source); + } + return PICO_IP_MULTICAST_EXCLUDE; +} + +static uint8_t pico_mcast_filter_incl_incl(struct pico_mcast_listen *listen) +{ + /* filter = summation of INCLUDEs */ + /* mode stays INCLUDE, add all sources to filter */ + struct pico_tree_node *index = NULL, *_tmp = NULL; + union pico_address *source = NULL; + pico_tree_foreach_safe(index, &listen->MCASTSources, _tmp) + { + source = index->keyValue; + pico_tree_insert(&MCASTFilter, source); + } + return PICO_IP_MULTICAST_INCLUDE; +} + +struct pico_mcast_filter_aggregation +{ + uint8_t (*call)(struct pico_mcast_listen *); +}; + +static const struct pico_mcast_filter_aggregation mcast_filter_aggr_call[2][2] = +{ + { + /* EXCL + EXCL */ {.call = pico_mcast_filter_excl_excl}, + /* EXCL + INCL */ {.call = pico_mcast_filter_excl_incl} + }, + + { + /* INCL + EXCL */ {.call = pico_mcast_filter_incl_excl}, + /* INCL + INCL */ {.call = pico_mcast_filter_incl_incl} + } +}; + +static int mcast_aggr_validate(uint8_t fm, struct pico_mcast_listen *l) +{ + if (!l) + return -1; + + if (fm > 1) + return -1; + + if (l->filter_mode > 1) + return -1; + + return 0; +} + + +/* MCASTFilter will be empty if no socket is listening on mcast_group on mcast_link anymore */ +static int pico_socket_aggregate_mcastfilters(union pico_address *mcast_link, union pico_address *mcast_group) +{ + uint8_t filter_mode = PICO_IP_MULTICAST_INCLUDE; + struct pico_mcast_listen *listen = NULL; + struct pico_socket *mcast_sock = NULL; + struct pico_tree_node *index = NULL, *_tmp = NULL; + + + /* cleanup old filter */ + pico_tree_foreach_safe(index, &MCASTFilter, _tmp) + { + pico_tree_delete(&MCASTFilter, index->keyValue); + } + + /* construct new filter */ + pico_tree_foreach_safe(index, &MCASTSockets, _tmp) + { + mcast_sock = index->keyValue; + listen = listen_find(mcast_sock, mcast_link, mcast_group); + if (listen) { + if (mcast_aggr_validate(filter_mode, listen) < 0) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + if (mcast_filter_aggr_call[filter_mode][listen->filter_mode].call) { + filter_mode = mcast_filter_aggr_call[filter_mode][listen->filter_mode].call(listen); + if (filter_mode > 1) + return -1; + } + } + } + return filter_mode; +} + +static int pico_socket_mcast_filter_include(struct pico_mcast_listen *listen, union pico_address *src) +{ + struct pico_tree_node *index = NULL; + pico_tree_foreach(index, &listen->MCASTSources) + { + if (src->ip4.addr == ((union pico_address *)index->keyValue)->ip4.addr) { + so_mcast_dbg("MCAST: IP %08X in included socket source list\n", src->ip4.addr); + return 0; + } + } + so_mcast_dbg("MCAST: IP %08X NOT in included socket source list\n", src->ip4.addr); + return -1; + +} + +static int pico_socket_mcast_filter_exclude(struct pico_mcast_listen *listen, union pico_address *src) +{ + struct pico_tree_node *index = NULL; + pico_tree_foreach(index, &listen->MCASTSources) + { + if (src->ip4.addr == ((union pico_address *)index->keyValue)->ip4.addr) { + so_mcast_dbg("MCAST: IP %08X in excluded socket source list\n", src->ip4.addr); + return -1; + } + } + so_mcast_dbg("MCAST: IP %08X NOT in excluded socket source list\n", src->ip4.addr); + return 0; +} + +static int pico_socket_mcast_source_filtering(struct pico_mcast_listen *listen, union pico_address *src) +{ + /* perform source filtering */ + if (listen->filter_mode == PICO_IP_MULTICAST_INCLUDE) + return pico_socket_mcast_filter_include(listen, src); + + if (listen->filter_mode == PICO_IP_MULTICAST_EXCLUDE) + return pico_socket_mcast_filter_exclude(listen, src); + + return -1; +} + +static struct pico_ipv4_link *pico_socket_mcast_filter_link_get(struct pico_socket *s) +{ + /* check if no multicast enabled on socket */ + if (!s->MCASTListen) + return NULL; + + if (!s->local_addr.ip4.addr) + return pico_ipv4_get_default_mcastlink(); + + return pico_ipv4_link_get(&s->local_addr.ip4); +} + +int pico_socket_mcast_filter(struct pico_socket *s, union pico_address *mcast_group, union pico_address *src) +{ + struct pico_ipv4_link *mcast_link = NULL; + struct pico_mcast_listen *listen = NULL; + + mcast_link = pico_socket_mcast_filter_link_get(s); + if (!mcast_link) + return -1; + + listen = listen_find(s, (union pico_address *)&mcast_link->address, mcast_group); + if (!listen) + return -1; + + return pico_socket_mcast_source_filtering(listen, src); +} + +static struct pico_ipv4_link *get_mcast_link(union pico_address *a) +{ + if (!a->ip4.addr) + return pico_ipv4_get_default_mcastlink(); + + return pico_ipv4_link_get(&a->ip4); +} + + +static int pico_socket_setoption_pre_validation(struct pico_ip_mreq *mreq) +{ + if (!mreq) + return -1; + + if (!mreq->mcast_group_addr.addr) + return -1; + + return 0; +} + +static struct pico_ipv4_link *pico_socket_setoption_validate_mreq(struct pico_ip_mreq *mreq) +{ + if (pico_socket_setoption_pre_validation(mreq) < 0) + return NULL; + + if (pico_ipv4_is_unicast(mreq->mcast_group_addr.addr)) + return NULL; + + return get_mcast_link((union pico_address *)&mreq->mcast_link_addr); +} + +static int pico_socket_setoption_pre_validation_s(struct pico_ip_mreq_source *mreq) +{ + if (!mreq) + return -1; + + if (!mreq->mcast_group_addr.addr) + return -1; + + return 0; +} + +static struct pico_ipv4_link *pico_socket_setoption_validate_s_mreq(struct pico_ip_mreq_source *mreq) +{ + if (pico_socket_setoption_pre_validation_s(mreq) < 0) + return NULL; + + if (pico_ipv4_is_unicast(mreq->mcast_group_addr.addr)) + return NULL; + + if (!pico_ipv4_is_unicast(mreq->mcast_source_addr.addr)) + return NULL; + + return get_mcast_link((union pico_address *)&mreq->mcast_link_addr); +} + + +static struct pico_ipv4_link *setop_multicast_link_search(void *value, int bysource) +{ + + struct pico_ip_mreq *mreq = NULL; + struct pico_ipv4_link *mcast_link = NULL; + struct pico_ip_mreq_source *mreq_src = NULL; + if (!bysource) { + mreq = (struct pico_ip_mreq *) value; + mcast_link = pico_socket_setoption_validate_mreq(mreq); + if (!mcast_link) + return NULL; + + if (!mreq->mcast_link_addr.addr) + mreq->mcast_link_addr.addr = mcast_link->address.addr; + } else { + mreq_src = (struct pico_ip_mreq_source *) value; + if (!mreq_src) + return NULL; + + mcast_link = pico_socket_setoption_validate_s_mreq(mreq_src); + if (!mcast_link) + return NULL; + + if (!mreq_src->mcast_link_addr.addr) + mreq_src->mcast_link_addr.addr = mcast_link->address.addr; + } + + return mcast_link; +} + +static int setop_verify_listen_tree(struct pico_socket *s, int alloc) +{ + if(!alloc) + return -1; + + s->MCASTListen = PICO_ZALLOC(sizeof(struct pico_tree)); + if (!s->MCASTListen) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + s->MCASTListen->root = &LEAF; + s->MCASTListen->compare = mcast_listen_cmp; + return 0; +} + + +static struct pico_ipv4_link *setopt_multicast_check(struct pico_socket *s, void *value, int alloc, int bysource) +{ + struct pico_ipv4_link *mcast_link = NULL; + + if (!value) { + pico_err = PICO_ERR_EINVAL; + return NULL; + } + + mcast_link = setop_multicast_link_search(value, bysource); + + if (!mcast_link) { + pico_err = PICO_ERR_EINVAL; + return NULL; + } + + if (!s->MCASTListen) { /* No RBTree allocated yet */ + if (setop_verify_listen_tree(s, alloc) < 0) + return NULL; + } + + return mcast_link; +} + + +void pico_multicast_delete(struct pico_socket *s) +{ + int filter_mode; + struct pico_tree_node *index = NULL, *_tmp = NULL, *index2 = NULL, *_tmp2 = NULL; + struct pico_mcast_listen *listen = NULL; + union pico_address *source = NULL; + if (s->MCASTListen) { + pico_tree_delete(&MCASTSockets, s); + pico_tree_foreach_safe(index, s->MCASTListen, _tmp) + { + listen = index->keyValue; + pico_tree_foreach_safe(index2, &listen->MCASTSources, _tmp2) + { + source = index->keyValue; + pico_tree_delete(&listen->MCASTSources, source); + PICO_FREE(source); + } + filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&listen->mcast_link, (union pico_address *)&listen->mcast_group); + if (filter_mode >= 0) + pico_ipv4_mcast_leave(&listen->mcast_link.ip4, &listen->mcast_group.ip4, 1, (uint8_t)filter_mode, &MCASTFilter); + + pico_tree_delete(s->MCASTListen, listen); + PICO_FREE(listen); + } + PICO_FREE(s->MCASTListen); + } +} + + +int pico_getsockopt_mcast(struct pico_socket *s, int option, void *value) +{ + switch(option) { + case PICO_IP_MULTICAST_IF: + pico_err = PICO_ERR_EOPNOTSUPP; + return -1; + + case PICO_IP_MULTICAST_TTL: + if (s->proto->proto_number == PICO_PROTO_UDP) { + pico_udp_get_mc_ttl(s, (uint8_t *) value); + } else { + *(uint8_t *)value = 0; + pico_err = PICO_ERR_EINVAL; + return -1; + } + + break; + + case PICO_IP_MULTICAST_LOOP: + if (s->proto->proto_number == PICO_PROTO_UDP) { + *(uint8_t *)value = (uint8_t)PICO_SOCKET_GETOPT(s, PICO_SOCKET_OPT_MULTICAST_LOOP); + } else { + *(uint8_t *)value = 0; + pico_err = PICO_ERR_EINVAL; + return -1; + } + + break; + default: + pico_err = PICO_ERR_EINVAL; + return -1; + } + + return 0; +} + +static int mcast_so_loop(struct pico_socket *s, void *value) +{ + uint8_t val = (*(uint8_t *)value); + if (val == 0u) { + PICO_SOCKET_SETOPT_DIS(s, PICO_SOCKET_OPT_MULTICAST_LOOP); + return 0; + } else if (val == 1u) { + PICO_SOCKET_SETOPT_EN(s, PICO_SOCKET_OPT_MULTICAST_LOOP); + return 0; + } + + pico_err = PICO_ERR_EINVAL; + return -1; +} + +static int mcast_so_addm(struct pico_socket *s, void *value) +{ + int filter_mode; + struct pico_mcast_listen *listen; + struct pico_ip_mreq *mreq = (struct pico_ip_mreq *)value; + struct pico_ipv4_link *mcast_link = setopt_multicast_check(s, value, 1, 0); + if (!mcast_link) + return -1; + + listen = listen_find(s, (union pico_address *)&mreq->mcast_link_addr, (union pico_address *)&mreq->mcast_group_addr); + if (listen) { + if (listen->filter_mode != PICO_IP_MULTICAST_EXCLUDE) { + so_mcast_dbg("pico_socket_setoption: ERROR any-source multicast (exclude) on source-specific multicast (include)\n"); + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + so_mcast_dbg("pico_socket_setoption: ERROR duplicate PICO_IP_ADD_MEMBERSHIP\n"); + pico_err = PICO_ERR_EINVAL; + return -1; + } + } else { + listen = PICO_ZALLOC(sizeof(struct pico_mcast_listen)); + if (!listen) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + listen->filter_mode = PICO_IP_MULTICAST_EXCLUDE; + listen->mcast_link.ip4 = mreq->mcast_link_addr; + listen->mcast_group.ip4 = mreq->mcast_group_addr; + listen->MCASTSources.root = &LEAF; + listen->MCASTSources.compare = mcast_sources_cmp; + listen->proto = s->net->proto_number; + pico_tree_insert(s->MCASTListen, listen); + } + + pico_tree_insert(&MCASTSockets, s); + filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr); + if (filter_mode < 0) + return -1; + + so_mcast_dbg("PICO_IP_ADD_MEMBERSHIP - success, added %p\n", s); + return pico_ipv4_mcast_join(&mreq->mcast_link_addr, &mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter); +} + +static int mcast_so_dropm(struct pico_socket *s, void *value) +{ + int filter_mode = 0; + struct pico_mcast_listen *listen; + struct pico_ip_mreq *mreq = (struct pico_ip_mreq *)value; + union pico_address *source = NULL; + struct pico_tree_node *index, *_tmp; + struct pico_ipv4_link *mcast_link = setopt_multicast_check(s, value, 0, 0); + if (!mcast_link) + return -1; + + listen = listen_find(s, (union pico_address *)&mreq->mcast_link_addr, (union pico_address *)&mreq->mcast_group_addr); + if (!listen) { + so_mcast_dbg("pico_socket_setoption: ERROR PICO_IP_DROP_MEMBERSHIP before PICO_IP_ADD_MEMBERSHIP/SOURCE_MEMBERSHIP\n"); + pico_err = PICO_ERR_EADDRNOTAVAIL; + return -1; + } else { + pico_tree_foreach_safe(index, &listen->MCASTSources, _tmp) + { + source = index->keyValue; + pico_tree_delete(&listen->MCASTSources, source); + } + pico_tree_delete(s->MCASTListen, listen); + PICO_FREE(listen); + if (pico_tree_empty(s->MCASTListen)) { + PICO_FREE(s->MCASTListen); + s->MCASTListen = NULL; + pico_tree_delete(&MCASTSockets, s); + } + } + + filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr); + if (filter_mode < 0) + return -1; + + return pico_ipv4_mcast_leave(&mreq->mcast_link_addr, &mreq->mcast_group_addr, 1, (uint8_t)filter_mode, &MCASTFilter); +} + +static int mcast_so_unblock_src(struct pico_socket *s, void *value) +{ + int filter_mode = 0; + struct pico_ip_mreq_source *mreq = (struct pico_ip_mreq_source *)value; + struct pico_mcast_listen *listen = NULL; + union pico_address *source = NULL, stest; + struct pico_ipv4_link *mcast_link = setopt_multicast_check(s, value, 0, 1); + + memset(&stest, 0, sizeof(union pico_address)); + if (!mcast_link) + return -1; + + + listen = listen_find(s, (union pico_address *) &mreq->mcast_link_addr, (union pico_address *) &mreq->mcast_group_addr); + if (!listen) { + so_mcast_dbg("pico_socket_setoption: ERROR PICO_IP_UNBLOCK_SOURCE before PICO_IP_ADD_MEMBERSHIP\n"); + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + if (listen->filter_mode != PICO_IP_MULTICAST_EXCLUDE) { + so_mcast_dbg("pico_socket_setoption: ERROR any-source multicast (exclude) on source-specific multicast (include)\n"); + pico_err = PICO_ERR_EINVAL; + return -1; + } + + stest.ip4.addr = mreq->mcast_source_addr.addr; + source = pico_tree_findKey(&listen->MCASTSources, &stest); + if (!source) { + so_mcast_dbg("pico_socket_setoption: ERROR address to unblock not in source list\n"); + pico_err = PICO_ERR_EADDRNOTAVAIL; + return -1; + } else { + pico_tree_delete(&listen->MCASTSources, source); + } + } + + filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr); + if (filter_mode < 0) + return -1; + + return pico_ipv4_mcast_leave(&mreq->mcast_link_addr, &mreq->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter); +} + +static int mcast_so_block_src(struct pico_socket *s, void *value) +{ + int filter_mode = 0; + struct pico_ip_mreq_source *mreq = (struct pico_ip_mreq_source *)value; + struct pico_mcast_listen *listen; + union pico_address *source, stest; + struct pico_ipv4_link *mcast_link = setopt_multicast_check(s, value, 0, 1); + if (!mcast_link) + return -1; + + memset(&stest, 0, sizeof(union pico_address)); + + listen = listen_find(s, (union pico_address *)&mreq->mcast_link_addr, (union pico_address *)&mreq->mcast_group_addr); + if (!listen) { + dbg("pico_socket_setoption: ERROR PICO_IP_BLOCK_SOURCE before PICO_IP_ADD_MEMBERSHIP\n"); + pico_err = PICO_ERR_EINVAL; + return -1; + } else { + if (listen->filter_mode != PICO_IP_MULTICAST_EXCLUDE) { + so_mcast_dbg("pico_socket_setoption: ERROR any-source multicast (exclude) on source-specific multicast (include)\n"); + pico_err = PICO_ERR_EINVAL; + return -1; + } + + stest.ip4.addr = mreq->mcast_source_addr.addr; + source = pico_tree_findKey(&listen->MCASTSources, &stest); + if (source) { + so_mcast_dbg("pico_socket_setoption: ERROR address to block already in source list\n"); + pico_err = PICO_ERR_EADDRNOTAVAIL; + return -1; + } else { + source = PICO_ZALLOC(sizeof(union pico_address)); + if (!source) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + source->ip4.addr = mreq->mcast_source_addr.addr; + pico_tree_insert(&listen->MCASTSources, source); + } + } + + filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr); + if (filter_mode < 0) + return -1; + + return pico_ipv4_mcast_join(&mreq->mcast_link_addr, &mreq->mcast_group_addr, 0, (uint8_t)filter_mode, &MCASTFilter); +} + +static int mcast_so_addsrcm(struct pico_socket *s, void *value) +{ + int filter_mode = 0, reference_count = 0; + struct pico_ip_mreq_source *mreq = (struct pico_ip_mreq_source *)value; + struct pico_mcast_listen *listen = NULL; + union pico_address *source = NULL, stest; + struct pico_ipv4_link *mcast_link = setopt_multicast_check(s, value, 1, 1); + if (!mcast_link) + return -1; + + memset(&stest, 0, sizeof(union pico_address)); + + listen = listen_find(s, (union pico_address *)&mreq->mcast_link_addr, (union pico_address *) &mreq->mcast_group_addr); + if (listen) { + if (listen->filter_mode != PICO_IP_MULTICAST_INCLUDE) { + so_mcast_dbg("pico_socket_setoption: ERROR source-specific multicast (include) on any-source multicast (exclude)\n"); + pico_err = PICO_ERR_EINVAL; + return -1; + } + + stest.ip4.addr = mreq->mcast_source_addr.addr; + source = pico_tree_findKey(&listen->MCASTSources, &stest); + if (source) { + so_mcast_dbg("pico_socket_setoption: ERROR source address to allow already in source list\n"); + pico_err = PICO_ERR_EADDRNOTAVAIL; + return -1; + } else { + source = PICO_ZALLOC(sizeof(union pico_address)); + if (!source) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + source->ip4.addr = mreq->mcast_source_addr.addr; + pico_tree_insert(&listen->MCASTSources, source); + } + } else { + listen = PICO_ZALLOC(sizeof(struct pico_mcast_listen)); + if (!listen) { + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + listen->filter_mode = PICO_IP_MULTICAST_INCLUDE; + listen->mcast_link.ip4 = mreq->mcast_link_addr; + listen->mcast_group.ip4 = mreq->mcast_group_addr; + listen->MCASTSources.root = &LEAF; + listen->MCASTSources.compare = mcast_sources_cmp; + source = PICO_ZALLOC(sizeof(union pico_address)); + if (!source) { + PICO_FREE(listen); + pico_err = PICO_ERR_ENOMEM; + return -1; + } + + source->ip4.addr = mreq->mcast_source_addr.addr; + pico_tree_insert(&listen->MCASTSources, source); + pico_tree_insert(s->MCASTListen, listen); + reference_count = 1; + } + + pico_tree_insert(&MCASTSockets, s); + filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr); + if (filter_mode < 0) + return -1; + + return pico_ipv4_mcast_join(&mreq->mcast_link_addr, &mreq->mcast_group_addr, (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter); +} + +static int mcast_so_dropsrcm(struct pico_socket *s, void *value) +{ + int filter_mode = 0, reference_count = 0; + struct pico_ip_mreq_source *mreq = (struct pico_ip_mreq_source *)value; + struct pico_mcast_listen *listen; + union pico_address *source, stest; + struct pico_ipv4_link *mcast_link = setopt_multicast_check(s, value, 0, 1); + if (!mcast_link) + return -1; + + memset(&stest, 0, sizeof(union pico_address)); + + listen = listen_find(s, (union pico_address *)&mreq->mcast_link_addr, (union pico_address *)&mreq->mcast_group_addr); + if (!listen) { + so_mcast_dbg("pico_socket_setoption: ERROR PICO_IP_DROP_SOURCE_MEMBERSHIP before PICO_IP_ADD_SOURCE_MEMBERSHIP\n"); + pico_err = PICO_ERR_EADDRNOTAVAIL; + return -1; + } else { + if (listen->filter_mode != PICO_IP_MULTICAST_INCLUDE) { + so_mcast_dbg("pico_socket_setoption: ERROR source-specific multicast (include) on any-source multicast (exclude)\n"); + pico_err = PICO_ERR_EINVAL; + return -1; + } + + stest.ip4.addr = mreq->mcast_source_addr.addr; + source = pico_tree_findKey(&listen->MCASTSources, &stest); + if (!source) { + so_mcast_dbg("pico_socket_setoption: ERROR address to drop not in source list\n"); + pico_err = PICO_ERR_EADDRNOTAVAIL; + return -1; + } else { + pico_tree_delete(&listen->MCASTSources, source); + if (pico_tree_empty(&listen->MCASTSources)) { /* 1 if empty, 0 otherwise */ + reference_count = 1; + pico_tree_delete(s->MCASTListen, listen); + PICO_FREE(listen); + if (pico_tree_empty(s->MCASTListen)) { + PICO_FREE(s->MCASTListen); + s->MCASTListen = NULL; + pico_tree_delete(&MCASTSockets, s); + } + } + } + } + + filter_mode = pico_socket_aggregate_mcastfilters((union pico_address *)&mcast_link->address, (union pico_address *)&mreq->mcast_group_addr); + if (filter_mode < 0) + return -1; + + return pico_ipv4_mcast_leave(&mreq->mcast_link_addr, &mreq->mcast_group_addr, (uint8_t)reference_count, (uint8_t)filter_mode, &MCASTFilter); +} + +struct pico_setsockopt_mcast_call +{ + int option; + int (*call)(struct pico_socket *, void *); +}; + +static const struct pico_setsockopt_mcast_call mcast_so_calls[1 + PICO_IP_DROP_SOURCE_MEMBERSHIP - PICO_IP_MULTICAST_IF] = +{ + { PICO_IP_MULTICAST_IF, NULL }, + { PICO_IP_MULTICAST_TTL, pico_udp_set_mc_ttl }, + { PICO_IP_MULTICAST_LOOP, mcast_so_loop }, + { PICO_IP_ADD_MEMBERSHIP, mcast_so_addm }, + { PICO_IP_DROP_MEMBERSHIP, mcast_so_dropm }, + { PICO_IP_UNBLOCK_SOURCE, mcast_so_unblock_src }, + { PICO_IP_BLOCK_SOURCE, mcast_so_block_src }, + { PICO_IP_ADD_SOURCE_MEMBERSHIP, mcast_so_addsrcm }, + { PICO_IP_DROP_SOURCE_MEMBERSHIP, mcast_so_dropsrcm } +}; + + +static int mcast_so_check_socket(struct pico_socket *s) +{ + pico_err = PICO_ERR_EINVAL; + if (!s) + return -1; + + if (!s->proto) + return -1; + + if (s->proto->proto_number != PICO_PROTO_UDP) + return -1; + + pico_err = PICO_ERR_NOERR; + return 0; +} + +int pico_setsockopt_mcast(struct pico_socket *s, int option, void *value) +{ + int arrayn = option - PICO_IP_MULTICAST_IF; + if (option < PICO_IP_MULTICAST_IF || option > PICO_IP_DROP_SOURCE_MEMBERSHIP) { + pico_err = PICO_ERR_EOPNOTSUPP; + return -1; + } + + if (mcast_so_check_socket(s) < 0) + return -1; + + if (!mcast_so_calls[arrayn].call) { + pico_err = PICO_ERR_EOPNOTSUPP; + return -1; + } + + return (mcast_so_calls[arrayn].call(s, value)); +} + +int pico_udp_set_mc_ttl(struct pico_socket *s, void *_ttl) +{ + struct pico_socket_udp *u; + uint8_t ttl = *(uint8_t *)_ttl; + if(!s) { + pico_err = PICO_ERR_EINVAL; + return -1; + } + + u = (struct pico_socket_udp *) s; + u->mc_ttl = ttl; + return 0; +} + +int pico_udp_get_mc_ttl(struct pico_socket *s, uint8_t *ttl) +{ + struct pico_socket_udp *u; + if(!s) + return -1; + + u = (struct pico_socket_udp *) s; + *ttl = u->mc_ttl; + return 0; +} +#else +int pico_udp_set_mc_ttl(struct pico_socket *s, void *_ttl) +{ + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + +int pico_udp_get_mc_ttl(struct pico_socket *s, uint8_t *ttl) +{ + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + +int pico_socket_mcast_filter(struct pico_socket *s, union pico_address *mcast_group, union pico_address *src) +{ + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + +void pico_multicast_delete(struct pico_socket *s) +{ + (void)s; +} + +int pico_getsockopt_mcast(struct pico_socket *s, int option, void *value) +{ + (void)s; + (void)option; + (void)value; + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; +} + +int pico_setsockopt_mcast(struct pico_socket *s, int option, void *value) +{ + (void)s; + (void)option; + (void)value; + pico_err = PICO_ERR_EPROTONOSUPPORT; + return -1; + +} +#endif /* PICO_SUPPORT_MCAST */ + diff --git a/net/picotcp/stack/pico_stack.c b/net/picotcp/stack/pico_stack.c new file mode 100644 index 0000000..b4fcdd6 --- /dev/null +++ b/net/picotcp/stack/pico_stack.c @@ -0,0 +1,1157 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + . + + Authors: Daniele Lacamera + *********************************************************************/ + + +#include "pico_config.h" +#include "pico_frame.h" +#include "pico_device.h" +#include "pico_protocol.h" +#include "pico_stack.h" +#include "pico_addressing.h" +#include "pico_dns_client.h" + +#include "pico_olsr.h" +#include "pico_aodv.h" +#include "pico_eth.h" +#include "pico_arp.h" +#include "pico_ipv4.h" +#include "pico_ipv6.h" +#include "pico_icmp4.h" +#include "pico_icmp6.h" +#include "pico_igmp.h" +#include "pico_udp.h" +#include "pico_tcp.h" +#include "pico_socket.h" +#include "heap.h" + +#define IS_LIMITED_BCAST(f) (((struct pico_ipv4_hdr *) f->net_hdr)->dst.addr == PICO_IP4_BCAST) + +const uint8_t PICO_ETHADDR_ALL[6] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; + +# define PICO_SIZE_MCAST 3 +const uint8_t PICO_ETHADDR_MCAST[6] = { + 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 +}; + +#ifdef PICO_SUPPORT_IPV6 +# define PICO_SIZE_MCAST6 2 +const uint8_t PICO_ETHADDR_MCAST6[6] = { + 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 +}; +#endif + + +volatile pico_time pico_tick; +volatile pico_err_t pico_err; + +static uint32_t _rand_seed; + +void WEAK pico_rand_feed(uint32_t feed) +{ + if (!feed) + return; + + _rand_seed *= 1664525; + _rand_seed += 1013904223; + _rand_seed ^= ~(feed); +} + +uint32_t WEAK pico_rand(void) +{ + pico_rand_feed((uint32_t)pico_tick); + return _rand_seed; +} + +void pico_to_lowercase(char *str) +{ + int i = 0; + if (!str) + return; + + while(str[i]) { + if ((str[i] <= 'Z') && (str[i] >= 'A')) + str[i] = (char) (str[i] - (char)('A' - 'a')); + + i++; + } +} + +/* NOTIFICATIONS: distributed notifications for stack internal errors. + */ + +int pico_notify_socket_unreachable(struct pico_frame *f) +{ + if (0) {} + +#ifdef PICO_SUPPORT_ICMP4 + else if (IS_IPV4(f)) { + pico_icmp4_port_unreachable(f); + } +#endif +#ifdef PICO_SUPPORT_ICMP6 + else if (IS_IPV6(f)) { + pico_icmp6_port_unreachable(f); + } +#endif + + return 0; +} + +int pico_notify_proto_unreachable(struct pico_frame *f) +{ + if (0) {} + +#ifdef PICO_SUPPORT_ICMP4 + else if (IS_IPV4(f)) { + pico_icmp4_proto_unreachable(f); + } +#endif +#ifdef PICO_SUPPORT_ICMP6 + else if (IS_IPV6(f)) { + pico_icmp6_proto_unreachable(f); + } +#endif + return 0; +} + +int pico_notify_dest_unreachable(struct pico_frame *f) +{ + if (0) {} + +#ifdef PICO_SUPPORT_ICMP4 + else if (IS_IPV4(f)) { + pico_icmp4_dest_unreachable(f); + } +#endif +#ifdef PICO_SUPPORT_ICMP6 + else if (IS_IPV6(f)) { + pico_icmp6_dest_unreachable(f); + } +#endif + return 0; +} + +int pico_notify_ttl_expired(struct pico_frame *f) +{ + if (0) {} + +#ifdef PICO_SUPPORT_ICMP4 + else if (IS_IPV4(f)) { + pico_icmp4_ttl_expired(f); + } +#endif +#ifdef PICO_SUPPORT_ICMP6 + else if (IS_IPV6(f)) { + pico_icmp6_ttl_expired(f); + } +#endif + return 0; +} + +int pico_notify_frag_expired(struct pico_frame *f) +{ + if (0) {} + +#ifdef PICO_SUPPORT_ICMP4 + else if (IS_IPV4(f)) { + pico_icmp4_frag_expired(f); + } +#endif +#ifdef PICO_SUPPORT_ICMP6 + else if (IS_IPV6(f)) { + pico_icmp6_frag_expired(f); + } +#endif + return 0; +} + +int pico_notify_pkt_too_big(struct pico_frame *f) +{ + if (0) {} + +#ifdef PICO_SUPPORT_ICMP4 + else if (IS_IPV4(f)) { + pico_icmp4_mtu_exceeded(f); + } +#endif +#ifdef PICO_SUPPORT_ICMP6 + else if (IS_IPV6(f)) { + pico_icmp6_pkt_too_big(f); + } +#endif + return 0; +} + + +/* Transport layer */ +MOCKABLE int32_t pico_transport_receive(struct pico_frame *f, uint8_t proto) +{ + int32_t ret = -1; + switch (proto) { + +#ifdef PICO_SUPPORT_ICMP4 + case PICO_PROTO_ICMP4: + ret = pico_enqueue(pico_proto_icmp4.q_in, f); + break; +#endif + +#ifdef PICO_SUPPORT_ICMP6 + case PICO_PROTO_ICMP6: + ret = pico_enqueue(pico_proto_icmp6.q_in, f); + break; +#endif + + +#ifdef PICO_SUPPORT_IGMP + case PICO_PROTO_IGMP: + ret = pico_enqueue(pico_proto_igmp.q_in, f); + break; +#endif + +#ifdef PICO_SUPPORT_UDP + case PICO_PROTO_UDP: + ret = pico_enqueue(pico_proto_udp.q_in, f); + break; +#endif + +#ifdef PICO_SUPPORT_TCP + case PICO_PROTO_TCP: + ret = pico_enqueue(pico_proto_tcp.q_in, f); + break; +#endif + + default: + /* Protocol not available */ + dbg("pkt: no such protocol (%d)\n", proto); + pico_notify_proto_unreachable(f); + pico_frame_discard(f); + ret = -1; + } + return ret; +} + +int32_t pico_network_receive(struct pico_frame *f) +{ + if (0) {} + +#ifdef PICO_SUPPORT_IPV4 + else if (IS_IPV4(f)) { + pico_enqueue(pico_proto_ipv4.q_in, f); + } +#endif +#ifdef PICO_SUPPORT_IPV6 + else if (IS_IPV6(f)) { + pico_enqueue(pico_proto_ipv6.q_in, f); + } +#endif + else { + dbg("Network not found.\n"); + pico_frame_discard(f); + return -1; + } + return (int32_t)f->buffer_len; +} + +/* Network layer: interface towards socket for frame sending */ +int32_t pico_network_send(struct pico_frame *f) +{ + if (!f || !f->sock || !f->sock->net) { + pico_frame_discard(f); + return -1; + } + + return f->sock->net->push(f->sock->net, f); +} + +int pico_source_is_local(struct pico_frame *f) +{ + if (0) { } + +#ifdef PICO_SUPPORT_IPV4 + else if (IS_IPV4(f)) { + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)f->net_hdr; + if (hdr->src.addr == PICO_IPV4_INADDR_ANY) + return 1; + + if (pico_ipv4_link_find(&hdr->src)) + return 1; + } +#endif +#ifdef PICO_SUPPORT_IPV6 + else if (IS_IPV6(f)) { + struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr; + if (pico_ipv6_is_unspecified(hdr->src.addr) || pico_ipv6_link_find(&hdr->src)) + return 1; + } +#endif + return 0; +} + +#ifdef PICO_SUPPORT_ETH +/* DATALINK LEVEL: interface from network to the device + * and vice versa. + */ + +/* The pico_ethernet_receive() function is used by + * those devices supporting ETH in order to push packets up + * into the stack. + */ + +static int destination_is_bcast(struct pico_frame *f) +{ + if (!f) + return 0; + + if (IS_IPV6(f)) + return 0; + +#ifdef PICO_SUPPORT_IPV4 + else { + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + return pico_ipv4_is_broadcast(hdr->dst.addr); + } +#else + return 0; +#endif +} + +static int destination_is_mcast(struct pico_frame *f) +{ + int ret = 0; + if (!f) + return 0; + +#ifdef PICO_SUPPORT_IPV6 + if (IS_IPV6(f)) { + struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *) f->net_hdr; + ret = pico_ipv6_is_multicast(hdr->dst.addr); + } + +#endif +#ifdef PICO_SUPPORT_IPV4 + else { + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + ret = pico_ipv4_is_multicast(hdr->dst.addr); + } +#endif + + return ret; +} + +#ifdef PICO_SUPPORT_IPV4 +static int32_t pico_ipv4_ethernet_receive(struct pico_frame *f) +{ + if (IS_IPV4(f)) { + pico_enqueue(pico_proto_ipv4.q_in, f); + } else { + (void)pico_icmp4_param_problem(f, 0); + pico_frame_discard(f); + return -1; + } + + return (int32_t)f->buffer_len; +} +#endif + +#ifdef PICO_SUPPORT_IPV6 +static int32_t pico_ipv6_ethernet_receive(struct pico_frame *f) +{ + if (IS_IPV6(f)) { + pico_enqueue(pico_proto_ipv6.q_in, f); + } else { + /* Wrong version for link layer type */ + pico_frame_discard(f); + return -1; + } + + return (int32_t)f->buffer_len; +} +#endif + +static int32_t pico_ll_receive(struct pico_frame *f) +{ + struct pico_eth_hdr *hdr = (struct pico_eth_hdr *) f->datalink_hdr; + f->net_hdr = f->datalink_hdr + sizeof(struct pico_eth_hdr); + +#if (defined PICO_SUPPORT_IPV4) && (defined PICO_SUPPORT_ETH) + if (hdr->proto == PICO_IDETH_ARP) + return pico_arp_receive(f); + +#endif + +#if defined (PICO_SUPPORT_IPV4) + if (hdr->proto == PICO_IDETH_IPV4) + return pico_ipv4_ethernet_receive(f); + +#endif + +#if defined (PICO_SUPPORT_IPV6) + if (hdr->proto == PICO_IDETH_IPV6) + return pico_ipv6_ethernet_receive(f); + +#endif + + pico_frame_discard(f); + return -1; +} + +static void pico_ll_check_bcast(struct pico_frame *f) +{ + struct pico_eth_hdr *hdr = (struct pico_eth_hdr *) f->datalink_hdr; + /* Indicate a link layer broadcast packet */ + if (memcmp(hdr->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH) == 0) + f->flags |= PICO_FRAME_FLAG_BCAST; +} + +int32_t pico_ethernet_receive(struct pico_frame *f) +{ + struct pico_eth_hdr *hdr; + if (!f || !f->dev || !f->datalink_hdr) + { + pico_frame_discard(f); + return -1; + } + + hdr = (struct pico_eth_hdr *) f->datalink_hdr; + if ((memcmp(hdr->daddr, f->dev->eth->mac.addr, PICO_SIZE_ETH) != 0) && + (memcmp(hdr->daddr, PICO_ETHADDR_MCAST, PICO_SIZE_MCAST) != 0) && +#ifdef PICO_SUPPORT_IPV6 + (memcmp(hdr->daddr, PICO_ETHADDR_MCAST6, PICO_SIZE_MCAST6) != 0) && +#endif + (memcmp(hdr->daddr, PICO_ETHADDR_ALL, PICO_SIZE_ETH) != 0)) + { + pico_frame_discard(f); + return -1; + } + + pico_ll_check_bcast(f); + return pico_ll_receive(f); +} + +static struct pico_eth *pico_ethernet_mcast_translate(struct pico_frame *f, uint8_t *pico_mcast_mac) +{ + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *) f->net_hdr; + + /* place 23 lower bits of IP in lower 23 bits of MAC */ + pico_mcast_mac[5] = (long_be(hdr->dst.addr) & 0x000000FFu); + pico_mcast_mac[4] = (uint8_t)((long_be(hdr->dst.addr) & 0x0000FF00u) >> 8u); + pico_mcast_mac[3] = (uint8_t)((long_be(hdr->dst.addr) & 0x007F0000u) >> 16u); + + return (struct pico_eth *)pico_mcast_mac; +} + + +#ifdef PICO_SUPPORT_IPV6 +static struct pico_eth *pico_ethernet_mcast6_translate(struct pico_frame *f, uint8_t *pico_mcast6_mac) +{ + struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr; + + /* first 2 octets are 0x33, last four are the last four of dst */ + pico_mcast6_mac[5] = hdr->dst.addr[PICO_SIZE_IP6 - 1]; + pico_mcast6_mac[4] = hdr->dst.addr[PICO_SIZE_IP6 - 2]; + pico_mcast6_mac[3] = hdr->dst.addr[PICO_SIZE_IP6 - 3]; + pico_mcast6_mac[2] = hdr->dst.addr[PICO_SIZE_IP6 - 4]; + + return (struct pico_eth *)pico_mcast6_mac; +} +#endif + +static int pico_ethernet_ipv6_dst(struct pico_frame *f, struct pico_eth *const dstmac) +{ + int retval = -1; + if (!dstmac) + return -1; + + #ifdef PICO_SUPPORT_IPV6 + if (destination_is_mcast(f)) { + uint8_t pico_mcast6_mac[6] = { + 0x33, 0x33, 0x00, 0x00, 0x00, 0x00 + }; + pico_ethernet_mcast6_translate(f, pico_mcast6_mac); + memcpy(dstmac, pico_mcast6_mac, PICO_SIZE_ETH); + retval = 0; + } else { + struct pico_eth *neighbor = pico_ipv6_get_neighbor(f); + if (neighbor) + { + memcpy(dstmac, neighbor, PICO_SIZE_ETH); + retval = 0; + } + } + + #else + (void)f; + pico_err = PICO_ERR_EPROTONOSUPPORT; + #endif + return retval; +} + + +/* Ethernet send, first attempt: try our own address. + * Returns 0 if the packet is not for us. + * Returns 1 if the packet is cloned to our own receive queue, so the caller can discard the original frame. + * */ +static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr) +{ + if (!hdr) + return 0; + + /* Check own mac */ + if(!memcmp(hdr->daddr, hdr->saddr, PICO_SIZE_ETH)) { + struct pico_frame *clone = pico_frame_copy(f); + dbg("sending out packet destined for our own mac\n"); + (void)pico_ethernet_receive(clone); + return 1; + } + + return 0; +} + +/* Ethernet send, second attempt: try bcast. + * Returns 0 if the packet is not bcast, so it will be handled somewhere else. + * Returns 1 if the packet is handled by the pico_device_broadcast() function, so it can be discarded. + * */ +static int32_t pico_ethsend_bcast(struct pico_frame *f) +{ + if (IS_LIMITED_BCAST(f)) { + (void)pico_device_broadcast(f); /* We can discard broadcast even if it's not sent. */ + return 1; + } + + return 0; +} + +/* Ethernet send, third attempt: try unicast. + * If the device driver is busy, we return 0, so the stack won't discard the frame. + * In case of success, we can safely return 1. + */ +static int32_t pico_ethsend_dispatch(struct pico_frame *f) +{ + int ret = f->dev->send(f->dev, f->start, (int) f->len); + if (ret <= 0) + return 0; /* Failure to deliver! */ + else { + return 1; /* Frame is in flight by now. */ + } +} + + + + +/* This function looks for the destination mac address + * in order to send the frame being processed. + */ + +int32_t MOCKABLE pico_ethernet_send(struct pico_frame *f) +{ + struct pico_eth dstmac; + uint8_t dstmac_valid = 0; + uint16_t proto = PICO_IDETH_IPV4; + +#ifdef PICO_SUPPORT_IPV6 + /* Step 1: If the frame has an IPv6 packet, + * destination address is taken from the ND tables + */ + if (IS_IPV6(f)) { + if (pico_ethernet_ipv6_dst(f, &dstmac) < 0) + { + pico_ipv6_nd_postpone(f); + return 0; /* I don't care if frame was actually postponed. If there is no room in the ND table, discard safely. */ + } + + dstmac_valid = 1; + proto = PICO_IDETH_IPV6; + } + else +#endif + + /* In case of broadcast (IPV4 only), dst mac is FF:FF:... */ + if (IS_BCAST(f) || destination_is_bcast(f)) + { + memcpy(&dstmac, PICO_ETHADDR_ALL, PICO_SIZE_ETH); + dstmac_valid = 1; + } + + /* In case of multicast, dst mac is translated from the group address */ + else if (destination_is_mcast(f)) { + uint8_t pico_mcast_mac[6] = { + 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 + }; + pico_ethernet_mcast_translate(f, pico_mcast_mac); + memcpy(&dstmac, pico_mcast_mac, PICO_SIZE_ETH); + dstmac_valid = 1; + } + +#if (defined PICO_SUPPORT_IPV4) + else { + struct pico_eth *arp_get; + arp_get = pico_arp_get(f); + if (arp_get) { + memcpy(&dstmac, arp_get, PICO_SIZE_ETH); + dstmac_valid = 1; + } else { + /* At this point, ARP will discard the frame in any case. + * It is safe to return without discarding. + */ + pico_arp_postpone(f); + return 0; + /* Same case as for IPv6 ... */ + } + + } +#endif + + /* This sets destination and source address, then pushes the packet to the device. */ + if (dstmac_valid) { + struct pico_eth_hdr *hdr; + hdr = (struct pico_eth_hdr *) f->datalink_hdr; + if ((f->start > f->buffer) && ((f->start - f->buffer) >= PICO_SIZE_ETHHDR)) + { + f->start -= PICO_SIZE_ETHHDR; + f->len += PICO_SIZE_ETHHDR; + f->datalink_hdr = f->start; + hdr = (struct pico_eth_hdr *) f->datalink_hdr; + memcpy(hdr->saddr, f->dev->eth->mac.addr, PICO_SIZE_ETH); + memcpy(hdr->daddr, &dstmac, PICO_SIZE_ETH); + hdr->proto = proto; + } + + if (pico_ethsend_local(f, hdr) || pico_ethsend_bcast(f) || pico_ethsend_dispatch(f)) { + /* one of the above functions has delivered the frame accordingly. (returned != 0) + * It is safe to directly return success. + * */ + return 0; + } + } + + /* Failure: do not dequeue the frame, keep it for later. */ + return -1; +} + +#endif /* PICO_SUPPORT_ETH */ + + +void pico_store_network_origin(void *src, struct pico_frame *f) +{ + #ifdef PICO_SUPPORT_IPV4 + struct pico_ip4 *ip4; + #endif + + #ifdef PICO_SUPPORT_IPV6 + struct pico_ip6 *ip6; + #endif + + #ifdef PICO_SUPPORT_IPV4 + if (IS_IPV4(f)) { + struct pico_ipv4_hdr *hdr; + hdr = (struct pico_ipv4_hdr *) f->net_hdr; + ip4 = (struct pico_ip4 *) src; + ip4->addr = hdr->src.addr; + } + + #endif + #ifdef PICO_SUPPORT_IPV6 + if (IS_IPV6(f)) { + struct pico_ipv6_hdr *hdr; + hdr = (struct pico_ipv6_hdr *) f->net_hdr; + ip6 = (struct pico_ip6 *) src; + memcpy(ip6->addr, hdr->src.addr, PICO_SIZE_IP6); + } + + #endif +} + +int pico_address_compare(union pico_address *a, union pico_address *b, uint16_t proto) +{ + #ifdef PICO_SUPPORT_IPV6 + if (proto == PICO_PROTO_IPV6) { + return pico_ipv6_compare(&a->ip6, &b->ip6); + } + + #endif + #ifdef PICO_SUPPORT_IPV4 + if (proto == PICO_PROTO_IPV4) { + return pico_ipv4_compare(&a->ip4, &b->ip4); + } + + #endif + return 0; + +} + +int pico_frame_dst_is_unicast(struct pico_frame *f) +{ + if (0) { + return 0; + } + +#ifdef PICO_SUPPORT_IPV4 + if (IS_IPV4(f)) { + struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)f->net_hdr; + if (pico_ipv4_is_multicast(hdr->dst.addr) || pico_ipv4_is_broadcast(hdr->dst.addr)) + return 0; + + return 1; + } + +#endif + +#ifdef PICO_SUPPORT_IPV6 + if (IS_IPV6(f)) { + struct pico_ipv6_hdr *hdr = (struct pico_ipv6_hdr *)f->net_hdr; + if (pico_ipv6_is_multicast(hdr->dst.addr) || pico_ipv6_is_unspecified(hdr->dst.addr)) + return 0; + + return 1; + } + +#endif + else return 0; +} + + +/* LOWEST LEVEL: interface towards devices. */ +/* Device driver will call this function which returns immediately. + * Incoming packet will be processed later on in the dev loop. + */ +int32_t pico_stack_recv(struct pico_device *dev, uint8_t *buffer, uint32_t len) +{ + struct pico_frame *f; + int32_t ret; + if (len <= 0) + return -1; + + f = pico_frame_alloc(len); + if (!f) + { + dbg("Cannot alloc incoming frame!\n"); + return -1; + } + + /* Association to the device that just received the frame. */ + f->dev = dev; + + /* Setup the start pointer, length. */ + f->start = f->buffer; + f->len = f->buffer_len; + if (f->len > 8) { + uint32_t rand, mid_frame = (f->buffer_len >> 2) << 1; + mid_frame -= (mid_frame % 4); + memcpy(&rand, f->buffer + mid_frame, sizeof(uint32_t)); + pico_rand_feed(rand); + } + + memcpy(f->buffer, buffer, len); + ret = pico_enqueue(dev->q_in, f); + if (ret <= 0) { + pico_frame_discard(f); + } + + return ret; +} + +static int32_t _pico_stack_recv_zerocopy(struct pico_device *dev, uint8_t *buffer, uint32_t len, int ext_buffer, void (*notify_free)(uint8_t *)) +{ + struct pico_frame *f; + int ret; + if (len <= 0) + return -1; + + f = pico_frame_alloc_skeleton(len, ext_buffer); + if (!f) + { + dbg("Cannot alloc incoming frame!\n"); + return -1; + } + + if (pico_frame_skeleton_set_buffer(f, buffer) < 0) + { + dbg("Invalid zero-copy buffer!\n"); + PICO_FREE(f->usage_count); + PICO_FREE(f); + return -1; + } + + if (notify_free) { + f->notify_free = notify_free; + } + + f->dev = dev; + ret = pico_enqueue(dev->q_in, f); + if (ret <= 0) { + pico_frame_discard(f); + } + + return ret; +} + +int32_t pico_stack_recv_zerocopy(struct pico_device *dev, uint8_t *buffer, uint32_t len) +{ + return _pico_stack_recv_zerocopy(dev, buffer, len, 0, NULL); +} + +int32_t pico_stack_recv_zerocopy_ext_buffer(struct pico_device *dev, uint8_t *buffer, uint32_t len) +{ + return _pico_stack_recv_zerocopy(dev, buffer, len, 1, NULL); +} + +int32_t pico_stack_recv_zerocopy_ext_buffer_notify(struct pico_device *dev, uint8_t *buffer, uint32_t len, void (*notify_free)(uint8_t *buffer)) +{ + return _pico_stack_recv_zerocopy(dev, buffer, len, 1, notify_free); +} + +int32_t pico_sendto_dev(struct pico_frame *f) +{ + if (!f->dev) { + pico_frame_discard(f); + return -1; + } else { + if (f->len > 8) { + uint32_t rand, mid_frame = (f->buffer_len >> 2) << 1; + mid_frame -= (mid_frame % 4); + memcpy(&rand, f->buffer + mid_frame, sizeof(uint32_t)); + pico_rand_feed(rand); + } + + return pico_enqueue(f->dev->q_out, f); + } +} + +struct pico_timer +{ + void *arg; + void (*timer)(pico_time timestamp, void *arg); +}; + +struct pico_timer_ref +{ + pico_time expire; + struct pico_timer *tmr; +}; + +typedef struct pico_timer_ref pico_timer_ref; + +DECLARE_HEAP(pico_timer_ref, expire); + +static heap_pico_timer_ref *Timers; + +int32_t pico_seq_compare(uint32_t a, uint32_t b) +{ + uint32_t thresh = ((uint32_t)(-1)) >> 1; + + if (a > b) /* return positive number, if not wrapped */ + { + if ((a - b) > thresh) /* b wrapped */ + return -(int32_t)(b - a); /* b = very small, a = very big */ + else + return (int32_t)(a - b); /* a = biggest, b = a bit smaller */ + + } + + if (a < b) /* return negative number, if not wrapped */ + { + if ((b - a) > thresh) /* a wrapped */ + return (int32_t)(a - b); /* a = very small, b = very big */ + else + return -(int32_t)(b - a); /* b = biggest, a = a bit smaller */ + + } + + return 0; +} + +static void pico_check_timers(void) +{ + struct pico_timer *t; + struct pico_timer_ref tref_unused, *tref = heap_first(Timers); + pico_tick = PICO_TIME_MS(); + while((tref) && (tref->expire < pico_tick)) { + t = tref->tmr; + if (t && t->timer) + t->timer(pico_tick, t->arg); + + if (t) + { + PICO_FREE(t); + } + + t = NULL; + heap_peek(Timers, &tref_unused); + tref = heap_first(Timers); + } +} + +void MOCKABLE pico_timer_cancel(struct pico_timer *t) +{ + uint32_t i; + struct pico_timer_ref *tref = Timers->top; + if (!t) + return; + + for (i = 1; i <= Timers->n; i++) { + if (tref[i].tmr == t) { + Timers->top[i].tmr = NULL; + PICO_FREE(t); + break; + } + } +} + +#define PROTO_DEF_NR 11 +#define PROTO_DEF_AVG_NR 4 +#define PROTO_DEF_SCORE 32 +#define PROTO_MIN_SCORE 32 +#define PROTO_MAX_SCORE 128 +#define PROTO_LAT_IND 3 /* latency indication 0-3 (lower is better latency performance), x1, x2, x4, x8 */ +#define PROTO_MAX_LOOP (PROTO_MAX_SCORE << PROTO_LAT_IND) /* max global loop score, so per tick */ + +static int calc_score(int *score, int *index, int avg[][PROTO_DEF_AVG_NR], int *ret) +{ + int temp, i, j, sum; + int max_total = PROTO_MAX_LOOP, total = 0; + + /* dbg("USED SCORES> "); */ + + for (i = 0; i < PROTO_DEF_NR; i++) { + + /* if used looped score */ + if (ret[i] < score[i]) { + temp = score[i] - ret[i]; /* remaining loop score */ + + /* dbg("%3d - ",temp); */ + + if (index[i] >= PROTO_DEF_AVG_NR) + index[i] = 0; /* reset index */ + + j = index[i]; + avg[i][j] = temp; + + index[i]++; + + if (ret[i] == 0 && ((score[i] * 2) <= PROTO_MAX_SCORE) && ((total + (score[i] * 2)) < max_total)) { /* used all loop score -> increase next score directly */ + score[i] *= 2; + total += score[i]; + continue; + } + + sum = 0; + for (j = 0; j < PROTO_DEF_AVG_NR; j++) + sum += avg[i][j]; /* calculate sum */ + + sum /= 4; /* divide by 4 to get average used score */ + + /* criterion to increase next loop score */ + if (sum > (score[i] - (score[i] / 4)) && ((score[i] * 2) <= PROTO_MAX_SCORE) && ((total + (score[i] / 2)) < max_total)) { /* > 3/4 */ + score[i] *= 2; /* double loop score */ + total += score[i]; + continue; + } + + /* criterion to decrease next loop score */ + if ((sum < (score[i] / 4)) && ((score[i] / 2) >= PROTO_MIN_SCORE)) { /* < 1/4 */ + score[i] /= 2; /* half loop score */ + total += score[i]; + continue; + } + + /* also add non-changed scores */ + total += score[i]; + } + else if (ret[i] == score[i]) { + /* no used loop score - gradually decrease */ + + /* dbg("%3d - ",0); */ + + if (index[i] >= PROTO_DEF_AVG_NR) + index[i] = 0; /* reset index */ + + j = index[i]; + avg[i][j] = 0; + + index[i]++; + + sum = 0; + for (j = 0; j < PROTO_DEF_AVG_NR; j++) + sum += avg[i][j]; /* calculate sum */ + + sum /= 2; /* divide by 4 to get average used score */ + + if ((sum == 0) && ((score[i] / 2) >= PROTO_MIN_SCORE)) { + score[i] /= 2; /* half loop score */ + total += score[i]; + for (j = 0; j < PROTO_DEF_AVG_NR; j++) + avg[i][j] = score[i]; + } + + } + } + /* dbg("\n"); */ + + return 0; +} + +void pico_stack_tick(void) +{ + static int score[PROTO_DEF_NR] = { + PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE, PROTO_DEF_SCORE + }; + static int index[PROTO_DEF_NR] = { + 0, 0, 0, 0, 0, 0 + }; + static int avg[PROTO_DEF_NR][PROTO_DEF_AVG_NR]; + static int ret[PROTO_DEF_NR] = { + 0 + }; + + pico_check_timers(); + + /* dbg("LOOP_SCORES> %3d - %3d - %3d - %3d - %3d - %3d - %3d - %3d - %3d - %3d - %3d\n",score[0],score[1],score[2],score[3],score[4],score[5],score[6],score[7],score[8],score[9],score[10]); */ + + /* score = pico_protocols_loop(100); */ + + ret[0] = pico_devices_loop(score[0], PICO_LOOP_DIR_IN); + pico_rand_feed((uint32_t)ret[0]); + + ret[1] = pico_protocol_datalink_loop(score[1], PICO_LOOP_DIR_IN); + pico_rand_feed((uint32_t)ret[1]); + + ret[2] = pico_protocol_network_loop(score[2], PICO_LOOP_DIR_IN); + pico_rand_feed((uint32_t)ret[2]); + + ret[3] = pico_protocol_transport_loop(score[3], PICO_LOOP_DIR_IN); + pico_rand_feed((uint32_t)ret[3]); + + + ret[5] = score[5]; +#if defined (PICO_SUPPORT_IPV4) || defined (PICO_SUPPORT_IPV6) +#if defined (PICO_SUPPORT_TCP) || defined (PICO_SUPPORT_UDP) + ret[5] = pico_sockets_loop(score[5]); /* swapped */ + pico_rand_feed((uint32_t)ret[5]); +#endif +#endif + + ret[4] = pico_protocol_socket_loop(score[4], PICO_LOOP_DIR_IN); + pico_rand_feed((uint32_t)ret[4]); + + + ret[6] = pico_protocol_socket_loop(score[6], PICO_LOOP_DIR_OUT); + pico_rand_feed((uint32_t)ret[6]); + + ret[7] = pico_protocol_transport_loop(score[7], PICO_LOOP_DIR_OUT); + pico_rand_feed((uint32_t)ret[7]); + + ret[8] = pico_protocol_network_loop(score[8], PICO_LOOP_DIR_OUT); + pico_rand_feed((uint32_t)ret[8]); + + ret[9] = pico_protocol_datalink_loop(score[9], PICO_LOOP_DIR_OUT); + pico_rand_feed((uint32_t)ret[9]); + + ret[10] = pico_devices_loop(score[10], PICO_LOOP_DIR_OUT); + pico_rand_feed((uint32_t)ret[10]); + + /* calculate new loop scores for next iteration */ + calc_score(score, index, (int (*)[])avg, ret); +} + +void pico_stack_loop(void) +{ + while(1) { + pico_stack_tick(); + PICO_IDLE(); + } +} + +MOCKABLE struct pico_timer *pico_timer_add(pico_time expire, void (*timer)(pico_time, void *), void *arg) +{ + struct pico_timer *t = PICO_ZALLOC(sizeof(struct pico_timer)); + struct pico_timer_ref tref; + if (!t) { + pico_err = PICO_ERR_ENOMEM; + return NULL; + } + + tref.expire = PICO_TIME_MS() + expire; + t->arg = arg; + t->timer = timer; + tref.tmr = t; + heap_insert(Timers, &tref); + if (Timers->n > PICO_MAX_TIMERS) { + dbg("Warning: I have %d timers\n", (int)Timers->n); + } + + return t; +} + +int pico_stack_init(void) +{ + +#ifdef PICO_SUPPORT_IPV4 + pico_protocol_init(&pico_proto_ipv4); +#endif + +#ifdef PICO_SUPPORT_IPV6 + pico_protocol_init(&pico_proto_ipv6); +#endif + +#ifdef PICO_SUPPORT_ICMP4 + pico_protocol_init(&pico_proto_icmp4); +#endif + +#ifdef PICO_SUPPORT_ICMP6 + pico_protocol_init(&pico_proto_icmp6); +#endif + +#ifdef PICO_SUPPORT_IGMP + pico_protocol_init(&pico_proto_igmp); +#endif + +#ifdef PICO_SUPPORT_UDP + pico_protocol_init(&pico_proto_udp); +#endif + +#ifdef PICO_SUPPORT_TCP + pico_protocol_init(&pico_proto_tcp); +#endif + +#ifdef PICO_SUPPORT_DNS_CLIENT + pico_dns_client_init(); +#endif + + pico_rand_feed(123456); + + /* Initialize timer heap */ + Timers = heap_init(); + if (!Timers) + return -1; + +#if ((defined PICO_SUPPORT_IPV4) && (defined PICO_SUPPORT_ETH)) + /* Initialize ARP module */ + pico_arp_init(); +#endif + +#ifdef PICO_SUPPORT_IPV6 + /* Initialize Neighbor discovery module */ + pico_ipv6_nd_init(); +#endif + +#ifdef PICO_SUPPORT_OLSR + pico_olsr_init(); +#endif +#ifdef PICO_SUPPORT_AODV + pico_aodv_init(); +#endif + + pico_stack_tick(); + pico_stack_tick(); + pico_stack_tick(); + return 0; +} + diff --git a/net/picotcp/stack/pico_tree.c b/net/picotcp/stack/pico_tree.c new file mode 100644 index 0000000..76eaf2f --- /dev/null +++ b/net/picotcp/stack/pico_tree.c @@ -0,0 +1,575 @@ +/********************************************************************* + PicoTCP. Copyright (c) 2012-2015 Altran Intelligent Systems. Some rights reserved. + See LICENSE and COPYING for usage. + + Author: Andrei Carp + *********************************************************************/ + +#include "pico_tree.h" +#include "pico_config.h" +#include "pico_protocol.h" +#include "pico_mm.h" + +#define RED 0 +#define BLACK 1 + +/* By default the null leafs are black */ +struct pico_tree_node LEAF = { + NULL, /* key */ + &LEAF, &LEAF, &LEAF, /* parent, left,right */ + BLACK, /* color */ +}; + +#define IS_LEAF(x) (x == &LEAF) +#define IS_NOT_LEAF(x) (x != &LEAF) +#define INIT_LEAF (&LEAF) + +#define AM_I_LEFT_CHILD(x) (x == x->parent->leftChild) +#define AM_I_RIGHT_CHILD(x) (x == x->parent->rightChild) + +#define PARENT(x) (x->parent) +#define GRANPA(x) (x->parent->parent) + +/* + * Local Functions + */ +static struct pico_tree_node *create_node(struct pico_tree *tree, void *key, uint8_t allocator); +static void rotateToLeft(struct pico_tree*tree, struct pico_tree_node*node); +static void rotateToRight(struct pico_tree*root, struct pico_tree_node*node); +static void fix_insert_collisions(struct pico_tree*tree, struct pico_tree_node*node); +static void fix_delete_collisions(struct pico_tree*tree, struct pico_tree_node *node); +static void switchNodes(struct pico_tree*tree, struct pico_tree_node*nodeA, struct pico_tree_node*nodeB); +void *pico_tree_insert_implementation(struct pico_tree *tree, void *key, uint8_t allocator); +void *pico_tree_delete_implementation(struct pico_tree *tree, void *key, uint8_t allocator); + +#ifdef PICO_SUPPORT_MM +/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has. + * These nodes should be placed in the manager page which is in a different memory region then the nodes + * which are used for the pico stack in general. + * Therefore the following 2 functions are created so that pico_tree can use them to to put these nodes + * into the correct memory regions. + * If pico_tree_insert is called from the memory manager module, then create_node should use + * pico_mem_page0_zalloc to create a node. The same for pico_tree_delete. + */ +extern void*pico_mem_page0_zalloc(size_t len); +extern void pico_mem_page0_free(void*ptr); +#endif /* PICO_SUPPORT_MM */ + +/* + * Exported functions + */ + +struct pico_tree_node *pico_tree_firstNode(struct pico_tree_node *node) +{ + while(IS_NOT_LEAF(node->leftChild)) + node = node->leftChild; + return node; +} + +struct pico_tree_node *pico_tree_lastNode(struct pico_tree_node *node) +{ + while(IS_NOT_LEAF(node->rightChild)) + node = node->rightChild; + return node; +} + +struct pico_tree_node *pico_tree_next(struct pico_tree_node *node) +{ + if(IS_NOT_LEAF(node->rightChild)) + { + node = node->rightChild; + while(IS_NOT_LEAF(node->leftChild)) + node = node->leftChild; + } + else + { + if (IS_NOT_LEAF(node->parent) && AM_I_LEFT_CHILD(node)) + node = node->parent; + else { + while (IS_NOT_LEAF(node->parent) && AM_I_RIGHT_CHILD(node)) + node = node->parent; + node = node->parent; + } + } + + return node; +} + +struct pico_tree_node *pico_tree_prev(struct pico_tree_node *node) +{ + if (IS_NOT_LEAF(node->leftChild)) { + node = node->leftChild; + while (IS_NOT_LEAF(node->rightChild)) + node = node->rightChild; + } else { + if (IS_NOT_LEAF(node->parent) && AM_I_RIGHT_CHILD(node)) + node = node->parent; + else { + while (IS_NOT_LEAF(node) && AM_I_LEFT_CHILD(node)) + node = node->parent; + node = node->parent; + } + } + + return node; +} + +/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has. + * These nodes should be placed in the manager page which is in a different memory region then the nodes + * which are used for the pico stack in general. + * Therefore the following wrapper for pico_tree_insert is created. + * The actual implementation can be found in pico_tree_insert_implementation. + */ +void *pico_tree_insert(struct pico_tree *tree, void *key) +{ + return pico_tree_insert_implementation(tree, key, USE_PICO_ZALLOC); +} + +void *pico_tree_insert_implementation(struct pico_tree*tree, void *key, uint8_t allocator) +{ + struct pico_tree_node *last_node = INIT_LEAF; + struct pico_tree_node *temp = tree->root; + struct pico_tree_node *insert; + void *LocalKey; + int result = 0; + + LocalKey = (IS_NOT_LEAF(tree->root) ? pico_tree_findKey(tree, key) : NULL); + /* if node already in, bail out */ + if(LocalKey) + return LocalKey; + else + { + if(allocator == USE_PICO_PAGE0_ZALLOC) + insert = create_node(tree, key, USE_PICO_PAGE0_ZALLOC); + else + insert = create_node(tree, key, USE_PICO_ZALLOC); + + if(!insert) + { + pico_err = PICO_ERR_ENOMEM; + /* to let the user know that it couldn't insert */ + return (void *)&LEAF; + } + } + + /* search for the place to insert the new node */ + while(IS_NOT_LEAF(temp)) + { + last_node = temp; + result = tree->compare(insert->keyValue, temp->keyValue); + + temp = (result < 0) ? (temp->leftChild) : (temp->rightChild); + } + /* make the needed connections */ + insert->parent = last_node; + + if(IS_LEAF(last_node)) + tree->root = insert; + else{ + result = tree->compare(insert->keyValue, last_node->keyValue); + if(result < 0) + last_node->leftChild = insert; + else + last_node->rightChild = insert; + } + + /* fix colour issues */ + fix_insert_collisions(tree, insert); + + return NULL; +} + +struct pico_tree_node *pico_tree_findNode(struct pico_tree *tree, void *key) +{ + struct pico_tree_node *found; + + found = tree->root; + + while(IS_NOT_LEAF(found)) + { + int result; + result = tree->compare(found->keyValue, key); + if(result == 0) + { + return found; + } + else if(result < 0) + found = found->rightChild; + else + found = found->leftChild; + } + return NULL; +} + +void *pico_tree_findKey(struct pico_tree *tree, void *key) +{ + struct pico_tree_node *found; + + + found = tree->root; + + while(IS_NOT_LEAF(found)) + { + int result; + + result = tree->compare(found->keyValue, key); + if(result == 0) + return found->keyValue; + else if(result < 0) + found = found->rightChild; + else + found = found->leftChild; + + } + return NULL; +} + +void *pico_tree_first(struct pico_tree *tree) +{ + return pico_tree_firstNode(tree->root)->keyValue; +} + +void *pico_tree_last(struct pico_tree *tree) +{ + return pico_tree_lastNode(tree->root)->keyValue; +} + +static uint8_t pico_tree_delete_node(struct pico_tree *tree, struct pico_tree_node *d, struct pico_tree_node **temp) +{ + struct pico_tree_node *min; + struct pico_tree_node *ltemp = d; + uint8_t nodeColor; + min = pico_tree_firstNode(d->rightChild); + nodeColor = min->color; + + *temp = min->rightChild; + if(min->parent == ltemp && IS_NOT_LEAF(*temp)) + (*temp)->parent = min; + else{ + switchNodes(tree, min, min->rightChild); + min->rightChild = ltemp->rightChild; + if(IS_NOT_LEAF(min->rightChild)) min->rightChild->parent = min; + } + + switchNodes(tree, ltemp, min); + min->leftChild = ltemp->leftChild; + + if(IS_NOT_LEAF(min->leftChild)) + min->leftChild->parent = min; + + min->color = ltemp->color; + return nodeColor; +} + +static uint8_t pico_tree_delete_check_switch(struct pico_tree *tree, struct pico_tree_node *delete, struct pico_tree_node **temp) +{ + struct pico_tree_node *ltemp = delete; + uint8_t nodeColor = delete->color; + if(IS_LEAF(delete->leftChild)) + { + *temp = ltemp->rightChild; + switchNodes(tree, ltemp, ltemp->rightChild); + } + else + if(IS_LEAF(delete->rightChild)) + { + struct pico_tree_node *_ltemp = delete; + *temp = _ltemp->leftChild; + switchNodes(tree, _ltemp, _ltemp->leftChild); + } + else{ + nodeColor = pico_tree_delete_node(tree, delete, temp); + } + + return nodeColor; + +} + +/* The memory manager also uses the pico_tree to keep track of all the different slab sizes it has. + * These nodes should be placed in the manager page which is in a different memory region then the nodes + * which are used for the pico stack in general. + * Therefore the following wrapper for pico_tree_delete is created. + * The actual implementation can be found in pico_tree_delete_implementation. + */ +void *pico_tree_delete(struct pico_tree *tree, void *key) +{ + return pico_tree_delete_implementation(tree, key, USE_PICO_ZALLOC); +} + +static inline void if_nodecolor_black_fix_collisions(struct pico_tree *tree, struct pico_tree_node *temp, uint8_t nodeColor) +{ + /* deleted node is black, this will mess up the black path property */ + if(nodeColor == BLACK) + fix_delete_collisions(tree, temp); + +} + +void *pico_tree_delete_implementation(struct pico_tree *tree, void *key, uint8_t allocator) +{ + struct pico_tree_node *temp; + uint8_t nodeColor; /* keeps the color of the node to be deleted */ + void *lkey; /* keeps a copy of the key which will be removed */ + struct pico_tree_node *delete; /* keeps a copy of the node to be extracted */ + + if (!key) + return NULL; + + delete = pico_tree_findNode(tree, key); + + /* this key isn't in the tree, bail out */ + if(!delete) + return NULL; + + lkey = delete->keyValue; + nodeColor = pico_tree_delete_check_switch(tree, delete, &temp); + + if_nodecolor_black_fix_collisions(tree, temp, nodeColor); + + if(allocator == USE_PICO_ZALLOC) + PICO_FREE(delete); + +#ifdef PICO_SUPPORT_MM + else + pico_mem_page0_free(delete); +#endif + return lkey; +} + +int pico_tree_empty(struct pico_tree *tree) +{ + return (!tree->root || IS_LEAF(tree->root)); +} + +/* + * Private functions + */ +static void rotateToLeft(struct pico_tree*tree, struct pico_tree_node*node) +{ + struct pico_tree_node*temp; + + temp = node->rightChild; + + if(temp == &LEAF) return; + + node->rightChild = temp->leftChild; + + if(IS_NOT_LEAF(temp->leftChild)) + temp->leftChild->parent = node; + + temp->parent = node->parent; + + if(IS_LEAF(node->parent)) + tree->root = temp; + else + if(node == node->parent->leftChild) + node->parent->leftChild = temp; + else + node->parent->rightChild = temp; + + temp->leftChild = node; + node->parent = temp; +} + + +static void rotateToRight(struct pico_tree *tree, struct pico_tree_node *node) +{ + struct pico_tree_node*temp; + + temp = node->leftChild; + node->leftChild = temp->rightChild; + + if(temp == &LEAF) return; + + if(IS_NOT_LEAF(temp->rightChild)) + temp->rightChild->parent = node; + + temp->parent = node->parent; + + if(IS_LEAF(node->parent)) + tree->root = temp; + else + if(node == node->parent->rightChild) + node->parent->rightChild = temp; + else + node->parent->leftChild = temp; + + temp->rightChild = node; + node->parent = temp; + return; +} + +static struct pico_tree_node *create_node(struct pico_tree *tree, void*key, uint8_t allocator) +{ + struct pico_tree_node *temp = NULL; + IGNORE_PARAMETER(tree); + if(allocator == USE_PICO_ZALLOC) + temp = (struct pico_tree_node *)PICO_ZALLOC(sizeof(struct pico_tree_node)); + +#ifdef PICO_SUPPORT_MM + else + temp = (struct pico_tree_node *)pico_mem_page0_zalloc(sizeof(struct pico_tree_node)); +#endif + + if(!temp) + return NULL; + + temp->keyValue = key; + temp->parent = &LEAF; + temp->leftChild = &LEAF; + temp->rightChild = &LEAF; + /* by default every new node is red */ + temp->color = RED; + return temp; +} + +/* + * This function fixes the possible collisions in the tree. + * Eg. if a node is red his children must be black ! + */ +static void fix_insert_collisions(struct pico_tree*tree, struct pico_tree_node*node) +{ + struct pico_tree_node*temp; + + while(node->parent->color == RED && IS_NOT_LEAF(GRANPA(node))) + { + if(AM_I_RIGHT_CHILD(node->parent)) + { + temp = GRANPA(node)->leftChild; + if(temp->color == RED) { + node->parent->color = BLACK; + temp->color = BLACK; + GRANPA(node)->color = RED; + node = GRANPA(node); + } + else if(temp->color == BLACK) { + if(node == node->parent->leftChild) { + node = node->parent; + rotateToRight(tree, node); + } + + node->parent->color = BLACK; + GRANPA(node)->color = RED; + rotateToLeft(tree, GRANPA(node)); + } + } + else if(AM_I_LEFT_CHILD(node->parent)) + { + temp = GRANPA(node)->rightChild; + if(temp->color == RED) { + node->parent->color = BLACK; + temp->color = BLACK; + GRANPA(node)->color = RED; + node = GRANPA(node); + } + else if(temp->color == BLACK) { + if(AM_I_RIGHT_CHILD(node)) { + node = node->parent; + rotateToLeft(tree, node); + } + + node->parent->color = BLACK; + GRANPA(node)->color = RED; + rotateToRight(tree, GRANPA(node)); + } + } + } + /* make sure that the root of the tree stays black */ + tree->root->color = BLACK; +} + +static void switchNodes(struct pico_tree*tree, struct pico_tree_node*nodeA, struct pico_tree_node*nodeB) +{ + + if(IS_LEAF(nodeA->parent)) + tree->root = nodeB; + else + if(IS_NOT_LEAF(nodeA)) + { + if(AM_I_LEFT_CHILD(nodeA)) + nodeA->parent->leftChild = nodeB; + else + nodeA->parent->rightChild = nodeB; + } + + if(IS_NOT_LEAF(nodeB)) nodeB->parent = nodeA->parent; + +} + +/* + * This function fixes the possible collisions in the tree. + * Eg. if a node is red his children must be black ! + * In this case the function fixes the constant black path property. + */ +static void fix_delete_collisions(struct pico_tree*tree, struct pico_tree_node *node) +{ + struct pico_tree_node*temp; + + while( node != tree->root && node->color == BLACK && IS_NOT_LEAF(node)) + { + if(AM_I_LEFT_CHILD(node)) { + + temp = node->parent->rightChild; + if(temp->color == RED) + { + temp->color = BLACK; + node->parent->color = RED; + rotateToLeft(tree, node->parent); + temp = node->parent->rightChild; + } + + if(temp->leftChild->color == BLACK && temp->rightChild->color == BLACK) + { + temp->color = RED; + node = node->parent; + } + else + { + if(temp->rightChild->color == BLACK) + { + temp->leftChild->color = BLACK; + temp->color = RED; + rotateToRight(tree, temp); + temp = temp->parent->rightChild; + } + + temp->color = node->parent->color; + node->parent->color = BLACK; + temp->rightChild->color = BLACK; + rotateToLeft(tree, node->parent); + node = tree->root; + } + } + else{ + temp = node->parent->leftChild; + if(temp->color == RED) + { + temp->color = BLACK; + node->parent->color = RED; + rotateToRight(tree, node->parent); + temp = node->parent->leftChild; + } + + if(temp->rightChild->color == BLACK && temp->leftChild->color == BLACK) + { + temp->color = RED; + node = node->parent; + } + else{ + if(temp->leftChild->color == BLACK) + { + temp->rightChild->color = BLACK; + temp->color = RED; + rotateToLeft(tree, temp); + temp = temp->parent->leftChild; + } + + temp->color = node->parent->color; + node->parent->color = BLACK; + temp->leftChild->color = BLACK; + rotateToRight(tree, node->parent); + node = tree->root; + } + } + } + node->color = BLACK; +} -- 2.1.4 _______________________________________________ barebox mailing list barebox@lists.infradead.org http://lists.infradead.org/mailman/listinfo/barebox