pax_global_header00006660000000000000000000000064146375125120014520gustar00rootroot0000000000000052 comment=2730638bf88984b09531813974f9bd14e1a50165 libndp-1.9/000077500000000000000000000000001463751251200126415ustar00rootroot00000000000000libndp-1.9/.gitignore000066400000000000000000000002741463751251200146340ustar00rootroot00000000000000*.o *.la *.pc *.lo .deps/ .libs/ Makefile Makefile.in /aclocal.m4 /autom4te.cache /build-aux/ /config.* /configure /libtool /ar-lib /depcomp /install-sh /ltmain.sh /missing utils/ndptool libndp-1.9/COPYING000066400000000000000000000636421463751251200137070ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 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. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. 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 library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; 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. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! libndp-1.9/Makefile.am000066400000000000000000000001401463751251200146700ustar00rootroot00000000000000MAINTAINERCLEANFILES = Makefile.in ACLOCAL_AMFLAGS = -I m4 SUBDIRS = include libndp utils man libndp-1.9/README000066400000000000000000000013571463751251200135270ustar00rootroot00000000000000# libndp - Library for Neighbor Discovery Protocol # This package contains a library which provides a wrapper for IPv6 Neighbor Discovery Protocol. It also provides a tool named ndptool for sending and receiving NDP messages. ## Install $ ./autogen.sh $ ./configure $ make $ sudo make install ## Authors * Jiri Pirko ## Internet Resources * Project Home: http://www.libndp.org/ * Git Source Tree: https://github.com/jpirko/libndp ## License Copyright (C) 2013-2015 Jiri Pirko libndp is distributed under GNU Lesser General Public License version 2.1. See the file "COPYING" in the source distribution for information on terms & conditions for accessing and otherwise using libndp. libndp-1.9/SubmittingPatches000066400000000000000000000025171463751251200162260ustar00rootroot00000000000000# How to Submit Patches for libndp # ======================================= Send changes to libndp as patches to libndp-list@redhat.com with copy to jiri@resnulli.us. Avoid using attachments because usually it doesn't work. In another words, inline the patch inside the e-mail's body. The simplest workflow to send changes is using git. The following steps will guide you to do the task. 1) Clone the source code on your local machine. $ git clone https://github.com/jpirko/libndp.git 2) Create a local branch for you $ cd libndp $ git checkout -b testbranch 3) Do your changes <> 4) Commit them to the repository $ git commit -a -s 5) Optionally, you can verify how it looks like in the repo $ git show 6) Extract the patch in a standard format into /tmp directory $ git format-patch origin -o /tmp --subject-prefix "patch libndp" 7) Use git send-email to send the patch*. $ git send-email /tmp/0001-somepatch.patch \ --to=libndp@lists.fedorahosted.org --cc=jiri@resnulli.us And you are done :) * You may need to configure smtp in order to send emails, so please check the "Example" section in the reference [3] below as a start. Additional references about git: [1] http://git-scm.com/docs/gittutorial [2] http://git-scm.com/docs/gitworkflows.html [3] http://git-scm.com/docs/git-send-email [4] http://git-scm.com/book libndp-1.9/autogen.sh000077500000000000000000000001071463751251200146400ustar00rootroot00000000000000#!/bin/bash autoreconf --force --install -I m4 rm -Rf autom4te.cache; libndp-1.9/configure.ac000066400000000000000000000041151463751251200151300ustar00rootroot00000000000000# -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_INIT([libndp], [1.9], [jiri@resnulli.us]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)], []) AM_PROG_AR # Here are a set of rules to help you update your library version information: # 1. Start with version information of ‘0:0:0’ for each libtool library. # 2. Update the version information only immediately before a public release # of your software. More frequent updates are unnecessary, and only guarantee # that the current interface number gets larger faster. # 3. If the library source code has changed at all since the last update, # then increment revision (‘c:r:a’ becomes ‘c:r+1:a’). # 4. If any interfaces have been added, removed, or changed since the last # update, increment current, and set revision to 0. # 5. If any interfaces have been added since the last public release, then # increment age. # 6. If any interfaces have been removed or changed since the last public # release, then set age to 0. AC_SUBST(LIBNDP_CURRENT, 3) AC_SUBST(LIBNDP_REVISION, 0) AC_SUBST(LIBNDP_AGE, 3) CFLAGS="$CFLAGS -Wall" # Checks for programs. AC_PROG_CC LT_INIT # Checks for header files. AC_CHECK_HEADERS([stdint.h stdlib.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE # Checks for library functions. AC_FUNC_MALLOC AC_ARG_ENABLE([logging], AS_HELP_STRING([--disable-logging], [disable system logging @<:@default=enabled@:>@]), [], enable_logging=yes) AS_IF([test "x$enable_logging" = "xyes"], [ AC_DEFINE(ENABLE_LOGGING, [1], [System logging.]) ]) AC_ARG_ENABLE([debug], AS_HELP_STRING([--enable-debug], [enable debug messages @<:@default=disabled@:>@]), [], [enable_debug=no]) AS_IF([test "x$enable_debug" = "xyes"], [ AC_DEFINE(ENABLE_DEBUG, [1], [Debug messages.]) ]) AC_CONFIG_FILES([Makefile include/Makefile \ libndp/Makefile \ libndp/libndp.pc \ utils/Makefile \ man/Makefile]) AC_OUTPUT libndp-1.9/include/000077500000000000000000000000001463751251200142645ustar00rootroot00000000000000libndp-1.9/include/Makefile.am000066400000000000000000000001531463751251200163170ustar00rootroot00000000000000MAINTAINERCLEANFILES = Makefile.in libndpincludedir = $(includedir) nobase_libndpinclude_HEADERS = ndp.h libndp-1.9/include/ndp.h000066400000000000000000000202521463751251200152170ustar00rootroot00000000000000/* * ndp.h - Neighbour discovery library * Copyright (C) 2013-2015 Jiri Pirko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _NDP_H_ #define _NDP_H_ #include #include #include #ifdef __cplusplus extern "C" { #endif struct ndp; void ndp_set_log_fn(struct ndp *ndp, void (*log_fn)(struct ndp *ndp, int priority, const char *file, int line, const char *fn, const char *format, va_list args)); int ndp_get_log_priority(struct ndp *ndp); void ndp_set_log_priority(struct ndp *ndp, int priority); struct ndp_msg; struct ndp_msgrs; struct ndp_msgra; struct ndp_msgns; struct ndp_msgna; struct ndp_msgr; enum ndp_msg_type { NDP_MSG_RS, /* Router Solicitation */ NDP_MSG_RA, /* Router Advertisement */ NDP_MSG_NS, /* Neighbor Solicitation */ NDP_MSG_NA, /* Neighbor Advertisement */ NDP_MSG_R, /* Redirect */ NDP_MSG_ALL, /* Matches all */ }; #define ND_OPT_NORMAL 0x0000 /* default, no change to ND message */ #define ND_OPT_NA_UNSOL 0x0001 /* Unsolicited Neighbour Advertisement */ enum ndp_route_preference { NDP_ROUTE_PREF_LOW = 3, NDP_ROUTE_PREF_MEDIUM = 0, NDP_ROUTE_PREF_HIGH = 1, }; int ndp_msg_new(struct ndp_msg **p_msg, enum ndp_msg_type msg_type); void ndp_msg_destroy(struct ndp_msg *msg); void *ndp_msg_payload(struct ndp_msg *msg); size_t ndp_msg_payload_maxlen(struct ndp_msg *msg); size_t ndp_msg_payload_len(struct ndp_msg *msg); void ndp_msg_payload_len_set(struct ndp_msg *msg, size_t len); void *ndp_msg_payload_opts(struct ndp_msg *msg); size_t ndp_msg_payload_opts_len(struct ndp_msg *msg); struct ndp_msgrs *ndp_msgrs(struct ndp_msg *msg); struct ndp_msgra *ndp_msgra(struct ndp_msg *msg); struct ndp_msgns *ndp_msgns(struct ndp_msg *msg); struct ndp_msgna *ndp_msgna(struct ndp_msg *msg); struct ndp_msgr *ndp_msgr(struct ndp_msg *msg); enum ndp_msg_type ndp_msg_type(struct ndp_msg *msg); struct in6_addr *ndp_msg_addrto(struct ndp_msg *msg); uint32_t ndp_msg_ifindex(struct ndp_msg *msg); void ndp_msg_ifindex_set(struct ndp_msg *msg, uint32_t ifindex); void ndp_msg_target_set(struct ndp_msg *msg, struct in6_addr *target); void ndp_msg_dest_set(struct ndp_msg *msg, struct in6_addr *dest); void ndp_msg_opt_set(struct ndp_msg *msg); int ndp_msg_send(struct ndp *ndp, struct ndp_msg *msg); int ndp_msg_send_with_flags(struct ndp *ndp, struct ndp_msg *msg, uint8_t flags); uint8_t ndp_msgra_curhoplimit(struct ndp_msgra *msgra); void ndp_msgra_curhoplimit_set(struct ndp_msgra *msgra, uint8_t curhoplimit); bool ndp_msgra_flag_managed(struct ndp_msgra *msgra); void ndp_msgra_flag_managed_set(struct ndp_msgra *msgra, bool flag_managed); bool ndp_msgra_flag_other(struct ndp_msgra *msgra); void ndp_msgra_flag_other_set(struct ndp_msgra *msgra, bool flag_other); bool ndp_msgra_flag_home_agent(struct ndp_msgra *msgra); void ndp_msgra_flag_home_agent_set(struct ndp_msgra *msgra, bool flag_home_agent); enum ndp_route_preference ndp_msgra_route_preference(struct ndp_msgra *msgra); void ndp_msgra_route_preference_set(struct ndp_msgra *msgra, enum ndp_route_preference pref); uint16_t ndp_msgra_router_lifetime(struct ndp_msgra *msgra); void ndp_msgra_router_lifetime_set(struct ndp_msgra *msgra, uint16_t router_lifetime); uint32_t ndp_msgra_reachable_time(struct ndp_msgra *msgra); void ndp_msgra_reachable_time_set(struct ndp_msgra *msgra, uint32_t reachable_time); uint32_t ndp_msgra_retransmit_time(struct ndp_msgra *msgra); void ndp_msgra_retransmit_time_set(struct ndp_msgra *msgra, uint32_t retransmit_time); bool ndp_msgna_flag_router(struct ndp_msgna *msgna); void ndp_msgna_flag_router_set(struct ndp_msgna *msgna, bool flag_router); bool ndp_msgna_flag_solicited(struct ndp_msgna *msgna); void ndp_msgna_flag_solicited_set(struct ndp_msgna *msgna, bool flag_solicited); bool ndp_msgna_flag_override(struct ndp_msgna *msgna); void ndp_msgna_flag_override_set(struct ndp_msgna *msgna, bool flag_override); enum ndp_msg_opt_type { NDP_MSG_OPT_SLLADDR, /* Source Link-layer Address */ NDP_MSG_OPT_TLLADDR, /* Target Link-layer Address */ NDP_MSG_OPT_PREFIX, /* Prefix Information */ NDP_MSG_OPT_REDIR, /* Redirected Header */ NDP_MSG_OPT_MTU, /* MTU */ NDP_MSG_OPT_ROUTE, /* Route Information */ NDP_MSG_OPT_RDNSS, /* Recursive DNS Server */ NDP_MSG_OPT_DNSSL, /* DNS Search List */ NDP_MSG_OPT_PREF64, /* NAT64 prefix */ }; int ndp_msg_next_opt_offset(struct ndp_msg *msg, int offset, enum ndp_msg_opt_type opt_type); #define ndp_msg_opt_for_each_offset(offset, msg, type) \ for (offset = ndp_msg_next_opt_offset(msg, -1, type); \ offset != -1; \ offset = ndp_msg_next_opt_offset(msg, offset, type)) unsigned char *ndp_msg_opt_slladdr(struct ndp_msg *msg, int offset); size_t ndp_msg_opt_slladdr_len(struct ndp_msg *msg, int offset); unsigned char *ndp_msg_opt_tlladdr(struct ndp_msg *msg, int offset); size_t ndp_msg_opt_tlladdr_len(struct ndp_msg *msg, int offset); struct in6_addr *ndp_msg_opt_prefix(struct ndp_msg *msg, int offset); uint8_t ndp_msg_opt_prefix_len(struct ndp_msg *msg, int offset); uint32_t ndp_msg_opt_prefix_valid_time(struct ndp_msg *msg, int offset); uint32_t ndp_msg_opt_prefix_preferred_time(struct ndp_msg *msg, int offset); bool ndp_msg_opt_prefix_flag_on_link(struct ndp_msg *msg, int offset); bool ndp_msg_opt_prefix_flag_auto_addr_conf(struct ndp_msg *msg, int offset); bool ndp_msg_opt_prefix_flag_router_addr(struct ndp_msg *msg, int offset); uint32_t ndp_msg_opt_mtu(struct ndp_msg *msg, int offset); struct in6_addr *ndp_msg_opt_route_prefix(struct ndp_msg *msg, int offset); uint8_t ndp_msg_opt_route_prefix_len(struct ndp_msg *msg, int offset); uint32_t ndp_msg_opt_route_lifetime(struct ndp_msg *msg, int offset); enum ndp_route_preference ndp_msg_opt_route_preference(struct ndp_msg *msg, int offset); uint32_t ndp_msg_opt_rdnss_lifetime(struct ndp_msg *msg, int offset); struct in6_addr *ndp_msg_opt_rdnss_addr(struct ndp_msg *msg, int offset, int addr_index); uint16_t ndp_msg_opt_pref64_lifetime(struct ndp_msg *msg, int offset); uint8_t ndp_msg_opt_pref64_prefix_length(struct ndp_msg *msg, int offset); struct in6_addr *ndp_msg_opt_pref64_prefix(struct ndp_msg *msg, int offset); #define ndp_msg_opt_rdnss_for_each_addr(addr, addr_index, msg, offset) \ for (addr_index = 0, \ addr = ndp_msg_opt_rdnss_addr(msg, offset, addr_index); \ addr; \ addr = ndp_msg_opt_rdnss_addr(msg, offset, ++addr_index)) uint32_t ndp_msg_opt_dnssl_lifetime(struct ndp_msg *msg, int offset); char *ndp_msg_opt_dnssl_domain(struct ndp_msg *msg, int offset, int domain_index); #define ndp_msg_opt_dnssl_for_each_domain(domain, domain_index, msg, offset) \ for (domain_index = 0, \ domain = ndp_msg_opt_dnssl_domain(msg, offset, domain_index); \ domain; \ domain = ndp_msg_opt_dnssl_domain(msg, offset, ++domain_index)) typedef int (*ndp_msgrcv_handler_func_t)(struct ndp *ndp, struct ndp_msg *msg, void *priv); int ndp_msgrcv_handler_register(struct ndp *ndp, ndp_msgrcv_handler_func_t func, enum ndp_msg_type msg_type, uint32_t ifindex, void *priv); void ndp_msgrcv_handler_unregister(struct ndp *ndp, ndp_msgrcv_handler_func_t func, enum ndp_msg_type msg_type, uint32_t ifindex, void *priv); int ndp_get_eventfd(struct ndp *ndp); int ndp_call_eventfd_handler(struct ndp *ndp); int ndp_callall_eventfd_handler(struct ndp *ndp); int ndp_open(struct ndp **p_ndp); void ndp_close(struct ndp *ndp); #ifdef __cplusplus } /* extern "C" */ #endif #endif /* _NDP_H_ */ libndp-1.9/libndp/000077500000000000000000000000001463751251200141115ustar00rootroot00000000000000libndp-1.9/libndp/Makefile.am000066400000000000000000000007611463751251200161510ustar00rootroot00000000000000MAINTAINERCLEANFILES = Makefile.in ACLOCAL_AMFLAGS = -I m4 AM_CFLAGS = -fvisibility=hidden -ffunction-sections -fdata-sections AM_LDFLAGS = -Wl,--gc-sections -Wl,--as-needed lib_LTLIBRARIES = libndp.la libndp_la_SOURCES = libndp.c libndp_la_CFLAGS= $(AM_CFLAGS) -I${top_srcdir}/include -D_GNU_SOURCE libndp_la_LDFLAGS = $(AM_LDFLAGS) -version-info @LIBNDP_CURRENT@:@LIBNDP_REVISION@:@LIBNDP_AGE@ pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libndp.pc EXTRA_DIST = ndp_private.h list.h libndp-1.9/libndp/libndp.c000066400000000000000000001463611463751251200155400ustar00rootroot00000000000000/* * libndp.c - Neighbour discovery library * Copyright (C) 2013-2015 Jiri Pirko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ndp_private.h" #include "list.h" #define pr_err(args...) fprintf(stderr, ##args) /** * SECTION: logging * @short_description: libndp logging facility */ void ndp_log(struct ndp *ndp, int priority, const char *file, int line, const char *fn, const char *format, ...) { va_list args; va_start(args, format); ndp->log_fn(ndp, priority, file, line, fn, format, args); va_end(args); } static void log_stderr(struct ndp *ndp, int priority, const char *file, int line, const char *fn, const char *format, va_list args) { fprintf(stderr, "libndp: %s: ", fn); vfprintf(stderr, format, args); fprintf(stderr, "\n"); } static int log_priority(const char *priority) { char *endptr; int prio; prio = strtol(priority, &endptr, 10); if (endptr[0] == '\0' || isspace(endptr[0])) return prio; if (strncmp(priority, "err", 3) == 0) return LOG_ERR; if (strncmp(priority, "info", 4) == 0) return LOG_INFO; if (strncmp(priority, "debug", 5) == 0) return LOG_DEBUG; return 0; } /** * ndp_set_log_fn: * @ndp: libndp library context * @log_fn: function to be called for logging messages * * The built-in logging writes to stderr. It can be * overridden by a custom function, to plug log messages * into the user's logging functionality. **/ NDP_EXPORT void ndp_set_log_fn(struct ndp *ndp, void (*log_fn)(struct ndp *ndp, int priority, const char *file, int line, const char *fn, const char *format, va_list args)) { ndp->log_fn = log_fn; dbg(ndp, "Custom logging function %p registered.", log_fn); } /** * ndp_get_log_priority: * @ndp: libndp library context * * Returns: the current logging priority. **/ NDP_EXPORT int ndp_get_log_priority(struct ndp *ndp) { return ndp->log_priority; } /** * ndp_set_log_priority: * @ndp: libndp library context * @priority: the new logging priority * * Set the current logging priority. The value controls which messages * are logged. **/ NDP_EXPORT void ndp_set_log_priority(struct ndp *ndp, int priority) { ndp->log_priority = priority; } /** * SECTION: helpers * @short_description: various internal helper functions */ #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #define BUG_ON(expr) { if (expr) assert(0); } static void *myzalloc(size_t size) { return calloc(1, size); } static int myrecvfrom6(int sockfd, void *buf, size_t *buflen, int flags, struct in6_addr *addr, uint32_t *ifindex, int *hoplimit) { struct sockaddr_in6 sin6; unsigned char cbuf[2 * CMSG_SPACE(sizeof(struct in6_pktinfo))]; struct iovec iovec; struct msghdr msghdr; struct cmsghdr *cmsghdr; ssize_t len; iovec.iov_len = *buflen; iovec.iov_base = buf; memset(&msghdr, 0, sizeof(msghdr)); msghdr.msg_name = &sin6; msghdr.msg_namelen = sizeof(sin6); msghdr.msg_iov = &iovec; msghdr.msg_iovlen = 1; msghdr.msg_control = cbuf; msghdr.msg_controllen = sizeof(cbuf); len = recvmsg(sockfd, &msghdr, flags); if (len == -1) return -errno; *buflen = len; /* Set ifindex to scope_id now. But since scope_id gets not * set by kernel for linklocal addresses, use pktinfo to obtain that * value right after. */ *ifindex = sin6.sin6_scope_id; for (cmsghdr = CMSG_FIRSTHDR(&msghdr); cmsghdr; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) { if (cmsghdr->cmsg_level != IPPROTO_IPV6) continue; switch(cmsghdr->cmsg_type) { case IPV6_PKTINFO: if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { struct in6_pktinfo *pktinfo; pktinfo = (struct in6_pktinfo *) CMSG_DATA(cmsghdr); *ifindex = pktinfo->ipi6_ifindex; } break; case IPV6_HOPLIMIT: if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(int))) { int *val; val = (int *) CMSG_DATA(cmsghdr); *hoplimit = *val; } break; } } *addr = sin6.sin6_addr; return 0; } static int mysendto6(int sockfd, void *buf, size_t buflen, int flags, struct in6_addr *addr, uint32_t ifindex) { struct sockaddr_in6 sin6; ssize_t ret; memset(&sin6, 0, sizeof(sin6)); memcpy(&sin6.sin6_addr, addr, sizeof(sin6.sin6_addr)); sin6.sin6_scope_id = ifindex; resend: ret = sendto(sockfd, buf, buflen, flags, &sin6, sizeof(sin6)); if (ret == -1) { switch(errno) { case EINTR: goto resend; default: return -errno; } } return 0; } static const char *str_in6_addr(struct in6_addr *addr, char buf[static INET6_ADDRSTRLEN]) { return inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN); } /** * SECTION: NDP implementation * @short_description: functions that actually implements NDP */ struct ndp_msggeneric { void *dataptr; /* must be first */ }; struct ndp_msgrs { struct nd_router_solicit *rs; /* must be first */ }; struct ndp_msgra { struct nd_router_advert *ra; /* must be first */ }; struct ndp_msgns { struct nd_neighbor_solicit *ns; /* must be first */ }; struct ndp_msgna { struct nd_neighbor_advert *na; /* must be first */ }; struct ndp_msgr { struct nd_redirect *r; /* must be first */ }; struct ndp_msg { #define NDP_MSG_BUFLEN 1500 unsigned char buf[NDP_MSG_BUFLEN]; size_t len; struct in6_addr addrto; uint32_t ifindex; int hoplimit; struct icmp6_hdr * icmp6_hdr; unsigned char * opts_start; /* pointer to buf at the place where opts start */ union { struct ndp_msggeneric generic; struct ndp_msgrs rs; struct ndp_msgra ra; struct ndp_msgns ns; struct ndp_msgna na; struct ndp_msgr r; } nd_msg; }; struct ndp_msg_type_info { #define NDP_STRABBR_SIZE 4 char strabbr[NDP_STRABBR_SIZE]; uint8_t raw_type; size_t raw_struct_size; void (*addrto_adjust)(struct in6_addr *addr); bool (*addrto_validate)(struct in6_addr *addr); }; static void ndp_msg_addrto_adjust_all_nodes(struct in6_addr *addr) { struct in6_addr any = IN6ADDR_ANY_INIT; if (memcmp(addr, &any, sizeof(any))) return; addr->s6_addr32[0] = htonl(0xFF020000); addr->s6_addr32[1] = 0; addr->s6_addr32[2] = 0; addr->s6_addr32[3] = htonl(0x1); } static void ndp_msg_addrto_adjust_all_routers(struct in6_addr *addr) { struct in6_addr any = IN6ADDR_ANY_INIT; if (memcmp(addr, &any, sizeof(any))) return; addr->s6_addr32[0] = htonl(0xFF020000); addr->s6_addr32[1] = 0; addr->s6_addr32[2] = 0; addr->s6_addr32[3] = htonl(0x2); } /* * compute link-local solicited-node multicast address */ static void ndp_msg_addrto_adjust_solicit_multi(struct in6_addr *addr, struct in6_addr *target) { addr->s6_addr32[0] = htonl(0xFF020000); addr->s6_addr32[1] = 0; addr->s6_addr32[2] = htonl(0x1); addr->s6_addr32[3] = htonl(0xFF000000) | target->s6_addr32[3]; } static bool ndp_msg_addrto_validate_link_local(struct in6_addr *addr) { return IN6_IS_ADDR_LINKLOCAL (addr); } static struct ndp_msg_type_info ndp_msg_type_info_list[] = { [NDP_MSG_RS] = { .strabbr = "RS", .raw_type = ND_ROUTER_SOLICIT, .raw_struct_size = sizeof(struct nd_router_solicit), .addrto_adjust = ndp_msg_addrto_adjust_all_routers, }, [NDP_MSG_RA] = { .strabbr = "RA", .raw_type = ND_ROUTER_ADVERT, .raw_struct_size = sizeof(struct nd_router_advert), .addrto_validate = ndp_msg_addrto_validate_link_local, }, [NDP_MSG_NS] = { .strabbr = "NS", .raw_type = ND_NEIGHBOR_SOLICIT, .raw_struct_size = sizeof(struct nd_neighbor_solicit), .addrto_adjust = ndp_msg_addrto_adjust_all_nodes, }, [NDP_MSG_NA] = { .strabbr = "NA", .raw_type = ND_NEIGHBOR_ADVERT, .raw_struct_size = sizeof(struct nd_neighbor_advert), }, [NDP_MSG_R] = { .strabbr = "R", .raw_type = ND_REDIRECT, .raw_struct_size = sizeof(struct nd_redirect), .addrto_validate = ndp_msg_addrto_validate_link_local, }, }; #define NDP_MSG_TYPE_LIST_SIZE ARRAY_SIZE(ndp_msg_type_info_list) struct ndp_msg_type_info *ndp_msg_type_info(enum ndp_msg_type msg_type) { return &ndp_msg_type_info_list[msg_type]; } static int ndp_msg_type_by_raw_type(enum ndp_msg_type *p_msg_type, uint8_t raw_type) { int i; for (i = 0; i < NDP_MSG_TYPE_LIST_SIZE; i++) { if (ndp_msg_type_info(i)->raw_type == raw_type) { *p_msg_type = i; return 0; } } return -ENOENT; } static bool ndp_msg_check_valid(struct ndp_msg *msg) { size_t len = ndp_msg_payload_len(msg); enum ndp_msg_type msg_type = ndp_msg_type(msg); if (len < ndp_msg_type_info(msg_type)->raw_struct_size) return false; if (ndp_msg_type_info(msg_type)->addrto_validate) return ndp_msg_type_info(msg_type)->addrto_validate(&msg->addrto); else return true; } static struct ndp_msg *ndp_msg_alloc(void) { struct ndp_msg *msg; msg = myzalloc(sizeof(*msg)); if (!msg) return NULL; msg->icmp6_hdr = (struct icmp6_hdr *) msg->buf; return msg; } static void ndp_msg_type_set(struct ndp_msg *msg, enum ndp_msg_type msg_type); static void ndp_msg_init(struct ndp_msg *msg, enum ndp_msg_type msg_type) { size_t raw_struct_size = ndp_msg_type_info(msg_type)->raw_struct_size; ndp_msg_type_set(msg, msg_type); msg->len = raw_struct_size; msg->opts_start = msg->buf + raw_struct_size; /* Set-up "first pointers" in all ndp_msgrs, ndp_msgra, ndp_msgns, * ndp_msgna, ndp_msgr structures. */ msg->nd_msg.generic.dataptr = ndp_msg_payload(msg); } /** * ndp_msg_new: * @p_msg: pointer where new message structure address will be stored * @msg_type: message type * * Allocate new message structure of a specified type and initialize it. * * Returns: zero on success or negative number in case of an error. **/ NDP_EXPORT int ndp_msg_new(struct ndp_msg **p_msg, enum ndp_msg_type msg_type) { struct ndp_msg *msg; if (msg_type == NDP_MSG_ALL) return -EINVAL; msg = ndp_msg_alloc(); if (!msg) return -ENOMEM; ndp_msg_init(msg, msg_type); *p_msg = msg; return 0; } /** * ndp_msg_destroy: * * Destroy message structure. **/ NDP_EXPORT void ndp_msg_destroy(struct ndp_msg *msg) { free(msg); } /** * ndp_msg_payload: * @msg: message structure * * Get raw Neighbour discovery packet data. * * Returns: pointer to raw data. **/ NDP_EXPORT void *ndp_msg_payload(struct ndp_msg *msg) { return msg->buf; } /** * ndp_msg_payload_maxlen: * @msg: message structure * * Get raw Neighbour discovery packet data maximum length. * * Returns: length in bytes. **/ NDP_EXPORT size_t ndp_msg_payload_maxlen(struct ndp_msg *msg) { return sizeof(msg->buf); } /** * ndp_msg_payload_len: * @msg: message structure * * Get raw Neighbour discovery packet data length. * * Returns: length in bytes. **/ NDP_EXPORT size_t ndp_msg_payload_len(struct ndp_msg *msg) { return msg->len; } /** * ndp_msg_payload_len_set: * @msg: message structure * * Set raw Neighbour discovery packet data length. **/ NDP_EXPORT void ndp_msg_payload_len_set(struct ndp_msg *msg, size_t len) { if (len > sizeof(msg->buf)) len = sizeof(msg->buf); msg->len = len; } /** * ndp_msg_payload_opts: * @msg: message structure * * Get raw Neighbour discovery packet options part data. * * Returns: pointer to raw data. **/ NDP_EXPORT void *ndp_msg_payload_opts(struct ndp_msg *msg) { return msg->opts_start; } static void *ndp_msg_payload_opts_offset(struct ndp_msg *msg, int offset) { unsigned char *ptr = ndp_msg_payload_opts(msg); return ptr + offset; } /** * ndp_msg_payload_opts_len: * @msg: message structure * * Get raw Neighbour discovery packet options part data length. * * Returns: length in bytes. **/ NDP_EXPORT size_t ndp_msg_payload_opts_len(struct ndp_msg *msg) { return msg->len - (msg->opts_start - msg->buf); } /** * ndp_msgrs: * @msg: message structure * * Get RS message structure by passed @msg. * * Returns: RS message structure or NULL in case the message is not of type RS. **/ NDP_EXPORT struct ndp_msgrs *ndp_msgrs(struct ndp_msg *msg) { if (ndp_msg_type(msg) != NDP_MSG_RS) return NULL; return &msg->nd_msg.rs; } /** * ndp_msgra: * @msg: message structure * * Get RA message structure by passed @msg. * * Returns: RA message structure or NULL in case the message is not of type RA. **/ NDP_EXPORT struct ndp_msgra *ndp_msgra(struct ndp_msg *msg) { if (ndp_msg_type(msg) != NDP_MSG_RA) return NULL; return &msg->nd_msg.ra; } /** * ndp_msgns: * @msg: message structure * * Get NS message structure by passed @msg. * * Returns: NS message structure or NULL in case the message is not of type NS. **/ NDP_EXPORT struct ndp_msgns *ndp_msgns(struct ndp_msg *msg) { if (ndp_msg_type(msg) != NDP_MSG_NS) return NULL; return &msg->nd_msg.ns; } /** * ndp_msgna: * @msg: message structure * * Get NA message structure by passed @msg. * * Returns: NA message structure or NULL in case the message is not of type NA. **/ NDP_EXPORT struct ndp_msgna *ndp_msgna(struct ndp_msg *msg) { if (ndp_msg_type(msg) != NDP_MSG_NA) return NULL; return &msg->nd_msg.na; } /** * ndp_msgr: * @msg: message structure * * Get R message structure by passed @msg. * * Returns: R message structure or NULL in case the message is not of type R. **/ NDP_EXPORT struct ndp_msgr *ndp_msgr(struct ndp_msg *msg) { if (ndp_msg_type(msg) != NDP_MSG_R) return NULL; return &msg->nd_msg.r; } /** * ndp_msg_type: * @msg: message structure * * Get type of message. * * Returns: Message type **/ NDP_EXPORT enum ndp_msg_type ndp_msg_type(struct ndp_msg *msg) { enum ndp_msg_type msg_type; int err; err = ndp_msg_type_by_raw_type(&msg_type, msg->icmp6_hdr->icmp6_type); /* Type should be always set correctly (ensured by ndp_msg_init) */ BUG_ON(err); return msg_type; } static void ndp_msg_type_set(struct ndp_msg *msg, enum ndp_msg_type msg_type) { msg->icmp6_hdr->icmp6_type = ndp_msg_type_info(msg_type)->raw_type; } /** * ndp_msg_addrto: * @msg: message structure * * Get "to address" of message. * * Returns: pointer to address. **/ NDP_EXPORT struct in6_addr *ndp_msg_addrto(struct ndp_msg *msg) { return &msg->addrto; } /** * ndp_msg_ifindex: * @msg: message structure * * Get interface index of message. * * Returns: Interface index **/ NDP_EXPORT uint32_t ndp_msg_ifindex(struct ndp_msg *msg) { return msg->ifindex; } /** * ndp_msg_ifindex_set: * @msg: message structure * * Set raw interface index of message. **/ NDP_EXPORT void ndp_msg_ifindex_set(struct ndp_msg *msg, uint32_t ifindex) { msg->ifindex = ifindex; } /** * ndp_msg_dest_set: * @msg: message structure * @dest: ns,na dest * * Set dest address in IPv6 header for NS and NA. **/ NDP_EXPORT void ndp_msg_dest_set(struct ndp_msg *msg, struct in6_addr *dest) { enum ndp_msg_type msg_type = ndp_msg_type(msg); switch (msg_type) { case NDP_MSG_NS: /* fall through */ case NDP_MSG_NA: msg->addrto = *dest; /* fall through */ default: break; } } /** * ndp_msg_target_set: * @msg: message structure * @target: ns,na target * * Set target address in ICMPv6 header for NS and NA. **/ NDP_EXPORT void ndp_msg_target_set(struct ndp_msg *msg, struct in6_addr *target) { struct in6_addr any = IN6ADDR_ANY_INIT; enum ndp_msg_type msg_type = ndp_msg_type(msg); switch (msg_type) { case NDP_MSG_NS: ((struct ndp_msgns*)&msg->nd_msg)->ns->nd_ns_target = *target; /* * Neighbor Solicitations are multicast when the node * needs to resolve an address and unicast when the * node seeks to verify the reachability of a * neighbor. * * In this case we need to update the dest address in * IPv6 header when * a) IPv6 dest address is not set * b) ICMPv6 target address is supplied * */ if (!memcmp(&msg->addrto, &any, sizeof(any)) && memcmp(target, &any, sizeof(any))) ndp_msg_addrto_adjust_solicit_multi(&msg->addrto, target); break; case NDP_MSG_NA: ((struct ndp_msgna*)&msg->nd_msg)->na->nd_na_target = *target; break; default: break; } } static int ndp_get_iface_mac(int ifindex, char *ptr) { int sockfd, err = 0; struct ifreq ifr; sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { pr_err("%s: Failed to create socket", __func__); return -errno; } if (if_indextoname(ifindex, (char *)&ifr.ifr_name) == NULL) { pr_err("%s: Failed to get iface name with index %d", __func__, ifindex); err = -errno; goto close_sock; } if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0) { pr_err("%s: Failed to get iface mac with index %d\n", __func__, ifindex); err = -errno; goto close_sock; } memcpy(ptr, &ifr.ifr_hwaddr.sa_data, sizeof(ifr.ifr_hwaddr.sa_data)); close_sock: close(sockfd); return err; } static void ndp_msg_opt_set_linkaddr(struct ndp_msg *msg, int ndp_opt) { char *opts_start = ndp_msg_payload_opts(msg); struct nd_opt_hdr *s_laddr_opt = (struct nd_opt_hdr *) opts_start; char *opt_data = (char *) s_laddr_opt + sizeof(struct nd_opt_hdr); int err; err = ndp_get_iface_mac(ndp_msg_ifindex(msg), opt_data); if (err) return; opt_data += 6; s_laddr_opt->nd_opt_type = ndp_opt; s_laddr_opt->nd_opt_len = (opt_data - opts_start) >> 3; msg->len += opt_data - opts_start; } /** * ndp_msg_opt_set: * @msg: message structure * * Set neighbor discovery option info. **/ NDP_EXPORT void ndp_msg_opt_set(struct ndp_msg *msg) { enum ndp_msg_type msg_type = ndp_msg_type(msg); switch (msg_type) { case NDP_MSG_NS: ndp_msg_opt_set_linkaddr(msg, ND_OPT_SOURCE_LINKADDR); break; case NDP_MSG_NA: ndp_msg_opt_set_linkaddr(msg, ND_OPT_TARGET_LINKADDR); break; default: break; } } /** * ndp_msg_send: * @ndp: libndp library context * @msg: message structure * * Send message. * * Returns: zero on success or negative number in case of an error. **/ NDP_EXPORT int ndp_msg_send(struct ndp *ndp, struct ndp_msg *msg) { return ndp_msg_send_with_flags(ndp, msg, ND_OPT_NORMAL); } /** * ndp_msg_send_with_flags: * @ndp: libndp library context * @msg: message structure * @flags: option flags within message type * * Send message. * * Returns: zero on success or negative number in case of an error. **/ NDP_EXPORT int ndp_msg_send_with_flags(struct ndp *ndp, struct ndp_msg *msg, uint8_t flags) { enum ndp_msg_type msg_type = ndp_msg_type(msg); if (ndp_msg_type_info(msg_type)->addrto_adjust) ndp_msg_type_info(msg_type)->addrto_adjust(&msg->addrto); switch (msg_type) { case NDP_MSG_NA: if (flags & ND_OPT_NA_UNSOL) { ndp_msgna_flag_override_set((struct ndp_msgna*)&msg->nd_msg, true); ndp_msgna_flag_solicited_set((struct ndp_msgna*)&msg->nd_msg, false); ndp_msg_addrto_adjust_all_nodes(&msg->addrto); } else { ndp_msgna_flag_solicited_set((struct ndp_msgna*)&msg->nd_msg, true); } break; default: break; } return mysendto6(ndp->sock, msg->buf, msg->len, 0, &msg->addrto, msg->ifindex); } /** * SECTION: msgra getters/setters * @short_description: Getters and setters for RA message */ /** * ndp_msgra_curhoplimit: * @msgra: RA message structure * * Get RA curhoplimit. * * Returns: curhoplimit. **/ NDP_EXPORT uint8_t ndp_msgra_curhoplimit(struct ndp_msgra *msgra) { return msgra->ra->nd_ra_curhoplimit; } /** * ndp_msgra_curhoplimit_set: * @msgra: RA message structure * * Set RA curhoplimit. **/ NDP_EXPORT void ndp_msgra_curhoplimit_set(struct ndp_msgra *msgra, uint8_t curhoplimit) { msgra->ra->nd_ra_curhoplimit = curhoplimit; } /** * ndp_msgra_flag_managed: * @msgra: RA message structure * * Get RA managed flag. * * Returns: managed flag. **/ NDP_EXPORT bool ndp_msgra_flag_managed(struct ndp_msgra *msgra) { return msgra->ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED; } /** * ndp_msgra_flag_managed_set: * @msgra: RA message structure * * Set RA managed flag. **/ NDP_EXPORT void ndp_msgra_flag_managed_set(struct ndp_msgra *msgra, bool flag_managed) { if (flag_managed) msgra->ra->nd_ra_flags_reserved |= ND_RA_FLAG_MANAGED; else msgra->ra->nd_ra_flags_reserved &= ~ND_RA_FLAG_MANAGED; } /** * ndp_msgra_flag_other: * @msgra: RA message structure * * Get RA other flag. * * Returns: other flag. **/ NDP_EXPORT bool ndp_msgra_flag_other(struct ndp_msgra *msgra) { return msgra->ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER; } /** * ndp_msgra_flag_other_set: * @msgra: RA message structure * * Set RA other flag. **/ NDP_EXPORT void ndp_msgra_flag_other_set(struct ndp_msgra *msgra, bool flag_other) { if (flag_other) msgra->ra->nd_ra_flags_reserved |= ND_RA_FLAG_OTHER; else msgra->ra->nd_ra_flags_reserved &= ~ND_RA_FLAG_OTHER; } /** * ndp_msgra_flag_home_agent: * @msgra: RA message structure * * Get RA home_agent flag. * * Returns: home_agent flag. **/ NDP_EXPORT bool ndp_msgra_flag_home_agent(struct ndp_msgra *msgra) { return msgra->ra->nd_ra_flags_reserved & ND_RA_FLAG_HOME_AGENT; } /** * ndp_msgra_flag_home_agent_set: * @msgra: RA message structure * * Set RA home_agent flag. **/ NDP_EXPORT void ndp_msgra_flag_home_agent_set(struct ndp_msgra *msgra, bool flag_home_agent) { if (flag_home_agent) msgra->ra->nd_ra_flags_reserved |= ND_RA_FLAG_HOME_AGENT; else msgra->ra->nd_ra_flags_reserved &= ~ND_RA_FLAG_HOME_AGENT; } /** * ndp_msgra_route_preference: * @msgra: RA message structure * * Get route preference. * * Returns: route preference. **/ NDP_EXPORT enum ndp_route_preference ndp_msgra_route_preference(struct ndp_msgra *msgra) { uint8_t prf = (msgra->ra->nd_ra_flags_reserved >> 3) & 3; /* rfc4191 says: * If the Router Lifetime is zero, the preference value MUST be set to * (00) by the sender and MUST be ignored by the receiver. * If the Reserved (10) value is received, the receiver MUST treat the * value as if it were (00). */ if (prf == 2 || !ndp_msgra_router_lifetime(msgra)) prf = 0; return prf; } /** * ndp_msgra_route_preference_set: * @msgra: RA message structure * @pref: preference * * Set route preference. **/ NDP_EXPORT void ndp_msgra_route_preference_set(struct ndp_msgra *msgra, enum ndp_route_preference pref) { msgra->ra->nd_ra_flags_reserved &= ~(3 << 3); msgra->ra->nd_ra_flags_reserved |= (pref << 3); } /** * ndp_msgra_router_lifetime: * @msgra: RA message structure * * Get RA router lifetime. * * Returns: router lifetime in seconds. **/ NDP_EXPORT uint16_t ndp_msgra_router_lifetime(struct ndp_msgra *msgra) { return ntohs(msgra->ra->nd_ra_router_lifetime); } /** * ndp_msgra_router_lifetime_set: * @msgra: RA message structure * * Set RA router lifetime. **/ NDP_EXPORT void ndp_msgra_router_lifetime_set(struct ndp_msgra *msgra, uint16_t router_lifetime) { msgra->ra->nd_ra_router_lifetime = htons(router_lifetime); } /** * ndp_msgra_reachable_time: * @msgra: RA message structure * * Get RA reachable time. * * Returns: reachable time in milliseconds. **/ NDP_EXPORT uint32_t ndp_msgra_reachable_time(struct ndp_msgra *msgra) { return ntohl(msgra->ra->nd_ra_reachable); } /** * ndp_msgra_reachable_time_set: * @msgra: RA message structure * * Set RA reachable time. **/ NDP_EXPORT void ndp_msgra_reachable_time_set(struct ndp_msgra *msgra, uint32_t reachable_time) { msgra->ra->nd_ra_reachable = htonl(reachable_time); } /** * ndp_msgra_retransmit_time: * @msgra: RA message structure * * Get RA retransmit time. * * Returns: retransmit time in milliseconds. **/ NDP_EXPORT uint32_t ndp_msgra_retransmit_time(struct ndp_msgra *msgra) { return ntohl(msgra->ra->nd_ra_retransmit); } /** * ndp_msgra_retransmit_time_set: * @msgra: RA message structure * * Set RA retransmit time. **/ NDP_EXPORT void ndp_msgra_retransmit_time_set(struct ndp_msgra *msgra, uint32_t retransmit_time) { msgra->ra->nd_ra_retransmit = htonl(retransmit_time); } /** * SECTION: msgna getters/setters * @short_description: Getters and setters for NA message */ /** * ndp_msgna_flag_router: * @msgna: NA message structure * * Get NA router flag. * * Returns: router flag. **/ NDP_EXPORT bool ndp_msgna_flag_router(struct ndp_msgna *msgna) { return msgna->na->nd_na_flags_reserved & ND_NA_FLAG_ROUTER; } /** * ndp_msgna_flag_router_set: * @msgna: NA message structure * * Set NA router flag. **/ NDP_EXPORT void ndp_msgna_flag_router_set(struct ndp_msgna *msgna, bool flag_router) { if (flag_router) msgna->na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER; else msgna->na->nd_na_flags_reserved &= ~ND_NA_FLAG_ROUTER; } /** * ndp_msgna_flag_solicited: * @msgna: NA message structure * * Get NA solicited flag. * * Returns: solicited flag. **/ NDP_EXPORT bool ndp_msgna_flag_solicited(struct ndp_msgna *msgna) { return msgna->na->nd_na_flags_reserved & ND_NA_FLAG_SOLICITED; } /** * ndp_msgna_flag_solicited_set: * @msgna: NA message structure * * Set NA managed flag. **/ NDP_EXPORT void ndp_msgna_flag_solicited_set(struct ndp_msgna *msgna, bool flag_solicited) { if (flag_solicited) msgna->na->nd_na_flags_reserved |= ND_NA_FLAG_SOLICITED; else msgna->na->nd_na_flags_reserved &= ~ND_NA_FLAG_SOLICITED; } /** * ndp_msgna_flag_override: * @msgna: NA message structure * * Get NA override flag. * * Returns: override flag. **/ NDP_EXPORT bool ndp_msgna_flag_override(struct ndp_msgna *msgna) { return msgna->na->nd_na_flags_reserved & ND_NA_FLAG_OVERRIDE; } /** * ndp_msgna_flag_override_set: * @msgra: NA message structure * * Set NA override flag. */ NDP_EXPORT void ndp_msgna_flag_override_set(struct ndp_msgna *msgna, bool flag_override) { if (flag_override) msgna->na->nd_na_flags_reserved |= ND_NA_FLAG_OVERRIDE; else msgna->na->nd_na_flags_reserved &= ~ND_NA_FLAG_OVERRIDE; } /** * SECTION: msg_opt infrastructure * @short_description: Infrastructure for options */ struct ndp_msg_opt_type_info { uint8_t raw_type; size_t raw_struct_size; bool (*check_valid)(void *opt_data); }; static bool ndp_msg_opt_route_check_valid(void *opt_data) { struct __nd_opt_route_info *ri = opt_data; /* rfc4191 says: * If the Reserved (10) value is received, the Route Information Option * MUST be ignored. */ if (((ri->nd_opt_ri_prf_reserved >> 3) & 3) == 2) return false; /* The Length field is 1, 2, or 3 depending on the Prefix Length. * If Prefix Length is greater than 64, then Length must be 3. * If Prefix Length is greater than 0, then Length must be 2 or 3. * If Prefix Length is zero, then Length must be 1, 2, or 3. */ if (ri->nd_opt_ri_len > 3 || (ri->nd_opt_ri_prefix_len > 64 && ri->nd_opt_ri_len != 3) || (ri->nd_opt_ri_prefix_len > 0 && ri->nd_opt_ri_len == 1)) return false; return true; } static bool ndp_msg_opt_pref64_check_valid(void *opt_data) { struct __nd_opt_pref64 *pref64 = opt_data; uint16_t plc; /* rfc8781: the receiver MUST ignore the PREF64 option if the * Length field value is not 2 */ if (pref64->nd_opt_pref64_len != 2) return false; /* The PLC field values 0, 1, 2, 3, 4, and 5 indicate the NAT64 * prefix length. The receiver MUST ignore the PREF64 option if the * Prefix Length Code field is not set to one of those values.*/ plc = ntohs(pref64->nd_opt_pref64_lft_plc) & ND_OPT_PREF64_PLC_MASK; if (plc > 5) return false; return true; } static struct ndp_msg_opt_type_info ndp_msg_opt_type_info_list[] = { [NDP_MSG_OPT_SLLADDR] = { .raw_type = ND_OPT_SOURCE_LINKADDR, }, [NDP_MSG_OPT_TLLADDR] = { .raw_type = ND_OPT_TARGET_LINKADDR, }, [NDP_MSG_OPT_PREFIX] = { .raw_type = ND_OPT_PREFIX_INFORMATION, .raw_struct_size = sizeof(struct nd_opt_prefix_info), }, [NDP_MSG_OPT_REDIR] = { .raw_type = ND_OPT_REDIRECTED_HEADER, }, [NDP_MSG_OPT_MTU] = { .raw_type = ND_OPT_MTU, .raw_struct_size = sizeof(struct nd_opt_mtu), }, [NDP_MSG_OPT_ROUTE] = { .raw_type = __ND_OPT_ROUTE_INFO, .raw_struct_size = sizeof(struct __nd_opt_route_info), .check_valid = ndp_msg_opt_route_check_valid, }, [NDP_MSG_OPT_RDNSS] = { .raw_type = __ND_OPT_RDNSS, .raw_struct_size = sizeof(struct __nd_opt_rdnss), }, [NDP_MSG_OPT_DNSSL] = { .raw_type = __ND_OPT_DNSSL, .raw_struct_size = sizeof(struct __nd_opt_dnssl), }, [NDP_MSG_OPT_PREF64] = { .raw_type = __ND_OPT_PREF64, .raw_struct_size = sizeof(struct __nd_opt_pref64), .check_valid = ndp_msg_opt_pref64_check_valid, }, }; #define NDP_MSG_OPT_TYPE_LIST_SIZE ARRAY_SIZE(ndp_msg_opt_type_info_list) struct ndp_msg_opt_type_info *ndp_msg_opt_type_info(enum ndp_msg_opt_type msg_opt_type) { return &ndp_msg_opt_type_info_list[msg_opt_type]; } struct ndp_msg_opt_type_info *ndp_msg_opt_type_info_by_raw_type(uint8_t raw_type) { struct ndp_msg_opt_type_info *info; int i; for (i = 0; i < NDP_MSG_OPT_TYPE_LIST_SIZE; i++) { info = &ndp_msg_opt_type_info_list[i]; if (info->raw_type == raw_type) return info; } return NULL; } /** * ndp_msg_next_opt_offset: * @msg: message structure * @offset: option payload offset * @opt_type: option type * * Find next offset of option of given type. If offset is -1, start from * beginning, otherwise start from the given offset. * This funstion is internally used by ndp_msg_opt_for_each_offset() macro. * * Returns: offset in opt payload of found opt of -1 in case it was not found. **/ NDP_EXPORT int ndp_msg_next_opt_offset(struct ndp_msg *msg, int offset, enum ndp_msg_opt_type opt_type) { unsigned char *opts_start = ndp_msg_payload_opts(msg); unsigned char *ptr = opts_start; size_t len = ndp_msg_payload_opts_len(msg); uint8_t opt_raw_type = ndp_msg_opt_type_info(opt_type)->raw_type; bool ignore = true; if (offset == -1) { offset = 0; ignore = false; } ptr += offset; len -= offset; while (len > 0) { uint8_t cur_opt_raw_type = ptr[0]; unsigned int cur_opt_len = ptr[1] << 3; /* convert to bytes */ if (!cur_opt_len || len < cur_opt_len) break; if (cur_opt_raw_type == opt_raw_type && !ignore) return ptr - opts_start; ptr += cur_opt_len; len -= cur_opt_len; ignore = false; } return -1; } #define __INVALID_OPT_TYPE_MAGIC 0xff /* * Check for validity of options and mark by magic opt type in case it is not * so ndp_msg_next_opt_offset() will ignore it. */ static bool ndp_msg_check_opts(struct ndp_msg *msg) { unsigned char *ptr = ndp_msg_payload_opts(msg); size_t len = ndp_msg_payload_opts_len(msg); struct ndp_msg_opt_type_info *info; while (len > 0) { uint8_t cur_opt_raw_type = ptr[0]; unsigned int cur_opt_len = ptr[1] << 3; /* convert to bytes */ if (!cur_opt_len) return false; if (len < cur_opt_len) break; info = ndp_msg_opt_type_info_by_raw_type(cur_opt_raw_type); if (info) { if (cur_opt_len < info->raw_struct_size || (info->check_valid && !info->check_valid(ptr))) ptr[0] = __INVALID_OPT_TYPE_MAGIC; } ptr += cur_opt_len; len -= cur_opt_len; } return true; } /** * SECTION: msg_opt getters/setters * @short_description: Getters and setters for options */ /** * ndp_msg_opt_slladdr: * @msg: message structure * @offset: in-message offset * * Get source linkaddr. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: pointer to source linkaddr. **/ NDP_EXPORT unsigned char *ndp_msg_opt_slladdr(struct ndp_msg *msg, int offset) { unsigned char *opt_data = ndp_msg_payload_opts_offset(msg, offset); return &opt_data[2]; } /** * ndp_msg_opt_slladdr_len: * @msg: message structure * @offset: in-message offset * * Get source linkaddr length. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: source linkaddr length. **/ NDP_EXPORT size_t ndp_msg_opt_slladdr_len(struct ndp_msg *msg, int offset) { return ETH_ALEN; } /** * ndp_msg_opt_tlladdr: * @msg: message structure * @offset: in-message offset * * Get target linkaddr. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: pointer to target linkaddr. **/ NDP_EXPORT unsigned char *ndp_msg_opt_tlladdr(struct ndp_msg *msg, int offset) { unsigned char *opt_data = ndp_msg_payload_opts_offset(msg, offset); return &opt_data[2]; } /** * ndp_msg_opt_tlladdr_len: * @msg: message structure * @offset: in-message offset * * Get target linkaddr length. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: target linkaddr length. **/ NDP_EXPORT size_t ndp_msg_opt_tlladdr_len(struct ndp_msg *msg, int offset) { return ETH_ALEN; } /** * ndp_msg_opt_prefix: * @msg: message structure * @offset: in-message offset * * Get prefix addr. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: pointer to address. **/ NDP_EXPORT struct in6_addr *ndp_msg_opt_prefix(struct ndp_msg *msg, int offset) { struct nd_opt_prefix_info *pi = ndp_msg_payload_opts_offset(msg, offset); return &pi->nd_opt_pi_prefix; } /** * ndp_msg_opt_prefix_len: * @msg: message structure * @offset: in-message offset * * Get prefix length. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: length of prefix. **/ NDP_EXPORT uint8_t ndp_msg_opt_prefix_len(struct ndp_msg *msg, int offset) { struct nd_opt_prefix_info *pi = ndp_msg_payload_opts_offset(msg, offset); return pi->nd_opt_pi_prefix_len; } /** * ndp_msg_opt_prefix_valid_time: * @msg: message structure * @offset: in-message offset * * Get prefix valid time. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: valid time in seconds, (uint32_t) -1 means infinity. **/ NDP_EXPORT uint32_t ndp_msg_opt_prefix_valid_time(struct ndp_msg *msg, int offset) { struct nd_opt_prefix_info *pi = ndp_msg_payload_opts_offset(msg, offset); return ntohl(pi->nd_opt_pi_valid_time); } /** * ndp_msg_opt_prefix_preferred_time: * @msg: message structure * @offset: in-message offset * * Get prefix preferred time. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: preferred time in seconds, (uint32_t) -1 means infinity. **/ NDP_EXPORT uint32_t ndp_msg_opt_prefix_preferred_time(struct ndp_msg *msg, int offset) { struct nd_opt_prefix_info *pi = ndp_msg_payload_opts_offset(msg, offset); return ntohl(pi->nd_opt_pi_preferred_time); } /** * ndp_msg_opt_prefix_flag_on_link: * @msg: message structure * @offset: in-message offset * * Get on-link flag. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: on-link flag. **/ NDP_EXPORT bool ndp_msg_opt_prefix_flag_on_link(struct ndp_msg *msg, int offset) { struct nd_opt_prefix_info *pi = ndp_msg_payload_opts_offset(msg, offset); return pi->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_ONLINK; } /** * ndp_msg_opt_prefix_flag_auto_addr_conf: * @msg: message structure * @offset: in-message offset * * Get autonomous address-configuration flag. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: autonomous address-configuration flag. **/ NDP_EXPORT bool ndp_msg_opt_prefix_flag_auto_addr_conf(struct ndp_msg *msg, int offset) { struct nd_opt_prefix_info *pi = ndp_msg_payload_opts_offset(msg, offset); return pi->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_AUTO; } /** * ndp_msg_opt_prefix_flag_router_addr: * @msg: message structure * @offset: in-message offset * * Get router address flag. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: router address flag. **/ NDP_EXPORT bool ndp_msg_opt_prefix_flag_router_addr(struct ndp_msg *msg, int offset) { struct nd_opt_prefix_info *pi = ndp_msg_payload_opts_offset(msg, offset); return pi->nd_opt_pi_flags_reserved & ND_OPT_PI_FLAG_RADDR; } /** * ndp_msg_opt_mtu: * @msg: message structure * @offset: in-message offset * * Get MTU. User should check if mtu option is present before calling this. * * Returns: MTU. **/ NDP_EXPORT uint32_t ndp_msg_opt_mtu(struct ndp_msg *msg, int offset) { struct nd_opt_mtu *mtu = ndp_msg_payload_opts_offset(msg, offset); return ntohl(mtu->nd_opt_mtu_mtu); } /** * ndp_msg_opt_route_prefix: * @msg: message structure * @offset: in-message offset * * Get route prefix addr. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: address. **/ NDP_EXPORT struct in6_addr *ndp_msg_opt_route_prefix(struct ndp_msg *msg, int offset) { static NDP_THREAD struct in6_addr prefix; struct __nd_opt_route_info *ri = ndp_msg_payload_opts_offset(msg, offset); memset(&prefix, 0, sizeof(prefix)); memcpy(&prefix, &ri->nd_opt_ri_prefix, (ri->nd_opt_ri_len - 1) << 3); return &prefix; } /** * ndp_msg_opt_route_prefix_len: * @msg: message structure * @offset: in-message offset * * Get route prefix length. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: length of route prefix. **/ NDP_EXPORT uint8_t ndp_msg_opt_route_prefix_len(struct ndp_msg *msg, int offset) { struct __nd_opt_route_info *ri = ndp_msg_payload_opts_offset(msg, offset); return ri->nd_opt_ri_prefix_len; } /** * ndp_msg_opt_route_lifetime: * @msg: message structure * @offset: in-message offset * * Get route lifetime. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: route lifetime in seconds, (uint32_t) -1 means infinity. **/ NDP_EXPORT uint32_t ndp_msg_opt_route_lifetime(struct ndp_msg *msg, int offset) { struct __nd_opt_route_info *ri = ndp_msg_payload_opts_offset(msg, offset); return ntohl(ri->nd_opt_ri_lifetime); } /** * ndp_msg_opt_route_preference: * @msg: message structure * @offset: in-message offset * * Get route preference. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: route preference. **/ NDP_EXPORT enum ndp_route_preference ndp_msg_opt_route_preference(struct ndp_msg *msg, int offset) { struct __nd_opt_route_info *ri = ndp_msg_payload_opts_offset(msg, offset); return (ri->nd_opt_ri_prf_reserved >> 3) & 3; } /** * ndp_msg_opt_rdnss_lifetime: * @msg: message structure * @offset: in-message offset * * Get Recursive DNS Server lifetime. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: route lifetime in seconds, (uint32_t) -1 means infinity. **/ NDP_EXPORT uint32_t ndp_msg_opt_rdnss_lifetime(struct ndp_msg *msg, int offset) { struct __nd_opt_rdnss *rdnss = ndp_msg_payload_opts_offset(msg, offset); return ntohl(rdnss->nd_opt_rdnss_lifetime); } /** * ndp_msg_opt_rdnss_addr: * @msg: message structure * @offset: in-message offset * @addr_index: address index * * Get Recursive DNS Server address. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: address. **/ NDP_EXPORT struct in6_addr *ndp_msg_opt_rdnss_addr(struct ndp_msg *msg, int offset, int addr_index) { static NDP_THREAD struct in6_addr addr; struct __nd_opt_rdnss *rdnss = ndp_msg_payload_opts_offset(msg, offset); size_t len = rdnss->nd_opt_rdnss_len << 3; /* convert to bytes */ len -= in_struct_offset(struct __nd_opt_rdnss, nd_opt_rdnss_addresses); if ((addr_index + 1) * sizeof(addr) > len) return NULL; memcpy(&addr, &rdnss->nd_opt_rdnss_addresses[addr_index * sizeof(addr)], sizeof(addr)); return &addr; } /** * ndp_msg_opt_dnssl_lifetime: * @msg: message structure * @offset: in-message offset * * Get DNS Search List lifetime. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: route lifetime in seconds, (uint32_t) -1 means infinity. **/ NDP_EXPORT uint32_t ndp_msg_opt_dnssl_lifetime(struct ndp_msg *msg, int offset) { struct __nd_opt_dnssl *dnssl = ndp_msg_payload_opts_offset(msg, offset); return ntohl(dnssl->nd_opt_dnssl_lifetime); } /** * ndp_msg_opt_dnssl_domain: * @msg: message structure * @offset: in-message offset * @domain_index: domain index * * Get DNS Search List domain. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: address. **/ NDP_EXPORT char *ndp_msg_opt_dnssl_domain(struct ndp_msg *msg, int offset, int domain_index) { int i; static NDP_THREAD char buf[256]; struct __nd_opt_dnssl *dnssl = ndp_msg_payload_opts_offset(msg, offset); size_t len = dnssl->nd_opt_dnssl_len << 3; /* convert to bytes */ char *ptr; len -= in_struct_offset(struct __nd_opt_dnssl, nd_opt_dnssl_domains); ptr = dnssl->nd_opt_dnssl_domains; i = 0; while (len > 0) { size_t buf_len = 0; while (len > 0) { uint8_t dom_len = *ptr; ptr++; len--; if (!dom_len) break; if (dom_len > len) return NULL; if (buf_len + dom_len + 1 > sizeof(buf)) return NULL; memcpy(buf + buf_len, ptr, dom_len); buf[buf_len + dom_len] = '.'; ptr += dom_len; len -= dom_len; buf_len += dom_len + 1; } if (!buf_len) break; buf[buf_len - 1] = '\0'; /* overwrite final '.' */ if (i++ == domain_index) return buf; } return NULL; } /** * ndp_msg_opt_pref64_lifetime: * @msg: message structure * @offset: in-message offset * * Get the NAT64 prefix lifetime. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: the NAT64 prefix lifetime in seconds. **/ NDP_EXPORT uint16_t ndp_msg_opt_pref64_lifetime(struct ndp_msg *msg, int offset) { struct __nd_opt_pref64 *pref64 = ndp_msg_payload_opts_offset(msg, offset); return ntohs(pref64->nd_opt_pref64_lft_plc) & ND_OPT_PREF64_LFT_MASK; } static uint8_t ndp_msg_opt_pref64_plen(struct __nd_opt_pref64 *pref64) { uint8_t plc; plc = ntohs(pref64->nd_opt_pref64_lft_plc) & ND_OPT_PREF64_PLC_MASK; /* The PLC field values 0, 1, 2, 3, 4, and 5 indicate the NAT64 * prefix length of 96, 64, 56, 48, 40, and 32 bits, respectively. */ if (plc == 0) return 96; return 72 - plc * 8; } /** * ndp_msg_opt_pref64_prefix_length: * @msg: message structure * @offset: in-message offset * * Get the NAT64 prefix length. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: the NAT64 prefix length (32, 40, 48, 56, 64 or 96). **/ NDP_EXPORT uint8_t ndp_msg_opt_pref64_prefix_length(struct ndp_msg *msg, int offset) { struct __nd_opt_pref64 *pref64 = ndp_msg_payload_opts_offset(msg, offset); return ndp_msg_opt_pref64_plen(pref64); } /** * ndp_msg_opt_pref64_prefix: * @msg: message structure * @offset: in-message offset * * Get the NAT64 prefix. * User should use this function only inside ndp_msg_opt_for_each_offset() * macro loop. * * Returns: the NAT64 prefix. **/ NDP_EXPORT struct in6_addr *ndp_msg_opt_pref64_prefix(struct ndp_msg *msg, int offset) { static NDP_THREAD struct in6_addr addr; struct __nd_opt_pref64 *pref64 = ndp_msg_payload_opts_offset(msg, offset); uint8_t plen; plen = ndp_msg_opt_pref64_plen(pref64); memset(&addr, 0, sizeof(addr)); memcpy(&addr, pref64->nd_opt_pref64_prefix, plen / 8); return &addr; } static int ndp_call_handlers(struct ndp *ndp, struct ndp_msg *msg); static int ndp_sock_recv(struct ndp *ndp) { struct ndp_msg *msg; enum ndp_msg_type msg_type; size_t len; int err; char buf[INET6_ADDRSTRLEN]; msg = ndp_msg_alloc(); if (!msg) return -ENOMEM; len = ndp_msg_payload_maxlen(msg); err = myrecvfrom6(ndp->sock, msg->buf, &len, 0, &msg->addrto, &msg->ifindex, &msg->hoplimit); if (err) { err(ndp, "Failed to receive message"); goto free_msg; } dbg(ndp, "rcvd from: %s, ifindex: %u, hoplimit: %d", str_in6_addr(&msg->addrto, buf), msg->ifindex, msg->hoplimit); if (msg->hoplimit != 255) { warn(ndp, "ignoring packet with bad hop limit (%d)", msg->hoplimit); err = 0; goto free_msg; } if (len < sizeof(*msg->icmp6_hdr)) { warn(ndp, "rcvd icmp6 packet too short (%luB)", len); err = 0; goto free_msg; } err = ndp_msg_type_by_raw_type(&msg_type, msg->icmp6_hdr->icmp6_type); if (err) { err = 0; goto free_msg; } ndp_msg_init(msg, msg_type); ndp_msg_payload_len_set(msg, len); if (!ndp_msg_check_valid(msg)) { warn(ndp, "rcvd invalid ND message"); err = 0; goto free_msg; } dbg(ndp, "rcvd %s, len: %zuB", ndp_msg_type_info(msg_type)->strabbr, len); if (!ndp_msg_check_opts(msg)) { err = 0; goto free_msg; } err = ndp_call_handlers(ndp, msg);; free_msg: ndp_msg_destroy(msg); return err; } /** * SECTION: socket open/close functions * @short_description: functions for opening and closing the ICMPv6 raw socket */ static int ndp_sock_open(struct ndp *ndp) { int sock; struct icmp6_filter flt; int ret; int err; int val; int i; sock = socket(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6); if (sock == -1) { err(ndp, "Failed to create ICMP6 socket."); return -errno; } val = 1; ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)); if (ret == -1) { err(ndp, "Failed to setsockopt IPV6_RECVPKTINFO."); err = -errno; goto close_sock; } val = 255; ret = setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)); if (ret == -1) { err(ndp, "Failed to setsockopt IPV6_MULTICAST_HOPS."); err = -errno; goto close_sock; } val = 1; ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, sizeof(val)); if (ret == -1) { err(ndp, "Failed to setsockopt IPV6_RECVHOPLIMIT,."); err = -errno; goto close_sock; } ICMP6_FILTER_SETBLOCKALL(&flt); for (i = 0; i < NDP_MSG_TYPE_LIST_SIZE; i++) ICMP6_FILTER_SETPASS(ndp_msg_type_info(i)->raw_type, &flt); ret = setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &flt, sizeof(flt)); if (ret == -1) { err(ndp, "Failed to setsockopt ICMP6_FILTER."); err = -errno; goto close_sock; } ndp->sock = sock; return 0; close_sock: close(sock); return err; } static void ndp_sock_close(struct ndp *ndp) { close(ndp->sock); } /** * SECTION: msgrcv handler * @short_description: msgrcv handler and related stuff */ struct ndp_msgrcv_handler_item { struct list_item list; ndp_msgrcv_handler_func_t func; enum ndp_msg_type msg_type; uint32_t ifindex; void * priv; }; static struct ndp_msgrcv_handler_item * ndp_find_msgrcv_handler_item(struct ndp *ndp, ndp_msgrcv_handler_func_t func, enum ndp_msg_type msg_type, uint32_t ifindex, void *priv) { struct ndp_msgrcv_handler_item *handler_item; list_for_each_node_entry(handler_item, &ndp->msgrcv_handler_list, list) if (handler_item->func == func && handler_item->msg_type == msg_type && handler_item->ifindex == ifindex && handler_item->priv == priv) return handler_item; return NULL; } static int ndp_call_handlers(struct ndp *ndp, struct ndp_msg *msg) { struct ndp_msgrcv_handler_item *handler_item; int err; list_for_each_node_entry(handler_item, &ndp->msgrcv_handler_list, list) { if (handler_item->msg_type != NDP_MSG_ALL && handler_item->msg_type != ndp_msg_type(msg)) continue; if (handler_item->ifindex && handler_item->ifindex != msg->ifindex) continue; err = handler_item->func(ndp, msg, handler_item->priv); if (err) return err; } return 0; } /** * ndp_msgrcv_handler_register: * @ndp: libndp library context * @func: handler function for received messages * @msg_type: message type to match * @ifindex: interface index to match * @priv: func private data * * Registers custom @func handler which is going to be called when * specified @msg_type is received. If one wants the function to be * called for all message types, pass NDP_MSG_ALL, * Note that @ifindex can be set to filter only messages received on * specified interface. For @func to be called for messages received on * all interfaces, just set 0. * * Returns: zero on success or negative number in case of an error. **/ NDP_EXPORT int ndp_msgrcv_handler_register(struct ndp *ndp, ndp_msgrcv_handler_func_t func, enum ndp_msg_type msg_type, uint32_t ifindex, void *priv) { struct ndp_msgrcv_handler_item *handler_item; if (ndp_find_msgrcv_handler_item(ndp, func, msg_type, ifindex, priv)) return -EEXIST; if (!func) return -EINVAL; handler_item = malloc(sizeof(*handler_item)); if (!handler_item) return -ENOMEM; handler_item->func = func; handler_item->msg_type = msg_type; handler_item->ifindex = ifindex; handler_item->priv = priv; list_add_tail(&ndp->msgrcv_handler_list, &handler_item->list); return 0; } /** * ndp_msgrcv_handler_unregister: * @ndp: libndp library context * @func: handler function for received messages * @msg_type: message type to match * @ifindex: interface index to match * @priv: func private data * * Unregisters custom @func handler. * **/ NDP_EXPORT void ndp_msgrcv_handler_unregister(struct ndp *ndp, ndp_msgrcv_handler_func_t func, enum ndp_msg_type msg_type, uint32_t ifindex, void *priv) { struct ndp_msgrcv_handler_item *handler_item; handler_item = ndp_find_msgrcv_handler_item(ndp, func, msg_type, ifindex, priv); if (!handler_item) return; list_del(&handler_item->list); free(handler_item); } /** * SECTION: event fd * @short_description: event filedescriptor related stuff */ /** * ndp_get_eventfd: * @ndp: libndp library context * * Get eventfd filedesctiptor. * * Returns: fd. **/ NDP_EXPORT int ndp_get_eventfd(struct ndp *ndp) { return ndp->sock; } /** * ndp_call_eventfd_handler: * @ndp: libndp library context * * Call eventfd handler. * * Returns: zero on success or negative number in case of an error. **/ NDP_EXPORT int ndp_call_eventfd_handler(struct ndp *ndp) { return ndp_sock_recv(ndp); } /** * ndp_callall_eventfd_handler: * @ndp: libndp library context * * Call all pending events on eventfd handler. * * Returns: zero on success or negative number in case of an error. **/ NDP_EXPORT int ndp_callall_eventfd_handler(struct ndp *ndp) { struct pollfd pfd; int ret; int err; pfd = (struct pollfd) { .fd = ndp_get_eventfd(ndp), .events = POLLIN, }; while (true) { ret = poll(&pfd, 1, 0); if (ret == -1) return -errno; if (!(pfd.revents & POLLIN)) return 0; err = ndp_call_eventfd_handler(ndp); if (err) return err; } } /** * SECTION: Exported context functions * @short_description: Core context functions exported to user */ /** * ndp_open: * @p_ndp: pointer where new libndp library context address will be stored * * Allocates and initializes library context, opens raw socket. * * Returns: zero on success or negative number in case of an error. **/ NDP_EXPORT int ndp_open(struct ndp **p_ndp) { struct ndp *ndp; const char *env; int err; ndp = myzalloc(sizeof(*ndp)); if (!ndp) return -ENOMEM; ndp->log_fn = log_stderr; ndp->log_priority = LOG_ERR; /* environment overwrites config */ env = getenv("NDP_LOG"); if (env != NULL) ndp_set_log_priority(ndp, log_priority(env)); dbg(ndp, "ndp context %p created.", ndp); dbg(ndp, "log_priority=%d", ndp->log_priority); list_init(&ndp->msgrcv_handler_list); err = ndp_sock_open(ndp); if (err) goto free_ndp; *p_ndp = ndp; return 0; free_ndp: free(ndp); return err; } /** * ndp_close: * @ndp: libndp library context * * Do library context cleanup. **/ NDP_EXPORT void ndp_close(struct ndp *ndp) { ndp_sock_close(ndp); free(ndp); } libndp-1.9/libndp/libndp.pc.in000066400000000000000000000003251463751251200163120ustar00rootroot00000000000000prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: libndp Description: Neighbour discovery library. Version: @PACKAGE_VERSION@ Libs: -L${libdir} -lndp Cflags: -I${includedir} libndp-1.9/libndp/list.h000066400000000000000000000101331463751251200152330ustar00rootroot00000000000000/* * list.c - Doubly linked list implementation * Copyright (C) 2013-2015 Jiri Pirko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _LIST_H_ #define _LIST_H_ #include struct list_item { struct list_item *prev; struct list_item *next; }; static inline void list_init(struct list_item *head) { head->prev = head; head->next = head; } static inline bool list_empty(struct list_item *head) { return head->next == head; } static inline void __list_add(struct list_item *new_node, struct list_item *prev_node, struct list_item *next_node) { new_node->prev = prev_node; new_node->next = next_node; prev_node->next = new_node; next_node->prev = new_node; } static inline void list_add(struct list_item *head, struct list_item *node) { __list_add(node, head, head->next); } static inline void list_add_tail(struct list_item *head, struct list_item *node) { __list_add(node, head->prev, head); } static inline void list_del(struct list_item *node) { node->prev->next = node->next; node->next->prev = node->prev; } static inline void list_move_nodes(struct list_item *dst_head, struct list_item *src_head) { if (list_empty(src_head)) return; dst_head->prev->next = src_head->next; src_head->next->prev = dst_head->prev; dst_head->prev = src_head->prev; src_head->prev->next = dst_head; list_init(src_head); } static inline struct list_item *list_get_next_node(struct list_item *head, struct list_item *node) { if (node->next == head) return NULL; return node->next; } #define list_for_each_node(node, head) \ for (node = list_get_next_node(head, head); \ node; \ node = list_get_next_node(head, node)) #define in_struct_offset(struct_type, struct_member) \ ((size_t) (&((struct_type *) 0)->struct_member)) #define get_container(ptr, struct_type, struct_member) \ ((struct_type *) ( \ ((size_t) ptr) - \ in_struct_offset(struct_type, struct_member))) #define list_get_node_entry(node, struct_type, struct_member) \ get_container(node, struct_type, struct_member) #define list_for_each_node_entry(entry, head, struct_member) \ for (entry = list_get_node_entry((head)->next, typeof(*entry), \ struct_member); \ &entry->struct_member != (head); \ entry = list_get_node_entry(entry->struct_member.next, \ typeof(*entry), struct_member)) #define list_for_each_node_entry_continue_reverse(entry, head, struct_member) \ for (entry = list_get_node_entry(entry->struct_member.prev, \ typeof(*entry), struct_member); \ &entry->struct_member != (head); \ entry = list_get_node_entry(entry->struct_member.prev, \ typeof(*entry), struct_member)) #define list_for_each_node_entry_safe(entry, tmp, head, struct_member) \ for (entry = list_get_node_entry((head)->next, \ typeof(*entry), struct_member), \ tmp = list_get_node_entry(entry->struct_member.next, \ typeof(*entry), struct_member); \ &entry->struct_member != (head); \ entry = tmp, \ tmp = list_get_node_entry(entry->struct_member.next, \ typeof(*entry), struct_member)) #define list_get_next_node_entry(head, entry, struct_member) ({ \ struct list_item *next = (entry ? &entry->struct_member : (head))->next;\ (next == (head)) ? NULL : list_get_node_entry(next, typeof(*entry), \ struct_member);}) #endif /* _LIST_H_ */ libndp-1.9/libndp/ndp_private.h000066400000000000000000000073461463751251200166070ustar00rootroot00000000000000/* * ndp_private.h - Neighbour discovery library private header * Copyright (C) 2013-2015 Jiri Pirko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef _NDP_PRIVATE_H_ #define _NDP_PRIVATE_H_ #include #include #include #include "list.h" #define NDP_EXPORT __attribute__ ((visibility("default"))) #define NDP_THREAD __thread /** * SECTION: ndp * @short_description: libndp context */ struct ndp { int sock; void (*log_fn)(struct ndp *ndp, int priority, const char *file, int line, const char *fn, const char *format, va_list args); int log_priority; struct list_item msgrcv_handler_list; }; /** * SECTION: logging * @short_description: libndp logging facility */ void ndp_log(struct ndp *ndp, int priority, const char *file, int line, const char *fn, const char *format, ...); static inline void __attribute__((always_inline, format(printf, 2, 3))) ndp_log_null(struct ndp *ndp, const char *format, ...) {} #define ndp_log_cond(ndp, prio, arg...) \ do { \ if (ndp_get_log_priority(ndp) >= prio) \ ndp_log(ndp, prio, __FILE__, __LINE__, \ __FUNCTION__, ## arg); \ } while (0) #ifdef ENABLE_LOGGING # ifdef ENABLE_DEBUG # define dbg(ndp, arg...) ndp_log_cond(ndp, LOG_DEBUG, ## arg) # else # define dbg(ndp, arg...) ndp_log_null(ndp, ## arg) # endif # define info(ndp, arg...) ndp_log_cond(ndp, LOG_INFO, ## arg) # define warn(ndp, arg...) ndp_log_cond(ndp, LOG_WARNING, ## arg) # define err(ndp, arg...) ndp_log_cond(ndp, LOG_ERR, ## arg) #else # define dbg(ndp, arg...) ndp_log_null(ndp, ## arg) # define info(ndp, arg...) ndp_log_null(ndp, ## arg) # define warn(ndp, arg...) ndp_log_null(ndp, ## arg) # define err(ndp, arg...) ndp_log_null(ndp, ## arg) #endif /** * SECTION: netinet/icmp6.h addendum * @short_description: defines and structs missing from netinet/icmp6.h */ #define __ND_OPT_ROUTE_INFO 24 /* rfc4191 */ #define __ND_OPT_RDNSS 25 /* rfc6106 */ #define __ND_OPT_DNSSL 31 /* rfc6106 */ #define __ND_OPT_PREF64 38 /* rfc8781 */ struct __nd_opt_route_info { /* route information */ uint8_t nd_opt_ri_type; uint8_t nd_opt_ri_len; uint8_t nd_opt_ri_prefix_len; uint8_t nd_opt_ri_prf_reserved; uint32_t nd_opt_ri_lifetime; char nd_opt_ri_prefix[0]; }; struct __nd_opt_rdnss { /* Recursive DNS Server */ uint8_t nd_opt_rdnss_type; uint8_t nd_opt_rdnss_len; uint16_t nd_opt_rdnss_reserved; uint32_t nd_opt_rdnss_lifetime; char nd_opt_rdnss_addresses[0]; }; struct __nd_opt_dnssl { /* DNS Search List */ uint8_t nd_opt_dnssl_type; uint8_t nd_opt_dnssl_len; uint16_t nd_opt_dnssl_reserved; uint32_t nd_opt_dnssl_lifetime; char nd_opt_dnssl_domains[0]; }; #define ND_OPT_PREF64_PLC_MASK 0x7 #define ND_OPT_PREF64_LFT_SHIFT 3 #define ND_OPT_PREF64_LFT_MASK (0x1FFF << ND_OPT_PREF64_LFT_SHIFT) struct __nd_opt_pref64 { /* NAT64 prefix */ uint8_t nd_opt_pref64_type; uint8_t nd_opt_pref64_len; uint16_t nd_opt_pref64_lft_plc; uint8_t nd_opt_pref64_prefix[12]; }; #endif /* _NDP_PRIVATE_H_ */ libndp-1.9/m4/000077500000000000000000000000001463751251200131615ustar00rootroot00000000000000libndp-1.9/m4/.gitignore000066400000000000000000000000021463751251200151410ustar00rootroot00000000000000* libndp-1.9/man/000077500000000000000000000000001463751251200134145ustar00rootroot00000000000000libndp-1.9/man/Makefile.am000066400000000000000000000000331463751251200154440ustar00rootroot00000000000000dist_man8_MANS = ndptool.8 libndp-1.9/man/ndptool.8000066400000000000000000000021671463751251200151720ustar00rootroot00000000000000.TH ndptool 8 "16 April 2013" "libndp" .SH NAME ndptool \(em Neighbor Discovery Protocol tool .SH SYNOPSIS .B ndptool .B \-h .TP .B ndptool [OPTIONS] COMMAND .TP .SH DESCRIPTION .PP ndptool is a tool which provides wrapper over Neighbor Discovery Protocol messages. .SH OPTIONS .TP .B "\-h, \-\-help" Print help text to console and exit. .TP .B "\-v, \-\-verbose" Increase output verbosity. .TP .B "\-t type, \-\-msg-type type" Specified message type. Following are supported: .BR "rs "- Router Solicitation. .BR "ra "- Router Advertisement. .BR "ns "- Neighbor Solicitation. .BR "na "- Neighbor Advertisement. .TP .B "\-i ifname, \-\-ifname ifname" Specified interface name. .TP .B "\-D dest, \-\-dest dest" Specified dest address in IPv6 header for NS/NA message. .TP .B "\-T target, \-\-target target" Specified target address in ICMPv6 header for NS/NA message. .TP .B "\-U, \-\-unsolicited" Send Unsolicited NA. .SH COMMAND .TP .B "monitor" Monitor incoming NDP messages and print them out. .TP .B "send" Send NDP message of specified type. .SH AUTHOR .PP Jiri Pirko is the original author and current maintainer of libndp. libndp-1.9/utils/000077500000000000000000000000001463751251200140015ustar00rootroot00000000000000libndp-1.9/utils/Makefile.am000066400000000000000000000003211463751251200160310ustar00rootroot00000000000000MAINTAINERCLEANFILES = Makefile.in ACLOCAL_AMFLAGS = -I m4 AM_CFLAGS = -I${top_srcdir}/include -D_GNU_SOURCE ndptool_LDADD = $(top_builddir)/libndp/libndp.la bin_PROGRAMS=ndptool ndptool_SOURCES=ndptool.c libndp-1.9/utils/ndptool.c000066400000000000000000000332671463751251200156370ustar00rootroot00000000000000/* * ndptool.c - Neighbour discovery tool * Copyright (C) 2013-2015 Jiri Pirko * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include #include #include enum verbosity_level { VERB1, VERB2, VERB3, VERB4, }; #define DEFAULT_VERB VERB1 static int g_verbosity = DEFAULT_VERB; static uint8_t flags = ND_OPT_NORMAL; #define pr_err(args...) fprintf(stderr, ##args) #define pr_outx(verb_level, args...) \ do { \ if (verb_level <= g_verbosity) \ fprintf(stdout, ##args); \ } while (0) #define pr_out(args...) pr_outx(DEFAULT_VERB, ##args) #define pr_out2(args...) pr_outx(VERB2, ##args) #define pr_out3(args...) pr_outx(VERB3, ##args) #define pr_out4(args...) pr_outx(VERB4, ##args) static void empty_signal_handler(int signal) { } static int run_main_loop(struct ndp *ndp) { struct pollfd pfd; int ret; struct sigaction siginfo; sigset_t mask; int err = 0; sigemptyset(&siginfo.sa_mask); siginfo.sa_flags = 0; siginfo.sa_handler = empty_signal_handler; ret = sigaction(SIGINT, &siginfo, NULL); if (ret == -1) { pr_err("Failed to set SIGINT handler\n"); return -errno; } ret = sigaction(SIGQUIT, &siginfo, NULL); if (ret == -1) { pr_err("Failed to set SIGQUIT handler\n"); return -errno; } ret = sigaction(SIGTERM, &siginfo, NULL); if (ret == -1) { pr_err("Failed to set SIGTERM handler\n"); return -errno; } sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGQUIT); sigaddset(&mask, SIGTERM); ret = sigprocmask(SIG_BLOCK, &mask, NULL); if (ret == -1) { pr_err("Failed to set blocked signals\n"); return -errno; } sigemptyset(&mask); pfd = (struct pollfd) { .fd = ndp_get_eventfd(ndp), .events = POLLIN, }; for (;;) { ret = ppoll(&pfd, 1, NULL, &mask); if (ret == -1) { if (errno == EINTR) { goto out; } pr_err("Poll failed\n"); err = -errno; goto out; } if (pfd.revents & POLLIN) { err = ndp_call_eventfd_handler(ndp); if (err) { pr_err("ndp eventfd handler call failed\n"); return err; } } } out: return err; } static void print_help(const char *argv0) { pr_out( "%s [options] command\n" "\t-h --help Show this help\n" "\t-v --verbose Increase output verbosity\n" "\t-t --msg-type=TYPE Specify message type\n" "\t (\"rs\", \"ra\", \"ns\", \"na\")\n" "\t-D --dest=DEST Dest address in IPv6 header for NS or NA\n" "\t-T --target=TARGET Target address in ICMPv6 header for NS or NA\n" "\t-i --ifname=IFNAME Specify interface name\n" "\t-U --unsolicited Send Unsolicited NA\n" "Available commands:\n" "\tmonitor\n" "\tsend\n", argv0); } static const char *str_in6_addr(struct in6_addr *addr, char buf[static INET6_ADDRSTRLEN]) { return inet_ntop(AF_INET6, addr, buf, INET6_ADDRSTRLEN); } static void pr_out_hwaddr(unsigned char *hwaddr, size_t len) { int i; for (i = 0; i < len; i++) { if (i) pr_out(":"); pr_out("%02x", hwaddr[i]); } pr_out("\n"); } static void pr_out_route_preference(enum ndp_route_preference pref) { switch (pref) { case NDP_ROUTE_PREF_LOW: pr_out("low"); break; case NDP_ROUTE_PREF_MEDIUM: pr_out("medium"); break; case NDP_ROUTE_PREF_HIGH: pr_out("high"); break; } } static void pr_out_lft(uint32_t lifetime) { if (lifetime == (uint32_t) -1) pr_out("infinity"); else pr_out("%us", lifetime); } static int msgrcv_handler_func(struct ndp *ndp, struct ndp_msg *msg, void *priv) { char buf[INET6_ADDRSTRLEN]; char ifname[IF_NAMESIZE]; enum ndp_msg_type msg_type = ndp_msg_type(msg); int offset; if_indextoname(ndp_msg_ifindex(msg), ifname); pr_out("NDP payload len %zu, from addr: %s, iface: %s\n", ndp_msg_payload_len(msg), str_in6_addr(ndp_msg_addrto(msg), buf), ifname); if (msg_type == NDP_MSG_RS) { pr_out(" Type: RS\n"); } else if (msg_type == NDP_MSG_RA) { struct ndp_msgra *msgra = ndp_msgra(msg); pr_out(" Type: RA\n"); pr_out(" Hop limit: %u\n", ndp_msgra_curhoplimit(msgra)); pr_out(" Managed address configuration: %s\n", ndp_msgra_flag_managed(msgra) ? "yes" : "no"); pr_out(" Other configuration: %s\n", ndp_msgra_flag_other(msgra) ? "yes" : "no"); pr_out(" Default router preference: "); pr_out_route_preference(ndp_msgra_route_preference(msgra)); pr_out("\n"); pr_out(" Router lifetime: %us\n", ndp_msgra_router_lifetime(msgra)); pr_out(" Reachable time: "); if (ndp_msgra_reachable_time(msgra)) { pr_out("%ums\n", ndp_msgra_reachable_time(msgra)); } else { pr_out("unspecified\n"); } pr_out(" Retransmit time: "); if (ndp_msgra_retransmit_time(msgra)) { pr_out("%ums\n", ndp_msgra_retransmit_time(msgra)); } else { pr_out("unspecified\n"); } ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_SLLADDR) { pr_out(" Source linkaddr: "); pr_out_hwaddr(ndp_msg_opt_slladdr(msg, offset), ndp_msg_opt_slladdr_len(msg, offset)); } ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_TLLADDR) { pr_out(" Target linkaddr: "); pr_out_hwaddr(ndp_msg_opt_tlladdr(msg, offset), ndp_msg_opt_tlladdr_len(msg, offset)); } ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_PREFIX) { uint32_t valid_time; uint32_t preferred_time; valid_time = ndp_msg_opt_prefix_valid_time(msg, offset); preferred_time = ndp_msg_opt_prefix_preferred_time(msg, offset); pr_out(" Prefix: %s/%u", str_in6_addr(ndp_msg_opt_prefix(msg, offset), buf), ndp_msg_opt_prefix_len(msg, offset)); pr_out(", valid_time: "); if (valid_time == (uint32_t) -1) pr_out("infinity"); else pr_out("%us", valid_time); pr_out(", preferred_time: "); if (preferred_time == (uint32_t) -1) pr_out("infinity"); else pr_out("%us", preferred_time); pr_out(", on_link: %s", ndp_msg_opt_prefix_flag_on_link(msg, offset) ? "yes" : "no"); pr_out(", autonomous_addr_conf: %s", ndp_msg_opt_prefix_flag_auto_addr_conf(msg, offset) ? "yes" : "no"); pr_out(", router_addr: %s", ndp_msg_opt_prefix_flag_router_addr(msg, offset) ? "yes" : "no"); pr_out("\n"); } ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_MTU) pr_out(" MTU: %u\n", ndp_msg_opt_mtu(msg, offset)); ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_ROUTE) { pr_out(" Route: %s/%u", str_in6_addr(ndp_msg_opt_route_prefix(msg, offset), buf), ndp_msg_opt_route_prefix_len(msg, offset)); pr_out(", lifetime: "); pr_out_lft(ndp_msg_opt_route_lifetime(msg, offset)); pr_out(", preference: "); pr_out_route_preference(ndp_msg_opt_route_preference(msg, offset)); pr_out("\n"); } ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_RDNSS) { struct in6_addr *addr; int addr_index; pr_out(" Recursive DNS Servers: "); ndp_msg_opt_rdnss_for_each_addr(addr, addr_index, msg, offset) { if (addr_index != 0) pr_out(", "); pr_out("%s", str_in6_addr(addr, buf)); } pr_out(", lifetime: "); pr_out_lft(ndp_msg_opt_rdnss_lifetime(msg, offset)); pr_out("\n"); } ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_DNSSL) { char *domain; int domain_index; pr_out(" DNS Search List: "); ndp_msg_opt_dnssl_for_each_domain(domain, domain_index, msg, offset) { if (domain_index != 0) pr_out(" "); pr_out("%s", domain); } pr_out(", lifetime: "); pr_out_lft(ndp_msg_opt_dnssl_lifetime(msg, offset)); pr_out("\n"); } ndp_msg_opt_for_each_offset(offset, msg, NDP_MSG_OPT_PREF64) { struct in6_addr *prefix; uint16_t lft; uint8_t plen; pr_out(" NAT64 Prefix: "); prefix = ndp_msg_opt_pref64_prefix(msg, offset); plen = ndp_msg_opt_pref64_prefix_length(msg, offset); lft = ndp_msg_opt_pref64_lifetime(msg, offset); pr_out("%s/%u", str_in6_addr(prefix, buf), plen); pr_out(", lifetime: %us", lft); pr_out("\n"); } } else if (msg_type == NDP_MSG_NS) { pr_out(" Type: NS\n"); } else if (msg_type == NDP_MSG_NA) { pr_out(" Type: NA\n"); } else if (msg_type == NDP_MSG_R) { pr_out(" Type: R\n"); } else { return 0; } return 0; } static int run_cmd_monitor(struct ndp *ndp, enum ndp_msg_type msg_type, uint32_t ifindex) { int err; err = ndp_msgrcv_handler_register(ndp, &msgrcv_handler_func, msg_type, ifindex, NULL); if (err) { pr_err("Failed to register msgrcv handler\n"); return err; } err = run_main_loop(ndp); ndp_msgrcv_handler_unregister(ndp, &msgrcv_handler_func, msg_type, ifindex, NULL); return err; } static int run_cmd_send(struct ndp *ndp, enum ndp_msg_type msg_type, uint32_t ifindex, struct in6_addr *dest, struct in6_addr *target) { struct ndp_msg *msg; int err; err = ndp_msg_new(&msg, msg_type); if (err) { pr_err("Failed to create message\n"); return err; } ndp_msg_ifindex_set(msg, ifindex); ndp_msg_dest_set(msg, dest); ndp_msg_target_set(msg, target); ndp_msg_opt_set(msg); err = ndp_msg_send_with_flags(ndp, msg, flags); if (err) { pr_err("Failed to send message\n"); goto msg_destroy; } msg_destroy: ndp_msg_destroy(msg); return err; } static int get_msg_type(enum ndp_msg_type *p_msg_type, char *msgtypestr) { if (!msgtypestr) *p_msg_type = NDP_MSG_ALL; else if (!strcmp(msgtypestr, "rs")) *p_msg_type = NDP_MSG_RS; else if (!strcmp(msgtypestr, "ra")) *p_msg_type = NDP_MSG_RA; else if (!strcmp(msgtypestr, "ns")) *p_msg_type = NDP_MSG_NS; else if (!strcmp(msgtypestr, "na")) *p_msg_type = NDP_MSG_NA; else if (!strcmp(msgtypestr, "r")) *p_msg_type = NDP_MSG_R; else return -EINVAL; return 0; } int main(int argc, char **argv) { char *argv0 = argv[0]; static const struct option long_options[] = { { "help", no_argument, NULL, 'h' }, { "verbose", no_argument, NULL, 'v' }, { "msg-type", required_argument, NULL, 't' }, { "ifname", required_argument, NULL, 'i' }, { "dest", required_argument, NULL, 'D' }, { "target", required_argument, NULL, 'T' }, { "unsolicited",no_argument, NULL, 'U' }, { NULL, 0, NULL, 0 } }; struct in6_addr target = IN6ADDR_ANY_INIT; struct in6_addr dest = IN6ADDR_ANY_INIT; enum ndp_msg_type msg_type; char *msgtypestr = NULL; int res = EXIT_FAILURE; char *ifname = NULL; char *daddr = NULL; char *taddr = NULL; uint32_t ifindex; struct ndp *ndp; char *cmd_name; int opt; int err; while ((opt = getopt_long(argc, argv, "hvt:D:T:i:U", long_options, NULL)) >= 0) { switch(opt) { case 'h': print_help(argv0); res = EXIT_SUCCESS; goto errout; case 'v': g_verbosity++; break; case 't': free(msgtypestr); msgtypestr = strdup(optarg); break; case 'i': free(ifname); ifname = strdup(optarg); break; case 'D': free(daddr); daddr = strdup(optarg); break; case 'T': free(taddr); taddr = strdup(optarg); break; case 'U': flags |= ND_OPT_NA_UNSOL; break; case '?': pr_err("unknown option.\n"); print_help(argv0); goto errout; default: pr_err("unknown option \"%c\".\n", opt); print_help(argv0); goto errout; } } if (optind >= argc) { pr_err("No command specified.\n"); print_help(argv0); goto errout; } argv += optind; cmd_name = *argv++; argc -= optind + 1; ifindex = 0; if (ifname) { ifindex = if_nametoindex(ifname); if (!ifindex) { pr_err("Interface \"%s\" does not exist\n", ifname); goto errout; } } if (daddr && (flags & ND_OPT_NA_UNSOL)) { pr_err("Conflicts for both setting dest address and unsolicited flag\n"); goto errout; } if (daddr && inet_pton(AF_INET6, daddr, &dest) <= 0) { pr_err("Invalid dest address \"%s\"\n", daddr); goto errout; } if (taddr && inet_pton(AF_INET6, taddr, &target) <= 0) { pr_err("Invalid target address \"%s\"\n", taddr); goto errout; } err = get_msg_type(&msg_type, msgtypestr); if (err) { pr_err("Invalid message type \"%s\" selected\n", msgtypestr); print_help(argv0); goto errout; } err = ndp_open(&ndp); if (err) { pr_err("Failed to open ndp: %s\n", strerror(-err)); goto errout; } if (!strncmp(cmd_name, "monitor", strlen(cmd_name))) { err = run_cmd_monitor(ndp, msg_type, ifindex); } else if (!strncmp(cmd_name, "send", strlen(cmd_name))) { bool all_ok = true; if (msg_type == NDP_MSG_ALL) { pr_err("Message type must be selected\n"); all_ok = false; } if (!ifindex) { pr_err("Interface name must be selected\n"); all_ok = false; } if (!all_ok) { print_help(argv0); goto errout; } err = run_cmd_send(ndp, msg_type, ifindex, &dest, &target); } else { pr_err("Unknown command \"%s\"\n", cmd_name); goto ndp_close; } if (err) { pr_err("Command failed \"%s\"\n", strerror(-err)); goto ndp_close; } res = EXIT_SUCCESS; ndp_close: ndp_close(ndp); errout: free(msgtypestr); free(ifname); free(daddr); free(taddr); return res; }