From 44d362409d5469aed47d19e7908d19bd194493a4 Mon Sep 17 00:00:00 2001 From: Thomas Graf Date: Sat, 15 Sep 2007 01:28:01 +0200 Subject: Initial import --- COPYING | 461 +++ ChangeLog | 333 +++ Makefile | 64 + Makefile.opts.in | 39 + Makefile.rules | 37 + aclocal.m4 | 831 ++++++ configure | 3724 +++++++++++++++++++++++++ configure.in | 80 + doc/Doxyfile | 1259 +++++++++ doc/Makefile | 35 + doc/libnl.css | 310 ++ include/Makefile | 38 + include/linux/gen_stats.h | 60 + include/linux/genetlink.h | 69 + include/linux/if.h | 102 + include/linux/if_addr.h | 62 + include/linux/if_arp.h | 149 + include/linux/if_ether.h | 106 + include/linux/if_link.h | 143 + include/linux/ip_mp_alg.h | 22 + include/linux/neighbour.h | 159 ++ include/linux/netfilter/nfnetlink.h | 60 + include/linux/netfilter/nfnetlink_conntrack.h | 140 + include/linux/netfilter/nfnetlink_log.h | 96 + include/linux/netlink.h | 150 + include/linux/pkt_cls.h | 426 +++ include/linux/pkt_sched.h | 478 ++++ include/linux/rtnetlink.h | 559 ++++ include/netlink-generic.h | 20 + include/netlink-local.h | 436 +++ include/netlink-tc.h | 70 + include/netlink-types.h | 865 ++++++ include/netlink/addr.h | 67 + include/netlink/attr.h | 287 ++ include/netlink/cache.h | 123 + include/netlink/data.h | 40 + include/netlink/fib_lookup/lookup.h | 41 + include/netlink/fib_lookup/request.h | 51 + include/netlink/genl/ctrl.h | 36 + include/netlink/genl/family.h | 50 + include/netlink/genl/genl.h | 37 + include/netlink/genl/mngt.h | 78 + include/netlink/handlers.h | 147 + include/netlink/list.h | 85 + include/netlink/msg.h | 143 + include/netlink/netfilter/ct.h | 115 + include/netlink/netfilter/log.h | 105 + include/netlink/netfilter/nfnl.h | 44 + include/netlink/netlink-compat.h | 50 + include/netlink/netlink-kernel.h | 196 ++ include/netlink/netlink.h | 74 + include/netlink/object.h | 64 + include/netlink/route/addr.h | 91 + include/netlink/route/class-modules.h | 73 + include/netlink/route/class.h | 68 + include/netlink/route/classifier-modules.h | 72 + include/netlink/route/classifier.h | 56 + include/netlink/route/cls/fw.h | 29 + include/netlink/route/cls/police.h | 29 + include/netlink/route/cls/u32.h | 43 + include/netlink/route/link.h | 151 + include/netlink/route/neighbour.h | 91 + include/netlink/route/neightbl.h | 64 + include/netlink/route/nexthop.h | 41 + include/netlink/route/qdisc-modules.h | 73 + include/netlink/route/qdisc.h | 87 + include/netlink/route/route.h | 125 + include/netlink/route/rtnl.h | 71 + include/netlink/route/rule.h | 79 + include/netlink/route/sch/cbq.h | 30 + include/netlink/route/sch/dsmark.h | 41 + include/netlink/route/sch/fifo.h | 28 + include/netlink/route/sch/htb.h | 41 + include/netlink/route/sch/netem.h | 62 + include/netlink/route/sch/prio.h | 53 + include/netlink/route/sch/red.h | 17 + include/netlink/route/sch/sfq.h | 36 + include/netlink/route/sch/tbf.h | 43 + include/netlink/route/tc.h | 63 + include/netlink/socket.h | 66 + include/netlink/types.h | 105 + include/netlink/utils.h | 76 + install-sh | 294 ++ lib/Makefile | 74 + lib/addr.c | 883 ++++++ lib/attr.c | 787 ++++++ lib/cache.c | 805 ++++++ lib/cache_mngr.c | 392 +++ lib/cache_mngt.c | 266 ++ lib/data.c | 172 ++ lib/defs.h | 29 + lib/defs.h.in | 28 + lib/doc.c | 106 + lib/family.c | 62 + lib/fib_lookup/lookup.c | 353 +++ lib/fib_lookup/request.c | 187 ++ lib/genl/ctrl.c | 319 +++ lib/genl/family.c | 285 ++ lib/genl/genl.c | 210 ++ lib/genl/mngt.c | 285 ++ lib/handlers.c | 447 +++ lib/msg.c | 931 +++++++ lib/netfilter/ct.c | 465 +++ lib/netfilter/ct_obj.c | 667 +++++ lib/netfilter/log.c | 349 +++ lib/netfilter/log_obj.c | 425 +++ lib/netfilter/nfnl.c | 246 ++ lib/nl.c | 798 ++++++ lib/object.c | 386 +++ lib/route/addr.c | 1130 ++++++++ lib/route/class.c | 230 ++ lib/route/class_api.c | 102 + lib/route/class_obj.c | 287 ++ lib/route/classifier.c | 343 +++ lib/route/cls/fw.c | 251 ++ lib/route/cls/police.c | 67 + lib/route/cls/u32.c | 602 ++++ lib/route/cls_api.c | 103 + lib/route/cls_obj.c | 203 ++ lib/route/link.c | 1313 +++++++++ lib/route/neigh.c | 1023 +++++++ lib/route/neightbl.c | 825 ++++++ lib/route/nexthop.c | 151 + lib/route/qdisc.c | 478 ++++ lib/route/qdisc_api.c | 98 + lib/route/qdisc_obj.c | 273 ++ lib/route/route.c | 444 +++ lib/route/route_obj.c | 908 ++++++ lib/route/route_utils.c | 183 ++ lib/route/rtnl.c | 125 + lib/route/rule.c | 900 ++++++ lib/route/sch/blackhole.c | 38 + lib/route/sch/cbq.c | 306 ++ lib/route/sch/dsmark.c | 459 +++ lib/route/sch/fifo.c | 205 ++ lib/route/sch/htb.c | 553 ++++ lib/route/sch/netem.c | 603 ++++ lib/route/sch/prio.c | 332 +++ lib/route/sch/red.c | 219 ++ lib/route/sch/sfq.c | 294 ++ lib/route/sch/tbf.c | 540 ++++ lib/route/tc.c | 585 ++++ lib/socket.c | 548 ++++ lib/utils.c | 701 +++++ libnl-1.pc.in | 10 + src/Makefile | 45 + src/f_addr.c | 107 + src/f_ct.c | 153 + src/f_link.c | 106 + src/f_neigh.c | 51 + src/f_route.c | 81 + src/genl-ctrl-dump.c | 65 + src/nf-ct-dump.c | 88 + src/nf-log.c | 142 + src/nf-monitor.c | 115 + src/nl-addr-add.c | 68 + src/nl-addr-delete.c | 70 + src/nl-addr-dump.c | 82 + src/nl-fib-lookup.c | 130 + src/nl-link-dump.c | 75 + src/nl-link-ifindex2name.c | 55 + src/nl-link-name2ifindex.c | 51 + src/nl-link-set.c | 83 + src/nl-link-stats.c | 109 + src/nl-list-caches.c | 119 + src/nl-list-sockets.c | 58 + src/nl-monitor.c | 135 + src/nl-neigh-add.c | 79 + src/nl-neigh-delete.c | 66 + src/nl-neigh-dump.c | 82 + src/nl-neightbl-dump.c | 70 + src/nl-qdisc-add.c | 196 ++ src/nl-qdisc-delete.c | 76 + src/nl-qdisc-dump.c | 74 + src/nl-route-add.c | 76 + src/nl-route-del.c | 76 + src/nl-route-dump.c | 81 + src/nl-route-get.c | 106 + src/nl-rule-dump.c | 78 + src/nl-tctree-dump.c | 144 + src/nl-util-addr.c | 41 + src/utils.c | 218 ++ src/utils.h | 63 + tests/Makefile | 37 + tests/test-cache-mngr.c | 73 + tests/test-genl.c | 56 + tests/test-nf-cache-mngr.c | 58 + tests/test-socket-creation.c | 15 + 188 files changed, 44556 insertions(+) create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 Makefile create mode 100644 Makefile.opts.in create mode 100644 Makefile.rules create mode 100644 aclocal.m4 create mode 100755 configure create mode 100644 configure.in create mode 100644 doc/Doxyfile create mode 100644 doc/Makefile create mode 100644 doc/libnl.css create mode 100644 include/Makefile create mode 100644 include/linux/gen_stats.h create mode 100644 include/linux/genetlink.h create mode 100644 include/linux/if.h create mode 100644 include/linux/if_addr.h create mode 100644 include/linux/if_arp.h create mode 100644 include/linux/if_ether.h create mode 100644 include/linux/if_link.h create mode 100644 include/linux/ip_mp_alg.h create mode 100644 include/linux/neighbour.h create mode 100644 include/linux/netfilter/nfnetlink.h create mode 100644 include/linux/netfilter/nfnetlink_conntrack.h create mode 100644 include/linux/netfilter/nfnetlink_log.h create mode 100644 include/linux/netlink.h create mode 100644 include/linux/pkt_cls.h create mode 100644 include/linux/pkt_sched.h create mode 100644 include/linux/rtnetlink.h create mode 100644 include/netlink-generic.h create mode 100644 include/netlink-local.h create mode 100644 include/netlink-tc.h create mode 100644 include/netlink-types.h create mode 100644 include/netlink/addr.h create mode 100644 include/netlink/attr.h create mode 100644 include/netlink/cache.h create mode 100644 include/netlink/data.h create mode 100644 include/netlink/fib_lookup/lookup.h create mode 100644 include/netlink/fib_lookup/request.h create mode 100644 include/netlink/genl/ctrl.h create mode 100644 include/netlink/genl/family.h create mode 100644 include/netlink/genl/genl.h create mode 100644 include/netlink/genl/mngt.h create mode 100644 include/netlink/handlers.h create mode 100644 include/netlink/list.h create mode 100644 include/netlink/msg.h create mode 100644 include/netlink/netfilter/ct.h create mode 100644 include/netlink/netfilter/log.h create mode 100644 include/netlink/netfilter/nfnl.h create mode 100644 include/netlink/netlink-compat.h create mode 100644 include/netlink/netlink-kernel.h create mode 100644 include/netlink/netlink.h create mode 100644 include/netlink/object.h create mode 100644 include/netlink/route/addr.h create mode 100644 include/netlink/route/class-modules.h create mode 100644 include/netlink/route/class.h create mode 100644 include/netlink/route/classifier-modules.h create mode 100644 include/netlink/route/classifier.h create mode 100644 include/netlink/route/cls/fw.h create mode 100644 include/netlink/route/cls/police.h create mode 100644 include/netlink/route/cls/u32.h create mode 100644 include/netlink/route/link.h create mode 100644 include/netlink/route/neighbour.h create mode 100644 include/netlink/route/neightbl.h create mode 100644 include/netlink/route/nexthop.h create mode 100644 include/netlink/route/qdisc-modules.h create mode 100644 include/netlink/route/qdisc.h create mode 100644 include/netlink/route/route.h create mode 100644 include/netlink/route/rtnl.h create mode 100644 include/netlink/route/rule.h create mode 100644 include/netlink/route/sch/cbq.h create mode 100644 include/netlink/route/sch/dsmark.h create mode 100644 include/netlink/route/sch/fifo.h create mode 100644 include/netlink/route/sch/htb.h create mode 100644 include/netlink/route/sch/netem.h create mode 100644 include/netlink/route/sch/prio.h create mode 100644 include/netlink/route/sch/red.h create mode 100644 include/netlink/route/sch/sfq.h create mode 100644 include/netlink/route/sch/tbf.h create mode 100644 include/netlink/route/tc.h create mode 100644 include/netlink/socket.h create mode 100644 include/netlink/types.h create mode 100644 include/netlink/utils.h create mode 100755 install-sh create mode 100644 lib/Makefile create mode 100644 lib/addr.c create mode 100644 lib/attr.c create mode 100644 lib/cache.c create mode 100644 lib/cache_mngr.c create mode 100644 lib/cache_mngt.c create mode 100644 lib/data.c create mode 100644 lib/defs.h create mode 100644 lib/defs.h.in create mode 100644 lib/doc.c create mode 100644 lib/family.c create mode 100644 lib/fib_lookup/lookup.c create mode 100644 lib/fib_lookup/request.c create mode 100644 lib/genl/ctrl.c create mode 100644 lib/genl/family.c create mode 100644 lib/genl/genl.c create mode 100644 lib/genl/mngt.c create mode 100644 lib/handlers.c create mode 100644 lib/msg.c create mode 100644 lib/netfilter/ct.c create mode 100644 lib/netfilter/ct_obj.c create mode 100644 lib/netfilter/log.c create mode 100644 lib/netfilter/log_obj.c create mode 100644 lib/netfilter/nfnl.c create mode 100644 lib/nl.c create mode 100644 lib/object.c create mode 100644 lib/route/addr.c create mode 100644 lib/route/class.c create mode 100644 lib/route/class_api.c create mode 100644 lib/route/class_obj.c create mode 100644 lib/route/classifier.c create mode 100644 lib/route/cls/fw.c create mode 100644 lib/route/cls/police.c create mode 100644 lib/route/cls/u32.c create mode 100644 lib/route/cls_api.c create mode 100644 lib/route/cls_obj.c create mode 100644 lib/route/link.c create mode 100644 lib/route/neigh.c create mode 100644 lib/route/neightbl.c create mode 100644 lib/route/nexthop.c create mode 100644 lib/route/qdisc.c create mode 100644 lib/route/qdisc_api.c create mode 100644 lib/route/qdisc_obj.c create mode 100644 lib/route/route.c create mode 100644 lib/route/route_obj.c create mode 100644 lib/route/route_utils.c create mode 100644 lib/route/rtnl.c create mode 100644 lib/route/rule.c create mode 100644 lib/route/sch/blackhole.c create mode 100644 lib/route/sch/cbq.c create mode 100644 lib/route/sch/dsmark.c create mode 100644 lib/route/sch/fifo.c create mode 100644 lib/route/sch/htb.c create mode 100644 lib/route/sch/netem.c create mode 100644 lib/route/sch/prio.c create mode 100644 lib/route/sch/red.c create mode 100644 lib/route/sch/sfq.c create mode 100644 lib/route/sch/tbf.c create mode 100644 lib/route/tc.c create mode 100644 lib/socket.c create mode 100644 lib/utils.c create mode 100644 libnl-1.pc.in create mode 100644 src/Makefile create mode 100644 src/f_addr.c create mode 100644 src/f_ct.c create mode 100644 src/f_link.c create mode 100644 src/f_neigh.c create mode 100644 src/f_route.c create mode 100644 src/genl-ctrl-dump.c create mode 100644 src/nf-ct-dump.c create mode 100644 src/nf-log.c create mode 100644 src/nf-monitor.c create mode 100644 src/nl-addr-add.c create mode 100644 src/nl-addr-delete.c create mode 100644 src/nl-addr-dump.c create mode 100644 src/nl-fib-lookup.c create mode 100644 src/nl-link-dump.c create mode 100644 src/nl-link-ifindex2name.c create mode 100644 src/nl-link-name2ifindex.c create mode 100644 src/nl-link-set.c create mode 100644 src/nl-link-stats.c create mode 100644 src/nl-list-caches.c create mode 100644 src/nl-list-sockets.c create mode 100644 src/nl-monitor.c create mode 100644 src/nl-neigh-add.c create mode 100644 src/nl-neigh-delete.c create mode 100644 src/nl-neigh-dump.c create mode 100644 src/nl-neightbl-dump.c create mode 100644 src/nl-qdisc-add.c create mode 100644 src/nl-qdisc-delete.c create mode 100644 src/nl-qdisc-dump.c create mode 100644 src/nl-route-add.c create mode 100644 src/nl-route-del.c create mode 100644 src/nl-route-dump.c create mode 100644 src/nl-route-get.c create mode 100644 src/nl-rule-dump.c create mode 100644 src/nl-tctree-dump.c create mode 100644 src/nl-util-addr.c create mode 100644 src/utils.c create mode 100644 src/utils.h create mode 100644 tests/Makefile create mode 100644 tests/test-cache-mngr.c create mode 100644 tests/test-genl.c create mode 100644 tests/test-nf-cache-mngr.c create mode 100644 tests/test-socket-creation.c diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..371ec20 --- /dev/null +++ b/COPYING @@ -0,0 +1,461 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..b400de6 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,333 @@ +Summary of Changes from 1.0-pre6 to HEAD +================================================ + Thomas Graf + o Generic netlink support + o Route Addition/Deletion + o Added nl_cache_subset() + o Have nl_object_clone() make real clones without + sharing any data. + o Remove old nl_object_alloc() not based on a object + operations to avoid bugs due to missing init. + o Added nl-list-caches utility + o Removed nlmsg_build_no_hdr(), duplicate + o Reworked message interface + o Fixed nlmsg_put() and genlmsg_put() to correctly reserve + tail room for user specific headers. + o Added nl_cache_move() + o Renamed nl_cache_delete() to nl_cache_remove() (no API break) + o Fixed reference counting while objects stay in caches. + o Object marking + o Moved attribute mask for objects into generic structure + o nl-list-caches: List available dump procedures + o Use PAGE_SIZE as initial buffer size when reading from + netlink socket + o Double buffer size when recv() returns MSG_TRUNC + o Replaced filter object operation with new compare operation + capable of listing differences between two objects + o Added nl_object_identical() to check if two objects are + identical from a uniqueness point of view + o Added nl_object_diff() returning a bitmask of differences in + attributes + o Added nl_object_attr_list() generating a list of attribute + name the object has assigned + o Cache updates based on event notifications, code based on + Patrick McHardy's patches + o Cache Manager + o Added NL_AUTO_PID, NL_AUTO_SEQ for convenience + o Disable MSG_PEEK by default and provide nl_socket_enable_msg_peek() + o Fixed nl_recvmsgs() to return 0 when interrupted via NL_STOP or + NL_SKIP + o Fixed nl_recvmsgs() to stop reading after parsing if not in the + middle of a multipart message. + o Fixed nl_recvmsgs() to not stop after receving an ACK + o Fixed nl_recvmsgs() to not blindly discard remaining messages + if a NLMSG_DONE message is received. + + Petr Gotthard + Siemens AG Oesterreich + o Fix u32 to properly handle multiple keys + o rtnl_htb_set_(r|c)buffer() + o Fixed MTU handling in HTB class, problem pointed out + by Edouard Thuleau + + Zilvinas Valinskas + o Fix wrong msg_namelen in nl_recv() + o Fix memory leak in flnl_request_put() + + Helmut Schaa + o Fix for using libnl from within C++ + + Patrick McHardy + o *_alloc_cache(): Only refill cache if handle is provided + + James Oakley + o Fix rtnl_link_set_arptype() typo + + Philip Craig + o Change address family type from char to int + o Fix the error handling when the build fails. + o add nl_cache_mngr_get_fd() + o add netfilter support + o add netfilter conntrack support + o add netfilter log support + +Summary of Changes from 1.0-pre5 to 1.0-pre6 +================================================ + Christopher Aillon + o Use $(libdir) instead of $(prefix)/lib for 64bit awesomeness. + + Thomas Graf + o Extend nl_msg to include source address, destination address + and the protocol being used. + o Make nl_send*() take a nl_msg instead of a nlmsghdr (API BREAK) + o Change callbacks to take a nl_msg instead of source address + and nlmsghdr (API BREAK) + o caches must specify the protocol they're hooked up from now on + if they intend to be associated with message types. + o cache_mngt_associate now takes the protocol besides the message + type to allow for multiple protocols to be supported (API BREAK) + o overwrite destination address in nl_send() when specified in the + message itself, allows for unbound addressing. + o Support for netlink based fib_lookup() + o Documentation fixes + o Fix double nlmsg_free() in nl_recvmsgs() while receiving + a multipart message and the read was interrupted. + o Change cache operations to store names for message types. + o Provide interface to convert message type to character string. + o Add dp_dump_msgtype to prefix each dumped element with a + pretty printed message type. + o netlink fib lookup support + o nl_recvmsgs() debugging + o use nl_cachemngt_type2name() when pretty printing netlink header + o Routing protocol translations. + o Routing metric translations. + o Revised route dumping + o Nexthop flag translations. + o Add support for IFF_DORMANT + + Petr Gotthard + Siemens AG Oesterreich + o Fix access to obj after freeing it + o Fix u32 selector access after realloc() + o Fix missing out-of-memory error handling in various places + o Enhance nl-monitor to have group selection selectable and + demonstrate usage of select() + o Don't ignore IFF_RUNNING any longer + o fw classifier support + + Patrick McHardy + o Fix conflicting types for __u64 + o Fix printf format string warnings + o Fix object cloning + o Deal with structure padding in nl_object_clone + o Fix nl_addr leak + o Set ce_msgtype in all parsed objects + o Fix addr flag filter + o Fix RTNLGRP definitions (was based on broken kernel version) + o Export nl_get_errno() + o Add function to get/set peer pid + o Add IFF_LOWER_UP + o Add/export some missing accessor functions + o print /0 prefix in nl_addr2str() + o Fix invalid free in nl_addr_parse for AF_UNSPEC addresses + o Use __str2flags instead of __str2type in rtnl_link_str2flags() + o Make sure object and filter types match in nl_object_match() + o Add support for credential passing over netlink sockets (API BREAK) + o Add support for custom dump callbacks + o Add NL_DUMP_ENV format + + Michael Biebl + "Alex V. Myltsev" + o Makefile fixes + + +Summary of Changes from 1.0-pre4 to 1.0-pre5 +================================================ + Thomas Graf + o Use minimized local copies for , , + and to avoid compile troubles with + applications including + Reported by Christopher Aillon. + +Summary of Changes from 1.0-pre3 to 1.0-pre4 +================================================ + Thomas Graf + o Fix wrong rtnl_addr_set_prefixlen() external declaration, + reported by Dan Williams. + o Fix nl_addr_parse() to not change the original string + for prefixes. + o Do not build documentation per default, but have the user + issue 'make gendoc' + o Assume neighbours to be permanent, set NUD_PERMANENT if not + specified otherwise. + +Summary of Changes from 1.0-pre2 to 1.0-pre3 +================================================ + Thomas Graf + o Fix SFQ parser to allocate qdisc options. + o Fix rule statistics dumping to not call itself. + o Complete Netem qdisc interface. + o Add rtnl_*_put() and rtnl_*_free() to increase readability. + o Cleanup of nl-* tools + o Fix inclusion guards of route/neightbl.h + o Fix nl_connect() to only modify rx/tx socket buffers if not + already modified by the user. + o Fix wrong nl_handle_alloc() prototype. + o Fix typo in route/addr.c causing label to be marked as + local address. + o Use ~0UL as default prefix length instead of 0. + o Fix neighbour message parser to correctly store core. + attributes and provide them again. + o Fix neighbour message parser to correctly guess address family. + to make it compatible with nl_addr_parse() and ether llc + addresses. + o Add rtnl_route_table2str(), rtnl_route_str2table(). + o Add nl_cache_nitems_filter() to find out if a filter produces + any matches. + o Remove rtnl_rule_set_(dst|src)_str() (obsolete). + o Remove scope and protocol field of routing rule. + o Complete routing rules module. + o Move realms translations from route to rtnl module. + +Summary of Changes from 1.0-pre1 to 1.0-pre2 +================================================ + Thomas Graf + o More API documentation + o Added flags argument to rtnl_addr_(add|build_add_request)(). + o Added rtnl_addr_(set|get)_multicast(). + o Moved scope translations routines from route/route.c to + route/rtnl.c, required by other modules as well. + o Removed old rtattr bits from rtnetlink-kernel.h + o Customized libnl.css for doxygen documentation + o Removed non-reentrant translation routines, only bloating + the code and too risky. + o Fixed wrong version number from 1.0-pre1. + o Reenabled unfinished policer module. + o Reworked TBF module, automatic caluclation of transmit times, + limit setable via latency, automatic cell size calculation, + options TLV generation. (untested) + o Renamed nl_xmittime() to rtnl_tc_calc_txtime(). + o Renamde nl_build_rtable() to rtnl_tc_build_rate_table() + + Petr Gotthard , + Siemens AG Oesterreich + o Fix symlinks to libnl library files to be moveable + o Fix extern struct prototypes meant to be static. + o Add empty install target to src/Makefile + + Simon Stelling + o Use LIBDIR instead of $(prefix)/lib for users to alllow librariers + into $(prefix)/lib64. + +Summary of Changes from 0.5.0 to 1.0-pre1 +================================================ + Thomas Graf + o Uncountable number of changes, rewrite of certain modules, + several major API breakages + + Petr Gotthard , + Siemens AG Oesterreich + o added class_build, rtnl_class_build_add_request, rtnl_class_add + o added HTB (Hierachical Token Bucket) class support + o added nl_xmittime, nl_build_rtable + o added nl_data_append to realloc a nl_data structure + o added rtnl_rcopy_ratespec as reverse to rtnl_copy_ratespec + o fixed byte order conversion of rtnl_filter.protocol + o SuSE and Fedora Linux compile fixes + o fixed u32 classifier support + o added rtnl_u32_set_handle, rtnl_u32_set_classid, rtnl_u32_set_flags + and several rtnl_u32_add_key_... operations to u32 classifier + +Summary of Changes from 0.4.4 to 0.5.0 +================================================ + Thomas Graf + o API documentation + o nl_cache_filter to manually filter on a object + o partial routing support + o routing rules support + o Propely set address family when setting addresses + o debug flag and some rare messages, more to come + o make error mesage verboseness configureable + o tc fixes to wait for ack + o cleanup and adaption of address code to latest internal API + o various cleanups + o dozens of API breakages (better now than later) + + Daniel Hottinger + o arch 64bit printf length modifier fixes + + Baruch Even , + Mediatrix Telecom, inc. + o address support + +Summary of changes from 0.4.3 to 0.4.4 +================================================ + Thomas Graf : + o overall cleanups for better code quality + o replace dump_(brief|full|with_stats) ops with + dump[NL_DUMP_MAX] array to allow further additions without + breaking the ABI. + o add of send_ack callback, called whenever when oppenent + asks for an ACK. + o make nl_parse_rtattr initialize the tb buffer like in the + kernel, caller must no longer take care of it. + o remove nl_addrnattr (obsolete) + o fixed nl_msg_append_raw to correctly calculate length + for raw data not aligned to NLMSG_ALIGN + o fix memory leak in nl_recv in case of errors + o correctly check sequence numbers if more than one message + was sent out before the answer is being received. + o add workaround for buggy netlink applications not properly + setting NLM_F_MULTI. + +Summary of changes from 0.4.2 to 0.4.3 +================================================ + + Thomas Graf : + o use parser_param in nl_cache_parse + o EGP: dump nfilters attribute + o allow retrieving of filters attached to classes via + FILTER_CACHE_PARENT(C) cache argument + o filter message building API + +Summary of changes from 0.4.1 to 0.4.2 +================================================ + + Baruch Even : + o memory leak fix in nl_parse_rtattr + o reset padding to 0 when appending raw data to a nl_msg + o avoid overwriting nlmsg ptr when buffer extending fails + o typo fixes + o create symlinks libnl.so.0 and libnl.so + + Thomas Graf : + o EGP classifier support + o avoid null pointer in printf call + o added nl_cache_parse to put nl_msg's into a cache + o added rtnl_filter_build to build a nl_msg filter message + o correctly install header files + o nl_msg_payload/nl_msg_payloadlen to access nl_msg payload + o nl_parse_nested macro to simplify nested TLV parsing + o NL_ERROR_ASSERT compile flag to assert(0) on errors + o rta alignment fix in nl_msg_append_tlv + o added nl_msg_parse_rtattr as shortcut for nl_parse_rtattr + for nl_msg API + o added nl_parse_nested for nested TLVs + o added RTA_ARRAY_ELEMS macro to calculate array length + for array TLVs + o added nl_wait_for_ack to wait for the next ack + o added rtnl_link_build_change_request(...) + o added rtnl_neigh_build_*_request + o converted neighbour code to use nl_wait_for_ack + o cb_recvmsgs_ow callback to overwrite internal calls to + nl_recvmsgs_def + o cb_seq_check callback to overwrite default sequence checking + o added nl_parser_param as argument for message parsers including + a callback to be called upon successful parsing of a message. + Removes the requirement of having all parsed messages to be added + to a cache. + o added cb_recv_ow and nl_send_ow callbacks to overwrite internal + calls to nl_recv and nl_send. + + Jamal Hadi Salim + o Linux 2.4 compile fixes diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..826b9a3 --- /dev/null +++ b/Makefile @@ -0,0 +1,64 @@ +# +# Makefile +# +# 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 version 2.1 +# of the License. +# +# Copyright (c) 2003-2006 Thomas Graf +# + +ifeq ($(shell [ ! -r Makefile.opts ] && echo 1),) + include Makefile.opts +endif + +SUBDIRS := lib include doc src tests +.PHONY: all clean distclean install gendoc $(SUBDIRS) + +all: Makefile.opts + @for dir in $(SUBDIRS); do \ + echo "Entering $$dir" && $(MAKE) -C $$dir || exit $$?; \ + done + +clean: Makefile.opts + rm -f cscope.* + @for dir in $(SUBDIRS); do \ + echo "Entering $$dir" && $(MAKE) -C $$dir clean || exit $$?; \ + done + +distclean: clean + @$(RM) -rf Makefile.opts autom4te.cache config.log config.status + @for dir in $(SUBDIRS); do \ + echo "Entering $$dir" && $(MAKE) -C $$dir distclean || exit $$?; \ + done + +install: Makefile.opts + @for dir in $(SUBDIRS); do \ + echo "Entering $$dir" && cd $$dir && $(MAKE) install && cd ..; \ + done + mkdir -p $(DESTDIR)$(libdir)/pkgconfig/ + install -m 0644 libnl-1.pc $(DESTDIR)$(libdir)/pkgconfig/ + +gendoc: + $(MAKE) -C doc gendoc + +show: Makefile.opts + @echo "CC: $(CC)" + @echo "RM: $(RM)" + @echo "CFLAGS: $(CFLAGS)" + @echo "DEPFLAGS: $(DEPFLAGS)" + @echo "LDFLAGS: $(LDFLAGS)" + @echo "DESTDIR: $(DESTDIR)" + @echo "prefix: $(prefix)" + @echo "libdir: $(libdir)" + @echo "includedir: $(includedir)" + +cscope: + cscope -b -q -R -Iinclude -slib -ssrc + + +$(SUBDIRS): + cd $@ && $(MAKE) + +-include Makefile.rules diff --git a/Makefile.opts.in b/Makefile.opts.in new file mode 100644 index 0000000..87d229b --- /dev/null +++ b/Makefile.opts.in @@ -0,0 +1,39 @@ +# +# Makefile.opts.in +# +# 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 version 2.1 +# of the License. +# +# Copyright (c) 2003-2006 Thomas Graf +# + +CC := @CC@ +CFLAGS := @CFLAGS@ +LDFLAGS := @LDFLAGS@ +CPPFLAGS := @CPPFLAGS@ +PACKAGE_NAME := @PACKAGE_NAME@ +PACKAGE_VERSION := @PACKAGE_VERSION@ + +LIBNL_LIB := @LIBNL_LIB@ + +prefix := @prefix@ +exec_prefix := @exec_prefix@ +libdir := @libdir@ +includedir := @includedir@ +mandir := @mandir@ +sysconfdir := @sysconfdir@ + +AR := ar +RM := rm +LN := ln + +DEPFLAGS += -M -I../include/ -I. $(CPPFLAGS) +CFLAGS += -g -I./include -I../include -I. $(CPPFLAGS) -D_GNU_SOURCE +MAKEFLAGS += --no-print-directory + +ifeq ($(CC),gcc) +CFLAGS += -Wall -ggdb +endif + diff --git a/Makefile.rules b/Makefile.rules new file mode 100644 index 0000000..5a1decf --- /dev/null +++ b/Makefile.rules @@ -0,0 +1,37 @@ +# +# Makefile.rules +# +# 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 version 2.1 +# of the License. +# +# Copyright (c) 2003-2006 Thomas Graf +# + +.SUFFIXES: +.SUFFIXES: .d .c + +%.o: %.c + @echo " CC $<"; \ + $(CC) $(CFLAGS) -c -o $@ $< + +%.d: %.c + @echo " DEP $<"; \ + $(CC) $(DEPFLAGS) $< > $@.tmp; \ + sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.tmp > $@; \ + rm -f $@.tmp + +Makefile.opts: + @echo "***" + @echo "*** No configuration found, please run ./configure" + @echo "***" + @exit 1 + +ifneq ($(MAKECMDGOALS),clean) +ifneq ($(MAKECMDGOALS),distclean) +ifneq ($(DEPS),) +-include $(DEPS) +endif +endif +endif diff --git a/aclocal.m4 b/aclocal.m4 new file mode 100644 index 0000000..177c013 --- /dev/null +++ b/aclocal.m4 @@ -0,0 +1,831 @@ +dnl aclocal.m4 generated automatically by aclocal 1.4-p6 + +dnl Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl This program is distributed in the hope that it will be useful, +dnl but WITHOUT ANY WARRANTY, to the extent permitted by law; without +dnl even the implied warranty of MERCHANTABILITY or FITNESS FOR A +dnl PARTICULAR PURPOSE. + +# lib-prefix.m4 serial 4 (gettext-0.14.2) +dnl Copyright (C) 2001-2005 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and +dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't +dnl require excessive bracketing. +ifdef([AC_HELP_STRING], +[AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])], +[AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])]) + +dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed +dnl to access previously installed libraries. The basic assumption is that +dnl a user will want packages to use other packages he previously installed +dnl with the same --prefix option. +dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate +dnl libraries, but is otherwise very convenient. +AC_DEFUN([AC_LIB_PREFIX], +[ + AC_BEFORE([$0], [AC_LIB_LINKFLAGS]) + AC_REQUIRE([AC_PROG_CC]) + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_LIB_ARG_WITH([lib-prefix], +[ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib + --without-lib-prefix don't search for libraries in includedir and libdir], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/lib" + fi + fi +]) + if test $use_additional = yes; then + dnl Potentially add $additional_includedir to $CPPFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's already present in $CPPFLAGS, + dnl 3. if it's /usr/local/include and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + for x in $CPPFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $CPPFLAGS. + CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" + fi + fi + fi + fi + dnl Potentially add $additional_libdir to $LDFLAGS. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's already present in $LDFLAGS, + dnl 3. if it's /usr/local/lib and we are using GCC on Linux, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/lib"; then + haveit= + for x in $LDFLAGS; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test "X$additional_libdir" = "X/usr/local/lib"; then + if test -n "$GCC"; then + case $host_os in + linux*) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LDFLAGS. + LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" + fi + fi + fi + fi + fi +]) + +dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix, +dnl acl_final_exec_prefix, containing the values to which $prefix and +dnl $exec_prefix will expand at the end of the configure script. +AC_DEFUN([AC_LIB_PREPARE_PREFIX], +[ + dnl Unfortunately, prefix and exec_prefix get only finally determined + dnl at the end of configure. + if test "X$prefix" = "XNONE"; then + acl_final_prefix="$ac_default_prefix" + else + acl_final_prefix="$prefix" + fi + if test "X$exec_prefix" = "XNONE"; then + acl_final_exec_prefix='${prefix}' + else + acl_final_exec_prefix="$exec_prefix" + fi + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" + prefix="$acl_save_prefix" +]) + +dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the +dnl variables prefix and exec_prefix bound to the values they will have +dnl at the end of the configure script. +AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], +[ + acl_save_prefix="$prefix" + prefix="$acl_final_prefix" + acl_save_exec_prefix="$exec_prefix" + exec_prefix="$acl_final_exec_prefix" + $1 + exec_prefix="$acl_save_exec_prefix" + prefix="$acl_save_prefix" +]) + +# lib-link.m4 serial 6 (gettext-0.14.3) +dnl Copyright (C) 2001-2005 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Bruno Haible. + +AC_PREREQ(2.50) + +dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and +dnl the libraries corresponding to explicit and implicit dependencies. +dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and +dnl augments the CPPFLAGS variable. +AC_DEFUN([AC_LIB_LINKFLAGS], +[ + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + define([Name],[translit([$1],[./-], [___])]) + define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [ + AC_LIB_LINKFLAGS_BODY([$1], [$2]) + ac_cv_lib[]Name[]_libs="$LIB[]NAME" + ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME" + ac_cv_lib[]Name[]_cppflags="$INC[]NAME" + ]) + LIB[]NAME="$ac_cv_lib[]Name[]_libs" + LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs" + INC[]NAME="$ac_cv_lib[]Name[]_cppflags" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) + AC_SUBST([LIB]NAME) + AC_SUBST([LTLIB]NAME) + dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the + dnl results of this search when this library appears as a dependency. + HAVE_LIB[]NAME=yes + undefine([Name]) + undefine([NAME]) +]) + +dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode) +dnl searches for libname and the libraries corresponding to explicit and +dnl implicit dependencies, together with the specified include files and +dnl the ability to compile and link the specified testcode. If found, it +dnl sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} and +dnl LTLIB${NAME} variables and augments the CPPFLAGS variable, and +dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs +dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty. +AC_DEFUN([AC_LIB_HAVE_LINKFLAGS], +[ + AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) + AC_REQUIRE([AC_LIB_RPATH]) + define([Name],[translit([$1],[./-], [___])]) + define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + + dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME + dnl accordingly. + AC_LIB_LINKFLAGS_BODY([$1], [$2]) + + dnl Add $INC[]NAME to CPPFLAGS before performing the following checks, + dnl because if the user has installed lib[]Name and not disabled its use + dnl via --without-lib[]Name-prefix, he wants to use it. + ac_save_CPPFLAGS="$CPPFLAGS" + AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) + + AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [ + ac_save_LIBS="$LIBS" + LIBS="$LIBS $LIB[]NAME" + AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name=no]) + LIBS="$ac_save_LIBS" + ]) + if test "$ac_cv_lib[]Name" = yes; then + HAVE_LIB[]NAME=yes + AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the $1 library.]) + AC_MSG_CHECKING([how to link with lib[]$1]) + AC_MSG_RESULT([$LIB[]NAME]) + else + HAVE_LIB[]NAME=no + dnl If $LIB[]NAME didn't lead to a usable library, we don't need + dnl $INC[]NAME either. + CPPFLAGS="$ac_save_CPPFLAGS" + LIB[]NAME= + LTLIB[]NAME= + fi + AC_SUBST([HAVE_LIB]NAME) + AC_SUBST([LIB]NAME) + AC_SUBST([LTLIB]NAME) + undefine([Name]) + undefine([NAME]) +]) + +dnl Determine the platform dependent parameters needed to use rpath: +dnl libext, shlibext, hardcode_libdir_flag_spec, hardcode_libdir_separator, +dnl hardcode_direct, hardcode_minus_L. +AC_DEFUN([AC_LIB_RPATH], +[ + dnl Tell automake >= 1.10 to complain if config.rpath is missing. + m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([config.rpath])]) + AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS + AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld + AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host + AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir + AC_CACHE_CHECK([for shared library run path origin], acl_cv_rpath, [ + CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ + ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh + . ./conftest.sh + rm -f ./conftest.sh + acl_cv_rpath=done + ]) + wl="$acl_cv_wl" + libext="$acl_cv_libext" + shlibext="$acl_cv_shlibext" + hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" + hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" + hardcode_direct="$acl_cv_hardcode_direct" + hardcode_minus_L="$acl_cv_hardcode_minus_L" + dnl Determine whether the user wants rpath handling at all. + AC_ARG_ENABLE(rpath, + [ --disable-rpath do not hardcode runtime library paths], + :, enable_rpath=yes) +]) + +dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and +dnl the libraries corresponding to explicit and implicit dependencies. +dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables. +AC_DEFUN([AC_LIB_LINKFLAGS_BODY], +[ + define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], + [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) + dnl By default, look in $includedir and $libdir. + use_additional=yes + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + AC_LIB_ARG_WITH([lib$1-prefix], +[ --with-lib$1-prefix[=DIR] search for lib$1 in DIR/include and DIR/lib + --without-lib$1-prefix don't search for lib$1 in includedir and libdir], +[ + if test "X$withval" = "Xno"; then + use_additional=no + else + if test "X$withval" = "X"; then + AC_LIB_WITH_FINAL_PREFIX([ + eval additional_includedir=\"$includedir\" + eval additional_libdir=\"$libdir\" + ]) + else + additional_includedir="$withval/include" + additional_libdir="$withval/lib" + fi + fi +]) + dnl Search the library and its dependencies in $additional_libdir and + dnl $LDFLAGS. Using breadth-first-seach. + LIB[]NAME= + LTLIB[]NAME= + INC[]NAME= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='$1 $2' + while test -n "$names_next_round"; do + names_this_round="$names_next_round" + names_next_round= + for name in $names_this_round; do + already_handled= + for n in $names_already_handled; do + if test "$n" = "$name"; then + already_handled=yes + break + fi + done + if test -z "$already_handled"; then + names_already_handled="$names_already_handled $name" + dnl See if it was already located by an earlier AC_LIB_LINKFLAGS + dnl or AC_LIB_HAVE_LINKFLAGS call. + uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` + eval value=\"\$HAVE_LIB$uppername\" + if test -n "$value"; then + if test "$value" = yes; then + eval value=\"\$LIB$uppername\" + test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value" + else + dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined + dnl that this library doesn't exist. So just drop it. + : + fi + else + dnl Search the library lib$name in $additional_libdir and $LDFLAGS + dnl and the already constructed $LIBNAME/$LTLIBNAME. + found_dir= + found_la= + found_so= + found_a= + if test $use_additional = yes; then + if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext"; then + found_dir="$additional_libdir" + found_so="$additional_libdir/lib$name.$shlibext" + if test -f "$additional_libdir/lib$name.la"; then + found_la="$additional_libdir/lib$name.la" + fi + else + if test -f "$additional_libdir/lib$name.$libext"; then + found_dir="$additional_libdir" + found_a="$additional_libdir/lib$name.$libext" + if test -f "$additional_libdir/lib$name.la"; then + found_la="$additional_libdir/lib$name.la" + fi + fi + fi + fi + if test "X$found_dir" = "X"; then + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + case "$x" in + -L*) + dir=`echo "X$x" | sed -e 's/^X-L//'` + if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext"; then + found_dir="$dir" + found_so="$dir/lib$name.$shlibext" + if test -f "$dir/lib$name.la"; then + found_la="$dir/lib$name.la" + fi + else + if test -f "$dir/lib$name.$libext"; then + found_dir="$dir" + found_a="$dir/lib$name.$libext" + if test -f "$dir/lib$name.la"; then + found_la="$dir/lib$name.la" + fi + fi + fi + ;; + esac + if test "X$found_dir" != "X"; then + break + fi + done + fi + if test "X$found_dir" != "X"; then + dnl Found the library. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name" + if test "X$found_so" != "X"; then + dnl Linking with a shared library. We attempt to hardcode its + dnl directory into the executable's runpath, unless it's the + dnl standard /usr/lib. + if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then + dnl No hardcoding is needed. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $found_dir" + fi + dnl The hardcoding into $LIBNAME is system dependent. + if test "$hardcode_direct" = yes; then + dnl Using DIR/libNAME.so during linking hardcodes DIR into the + dnl resulting binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then + dnl Use an explicit option to hardcode DIR into the resulting + dnl binary. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $found_dir" + fi + else + dnl Rely on "-L$found_dir". + dnl But don't add it if it's already contained in the LDFLAGS + dnl or the already constructed $LIBNAME + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$found_dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir" + fi + if test "$hardcode_minus_L" != no; then + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" + else + dnl We cannot use $hardcode_runpath_var and LD_RUN_PATH + dnl here, because this doesn't fit in flags passed to the + dnl compiler. So give up. No hardcoding. This affects only + dnl very old systems. + dnl FIXME: Not sure whether we should use + dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" + dnl here. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + dnl Linking with a static library. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a" + else + dnl We shouldn't come here, but anyway it's good to have a + dnl fallback. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name" + fi + fi + dnl Assume the include files are nearby. + additional_includedir= + case "$found_dir" in + */lib | */lib/) + basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'` + additional_includedir="$basedir/include" + ;; + esac + if test "X$additional_includedir" != "X"; then + dnl Potentially add $additional_includedir to $INCNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/include, + dnl 2. if it's /usr/local/include and we are using GCC on Linux, + dnl 3. if it's already present in $CPPFLAGS or the already + dnl constructed $INCNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_includedir" != "X/usr/include"; then + haveit= + if test "X$additional_includedir" = "X/usr/local/include"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + for x in $CPPFLAGS $INC[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-I$additional_includedir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_includedir"; then + dnl Really add $additional_includedir to $INCNAME. + INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir" + fi + fi + fi + fi + fi + dnl Look for dependencies. + if test -n "$found_la"; then + dnl Read the .la file. It defines the variables + dnl dlname, library_names, old_library, dependency_libs, current, + dnl age, revision, installed, dlopen, dlpreopen, libdir. + save_libdir="$libdir" + case "$found_la" in + */* | *\\*) . "$found_la" ;; + *) . "./$found_la" ;; + esac + libdir="$save_libdir" + dnl We use only dependency_libs. + for dep in $dependency_libs; do + case "$dep" in + -L*) + additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` + dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME. + dnl But don't add it + dnl 1. if it's the standard /usr/lib, + dnl 2. if it's /usr/local/lib and we are using GCC on Linux, + dnl 3. if it's already present in $LDFLAGS or the already + dnl constructed $LIBNAME, + dnl 4. if it doesn't exist as a directory. + if test "X$additional_libdir" != "X/usr/lib"; then + haveit= + if test "X$additional_libdir" = "X/usr/local/lib"; then + if test -n "$GCC"; then + case $host_os in + linux* | gnu* | k*bsd*-gnu) haveit=yes;; + esac + fi + fi + if test -z "$haveit"; then + haveit= + for x in $LDFLAGS $LIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LIBNAME. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIB[]NAME; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X-L$additional_libdir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + if test -d "$additional_libdir"; then + dnl Really add $additional_libdir to $LTLIBNAME. + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir" + fi + fi + fi + fi + ;; + -R*) + dir=`echo "X$dep" | sed -e 's/^X-R//'` + if test "$enable_rpath" != no; then + dnl Potentially add DIR to rpathdirs. + dnl The rpathdirs will be appended to $LIBNAME at the end. + haveit= + for x in $rpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + rpathdirs="$rpathdirs $dir" + fi + dnl Potentially add DIR to ltrpathdirs. + dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. + haveit= + for x in $ltrpathdirs; do + if test "X$x" = "X$dir"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + ltrpathdirs="$ltrpathdirs $dir" + fi + fi + ;; + -l*) + dnl Handle this in the next round. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` + ;; + *.la) + dnl Handle this in the next round. Throw away the .la's + dnl directory; it is already contained in a preceding -L + dnl option. + names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` + ;; + *) + dnl Most likely an immediate library name. + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep" + ;; + esac + done + fi + else + dnl Didn't find the library; assume it is in the system directories + dnl known to the linker and runtime loader. (All the system + dnl directories known to the linker should also be known to the + dnl runtime loader, otherwise the system is severely misconfigured.) + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name" + fi + fi + fi + done + done + if test "X$rpathdirs" != "X"; then + if test -n "$hardcode_libdir_separator"; then + dnl Weird platform: only the last -rpath option counts, the user must + dnl pass all path elements in one option. We can arrange that for a + dnl single library, but not when more than one $LIBNAMEs are used. + alldirs= + for found_dir in $rpathdirs; do + alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir" + done + dnl Note: hardcode_libdir_flag_spec uses $libdir and $wl. + acl_save_libdir="$libdir" + libdir="$alldirs" + eval flag=\"$hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + else + dnl The -rpath options are cumulative. + for found_dir in $rpathdirs; do + acl_save_libdir="$libdir" + libdir="$found_dir" + eval flag=\"$hardcode_libdir_flag_spec\" + libdir="$acl_save_libdir" + LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + dnl When using libtool, the option that works for both libraries and + dnl executables is -R. The -R options are cumulative. + for found_dir in $ltrpathdirs; do + LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir" + done + fi +]) + +dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR, +dnl unless already present in VAR. +dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes +dnl contains two or three consecutive elements that belong together. +AC_DEFUN([AC_LIB_APPENDTOVAR], +[ + for element in [$2]; do + haveit= + for x in $[$1]; do + AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) + if test "X$x" = "X$element"; then + haveit=yes + break + fi + done + if test -z "$haveit"; then + [$1]="${[$1]}${[$1]:+ }$element" + fi + done +]) + +# lib-ld.m4 serial 3 (gettext-0.13) +dnl Copyright (C) 1996-2003 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl Subroutines of libtool.m4, +dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision +dnl with libtool.m4. + +dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no. +AC_DEFUN([AC_LIB_PROG_LD_GNU], +[AC_CACHE_CHECK([if the linker ($LD) is GNU ld], acl_cv_prog_gnu_ld, +[# I'd rather use --version here, but apparently some GNU ld's only accept -v. +case `$LD -v 2>&1 conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by GCC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]* | [A-Za-z]:[\\/]*)] + [re_direlt='/[^/][^/]*/\.\./'] + # Canonicalize the path of ld + ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` + while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do + ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(acl_cv_path_LD, +[if test -z "$LD"; then + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" + for ac_dir in $PATH; do + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + acl_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some GNU ld's only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in + *GNU* | *'with BFD'*) + test "$with_gnu_ld" != no && break ;; + *) + test "$with_gnu_ld" != yes && break ;; + esac + fi + done + IFS="$ac_save_ifs" +else + acl_cv_path_LD="$LD" # Let the user override the test with a path. +fi]) +LD="$acl_cv_path_LD" +if test -n "$LD"; then + AC_MSG_RESULT($LD) +else + AC_MSG_RESULT(no) +fi +test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH]) +AC_LIB_PROG_LD_GNU +]) + diff --git a/configure b/configure new file mode 100755 index 0000000..f76b7b4 --- /dev/null +++ b/configure @@ -0,0 +1,3724 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.59 for libnl 1.0-pre6. +# +# Report bugs to . +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME='libnl' +PACKAGE_TARNAME='libnl' +PACKAGE_VERSION='1.0-pre6' +PACKAGE_STRING='libnl 1.0-pre6' +PACKAGE_BUGREPORT='tgraf@suug.ch' + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LIBNL_LIB LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures libnl 1.0-pre6 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of libnl 1.0-pre6:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-verbose-errors enable verbose errors (debugging) + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + CPPFLAGS C/C++ preprocessor flags, e.g. -I if you have + headers in a nonstandard directory + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd "$ac_popdir" + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF +libnl configure 1.0-pre6 +generated by GNU Autoconf 2.59 + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by libnl $as_me 1.0-pre6, which was +generated by GNU Autoconf 2.59. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + + + + + + + + + ac_config_headers="$ac_config_headers lib/defs.h" + + +save_CFLAGS="${CFLAGS}" +save_LDFLAGS="${LDFLAGS}" +save_CPPFLAGS="${CPPFLAGS}" + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version &5\"") >&5 + (eval $ac_compiler --version &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v &5\"") >&5 + (eval $ac_compiler -v &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V &5\"") >&5 + (eval $ac_compiler -V &5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $ac_aux_dir/configure" # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL=$ac_install_sh + fi +fi +echo "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + + + +echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset x; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *ccp; + char **p; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + ccp = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++ccp; + p = (char**) ccp; + ccp = (char const *const *) p; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + } +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_const=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6 +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for inline" >&5 +echo $ECHO_N "checking for inline... $ECHO_C" >&6 +if test "${ac_cv_c_inline+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_inline=$ac_kw; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5 +echo "${ECHO_T}$ac_cv_c_inline" >&6 + + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + + +##################################################################### +## +## libm check +## +##################################################################### +M="No " +echo "$as_me:$LINENO: checking for pow in -lm" >&5 +echo $ECHO_N "checking for pow in -lm... $ECHO_C" >&6 +if test "${ac_cv_lib_m_pow+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lm $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pow (); +int +main () +{ +pow (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_m_pow=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_m_pow=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_m_pow" >&5 +echo "${ECHO_T}$ac_cv_lib_m_pow" >&6 +if test $ac_cv_lib_m_pow = yes; then + + LIBM="-lm" + M="Yes" + +else + + echo + echo "*** Error: libm required ***" + echo + exit + +fi + + +##################################################################### +## +## verbose error strings +## +##################################################################### +# Check whether --enable-verbose-errors or --disable-verbose-errors was given. +if test "${enable_verbose_errors+set}" = set; then + enableval="$enable_verbose_errors" + + if test x$enableval = xyes; then + +cat >>confdefs.h <<_ACEOF +#define VERBOSE_ERRORS "1" +_ACEOF + + fi + +fi; + +##################################################################### +## +## compile decisions +## +##################################################################### +COMPILE_LIBNL="Yes " +LIBNL_LIB="$LIBM" + + + + ac_config_files="$ac_config_files Makefile.opts libnl-1.pc" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by libnl $as_me 1.0-pre6, which was +generated by GNU Autoconf 2.59. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to ." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +libnl config.status 1.0-pre6 +configured by $0, generated by GNU Autoconf 2.59, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile.opts" ) CONFIG_FILES="$CONFIG_FILES Makefile.opts" ;; + "libnl-1.pc" ) CONFIG_FILES="$CONFIG_FILES libnl-1.pc" ;; + "lib/defs.h" ) CONFIG_HEADERS="$CONFIG_HEADERS lib/defs.h" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@LIBNL_LIB@,$LIBNL_LIB,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + +# +# CONFIG_HEADER section. +# + +# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where +# NAME is the cpp macro being defined and VALUE is the value it is being given. +# +# ac_d sets the value in "#define NAME VALUE" lines. +ac_dA='s,^\([ ]*\)#\([ ]*define[ ][ ]*\)' +ac_dB='[ ].*$,\1#\2' +ac_dC=' ' +ac_dD=',;t' +# ac_u turns "#undef NAME" without trailing blanks into "#define NAME VALUE". +ac_uA='s,^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)' +ac_uB='$,\1#\2define\3' +ac_uC=' ' +ac_uD=',;t' + +for ac_file in : $CONFIG_HEADERS; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + test x"$ac_file" != x- && { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + # Do quote $f, to prevent DOS paths from being IFS'd. + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + # Remove the trailing spaces. + sed 's/[ ]*$//' $ac_file_inputs >$tmp/in + +_ACEOF + +# Transform confdefs.h into two sed scripts, `conftest.defines' and +# `conftest.undefs', that substitutes the proper values into +# config.h.in to produce config.h. The first handles `#define' +# templates, and the second `#undef' templates. +# And first: Protect against being on the right side of a sed subst in +# config.status. Protect against being in an unquoted here document +# in config.status. +rm -f conftest.defines conftest.undefs +# Using a here document instead of a string reduces the quoting nightmare. +# Putting comments in sed scripts is not portable. +# +# `end' is used to avoid that the second main sed command (meant for +# 0-ary CPP macros) applies to n-ary macro definitions. +# See the Autoconf documentation for `clear'. +cat >confdef2sed.sed <<\_ACEOF +s/[\\&,]/\\&/g +s,[\\$`],\\&,g +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*\)\(([^)]*)\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1\2${ac_dC}\3${ac_dD},gp +t end +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)$,${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD},gp +: end +_ACEOF +# If some macros were called several times there might be several times +# the same #defines, which is useless. Nevertheless, we may not want to +# sort them, since we want the *last* AC-DEFINE to be honored. +uniq confdefs.h | sed -n -f confdef2sed.sed >conftest.defines +sed 's/ac_d/ac_u/g' conftest.defines >conftest.undefs +rm -f confdef2sed.sed + +# This sed command replaces #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +cat >>conftest.undefs <<\_ACEOF +s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */, +_ACEOF + +# Break up conftest.defines because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #define templates only if necessary.' >>$CONFIG_STATUS +echo ' if grep "^[ ]*#[ ]*define" $tmp/in >/dev/null; then' >>$CONFIG_STATUS +echo ' # If there are no defines, we may have an empty if/fi' >>$CONFIG_STATUS +echo ' :' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.defines >/dev/null +do + # Write a limited-size here document to $tmp/defines.sed. + echo ' cat >$tmp/defines.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#define' lines. + echo '/^[ ]*#[ ]*define/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/defines.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.defines >conftest.tail + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines +echo ' fi # grep' >>$CONFIG_STATUS +echo >>$CONFIG_STATUS + +# Break up conftest.undefs because some shells have a limit on the size +# of here documents, and old seds have small limits too (100 cmds). +echo ' # Handle all the #undef templates' >>$CONFIG_STATUS +rm -f conftest.tail +while grep . conftest.undefs >/dev/null +do + # Write a limited-size here document to $tmp/undefs.sed. + echo ' cat >$tmp/undefs.sed <>$CONFIG_STATUS + # Speed up: don't consider the non `#undef' + echo '/^[ ]*#[ ]*undef/!b' >>$CONFIG_STATUS + # Work around the forget-to-reset-the-flag bug. + echo 't clr' >>$CONFIG_STATUS + echo ': clr' >>$CONFIG_STATUS + sed ${ac_max_here_lines}q conftest.undefs >>$CONFIG_STATUS + echo 'CEOF + sed -f $tmp/undefs.sed $tmp/in >$tmp/out + rm -f $tmp/in + mv $tmp/out $tmp/in +' >>$CONFIG_STATUS + sed 1,${ac_max_here_lines}d conftest.undefs >conftest.tail + rm -f conftest.undefs + mv conftest.tail conftest.undefs +done +rm -f conftest.undefs + +cat >>$CONFIG_STATUS <<\_ACEOF + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + echo "/* Generated by configure. */" >$tmp/config.h + else + echo "/* $ac_file. Generated by configure. */" >$tmp/config.h + fi + cat $tmp/in >>$tmp/config.h + rm -f $tmp/in + if test x"$ac_file" != x-; then + if diff $ac_file $tmp/config.h >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + rm -f $ac_file + mv $tmp/config.h $ac_file + fi + else + cat $tmp/config.h + rm -f $tmp/config.h + fi +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + + +##################################################################### +## +## status report +## +##################################################################### +echo " +---------------------------------------------------------------------- +SUMMARY: + +Included in Compilation: + libnl: $COMPILE_LIBNL $LIBNL_LIB + +Dependencies: +bmon: + libm $M (required)" diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..68aa586 --- /dev/null +++ b/configure.in @@ -0,0 +1,80 @@ +# +# configure.in +# +# 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 version 2.1 +# of the License. +# +# Copyright (c) 2003-2006 Thomas Graf +# + +AC_INIT(libnl, 1.0-pre6, tgraf@suug.ch) +AC_CONFIG_HEADER(lib/defs.h) + +save_CFLAGS="${CFLAGS}" +save_LDFLAGS="${LDFLAGS}" +save_CPPFLAGS="${CPPFLAGS}" + +AC_PROG_CC +AC_PROG_INSTALL + +AC_C_CONST +AC_C_INLINE + +##################################################################### +## +## libm check +## +##################################################################### +M="No " +AC_CHECK_LIB(m, pow, +[ + LIBM="-lm" + M="Yes" +],[ + echo + echo "*** Error: libm required ***" + echo + exit +]) + +##################################################################### +## +## verbose error strings +## +##################################################################### +AC_ARG_ENABLE(verbose-errors, +[ --enable-verbose-errors enable verbose errors (debugging)],[ + if test x$enableval = xyes; then + AC_DEFINE_UNQUOTED(VERBOSE_ERRORS,"1",[verbose errors]) + fi +]) + +##################################################################### +## +## compile decisions +## +##################################################################### +COMPILE_LIBNL="Yes " +LIBNL_LIB="$LIBM" + +AC_SUBST(LIBNL_LIB) + +AC_OUTPUT([Makefile.opts libnl-1.pc]) + +##################################################################### +## +## status report +## +##################################################################### +echo " +---------------------------------------------------------------------- +SUMMARY: + +Included in Compilation: + libnl: $COMPILE_LIBNL $LIBNL_LIB + +Dependencies: +bmon: + libm $M (required)" diff --git a/doc/Doxyfile b/doc/Doxyfile new file mode 100644 index 0000000..8f57689 --- /dev/null +++ b/doc/Doxyfile @@ -0,0 +1,1259 @@ +# Doxyfile 1.5.2 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file that +# follow. The default is UTF-8 which is also the encoding used for all text before +# the first occurrence of this tag. Doxygen uses libiconv (or the iconv built into +# libc) for the transcoding. See http://www.gnu.org/software/libiconv for the list of +# possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = libnl + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 1.0-pre6 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ./ + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Finnish, French, German, Greek, Hungarian, +# Italian, Japanese, Japanese-en (Japanese with English messages), Korean, +# Korean-en, Lithuanian, Norwegian, Polish, Portuguese, Romanian, Russian, +# Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = NO + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = YES + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = YES + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = YES + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = arg=\param + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = NO + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = NO + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = NO + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= NO + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = NO + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = NO + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ../lib \ + ../include/netlink + +# This tag can be used to specify the character encoding of the source files that +# doxygen parses. Internally doxygen uses the UTF-8 encoding, which is also the default +# input encoding. Doxygen uses libiconv (or the iconv built into libc) for the transcoding. +# See http://www.gnu.org/software/libiconv for the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +FILE_PATTERNS = *.c \ + *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = SCCS + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the output. +# The symbol name can be a fully qualified name, a word, or if the wildcard * is used, +# a substring. Examples: ANamespace, AClass, AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. Otherwise they will link to the documentstion. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = nl_ + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = NO + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = YES + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see http://www.mcternan.me.uk/mscgen/) to +# produce the chart and insert it in the documentation. The MSCGEN_PATH tag allows you to +# specify the directory where the mscgen tool resides. If left empty the tool is assumed to +# be found in the default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = NO + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = NO + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = NO + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a caller dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable caller graphs for selected +# functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = NO + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen will always +# show the root nodes and its direct children regardless of this setting. + +DOT_GRAPH_MAX_NODES = 50 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..9c34e0f --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,35 @@ +# +# doc/Makefile +# +# 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 version 2.1 +# of the License. +# +# Copyright (c) 2003-2006 Thomas Graf +# + +ifeq ($(shell [ ! -r ../Makefile.opts ] && echo 1),) + include ../Makefile.opts +endif + +export + +.PHONY: all gendoc clean distclean install + +all: + @true + +gendoc: + doxygen Doxyfile + +clean: + @true + +distclean: + $(RM) -f html/* + +install: + @true + +$(DEPS): ../Makefile.opts diff --git a/doc/libnl.css b/doc/libnl.css new file mode 100644 index 0000000..42e3e27 --- /dev/null +++ b/doc/libnl.css @@ -0,0 +1,310 @@ +BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV { + font-family: Geneva, Arial, Helvetica, sans-serif; +} +BODY,TD { + font-size: 90%; +} +H1 { + text-align: center; + font-size: 160%; +} +H2 { + font-size: 120%; +} +H3 { + font-size: 100%; +} +CAPTION { font-weight: bold } +DIV.qindex { + width: 100%; + background-color: #eeeeff; + border: 1px solid #b0b0b0; + text-align: center; + margin: 2px; + padding: 2px; + line-height: 140%; +} +DIV.nav { + width: 100%; + background-color: #eeeeff; + border: 1px solid #b0b0b0; + text-align: center; + margin: 2px; + padding: 2px; + line-height: 140%; +} +DIV.navtab { + background-color: #eeeeff; + border: 1px solid #b0b0b0; + text-align: center; + margin: 2px; + margin-right: 15px; + padding: 2px; +} +TD.navtab { + font-size: 70%; +} +A.qindex { + text-decoration: none; + font-weight: bold; + color: #1A419D; +} +A.qindex:visited { + text-decoration: none; + font-weight: bold; + color: #1A419D +} +A.qindex:hover { + text-decoration: none; + background-color: #ddddff; +} +A.qindexHL { + text-decoration: none; + font-weight: bold; + background-color: #6666cc; + color: #ffffff; + border: 1px double #9295C2; +} +A.qindexHL:hover { + text-decoration: none; + background-color: #6666cc; + color: #ffffff; +} +A.qindexHL:visited { text-decoration: none; background-color: #6666cc; color: #ffffff } +A.el { text-decoration: none; font-weight: bold } +A.elRef { font-weight: bold } +A.code:link { text-decoration: none; font-weight: normal; color: #0000FF} +A.code:visited { text-decoration: none; font-weight: normal; color: #0000FF} +A.codeRef:link { font-weight: normal; color: #0000FF} +A.codeRef:visited { font-weight: normal; color: #0000FF} +A:hover { text-decoration: none; background-color: #f2f2ff } +DL.el { margin-left: -1cm } +.fragment { + font-family: Fixed, monospace; + font-size: 95%; +} +PRE.fragment { + border: 1px solid #CCCCCC; + background-color: #f5f5f5; + margin-top: 4px; + margin-bottom: 4px; + margin-left: 2px; + margin-right: 8px; + padding-left: 6px; + padding-right: 6px; + padding-top: 4px; + padding-bottom: 4px; +} +DIV.ah { background-color: black; font-weight: bold; color: #ffffff; margin-bottom: 3px; margin-top: 3px } +TD.md { background-color: #F4F4FB; font-weight: bold; } +TD.mdPrefix { + background-color: #F4F4FB; + color: #606060; + font-size: 80%; +} +TD.mdname1 { background-color: #F4F4FB; font-weight: bold; color: #602020; } +TD.mdname { background-color: #F4F4FB; font-weight: bold; color: #602020; width: 600px; } +DIV.groupHeader { + margin-left: 16px; + margin-top: 12px; + margin-bottom: 6px; + font-weight: bold; +} +DIV.groupText { margin-left: 16px; font-style: italic; font-size: 90% } +BODY { + background: white; + color: black; + margin-right: 20px; + margin-left: 20px; +} +TD.indexkey { + background-color: #eeeeff; + font-weight: bold; + padding-right : 10px; + padding-top : 2px; + padding-left : 10px; + padding-bottom : 2px; + margin-left : 0px; + margin-right : 0px; + margin-top : 2px; + margin-bottom : 2px; + border: 1px solid #CCCCCC; +} +TD.indexvalue { + background-color: #eeeeff; + font-style: italic; + padding-right : 10px; + padding-top : 2px; + padding-left : 10px; + padding-bottom : 2px; + margin-left : 0px; + margin-right : 0px; + margin-top : 2px; + margin-bottom : 2px; + border: 1px solid #CCCCCC; +} +TR.memlist { + background-color: #f0f0f0; +} +P.formulaDsp { text-align: center; } +IMG.formulaDsp { } +IMG.formulaInl { vertical-align: middle; } +SPAN.keyword { color: #008000 } +SPAN.keywordtype { color: #604020 } +SPAN.keywordflow { color: #e08000 } +SPAN.comment { color: #800000 } +SPAN.preprocessor { color: #806020 } +SPAN.stringliteral { color: #002080 } +SPAN.charliteral { color: #008080 } +.mdTable { + border: 1px solid #868686; + background-color: #F4F4FB; + width: 100%; +} +.mdRow { + padding: 8px 10px; +} +.mdescLeft { + padding: 0px 8px 4px 8px; + font-size: 80%; + font-style: italic; + background-color: #FAFAFA; + border-top: 1px none #E0E0E0; + border-right: 1px none #E0E0E0; + border-bottom: 1px none #E0E0E0; + border-left: 1px none #E0E0E0; + margin: 0px; +} +.mdescRight { + padding: 0px 8px 4px 8px; + font-size: 80%; + font-style: italic; + background-color: #FAFAFA; + border-top: 1px none #E0E0E0; + border-right: 1px none #E0E0E0; + border-bottom: 1px none #E0E0E0; + border-left: 1px none #E0E0E0; + margin: 0px; +} +.memItemLeft { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memItemRight { + padding: 1px 8px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memTemplItemLeft { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: none; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memTemplItemRight { + padding: 1px 8px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: none; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + background-color: #FAFAFA; + font-size: 80%; +} +.memTemplParams { + padding: 1px 0px 0px 8px; + margin: 4px; + border-top-width: 1px; + border-right-width: 1px; + border-bottom-width: 1px; + border-left-width: 1px; + border-top-color: #E0E0E0; + border-right-color: #E0E0E0; + border-bottom-color: #E0E0E0; + border-left-color: #E0E0E0; + border-top-style: solid; + border-right-style: none; + border-bottom-style: none; + border-left-style: none; + color: #606060; + background-color: #FAFAFA; + font-size: 80%; +} +.search { color: #003399; + font-weight: bold; +} +FORM.search { + margin-bottom: 0px; + margin-top: 0px; +} +INPUT.search { font-size: 75%; + color: #000080; + font-weight: normal; + background-color: #eeeeff; +} +TD.tiny { font-size: 75%; +} +a { + color: #252E78; +} +a:visited { + color: #3D2185; +} +.dirtab { padding: 4px; + border-collapse: collapse; + border: 1px solid #b0b0b0; +} +TH.dirtab { background: #eeeeff; + font-weight: bold; +} +HR { height: 1px; + border: none; + border-top: 1px solid black; +} diff --git a/include/Makefile b/include/Makefile new file mode 100644 index 0000000..a2b23e0 --- /dev/null +++ b/include/Makefile @@ -0,0 +1,38 @@ +# +# include/Makefile +# +# 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 version 2.1 +# of the License. +# +# Copyright (c) 2003-2006 Thomas Graf +# + +ifeq ($(shell [ ! -r ../Makefile.opts ] && echo 1),) + include ../Makefile.opts +endif + +.PHONY: all clean install + +all: + @true + +clean: + @true + +distclean: + @true + +install: + mkdir -p $(DESTDIR)$(includedir)/netlink/route/sch/ + mkdir -p $(DESTDIR)$(includedir)/netlink/route/cls/ + mkdir -p $(DESTDIR)$(includedir)/netlink/genl/ + mkdir -p $(DESTDIR)$(includedir)/netlink/fib_lookup/ + install -m 0644 netlink/*.h $(DESTDIR)$(includedir)/netlink/ + install -m 0644 netlink/route/*.h $(DESTDIR)$(includedir)/netlink/route/ + install -m 0644 netlink/route/sch/*.h $(DESTDIR)$(includedir)/netlink/route/sch/ + install -m 0644 netlink/route/cls/*.h $(DESTDIR)$(includedir)/netlink/route/cls/ + install -m 0644 netlink/genl/*.h $(DESTDIR)$(includedir)/netlink/genl/ + install -m 0644 netlink/fib_lookup/*.h $(DESTDIR)$(includedir)/netlink/fib_lookup/ + diff --git a/include/linux/gen_stats.h b/include/linux/gen_stats.h new file mode 100644 index 0000000..ca60fc1 --- /dev/null +++ b/include/linux/gen_stats.h @@ -0,0 +1,60 @@ +#ifndef __LINUX_GEN_STATS_H +#define __LINUX_GEN_STATS_H + +enum { + TCA_STATS_UNSPEC, + TCA_STATS_BASIC, + TCA_STATS_RATE_EST, + TCA_STATS_QUEUE, + TCA_STATS_APP, + __TCA_STATS_MAX, +}; +#define TCA_STATS_MAX (__TCA_STATS_MAX - 1) + +/** + * @bytes: number of seen bytes + * @packets: number of seen packets + */ +struct gnet_stats_basic +{ + __u64 bytes; + __u32 packets; +}; + +/** + * @bps: current byte rate + * @pps: current packet rate + */ +struct gnet_stats_rate_est +{ + __u32 bps; + __u32 pps; +}; + +/** + * @qlen: queue length + * @backlog: backlog size of queue + * @drops: number of dropped packets + * @requeues: number of requeues + */ +struct gnet_stats_queue +{ + __u32 qlen; + __u32 backlog; + __u32 drops; + __u32 requeues; + __u32 overlimits; +}; + +/** + * @interval: sampling period + * @ewma_log: the log of measurement window weight + */ +struct gnet_estimator +{ + signed char interval; + unsigned char ewma_log; +}; + + +#endif /* __LINUX_GEN_STATS_H */ diff --git a/include/linux/genetlink.h b/include/linux/genetlink.h new file mode 100644 index 0000000..f7a9377 --- /dev/null +++ b/include/linux/genetlink.h @@ -0,0 +1,69 @@ +#ifndef __LINUX_GENERIC_NETLINK_H +#define __LINUX_GENERIC_NETLINK_H + +#include + +#define GENL_NAMSIZ 16 /* length of family name */ + +#define GENL_MIN_ID NLMSG_MIN_TYPE +#define GENL_MAX_ID 1023 + +struct genlmsghdr { + __u8 cmd; + __u8 version; + __u16 reserved; +}; + +#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr)) + +#define GENL_ADMIN_PERM 0x01 +#define GENL_CMD_CAP_DO 0x02 +#define GENL_CMD_CAP_DUMP 0x04 +#define GENL_CMD_CAP_HASPOL 0x08 + +/* + * List of reserved static generic netlink identifiers: + */ +#define GENL_ID_GENERATE 0 +#define GENL_ID_CTRL NLMSG_MIN_TYPE + +/************************************************************************** + * Controller + **************************************************************************/ + +enum { + CTRL_CMD_UNSPEC, + CTRL_CMD_NEWFAMILY, + CTRL_CMD_DELFAMILY, + CTRL_CMD_GETFAMILY, + CTRL_CMD_NEWOPS, + CTRL_CMD_DELOPS, + CTRL_CMD_GETOPS, + __CTRL_CMD_MAX, +}; + +#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1) + +enum { + CTRL_ATTR_UNSPEC, + CTRL_ATTR_FAMILY_ID, + CTRL_ATTR_FAMILY_NAME, + CTRL_ATTR_VERSION, + CTRL_ATTR_HDRSIZE, + CTRL_ATTR_MAXATTR, + CTRL_ATTR_OPS, + __CTRL_ATTR_MAX, +}; + +#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1) + +enum { + CTRL_ATTR_OP_UNSPEC, + CTRL_ATTR_OP_ID, + CTRL_ATTR_OP_FLAGS, + __CTRL_ATTR_OP_MAX, +}; + +#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1) + +#endif /* __LINUX_GENERIC_NETLINK_H */ diff --git a/include/linux/if.h b/include/linux/if.h new file mode 100644 index 0000000..9128570 --- /dev/null +++ b/include/linux/if.h @@ -0,0 +1,102 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for the INET interface module. + * + * Version: @(#)if.h 1.0.2 04/18/93 + * + * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988 + * Ross Biro + * Fred N. van Kempen, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _LINUX_IF_H +#define _LINUX_IF_H + +#define IFNAMSIZ 16 + +/* Standard interface flags (netdevice->flags). */ +#define IFF_UP 0x1 /* interface is up */ +#define IFF_BROADCAST 0x2 /* broadcast address valid */ +#define IFF_DEBUG 0x4 /* turn on debugging */ +#define IFF_LOOPBACK 0x8 /* is a loopback net */ +#define IFF_POINTOPOINT 0x10 /* interface is has p-p link */ +#define IFF_NOTRAILERS 0x20 /* avoid use of trailers */ +#define IFF_RUNNING 0x40 /* interface running and carrier ok */ +#define IFF_NOARP 0x80 /* no ARP protocol */ +#define IFF_PROMISC 0x100 /* receive all packets */ +#define IFF_ALLMULTI 0x200 /* receive all multicast packets*/ + +#define IFF_MASTER 0x400 /* master of a load balancer */ +#define IFF_SLAVE 0x800 /* slave of a load balancer */ + +#define IFF_MULTICAST 0x1000 /* Supports multicast */ + +#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_MASTER|IFF_SLAVE|IFF_RUNNING) + +#define IFF_PORTSEL 0x2000 /* can set media type */ +#define IFF_AUTOMEDIA 0x4000 /* auto media select active */ +#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses*/ +#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ +#define IFF_DORMANT 0x20000 /* driver signals dormant */ + +/* Private (from user) interface flags (netdevice->priv_flags). */ +#define IFF_802_1Q_VLAN 0x1 /* 802.1Q VLAN device. */ +#define IFF_EBRIDGE 0x2 /* Ethernet bridging device. */ + +#define IF_GET_IFACE 0x0001 /* for querying only */ +#define IF_GET_PROTO 0x0002 + +/* For definitions see hdlc.h */ +#define IF_IFACE_V35 0x1000 /* V.35 serial interface */ +#define IF_IFACE_V24 0x1001 /* V.24 serial interface */ +#define IF_IFACE_X21 0x1002 /* X.21 serial interface */ +#define IF_IFACE_T1 0x1003 /* T1 telco serial interface */ +#define IF_IFACE_E1 0x1004 /* E1 telco serial interface */ +#define IF_IFACE_SYNC_SERIAL 0x1005 /* can't be set by software */ +#define IF_IFACE_X21D 0x1006 /* X.21 Dual Clocking (FarSite) */ + +/* For definitions see hdlc.h */ +#define IF_PROTO_HDLC 0x2000 /* raw HDLC protocol */ +#define IF_PROTO_PPP 0x2001 /* PPP protocol */ +#define IF_PROTO_CISCO 0x2002 /* Cisco HDLC protocol */ +#define IF_PROTO_FR 0x2003 /* Frame Relay protocol */ +#define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */ +#define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */ +#define IF_PROTO_X25 0x2006 /* X.25 */ +#define IF_PROTO_HDLC_ETH 0x2007 /* raw HDLC, Ethernet emulation */ +#define IF_PROTO_FR_ADD_ETH_PVC 0x2008 /* Create FR Ethernet-bridged PVC */ +#define IF_PROTO_FR_DEL_ETH_PVC 0x2009 /* Delete FR Ethernet-bridged PVC */ +#define IF_PROTO_FR_PVC 0x200A /* for reading PVC status */ +#define IF_PROTO_FR_ETH_PVC 0x200B +#define IF_PROTO_RAW 0x200C /* RAW Socket */ + + +/* + * Device mapping structure. I'd just gone off and designed a + * beautiful scheme using only loadable modules with arguments + * for driver options and along come the PCMCIA people 8) + * + * Ah well. The get() side of this is good for WDSETUP, and it'll + * be handy for debugging things. The set side is fine for now and + * being very small might be worth keeping for clean configuration. + */ + +struct ifmap +{ + unsigned long mem_start; + unsigned long mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; + /* 3 bytes spare */ +}; + +#endif /* _LINUX_IF_H */ diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h new file mode 100644 index 0000000..43f3bed --- /dev/null +++ b/include/linux/if_addr.h @@ -0,0 +1,62 @@ +#ifndef __LINUX_IF_ADDR_H +#define __LINUX_IF_ADDR_H + +#include + +struct ifaddrmsg +{ + __u8 ifa_family; + __u8 ifa_prefixlen; /* The prefix length */ + __u8 ifa_flags; /* Flags */ + __u8 ifa_scope; /* Address scope */ + __u32 ifa_index; /* Link index */ +}; + +/* + * Important comment: + * IFA_ADDRESS is prefix address, rather than local interface address. + * It makes no difference for normally configured broadcast interfaces, + * but for point-to-point IFA_ADDRESS is DESTINATION address, + * local address is supplied in IFA_LOCAL attribute. + */ +enum +{ + IFA_UNSPEC, + IFA_ADDRESS, + IFA_LOCAL, + IFA_LABEL, + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO, + IFA_MULTICAST, + __IFA_MAX, +}; + +#define IFA_MAX (__IFA_MAX - 1) + +/* ifa_flags */ +#define IFA_F_SECONDARY 0x01 +#define IFA_F_TEMPORARY IFA_F_SECONDARY + +#define IFA_F_NODAD 0x02 +#define IFA_F_OPTIMISTIC 0x04 +#define IFA_F_HOMEADDRESS 0x10 +#define IFA_F_DEPRECATED 0x20 +#define IFA_F_TENTATIVE 0x40 +#define IFA_F_PERMANENT 0x80 + +struct ifa_cacheinfo +{ + __u32 ifa_prefered; + __u32 ifa_valid; + __u32 cstamp; /* created timestamp, hundredths of seconds */ + __u32 tstamp; /* updated timestamp, hundredths of seconds */ +}; + +/* backwards compatibility for userspace */ +#ifndef __KERNEL__ +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) +#endif + +#endif diff --git a/include/linux/if_arp.h b/include/linux/if_arp.h new file mode 100644 index 0000000..1c7417b --- /dev/null +++ b/include/linux/if_arp.h @@ -0,0 +1,149 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for the ARP (RFC 826) protocol. + * + * Version: @(#)if_arp.h 1.0.1 04/16/93 + * + * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988 + * Portions taken from the KA9Q/NOS (v2.00m PA0GRI) source. + * Ross Biro + * Fred N. van Kempen, + * Florian La Roche, + * Jonathan Layes + * Arnaldo Carvalho de Melo ARPHRD_HWX25 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#ifndef _LINUX_IF_ARP_H +#define _LINUX_IF_ARP_H + +/* ARP protocol HARDWARE identifiers. */ +#define ARPHRD_NETROM 0 /* from KA9Q: NET/ROM pseudo */ +#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */ +#define ARPHRD_EETHER 2 /* Experimental Ethernet */ +#define ARPHRD_AX25 3 /* AX.25 Level 2 */ +#define ARPHRD_PRONET 4 /* PROnet token ring */ +#define ARPHRD_CHAOS 5 /* Chaosnet */ +#define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet/TR/TB */ +#define ARPHRD_ARCNET 7 /* ARCnet */ +#define ARPHRD_APPLETLK 8 /* APPLEtalk */ +#define ARPHRD_DLCI 15 /* Frame Relay DLCI */ +#define ARPHRD_ATM 19 /* ATM */ +#define ARPHRD_METRICOM 23 /* Metricom STRIP (new IANA id) */ +#define ARPHRD_IEEE1394 24 /* IEEE 1394 IPv4 - RFC 2734 */ +#define ARPHRD_EUI64 27 /* EUI-64 */ +#define ARPHRD_INFINIBAND 32 /* InfiniBand */ + +/* Dummy types for non ARP hardware */ +#define ARPHRD_SLIP 256 +#define ARPHRD_CSLIP 257 +#define ARPHRD_SLIP6 258 +#define ARPHRD_CSLIP6 259 +#define ARPHRD_RSRVD 260 /* Notional KISS type */ +#define ARPHRD_ADAPT 264 +#define ARPHRD_ROSE 270 +#define ARPHRD_X25 271 /* CCITT X.25 */ +#define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */ +#define ARPHRD_PPP 512 +#define ARPHRD_CISCO 513 /* Cisco HDLC */ +#define ARPHRD_HDLC ARPHRD_CISCO +#define ARPHRD_LAPB 516 /* LAPB */ +#define ARPHRD_DDCMP 517 /* Digital's DDCMP protocol */ +#define ARPHRD_RAWHDLC 518 /* Raw HDLC */ + +#define ARPHRD_TUNNEL 768 /* IPIP tunnel */ +#define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */ +#define ARPHRD_FRAD 770 /* Frame Relay Access Device */ +#define ARPHRD_SKIP 771 /* SKIP vif */ +#define ARPHRD_LOOPBACK 772 /* Loopback device */ +#define ARPHRD_LOCALTLK 773 /* Localtalk device */ +#define ARPHRD_FDDI 774 /* Fiber Distributed Data Interface */ +#define ARPHRD_BIF 775 /* AP1000 BIF */ +#define ARPHRD_SIT 776 /* sit0 device - IPv6-in-IPv4 */ +#define ARPHRD_IPDDP 777 /* IP over DDP tunneller */ +#define ARPHRD_IPGRE 778 /* GRE over IP */ +#define ARPHRD_PIMREG 779 /* PIMSM register interface */ +#define ARPHRD_HIPPI 780 /* High Performance Parallel Interface */ +#define ARPHRD_ASH 781 /* Nexus 64Mbps Ash */ +#define ARPHRD_ECONET 782 /* Acorn Econet */ +#define ARPHRD_IRDA 783 /* Linux-IrDA */ +/* ARP works differently on different FC media .. so */ +#define ARPHRD_FCPP 784 /* Point to point fibrechannel */ +#define ARPHRD_FCAL 785 /* Fibrechannel arbitrated loop */ +#define ARPHRD_FCPL 786 /* Fibrechannel public loop */ +#define ARPHRD_FCFABRIC 787 /* Fibrechannel fabric */ + /* 787->799 reserved for fibrechannel media types */ +#define ARPHRD_IEEE802_TR 800 /* Magic type ident for TR */ +#define ARPHRD_IEEE80211 801 /* IEEE 802.11 */ +#define ARPHRD_IEEE80211_PRISM 802 /* IEEE 802.11 + Prism2 header */ +#define ARPHRD_IEEE80211_RADIOTAP 803 /* IEEE 802.11 + radiotap header */ + +#define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */ +#define ARPHRD_NONE 0xFFFE /* zero header length */ + +/* ARP protocol opcodes. */ +#define ARPOP_REQUEST 1 /* ARP request */ +#define ARPOP_REPLY 2 /* ARP reply */ +#define ARPOP_RREQUEST 3 /* RARP request */ +#define ARPOP_RREPLY 4 /* RARP reply */ +#define ARPOP_InREQUEST 8 /* InARP request */ +#define ARPOP_InREPLY 9 /* InARP reply */ +#define ARPOP_NAK 10 /* (ATM)ARP NAK */ + + +/* ARP ioctl request. */ +struct arpreq { + struct sockaddr arp_pa; /* protocol address */ + struct sockaddr arp_ha; /* hardware address */ + int arp_flags; /* flags */ + struct sockaddr arp_netmask; /* netmask (only for proxy arps) */ + char arp_dev[16]; +}; + +struct arpreq_old { + struct sockaddr arp_pa; /* protocol address */ + struct sockaddr arp_ha; /* hardware address */ + int arp_flags; /* flags */ + struct sockaddr arp_netmask; /* netmask (only for proxy arps) */ +}; + +/* ARP Flag values. */ +#define ATF_COM 0x02 /* completed entry (ha valid) */ +#define ATF_PERM 0x04 /* permanent entry */ +#define ATF_PUBL 0x08 /* publish entry */ +#define ATF_USETRAILERS 0x10 /* has requested trailers */ +#define ATF_NETMASK 0x20 /* want to use a netmask (only + for proxy entries) */ +#define ATF_DONTPUB 0x40 /* don't answer this addresses */ + +/* + * This structure defines an ethernet arp header. + */ + +struct arphdr +{ + unsigned short ar_hrd; /* format of hardware address */ + unsigned short ar_pro; /* format of protocol address */ + unsigned char ar_hln; /* length of hardware address */ + unsigned char ar_pln; /* length of protocol address */ + unsigned short ar_op; /* ARP opcode (command) */ + +#if 0 + /* + * Ethernet looks like this : This bit is variable sized however... + */ + unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */ + unsigned char ar_sip[4]; /* sender IP address */ + unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ + unsigned char ar_tip[4]; /* target IP address */ +#endif + +}; + +#endif /* _LINUX_IF_ARP_H */ diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h new file mode 100644 index 0000000..ab7df16 --- /dev/null +++ b/include/linux/if_ether.h @@ -0,0 +1,106 @@ +/* + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * Global definitions for the Ethernet IEEE 802.3 interface. + * + * Version: @(#)if_ether.h 1.0.1a 02/08/94 + * + * Author: Fred N. van Kempen, + * Donald Becker, + * Alan Cox, + * Steve Whitehouse, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_IF_ETHER_H +#define _LINUX_IF_ETHER_H + +/* + * IEEE 802.3 Ethernet magic constants. The frame sizes omit the preamble + * and FCS/CRC (frame check sequence). + */ + +#define ETH_ALEN 6 /* Octets in one ethernet addr */ +#define ETH_HLEN 14 /* Total octets in header. */ +#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */ +#define ETH_DATA_LEN 1500 /* Max. octets in payload */ +#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */ + +/* + * These are the defined Ethernet Protocol ID's. + */ + +#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */ +#define ETH_P_PUP 0x0200 /* Xerox PUP packet */ +#define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */ +#define ETH_P_IP 0x0800 /* Internet Protocol packet */ +#define ETH_P_X25 0x0805 /* CCITT X.25 */ +#define ETH_P_ARP 0x0806 /* Address Resolution packet */ +#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 Ethernet Packet [ NOT AN OFFICIALLY REGISTERED ID ] */ +#define ETH_P_IEEEPUP 0x0a00 /* Xerox IEEE802.3 PUP packet */ +#define ETH_P_IEEEPUPAT 0x0a01 /* Xerox IEEE802.3 PUP Addr Trans packet */ +#define ETH_P_DEC 0x6000 /* DEC Assigned proto */ +#define ETH_P_DNA_DL 0x6001 /* DEC DNA Dump/Load */ +#define ETH_P_DNA_RC 0x6002 /* DEC DNA Remote Console */ +#define ETH_P_DNA_RT 0x6003 /* DEC DNA Routing */ +#define ETH_P_LAT 0x6004 /* DEC LAT */ +#define ETH_P_DIAG 0x6005 /* DEC Diagnostics */ +#define ETH_P_CUST 0x6006 /* DEC Customer use */ +#define ETH_P_SCA 0x6007 /* DEC Systems Comms Arch */ +#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */ +#define ETH_P_ATALK 0x809B /* Appletalk DDP */ +#define ETH_P_AARP 0x80F3 /* Appletalk AARP */ +#define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ +#define ETH_P_IPX 0x8137 /* IPX over DIX */ +#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ +#define ETH_P_WCCP 0x883E /* Web-cache coordination protocol + * defined in draft-wilson-wrec-wccp-v2-00.txt */ +#define ETH_P_PPP_DISC 0x8863 /* PPPoE discovery messages */ +#define ETH_P_PPP_SES 0x8864 /* PPPoE session messages */ +#define ETH_P_MPLS_UC 0x8847 /* MPLS Unicast traffic */ +#define ETH_P_MPLS_MC 0x8848 /* MPLS Multicast traffic */ +#define ETH_P_ATMMPOA 0x884c /* MultiProtocol Over ATM */ +#define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport + * over Ethernet + */ +#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */ + +/* + * Non DIX types. Won't clash for 1500 types. + */ + +#define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */ +#define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */ +#define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */ +#define ETH_P_802_2 0x0004 /* 802.2 frames */ +#define ETH_P_SNAP 0x0005 /* Internal only */ +#define ETH_P_DDCMP 0x0006 /* DEC DDCMP: Internal only */ +#define ETH_P_WAN_PPP 0x0007 /* Dummy type for WAN PPP frames*/ +#define ETH_P_PPP_MP 0x0008 /* Dummy type for PPP MP frames */ +#define ETH_P_LOCALTALK 0x0009 /* Localtalk pseudo type */ +#define ETH_P_PPPTALK 0x0010 /* Dummy type for Atalk over PPP*/ +#define ETH_P_TR_802_2 0x0011 /* 802.2 frames */ +#define ETH_P_MOBITEX 0x0015 /* Mobitex (kaz@cafe.net) */ +#define ETH_P_CONTROL 0x0016 /* Card specific control frames */ +#define ETH_P_IRDA 0x0017 /* Linux-IrDA */ +#define ETH_P_ECONET 0x0018 /* Acorn Econet */ +#define ETH_P_HDLC 0x0019 /* HDLC frames */ +#define ETH_P_ARCNET 0x001A /* 1A for ArcNet :-) */ + +/* + * This is an Ethernet frame header. + */ + +struct ethhdr { + unsigned char h_dest[ETH_ALEN]; /* destination eth addr */ + unsigned char h_source[ETH_ALEN]; /* source ether addr */ + unsigned short h_proto; /* packet type ID field */ +} __attribute__((packed)); + +#endif /* _LINUX_IF_ETHER_H */ diff --git a/include/linux/if_link.h b/include/linux/if_link.h new file mode 100644 index 0000000..604c243 --- /dev/null +++ b/include/linux/if_link.h @@ -0,0 +1,143 @@ +#ifndef _LINUX_IF_LINK_H +#define _LINUX_IF_LINK_H + +#include + +/* The struct should be in sync with struct net_device_stats */ +struct rtnl_link_stats +{ + __u32 rx_packets; /* total packets received */ + __u32 tx_packets; /* total packets transmitted */ + __u32 rx_bytes; /* total bytes received */ + __u32 tx_bytes; /* total bytes transmitted */ + __u32 rx_errors; /* bad packets received */ + __u32 tx_errors; /* packet transmit problems */ + __u32 rx_dropped; /* no space in linux buffers */ + __u32 tx_dropped; /* no space available in linux */ + __u32 multicast; /* multicast packets received */ + __u32 collisions; + + /* detailed rx_errors: */ + __u32 rx_length_errors; + __u32 rx_over_errors; /* receiver ring buff overflow */ + __u32 rx_crc_errors; /* recved pkt with crc error */ + __u32 rx_frame_errors; /* recv'd frame alignment error */ + __u32 rx_fifo_errors; /* recv'r fifo overrun */ + __u32 rx_missed_errors; /* receiver missed packet */ + + /* detailed tx_errors */ + __u32 tx_aborted_errors; + __u32 tx_carrier_errors; + __u32 tx_fifo_errors; + __u32 tx_heartbeat_errors; + __u32 tx_window_errors; + + /* for cslip etc */ + __u32 rx_compressed; + __u32 tx_compressed; +}; + +/* The struct should be in sync with struct ifmap */ +struct rtnl_link_ifmap +{ + __u64 mem_start; + __u64 mem_end; + __u64 base_addr; + __u16 irq; + __u8 dma; + __u8 port; +}; + +enum +{ + IFLA_UNSPEC, + IFLA_ADDRESS, + IFLA_BROADCAST, + IFLA_IFNAME, + IFLA_MTU, + IFLA_LINK, + IFLA_QDISC, + IFLA_STATS, + IFLA_COST, +#define IFLA_COST IFLA_COST + IFLA_PRIORITY, +#define IFLA_PRIORITY IFLA_PRIORITY + IFLA_MASTER, +#define IFLA_MASTER IFLA_MASTER + IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */ +#define IFLA_WIRELESS IFLA_WIRELESS + IFLA_PROTINFO, /* Protocol specific information for a link */ +#define IFLA_PROTINFO IFLA_PROTINFO + IFLA_TXQLEN, +#define IFLA_TXQLEN IFLA_TXQLEN + IFLA_MAP, +#define IFLA_MAP IFLA_MAP + IFLA_WEIGHT, +#define IFLA_WEIGHT IFLA_WEIGHT + IFLA_OPERSTATE, + IFLA_LINKMODE, + __IFLA_MAX +}; + + +#define IFLA_MAX (__IFLA_MAX - 1) + +/* backwards compatibility for userspace */ +#ifndef __KERNEL__ +#define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) +#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) +#endif + +/* ifi_flags. + + IFF_* flags. + + The only change is: + IFF_LOOPBACK, IFF_BROADCAST and IFF_POINTOPOINT are + more not changeable by user. They describe link media + characteristics and set by device driver. + + Comments: + - Combination IFF_BROADCAST|IFF_POINTOPOINT is invalid + - If neither of these three flags are set; + the interface is NBMA. + + - IFF_MULTICAST does not mean anything special: + multicasts can be used on all not-NBMA links. + IFF_MULTICAST means that this media uses special encapsulation + for multicast frames. Apparently, all IFF_POINTOPOINT and + IFF_BROADCAST devices are able to use multicasts too. + */ + +/* IFLA_LINK. + For usual devices it is equal ifi_index. + If it is a "virtual interface" (f.e. tunnel), ifi_link + can point to real physical interface (f.e. for bandwidth calculations), + or maybe 0, what means, that real media is unknown (usual + for IPIP tunnels, when route to endpoint is allowed to change) + */ + +/* Subtype attributes for IFLA_PROTINFO */ +enum +{ + IFLA_INET6_UNSPEC, + IFLA_INET6_FLAGS, /* link flags */ + IFLA_INET6_CONF, /* sysctl parameters */ + IFLA_INET6_STATS, /* statistics */ + IFLA_INET6_MCAST, /* MC things. What of them? */ + IFLA_INET6_CACHEINFO, /* time values and max reasm size */ + IFLA_INET6_ICMP6STATS, /* statistics (icmpv6) */ + __IFLA_INET6_MAX +}; + +#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1) + +struct ifla_cacheinfo +{ + __u32 max_reasm_len; + __u32 tstamp; /* ipv6InterfaceTable updated timestamp */ + __u32 reachable_time; + __u32 retrans_time; +}; + +#endif /* _LINUX_IF_LINK_H */ diff --git a/include/linux/ip_mp_alg.h b/include/linux/ip_mp_alg.h new file mode 100644 index 0000000..e234e20 --- /dev/null +++ b/include/linux/ip_mp_alg.h @@ -0,0 +1,22 @@ +/* ip_mp_alg.h: IPV4 multipath algorithm support, user-visible values. + * + * Copyright (C) 2004, 2005 Einar Lueck + * Copyright (C) 2005 David S. Miller + */ + +#ifndef _LINUX_IP_MP_ALG_H +#define _LINUX_IP_MP_ALG_H + +enum ip_mp_alg { + IP_MP_ALG_NONE, + IP_MP_ALG_RR, + IP_MP_ALG_DRR, + IP_MP_ALG_RANDOM, + IP_MP_ALG_WRANDOM, + __IP_MP_ALG_MAX +}; + +#define IP_MP_ALG_MAX (__IP_MP_ALG_MAX - 1) + +#endif /* _LINUX_IP_MP_ALG_H */ + diff --git a/include/linux/neighbour.h b/include/linux/neighbour.h new file mode 100644 index 0000000..bd3bbf6 --- /dev/null +++ b/include/linux/neighbour.h @@ -0,0 +1,159 @@ +#ifndef __LINUX_NEIGHBOUR_H +#define __LINUX_NEIGHBOUR_H + +#include + +struct ndmsg +{ + __u8 ndm_family; + __u8 ndm_pad1; + __u16 ndm_pad2; + __s32 ndm_ifindex; + __u16 ndm_state; + __u8 ndm_flags; + __u8 ndm_type; +}; + +enum +{ + NDA_UNSPEC, + NDA_DST, + NDA_LLADDR, + NDA_CACHEINFO, + NDA_PROBES, + __NDA_MAX +}; + +#define NDA_MAX (__NDA_MAX - 1) + +/* + * Neighbor Cache Entry Flags + */ + +#define NTF_PROXY 0x08 /* == ATF_PUBL */ +#define NTF_ROUTER 0x80 + +/* + * Neighbor Cache Entry States. + */ + +#define NUD_INCOMPLETE 0x01 +#define NUD_REACHABLE 0x02 +#define NUD_STALE 0x04 +#define NUD_DELAY 0x08 +#define NUD_PROBE 0x10 +#define NUD_FAILED 0x20 + +/* Dummy states */ +#define NUD_NOARP 0x40 +#define NUD_PERMANENT 0x80 +#define NUD_NONE 0x00 + +/* NUD_NOARP & NUD_PERMANENT are pseudostates, they never change + and make no address resolution or NUD. + NUD_PERMANENT is also cannot be deleted by garbage collectors. + */ + +struct nda_cacheinfo +{ + __u32 ndm_confirmed; + __u32 ndm_used; + __u32 ndm_updated; + __u32 ndm_refcnt; +}; + +/***************************************************************** + * Neighbour tables specific messages. + * + * To retrieve the neighbour tables send RTM_GETNEIGHTBL with the + * NLM_F_DUMP flag set. Every neighbour table configuration is + * spread over multiple messages to avoid running into message + * size limits on systems with many interfaces. The first message + * in the sequence transports all not device specific data such as + * statistics, configuration, and the default parameter set. + * This message is followed by 0..n messages carrying device + * specific parameter sets. + * Although the ordering should be sufficient, NDTA_NAME can be + * used to identify sequences. The initial message can be identified + * by checking for NDTA_CONFIG. The device specific messages do + * not contain this TLV but have NDTPA_IFINDEX set to the + * corresponding interface index. + * + * To change neighbour table attributes, send RTM_SETNEIGHTBL + * with NDTA_NAME set. Changeable attribute include NDTA_THRESH[1-3], + * NDTA_GC_INTERVAL, and all TLVs in NDTA_PARMS unless marked + * otherwise. Device specific parameter sets can be changed by + * setting NDTPA_IFINDEX to the interface index of the corresponding + * device. + ****/ + +struct ndt_stats +{ + __u64 ndts_allocs; + __u64 ndts_destroys; + __u64 ndts_hash_grows; + __u64 ndts_res_failed; + __u64 ndts_lookups; + __u64 ndts_hits; + __u64 ndts_rcv_probes_mcast; + __u64 ndts_rcv_probes_ucast; + __u64 ndts_periodic_gc_runs; + __u64 ndts_forced_gc_runs; +}; + +enum { + NDTPA_UNSPEC, + NDTPA_IFINDEX, /* u32, unchangeable */ + NDTPA_REFCNT, /* u32, read-only */ + NDTPA_REACHABLE_TIME, /* u64, read-only, msecs */ + NDTPA_BASE_REACHABLE_TIME, /* u64, msecs */ + NDTPA_RETRANS_TIME, /* u64, msecs */ + NDTPA_GC_STALETIME, /* u64, msecs */ + NDTPA_DELAY_PROBE_TIME, /* u64, msecs */ + NDTPA_QUEUE_LEN, /* u32 */ + NDTPA_APP_PROBES, /* u32 */ + NDTPA_UCAST_PROBES, /* u32 */ + NDTPA_MCAST_PROBES, /* u32 */ + NDTPA_ANYCAST_DELAY, /* u64, msecs */ + NDTPA_PROXY_DELAY, /* u64, msecs */ + NDTPA_PROXY_QLEN, /* u32 */ + NDTPA_LOCKTIME, /* u64, msecs */ + __NDTPA_MAX +}; +#define NDTPA_MAX (__NDTPA_MAX - 1) + +struct ndtmsg +{ + __u8 ndtm_family; + __u8 ndtm_pad1; + __u16 ndtm_pad2; +}; + +struct ndt_config +{ + __u16 ndtc_key_len; + __u16 ndtc_entry_size; + __u32 ndtc_entries; + __u32 ndtc_last_flush; /* delta to now in msecs */ + __u32 ndtc_last_rand; /* delta to now in msecs */ + __u32 ndtc_hash_rnd; + __u32 ndtc_hash_mask; + __u32 ndtc_hash_chain_gc; + __u32 ndtc_proxy_qlen; +}; + +enum { + NDTA_UNSPEC, + NDTA_NAME, /* char *, unchangeable */ + NDTA_THRESH1, /* u32 */ + NDTA_THRESH2, /* u32 */ + NDTA_THRESH3, /* u32 */ + NDTA_CONFIG, /* struct ndt_config, read-only */ + NDTA_PARMS, /* nested TLV NDTPA_* */ + NDTA_STATS, /* struct ndt_stats, read-only */ + NDTA_GC_INTERVAL, /* u64, msecs */ + __NDTA_MAX +}; +#define NDTA_MAX (__NDTA_MAX - 1) + +#endif diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h new file mode 100644 index 0000000..1e59984 --- /dev/null +++ b/include/linux/netfilter/nfnetlink.h @@ -0,0 +1,60 @@ +#ifndef _NFNETLINK_H +#define _NFNETLINK_H +#include + +#ifndef __KERNEL__ +/* nfnetlink groups: Up to 32 maximum - backwards compatibility for userspace */ +#define NF_NETLINK_CONNTRACK_NEW 0x00000001 +#define NF_NETLINK_CONNTRACK_UPDATE 0x00000002 +#define NF_NETLINK_CONNTRACK_DESTROY 0x00000004 +#define NF_NETLINK_CONNTRACK_EXP_NEW 0x00000008 +#define NF_NETLINK_CONNTRACK_EXP_UPDATE 0x00000010 +#define NF_NETLINK_CONNTRACK_EXP_DESTROY 0x00000020 +#endif + +enum nfnetlink_groups { + NFNLGRP_NONE, +#define NFNLGRP_NONE NFNLGRP_NONE + NFNLGRP_CONNTRACK_NEW, +#define NFNLGRP_CONNTRACK_NEW NFNLGRP_CONNTRACK_NEW + NFNLGRP_CONNTRACK_UPDATE, +#define NFNLGRP_CONNTRACK_UPDATE NFNLGRP_CONNTRACK_UPDATE + NFNLGRP_CONNTRACK_DESTROY, +#define NFNLGRP_CONNTRACK_DESTROY NFNLGRP_CONNTRACK_DESTROY + NFNLGRP_CONNTRACK_EXP_NEW, +#define NFNLGRP_CONNTRACK_EXP_NEW NFNLGRP_CONNTRACK_EXP_NEW + NFNLGRP_CONNTRACK_EXP_UPDATE, +#define NFNLGRP_CONNTRACK_EXP_UPDATE NFNLGRP_CONNTRACK_EXP_UPDATE + NFNLGRP_CONNTRACK_EXP_DESTROY, +#define NFNLGRP_CONNTRACK_EXP_DESTROY NFNLGRP_CONNTRACK_EXP_DESTROY + __NFNLGRP_MAX, +}; +#define NFNLGRP_MAX (__NFNLGRP_MAX - 1) + +/* General form of address family dependent message. + */ +struct nfgenmsg { + u_int8_t nfgen_family; /* AF_xxx */ + u_int8_t version; /* nfnetlink version */ + __be16 res_id; /* resource id */ +}; + +#define NFNETLINK_V0 0 + +/* netfilter netlink message types are split in two pieces: + * 8 bit subsystem, 8bit operation. + */ + +#define NFNL_SUBSYS_ID(x) ((x & 0xff00) >> 8) +#define NFNL_MSG_TYPE(x) (x & 0x00ff) + +/* No enum here, otherwise __stringify() trick of MODULE_ALIAS_NFNL_SUBSYS() + * won't work anymore */ +#define NFNL_SUBSYS_NONE 0 +#define NFNL_SUBSYS_CTNETLINK 1 +#define NFNL_SUBSYS_CTNETLINK_EXP 2 +#define NFNL_SUBSYS_QUEUE 3 +#define NFNL_SUBSYS_ULOG 4 +#define NFNL_SUBSYS_COUNT 5 + +#endif /* _NFNETLINK_H */ diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h new file mode 100644 index 0000000..d7c3503 --- /dev/null +++ b/include/linux/netfilter/nfnetlink_conntrack.h @@ -0,0 +1,140 @@ +#ifndef _IPCONNTRACK_NETLINK_H +#define _IPCONNTRACK_NETLINK_H +#include + +enum cntl_msg_types { + IPCTNL_MSG_CT_NEW, + IPCTNL_MSG_CT_GET, + IPCTNL_MSG_CT_DELETE, + IPCTNL_MSG_CT_GET_CTRZERO, + + IPCTNL_MSG_MAX +}; + +enum ctnl_exp_msg_types { + IPCTNL_MSG_EXP_NEW, + IPCTNL_MSG_EXP_GET, + IPCTNL_MSG_EXP_DELETE, + + IPCTNL_MSG_EXP_MAX +}; + + +enum ctattr_type { + CTA_UNSPEC, + CTA_TUPLE_ORIG, + CTA_TUPLE_REPLY, + CTA_STATUS, + CTA_PROTOINFO, + CTA_HELP, + CTA_NAT_SRC, +#define CTA_NAT CTA_NAT_SRC /* backwards compatibility */ + CTA_TIMEOUT, + CTA_MARK, + CTA_COUNTERS_ORIG, + CTA_COUNTERS_REPLY, + CTA_USE, + CTA_ID, + CTA_NAT_DST, + __CTA_MAX +}; +#define CTA_MAX (__CTA_MAX - 1) + +enum ctattr_tuple { + CTA_TUPLE_UNSPEC, + CTA_TUPLE_IP, + CTA_TUPLE_PROTO, + __CTA_TUPLE_MAX +}; +#define CTA_TUPLE_MAX (__CTA_TUPLE_MAX - 1) + +enum ctattr_ip { + CTA_IP_UNSPEC, + CTA_IP_V4_SRC, + CTA_IP_V4_DST, + CTA_IP_V6_SRC, + CTA_IP_V6_DST, + __CTA_IP_MAX +}; +#define CTA_IP_MAX (__CTA_IP_MAX - 1) + +enum ctattr_l4proto { + CTA_PROTO_UNSPEC, + CTA_PROTO_NUM, + CTA_PROTO_SRC_PORT, + CTA_PROTO_DST_PORT, + CTA_PROTO_ICMP_ID, + CTA_PROTO_ICMP_TYPE, + CTA_PROTO_ICMP_CODE, + CTA_PROTO_ICMPV6_ID, + CTA_PROTO_ICMPV6_TYPE, + CTA_PROTO_ICMPV6_CODE, + __CTA_PROTO_MAX +}; +#define CTA_PROTO_MAX (__CTA_PROTO_MAX - 1) + +enum ctattr_protoinfo { + CTA_PROTOINFO_UNSPEC, + CTA_PROTOINFO_TCP, + __CTA_PROTOINFO_MAX +}; +#define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1) + +enum ctattr_protoinfo_tcp { + CTA_PROTOINFO_TCP_UNSPEC, + CTA_PROTOINFO_TCP_STATE, + CTA_PROTOINFO_TCP_WSCALE_ORIGINAL, + CTA_PROTOINFO_TCP_WSCALE_REPLY, + CTA_PROTOINFO_TCP_FLAGS_ORIGINAL, + CTA_PROTOINFO_TCP_FLAGS_REPLY, + __CTA_PROTOINFO_TCP_MAX +}; +#define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1) + +enum ctattr_counters { + CTA_COUNTERS_UNSPEC, + CTA_COUNTERS_PACKETS, /* old 64bit counters */ + CTA_COUNTERS_BYTES, /* old 64bit counters */ + CTA_COUNTERS32_PACKETS, + CTA_COUNTERS32_BYTES, + __CTA_COUNTERS_MAX +}; +#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) + +enum ctattr_nat { + CTA_NAT_UNSPEC, + CTA_NAT_MINIP, + CTA_NAT_MAXIP, + CTA_NAT_PROTO, + __CTA_NAT_MAX +}; +#define CTA_NAT_MAX (__CTA_NAT_MAX - 1) + +enum ctattr_protonat { + CTA_PROTONAT_UNSPEC, + CTA_PROTONAT_PORT_MIN, + CTA_PROTONAT_PORT_MAX, + __CTA_PROTONAT_MAX +}; +#define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1) + +enum ctattr_expect { + CTA_EXPECT_UNSPEC, + CTA_EXPECT_MASTER, + CTA_EXPECT_TUPLE, + CTA_EXPECT_MASK, + CTA_EXPECT_TIMEOUT, + CTA_EXPECT_ID, + CTA_EXPECT_HELP_NAME, + __CTA_EXPECT_MAX +}; +#define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1) + +enum ctattr_help { + CTA_HELP_UNSPEC, + CTA_HELP_NAME, + __CTA_HELP_MAX +}; +#define CTA_HELP_MAX (__CTA_HELP_MAX - 1) + +#endif /* _IPCONNTRACK_NETLINK_H */ diff --git a/include/linux/netfilter/nfnetlink_log.h b/include/linux/netfilter/nfnetlink_log.h new file mode 100644 index 0000000..2de5df9 --- /dev/null +++ b/include/linux/netfilter/nfnetlink_log.h @@ -0,0 +1,96 @@ +#ifndef _NFNETLINK_LOG_H +#define _NFNETLINK_LOG_H + +/* This file describes the netlink messages (i.e. 'protocol packets'), + * and not any kind of function definitions. It is shared between kernel and + * userspace. Don't put kernel specific stuff in here */ + +#ifndef aligned_be64 +#define aligned_be64 u_int64_t __attribute__((aligned(8))) +#endif + +#include +#include + +enum nfulnl_msg_types { + NFULNL_MSG_PACKET, /* packet from kernel to userspace */ + NFULNL_MSG_CONFIG, /* connect to a particular queue */ + + NFULNL_MSG_MAX +}; + +struct nfulnl_msg_packet_hdr { + __be16 hw_protocol; /* hw protocol (network order) */ + u_int8_t hook; /* netfilter hook */ + u_int8_t _pad; +}; + +struct nfulnl_msg_packet_hw { + __be16 hw_addrlen; + u_int16_t _pad; + u_int8_t hw_addr[8]; +}; + +struct nfulnl_msg_packet_timestamp { + aligned_be64 sec; + aligned_be64 usec; +}; + +enum nfulnl_attr_type { + NFULA_UNSPEC, + NFULA_PACKET_HDR, + NFULA_MARK, /* u_int32_t nfmark */ + NFULA_TIMESTAMP, /* nfulnl_msg_packet_timestamp */ + NFULA_IFINDEX_INDEV, /* u_int32_t ifindex */ + NFULA_IFINDEX_OUTDEV, /* u_int32_t ifindex */ + NFULA_IFINDEX_PHYSINDEV, /* u_int32_t ifindex */ + NFULA_IFINDEX_PHYSOUTDEV, /* u_int32_t ifindex */ + NFULA_HWADDR, /* nfulnl_msg_packet_hw */ + NFULA_PAYLOAD, /* opaque data payload */ + NFULA_PREFIX, /* string prefix */ + NFULA_UID, /* user id of socket */ + NFULA_SEQ, /* instance-local sequence number */ + NFULA_SEQ_GLOBAL, /* global sequence number */ + + __NFULA_MAX +}; +#define NFULA_MAX (__NFULA_MAX - 1) + +enum nfulnl_msg_config_cmds { + NFULNL_CFG_CMD_NONE, + NFULNL_CFG_CMD_BIND, + NFULNL_CFG_CMD_UNBIND, + NFULNL_CFG_CMD_PF_BIND, + NFULNL_CFG_CMD_PF_UNBIND, +}; + +struct nfulnl_msg_config_cmd { + u_int8_t command; /* nfulnl_msg_config_cmds */ +} __attribute__ ((packed)); + +struct nfulnl_msg_config_mode { + __be32 copy_range; + u_int8_t copy_mode; + u_int8_t _pad; +} __attribute__ ((packed)); + +enum nfulnl_attr_config { + NFULA_CFG_UNSPEC, + NFULA_CFG_CMD, /* nfulnl_msg_config_cmd */ + NFULA_CFG_MODE, /* nfulnl_msg_config_mode */ + NFULA_CFG_NLBUFSIZ, /* u_int32_t buffer size */ + NFULA_CFG_TIMEOUT, /* u_int32_t in 1/100 s */ + NFULA_CFG_QTHRESH, /* u_int32_t */ + NFULA_CFG_FLAGS, /* u_int16_t */ + __NFULA_CFG_MAX +}; +#define NFULA_CFG_MAX (__NFULA_CFG_MAX -1) + +#define NFULNL_COPY_NONE 0x00 +#define NFULNL_COPY_META 0x01 +#define NFULNL_COPY_PACKET 0x02 + +#define NFULNL_CFG_F_SEQ 0x0001 +#define NFULNL_CFG_F_SEQ_GLOBAL 0x0002 + +#endif /* _NFNETLINK_LOG_H */ diff --git a/include/linux/netlink.h b/include/linux/netlink.h new file mode 100644 index 0000000..d252103 --- /dev/null +++ b/include/linux/netlink.h @@ -0,0 +1,150 @@ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +#include /* for sa_family_t */ +#include + +#define NETLINK_ROUTE 0 /* Routing/device hook */ +#define NETLINK_UNUSED 1 /* Unused number */ +#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ +#define NETLINK_FIREWALL 3 /* Firewalling hook */ +#define NETLINK_INET_DIAG 4 /* INET socket monitoring */ +#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ +#define NETLINK_XFRM 6 /* ipsec */ +#define NETLINK_SELINUX 7 /* SELinux event notifications */ +#define NETLINK_ISCSI 8 /* Open-iSCSI */ +#define NETLINK_AUDIT 9 /* auditing */ +#define NETLINK_FIB_LOOKUP 10 +#define NETLINK_CONNECTOR 11 +#define NETLINK_NETFILTER 12 /* netfilter subsystem */ +#define NETLINK_IP6_FW 13 +#define NETLINK_DNRTMSG 14 /* DECnet routing messages */ +#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ +#define NETLINK_GENERIC 16 +/* leave room for NETLINK_DM (DM Events) */ +#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ +#define NETLINK_ECRYPTFS 19 + +#define MAX_LINKS 32 + +struct sockaddr_nl +{ + sa_family_t nl_family; /* AF_NETLINK */ + unsigned short nl_pad; /* zero */ + __u32 nl_pid; /* port ID */ + __u32 nl_groups; /* multicast groups mask */ +}; + +struct nlmsghdr +{ + __u32 nlmsg_len; /* Length of message including header */ + __u16 nlmsg_type; /* Message content */ + __u16 nlmsg_flags; /* Additional flags */ + __u32 nlmsg_seq; /* Sequence number */ + __u32 nlmsg_pid; /* Sending process port ID */ +}; + +/* Flags values */ + +#define NLM_F_REQUEST 1 /* It is request message. */ +#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */ +#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */ +#define NLM_F_ECHO 8 /* Echo this request */ + +/* Modifiers to GET request */ +#define NLM_F_ROOT 0x100 /* specify tree root */ +#define NLM_F_MATCH 0x200 /* return all matching */ +#define NLM_F_ATOMIC 0x400 /* atomic GET */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/* Modifiers to NEW request */ +#define NLM_F_REPLACE 0x100 /* Override existing */ +#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ +#define NLM_F_CREATE 0x400 /* Create, if it does not exist */ +#define NLM_F_APPEND 0x800 /* Add to end of list */ + +/* + 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL + 4.4BSD CHANGE NLM_F_REPLACE + + True CHANGE NLM_F_CREATE|NLM_F_REPLACE + Append NLM_F_CREATE + Check NLM_F_EXCL + */ + +#define NLMSG_ALIGNTO 4 +#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN)) +#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) +#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) +#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ + (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) +#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len <= (len)) +#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) + +#define NLMSG_NOOP 0x1 /* Nothing. */ +#define NLMSG_ERROR 0x2 /* Error */ +#define NLMSG_DONE 0x3 /* End of a dump */ +#define NLMSG_OVERRUN 0x4 /* Data lost */ + +#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */ + +struct nlmsgerr +{ + int error; + struct nlmsghdr msg; +}; + +#define NETLINK_ADD_MEMBERSHIP 1 +#define NETLINK_DROP_MEMBERSHIP 2 +#define NETLINK_PKTINFO 3 + +struct nl_pktinfo +{ + __u32 group; +}; + +#define NET_MAJOR 36 /* Major 36 is reserved for networking */ + +enum { + NETLINK_UNCONNECTED = 0, + NETLINK_CONNECTED, +}; + +/* + * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | (struct nlattr) | ing | | ing | + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * <-------------- nlattr->nla_len --------------> + */ + +struct nlattr +{ + __u16 nla_len; + __u16 nla_type; +}; + +/* + * nla_type (16 bits) + * +---+---+-------------------------------+ + * | N | O | Attribute Type | + * +---+---+-------------------------------+ + * N := Carries nested attributes + * O := Payload stored in network byte order + * + * Note: The N and O flag are mutually exclusive. + */ +#define NLA_F_NESTED (1 << 15) +#define NLA_F_NET_BYTEORDER (1 << 14) +#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) + +#define NLA_ALIGNTO 4 +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) +#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) + +#endif /* __LINUX_NETLINK_H */ diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h new file mode 100644 index 0000000..30b8571 --- /dev/null +++ b/include/linux/pkt_cls.h @@ -0,0 +1,426 @@ +#ifndef __LINUX_PKT_CLS_H +#define __LINUX_PKT_CLS_H + +#include + +/* I think i could have done better macros ; for now this is stolen from + * some arch/mips code - jhs +*/ +#define _TC_MAKE32(x) ((x)) + +#define _TC_MAKEMASK1(n) (_TC_MAKE32(1) << _TC_MAKE32(n)) +#define _TC_MAKEMASK(v,n) (_TC_MAKE32((_TC_MAKE32(1)<<(v))-1) << _TC_MAKE32(n)) +#define _TC_MAKEVALUE(v,n) (_TC_MAKE32(v) << _TC_MAKE32(n)) +#define _TC_GETVALUE(v,n,m) ((_TC_MAKE32(v) & _TC_MAKE32(m)) >> _TC_MAKE32(n)) + +/* verdict bit breakdown + * +bit 0: when set -> this packet has been munged already + +bit 1: when set -> It is ok to munge this packet + +bit 2,3,4,5: Reclassify counter - sort of reverse TTL - if exceeded +assume loop + +bit 6,7: Where this packet was last seen +0: Above the transmit example at the socket level +1: on the Ingress +2: on the Egress + +bit 8: when set --> Request not to classify on ingress. + +bits 9,10,11: redirect counter - redirect TTL. Loop avoidance + + * + * */ + +#define TC_MUNGED _TC_MAKEMASK1(0) +#define SET_TC_MUNGED(v) ( TC_MUNGED | (v & ~TC_MUNGED)) +#define CLR_TC_MUNGED(v) ( v & ~TC_MUNGED) + +#define TC_OK2MUNGE _TC_MAKEMASK1(1) +#define SET_TC_OK2MUNGE(v) ( TC_OK2MUNGE | (v & ~TC_OK2MUNGE)) +#define CLR_TC_OK2MUNGE(v) ( v & ~TC_OK2MUNGE) + +#define S_TC_VERD _TC_MAKE32(2) +#define M_TC_VERD _TC_MAKEMASK(4,S_TC_VERD) +#define G_TC_VERD(x) _TC_GETVALUE(x,S_TC_VERD,M_TC_VERD) +#define V_TC_VERD(x) _TC_MAKEVALUE(x,S_TC_VERD) +#define SET_TC_VERD(v,n) ((V_TC_VERD(n)) | (v & ~M_TC_VERD)) + +#define S_TC_FROM _TC_MAKE32(6) +#define M_TC_FROM _TC_MAKEMASK(2,S_TC_FROM) +#define G_TC_FROM(x) _TC_GETVALUE(x,S_TC_FROM,M_TC_FROM) +#define V_TC_FROM(x) _TC_MAKEVALUE(x,S_TC_FROM) +#define SET_TC_FROM(v,n) ((V_TC_FROM(n)) | (v & ~M_TC_FROM)) +#define AT_STACK 0x0 +#define AT_INGRESS 0x1 +#define AT_EGRESS 0x2 + +#define TC_NCLS _TC_MAKEMASK1(8) +#define SET_TC_NCLS(v) ( TC_NCLS | (v & ~TC_NCLS)) +#define CLR_TC_NCLS(v) ( v & ~TC_NCLS) + +#define S_TC_RTTL _TC_MAKE32(9) +#define M_TC_RTTL _TC_MAKEMASK(3,S_TC_RTTL) +#define G_TC_RTTL(x) _TC_GETVALUE(x,S_TC_RTTL,M_TC_RTTL) +#define V_TC_RTTL(x) _TC_MAKEVALUE(x,S_TC_RTTL) +#define SET_TC_RTTL(v,n) ((V_TC_RTTL(n)) | (v & ~M_TC_RTTL)) + +#define S_TC_AT _TC_MAKE32(12) +#define M_TC_AT _TC_MAKEMASK(2,S_TC_AT) +#define G_TC_AT(x) _TC_GETVALUE(x,S_TC_AT,M_TC_AT) +#define V_TC_AT(x) _TC_MAKEVALUE(x,S_TC_AT) +#define SET_TC_AT(v,n) ((V_TC_AT(n)) | (v & ~M_TC_AT)) + +/* Action attributes */ +enum +{ + TCA_ACT_UNSPEC, + TCA_ACT_KIND, + TCA_ACT_OPTIONS, + TCA_ACT_INDEX, + TCA_ACT_STATS, + __TCA_ACT_MAX +}; + +#define TCA_ACT_MAX __TCA_ACT_MAX +#define TCA_OLD_COMPAT (TCA_ACT_MAX+1) +#define TCA_ACT_MAX_PRIO 32 +#define TCA_ACT_BIND 1 +#define TCA_ACT_NOBIND 0 +#define TCA_ACT_UNBIND 1 +#define TCA_ACT_NOUNBIND 0 +#define TCA_ACT_REPLACE 1 +#define TCA_ACT_NOREPLACE 0 +#define MAX_REC_LOOP 4 +#define MAX_RED_LOOP 4 + +#define TC_ACT_UNSPEC (-1) +#define TC_ACT_OK 0 +#define TC_ACT_RECLASSIFY 1 +#define TC_ACT_SHOT 2 +#define TC_ACT_PIPE 3 +#define TC_ACT_STOLEN 4 +#define TC_ACT_QUEUED 5 +#define TC_ACT_REPEAT 6 +#define TC_ACT_JUMP 0x10000000 + +/* Action type identifiers*/ +enum +{ + TCA_ID_UNSPEC=0, + TCA_ID_POLICE=1, + /* other actions go here */ + __TCA_ID_MAX=255 +}; + +#define TCA_ID_MAX __TCA_ID_MAX + +struct tc_police +{ + __u32 index; + int action; +#define TC_POLICE_UNSPEC TC_ACT_UNSPEC +#define TC_POLICE_OK TC_ACT_OK +#define TC_POLICE_RECLASSIFY TC_ACT_RECLASSIFY +#define TC_POLICE_SHOT TC_ACT_SHOT +#define TC_POLICE_PIPE TC_ACT_PIPE + + __u32 limit; + __u32 burst; + __u32 mtu; + struct tc_ratespec rate; + struct tc_ratespec peakrate; + int refcnt; + int bindcnt; + __u32 capab; +}; + +struct tcf_t +{ + __u64 install; + __u64 lastuse; + __u64 expires; +}; + +struct tc_cnt +{ + int refcnt; + int bindcnt; +}; + +#define tc_gen \ + __u32 index; \ + __u32 capab; \ + int action; \ + int refcnt; \ + int bindcnt + +enum +{ + TCA_POLICE_UNSPEC, + TCA_POLICE_TBF, + TCA_POLICE_RATE, + TCA_POLICE_PEAKRATE, + TCA_POLICE_AVRATE, + TCA_POLICE_RESULT, + __TCA_POLICE_MAX +#define TCA_POLICE_RESULT TCA_POLICE_RESULT +}; + +#define TCA_POLICE_MAX (__TCA_POLICE_MAX - 1) + +/* U32 filters */ + +#define TC_U32_HTID(h) ((h)&0xFFF00000) +#define TC_U32_USERHTID(h) (TC_U32_HTID(h)>>20) +#define TC_U32_HASH(h) (((h)>>12)&0xFF) +#define TC_U32_NODE(h) ((h)&0xFFF) +#define TC_U32_KEY(h) ((h)&0xFFFFF) +#define TC_U32_UNSPEC 0 +#define TC_U32_ROOT (0xFFF00000) + +enum +{ + TCA_U32_UNSPEC, + TCA_U32_CLASSID, + TCA_U32_HASH, + TCA_U32_LINK, + TCA_U32_DIVISOR, + TCA_U32_SEL, + TCA_U32_POLICE, + TCA_U32_ACT, + TCA_U32_INDEV, + TCA_U32_PCNT, + TCA_U32_MARK, + __TCA_U32_MAX +}; + +#define TCA_U32_MAX (__TCA_U32_MAX - 1) + +struct tc_u32_key +{ + __u32 mask; + __u32 val; + int off; + int offmask; +}; + +struct tc_u32_sel +{ + unsigned char flags; + unsigned char offshift; + unsigned char nkeys; + + __u16 offmask; + __u16 off; + short offoff; + + short hoff; + __u32 hmask; + struct tc_u32_key keys[0]; +}; + +struct tc_u32_mark +{ + __u32 val; + __u32 mask; + __u32 success; +}; + +struct tc_u32_pcnt +{ + __u64 rcnt; + __u64 rhit; + __u64 kcnts[0]; +}; + +/* Flags */ + +#define TC_U32_TERMINAL 1 +#define TC_U32_OFFSET 2 +#define TC_U32_VAROFFSET 4 +#define TC_U32_EAT 8 + +#define TC_U32_MAXDEPTH 8 + + +/* RSVP filter */ + +enum +{ + TCA_RSVP_UNSPEC, + TCA_RSVP_CLASSID, + TCA_RSVP_DST, + TCA_RSVP_SRC, + TCA_RSVP_PINFO, + TCA_RSVP_POLICE, + TCA_RSVP_ACT, + __TCA_RSVP_MAX +}; + +#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 ) + +struct tc_rsvp_gpi +{ + __u32 key; + __u32 mask; + int offset; +}; + +struct tc_rsvp_pinfo +{ + struct tc_rsvp_gpi dpi; + struct tc_rsvp_gpi spi; + __u8 protocol; + __u8 tunnelid; + __u8 tunnelhdr; + __u8 pad; +}; + +/* ROUTE filter */ + +enum +{ + TCA_ROUTE4_UNSPEC, + TCA_ROUTE4_CLASSID, + TCA_ROUTE4_TO, + TCA_ROUTE4_FROM, + TCA_ROUTE4_IIF, + TCA_ROUTE4_POLICE, + TCA_ROUTE4_ACT, + __TCA_ROUTE4_MAX +}; + +#define TCA_ROUTE4_MAX (__TCA_ROUTE4_MAX - 1) + + +/* FW filter */ + +enum +{ + TCA_FW_UNSPEC, + TCA_FW_CLASSID, + TCA_FW_POLICE, + TCA_FW_INDEV, /* used by CONFIG_NET_CLS_IND */ + TCA_FW_ACT, /* used by CONFIG_NET_CLS_ACT */ + TCA_FW_MASK, + __TCA_FW_MAX +}; + +#define TCA_FW_MAX (__TCA_FW_MAX - 1) + +/* TC index filter */ + +enum +{ + TCA_TCINDEX_UNSPEC, + TCA_TCINDEX_HASH, + TCA_TCINDEX_MASK, + TCA_TCINDEX_SHIFT, + TCA_TCINDEX_FALL_THROUGH, + TCA_TCINDEX_CLASSID, + TCA_TCINDEX_POLICE, + TCA_TCINDEX_ACT, + __TCA_TCINDEX_MAX +}; + +#define TCA_TCINDEX_MAX (__TCA_TCINDEX_MAX - 1) + +/* Basic filter */ + +enum +{ + TCA_BASIC_UNSPEC, + TCA_BASIC_CLASSID, + TCA_BASIC_EMATCHES, + TCA_BASIC_ACT, + TCA_BASIC_POLICE, + __TCA_BASIC_MAX +}; + +#define TCA_BASIC_MAX (__TCA_BASIC_MAX - 1) + +/* Extended Matches */ + +struct tcf_ematch_tree_hdr +{ + __u16 nmatches; + __u16 progid; +}; + +enum +{ + TCA_EMATCH_TREE_UNSPEC, + TCA_EMATCH_TREE_HDR, + TCA_EMATCH_TREE_LIST, + __TCA_EMATCH_TREE_MAX +}; +#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1) + +struct tcf_ematch_hdr +{ + __u16 matchid; + __u16 kind; + __u16 flags; + __u16 pad; /* currently unused */ +}; + +/* 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-----------------------+-+-+---+ + * | Unused |S|I| R | + * +-----------------------+-+-+---+ + * + * R(2) ::= relation to next ematch + * where: 0 0 END (last ematch) + * 0 1 AND + * 1 0 OR + * 1 1 Unused (invalid) + * I(1) ::= invert result + * S(1) ::= simple payload + */ +#define TCF_EM_REL_END 0 +#define TCF_EM_REL_AND (1<<0) +#define TCF_EM_REL_OR (1<<1) +#define TCF_EM_INVERT (1<<2) +#define TCF_EM_SIMPLE (1<<3) + +#define TCF_EM_REL_MASK 3 +#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK) + +enum +{ + TCF_LAYER_LINK, + TCF_LAYER_NETWORK, + TCF_LAYER_TRANSPORT, + __TCF_LAYER_MAX +}; +#define TCF_LAYER_MAX (__TCF_LAYER_MAX - 1) + +/* Ematch type assignments + * 1..32767 Reserved for ematches inside kernel tree + * 32768..65535 Free to use, not reliable + */ +#define TCF_EM_CONTAINER 0 +#define TCF_EM_CMP 1 +#define TCF_EM_NBYTE 2 +#define TCF_EM_U32 3 +#define TCF_EM_META 4 +#define TCF_EM_TEXT 5 +#define TCF_EM_MAX 5 + +enum +{ + TCF_EM_PROG_TC +}; + +enum +{ + TCF_EM_OPND_EQ, + TCF_EM_OPND_GT, + TCF_EM_OPND_LT +}; + +#endif diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h new file mode 100644 index 0000000..268c515 --- /dev/null +++ b/include/linux/pkt_sched.h @@ -0,0 +1,478 @@ +#ifndef __LINUX_PKT_SCHED_H +#define __LINUX_PKT_SCHED_H + +/* Logical priority bands not depending on specific packet scheduler. + Every scheduler will map them to real traffic classes, if it has + no more precise mechanism to classify packets. + + These numbers have no special meaning, though their coincidence + with obsolete IPv6 values is not occasional :-). New IPv6 drafts + preferred full anarchy inspired by diffserv group. + + Note: TC_PRIO_BESTEFFORT does not mean that it is the most unhappy + class, actually, as rule it will be handled with more care than + filler or even bulk. + */ + +#define TC_PRIO_BESTEFFORT 0 +#define TC_PRIO_FILLER 1 +#define TC_PRIO_BULK 2 +#define TC_PRIO_INTERACTIVE_BULK 4 +#define TC_PRIO_INTERACTIVE 6 +#define TC_PRIO_CONTROL 7 + +#define TC_PRIO_MAX 15 + +/* Generic queue statistics, available for all the elements. + Particular schedulers may have also their private records. + */ + +struct tc_stats +{ + __u64 bytes; /* NUmber of enqueues bytes */ + __u32 packets; /* Number of enqueued packets */ + __u32 drops; /* Packets dropped because of lack of resources */ + __u32 overlimits; /* Number of throttle events when this + * flow goes out of allocated bandwidth */ + __u32 bps; /* Current flow byte rate */ + __u32 pps; /* Current flow packet rate */ + __u32 qlen; + __u32 backlog; +}; + +struct tc_estimator +{ + signed char interval; + unsigned char ewma_log; +}; + +/* "Handles" + --------- + + All the traffic control objects have 32bit identifiers, or "handles". + + They can be considered as opaque numbers from user API viewpoint, + but actually they always consist of two fields: major and + minor numbers, which are interpreted by kernel specially, + that may be used by applications, though not recommended. + + F.e. qdisc handles always have minor number equal to zero, + classes (or flows) have major equal to parent qdisc major, and + minor uniquely identifying class inside qdisc. + + Macros to manipulate handles: + */ + +#define TC_H_MAJ_MASK (0xFFFF0000U) +#define TC_H_MIN_MASK (0x0000FFFFU) +#define TC_H_MAJ(h) ((h)&TC_H_MAJ_MASK) +#define TC_H_MIN(h) ((h)&TC_H_MIN_MASK) +#define TC_H_MAKE(maj,min) (((maj)&TC_H_MAJ_MASK)|((min)&TC_H_MIN_MASK)) + +#define TC_H_UNSPEC (0U) +#define TC_H_ROOT (0xFFFFFFFFU) +#define TC_H_INGRESS (0xFFFFFFF1U) + +struct tc_ratespec +{ + unsigned char cell_log; + unsigned char __reserved; + unsigned short feature; + short addend; + unsigned short mpu; + __u32 rate; +}; + +/* FIFO section */ + +struct tc_fifo_qopt +{ + __u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */ +}; + +/* PRIO section */ + +#define TCQ_PRIO_BANDS 16 +#define TCQ_MIN_PRIO_BANDS 2 + +struct tc_prio_qopt +{ + int bands; /* Number of bands */ + __u8 priomap[TC_PRIO_MAX+1]; /* Map: logical priority -> PRIO band */ +}; + +enum +{ + TCA_PRIO_UNSPEC, + TCA_PRIO_MQ, + __TCA_PRIO_MAX +}; + +#define TCA_PRIO_MAX (__TCA_PRIO_MAX - 1) + +/* TBF section */ + +struct tc_tbf_qopt +{ + struct tc_ratespec rate; + struct tc_ratespec peakrate; + __u32 limit; + __u32 buffer; + __u32 mtu; +}; + +enum +{ + TCA_TBF_UNSPEC, + TCA_TBF_PARMS, + TCA_TBF_RTAB, + TCA_TBF_PTAB, + __TCA_TBF_MAX, +}; + +#define TCA_TBF_MAX (__TCA_TBF_MAX - 1) + + +/* TEQL section */ + +/* TEQL does not require any parameters */ + +/* SFQ section */ + +struct tc_sfq_qopt +{ + unsigned quantum; /* Bytes per round allocated to flow */ + int perturb_period; /* Period of hash perturbation */ + __u32 limit; /* Maximal packets in queue */ + unsigned divisor; /* Hash divisor */ + unsigned flows; /* Maximal number of flows */ +}; + +/* + * NOTE: limit, divisor and flows are hardwired to code at the moment. + * + * limit=flows=128, divisor=1024; + * + * The only reason for this is efficiency, it is possible + * to change these parameters in compile time. + */ + +/* RED section */ + +enum +{ + TCA_RED_UNSPEC, + TCA_RED_PARMS, + TCA_RED_STAB, + __TCA_RED_MAX, +}; + +#define TCA_RED_MAX (__TCA_RED_MAX - 1) + +struct tc_red_qopt +{ + __u32 limit; /* HARD maximal queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + unsigned char Wlog; /* log(W) */ + unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */ + unsigned char Scell_log; /* cell size for idle damping */ + unsigned char flags; +#define TC_RED_ECN 1 +#define TC_RED_HARDDROP 2 +}; + +struct tc_red_xstats +{ + __u32 early; /* Early drops */ + __u32 pdrop; /* Drops due to queue limits */ + __u32 other; /* Drops due to drop() calls */ + __u32 marked; /* Marked packets */ +}; + +/* GRED section */ + +#define MAX_DPs 16 + +enum +{ + TCA_GRED_UNSPEC, + TCA_GRED_PARMS, + TCA_GRED_STAB, + TCA_GRED_DPS, + __TCA_GRED_MAX, +}; + +#define TCA_GRED_MAX (__TCA_GRED_MAX - 1) + +struct tc_gred_qopt +{ + __u32 limit; /* HARD maximal queue length (bytes) */ + __u32 qth_min; /* Min average length threshold (bytes) */ + __u32 qth_max; /* Max average length threshold (bytes) */ + __u32 DP; /* upto 2^32 DPs */ + __u32 backlog; + __u32 qave; + __u32 forced; + __u32 early; + __u32 other; + __u32 pdrop; + __u8 Wlog; /* log(W) */ + __u8 Plog; /* log(P_max/(qth_max-qth_min)) */ + __u8 Scell_log; /* cell size for idle damping */ + __u8 prio; /* prio of this VQ */ + __u32 packets; + __u32 bytesin; +}; + +/* gred setup */ +struct tc_gred_sopt +{ + __u32 DPs; + __u32 def_DP; + __u8 grio; + __u8 flags; + __u16 pad1; +}; + +/* HTB section */ +#define TC_HTB_NUMPRIO 8 +#define TC_HTB_MAXDEPTH 8 +#define TC_HTB_PROTOVER 3 /* the same as HTB and TC's major */ + +struct tc_htb_opt +{ + struct tc_ratespec rate; + struct tc_ratespec ceil; + __u32 buffer; + __u32 cbuffer; + __u32 quantum; + __u32 level; /* out only */ + __u32 prio; +}; +struct tc_htb_glob +{ + __u32 version; /* to match HTB/TC */ + __u32 rate2quantum; /* bps->quantum divisor */ + __u32 defcls; /* default class number */ + __u32 debug; /* debug flags */ + + /* stats */ + __u32 direct_pkts; /* count of non shapped packets */ +}; +enum +{ + TCA_HTB_UNSPEC, + TCA_HTB_PARMS, + TCA_HTB_INIT, + TCA_HTB_CTAB, + TCA_HTB_RTAB, + __TCA_HTB_MAX, +}; + +#define TCA_HTB_MAX (__TCA_HTB_MAX - 1) + +struct tc_htb_xstats +{ + __u32 lends; + __u32 borrows; + __u32 giants; /* too big packets (rate will not be accurate) */ + __u32 tokens; + __u32 ctokens; +}; + +/* HFSC section */ + +struct tc_hfsc_qopt +{ + __u16 defcls; /* default class */ +}; + +struct tc_service_curve +{ + __u32 m1; /* slope of the first segment in bps */ + __u32 d; /* x-projection of the first segment in us */ + __u32 m2; /* slope of the second segment in bps */ +}; + +struct tc_hfsc_stats +{ + __u64 work; /* total work done */ + __u64 rtwork; /* work done by real-time criteria */ + __u32 period; /* current period */ + __u32 level; /* class level in hierarchy */ +}; + +enum +{ + TCA_HFSC_UNSPEC, + TCA_HFSC_RSC, + TCA_HFSC_FSC, + TCA_HFSC_USC, + __TCA_HFSC_MAX, +}; + +#define TCA_HFSC_MAX (__TCA_HFSC_MAX - 1) + + +/* CBQ section */ + +#define TC_CBQ_MAXPRIO 8 +#define TC_CBQ_MAXLEVEL 8 +#define TC_CBQ_DEF_EWMA 5 + +struct tc_cbq_lssopt +{ + unsigned char change; + unsigned char flags; +#define TCF_CBQ_LSS_BOUNDED 1 +#define TCF_CBQ_LSS_ISOLATED 2 + unsigned char ewma_log; + unsigned char level; +#define TCF_CBQ_LSS_FLAGS 1 +#define TCF_CBQ_LSS_EWMA 2 +#define TCF_CBQ_LSS_MAXIDLE 4 +#define TCF_CBQ_LSS_MINIDLE 8 +#define TCF_CBQ_LSS_OFFTIME 0x10 +#define TCF_CBQ_LSS_AVPKT 0x20 + __u32 maxidle; + __u32 minidle; + __u32 offtime; + __u32 avpkt; +}; + +struct tc_cbq_wrropt +{ + unsigned char flags; + unsigned char priority; + unsigned char cpriority; + unsigned char __reserved; + __u32 allot; + __u32 weight; +}; + +struct tc_cbq_ovl +{ + unsigned char strategy; +#define TC_CBQ_OVL_CLASSIC 0 +#define TC_CBQ_OVL_DELAY 1 +#define TC_CBQ_OVL_LOWPRIO 2 +#define TC_CBQ_OVL_DROP 3 +#define TC_CBQ_OVL_RCLASSIC 4 + unsigned char priority2; + __u16 pad; + __u32 penalty; +}; + +struct tc_cbq_police +{ + unsigned char police; + unsigned char __res1; + unsigned short __res2; +}; + +struct tc_cbq_fopt +{ + __u32 split; + __u32 defmap; + __u32 defchange; +}; + +struct tc_cbq_xstats +{ + __u32 borrows; + __u32 overactions; + __s32 avgidle; + __s32 undertime; +}; + +enum +{ + TCA_CBQ_UNSPEC, + TCA_CBQ_LSSOPT, + TCA_CBQ_WRROPT, + TCA_CBQ_FOPT, + TCA_CBQ_OVL_STRATEGY, + TCA_CBQ_RATE, + TCA_CBQ_RTAB, + TCA_CBQ_POLICE, + __TCA_CBQ_MAX, +}; + +#define TCA_CBQ_MAX (__TCA_CBQ_MAX - 1) + +/* dsmark section */ + +enum { + TCA_DSMARK_UNSPEC, + TCA_DSMARK_INDICES, + TCA_DSMARK_DEFAULT_INDEX, + TCA_DSMARK_SET_TC_INDEX, + TCA_DSMARK_MASK, + TCA_DSMARK_VALUE, + __TCA_DSMARK_MAX, +}; + +#define TCA_DSMARK_MAX (__TCA_DSMARK_MAX - 1) + +/* ATM section */ + +enum { + TCA_ATM_UNSPEC, + TCA_ATM_FD, /* file/socket descriptor */ + TCA_ATM_PTR, /* pointer to descriptor - later */ + TCA_ATM_HDR, /* LL header */ + TCA_ATM_EXCESS, /* excess traffic class (0 for CLP) */ + TCA_ATM_ADDR, /* PVC address (for output only) */ + TCA_ATM_STATE, /* VC state (ATM_VS_*; for output only) */ + __TCA_ATM_MAX, +}; + +#define TCA_ATM_MAX (__TCA_ATM_MAX - 1) + +/* Network emulator */ + +enum +{ + TCA_NETEM_UNSPEC, + TCA_NETEM_CORR, + TCA_NETEM_DELAY_DIST, + TCA_NETEM_REORDER, + TCA_NETEM_CORRUPT, + __TCA_NETEM_MAX, +}; + +#define TCA_NETEM_MAX (__TCA_NETEM_MAX - 1) + +struct tc_netem_qopt +{ + __u32 latency; /* added delay (us) */ + __u32 limit; /* fifo limit (packets) */ + __u32 loss; /* random packet loss (0=none ~0=100%) */ + __u32 gap; /* re-ordering gap (0 for none) */ + __u32 duplicate; /* random packet dup (0=none ~0=100%) */ + __u32 jitter; /* random jitter in latency (us) */ +}; + +struct tc_netem_corr +{ + __u32 delay_corr; /* delay correlation */ + __u32 loss_corr; /* packet loss correlation */ + __u32 dup_corr; /* duplicate correlation */ +}; + +struct tc_netem_reorder +{ + __u32 probability; + __u32 correlation; +}; + +struct tc_netem_corrupt +{ + __u32 probability; + __u32 correlation; +}; + +#define NETEM_DIST_SCALE 8192 + +#endif diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h new file mode 100644 index 0000000..29d358c --- /dev/null +++ b/include/linux/rtnetlink.h @@ -0,0 +1,559 @@ +#ifndef __LINUX_RTNETLINK_H +#define __LINUX_RTNETLINK_H + +#include +#include +#include +#include + +/**** + * Routing/neighbour discovery messages. + ****/ + +/* Types of messages */ + +enum { + RTM_BASE = 16, +#define RTM_BASE RTM_BASE + + RTM_NEWLINK = 16, +#define RTM_NEWLINK RTM_NEWLINK + RTM_DELLINK, +#define RTM_DELLINK RTM_DELLINK + RTM_GETLINK, +#define RTM_GETLINK RTM_GETLINK + RTM_SETLINK, +#define RTM_SETLINK RTM_SETLINK + + RTM_NEWADDR = 20, +#define RTM_NEWADDR RTM_NEWADDR + RTM_DELADDR, +#define RTM_DELADDR RTM_DELADDR + RTM_GETADDR, +#define RTM_GETADDR RTM_GETADDR + + RTM_NEWROUTE = 24, +#define RTM_NEWROUTE RTM_NEWROUTE + RTM_DELROUTE, +#define RTM_DELROUTE RTM_DELROUTE + RTM_GETROUTE, +#define RTM_GETROUTE RTM_GETROUTE + + RTM_NEWNEIGH = 28, +#define RTM_NEWNEIGH RTM_NEWNEIGH + RTM_DELNEIGH, +#define RTM_DELNEIGH RTM_DELNEIGH + RTM_GETNEIGH, +#define RTM_GETNEIGH RTM_GETNEIGH + + RTM_NEWRULE = 32, +#define RTM_NEWRULE RTM_NEWRULE + RTM_DELRULE, +#define RTM_DELRULE RTM_DELRULE + RTM_GETRULE, +#define RTM_GETRULE RTM_GETRULE + + RTM_NEWQDISC = 36, +#define RTM_NEWQDISC RTM_NEWQDISC + RTM_DELQDISC, +#define RTM_DELQDISC RTM_DELQDISC + RTM_GETQDISC, +#define RTM_GETQDISC RTM_GETQDISC + + RTM_NEWTCLASS = 40, +#define RTM_NEWTCLASS RTM_NEWTCLASS + RTM_DELTCLASS, +#define RTM_DELTCLASS RTM_DELTCLASS + RTM_GETTCLASS, +#define RTM_GETTCLASS RTM_GETTCLASS + + RTM_NEWTFILTER = 44, +#define RTM_NEWTFILTER RTM_NEWTFILTER + RTM_DELTFILTER, +#define RTM_DELTFILTER RTM_DELTFILTER + RTM_GETTFILTER, +#define RTM_GETTFILTER RTM_GETTFILTER + + RTM_NEWACTION = 48, +#define RTM_NEWACTION RTM_NEWACTION + RTM_DELACTION, +#define RTM_DELACTION RTM_DELACTION + RTM_GETACTION, +#define RTM_GETACTION RTM_GETACTION + + RTM_NEWPREFIX = 52, +#define RTM_NEWPREFIX RTM_NEWPREFIX + + RTM_GETMULTICAST = 58, +#define RTM_GETMULTICAST RTM_GETMULTICAST + + RTM_GETANYCAST = 62, +#define RTM_GETANYCAST RTM_GETANYCAST + + RTM_NEWNEIGHTBL = 64, +#define RTM_NEWNEIGHTBL RTM_NEWNEIGHTBL + RTM_GETNEIGHTBL = 66, +#define RTM_GETNEIGHTBL RTM_GETNEIGHTBL + RTM_SETNEIGHTBL, +#define RTM_SETNEIGHTBL RTM_SETNEIGHTBL + + __RTM_MAX, +#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1) +}; + +#define RTM_NR_MSGTYPES (RTM_MAX + 1 - RTM_BASE) +#define RTM_NR_FAMILIES (RTM_NR_MSGTYPES >> 2) +#define RTM_FAM(cmd) (((cmd) - RTM_BASE) >> 2) + +/* + Generic structure for encapsulation of optional route information. + It is reminiscent of sockaddr, but with sa_family replaced + with attribute type. + */ + +struct rtattr +{ + unsigned short rta_len; + unsigned short rta_type; +}; + +/* Macros to handle rtattributes */ + +#define RTA_ALIGNTO 4 +#define RTA_ALIGN(len) ( ((len)+RTA_ALIGNTO-1) & ~(RTA_ALIGNTO-1) ) +#define RTA_OK(rta,len) ((len) >= (int)sizeof(struct rtattr) && \ + (rta)->rta_len >= sizeof(struct rtattr) && \ + (rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) ((attrlen) -= RTA_ALIGN((rta)->rta_len), \ + (struct rtattr*)(((char*)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len)) +#define RTA_DATA(rta) ((void*)(((char*)(rta)) + RTA_LENGTH(0))) +#define RTA_PAYLOAD(rta) ((int)((rta)->rta_len) - RTA_LENGTH(0)) + + + + +/****************************************************************************** + * Definitions used in routing table administration. + ****/ + +struct rtmsg +{ + unsigned char rtm_family; + unsigned char rtm_dst_len; + unsigned char rtm_src_len; + unsigned char rtm_tos; + + unsigned char rtm_table; /* Routing table id */ + unsigned char rtm_protocol; /* Routing protocol; see below */ + unsigned char rtm_scope; /* See below */ + unsigned char rtm_type; /* See below */ + + unsigned rtm_flags; +}; + +/* rtm_type */ + +enum +{ + RTN_UNSPEC, + RTN_UNICAST, /* Gateway or direct route */ + RTN_LOCAL, /* Accept locally */ + RTN_BROADCAST, /* Accept locally as broadcast, + send as broadcast */ + RTN_ANYCAST, /* Accept locally as broadcast, + but send as unicast */ + RTN_MULTICAST, /* Multicast route */ + RTN_BLACKHOLE, /* Drop */ + RTN_UNREACHABLE, /* Destination is unreachable */ + RTN_PROHIBIT, /* Administratively prohibited */ + RTN_THROW, /* Not in this table */ + RTN_NAT, /* Translate this address */ + RTN_XRESOLVE, /* Use external resolver */ + __RTN_MAX +}; + +#define RTN_MAX (__RTN_MAX - 1) + + +/* rtm_protocol */ + +#define RTPROT_UNSPEC 0 +#define RTPROT_REDIRECT 1 /* Route installed by ICMP redirects; + not used by current IPv4 */ +#define RTPROT_KERNEL 2 /* Route installed by kernel */ +#define RTPROT_BOOT 3 /* Route installed during boot */ +#define RTPROT_STATIC 4 /* Route installed by administrator */ + +/* Values of protocol >= RTPROT_STATIC are not interpreted by kernel; + they are just passed from user and back as is. + It will be used by hypothetical multiple routing daemons. + Note that protocol values should be standardized in order to + avoid conflicts. + */ + +#define RTPROT_GATED 8 /* Apparently, GateD */ +#define RTPROT_RA 9 /* RDISC/ND router advertisements */ +#define RTPROT_MRT 10 /* Merit MRT */ +#define RTPROT_ZEBRA 11 /* Zebra */ +#define RTPROT_BIRD 12 /* BIRD */ +#define RTPROT_DNROUTED 13 /* DECnet routing daemon */ +#define RTPROT_XORP 14 /* XORP */ +#define RTPROT_NTK 15 /* Netsukuku */ + +/* rtm_scope + + Really it is not scope, but sort of distance to the destination. + NOWHERE are reserved for not existing destinations, HOST is our + local addresses, LINK are destinations, located on directly attached + link and UNIVERSE is everywhere in the Universe. + + Intermediate values are also possible f.e. interior routes + could be assigned a value between UNIVERSE and LINK. +*/ + +enum rt_scope_t +{ + RT_SCOPE_UNIVERSE=0, +/* User defined values */ + RT_SCOPE_SITE=200, + RT_SCOPE_LINK=253, + RT_SCOPE_HOST=254, + RT_SCOPE_NOWHERE=255 +}; + +/* rtm_flags */ + +#define RTM_F_NOTIFY 0x100 /* Notify user of route change */ +#define RTM_F_CLONED 0x200 /* This route is cloned */ +#define RTM_F_EQUALIZE 0x400 /* Multipath equalizer: NI */ +#define RTM_F_PREFIX 0x800 /* Prefix addresses */ + +/* Reserved table identifiers */ + +enum rt_class_t +{ + RT_TABLE_UNSPEC=0, +/* User defined values */ + RT_TABLE_DEFAULT=253, + RT_TABLE_MAIN=254, + RT_TABLE_LOCAL=255, + RT_TABLE_MAX=0xFFFFFFFF +}; + + +/* Routing message attributes */ + +enum rtattr_type_t +{ + RTA_UNSPEC, + RTA_DST, + RTA_SRC, + RTA_IIF, + RTA_OIF, + RTA_GATEWAY, + RTA_PRIORITY, + RTA_PREFSRC, + RTA_METRICS, + RTA_MULTIPATH, + RTA_PROTOINFO, + RTA_FLOW, + RTA_CACHEINFO, + RTA_SESSION, + RTA_MP_ALGO, + RTA_TABLE, + __RTA_MAX +}; + +#define RTA_MAX (__RTA_MAX - 1) + +#define RTM_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))) +#define RTM_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct rtmsg)) + +/* RTM_MULTIPATH --- array of struct rtnexthop. + * + * "struct rtnexthop" describes all necessary nexthop information, + * i.e. parameters of path to a destination via this nexthop. + * + * At the moment it is impossible to set different prefsrc, mtu, window + * and rtt for different paths from multipath. + */ + +struct rtnexthop +{ + unsigned short rtnh_len; + unsigned char rtnh_flags; + unsigned char rtnh_hops; + int rtnh_ifindex; +}; + +/* rtnh_flags */ + +#define RTNH_F_DEAD 1 /* Nexthop is dead (used by multipath) */ +#define RTNH_F_PERVASIVE 2 /* Do recursive gateway lookup */ +#define RTNH_F_ONLINK 4 /* Gateway is forced on link */ + +/* Macros to handle hexthops */ + +#define RTNH_ALIGNTO 4 +#define RTNH_ALIGN(len) ( ((len)+RTNH_ALIGNTO-1) & ~(RTNH_ALIGNTO-1) ) +#define RTNH_OK(rtnh,len) ((rtnh)->rtnh_len >= sizeof(struct rtnexthop) && \ + ((int)(rtnh)->rtnh_len) <= (len)) +#define RTNH_NEXT(rtnh) ((struct rtnexthop*)(((char*)(rtnh)) + RTNH_ALIGN((rtnh)->rtnh_len))) +#define RTNH_LENGTH(len) (RTNH_ALIGN(sizeof(struct rtnexthop)) + (len)) +#define RTNH_SPACE(len) RTNH_ALIGN(RTNH_LENGTH(len)) +#define RTNH_DATA(rtnh) ((struct rtattr*)(((char*)(rtnh)) + RTNH_LENGTH(0))) + +/* RTM_CACHEINFO */ + +struct rta_cacheinfo +{ + __u32 rta_clntref; + __u32 rta_lastuse; + __s32 rta_expires; + __u32 rta_error; + __u32 rta_used; + +#define RTNETLINK_HAVE_PEERINFO 1 + __u32 rta_id; + __u32 rta_ts; + __u32 rta_tsage; +}; + +/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */ + +enum +{ + RTAX_UNSPEC, +#define RTAX_UNSPEC RTAX_UNSPEC + RTAX_LOCK, +#define RTAX_LOCK RTAX_LOCK + RTAX_MTU, +#define RTAX_MTU RTAX_MTU + RTAX_WINDOW, +#define RTAX_WINDOW RTAX_WINDOW + RTAX_RTT, +#define RTAX_RTT RTAX_RTT + RTAX_RTTVAR, +#define RTAX_RTTVAR RTAX_RTTVAR + RTAX_SSTHRESH, +#define RTAX_SSTHRESH RTAX_SSTHRESH + RTAX_CWND, +#define RTAX_CWND RTAX_CWND + RTAX_ADVMSS, +#define RTAX_ADVMSS RTAX_ADVMSS + RTAX_REORDERING, +#define RTAX_REORDERING RTAX_REORDERING + RTAX_HOPLIMIT, +#define RTAX_HOPLIMIT RTAX_HOPLIMIT + RTAX_INITCWND, +#define RTAX_INITCWND RTAX_INITCWND + RTAX_FEATURES, +#define RTAX_FEATURES RTAX_FEATURES + __RTAX_MAX +}; + +#define RTAX_MAX (__RTAX_MAX - 1) + +#define RTAX_FEATURE_ECN 0x00000001 +#define RTAX_FEATURE_SACK 0x00000002 +#define RTAX_FEATURE_TIMESTAMP 0x00000004 +#define RTAX_FEATURE_ALLFRAG 0x00000008 + +struct rta_session +{ + __u8 proto; + __u8 pad1; + __u16 pad2; + + union { + struct { + __u16 sport; + __u16 dport; + } ports; + + struct { + __u8 type; + __u8 code; + __u16 ident; + } icmpt; + + __u32 spi; + } u; +}; + +/**** + * General form of address family dependent message. + ****/ + +struct rtgenmsg +{ + unsigned char rtgen_family; +}; + +/***************************************************************** + * Link layer specific messages. + ****/ + +/* struct ifinfomsg + * passes link level specific information, not dependent + * on network protocol. + */ + +struct ifinfomsg +{ + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; /* ARPHRD_* */ + int ifi_index; /* Link index */ + unsigned ifi_flags; /* IFF_* flags */ + unsigned ifi_change; /* IFF_* change mask */ +}; + +/******************************************************************** + * prefix information + ****/ + +struct prefixmsg +{ + unsigned char prefix_family; + unsigned char prefix_pad1; + unsigned short prefix_pad2; + int prefix_ifindex; + unsigned char prefix_type; + unsigned char prefix_len; + unsigned char prefix_flags; + unsigned char prefix_pad3; +}; + +enum +{ + PREFIX_UNSPEC, + PREFIX_ADDRESS, + PREFIX_CACHEINFO, + __PREFIX_MAX +}; + +#define PREFIX_MAX (__PREFIX_MAX - 1) + +struct prefix_cacheinfo +{ + __u32 preferred_time; + __u32 valid_time; +}; + + +/***************************************************************** + * Traffic control messages. + ****/ + +struct tcmsg +{ + unsigned char tcm_family; + unsigned char tcm__pad1; + unsigned short tcm__pad2; + int tcm_ifindex; + __u32 tcm_handle; + __u32 tcm_parent; + __u32 tcm_info; +}; + +enum +{ + TCA_UNSPEC, + TCA_KIND, + TCA_OPTIONS, + TCA_STATS, + TCA_XSTATS, + TCA_RATE, + TCA_FCNT, + TCA_STATS2, + __TCA_MAX +}; + +#define TCA_MAX (__TCA_MAX - 1) + +#define TCA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcmsg)))) +#define TCA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcmsg)) + +#ifndef __KERNEL__ +/* RTnetlink multicast groups - backwards compatibility for userspace */ +#define RTMGRP_LINK 1 +#define RTMGRP_NOTIFY 2 +#define RTMGRP_NEIGH 4 +#define RTMGRP_TC 8 + +#define RTMGRP_IPV4_IFADDR 0x10 +#define RTMGRP_IPV4_MROUTE 0x20 +#define RTMGRP_IPV4_ROUTE 0x40 +#define RTMGRP_IPV4_RULE 0x80 + +#define RTMGRP_IPV6_IFADDR 0x100 +#define RTMGRP_IPV6_MROUTE 0x200 +#define RTMGRP_IPV6_ROUTE 0x400 +#define RTMGRP_IPV6_IFINFO 0x800 + +#define RTMGRP_DECnet_IFADDR 0x1000 +#define RTMGRP_DECnet_ROUTE 0x4000 + +#define RTMGRP_IPV6_PREFIX 0x20000 +#endif + +/* RTnetlink multicast groups */ +enum rtnetlink_groups { + RTNLGRP_NONE, +#define RTNLGRP_NONE RTNLGRP_NONE + RTNLGRP_LINK, +#define RTNLGRP_LINK RTNLGRP_LINK + RTNLGRP_NOTIFY, +#define RTNLGRP_NOTIFY RTNLGRP_NOTIFY + RTNLGRP_NEIGH, +#define RTNLGRP_NEIGH RTNLGRP_NEIGH + RTNLGRP_TC, +#define RTNLGRP_TC RTNLGRP_TC + RTNLGRP_IPV4_IFADDR, +#define RTNLGRP_IPV4_IFADDR RTNLGRP_IPV4_IFADDR + RTNLGRP_IPV4_MROUTE, +#define RTNLGRP_IPV4_MROUTE RTNLGRP_IPV4_MROUTE + RTNLGRP_IPV4_ROUTE, +#define RTNLGRP_IPV4_ROUTE RTNLGRP_IPV4_ROUTE + RTNLGRP_IPV4_RULE, +#define RTNLGRP_IPV4_RULE RTNLGRP_IPV4_RULE + RTNLGRP_IPV6_IFADDR, +#define RTNLGRP_IPV6_IFADDR RTNLGRP_IPV6_IFADDR + RTNLGRP_IPV6_MROUTE, +#define RTNLGRP_IPV6_MROUTE RTNLGRP_IPV6_MROUTE + RTNLGRP_IPV6_ROUTE, +#define RTNLGRP_IPV6_ROUTE RTNLGRP_IPV6_ROUTE + RTNLGRP_IPV6_IFINFO, +#define RTNLGRP_IPV6_IFINFO RTNLGRP_IPV6_IFINFO + RTNLGRP_DECnet_IFADDR, +#define RTNLGRP_DECnet_IFADDR RTNLGRP_DECnet_IFADDR + RTNLGRP_NOP2, + RTNLGRP_DECnet_ROUTE, +#define RTNLGRP_DECnet_ROUTE RTNLGRP_DECnet_ROUTE + RTNLGRP_DECnet_RULE, +#define RTNLGRP_DECnet_RULE RTNLGRP_DECnet_RULE + RTNLGRP_NOP4, + RTNLGRP_IPV6_PREFIX, +#define RTNLGRP_IPV6_PREFIX RTNLGRP_IPV6_PREFIX + RTNLGRP_IPV6_RULE, +#define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE + __RTNLGRP_MAX +}; +#define RTNLGRP_MAX (__RTNLGRP_MAX - 1) + +/* TC action piece */ +struct tcamsg +{ + unsigned char tca_family; + unsigned char tca__pad1; + unsigned short tca__pad2; +}; +#define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg)))) +#define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg)) +#define TCA_ACT_TAB 1 /* attr type must be >=1 */ +#define TCAA_MAX 1 + +#endif /* __LINUX_RTNETLINK_H */ diff --git a/include/netlink-generic.h b/include/netlink-generic.h new file mode 100644 index 0000000..10aa2f0 --- /dev/null +++ b/include/netlink-generic.h @@ -0,0 +1,20 @@ +/* + * netlink-generic.h Local Generic Netlink Interface + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_GENL_PRIV_H_ +#define NETLINK_GENL_PRIV_H_ + +#include +#include + +#define GENL_HDRSIZE(hdrlen) (GENL_HDRLEN + (hdrlen)) + +#endif diff --git a/include/netlink-local.h b/include/netlink-local.h new file mode 100644 index 0000000..a50745c --- /dev/null +++ b/include/netlink-local.h @@ -0,0 +1,436 @@ +/* + * netlink-local.h Local Netlink Interface + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_LOCAL_H_ +#define NETLINK_LOCAL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +typedef uint8_t __u8; +typedef uint16_t __u16; +typedef int16_t __s16; +typedef uint32_t __u32; +typedef int32_t __s32; +typedef uint64_t __u64; + +/* local header copies */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +struct trans_tbl { + int i; + const char *a; +}; + +#define __ADD(id, name) { .i = id, .a = #name }, + +struct trans_list { + int i; + char *a; + struct nl_list_head list; +}; + +#define NL_DEBUG 1 + +#define NL_DBG(LVL,FMT,ARG...) \ + do { \ + if (LVL <= nl_debug) \ + fprintf(stderr, "DBG<" #LVL ">: " FMT, ##ARG); \ + } while (0) + +#define BUG() \ + do { \ + fprintf(stderr, "BUG: %s:%d\n", \ + __FILE__, __LINE__); \ + assert(0); \ + } while (0) + +#define RET_ERR(R, E) \ + do { \ + errno = E; \ + return -R; \ + } while (0) + +extern int __nl_error(int, const char *, unsigned int, + const char *, const char *, ...); + +extern int __nl_read_num_str_file(const char *path, + int (*cb)(long, const char *)); + +#ifdef NL_ERROR_ASSERT +#include +static inline int __assert_error(const char *file, int line, char *func, + const char *fmt, ...) +{ + va_list args; + fprintf(stderr, "%s:%d:%s: ", file, line, func); + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + fprintf(stderr, "\n"); + assert(0); + return 0; +} +#define nl_error(E, FMT,ARG...) \ + __assert_error(__FILE__, __LINE__, __FUNCTION__, FMT, ##ARG) + +#else +#define nl_error(E, FMT,ARG...) \ + __nl_error(E, __FILE__, __LINE__, __FUNCTION__, FMT, ##ARG) + +#endif + +#define nl_errno(E) nl_error(E, NULL) + +static inline int __trans_list_add(int i, const char *a, + struct nl_list_head *head) +{ + struct trans_list *tl; + + tl = calloc(1, sizeof(*tl)); + if (!tl) + return nl_errno(ENOMEM); + + tl->i = i; + tl->a = strdup(a); + + nl_list_add_tail(&tl->list, head); + + return 0; +} + +static inline void __trans_list_clear(struct nl_list_head *head) +{ + struct trans_list *tl, *next; + + nl_list_for_each_entry_safe(tl, next, head, list) { + free(tl->a); + free(tl); + } +} + +static inline char *__type2str(int type, char *buf, size_t len, + struct trans_tbl *tbl, size_t tbl_len) +{ + int i; + for (i = 0; i < tbl_len; i++) { + if (tbl[i].i == type) { + snprintf(buf, len, "%s", tbl[i].a); + return buf; + } + } + + snprintf(buf, len, "0x%x", type); + return buf; +} + +static inline char *__list_type2str(int type, char *buf, size_t len, + struct nl_list_head *head) +{ + struct trans_list *tl; + + nl_list_for_each_entry(tl, head, list) { + if (tl->i == type) { + snprintf(buf, len, "%s", tl->a); + return buf; + } + } + + snprintf(buf, len, "0x%x", type); + return buf; +} + +static inline char *__flags2str(int flags, char *buf, size_t len, + struct trans_tbl *tbl, size_t tbl_len) +{ + int i; + int tmp = flags; + + memset(buf, 0, len); + + for (i = 0; i < tbl_len; i++) { + if (tbl[i].i & tmp) { + tmp &= ~tbl[i].i; + strncat(buf, tbl[i].a, len - strlen(buf) - 1); + if ((tmp & flags)) + strncat(buf, ",", len - strlen(buf) - 1); + } + } + + return buf; +} + +static inline int __str2type(const char *buf, struct trans_tbl *tbl, + size_t tbl_len) +{ + unsigned long l; + char *end; + int i; + + if (*buf == '\0') + return -1; + + for (i = 0; i < tbl_len; i++) + if (!strcasecmp(tbl[i].a, buf)) + return tbl[i].i; + + l = strtoul(buf, &end, 0); + if (l == ULONG_MAX || *end != '\0') + return -1; + + return (int) l; +} + +static inline int __list_str2type(const char *buf, struct nl_list_head *head) +{ + struct trans_list *tl; + unsigned long l; + char *end; + + if (*buf == '\0') + return -1; + + nl_list_for_each_entry(tl, head, list) { + if (!strcasecmp(tl->a, buf)) + return tl->i; + } + + l = strtoul(buf, &end, 0); + if (l == ULONG_MAX || *end != '\0') + return -1; + + return (int) l; +} + +static inline int __str2flags(const char *buf, struct trans_tbl *tbl, + size_t tbl_len) +{ + int i, flags = 0, len; + char *p = (char *) buf, *t; + + for (;;) { + if (*p == ' ') + p++; + + t = strchr(p, ','); + len = t ? t - p : strlen(p); + for (i = 0; i < tbl_len; i++) + if (!strncasecmp(tbl[i].a, p, len)) + flags |= tbl[i].i; + + if (!t) + return flags; + + p = ++t; + } + + return 0; +} + + +static inline void dp_new_line(struct nl_dump_params *params, + int line_nr) +{ + if (params->dp_prefix) { + int i; + for (i = 0; i < params->dp_prefix; i++) { + if (params->dp_fd) + fprintf(params->dp_fd, " "); + else if (params->dp_buf) + strncat(params->dp_buf, " ", + params->dp_buflen - + sizeof(params->dp_buf) - 1); + } + } + + if (params->dp_nl_cb) + params->dp_nl_cb(params, line_nr); +} + +static inline void __dp_dump(struct nl_dump_params *parms, const char *fmt, + va_list args) +{ + if (parms->dp_fd) + vfprintf(parms->dp_fd, fmt, args); + else if (parms->dp_buf || parms->dp_cb) { + char *buf = NULL; + vasprintf(&buf, fmt, args); + if (parms->dp_cb) + parms->dp_cb(parms, buf); + else + strncat(parms->dp_buf, buf, + parms->dp_buflen - strlen(parms->dp_buf) - 1); + free(buf); + } +} + +static inline void dp_dump(struct nl_dump_params *parms, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + __dp_dump(parms, fmt, args); + va_end(args); +} + +static inline void dp_dump_line(struct nl_dump_params *parms, int line, + const char *fmt, ...) +{ + va_list args; + + dp_new_line(parms, line); + + va_start(args, fmt); + __dp_dump(parms, fmt, args); + va_end(args); +} + +static inline void dump_from_ops(struct nl_object *obj, + struct nl_dump_params *params) +{ + int type = params->dp_type; + + if (type < 0 || type > NL_DUMP_MAX) + BUG(); + + if (params->dp_dump_msgtype) { +#if 0 + /* XXX */ + char buf[64]; + + dp_dump_line(params, 0, "%s ", + nl_cache_mngt_type2name(obj->ce_ops, + obj->ce_ops->co_protocol, + obj->ce_msgtype, + buf, sizeof(buf))); +#endif + params->dp_pre_dump = 1; + } else + dp_new_line(params, 0); + + if (obj->ce_ops->oo_dump[type]) + obj->ce_ops->oo_dump[type](obj, params); +} + +static inline struct nl_cache *dp_cache(struct nl_object *obj) +{ + if (obj->ce_cache == NULL) + return nl_cache_mngt_require(obj->ce_ops->oo_name); + + return obj->ce_cache; +} + +static inline int nl_cb_call(struct nl_cb *cb, int type, struct nl_msg *msg) +{ + return cb->cb_set[type](msg, cb->cb_args[type]); +} + +#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) +#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) + +#define __init __attribute__ ((constructor)) +#define __exit __attribute__ ((destructor)) + +#define P_ACCEPT 0 +#define P_IGNORE 0 + +#define min(x,y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; }) + +#define max(x,y) ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; }) + +#define min_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) +#define max_t(type,x,y) \ + ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; }) + +extern int nl_cache_parse(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, struct nl_parser_param *); + + +static inline void rtnl_copy_ratespec(struct rtnl_ratespec *dst, + struct tc_ratespec *src) +{ + dst->rs_cell_log = src->cell_log; + dst->rs_feature = src->feature; + dst->rs_addend = src->addend; + dst->rs_mpu = src->mpu; + dst->rs_rate = src->rate; +} + +static inline void rtnl_rcopy_ratespec(struct tc_ratespec *dst, + struct rtnl_ratespec *src) +{ + dst->cell_log = src->rs_cell_log; + dst->feature = src->rs_feature; + dst->addend = src->rs_addend; + dst->mpu = src->rs_mpu; + dst->rate = src->rs_rate; +} + +static inline char *nl_cache_name(struct nl_cache *cache) +{ + return cache->c_ops ? cache->c_ops->co_name : "unknown"; +} + +#define GENL_FAMILY(id, name) \ + { \ + { id, NL_ACT_UNSPEC, name }, \ + END_OF_MSGTYPES_LIST, \ + } + +#define REQUESTED(LIST, ATTR) ((LIST) & (ATTR)) +#define AVAILABLE(A, B, ATTR) (((A)->ce_mask & (B)->ce_mask) & (ATTR)) +#define ATTR_MATCH(A, B, ATTR, EXPR) (!AVAILABLE(A, B, ATTR) || (EXPR)) +#define ATTR_DIFF(LIST, ATTR, A, B, EXPR) \ +({ int diff = 0; \ + if (REQUESTED(LIST, ATTR) && ATTR_MATCH(A, B, ATTR, EXPR)) \ + diff = ATTR; \ + diff; }) + +#endif diff --git a/include/netlink-tc.h b/include/netlink-tc.h new file mode 100644 index 0000000..65be588 --- /dev/null +++ b/include/netlink-tc.h @@ -0,0 +1,70 @@ +/* + * netlink-tc.h Local Traffic Control Interface + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_TC_PRIV_H_ +#define NETLINK_TC_PRIV_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define TCA_ATTR_HANDLE 0x001 +#define TCA_ATTR_PARENT 0x002 +#define TCA_ATTR_IFINDEX 0x004 +#define TCA_ATTR_KIND 0x008 +#define TCA_ATTR_FAMILY 0x010 +#define TCA_ATTR_INFO 0x020 +#define TCA_ATTR_OPTS 0x040 +#define TCA_ATTR_STATS 0x080 +#define TCA_ATTR_XSTATS 0x100 +#define TCA_ATTR_MAX TCA_ATTR_XSTATS + +extern int tca_parse(struct nlattr **, int, struct rtnl_tca *, + struct nla_policy *); +extern int tca_msg_parser(struct nlmsghdr *, struct rtnl_tca *); +extern void tca_free_data(struct rtnl_tca *); +extern int tca_clone(struct rtnl_tca *, struct rtnl_tca *); +extern int tca_dump_brief(struct rtnl_tca *, const char *, + struct nl_dump_params *, int); +extern int tca_dump_full(struct rtnl_tca *, struct nl_dump_params *, int); +extern int tca_dump_stats(struct rtnl_tca *, + struct nl_dump_params *, int); +extern int tca_compare(struct nl_object *, struct nl_object *, uint32_t, int); + +extern void tca_set_ifindex(struct rtnl_tca *, int); +extern int tca_get_ifindex(struct rtnl_tca *); +extern void tca_set_handle(struct rtnl_tca *, uint32_t); +extern uint32_t tca_get_handle(struct rtnl_tca *); +extern void tca_set_parent(struct rtnl_tca *, uint32_t); +extern uint32_t tca_get_parent(struct rtnl_tca *); +extern void tca_set_kind(struct rtnl_tca *, const char *); +extern char *tca_get_kind(struct rtnl_tca *); +extern uint64_t tca_get_stat(struct rtnl_tca *, int ); + +extern struct nl_msg *tca_build_msg(struct rtnl_tca *tca, int type, int flags); + +static inline void *tca_priv(struct rtnl_tca *tca) +{ + return tca->tc_subdata; +} + +static inline void *tca_xstats(struct rtnl_tca *tca) +{ + return tca->tc_xstats->d_data; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink-types.h b/include/netlink-types.h new file mode 100644 index 0000000..7a06582 --- /dev/null +++ b/include/netlink-types.h @@ -0,0 +1,865 @@ +/* + * netlink-types.h Netlink Types (Private) + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_LOCAL_TYPES_H_ +#define NETLINK_LOCAL_TYPES_H_ + +#include +#include +#include +#include +#include + +#define NL_SOCK_BUFSIZE_SET (1<<0) +#define NL_SOCK_PASSCRED (1<<1) +#define NL_OWN_PORT (1<<2) +#define NL_MSG_PEEK (1<<3) + +#define NL_MSG_CRED_PRESENT 1 + +struct nl_cache_ops; +struct nl_handle; +struct nl_object; + +struct nl_cb +{ + nl_recvmsg_msg_cb_t cb_set[NL_CB_TYPE_MAX+1]; + void * cb_args[NL_CB_TYPE_MAX+1]; + + nl_recvmsg_err_cb_t cb_err; + void * cb_err_arg; + + /** May be used to replace nl_recvmsgs with your own implementation + * in all internal calls to nl_recvmsgs. */ + int (*cb_recvmsgs_ow)(struct nl_handle *, + struct nl_cb *); + + /** Overwrite internal calls to nl_recv, must return the number of + * octets read and allocate a buffer for the received data. */ + int (*cb_recv_ow)(struct nl_handle *, + struct sockaddr_nl *, + unsigned char **, + struct ucred **); + + /** Overwrites internal calls to nl_send, must send the netlink + * message. */ + int (*cb_send_ow)(struct nl_handle *, + struct nl_msg *); + + int cb_refcnt; +}; + +struct nl_handle +{ + struct sockaddr_nl h_local; + struct sockaddr_nl h_peer; + int h_fd; + int h_proto; + unsigned int h_seq_next; + unsigned int h_seq_expect; + int h_flags; + struct nl_cb * h_cb; +}; + +struct nl_cache +{ + struct nl_list_head c_items; + int c_nitems; + int c_iarg1; + int c_iarg2; + struct nl_cache_ops * c_ops; +}; + +struct nl_cache_assoc +{ + struct nl_cache * ca_cache; + change_func_t ca_change; +}; + +struct nl_cache_mngr +{ + int cm_protocol; + int cm_flags; + int cm_nassocs; + struct nl_handle * cm_handle; + struct nl_cache_assoc * cm_assocs; +}; + +struct nl_parser_param; + +enum { + NL_ACT_UNSPEC, + NL_ACT_NEW, + NL_ACT_DEL, + NL_ACT_GET, + NL_ACT_SET, + NL_ACT_CHANGE, + __NL_ACT_MAX, +}; + +#define NL_ACT_MAX (__NL_ACT_MAX - 1) + +#define END_OF_MSGTYPES_LIST { -1, -1, NULL } + +struct nl_msgtype +{ + int mt_id; + int mt_act; + char * mt_name; +}; + +struct genl_info +{ + struct sockaddr_nl * who; + struct nlmsghdr * nlh; + struct genlmsghdr * genlhdr; + void * userhdr; + struct nlattr ** attrs; +}; + +#define LOOSE_FLAG_COMPARISON 1 + +struct nl_object_ops +{ + /* Name of object */ + char * oo_name; + + /* Size of object structure */ + size_t oo_size; + + /* List of attributes needed to uniquely identify the object */ + uint32_t oo_id_attrs; + + /** + * Called whenever a new object was allocated + */ + void (*oo_constructor)(struct nl_object *); + + /** + * Called whenever a object in the cache gets destroyed, must + * free the type specific memory allocations + */ + void (*oo_free_data)(struct nl_object *); + + /** + * Callened whenever an object needs to be cloned + */ + int (*oo_clone)(struct nl_object *, struct nl_object *); + + /** + * Called whenever a dump of a cache object is requested. Must + * dump the specified object to the specified file descriptor + */ + int (*oo_dump[NL_DUMP_MAX+1])(struct nl_object *, + struct nl_dump_params *); + + int (*oo_compare)(struct nl_object *, struct nl_object *, + uint32_t, int); + + + char *(*oo_attrs2str)(int, char *, size_t); +}; + +struct nl_af_group +{ + int ag_family; + int ag_group; +}; + +#define END_OF_GROUP_LIST AF_UNSPEC, 0 + +struct nl_cache_ops +{ + char * co_name; + int co_hdrsize; + int co_protocol; + struct nl_af_group * co_groups; + + /** + * Called whenever an update of the cache is required. Must send + * a request message to the kernel requesting a complete dump. + */ + int (*co_request_update)(struct nl_cache *, struct nl_handle *); + + /** + * Called whenever a message was received that needs to be parsed. + * Must parse the message and call the paser callback function + * (nl_parser_param) provided via the argument. + */ + int (*co_msg_parser)(struct nl_cache_ops *, struct sockaddr_nl *, + struct nlmsghdr *, void *); + + struct nl_object_ops * co_obj_ops; + + struct nl_cache_ops *co_next; + struct nl_cache *co_major_cache; + struct genl_ops * co_genl; + struct nl_msgtype co_msgtypes[]; +}; + +#define NL_OBJ_MARK 1 + +#define NLHDR_COMMON \ + int ce_refcnt; \ + struct nl_object_ops * ce_ops; \ + struct nl_cache * ce_cache; \ + struct nl_list_head ce_list; \ + int ce_msgtype; \ + int ce_flags; \ + uint32_t ce_mask; + +struct nl_object +{ + NLHDR_COMMON +}; + +struct nl_parser_param +{ + int (*pp_cb)(struct nl_object *, struct nl_parser_param *); + void * pp_arg; +}; + +struct nl_data +{ + size_t d_size; + void * d_data; +}; + +struct nl_addr +{ + int a_family; + unsigned int a_maxsize; + unsigned int a_len; + int a_prefixlen; + int a_refcnt; + char a_addr[0]; +}; + +struct nl_msg +{ + int nm_protocol; + int nm_flags; + struct sockaddr_nl nm_src; + struct sockaddr_nl nm_dst; + struct ucred nm_creds; + struct nlmsghdr * nm_nlh; +}; + +struct rtnl_link_map +{ + uint64_t lm_mem_start; + uint64_t lm_mem_end; + uint64_t lm_base_addr; + uint16_t lm_irq; + uint8_t lm_dma; + uint8_t lm_port; +}; + +#define IFQDISCSIZ 32 + +struct rtnl_link +{ + NLHDR_COMMON + + char l_name[IFNAMSIZ]; + + uint32_t l_family; + uint32_t l_arptype; + uint32_t l_index; + uint32_t l_flags; + uint32_t l_change; + uint32_t l_mtu; + uint32_t l_link; + uint32_t l_txqlen; + uint32_t l_weight; + uint32_t l_master; + struct nl_addr *l_addr; + struct nl_addr *l_bcast; + char l_qdisc[IFQDISCSIZ]; + struct rtnl_link_map l_map; + uint64_t l_stats[RTNL_LINK_STATS_MAX+1]; + uint32_t l_flag_mask; +}; + +struct rtnl_ncacheinfo +{ + uint32_t nci_confirmed; /**< Time since neighbour validty was last confirmed */ + uint32_t nci_used; /**< Time since neighbour entry was last ued */ + uint32_t nci_updated; /**< Time since last update */ + uint32_t nci_refcnt; /**< Reference counter */ +}; + + +struct rtnl_neigh +{ + NLHDR_COMMON + uint32_t n_family; + uint32_t n_ifindex; + uint16_t n_state; + uint8_t n_flags; + uint8_t n_type; + struct nl_addr *n_lladdr; + struct nl_addr *n_dst; + uint32_t n_probes; + struct rtnl_ncacheinfo n_cacheinfo; + uint32_t n_state_mask; + uint32_t n_flag_mask; +}; + + +struct rtnl_addr_cacheinfo +{ + /* Preferred lifetime in seconds */ + uint32_t aci_prefered; + + /* Valid lifetime in seconds */ + uint32_t aci_valid; + + /* Timestamp of creation in 1/100s seince boottime */ + uint32_t aci_cstamp; + + /* Timestamp of last update in 1/100s since boottime */ + uint32_t aci_tstamp; +}; + +struct rtnl_addr +{ + NLHDR_COMMON + + uint8_t a_family; + uint8_t a_prefixlen; + uint8_t a_flags; + uint8_t a_scope; + uint32_t a_ifindex; + + struct nl_addr *a_peer; + struct nl_addr *a_local; + struct nl_addr *a_bcast; + struct nl_addr *a_anycast; + struct nl_addr *a_multicast; + + struct rtnl_addr_cacheinfo a_cacheinfo; + + char a_label[IFNAMSIZ]; + uint32_t a_flag_mask; +}; + +#define NEXTHOP_HAS_FLAGS 0x000001 +#define NEXTHOP_HAS_WEIGHT 0x000002 +#define NEXTHOP_HAS_IFINDEX 0x000004 +#define NEXTHOP_HAS_GATEWAY 0x000008 + +struct rtnl_nexthop +{ + uint8_t rtnh_flags; + uint8_t rtnh_flag_mask; + uint8_t rtnh_weight; + /* 1 byte spare */ + uint32_t rtnh_ifindex; + struct nl_addr * rtnh_gateway; + uint32_t rtnh_mask; + + struct nl_list_head rtnh_list; +}; + +struct rtnl_route +{ + NLHDR_COMMON + + uint8_t rt_family; + uint8_t rt_dst_len; + uint8_t rt_src_len; + uint8_t rt_tos; + uint8_t rt_table; + uint8_t rt_protocol; + uint8_t rt_scope; + uint8_t rt_type; + uint32_t rt_flags; + struct nl_addr * rt_dst; + struct nl_addr * rt_src; + char rt_iif[IFNAMSIZ]; + uint32_t rt_oif; + struct nl_addr * rt_gateway; + uint32_t rt_prio; + uint32_t rt_metrics[RTAX_MAX]; + uint32_t rt_metrics_mask; + struct nl_addr * rt_pref_src; + struct nl_list_head rt_nexthops; + realm_t rt_realms; + struct rtnl_rtcacheinfo rt_cacheinfo; + uint32_t rt_mp_algo; + uint32_t rt_flag_mask; +}; + +struct rtnl_rule +{ + NLHDR_COMMON + + uint64_t r_mark; + uint32_t r_prio; + uint32_t r_realms; + uint32_t r_table; + uint8_t r_dsfield; + uint8_t r_type; + uint8_t r_family; + uint8_t r_src_len; + uint8_t r_dst_len; + char r_iif[IFNAMSIZ]; + struct nl_addr *r_src; + struct nl_addr *r_dst; + struct nl_addr *r_srcmap; +}; + +struct rtnl_neightbl_parms +{ + /** + * Interface index of the device this parameter set is assigned + * to or 0 for the default set. + */ + uint32_t ntp_ifindex; + + /** + * Number of references to this parameter set. + */ + uint32_t ntp_refcnt; + + /** + * Queue length for pending arp requests, i.e. the number of + * packets which are accepted from other layers while the + * neighbour address is still being resolved + */ + uint32_t ntp_queue_len; + + /** + * Number of requests to send to the user level ARP daemon. + * Specify 0 to disable. + */ + uint32_t ntp_app_probes; + + /** + * Maximum number of retries for unicast solicitation. + */ + uint32_t ntp_ucast_probes; + + /** + * Maximum number of retries for multicast solicitation. + */ + uint32_t ntp_mcast_probes; + + /** + * Base value in milliseconds to ompute reachable time, see RFC2461. + */ + uint64_t ntp_base_reachable_time; + + /** + * Actual reachable time (read-only) + */ + uint64_t ntp_reachable_time; /* secs */ + + /** + * The time in milliseconds between retransmitted Neighbor + * Solicitation messages. + */ + uint64_t ntp_retrans_time; + + /** + * Interval in milliseconds to check for stale neighbour + * entries. + */ + uint64_t ntp_gc_stale_time; /* secs */ + + /** + * Delay in milliseconds for the first time probe if + * the neighbour is reachable. + */ + uint64_t ntp_probe_delay; /* secs */ + + /** + * Maximum delay in milliseconds of an answer to a neighbour + * solicitation message. + */ + uint64_t ntp_anycast_delay; + + /** + * Minimum age in milliseconds before a neighbour entry + * may be replaced. + */ + uint64_t ntp_locktime; + + /** + * Delay in milliseconds before answering to an ARP request + * for which a proxy ARP entry exists. + */ + uint64_t ntp_proxy_delay; + + /** + * Queue length for the delayed proxy arp requests. + */ + uint32_t ntp_proxy_qlen; + + /** + * Mask of available parameter attributes + */ + uint32_t ntp_mask; +}; + +#define NTBLNAMSIZ 32 + +/** + * Neighbour table + * @ingroup neightbl + */ +struct rtnl_neightbl +{ + NLHDR_COMMON + + char nt_name[NTBLNAMSIZ]; + uint32_t nt_family; + uint32_t nt_gc_thresh1; + uint32_t nt_gc_thresh2; + uint32_t nt_gc_thresh3; + uint64_t nt_gc_interval; + struct ndt_config nt_config; + struct rtnl_neightbl_parms nt_parms; + struct ndt_stats nt_stats; +}; + +struct rtnl_ratespec +{ + uint8_t rs_cell_log; + uint16_t rs_feature; + uint16_t rs_addend; + uint16_t rs_mpu; + uint32_t rs_rate; +}; + +struct rtnl_tstats +{ + struct { + uint64_t bytes; + uint64_t packets; + } tcs_basic; + + struct { + uint32_t bps; + uint32_t pps; + } tcs_rate_est; + + struct { + uint32_t qlen; + uint32_t backlog; + uint32_t drops; + uint32_t requeues; + uint32_t overlimits; + } tcs_queue; +}; + +#define TCKINDSIZ 32 + +#define NL_TCA_GENERIC(pre) \ + NLHDR_COMMON \ + uint32_t pre ##_family; \ + uint32_t pre ##_ifindex; \ + uint32_t pre ##_handle; \ + uint32_t pre ##_parent; \ + uint32_t pre ##_info; \ + char pre ##_kind[TCKINDSIZ]; \ + struct nl_data * pre ##_opts; \ + uint64_t pre ##_stats[RTNL_TC_STATS_MAX+1]; \ + struct nl_data * pre ##_xstats; \ + void * pre ##_subdata; \ + + +struct rtnl_tca +{ + NL_TCA_GENERIC(tc); +}; + +struct rtnl_qdisc +{ + NL_TCA_GENERIC(q); + struct rtnl_qdisc_ops *q_ops; +}; + +struct rtnl_class +{ + NL_TCA_GENERIC(c); + struct rtnl_class_ops *c_ops; +}; + +struct rtnl_cls +{ + NL_TCA_GENERIC(c); + uint32_t c_prio; + uint32_t c_protocol; + struct rtnl_cls_ops *c_ops; +}; + +struct rtnl_u32 +{ + uint32_t cu_divisor; + uint32_t cu_hash; + uint32_t cu_classid; + uint32_t cu_link; + struct nl_data * cu_pcnt; + struct nl_data * cu_selector; + struct nl_data * cu_act; + struct nl_data * cu_police; + char cu_indev[IFNAMSIZ]; + int cu_mask; +}; + +struct rtnl_fw +{ + uint32_t cf_classid; + struct nl_data * cf_act; + struct nl_data * cf_police; + char cf_indev[IFNAMSIZ]; + int cf_mask; +}; + +struct rtnl_dsmark_qdisc +{ + uint16_t qdm_indices; + uint16_t qdm_default_index; + uint32_t qdm_set_tc_index; + uint32_t qdm_mask; +}; + +struct rtnl_dsmark_class +{ + uint8_t cdm_bmask; + uint8_t cdm_value; + uint32_t cdm_mask; +}; + +struct rtnl_fifo +{ + uint32_t qf_limit; + uint32_t qf_mask; +}; + +struct rtnl_prio +{ + uint32_t qp_bands; + uint8_t qp_priomap[TC_PRIO_MAX+1]; + uint32_t qp_mask; +}; + +struct rtnl_tbf +{ + uint32_t qt_limit; + uint32_t qt_mpu; + struct rtnl_ratespec qt_rate; + uint32_t qt_rate_bucket; + uint32_t qt_rate_txtime; + struct rtnl_ratespec qt_peakrate; + uint32_t qt_peakrate_bucket; + uint32_t qt_peakrate_txtime; + uint32_t qt_mask; +}; + +struct rtnl_sfq +{ + uint32_t qs_quantum; + uint32_t qs_perturb; + uint32_t qs_limit; + uint32_t qs_divisor; + uint32_t qs_flows; + uint32_t qs_mask; +}; + +struct rtnl_netem_corr +{ + uint32_t nmc_delay; + uint32_t nmc_loss; + uint32_t nmc_duplicate; +}; + +struct rtnl_netem_reo +{ + uint32_t nmro_probability; + uint32_t nmro_correlation; +}; + +struct rtnl_netem +{ + uint32_t qnm_latency; + uint32_t qnm_limit; + uint32_t qnm_loss; + uint32_t qnm_gap; + uint32_t qnm_duplicate; + uint32_t qnm_jitter; + uint32_t qnm_mask; + struct rtnl_netem_corr qnm_corr; + struct rtnl_netem_reo qnm_ro; +}; + +struct rtnl_htb_qdisc +{ + uint32_t qh_rate2quantum; + uint32_t qh_defcls; + uint32_t qh_mask; +}; + +struct rtnl_htb_class +{ + uint32_t ch_prio; + uint32_t ch_mtu; + struct rtnl_ratespec ch_rate; + struct rtnl_ratespec ch_ceil; + uint32_t ch_rbuffer; + uint32_t ch_cbuffer; + uint32_t ch_quantum; + uint8_t ch_overhead; + uint8_t ch_mpu; + uint32_t ch_mask; +}; + +struct rtnl_cbq +{ + struct tc_cbq_lssopt cbq_lss; + struct tc_ratespec cbq_rate; + struct tc_cbq_wrropt cbq_wrr; + struct tc_cbq_ovl cbq_ovl; + struct tc_cbq_fopt cbq_fopt; + struct tc_cbq_police cbq_police; +}; + +struct rtnl_red +{ + uint32_t qr_limit; + uint32_t qr_qth_min; + uint32_t qr_qth_max; + uint8_t qr_flags; + uint8_t qr_wlog; + uint8_t qr_plog; + uint8_t qr_scell_log; + uint32_t qr_mask; +}; + +struct flnl_request +{ + NLHDR_COMMON + + struct nl_addr * lr_addr; + uint32_t lr_fwmark; + uint8_t lr_tos; + uint8_t lr_scope; + uint8_t lr_table; +}; + + +struct flnl_result +{ + NLHDR_COMMON + + struct flnl_request * fr_req; + uint8_t fr_table_id; + uint8_t fr_prefixlen; + uint8_t fr_nh_sel; + uint8_t fr_type; + uint8_t fr_scope; + uint32_t fr_error; +}; + +#define GENL_OP_HAS_POLICY 1 +#define GENL_OP_HAS_DOIT 2 +#define GENL_OP_HAS_DUMPIT 4 + +struct genl_family_op +{ + uint32_t o_id; + uint32_t o_flags; + + struct nl_list_head o_list; +}; + +struct genl_family +{ + NLHDR_COMMON + + uint16_t gf_id; + char gf_name[GENL_NAMSIZ]; + uint32_t gf_version; + uint32_t gf_hdrsize; + uint32_t gf_maxattr; + + struct nl_list_head gf_ops; +}; + +union nfnl_ct_proto +{ + struct { + uint16_t src; + uint16_t dst; + } port; + struct { + uint16_t id; + uint8_t type; + uint8_t code; + } icmp; +}; + +struct nfnl_ct_dir { + struct nl_addr * src; + struct nl_addr * dst; + union nfnl_ct_proto proto; + uint64_t packets; + uint64_t bytes; +}; + +union nfnl_ct_protoinfo { + struct { + uint8_t state; + } tcp; +}; + +struct nfnl_ct { + NLHDR_COMMON + + uint8_t ct_family; + uint8_t ct_proto; + union nfnl_ct_protoinfo ct_protoinfo; + + uint32_t ct_status; + uint32_t ct_timeout; + uint32_t ct_mark; + uint32_t ct_use; + uint32_t ct_id; + + struct nfnl_ct_dir ct_orig; + struct nfnl_ct_dir ct_repl; +}; + +struct nfnl_log { + NLHDR_COMMON + + uint8_t log_family; + uint8_t log_hook; + uint16_t log_hwproto; + uint32_t log_mark; + struct timeval log_timestamp; + uint32_t log_indev; + uint32_t log_outdev; + uint32_t log_physindev; + uint32_t log_physoutdev; + uint8_t log_hwaddr[8]; + int log_hwaddr_len; + void * log_payload; + int log_payload_len; + char * log_prefix; + uint32_t log_uid; + uint32_t log_seq; + uint32_t log_seq_global; +}; + +#endif diff --git a/include/netlink/addr.h b/include/netlink/addr.h new file mode 100644 index 0000000..df5c868 --- /dev/null +++ b/include/netlink/addr.h @@ -0,0 +1,67 @@ +/* + * netlink/addr.h Abstract Address + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_ADDR_H_ +#define NETLINK_ADDR_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_addr; + +/* Creation */ +extern struct nl_addr * nl_addr_alloc(size_t); +extern struct nl_addr * nl_addr_build(int, void *, size_t); +extern struct nl_addr * nl_addr_parse(const char *, int); +extern struct nl_addr * nl_addr_clone(struct nl_addr *); + +/* Destroyage */ +extern void nl_addr_destroy(struct nl_addr *); + +/* Usage Management */ +extern struct nl_addr * nl_addr_get(struct nl_addr *); +extern void nl_addr_put(struct nl_addr *); +extern int nl_addr_shared(struct nl_addr *); + +extern int nl_addr_cmp(struct nl_addr *, struct nl_addr *); +extern int nl_addr_cmp_prefix(struct nl_addr *, struct nl_addr *); +extern int nl_addr_valid(char *, int); +extern int nl_addr_guess_family(struct nl_addr *); +extern int nl_addr_fill_sockaddr(struct nl_addr *, + struct sockaddr *, socklen_t *); +extern struct addrinfo *nl_addr_info(struct nl_addr *addr); +extern int nl_addr_resolve(struct nl_addr *addr, char *host, size_t hostlen); + +/* Access Functions */ +extern void nl_addr_set_family(struct nl_addr *, int); +extern int nl_addr_get_family(struct nl_addr *); +extern int nl_addr_set_binary_addr(struct nl_addr *, void *, + size_t); +extern void * nl_addr_get_binary_addr(struct nl_addr *); +extern unsigned int nl_addr_get_len(struct nl_addr *); +extern void nl_addr_set_prefixlen(struct nl_addr *, int); +extern unsigned int nl_addr_get_prefixlen(struct nl_addr *); + +/* Address Family Translations */ +extern char * nl_af2str(int, char *, size_t); +extern int nl_str2af(const char *); + +/* Translations to Strings */ +extern char * nl_addr2str(struct nl_addr *, char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/attr.h b/include/netlink/attr.h new file mode 100644 index 0000000..6160f5f --- /dev/null +++ b/include/netlink/attr.h @@ -0,0 +1,287 @@ +/* + * netlink/attr.h Netlink Attributes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_ATTR_H_ +#define NETLINK_ATTR_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_msg; + +/** + * @name Validation Policy Types + * @{ + */ + + /** + * @ingroup attr + * Standard attribute types to specify validation policy + */ +enum { + NLA_UNSPEC, /**< Unspecified type */ + NLA_U8, /**< 8bit integer */ + NLA_U16, /**< 16bit integer */ + NLA_U32, /**< 32bit integer */ + NLA_U64, /**< 64bit integer */ + NLA_STRING, /**< character string */ + NLA_FLAG, /**< flag */ + NLA_MSECS, /**< micro seconds (64bit) */ + NLA_NESTED, /**< nested attributes */ + __NLA_TYPE_MAX, +}; + +/** + * @ingroup attr + * Maximum netlink validation policy type + */ +#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1) + +/** @} */ + +/** + * @ingroup attr + * attribute validation policy + * + * Policies are defined as arrays of this struct, the array must + * be accessible by attribute type up to the highest identifier + * to be expected. + * + * Example: + * @code + * static struct nla_policy my_policy[ATTR_MAX+1] __read_mostly = { + * [ATTR_FOO] = { .type = NLA_U16 }, + * [ATTR_BAR] = { .type = NLA_STRING }, + * [ATTR_BAZ] = { .minlen = sizeof(struct mystruct) }, + * }; + * @endcode + */ +struct nla_policy { + /** Type of attribute or NLA_UNSPEC */ + uint16_t type; + + /** Minimal length of payload required to be available */ + uint16_t minlen; + + /** Maximal length of payload required to be available */ + uint16_t maxlen; +}; + +/* size calculations */ +extern int nla_attr_size(int payload); +extern int nla_total_size(int payload); +extern int nla_padlen(int payload); + +/* payload access */ +extern int nla_type(const struct nlattr *); +extern void * nla_data(const struct nlattr *); +extern int nla_len(const struct nlattr *); + +/* attribute parsing */ +extern int nla_ok(const struct nlattr *, int); +extern struct nlattr * nla_next(const struct nlattr *, int *); +extern int nla_parse(struct nlattr **, int, struct nlattr *, + int, struct nla_policy *); +extern int nla_parse_nested(struct nlattr **, int, struct nlattr *, + struct nla_policy *); +extern int nla_validate(struct nlattr *, int, int, + struct nla_policy *); +extern struct nlattr * nla_find(struct nlattr *, int, int); + +/* utilities */ +extern int nla_memcpy(void *, struct nlattr *, int); +extern size_t nla_strlcpy(char *, const struct nlattr *, size_t); +extern int nla_memcmp(const struct nlattr *, const void *, size_t); +extern int nla_strcmp(const struct nlattr *, const char *); + +/* attribute construction */ +extern struct nlattr * nla_reserve(struct nl_msg *, int, int); +extern int nla_put(struct nl_msg *, int, int, const void *); +extern int nla_put_nested(struct nl_msg *, int, struct nl_msg *); +extern int nla_put_u8(struct nl_msg *, int, uint8_t); +extern int nla_put_u16(struct nl_msg *, int, uint16_t); +extern int nla_put_u32(struct nl_msg *, int, uint32_t); +extern int nla_put_u64(struct nl_msg *, int, uint64_t); +extern int nla_put_string(struct nl_msg *, int, const char *); +extern int nla_put_flag(struct nl_msg *, int); +extern int nla_put_msecs(struct nl_msg *, int, unsigned long); +extern int nla_put_data(struct nl_msg *, int, struct nl_data *); +extern int nla_put_addr(struct nl_msg *, int, struct nl_addr *); + +/* attribute nesting */ +extern struct nlattr * nla_nest_start(struct nl_msg *, int); +extern int nla_nest_end(struct nl_msg *, struct nlattr *); + +/* attribute reading */ +extern uint8_t nla_get_u8(struct nlattr *); +extern uint16_t nla_get_u16(struct nlattr *); +extern uint32_t nla_get_u32(struct nlattr *); +extern uint64_t nla_get_u64(struct nlattr *); +extern char * nla_get_string(struct nlattr *); +extern int nla_get_flag(struct nlattr *); +extern unsigned long nla_get_msecs(struct nlattr *); +extern struct nl_data * nla_get_data(struct nlattr *); +extern struct nl_addr * nla_get_addr(struct nlattr *, int); + +/** + * @name Attribute Construction (Exception Based) + * + * All these functions jump to nla_put_failure in case of a failure + * instead of returning an error code. + * + * @{ + */ + +/** + * @ingroup attr + * Add a netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg attrlen length of attribute payload + * @arg data head of attribute payload + */ +#define NLA_PUT(n, attrtype, attrlen, data) \ + do { \ + if (nla_put(n, attrtype, attrlen, data) < 0) \ + goto nla_put_failure; \ + } while(0) + +/** + * @ingroup attr + * Add a basic netlink attribute to a netlink message + * @arg n netlink message + * @arg type atomic type + * @arg attrtype attribute type + * @arg value head of attribute payload + */ +#define NLA_PUT_TYPE(n, type, attrtype, value) \ + do { \ + type __tmp = value; \ + NLA_PUT(n, attrtype, sizeof(type), &__tmp); \ + } while(0) + +/** + * Add a u8 netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg value numeric value + */ +#define NLA_PUT_U8(n, attrtype, value) \ + NLA_PUT_TYPE(n, uint8_t, attrtype, value) + +/** + * Add a u16 netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg value numeric value + */ +#define NLA_PUT_U16(n, attrtype, value) \ + NLA_PUT_TYPE(n, uint16_t, attrtype, value) + +/** + * Add a u32 netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg value numeric value + */ +#define NLA_PUT_U32(n, attrtype, value) \ + NLA_PUT_TYPE(n, uint32_t, attrtype, value) + +/** + * Add a u64 netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg value numeric value + */ +#define NLA_PUT_U64(n, attrtype, value) \ + NLA_PUT_TYPE(n, uint64_t, attrtype, value) + +/** + * Add a character string netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg value character string + */ +#define NLA_PUT_STRING(n, attrtype, value) \ + NLA_PUT(n, attrtype, strlen(value) + 1, value) + +/** + * Add a flag netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + */ +#define NLA_PUT_FLAG(n, attrtype) \ + NLA_PUT(n, attrtype, 0, NULL) + +/** + * Add a msecs netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg msecs numeric value in micro seconds + */ +#define NLA_PUT_MSECS(n, attrtype, msecs) \ + NLA_PUT_U64(n, attrtype, msecs) + +/** + * Add a address attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg addr abstract address object + */ +#define NLA_PUT_ADDR(n, attrtype, addr) \ + NLA_PUT(n, attrtype, nl_addr_get_len(addr), \ + nl_addr_get_binary_addr(addr)) + +/** @} */ + +/** + * @name Iterators + * @{ + */ + +/** + * @ingroup attr + * iterate over a stream of attributes + * @arg pos loop counter, set to current attribute + * @arg head head of attribute stream + * @arg len length of attribute stream + * @arg rem initialized to len, holds bytes currently remaining in stream + */ +#define nla_for_each_attr(pos, head, len, rem) \ + for (pos = head, rem = len; \ + nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +/** + * @ingroup attr + * iterate over a stream of nested attributes + * @arg pos loop counter, set to current attribute + * @arg nla attribute containing the nested attributes + * @arg rem initialized to len, holds bytes currently remaining in stream + */ +#define nla_for_each_nested(pos, nla, rem) \ + for (pos = nla_data(nla), rem = nla_len(nla); \ + nla_ok(pos, rem); \ + pos = nla_next(pos, &(rem))) + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/cache.h b/include/netlink/cache.h new file mode 100644 index 0000000..5f31d8e --- /dev/null +++ b/include/netlink/cache.h @@ -0,0 +1,123 @@ +/* + * netlink/cache.h Caching Module + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_CACHE_H_ +#define NETLINK_CACHE_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_cache; +struct nl_cache_ops; + +typedef void (*change_func_t)(struct nl_cache *, struct nl_object *, int); + +/* Access Functions */ +extern int nl_cache_nitems(struct nl_cache *); +extern int nl_cache_nitems_filter(struct nl_cache *, + struct nl_object *); +extern struct nl_cache_ops * nl_cache_get_ops(struct nl_cache *); +extern struct nl_object * nl_cache_get_first(struct nl_cache *); +extern struct nl_object * nl_cache_get_last(struct nl_cache *); +extern struct nl_object * nl_cache_get_next(struct nl_object *); +extern struct nl_object * nl_cache_get_prev(struct nl_object *); + +/* Cache creation/deletion */ +#define nl_cache_alloc_from_ops(ptr) nl_cache_alloc(ptr) +extern struct nl_cache * nl_cache_alloc(struct nl_cache_ops *); +extern struct nl_cache * nl_cache_alloc_name(const char *); +extern struct nl_cache * nl_cache_subset(struct nl_cache *, + struct nl_object *); +extern void nl_cache_clear(struct nl_cache *); +extern void nl_cache_free(struct nl_cache *); + +/* Cache modification */ +extern int nl_cache_add(struct nl_cache *, + struct nl_object *); +extern int nl_cache_parse_and_add(struct nl_cache *, + struct nl_msg *); +#define nl_cache_delete(a, b) nl_cache_remove(b) +extern void nl_cache_remove(struct nl_object *); +#define nl_cache_update(a, b) nl_cache_refill(a, b) +extern int nl_cache_refill(struct nl_handle *, + struct nl_cache *); +extern int nl_cache_pickup(struct nl_handle *, + struct nl_cache *); +extern int nl_cache_resync(struct nl_handle *, + struct nl_cache *, + change_func_t); +extern int nl_cache_include(struct nl_cache *, + struct nl_object *, + change_func_t); + +/* General */ +extern int nl_cache_is_empty(struct nl_cache *); +extern void nl_cache_mark_all(struct nl_cache *); + +/* Dumping */ +extern void nl_cache_dump(struct nl_cache *, + struct nl_dump_params *); +extern void nl_cache_dump_filter(struct nl_cache *, + struct nl_dump_params *, + struct nl_object *); + +/* Iterators */ +extern void nl_cache_foreach(struct nl_cache *, + void (*cb)(struct nl_object *, + void *), + void *arg); +extern void nl_cache_foreach_filter(struct nl_cache *, + struct nl_object *, + void (*cb)(struct + nl_object *, + void *), + void *arg); + +/* --- cache management --- */ + +/* Cache type management */ +extern struct nl_cache_ops * nl_cache_ops_lookup(const char *); +extern struct nl_cache_ops * nl_cache_ops_lookup_for_obj(struct nl_object_ops *); +extern void nl_cache_mngt_foreach(void (*cb)(struct nl_cache_ops *, void *), void *); +extern int nl_cache_mngt_register(struct nl_cache_ops *); +extern int nl_cache_mngt_unregister(struct nl_cache_ops *); + +/* Global cache provisioning/requiring */ +extern void nl_cache_mngt_provide(struct nl_cache *); +extern void nl_cache_mngt_unprovide(struct nl_cache *); +extern struct nl_cache * nl_cache_mngt_require(const char *); + +struct nl_cache_mngr; + +#define NL_AUTO_PROVIDE 1 + +extern struct nl_cache_mngr * nl_cache_mngr_alloc(struct nl_handle *, + int, int); +extern struct nl_cache * nl_cache_mngr_add(struct nl_cache_mngr *, + const char *, + change_func_t); +extern int nl_cache_mngr_get_fd(struct nl_cache_mngr *); +extern int nl_cache_mngr_poll(struct nl_cache_mngr *, + int); +extern int nl_cache_mngr_data_ready(struct nl_cache_mngr *); +extern void nl_cache_mngr_free(struct nl_cache_mngr *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/data.h b/include/netlink/data.h new file mode 100644 index 0000000..bb28d13 --- /dev/null +++ b/include/netlink/data.h @@ -0,0 +1,40 @@ +/* + * netlink/data.h Abstract Data + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_DATA_H_ +#define NETLINK_DATA_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_data; + +/* General */ +extern struct nl_data * nl_data_alloc(void *, size_t); +extern struct nl_data * nl_data_clone(struct nl_data *); +extern int nl_data_append(struct nl_data *, void *, size_t); +extern void nl_data_free(struct nl_data *); + +/* Access Functions */ +extern void * nl_data_get(struct nl_data *); +extern size_t nl_data_get_size(struct nl_data *); + +/* Misc */ +extern int nl_data_cmp(struct nl_data *, struct nl_data *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/fib_lookup/lookup.h b/include/netlink/fib_lookup/lookup.h new file mode 100644 index 0000000..29c7ee8 --- /dev/null +++ b/include/netlink/fib_lookup/lookup.h @@ -0,0 +1,41 @@ +/* + * netlink/fib_lookup/fib_lookup.h FIB Lookup + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_FIB_LOOKUP_H_ +#define NETLINK_FIB_LOOKUP_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct flnl_result; + +extern struct flnl_result * flnl_result_alloc(void); +extern void flnl_result_put(struct flnl_result *); + +extern struct nl_cache * flnl_result_alloc_cache(void); + +extern struct nl_msg * flnl_lookup_build_request(struct flnl_request *, + int); +extern int flnl_lookup(struct nl_handle *, + struct flnl_request *, + struct nl_cache *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/fib_lookup/request.h b/include/netlink/fib_lookup/request.h new file mode 100644 index 0000000..60e8820 --- /dev/null +++ b/include/netlink/fib_lookup/request.h @@ -0,0 +1,51 @@ +/* + * netlink/fib_lookup/request.h FIB Lookup Request + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_FIB_LOOKUP_REQUEST_H_ +#define NETLINK_FIB_LOOKUP_REQUEST_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct flnl_request; + +#define REQUEST_CAST(ptr) ((struct flnl_request *) (ptr)) + +extern struct flnl_request * flnl_request_alloc(void); + +extern void flnl_request_set_fwmark(struct flnl_request *, + uint64_t); +extern uint64_t flnl_request_get_fwmark(struct flnl_request *); +extern void flnl_request_set_tos(struct flnl_request *, + int); +extern int flnl_request_get_tos(struct flnl_request *); +extern void flnl_request_set_scope(struct flnl_request *, + int); +extern int flnl_request_get_scope(struct flnl_request *); +extern void flnl_request_set_table(struct flnl_request *, + int); +extern int flnl_request_get_table(struct flnl_request *); +extern int flnl_request_set_addr(struct flnl_request *, + struct nl_addr *); +extern struct nl_addr * flnl_request_get_addr(struct flnl_request *); + +extern int flnl_request_cmp(struct flnl_request *, + struct flnl_request *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/genl/ctrl.h b/include/netlink/genl/ctrl.h new file mode 100644 index 0000000..5d65c68 --- /dev/null +++ b/include/netlink/genl/ctrl.h @@ -0,0 +1,36 @@ +/* + * netlink/genl/ctrl.h Generic Netlink Controller + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_GENL_CTRL_H_ +#define NETLINK_GENL_CTRL_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct genl_family; + +extern struct nl_cache * genl_ctrl_alloc_cache(struct nl_handle *); +extern struct genl_family * genl_ctrl_search(struct nl_cache *, int); +extern struct genl_family * genl_ctrl_search_by_name(struct nl_cache *, + const char *); +extern int genl_ctrl_resolve(struct nl_handle *, + const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/genl/family.h b/include/netlink/genl/family.h new file mode 100644 index 0000000..74319e5 --- /dev/null +++ b/include/netlink/genl/family.h @@ -0,0 +1,50 @@ +/* + * netlink/genl/family.h Generic Netlink Family + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_GENL_FAMILY_H_ +#define NETLINK_GENL_FAMILY_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct genl_family; + +extern struct genl_family * genl_family_alloc(void); +extern void genl_family_put(struct genl_family *); + +extern unsigned int genl_family_get_id(struct genl_family *); +extern void genl_family_set_id(struct genl_family *, + unsigned int); +extern char * genl_family_get_name(struct genl_family *); +extern void genl_family_set_name(struct genl_family *, + const char *name); +extern uint8_t genl_family_get_version(struct genl_family *); +extern void genl_family_set_version(struct genl_family *, + uint8_t); +extern uint32_t genl_family_get_hdrsize(struct genl_family *); +extern void genl_family_set_hdrsize(struct genl_family *, + uint32_t); +extern uint32_t genl_family_get_maxattr(struct genl_family *); +extern void genl_family_set_maxattr(struct genl_family *, + uint32_t); + +extern int genl_family_add_op(struct genl_family *, + int, int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/genl/genl.h b/include/netlink/genl/genl.h new file mode 100644 index 0000000..b423dc3 --- /dev/null +++ b/include/netlink/genl/genl.h @@ -0,0 +1,37 @@ +/* + * netlink/genl/genl.h Generic Netlink + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_GENL_H_ +#define NETLINK_GENL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int genl_connect(struct nl_handle *); + +extern int genl_send_simple(struct nl_handle *, int, int, + int, int); + +extern void * genlmsg_put(struct nl_msg *, uint32_t, uint32_t, + int, int, int, uint8_t, uint8_t); +extern void * genlmsg_data(const struct genlmsghdr *); +extern int genlmsg_len(const struct genlmsghdr *); + +extern char * genl_op2name(int, int, char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/genl/mngt.h b/include/netlink/genl/mngt.h new file mode 100644 index 0000000..467922a --- /dev/null +++ b/include/netlink/genl/mngt.h @@ -0,0 +1,78 @@ +/* + * netlink/genl/mngt.h Generic Netlink Management + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_GENL_MNGT_H_ +#define NETLINK_GENL_MNGT_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_cache_ops; + +/** + * @ingroup genl_mngt + * Generic Netlink Command + */ +struct genl_cmd +{ + /** Unique command identifier */ + int c_id; + + /** Name/description of command */ + char * c_name; + + /** + * Maximum attribute identifier, must be provided if + * a message parser is available. + */ + int c_maxattr; + + int (*c_msg_parser)(struct nl_cache_ops *, + struct genl_cmd *, + struct genl_info *, void *); + + /** + * Attribute validation policy (optional) + */ + struct nla_policy * c_attr_policy; +}; + +/** + * @ingroup genl_mngt + * Generic Netlink Operations + */ +struct genl_ops +{ + int o_family; + int o_id; + char * o_name; + struct nl_cache_ops * o_cache_ops; + struct genl_cmd * o_cmds; + int o_ncmds; + + /* linked list of all genl cache operations */ + struct nl_list_head o_list; +}; + + +extern int genl_register(struct nl_cache_ops *); +extern void genl_unregister(struct nl_cache_ops *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/handlers.h b/include/netlink/handlers.h new file mode 100644 index 0000000..fcd3e48 --- /dev/null +++ b/include/netlink/handlers.h @@ -0,0 +1,147 @@ +/* + * netlink/handlers.c default netlink message handlers + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_HANDLERS_H_ +#define NETLINK_HANDLERS_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_cb; +struct nl_handle; +struct nl_msg; + +/** + * @name Callback Typedefs + * @{ + */ + +/** + * nl_recvmsgs() callback for message processing customization + * @ingroup cb + * @arg msg netlink message being processed + * @arg arg argument passwd on through caller + */ +typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg); + +/** + * nl_recvmsgs() callback for error message processing customization + * @ingroup cb + * @arg nla netlink address of the peer + * @arg nlerr netlink error message being processed + * @arg arg argument passed on through caller + */ +typedef int (*nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, + struct nlmsgerr *nlerr, void *arg); + +/** @} */ + +/** + * Callback actions + * @ingroup cb + */ +enum nl_cb_action { + /** Proceed with wathever would come next */ + NL_OK, + /** Skip this message */ + NL_SKIP, + /** Stop parsing altogether and discard remaining messages */ + NL_STOP, +}; + +/* backwards compatibility */ +#define NL_PROCEED NL_OK +#define NL_EXIT NL_STOP + +/** + * Callback kinds + * @ingroup cb + */ +enum nl_cb_kind { + /** Default handlers (quiet) */ + NL_CB_DEFAULT, + /** Verbose default handlers (error messages printed) */ + NL_CB_VERBOSE, + /** Debug handlers for debugging */ + NL_CB_DEBUG, + /** Customized handler specified by the user */ + NL_CB_CUSTOM, + __NL_CB_KIND_MAX, +}; + +#define NL_CB_KIND_MAX (__NL_CB_KIND_MAX - 1) + +/** + * Callback types + * @ingroup cb + */ +enum nl_cb_type { + /** Message is valid */ + NL_CB_VALID, + /** Last message in a series of multi part messages received */ + NL_CB_FINISH, + /** Report received that data was lost */ + NL_CB_OVERRUN, + /** Message wants to be skipped */ + NL_CB_SKIPPED, + /** Message is an acknowledge */ + NL_CB_ACK, + /** Called for every message received */ + NL_CB_MSG_IN, + /** Called for every message sent out except for nl_sendto() */ + NL_CB_MSG_OUT, + /** Message is malformed and invalid */ + NL_CB_INVALID, + /** Called instead of internal sequence number checking */ + NL_CB_SEQ_CHECK, + /** Sending of an acknowledge message has been requested */ + NL_CB_SEND_ACK, + __NL_CB_TYPE_MAX, +}; + +#define NL_CB_TYPE_MAX (__NL_CB_TYPE_MAX - 1) + +extern struct nl_cb * nl_cb_alloc(enum nl_cb_kind); +extern struct nl_cb * nl_cb_clone(struct nl_cb *); +extern struct nl_cb * nl_cb_get(struct nl_cb *); +extern void nl_cb_put(struct nl_cb *); + +extern int nl_cb_set(struct nl_cb *, enum nl_cb_type, enum nl_cb_kind, + nl_recvmsg_msg_cb_t, void *); +extern int nl_cb_set_all(struct nl_cb *, enum nl_cb_kind, + nl_recvmsg_msg_cb_t, void *); +extern int nl_cb_err(struct nl_cb *, enum nl_cb_kind, nl_recvmsg_err_cb_t, + void *); + +extern void nl_cb_overwrite_recvmsgs(struct nl_cb *, + int (*func)(struct nl_handle *, + struct nl_cb *)); +extern void nl_cb_overwrite_recv(struct nl_cb *, + int (*func)(struct nl_handle *, + struct sockaddr_nl *, + unsigned char **, + struct ucred **)); +extern void nl_cb_overwrite_send(struct nl_cb *, + int (*func)(struct nl_handle *, + struct nl_msg *)); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/list.h b/include/netlink/list.h new file mode 100644 index 0000000..e7a2646 --- /dev/null +++ b/include/netlink/list.h @@ -0,0 +1,85 @@ +/* + * netlink/list.h Netlink List Utilities + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_LIST_H_ +#define NETLINK_LIST_H_ + +struct nl_list_head +{ + struct nl_list_head * next; + struct nl_list_head * prev; +}; + + +static inline void __nl_list_add(struct nl_list_head *obj, + struct nl_list_head *prev, + struct nl_list_head *next) +{ + prev->next = obj; + obj->prev = prev; + next->prev = obj; + obj->next = next; +} + +static inline void nl_list_add_tail(struct nl_list_head *obj, + struct nl_list_head *head) +{ + __nl_list_add(obj, head->prev, head); +} + +static inline void nl_list_add_head(struct nl_list_head *obj, + struct nl_list_head *head) +{ + __nl_list_add(obj, head, head->next); +} + +static inline void nl_list_del(struct nl_list_head *obj) +{ + obj->next->prev = obj->prev; + obj->prev->next = obj->next; +} + +static inline int nl_list_empty(struct nl_list_head *head) +{ + return head->next == head; +} + +#define nl_container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - ((size_t) &((type *)0)->member));}) + +#define nl_list_entry(ptr, type, member) \ + nl_container_of(ptr, type, member) + +#define nl_list_at_tail(pos, head, member) \ + ((pos)->member.next == (head)) + +#define nl_list_at_head(pos, head, member) \ + ((pos)->member.prev == (head)) + +#define NL_LIST_HEAD(name) \ + struct nl_list_head name = { &(name), &(name) } + +#define nl_list_for_each_entry(pos, head, member) \ + for (pos = nl_list_entry((head)->next, typeof(*pos), member); \ + &(pos)->member != (head); \ + (pos) = nl_list_entry((pos)->member.next, typeof(*(pos)), member)) + +#define nl_list_for_each_entry_safe(pos, n, head, member) \ + for (pos = nl_list_entry((head)->next, typeof(*pos), member), \ + n = nl_list_entry(pos->member.next, typeof(*pos), member); \ + &(pos)->member != (head); \ + pos = n, n = nl_list_entry(n->member.next, typeof(*n), member)) + +#define nl_init_list_head(head) \ + do { (head)->next = (head); (head)->prev = (head); } while (0) + +#endif diff --git a/include/netlink/msg.h b/include/netlink/msg.h new file mode 100644 index 0000000..d7da2cf --- /dev/null +++ b/include/netlink/msg.h @@ -0,0 +1,143 @@ +/* + * netlink/msg.c Netlink Messages Interface + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_MSG_H_ +#define NETLINK_MSG_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NL_DONTPAD 0 + +/** + * @ingroup msg + * @brief + * Will cause the netlink pid to be set to the pid assigned to + * the netlink handle (socket) just before sending the message off. + * @note Requires the use of nl_send_auto_complete()! + */ +#define NL_AUTO_PID 0 + +/** + * @ingroup msg + * @brief + * May be used to refer to a sequence number which should be + * automatically set just before sending the message off. + * @note Requires the use of nl_send_auto_complete()! + */ +#define NL_AUTO_SEQ 0 + +struct nl_msg; +struct nl_tree; +struct ucred; + +/* size calculations */ +extern int nlmsg_msg_size(int); +extern int nlmsg_total_size(int); +extern int nlmsg_padlen(int); + +/* payload access */ +extern void * nlmsg_data(const struct nlmsghdr *); +extern int nlmsg_len(const struct nlmsghdr *); +extern void * nlmsg_tail(const struct nlmsghdr *); + +/* attribute access */ +extern struct nlattr * nlmsg_attrdata(const struct nlmsghdr *, int); +extern int nlmsg_attrlen(const struct nlmsghdr *, int); + +/* message parsing */ +extern int nlmsg_ok(const struct nlmsghdr *, int); +extern struct nlmsghdr * nlmsg_next(struct nlmsghdr *, int *); +extern int nlmsg_parse(struct nlmsghdr *, int, struct nlattr **, + int, struct nla_policy *); +extern struct nlattr * nlmsg_find_attr(struct nlmsghdr *, int, int); +extern int nlmsg_validate(struct nlmsghdr *, int, int, + struct nla_policy *); + +/* Backward compatibility */ +#define nlmsg_new() nlmsg_alloc() +#define nlmsg_build_simple(a, b) nlmsg_alloc_simple(a, b) +#define nlmsg_build(ptr) nlmsg_inherit(ptr) + +extern struct nl_msg * nlmsg_alloc(void); +extern struct nl_msg * nlmsg_alloc_simple(int, int); +extern struct nl_msg * nlmsg_inherit(struct nlmsghdr *); +extern struct nl_msg * nlmsg_convert(struct nlmsghdr *); +extern void * nlmsg_reserve(struct nl_msg *, size_t, int); +extern int nlmsg_append(struct nl_msg *, void *, size_t, int); + +extern struct nlmsghdr * nlmsg_put(struct nl_msg *, uint32_t, uint32_t, + int, int, int); +extern struct nlmsghdr * nlmsg_hdr(struct nl_msg *); +extern void nlmsg_free(struct nl_msg *); + +/* attribute modification */ +extern void nlmsg_set_proto(struct nl_msg *, int); +extern int nlmsg_get_proto(struct nl_msg *); +extern void nlmsg_set_src(struct nl_msg *, struct sockaddr_nl *); +extern struct sockaddr_nl *nlmsg_get_src(struct nl_msg *); +extern void nlmsg_set_dst(struct nl_msg *, struct sockaddr_nl *); +extern struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *); +extern void nlmsg_set_creds(struct nl_msg *, struct ucred *); +extern struct ucred * nlmsg_get_creds(struct nl_msg *); + +extern char * nl_nlmsgtype2str(int, char *, size_t); +extern int nl_str2nlmsgtype(const char *); + +extern char * nl_nlmsg_flags2str(int, char *, size_t); + +extern int nl_msg_parse(struct nl_msg *, + void (*cb)(struct nl_object *, void *), + void *); + +extern void nl_msg_dump(struct nl_msg *, FILE *); + +/** + * @name Iterators + * @{ + */ + +/** + * @ingroup msg + * Iterate over a stream of attributes in a message + * @arg pos loop counter, set to current attribute + * @arg nlh netlink message header + * @arg hdrlen length of family header + * @arg rem initialized to len, holds bytes currently remaining in stream + */ +#define nlmsg_for_each_attr(pos, nlh, hdrlen, rem) \ + nla_for_each_attr(pos, nlmsg_attrdata(nlh, hdrlen), \ + nlmsg_attrlen(nlh, hdrlen), rem) + +/** + * Iterate over a stream of messages + * @arg pos loop counter, set to current message + * @arg head head of message stream + * @arg len length of message stream + * @arg rem initialized to len, holds bytes currently remaining in stream + */ +#define nlmsg_for_each_msg(pos, head, len, rem) \ + for (pos = head, rem = len; \ + nlmsg_ok(pos, rem); \ + pos = nlmsg_next(pos, &(rem))) + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/netfilter/ct.h b/include/netlink/netfilter/ct.h new file mode 100644 index 0000000..b0bca94 --- /dev/null +++ b/include/netlink/netfilter/ct.h @@ -0,0 +1,115 @@ +/* + * netlink/netfilter/ct.h Conntrack + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#ifndef NETLINK_CT_H_ +#define NETLINK_CT_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nfnl_ct; + +extern struct nl_object_ops ct_obj_ops; + +/* General */ +extern struct nfnl_ct * nfnl_ct_alloc(void); +extern struct nl_cache *nfnl_ct_alloc_cache(struct nl_handle *); + +extern int nfnlmsg_ct_group(struct nlmsghdr *); +extern struct nfnl_ct * nfnlmsg_ct_parse(struct nlmsghdr *); + +extern void nfnl_ct_get(struct nfnl_ct *); +extern void nfnl_ct_put(struct nfnl_ct *); + +extern int nfnl_ct_dump_request(struct nl_handle *); + +extern void nfnl_ct_set_family(struct nfnl_ct *, uint8_t); +extern uint8_t nfnl_ct_get_family(const struct nfnl_ct *); + +extern void nfnl_ct_set_proto(struct nfnl_ct *, uint8_t); +extern int nfnl_ct_test_proto(const struct nfnl_ct *); +extern uint8_t nfnl_ct_get_proto(const struct nfnl_ct *); + +extern void nfnl_ct_set_tcp_state(struct nfnl_ct *, uint8_t); +extern int nfnl_ct_test_tcp_state(const struct nfnl_ct *); +extern uint8_t nfnl_ct_get_tcp_state(const struct nfnl_ct *); +extern char * nfnl_ct_tcp_state2str(uint8_t, char *, size_t); +extern int nfnl_ct_str2tcp_state(const char *name); + +extern void nfnl_ct_set_status(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_status(const struct nfnl_ct *); +extern uint32_t nfnl_ct_get_status(const struct nfnl_ct *); + +extern void nfnl_ct_set_timeout(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_timeout(const struct nfnl_ct *); +extern uint32_t nfnl_ct_get_timeout(const struct nfnl_ct *); + +extern void nfnl_ct_set_mark(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_mark(const struct nfnl_ct *); +extern uint32_t nfnl_ct_get_mark(const struct nfnl_ct *); + +extern void nfnl_ct_set_use(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_use(const struct nfnl_ct *); +extern uint32_t nfnl_ct_get_use(const struct nfnl_ct *); + +extern void nfnl_ct_set_id(struct nfnl_ct *, uint32_t); +extern int nfnl_ct_test_id(const struct nfnl_ct *); +extern uint32_t nfnl_ct_get_id(const struct nfnl_ct *); + +extern int nfnl_ct_set_src(struct nfnl_ct *, int, + struct nl_addr *); +extern struct nl_addr * nfnl_ct_get_src(const struct nfnl_ct *, int); + +extern int nfnl_ct_set_dst(struct nfnl_ct *, int, + struct nl_addr *); +extern struct nl_addr * nfnl_ct_get_dst(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_src_port(struct nfnl_ct *, int, uint16_t); +extern int nfnl_ct_test_src_port(const struct nfnl_ct *, int); +extern uint16_t nfnl_ct_get_src_port(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_dst_port(struct nfnl_ct *, int, uint16_t); +extern int nfnl_ct_test_dst_port(const struct nfnl_ct *, int); +extern uint16_t nfnl_ct_get_dst_port(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_icmp_id(struct nfnl_ct *, int, uint16_t); +extern int nfnl_ct_test_icmp_id(const struct nfnl_ct *, int); +extern uint16_t nfnl_ct_get_icmp_id(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_icmp_type(struct nfnl_ct *, int, uint8_t); +extern int nfnl_ct_test_icmp_type(const struct nfnl_ct *, int); +extern uint8_t nfnl_ct_get_icmp_type(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_icmp_code(struct nfnl_ct *, int, uint8_t); +extern int nfnl_ct_test_icmp_code(const struct nfnl_ct *, int); +extern uint8_t nfnl_ct_get_icmp_code(const struct nfnl_ct *, int); + +extern void nfnl_ct_set_packets(struct nfnl_ct *, int, uint64_t); +extern int nfnl_ct_test_packets(const struct nfnl_ct *, int); +extern uint64_t nfnl_ct_get_packets(const struct nfnl_ct *,int); + +extern void nfnl_ct_set_bytes(struct nfnl_ct *, int, uint64_t); +extern int nfnl_ct_test_bytes(const struct nfnl_ct *, int); +extern uint64_t nfnl_ct_get_bytes(const struct nfnl_ct *, int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/netfilter/log.h b/include/netlink/netfilter/log.h new file mode 100644 index 0000000..e65cc58 --- /dev/null +++ b/include/netlink/netfilter/log.h @@ -0,0 +1,105 @@ +/* + * netlink/netfilter/log.h Netfilter Log + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#ifndef NETLINK_LOG_H_ +#define NETLINK_LOG_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_handle; +struct nlmsghdr; +struct nfnl_log; + +extern struct nl_object_ops log_obj_ops; + +/* General */ +extern struct nfnl_log *nfnl_log_alloc(void); +extern struct nfnl_log *nfnlmsg_log_parse(struct nlmsghdr *); + +extern void nfnl_log_get(struct nfnl_log *); +extern void nfnl_log_put(struct nfnl_log *); + +extern struct nl_msg * nfnl_log_build_bind(uint16_t);; +extern int nfnl_log_bind(struct nl_handle *, uint16_t); +extern struct nl_msg * nfnl_log_build_unbind(uint16_t); +extern int nfnl_log_unbind(struct nl_handle *, uint16_t); +extern struct nl_msg * nfnl_log_build_pf_bind(uint8_t); +extern int nfnl_log_pf_bind(struct nl_handle *, uint8_t); +extern struct nl_msg * nfnl_log_build_pf_unbind(uint8_t); +extern int nfnl_log_pf_unbind(struct nl_handle *, uint8_t); +extern struct nl_msg * nfnl_log_build_mode(uint16_t, uint8_t, uint32_t); +extern int nfnl_log_set_mode(struct nl_handle *, uint16_t, + uint8_t, uint32_t); + +extern void nfnl_log_set_family(struct nfnl_log *, uint8_t); +extern uint8_t nfnl_log_get_family(const struct nfnl_log *); + +extern void nfnl_log_set_hwproto(struct nfnl_log *, uint16_t); +extern int nfnl_log_test_hwproto(const struct nfnl_log *); +extern uint16_t nfnl_log_get_hwproto(const struct nfnl_log *); + +extern void nfnl_log_set_hook(struct nfnl_log *, uint8_t); +extern int nfnl_log_test_hook(const struct nfnl_log *); +extern uint8_t nfnl_log_get_hook(const struct nfnl_log *); + +extern void nfnl_log_set_mark(struct nfnl_log *, uint32_t); +extern int nfnl_log_test_mark(const struct nfnl_log *); +extern uint32_t nfnl_log_get_mark(const struct nfnl_log *); + +extern void nfnl_log_set_timestamp(struct nfnl_log *, + struct timeval *); +extern const struct timeval *nfnl_log_get_timestamp(const struct nfnl_log *); + +extern void nfnl_log_set_indev(struct nfnl_log *, uint32_t); +extern uint32_t nfnl_log_get_indev(const struct nfnl_log *); + +extern void nfnl_log_set_outdev(struct nfnl_log *, uint32_t); +extern uint32_t nfnl_log_get_outdev(const struct nfnl_log *); + +extern void nfnl_log_set_physindev(struct nfnl_log *, uint32_t); +extern uint32_t nfnl_log_get_physindev(const struct nfnl_log *); + +extern void nfnl_log_set_physoutdev(struct nfnl_log *, uint32_t); +extern uint32_t nfnl_log_get_physoutdev(const struct nfnl_log *); + +extern void nfnl_log_set_hwaddr(struct nfnl_log *, uint8_t *, int); +extern const uint8_t * nfnl_log_get_hwaddr(const struct nfnl_log *, int *); + +extern int nfnl_log_set_payload(struct nfnl_log *, uint8_t *, int); +extern const void * nfnl_log_get_payload(const struct nfnl_log *, int *); + +extern int nfnl_log_set_prefix(struct nfnl_log *, void *); +extern const char * nfnl_log_get_prefix(const struct nfnl_log *); + +extern void nfnl_log_set_uid(struct nfnl_log *, uint32_t); +extern int nfnl_log_test_uid(const struct nfnl_log *); +extern uint32_t nfnl_log_get_uid(const struct nfnl_log *); + +extern void nfnl_log_set_seq(struct nfnl_log *, uint32_t); +extern int nfnl_log_test_seq(const struct nfnl_log *); +extern uint32_t nfnl_log_get_seq(const struct nfnl_log *); + +extern void nfnl_log_set_seq_global(struct nfnl_log *, uint32_t); +extern int nfnl_log_test_seq_global(const struct nfnl_log *); +extern uint32_t nfnl_log_get_seq_global(const struct nfnl_log *); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/include/netlink/netfilter/nfnl.h b/include/netlink/netfilter/nfnl.h new file mode 100644 index 0000000..123d93e --- /dev/null +++ b/include/netlink/netfilter/nfnl.h @@ -0,0 +1,44 @@ +/* + * netlink/nfnl/nfnl.h Netfilter Netlink + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#ifndef NETLINK_NFNL_H_ +#define NETLINK_NFNL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define NFNL_HDRLEN NLMSG_ALIGN(sizeof(struct nfgenmsg)) +#define NFNLMSG_TYPE(subsys, subtype) (((subsys) << 8) | (subtype)) + +extern int nfnl_connect(struct nl_handle *); + +extern uint8_t nfnlmsg_subsys(struct nlmsghdr *); +extern uint8_t nfnlmsg_subtype(struct nlmsghdr *); +extern uint8_t nfnlmsg_family(struct nlmsghdr *); +extern uint16_t nfnlmsg_res_id(struct nlmsghdr *); + +extern int nfnl_send_simple(struct nl_handle *, uint8_t, uint8_t, + int, uint8_t, uint16_t); +extern struct nl_msg * nfnlmsg_alloc_simple(uint8_t, uint8_t, int, + uint8_t, uint16_t); +extern int nfnlmsg_put(struct nl_msg *, uint32_t, uint32_t, + uint8_t, uint8_t, int, uint8_t, uint16_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/netlink-compat.h b/include/netlink/netlink-compat.h new file mode 100644 index 0000000..17ec9fc --- /dev/null +++ b/include/netlink/netlink-compat.h @@ -0,0 +1,50 @@ +/* + * netlink/netlink-compat.h Netlink Compatability + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_COMPAT_H_ +#define NETLINK_COMPAT_H_ + +#if !defined _LINUX_SOCKET_H && !defined _BITS_SOCKADDR_H +typedef unsigned short sa_family_t; +#endif + +#ifndef IFNAMSIZ +/** Maximum length of a interface name */ +#define IFNAMSIZ 16 +#endif + +/* patch 2.4.x if_arp */ +#ifndef ARPHRD_INFINIBAND +#define ARPHRD_INFINIBAND 32 +#endif + +/* patch 2.4.x eth header file */ +#ifndef ETH_P_MPLS_UC +#define ETH_P_MPLS_UC 0x8847 +#endif + +#ifndef ETH_P_MPLS_MC +#define ETH_P_MPLS_MC 0x8848 +#endif + +#ifndef ETH_P_EDP2 +#define ETH_P_EDP2 0x88A2 +#endif + +#ifndef ETH_P_HDLC +#define ETH_P_HDLC 0x0019 +#endif + +#ifndef AF_LLC +#define AF_LLC 26 +#endif + +#endif diff --git a/include/netlink/netlink-kernel.h b/include/netlink/netlink-kernel.h new file mode 100644 index 0000000..a0f5535 --- /dev/null +++ b/include/netlink/netlink-kernel.h @@ -0,0 +1,196 @@ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +/** + * Netlink socket address + * @ingroup nl + */ +struct sockaddr_nl +{ + /** socket family (AF_NETLINK) */ + sa_family_t nl_family; + + /** Padding (unused) */ + unsigned short nl_pad; + + /** Unique process ID */ + uint32_t nl_pid; + + /** Multicast group subscriptions */ + uint32_t nl_groups; +}; + +/** + * Netlink message header + * @ingroup msg + */ +struct nlmsghdr +{ + /** + * Length of message including header. + */ + uint32_t nlmsg_len; + + /** + * Message type (content type) + */ + uint16_t nlmsg_type; + + /** + * Message flags + */ + uint16_t nlmsg_flags; + + /** + * Sequence number + */ + uint32_t nlmsg_seq; + + /** + * Netlink PID of the proccess sending the message. + */ + uint32_t nlmsg_pid; +}; + +/** + * @name Standard message flags + * @{ + */ + +/** + * Must be set on all request messages (typically from user space to + * kernel space). + * @ingroup msg + */ +#define NLM_F_REQUEST 1 + +/** + * Indicates the message is part of a multipart message terminated + * by NLMSG_DONE. + */ +#define NLM_F_MULTI 2 + +/** + * Request for an acknowledgment on success. + */ +#define NLM_F_ACK 4 + +/** + * Echo this request + */ +#define NLM_F_ECHO 8 + +/** @} */ + +/** + * @name Additional message flags for GET requests + * @{ + */ + +/** + * Return the complete table instead of a single entry. + * @ingroup msg + */ +#define NLM_F_ROOT 0x100 + +/** + * Return all entries matching criteria passed in message content. + */ +#define NLM_F_MATCH 0x200 + +/** + * Return an atomic snapshot of the table being referenced. This + * may require special privileges because it has the potential to + * interrupt service in the FE for a longer time. + */ +#define NLM_F_ATOMIC 0x400 + +/** + * Dump all entries + */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/** @} */ + +/** + * @name Additional messsage flags for NEW requests + * @{ + */ + +/** + * Replace existing matching config object with this request. + * @ingroup msg + */ +#define NLM_F_REPLACE 0x100 + +/** + * Don't replace the config object if it already exists. + */ +#define NLM_F_EXCL 0x200 + +/** + * Create config object if it doesn't already exist. + */ +#define NLM_F_CREATE 0x400 + +/** + * Add to the end of the object list. + */ +#define NLM_F_APPEND 0x800 + +/** @} */ + +/** + * @name Standard Message types + * @{ + */ + +/** + * No operation, message must be ignored + * @ingroup msg + */ +#define NLMSG_NOOP 0x1 + +/** + * The message signals an error and the payload contains a nlmsgerr + * structure. This can be looked at as a NACK and typically it is + * from FEC to CPC. + */ +#define NLMSG_ERROR 0x2 + +/** + * Message terminates a multipart message. + */ +#define NLMSG_DONE 0x3 + +/** + * The message signals that data got lost + */ +#define NLMSG_OVERRUN 0x4 + +/** + * Lower limit of reserved message types + */ +#define NLMSG_MIN_TYPE 0x10 + +/** @} */ + +/** + * Netlink error message + * @ingroup msg + */ +struct nlmsgerr +{ + /** Error code (errno number) */ + int error; + + /** Original netlink message causing the error */ + struct nlmsghdr msg; +}; + +struct nl_pktinfo +{ + __u32 group; +}; + +#endif /* __LINUX_NETLINK_H */ diff --git a/include/netlink/netlink.h b/include/netlink/netlink.h new file mode 100644 index 0000000..138535b --- /dev/null +++ b/include/netlink/netlink.h @@ -0,0 +1,74 @@ +/* + * netlink/netlink.h Netlink Interface + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_NETLINK_H_ +#define NETLINK_NETLINK_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int nl_debug; +extern struct nl_dump_params nl_debug_dp; + +/* Connection Management */ +extern int nl_connect(struct nl_handle *, int); +extern void nl_close(struct nl_handle *); + +/* Send */ +extern int nl_sendto(struct nl_handle *, void *, size_t); +extern int nl_sendmsg(struct nl_handle *, struct nl_msg *, + struct msghdr *); +extern int nl_send(struct nl_handle *, struct nl_msg *); +extern int nl_send_auto_complete(struct nl_handle *, + struct nl_msg *); +extern int nl_send_simple(struct nl_handle *, int, int, + void *, size_t); + +/* Receive */ +extern int nl_recv(struct nl_handle *, + struct sockaddr_nl *, unsigned char **, + struct ucred **); + +extern int nl_recvmsgs(struct nl_handle *, struct nl_cb *); + +#define nl_recvmsgs_def(handle) nl_recvmsgs_default(handle) +extern int nl_recvmsgs_default(struct nl_handle *); + +extern int nl_wait_for_ack(struct nl_handle *); + +/* Netlink Family Translations */ +extern char * nl_nlfamily2str(int, char *, size_t); +extern int nl_str2nlfamily(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/object.h b/include/netlink/object.h new file mode 100644 index 0000000..751a1b3 --- /dev/null +++ b/include/netlink/object.h @@ -0,0 +1,64 @@ +/* + * netlink/object.c Generic Cacheable Object + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_OBJECT_H_ +#define NETLINK_OBJECT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_cache; +struct nl_object; +struct nl_object_ops; + +#define OBJ_CAST(ptr) ((struct nl_object *) (ptr)) + +/* General */ +extern struct nl_object * nl_object_alloc(struct nl_object_ops *); +extern struct nl_object * nl_object_alloc_name(const char *); +extern void nl_object_free(struct nl_object *); +extern struct nl_object * nl_object_clone(struct nl_object *obj); +extern void nl_object_get(struct nl_object *); +extern void nl_object_put(struct nl_object *); +extern int nl_object_shared(struct nl_object *); +extern void nl_object_dump(struct nl_object *, + struct nl_dump_params *); +extern int nl_object_identical(struct nl_object *, + struct nl_object *); +extern uint32_t nl_object_diff(struct nl_object *, + struct nl_object *); +extern int nl_object_match_filter(struct nl_object *, + struct nl_object *); +extern char * nl_object_attrs2str(struct nl_object *, + uint32_t attrs, char *buf, + size_t); +extern char * nl_object_attr_list(struct nl_object *, + char *, size_t); + +/* Marks */ +extern void nl_object_mark(struct nl_object *); +extern void nl_object_unmark(struct nl_object *); +extern int nl_object_is_marked(struct nl_object *); + +/* Access Functions */ +extern int nl_object_get_refcnt(struct nl_object *); +extern struct nl_cache * nl_object_get_cache(struct nl_object *); +extern inline void * nl_object_priv(struct nl_object *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/addr.h b/include/netlink/route/addr.h new file mode 100644 index 0000000..71a90e0 --- /dev/null +++ b/include/netlink/route/addr.h @@ -0,0 +1,91 @@ +/* + * netlink/route/addr.c rtnetlink addr layer + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Baruch Even , + * Mediatrix Telecom, inc. + */ + +#ifndef NETADDR_ADDR_H_ +#define NETADDR_ADDR_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_addr; + +/* General */ +extern struct rtnl_addr *rtnl_addr_alloc(void); +extern void rtnl_addr_put(struct rtnl_addr *); + +extern struct nl_cache *rtnl_addr_alloc_cache(struct nl_handle *); + +/* Address Addition */ +extern struct nl_msg * rtnl_addr_build_add_request(struct rtnl_addr *, int); +extern int rtnl_addr_add(struct nl_handle *, struct rtnl_addr *, + int); + +/* Address Deletion */ +extern struct nl_msg * rtnl_addr_build_delete_request(struct rtnl_addr *, int); +extern int rtnl_addr_delete(struct nl_handle *, + struct rtnl_addr *, int); + +/* Address Flags Translations */ +extern char * rtnl_addr_flags2str(int, char *, size_t); +extern int rtnl_addr_str2flags(const char *); + +/* Attribute Access */ +extern void rtnl_addr_set_label(struct rtnl_addr *, const char *); +extern char * rtnl_addr_get_label(struct rtnl_addr *); + +extern void rtnl_addr_set_ifindex(struct rtnl_addr *, int); +extern int rtnl_addr_get_ifindex(struct rtnl_addr *); + +extern void rtnl_addr_set_family(struct rtnl_addr *, int); +extern int rtnl_addr_get_family(struct rtnl_addr *); + +extern void rtnl_addr_set_prefixlen(struct rtnl_addr *, int); +extern int rtnl_addr_get_prefixlen(struct rtnl_addr *); + +extern void rtnl_addr_set_scope(struct rtnl_addr *, int); +extern int rtnl_addr_get_scope(struct rtnl_addr *); + +extern void rtnl_addr_set_flags(struct rtnl_addr *, unsigned int); +extern void rtnl_addr_unset_flags(struct rtnl_addr *, unsigned int); +extern unsigned int rtnl_addr_get_flags(struct rtnl_addr *); + +extern int rtnl_addr_set_local(struct rtnl_addr *, + struct nl_addr *); +extern struct nl_addr * rtnl_addr_get_local(struct rtnl_addr *); + +extern int rtnl_addr_set_peer(struct rtnl_addr *, + struct nl_addr *); +extern struct nl_addr * rtnl_addr_get_peer(struct rtnl_addr *); + +extern int rtnl_addr_set_broadcast(struct rtnl_addr *, + struct nl_addr *); +extern struct nl_addr * rtnl_addr_get_broadcast(struct rtnl_addr *); + +extern int rtnl_addr_set_anycast(struct rtnl_addr *, + struct nl_addr *); +extern struct nl_addr * rtnl_addr_get_anycast(struct rtnl_addr *); + +extern int rtnl_addr_set_multicast(struct rtnl_addr *, + struct nl_addr *); +extern struct nl_addr * rtnl_addr_get_multicast(struct rtnl_addr *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/class-modules.h b/include/netlink/route/class-modules.h new file mode 100644 index 0000000..2400a60 --- /dev/null +++ b/include/netlink/route/class-modules.h @@ -0,0 +1,73 @@ +/* + * netlink/route/class-modules.h Class Module API + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_CLASS_MODULES_H_ +#define NETLINK_CLASS_MODULES_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Class operations + * @ingroup class_api + */ +struct rtnl_class_ops +{ + /** + * Kind/Name of class + */ + char co_kind[32]; + + /** + * Dump callbacks + */ + int (*co_dump[NL_DUMP_MAX+1])(struct rtnl_class *, + struct nl_dump_params *, int); + + /** + * Must return the contents supposed to be in TCA_OPTIONS + */ + struct nl_msg *(*co_get_opts)(struct rtnl_class *); + + /** + * TCA_OPTIONS message parser + */ + int (*co_msg_parser)(struct rtnl_class *); + + /** + * Called before a class object gets destroyed + */ + void (*co_free_data)(struct rtnl_class *); + + /** + * Called whenever a class object needs to be cloned + */ + int (*co_clone)(struct rtnl_class *, struct rtnl_class *); + + /** + * INTERNAL (Do not use) + */ + struct rtnl_class_ops *co_next; +}; + +extern int rtnl_class_register(struct rtnl_class_ops *); +extern int rtnl_class_unregister(struct rtnl_class_ops *); +extern struct rtnl_class_ops * rtnl_class_lookup_ops(struct rtnl_class *); +extern struct rtnl_class_ops * __rtnl_class_lookup_ops(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/class.h b/include/netlink/route/class.h new file mode 100644 index 0000000..a624ef6 --- /dev/null +++ b/include/netlink/route/class.h @@ -0,0 +1,68 @@ +/* + * netlink/route/class.h Classes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_CLASS_H_ +#define NETLINK_CLASS_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_class; + +extern struct nl_object_ops class_obj_ops; + +/* General */ +extern struct rtnl_class * rtnl_class_alloc(void); +extern void rtnl_class_put(struct rtnl_class *); +extern struct nl_cache * rtnl_class_alloc_cache(struct nl_handle *, int); + +/* leaf qdisc access */ +extern struct rtnl_qdisc * rtnl_class_leaf_qdisc(struct rtnl_class *, + struct nl_cache *); + +/* class addition */ +extern struct nl_msg * rtnl_class_build_add_request(struct rtnl_class *, int); +extern int rtnl_class_add(struct nl_handle *, struct rtnl_class *, int); + +/* attribute modification */ +extern void rtnl_class_set_ifindex(struct rtnl_class *, int); +extern int rtnl_class_get_ifindex(struct rtnl_class *); +extern void rtnl_class_set_handle(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_class_get_handle(struct rtnl_class *); +extern void rtnl_class_set_parent(struct rtnl_class *, uint32_t); +extern uint32_t rtnl_class_get_parent(struct rtnl_class *); +extern void rtnl_class_set_kind(struct rtnl_class *, const char *); +extern char * rtnl_class_get_kind(struct rtnl_class *); +extern uint64_t rtnl_class_get_stat(struct rtnl_class *, + enum rtnl_tc_stats_id); + +/* iterators */ +extern void rtnl_class_foreach_child(struct rtnl_class *, + struct nl_cache *, + void (*cb)(struct nl_object *, + void *), + void *); +extern void rtnl_class_foreach_cls(struct rtnl_class *, + struct nl_cache *, + void (*cb)(struct nl_object *, + void *), + void *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/classifier-modules.h b/include/netlink/route/classifier-modules.h new file mode 100644 index 0000000..8c31e67 --- /dev/null +++ b/include/netlink/route/classifier-modules.h @@ -0,0 +1,72 @@ +/* + * netlink/route/classifier-modules.h Classifier Module API + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_CLASS_MODULES_H_ +#define NETLINK_CLASS_MODULES_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Classifier operations + * @ingroup cls_api + */ +struct rtnl_cls_ops +{ + /** + * Kind/Name of classifier + */ + char co_kind[32]; + + /** + * Dump callbacks + */ + int (*co_dump[NL_DUMP_MAX+1])(struct rtnl_cls *, + struct nl_dump_params *, int); + /** + * Must return the contents supposed to be in TCA_OPTIONS + */ + struct nl_msg *(*co_get_opts)(struct rtnl_cls *); + + /** + * TCA_OPTIONS message parser + */ + int (*co_msg_parser)(struct rtnl_cls *); + + /** + * Called before a class object gets destroyed + */ + void (*co_free_data)(struct rtnl_cls *); + + /** + * Called whenever a classifier object needs to be cloned + */ + int (*co_clone)(struct rtnl_cls *, struct rtnl_cls *); + + /** + * INTERNAL (Do not use) + */ + struct rtnl_cls_ops *co_next; +}; + +extern int rtnl_cls_register(struct rtnl_cls_ops *); +extern int rtnl_cls_unregister(struct rtnl_cls_ops *); +extern struct rtnl_cls_ops * rtnl_cls_lookup_ops(struct rtnl_cls *); +extern struct rtnl_cls_ops * __rtnl_cls_lookup_ops(const char *kind); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/classifier.h b/include/netlink/route/classifier.h new file mode 100644 index 0000000..7ef0da4 --- /dev/null +++ b/include/netlink/route/classifier.h @@ -0,0 +1,56 @@ +/* + * netlink/route/classifier.h Classifiers + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_CLASSIFIER_H_ +#define NETLINK_CLASSIFIER_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct nl_object_ops cls_obj_ops; + +extern struct rtnl_cls *rtnl_cls_alloc(void); +extern void rtnl_cls_put(struct rtnl_cls *); + +extern struct nl_cache *rtnl_cls_alloc_cache(struct nl_handle *, int, uint32_t); + +/* classifier addition */ +extern int rtnl_cls_add(struct nl_handle *, struct rtnl_cls *, + int); +extern struct nl_msg * rtnl_cls_build_add_request(struct rtnl_cls *, int); + +extern struct nl_msg *rtnl_cls_build_change_request(struct rtnl_cls *, int); +extern struct nl_msg *rtnl_cls_build_delete_request(struct rtnl_cls *, int); +extern int rtnl_cls_delete(struct nl_handle *, struct rtnl_cls *, int); + +/* attribute modification */ +extern void rtnl_cls_set_ifindex(struct rtnl_cls *, int); +extern void rtnl_cls_set_handle(struct rtnl_cls *, uint32_t); +extern void rtnl_cls_set_parent(struct rtnl_cls *, uint32_t); +extern void rtnl_cls_set_kind(struct rtnl_cls *, const char *); + +extern void rtnl_cls_set_prio(struct rtnl_cls *, int); +extern int rtnl_cls_get_prio(struct rtnl_cls *); + +extern void rtnl_cls_set_protocol(struct rtnl_cls *, int); +extern int rtnl_cls_get_protocol(struct rtnl_cls *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/cls/fw.h b/include/netlink/route/cls/fw.h new file mode 100644 index 0000000..39878de --- /dev/null +++ b/include/netlink/route/cls/fw.h @@ -0,0 +1,29 @@ +/* + * netlink/route/cls/fw.h fw classifier + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2006 Petr Gotthard + * Copyright (c) 2006 Siemens AG Oesterreich + */ + +#ifndef NETLINK_FW_H_ +#define NETLINK_FW_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_fw_set_classid(struct rtnl_cls *, uint32_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/cls/police.h b/include/netlink/route/cls/police.h new file mode 100644 index 0000000..cd1efb0 --- /dev/null +++ b/include/netlink/route/cls/police.h @@ -0,0 +1,29 @@ +/* + * netlink/route/cls/police.h Policer + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_CLS_POLICE_H_ +#define NETLINK_CLS_POLICE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern char * nl_police2str(int, char *, size_t); +extern int nl_str2police(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/cls/u32.h b/include/netlink/route/cls/u32.h new file mode 100644 index 0000000..cf35e26 --- /dev/null +++ b/include/netlink/route/cls/u32.h @@ -0,0 +1,43 @@ +/* + * netlink/route/cls/u32.h u32 classifier + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_U32_H_ +#define NETLINK_U32_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_u32_set_handle(struct rtnl_cls *, int, int, int); +extern int rtnl_u32_set_classid(struct rtnl_cls *, uint32_t); + +extern int rtnl_u32_set_flags(struct rtnl_cls *, int); +extern int rtnl_u32_add_key(struct rtnl_cls *, uint32_t, uint32_t, + int, int); +extern int rtnl_u32_add_key_uint8(struct rtnl_cls *, uint8_t, uint8_t, + int, int); +extern int rtnl_u32_add_key_uint16(struct rtnl_cls *, uint16_t, uint16_t, + int, int); +extern int rtnl_u32_add_key_uint32(struct rtnl_cls *, uint32_t, uint32_t, + int, int); +extern int rtnl_u32_add_key_in_addr(struct rtnl_cls *, struct in_addr *, + uint8_t, int, int); +extern int rtnl_u32_add_key_in6_addr(struct rtnl_cls *, struct in6_addr *, + uint8_t, int, int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/link.h b/include/netlink/route/link.h new file mode 100644 index 0000000..8bcae24 --- /dev/null +++ b/include/netlink/route/link.h @@ -0,0 +1,151 @@ +/* + * netlink/route/link.h Links (Interfaces) + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_LINK_H_ +#define NETLINK_LINK_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_link; + +enum rtnl_link_st { + RTNL_LINK_RX_PACKETS, + RTNL_LINK_TX_PACKETS, + RTNL_LINK_RX_BYTES, + RTNL_LINK_TX_BYTES, + RTNL_LINK_RX_ERRORS, + RTNL_LINK_TX_ERRORS, + RTNL_LINK_RX_DROPPED, + RTNL_LINK_TX_DROPPED, + RTNL_LINK_RX_COMPRESSED, + RTNL_LINK_TX_COMPRESSED, + RTNL_LINK_RX_FIFO_ERR, + RTNL_LINK_TX_FIFO_ERR, + RTNL_LINK_RX_LEN_ERR, + RTNL_LINK_RX_OVER_ERR, + RTNL_LINK_RX_CRC_ERR, + RTNL_LINK_RX_FRAME_ERR, + RTNL_LINK_RX_MISSED_ERR, + RTNL_LINK_TX_ABORT_ERR, + RTNL_LINK_TX_CARRIER_ERR, + RTNL_LINK_TX_HBEAT_ERR, + RTNL_LINK_TX_WIN_ERR, + RTNL_LINK_TX_COLLISIONS, + RTNL_LINK_MULTICAST, + __RTNL_LINK_STATS_MAX, +}; + +#define RTNL_LINK_STATS_MAX (__RTNL_LINK_STATS_MAX - 1) + +/** + * Special interface index stating the link was not found. + * @ingroup link + */ +#define RTNL_LINK_NOT_FOUND -1 + +/* link object allocation/freeage */ +extern struct rtnl_link * rtnl_link_alloc(void); +extern void rtnl_link_put(struct rtnl_link *); +extern void rtnl_link_free(struct rtnl_link *); + +/* link cache management */ +extern struct nl_cache * rtnl_link_alloc_cache(struct nl_handle *); +extern struct rtnl_link * rtnl_link_get(struct nl_cache *, int); +extern struct rtnl_link * rtnl_link_get_by_name(struct nl_cache *, + const char *); + + +/* Link Modifications */ +extern struct nl_msg * rtnl_link_build_change_request(struct rtnl_link *, + struct rtnl_link *, + int); +extern int rtnl_link_change(struct nl_handle *, + struct rtnl_link *, + struct rtnl_link *, int); + +/* Name <-> Index Translations */ +extern char * rtnl_link_i2name(struct nl_cache *, int, + char *, size_t); +extern int rtnl_link_name2i(struct nl_cache *, + const char *); + +/* Name <-> Statistic Translations */ +extern char * rtnl_link_stat2str(int, char *, size_t); +extern int rtnl_link_str2stat(const char *); + +/* Link Flags Translations */ +extern char * rtnl_link_flags2str(int, char *, size_t); +extern int rtnl_link_str2flags(const char *); + +/* Access Functions */ +extern void rtnl_link_set_qdisc(struct rtnl_link *, + const char *); +extern char * rtnl_link_get_qdisc(struct rtnl_link *); + +extern void rtnl_link_set_name(struct rtnl_link *, + const char *); +extern char * rtnl_link_get_name(struct rtnl_link *); + +extern void rtnl_link_set_flags(struct rtnl_link *, + unsigned int); +extern void rtnl_link_unset_flags(struct rtnl_link *, + unsigned int); +extern unsigned int rtnl_link_get_flags(struct rtnl_link *); + +extern void rtnl_link_set_mtu(struct rtnl_link *, + unsigned int); +extern unsigned int rtnl_link_get_mtu(struct rtnl_link *); + +extern void rtnl_link_set_txqlen(struct rtnl_link *, + unsigned int); +extern unsigned int rtnl_link_get_txqlen(struct rtnl_link *); + +extern void rtnl_link_set_weight(struct rtnl_link *, + unsigned int); +extern unsigned int rtnl_link_get_weight(struct rtnl_link *); + +extern void rtnl_link_set_ifindex(struct rtnl_link *, int); +extern int rtnl_link_get_ifindex(struct rtnl_link *); + +extern void rtnl_link_set_family(struct rtnl_link *, int); +extern int rtnl_link_get_family(struct rtnl_link *); + +extern void rtnl_link_set_arptype(struct rtnl_link *, + unsigned int); +extern unsigned int rtnl_link_get_arptype(struct rtnl_link *); + +extern void rtnl_link_set_addr(struct rtnl_link *, + struct nl_addr *); +extern struct nl_addr * rtnl_link_get_addr(struct rtnl_link *); + +extern void rtnl_link_set_broadcast(struct rtnl_link *, + struct nl_addr *); +extern struct nl_addr * rtnl_link_get_broadcast(struct rtnl_link *); + +extern void rtnl_link_set_link(struct rtnl_link *, int); +extern int rtnl_link_get_link(struct rtnl_link *); + +extern void rtnl_link_set_master(struct rtnl_link *, int); +extern int rtnl_link_get_master(struct rtnl_link *); + +extern uint64_t rtnl_link_get_stat(struct rtnl_link *, int); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/neighbour.h b/include/netlink/route/neighbour.h new file mode 100644 index 0000000..078c3f4 --- /dev/null +++ b/include/netlink/route/neighbour.h @@ -0,0 +1,91 @@ +/* + * netlink/route/neighbour.h Neighbours + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_NEIGHBOUR_H_ +#define NETLINK_NEIGHBOUR_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_neigh; + +/* neighbour object allocation/freeage */ +extern struct rtnl_neigh * rtnl_neigh_alloc(void); +extern void rtnl_neigh_put(struct rtnl_neigh *); + +/* neighbour cache management */ +extern struct nl_cache * rtnl_neigh_alloc_cache(struct nl_handle *); +extern struct rtnl_neigh * rtnl_neigh_get(struct nl_cache *, int, + struct nl_addr *); + +/* Neigbour state translations */ +extern char * rtnl_neigh_state2str(int, char *, size_t); +extern int rtnl_neigh_str2state(const char *); + +/* Neighbour flags translations */ +extern char * rtnl_neigh_flags2str(int, char *, size_t); +extern int rtnl_neigh_str2flag(const char *); + +/* Neighbour Addition */ +extern int rtnl_neigh_add(struct nl_handle *, + struct rtnl_neigh *, int); +extern struct nl_msg * rtnl_neigh_build_add_request(struct rtnl_neigh *, int); + +/* Neighbour Modification */ +extern int rtnl_neigh_change(struct nl_handle *, + struct rtnl_neigh *, int); +extern struct nl_msg * rtnl_neigh_build_change_request(struct rtnl_neigh *, int); + +/* Neighbour Deletion */ +extern int rtnl_neigh_delete(struct nl_handle *, + struct rtnl_neigh *, int); +extern struct nl_msg * rtnl_neigh_build_delete_request(struct rtnl_neigh *, int); + +/* Access functions */ +extern void rtnl_neigh_set_state(struct rtnl_neigh *, int); +extern int rtnl_neigh_get_state(struct rtnl_neigh *); +extern void rtnl_neigh_unset_state(struct rtnl_neigh *, + int); + +extern void rtnl_neigh_set_flags(struct rtnl_neigh *, + unsigned int); +extern void rtnl_neigh_unset_flags(struct rtnl_neigh *, + unsigned int); +extern unsigned int rtnl_neigh_get_flags(struct rtnl_neigh *); + +extern void rtnl_neigh_set_ifindex(struct rtnl_neigh *, + int); +extern int rtnl_neigh_get_ifindex(struct rtnl_neigh *); + +extern void rtnl_neigh_set_lladdr(struct rtnl_neigh *, + struct nl_addr *); +extern struct nl_addr * rtnl_neigh_get_lladdr(struct rtnl_neigh *); + +extern int rtnl_neigh_set_dst(struct rtnl_neigh *, + struct nl_addr *); +extern struct nl_addr * rtnl_neigh_get_dst(struct rtnl_neigh *); + +extern void rtnl_neigh_set_type(struct rtnl_neigh *, int); +extern int rtnl_neigh_get_type(struct rtnl_neigh *); + +extern void rtnl_neigh_set_family(struct rtnl_neigh *, int); +extern int rtnl_neigh_get_family(struct rtnl_neigh *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/neightbl.h b/include/netlink/route/neightbl.h new file mode 100644 index 0000000..20285ee --- /dev/null +++ b/include/netlink/route/neightbl.h @@ -0,0 +1,64 @@ +/* + * netlink/route/neightbl.h Neighbour Tables + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_NEIGHTBL_H_ +#define NETLINK_NEIGHTBL_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_neightbl; + +extern struct rtnl_neightbl *rtnl_neightbl_alloc(void); +extern void rtnl_neightbl_put(struct rtnl_neightbl *); +extern void rtnl_neightbl_free(struct rtnl_neightbl *); +extern struct nl_cache *rtnl_neightbl_alloc_cache(struct nl_handle *); +extern struct rtnl_neightbl *rtnl_neightbl_get(struct nl_cache *, + const char *, int); +extern void rtnl_neightbl_dump(struct rtnl_neightbl *, FILE *, + struct nl_dump_params *); + +extern struct nl_msg *rtnl_neightbl_build_change_request(struct rtnl_neightbl *, + struct rtnl_neightbl *); +extern int rtnl_neightbl_change(struct nl_handle *, struct rtnl_neightbl *, + struct rtnl_neightbl *); + +extern void rtnl_neightbl_set_family(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_gc_tresh1(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_gc_tresh2(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_gc_tresh3(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_name(struct rtnl_neightbl *, const char *); +extern void rtnl_neightbl_set_dev(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_queue_len(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_proxy_queue_len(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_app_probes(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_ucast_probes(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_mcast_probes(struct rtnl_neightbl *, int); +extern void rtnl_neightbl_set_base_reachable_time(struct rtnl_neightbl *, + uint64_t); +extern void rtnl_neightbl_set_retrans_time(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_gc_stale_time(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_delay_probe_time(struct rtnl_neightbl *, + uint64_t); +extern void rtnl_neightbl_set_anycast_delay(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_proxy_delay(struct rtnl_neightbl *, uint64_t); +extern void rtnl_neightbl_set_locktime(struct rtnl_neightbl *, uint64_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/nexthop.h b/include/netlink/route/nexthop.h new file mode 100644 index 0000000..984f4b5 --- /dev/null +++ b/include/netlink/route/nexthop.h @@ -0,0 +1,41 @@ +/* + * netlink/route/nexthop.h Routing Nexthop + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_ROUTE_NEXTHOP_H_ +#define NETLINK_ROUTE_NEXTHOP_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_nexthop; + +extern struct rtnl_nexthop * rtnl_route_nh_alloc(void); +extern struct rtnl_nexthop * rtnl_route_nh_clone(struct rtnl_nexthop *); +extern void rtnl_route_nh_free(struct rtnl_nexthop *); +extern void rtnl_route_nh_set_weight(struct rtnl_nexthop *, int); +extern void rtnl_route_nh_set_ifindex(struct rtnl_nexthop *, int); +extern void rtnl_route_nh_set_gateway(struct rtnl_nexthop *, + struct nl_addr *); +extern void rtnl_route_nh_set_flags(struct rtnl_nexthop *, + unsigned int); +extern void rtnl_route_nh_unset_flags(struct rtnl_nexthop *, + unsigned int); +extern unsigned int rtnl_route_nh_get_flags(struct rtnl_nexthop *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/qdisc-modules.h b/include/netlink/route/qdisc-modules.h new file mode 100644 index 0000000..802eac4 --- /dev/null +++ b/include/netlink/route/qdisc-modules.h @@ -0,0 +1,73 @@ +/* + * netlink/route/qdisc-modules.h Qdisc Module API + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_QDISC_MODULES_H_ +#define NETLINK_QDISC_MODULES_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Qdisc Operations + * @ingroup qdisc + */ +struct rtnl_qdisc_ops +{ + /** + * Kind/Name of Qdisc + */ + char qo_kind[32]; + + /** + * Dump callbacks + */ + int (*qo_dump[NL_DUMP_MAX+1])(struct rtnl_qdisc *, + struct nl_dump_params *, int); + + /** + * Must return the contents supposed to be in TCA_OPTIONS + */ + struct nl_msg *(*qo_get_opts)(struct rtnl_qdisc *); + + /** + * TCA_OPTIONS message parser + */ + int (*qo_msg_parser)(struct rtnl_qdisc *); + + /** + * Called before a Qdisc object gets destroyed + */ + void (*qo_free_data)(struct rtnl_qdisc *); + + /** + * Called whenever a qdisc object needs to be cloned + */ + int (*qo_clone)(struct rtnl_qdisc *, struct rtnl_qdisc *); + + /** + * INTERNAL (Do not use) + */ + struct rtnl_qdisc_ops *qo_next; +}; + +extern int rtnl_qdisc_register(struct rtnl_qdisc_ops *); +extern int rtnl_qdisc_unregister(struct rtnl_qdisc_ops *); +extern struct rtnl_qdisc_ops * rtnl_qdisc_lookup_ops(struct rtnl_qdisc *); +extern struct rtnl_qdisc_ops * __rtnl_qdisc_lookup_ops(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/qdisc.h b/include/netlink/route/qdisc.h new file mode 100644 index 0000000..ee71304 --- /dev/null +++ b/include/netlink/route/qdisc.h @@ -0,0 +1,87 @@ +/* + * netlink/route/qdisc.h Queueing Disciplines + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_QDISC_H_ +#define NETLINK_QDISC_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_qdisc; + +extern struct nl_object_ops qdisc_obj_ops; + +/* General */ +extern struct rtnl_qdisc * rtnl_qdisc_alloc(void); +extern void rtnl_qdisc_put(struct rtnl_qdisc *); + +/* Cache Management */ +extern struct nl_cache * rtnl_qdisc_alloc_cache(struct nl_handle *); +extern struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *, + int, uint32_t); +extern struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *, + int, uint32_t); + +/* qdisc addition */ +extern struct nl_msg * rtnl_qdisc_build_add_request(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_add(struct nl_handle *, struct rtnl_qdisc *, + int); + +/* qdisc modification */ +extern struct nl_msg * rtnl_qdisc_build_change_request(struct rtnl_qdisc *, + struct rtnl_qdisc *); +extern int rtnl_qdisc_change(struct nl_handle *, + struct rtnl_qdisc *, + struct rtnl_qdisc *); + +/* qdisc deletion */ +extern struct nl_msg * rtnl_qdisc_build_delete_request(struct rtnl_qdisc *); +extern int rtnl_qdisc_delete(struct nl_handle *, + struct rtnl_qdisc *); + +/* attribute modifications */ +extern void rtnl_qdisc_set_ifindex(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_get_ifindex(struct rtnl_qdisc *); +extern void rtnl_qdisc_set_handle(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_qdisc_get_handle(struct rtnl_qdisc *); +extern void rtnl_qdisc_set_parent(struct rtnl_qdisc *, uint32_t); +extern uint32_t rtnl_qdisc_get_parent(struct rtnl_qdisc *); +extern void rtnl_qdisc_set_kind(struct rtnl_qdisc *, const char *); +extern char * rtnl_qdisc_get_kind(struct rtnl_qdisc *); +extern uint64_t rtnl_qdisc_get_stat(struct rtnl_qdisc *, + enum rtnl_tc_stats_id); + +/* iterators */ +extern void rtnl_qdisc_foreach_child(struct rtnl_qdisc *, + struct nl_cache *, + void (*cb)(struct nl_object *, + void *), + void *); + +extern void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *, + struct nl_cache *, + void (*cb)(struct nl_object *, + void *), + void *); + +/* qdisc specific options */ +extern struct nl_msg * rtnl_qdisc_get_opts(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/route.h b/include/netlink/route/route.h new file mode 100644 index 0000000..f59f36b --- /dev/null +++ b/include/netlink/route/route.h @@ -0,0 +1,125 @@ +/* + * netlink/route/route.h Routes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_ROUTE_H_ +#define NETLINK_ROUTE_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_route; + +struct rtnl_rtcacheinfo +{ + uint32_t rtci_clntref; + uint32_t rtci_last_use; + uint32_t rtci_expires; + int32_t rtci_error; + uint32_t rtci_used; + uint32_t rtci_id; + uint32_t rtci_ts; + uint32_t rtci_tsage; +}; + +extern struct nl_object_ops route_obj_ops; + +/* General */ +extern struct rtnl_route * rtnl_route_alloc(void); +extern void rtnl_route_put(struct rtnl_route *); +extern struct nl_cache * rtnl_route_alloc_cache(struct nl_handle *); + +extern void rtnl_route_get(struct rtnl_route *); +extern void rtnl_route_put(struct rtnl_route *); + +extern struct nl_msg *rtnl_route_build_add_request(struct rtnl_route *, int); +extern int rtnl_route_add(struct nl_handle *, struct rtnl_route *, int); +extern struct nl_msg *rtnl_route_build_del_request(struct rtnl_route *, int); +extern int rtnl_route_del(struct nl_handle *, struct rtnl_route *, int); + +extern void rtnl_route_set_table(struct rtnl_route *, int); +extern int rtnl_route_get_table(struct rtnl_route *); +extern void rtnl_route_set_scope(struct rtnl_route *, int); +extern int rtnl_route_get_scope(struct rtnl_route *); +extern void rtnl_route_set_tos(struct rtnl_route *, int); +extern int rtnl_route_get_tos(struct rtnl_route *); +extern void rtnl_route_set_realms(struct rtnl_route *, realm_t); +extern realm_t rtnl_route_get_realms(struct rtnl_route *); +extern void rtnl_route_set_protocol(struct rtnl_route *, int); +extern int rtnl_route_get_protocol(struct rtnl_route *); +extern void rtnl_route_set_prio(struct rtnl_route *, int); +extern int rtnl_route_get_prio(struct rtnl_route *); +extern void rtnl_route_set_family(struct rtnl_route *, int); +extern int rtnl_route_get_family(struct rtnl_route *); +extern void rtnl_route_set_type(struct rtnl_route *, int); +extern int rtnl_route_get_type(struct rtnl_route *); +extern void rtnl_route_set_flags(struct rtnl_route *, + unsigned int); +extern void rtnl_route_unset_flags(struct rtnl_route *, + unsigned int); +extern unsigned int rtnl_route_get_flags(struct rtnl_route *); +extern int rtnl_route_set_metric(struct rtnl_route *, int, + unsigned int); +extern int rtnl_route_unset_metric(struct rtnl_route *, int); +extern unsigned int rtnl_route_get_metric(struct rtnl_route *, int); +extern int rtnl_route_set_dst(struct rtnl_route *, + struct nl_addr *); +extern struct nl_addr * rtnl_route_get_dst(struct rtnl_route *); +extern int rtnl_route_set_src(struct rtnl_route *, + struct nl_addr *); +extern struct nl_addr * rtnl_route_get_src(struct rtnl_route *); +extern int rtnl_route_set_gateway(struct rtnl_route *, + struct nl_addr *); +extern struct nl_addr * rtnl_route_get_gateway(struct rtnl_route *); +extern int rtnl_route_set_pref_src(struct rtnl_route *, + struct nl_addr *); +extern struct nl_addr * rtnl_route_get_pref_src(struct rtnl_route *); +extern void rtnl_route_set_oif(struct rtnl_route *, int); +extern int rtnl_route_get_oif(struct rtnl_route *); +extern void rtnl_route_set_iif(struct rtnl_route *, const char *); +extern char * rtnl_route_get_iif(struct rtnl_route *); +extern int rtnl_route_get_dst_len(struct rtnl_route *); +extern int rtnl_route_get_src_len(struct rtnl_route *); + +extern void rtnl_route_add_nexthop(struct rtnl_route *, + struct rtnl_nexthop *); +extern void rtnl_route_remove_nexthop(struct rtnl_nexthop *); +extern struct nl_list_head * rtnl_route_get_nexthops(struct rtnl_route *); +extern void rtnl_route_set_cacheinfo(struct rtnl_route *, + struct rtnl_rtcacheinfo *); +extern uint32_t rtnl_route_get_mp_algo(struct rtnl_route *); +extern void rtnl_route_set_mp_algo(struct rtnl_route *, uint32_t); + +extern char * rtnl_route_table2str(int, char *, size_t); +extern int rtnl_route_str2table(const char *); +extern int rtnl_route_read_table_names(const char *); + +extern char * rtnl_route_proto2str(int, char *, size_t); +extern int rtnl_route_str2proto(const char *); +extern int rtnl_route_read_protocol_names(const char *); + +extern char * rtnl_route_metric2str(int, char *, size_t); +extern int rtnl_route_str2metric(const char *); + +extern char * rtnl_route_nh_flags2str(int, char *, size_t); +extern int rtnl_route_nh_str2flags(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/rtnl.h b/include/netlink/route/rtnl.h new file mode 100644 index 0000000..9d116cd --- /dev/null +++ b/include/netlink/route/rtnl.h @@ -0,0 +1,71 @@ +/* + * netlink/route/rtnl.h Routing Netlink + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_RTNL_H_ +#define NETLINK_RTNL_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Realms + * @{ + */ + +typedef uint32_t realm_t; + +/** + * Mask specying the size of each realm part + * @ingroup rtnl + */ +#define RTNL_REALM_MASK (0xFFFF) + +/** + * Extract FROM realm from a realms field + */ +#define RTNL_REALM_FROM(realm) ((realm) >> 16) + +/** + * Extract TO realm from a realms field + */ +#define RTNL_REALM_TO(realm) ((realm) & RTNL_REALM_MASK) + +/** + * Build a realms field + */ +#define RTNL_MAKE_REALM(from, to) \ + ((RTNL_REALM_TO(from) << 16) & RTNL_REALM_TO(to)) + +/** @} */ + + +/* General */ +extern int nl_rtgen_request(struct nl_handle *, int, int, int); + +/* Routing Type Translations */ +extern char * nl_rtntype2str(int, char *, size_t); +extern int nl_str2rtntype(const char *); + +/* Scope Translations */ +extern char * rtnl_scope2str(int, char *, size_t); +extern int rtnl_str2scope(const char *); + +/* Realms Translations */ +extern char * rtnl_realms2str(uint32_t, char *, size_t); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/rule.h b/include/netlink/route/rule.h new file mode 100644 index 0000000..d295b0d --- /dev/null +++ b/include/netlink/route/rule.h @@ -0,0 +1,79 @@ +/* + * netlink/route/rule.h Rules + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_RULE_H_ +#define NETLINK_RULE_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct rtnl_rule; + +/* General */ +extern struct rtnl_rule * rtnl_rule_alloc(void); +extern void rtnl_rule_put(struct rtnl_rule *); + +extern struct nl_cache * rtnl_rule_alloc_cache(struct nl_handle *); +extern struct nl_cache * rtnl_rule_alloc_cache_by_family(struct nl_handle *, + int); +extern void rtnl_rule_dump(struct rtnl_rule *, FILE *, struct nl_dump_params *); + +extern struct nl_msg * rtnl_rule_build_add_request(struct rtnl_rule *, int); +extern int rtnl_rule_add(struct nl_handle *, struct rtnl_rule *, int); +extern struct nl_msg * rtnl_rule_build_delete_request(struct rtnl_rule *, int); +extern int rtnl_rule_delete(struct nl_handle *, struct rtnl_rule *, int); + + +/* attribute modification */ +extern void rtnl_rule_set_family(struct rtnl_rule *, int); +extern int rtnl_rule_get_family(struct rtnl_rule *); +extern void rtnl_rule_set_prio(struct rtnl_rule *, int); +extern int rtnl_rule_get_prio(struct rtnl_rule *); +#define rtnl_rule_set_fwmark(ptr, n) rtnl_rule_set_mark(ptr, n) +extern void rtnl_rule_set_mark(struct rtnl_rule *, uint64_t); +#define rtnl_rule_get_fwmark(ptr) rtnl_rule_get_mark(ptr) +extern uint64_t rtnl_rule_get_mark(struct rtnl_rule *); +extern void rtnl_rule_set_table(struct rtnl_rule *, int); +extern int rtnl_rule_get_table(struct rtnl_rule *); +extern void rtnl_rule_set_dsfield(struct rtnl_rule *, int); +extern int rtnl_rule_get_dsfield(struct rtnl_rule *); +extern int rtnl_rule_set_src(struct rtnl_rule *, struct nl_addr *); +extern struct nl_addr * rtnl_rule_get_src(struct rtnl_rule *); +extern int rtnl_rule_set_dst(struct rtnl_rule *, struct nl_addr *); +extern struct nl_addr * rtnl_rule_get_dst(struct rtnl_rule *); +extern void rtnl_rule_set_src_len(struct rtnl_rule *, int); +extern int rtnl_rule_get_src_len(struct rtnl_rule *); +extern void rtnl_rule_set_dst_len(struct rtnl_rule *, int); +extern int rtnl_rule_get_dst_len(struct rtnl_rule *); + +extern void rtnl_rule_set_action(struct rtnl_rule *, int); +extern int rtnl_rule_get_action(struct rtnl_rule *); + +extern int rtnl_rule_set_iif(struct rtnl_rule *, const char *); +extern char * rtnl_rule_get_iif(struct rtnl_rule *); + +extern void rtnl_rule_set_classid(struct rtnl_rule *, uint32_t); +extern uint32_t rtnl_rule_get_classid(struct rtnl_rule *); + +extern void rtnl_rule_set_realms(struct rtnl_rule *, realm_t); +extern realm_t rtnl_rule_get_realms(struct rtnl_rule *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/sch/cbq.h b/include/netlink/route/sch/cbq.h new file mode 100644 index 0000000..3dbdd2d --- /dev/null +++ b/include/netlink/route/sch/cbq.h @@ -0,0 +1,30 @@ +/* + * netlink/route/sch/cbq.h Class Based Queueing + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_CBQ_H_ +#define NETLINK_CBQ_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern char * nl_ovl_strategy2str(int, char *, size_t); +extern int nl_str2ovl_strategy(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/sch/dsmark.h b/include/netlink/route/sch/dsmark.h new file mode 100644 index 0000000..de65496 --- /dev/null +++ b/include/netlink/route/sch/dsmark.h @@ -0,0 +1,41 @@ +/* + * netlink/route/sch/dsmark.h DSMARK + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_DSMARK_H_ +#define NETLINK_DSMARK_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_class_dsmark_set_bmask(struct rtnl_class *, uint8_t); +extern int rtnl_class_dsmark_get_bmask(struct rtnl_class *); + +extern int rtnl_class_dsmark_set_value(struct rtnl_class *, uint8_t); +extern int rtnl_class_dsmark_get_value(struct rtnl_class *); + +extern int rtnl_qdisc_dsmark_set_indices(struct rtnl_qdisc *, uint16_t); +extern int rtnl_qdisc_dsmark_get_indices(struct rtnl_qdisc *); + +extern int rtnl_qdisc_dsmark_set_default_index(struct rtnl_qdisc *, + uint16_t); +extern int rtnl_qdisc_dsmark_get_default_index(struct rtnl_qdisc *); + +extern int rtnl_qdisc_dsmark_set_set_tc_index(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_dsmark_get_set_tc_index(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/sch/fifo.h b/include/netlink/route/sch/fifo.h new file mode 100644 index 0000000..c18dd79 --- /dev/null +++ b/include/netlink/route/sch/fifo.h @@ -0,0 +1,28 @@ +/* + * netlink/route/sch/fifo.c FIFO Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_FIFO_H_ +#define NETLINK_FIFO_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_qdisc_fifo_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_fifo_get_limit(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/sch/htb.h b/include/netlink/route/sch/htb.h new file mode 100644 index 0000000..5d4d681 --- /dev/null +++ b/include/netlink/route/sch/htb.h @@ -0,0 +1,41 @@ +/* + * netlink/route/sch/htb.h HTB Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2005 Petr Gotthard + * Copyright (c) 2005 Siemens AG Oesterreich + */ + +#ifndef NETLINK_HTB_H_ +#define NETLINK_HTB_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *, uint32_t); +extern void rtnl_htb_set_defcls(struct rtnl_qdisc *, uint32_t); + +extern void rtnl_htb_set_prio(struct rtnl_class *, uint32_t); +extern void rtnl_htb_set_mtu(struct rtnl_class *, uint32_t); +extern void rtnl_htb_set_rate(struct rtnl_class *, uint32_t); +extern void rtnl_htb_set_ceil(struct rtnl_class *, uint32_t); +extern void rtnl_htb_set_rbuffer(struct rtnl_class *, uint32_t); +extern void rtnl_htb_set_cbuffer(struct rtnl_class *, uint32_t); +extern void rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum); +extern void rtnl_htb_set_overhead(struct rtnl_class *class, uint8_t overhead); +extern void rtnl_htb_set_mpu(struct rtnl_class *class, uint8_t mpu); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/sch/netem.h b/include/netlink/route/sch/netem.h new file mode 100644 index 0000000..b100741 --- /dev/null +++ b/include/netlink/route/sch/netem.h @@ -0,0 +1,62 @@ +/* + * netlink/route/sch/netem.h Network Emulator Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_NETEM_H_ +#define NETLINK_NETEM_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_netem_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_limit(struct rtnl_qdisc *); + +/* Packet Re-ordering */ +extern int rtnl_netem_set_gap(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_gap(struct rtnl_qdisc *); + +extern int rtnl_netem_set_reorder_probability(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *); + +extern int rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *); + +/* Packet Loss */ +extern int rtnl_netem_set_loss(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_loss(struct rtnl_qdisc *); + +extern int rtnl_netem_set_loss_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *); + +/* Packet Duplication */ +extern int rtnl_netem_set_duplicate(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_duplicate(struct rtnl_qdisc *); + +extern int rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *); + +/* Packet Delay */ +extern int rtnl_netem_set_delay(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_delay(struct rtnl_qdisc *); + +extern int rtnl_netem_set_jitter(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_jitter(struct rtnl_qdisc *); + +extern int rtnl_netem_set_delay_correlation(struct rtnl_qdisc *, int); +extern int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/sch/prio.h b/include/netlink/route/sch/prio.h new file mode 100644 index 0000000..ff35b07 --- /dev/null +++ b/include/netlink/route/sch/prio.h @@ -0,0 +1,53 @@ +/* + * netlink/route/sch/prio.c PRIO Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_PRIO_H_ +#define NETLINK_PRIO_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Default Values + * @{ + */ + +/** + * Default number of bands. + * @ingroup prio + */ +#define QDISC_PRIO_DEFAULT_BANDS 3 + +/** + * Default priority mapping. + * @ingroup prio + */ +#define QDISC_PRIO_DEFAULT_PRIOMAP \ + { 1, 2, 2, 2, 1, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1 } + +/** @} */ + +extern int rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_prio_get_bands(struct rtnl_qdisc *); +extern int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *, uint8_t[], int); +extern uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *); + +extern char * rtnl_prio2str(int, char *, size_t); +extern int rtnl_str2prio(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/sch/red.h b/include/netlink/route/sch/red.h new file mode 100644 index 0000000..a4e8642 --- /dev/null +++ b/include/netlink/route/sch/red.h @@ -0,0 +1,17 @@ +/* + * netlink/route/sch/red.h RED Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_RED_H_ +#define NETLINK_RED_H_ + +#include + +#endif diff --git a/include/netlink/route/sch/sfq.h b/include/netlink/route/sch/sfq.h new file mode 100644 index 0000000..19b3817 --- /dev/null +++ b/include/netlink/route/sch/sfq.h @@ -0,0 +1,36 @@ +/* + * netlink/route/sch/sfq.c SFQ Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_SFQ_H_ +#define NETLINK_SFQ_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_sfq_set_quantum(struct rtnl_qdisc *, int); +extern int rtnl_sfq_get_quantum(struct rtnl_qdisc *); + +extern int rtnl_sfq_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_sfq_get_limit(struct rtnl_qdisc *); + +extern int rtnl_sfq_set_perturb(struct rtnl_qdisc *, int); +extern int rtnl_sfq_get_perturb(struct rtnl_qdisc *); + +extern int rtnl_sfq_get_divisor(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/sch/tbf.h b/include/netlink/route/sch/tbf.h new file mode 100644 index 0000000..8e0ea1e --- /dev/null +++ b/include/netlink/route/sch/tbf.h @@ -0,0 +1,43 @@ +/* + * netlink/route/sch/tbf.h TBF Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_TBF_H_ +#define NETLINK_TBF_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *); + +extern int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *, int); +extern int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *); + +extern int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *, int, int, int); +extern int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *); +extern int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *); +extern int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *); + +extern int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *, int, int, int); +extern int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *); +extern int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *); +extern int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/route/tc.h b/include/netlink/route/tc.h new file mode 100644 index 0000000..3cb876f --- /dev/null +++ b/include/netlink/route/tc.h @@ -0,0 +1,63 @@ +/* + * netlink/route/tc.h Traffic Control + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_TC_H_ +#define NETLINK_TC_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * TC statistics identifiers + * @ingroup tc + */ +enum rtnl_tc_stats_id { + RTNL_TC_PACKETS, /**< Packets seen */ + RTNL_TC_BYTES, /**< Bytes seen */ + RTNL_TC_RATE_BPS, /**< Current bits/s (rate estimator) */ + RTNL_TC_RATE_PPS, /**< Current packet/s (rate estimator) */ + RTNL_TC_QLEN, /**< Queue length */ + RTNL_TC_BACKLOG, /**< Backlog length */ + RTNL_TC_DROPS, /**< Packets dropped */ + RTNL_TC_REQUEUES, /**< Number of requeues */ + RTNL_TC_OVERLIMITS, /**< Number of overlimits */ + __RTNL_TC_STATS_MAX, +}; + +#define RTNL_TC_STATS_MAX (__RTNL_TC_STATS_MAX - 1) + +extern int rtnl_tc_calc_txtime(int, int); +extern int rtnl_tc_calc_bufsize(int, int); +extern int rtnl_tc_calc_cell_log(int); + +/** + * Number of entries in a transmission time lookup table + * @ingroup tc + */ +#define RTNL_TC_RTABLE_SIZE 256 + +extern int rtnl_tc_build_rate_table(uint32_t *, uint8_t, uint8_t, int, int); + + +/* TC Handle Translations */ +extern char * rtnl_tc_handle2str(uint32_t, char *, size_t); +extern int rtnl_tc_str2handle(const char *, uint32_t *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/socket.h b/include/netlink/socket.h new file mode 100644 index 0000000..038df7a --- /dev/null +++ b/include/netlink/socket.h @@ -0,0 +1,66 @@ +/* + * netlink/socket.h Netlink Socket + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_SOCKET_H_ +#define NETLINK_SOCKET_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct nl_handle * nl_handle_alloc(void); +extern struct nl_handle * nl_handle_alloc_cb(struct nl_cb *); +extern void nl_handle_destroy(struct nl_handle *); + +extern uint32_t nl_socket_get_local_port(struct nl_handle *); +extern void nl_socket_set_local_port(struct nl_handle *, + uint32_t); + +extern int nl_socket_add_membership(struct nl_handle *, + int); +extern int nl_socket_drop_membership(struct nl_handle *, + int); +extern void nl_join_groups(struct nl_handle *, int); + +extern uint32_t nl_socket_get_peer_port(struct nl_handle *); +extern void nl_socket_set_peer_port(struct nl_handle *, + uint32_t); + +extern struct nl_cb * nl_socket_get_cb(struct nl_handle *); +extern void nl_socket_set_cb(struct nl_handle *, + struct nl_cb *); +extern int nl_socket_modify_cb(struct nl_handle *, + enum nl_cb_type, + enum nl_cb_kind, + nl_recvmsg_msg_cb_t, + void *); + +extern int nl_set_buffer_size(struct nl_handle *, + int, int); +extern int nl_set_passcred(struct nl_handle *, int); +extern int nl_socket_recv_pktinfo(struct nl_handle *, int); + +extern void nl_disable_sequence_check(struct nl_handle *); +extern unsigned int nl_socket_use_seq(struct nl_handle *); + +extern int nl_socket_get_fd(struct nl_handle *); +extern int nl_socket_set_nonblocking(struct nl_handle *); +extern void nl_socket_enable_msg_peek(struct nl_handle *); +extern void nl_socket_disable_msg_peek(struct nl_handle *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/netlink/types.h b/include/netlink/types.h new file mode 100644 index 0000000..903028e --- /dev/null +++ b/include/netlink/types.h @@ -0,0 +1,105 @@ +/* + * netlink/netlink-types.h Netlink Types + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef __NETLINK_TYPES_H_ +#define __NETLINK_TYPES_H_ + +#include + +/** + * Dumping types (dp_type) + * @ingroup utils + */ +enum nl_dump_type { + NL_DUMP_BRIEF, /**< Dump object in a brief one-liner */ + NL_DUMP_FULL, /**< Dump all attributes but no statistics */ + NL_DUMP_STATS, /**< Dump all attributes including statistics */ + NL_DUMP_XML, /**< Dump all attribtes in XML format */ + NL_DUMP_ENV, /**< Dump all attribtues as env variables */ + NL_DUMP_EVENTS, /**< Dump event */ + __NL_DUMP_MAX, +}; +#define NL_DUMP_MAX (__NL_DUMP_MAX - 1) + +/** + * Dumping parameters + * @ingroup utils + */ +struct nl_dump_params +{ + /** + * Specifies the type of dump that is requested. + */ + enum nl_dump_type dp_type; + + /** + * Specifies the number of whitespaces to be put in front + * of every new line (indentation). + */ + int dp_prefix; + + /** + * Causes the cache index to be printed for each element. + */ + int dp_print_index; + + /** + * Causes each element to be prefixed with the message type. + */ + int dp_dump_msgtype; + + /** + * A callback invoked for output + * + * Passed arguments are: + * - dumping parameters + * - string to append to the output + */ + void (*dp_cb)(struct nl_dump_params *, char *); + + /** + * A callback invoked for every new line, can be used to + * customize the indentation. + * + * Passed arguments are: + * - dumping parameters + * - line number starting from 0 + */ + void (*dp_nl_cb)(struct nl_dump_params *, int); + + /** + * User data pointer, can be used to pass data to callbacks. + */ + void *dp_data; + + /** + * File descriptor the dumping output should go to + */ + FILE * dp_fd; + + /** + * Alternatively the output may be redirected into a buffer + */ + char * dp_buf; + + /** + * Length of the buffer dp_buf + */ + size_t dp_buflen; + + /** + * PRIVATE + * Set if a dump was performed prior to the actual dump handler. + */ + int dp_pre_dump; +}; + +#endif diff --git a/include/netlink/utils.h b/include/netlink/utils.h new file mode 100644 index 0000000..5b9e8af --- /dev/null +++ b/include/netlink/utils.h @@ -0,0 +1,76 @@ +/* + * netlink/utils.h Utility Functions + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_UTILS_H_ +#define NETLINK_UTILS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Probability Constants + * @{ + */ + +/** + * Lower probability limit + * @ingroup utils + */ +#define NL_PROB_MIN 0x0 + +/** + * Upper probability limit + * @ingroup utils + */ +#define NL_PROB_MAX 0xffffffff + +/** @} */ + +extern char * nl_geterror(void); +extern int nl_get_errno(void); +extern void nl_perror(const char *); + +/* unit pretty-printing */ +extern double nl_cancel_down_bytes(unsigned long long, char **); +extern double nl_cancel_down_bits(unsigned long long, char **); +extern double nl_cancel_down_us(uint32_t, char **); + +/* generic unit translations */ +extern long nl_size2int(const char *); +extern long nl_prob2int(const char *); + +/* time translations */ +extern int nl_get_hz(void); +extern uint32_t nl_us2ticks(uint32_t); +extern uint32_t nl_ticks2us(uint32_t); +extern char * nl_msec2str(uint64_t, char *, size_t); + +/* link layer protocol translations */ +extern char * nl_llproto2str(int, char *, size_t); +extern int nl_str2llproto(const char *); + +/* ethernet protocol translations */ +extern char * nl_ether_proto2str(int, char *, size_t); +extern int nl_str2ether_proto(const char *); + +/* IP protocol translations */ +extern char * nl_ip_proto2str(int, char *, size_t); +extern int nl_str2ip_proto(const char *); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/install-sh b/install-sh new file mode 100755 index 0000000..6ce63b9 --- /dev/null +++ b/install-sh @@ -0,0 +1,294 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd=$cpprog + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "$0: no input file specified" >&2 + exit 1 +else + : +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d "$dst" ]; then + instcmd=: + chmodcmd="" + else + instcmd=$mkdirprog + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f "$src" ] || [ -d "$src" ] + then + : + else + echo "$0: $src does not exist" >&2 + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "$0: no destination specified" >&2 + exit 1 + else + : + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d "$dst" ] + then + dst=$dst/`basename "$src"` + else + : + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' + ' +IFS="${IFS-$defaultIFS}" + +oIFS=$IFS +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS=$oIFS + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp=$pathcomp$1 + shift + + if [ ! -d "$pathcomp" ] ; + then + $mkdirprog "$pathcomp" + else + : + fi + + pathcomp=$pathcomp/ +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd "$dst" && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dst"; else : ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dst"; else : ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dst"; else : ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dst"; else : ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename "$dst"` + else + dstfile=`basename "$dst" $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename "$dst"` + else + : + fi + +# Make a couple of temp file names in the proper directory. + + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + +# Trap to clean up temp files at exit. + + trap 'status=$?; rm -f "$dsttmp" "$rmtmp" && exit $status' 0 + trap '(exit $?); exit' 1 2 13 15 + +# Move or copy the file name to the temp name + + $doit $instcmd "$src" "$dsttmp" && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dsttmp"; else :;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dsttmp"; else :;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dsttmp"; else :;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dsttmp"; else :;fi && + +# Now remove or move aside any old file at destination location. We try this +# two ways since rm can't unlink itself on some systems and the destination +# file might be busy for other reasons. In this case, the final cleanup +# might fail but the new file should still install successfully. + +{ + if [ -f "$dstdir/$dstfile" ] + then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null || + $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null || + { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit + } + else + : + fi +} && + +# Now rename the file to the real destination. + + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + +fi && + +# The final little trick to "correctly" pass the exit status to the exit trap. + +{ + (exit 0); exit +} diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..a654242 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,74 @@ +# +# lib/Makefile +# +# 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 version 2.1 +# of the License. +# +# Copyright (c) 2003-2006 Thomas Graf +# + +ifeq ($(shell [ ! -r ../Makefile.opts ] && echo 1),) + include ../Makefile.opts +endif + +# Core +CIN := $(wildcard *.c) +# NETLINK_ROUTE +CIN += $(wildcard route/*.c) +# Schedulers +CIN += $(wildcard route/sch/*.c) +# Classifiers +CIN += $(wildcard route/cls/*.c) +# NETLINK_GENERIC +CIN += $(wildcard genl/*.c) +# fib lookup +CIN += $(wildcard fib_lookup/*.c) +# Netfilter +CIN += $(wildcard netfilter/*.c) + +DEPS := $(CIN:%.c=%.d) +OBJ := $(CIN:%.c=%.o) +CFLAGS += -fPIC +OUT_SLIB := $(PACKAGE_NAME).so.$(PACKAGE_VERSION) +LN_SLIB := $(PACKAGE_NAME).so +LN1_SLIB := $(LN_SLIB).1 + +export + +.PHONY: all clean install librtn.a $(OUT_SLIB) + + +all: + @echo " MAKE $(OUT_SLIB)"; \ + $(MAKE) $(OUT_SLIB) + +$(OUT_SLIB): ../Makefile.opts $(OBJ) + @echo " LD $(OUT_SLIB)"; \ + $(CC) -shared -Wl,-soname,libnl.so.1 -o $(OUT_SLIB) $(OBJ) $(LIBNL_LIB) -lc + @echo " LN $(OUT_SLIB) $(LN1_SLIB)"; \ + rm -f $(LN1_SLIB) ; $(LN) -s $(OUT_SLIB) $(LN1_SLIB) + @echo " LN $(LN1_SLIB) $(LN_SLIB)"; \ + rm -f $(LN_SLIB) ; $(LN) -s $(LN1_SLIB) $(LN_SLIB) + +clean: + @echo " CLEAN lib"; \ + $(RM) -f $(OBJ) $(OUT_SLIB) $(LN_SLIB) $(LN1_SLIB); \ + $(RM) -f $(DEPS) $(OUT_SLIB) $(LN_SLIB) $(LN1_SLIB) + +distclean: + @echo " DISTCLEAN lib"; \ + $(RM) -f $(DEPS) + +install: + mkdir -p $(DESTDIR)$(libdir)/ + install -m 0644 $(OUT_SLIB) $(DESTDIR)$(libdir) + rm -f $(DESTDIR)$(libdir)/$(LN1_SLIB) + $(LN) -s $(OUT_SLIB) $(DESTDIR)$(libdir)/$(LN1_SLIB) + rm -f $(DESTDIR)$(libdir)/$(LN_SLIB) + $(LN) -s $(LN1_SLIB) $(DESTDIR)$(libdir)/$(LN_SLIB) + +$(DEPS): ../Makefile.opts + +include ../Makefile.rules diff --git a/lib/addr.c b/lib/addr.c new file mode 100644 index 0000000..7fe3781 --- /dev/null +++ b/lib/addr.c @@ -0,0 +1,883 @@ +/* + * lib/addr.c Abstract Address + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup utils + * @defgroup addr Abstract Address + * + * @par 1) Transform character string to abstract address + * @code + * struct nl_addr *a = nl_addr_parse("::1", AF_UNSPEC); + * printf("Address family: %s\n", nl_af2str(nl_addr_get_family(a))); + * nl_addr_put(a); + * a = nl_addr_parse("11:22:33:44:55:66", AF_UNSPEC); + * printf("Address family: %s\n", nl_af2str(nl_addr_get_family(a))); + * nl_addr_put(a); + * @endcode + * @{ + */ + +#include +#include +#include +#include +#include + +/* All this DECnet stuff is stolen from iproute2, thanks to whoever wrote + * this, probably Alexey. */ +static inline uint16_t dn_ntohs(uint16_t addr) +{ + union { + uint8_t byte[2]; + uint16_t word; + } u = { + .word = addr, + }; + + return ((uint16_t) u.byte[0]) | (((uint16_t) u.byte[1]) << 8); +} + +static inline int do_digit(char *str, uint16_t *addr, uint16_t scale, + size_t *pos, size_t len, int *started) +{ + uint16_t tmp = *addr / scale; + + if (*pos == len) + return 1; + + if (((tmp) > 0) || *started || (scale == 1)) { + *str = tmp + '0'; + *started = 1; + (*pos)++; + *addr -= (tmp * scale); + } + + return 0; +} + +static const char *dnet_ntop(char *addrbuf, size_t addrlen, char *str, + size_t len) +{ + uint16_t addr = dn_ntohs(*(uint16_t *)addrbuf); + uint16_t area = addr >> 10; + size_t pos = 0; + int started = 0; + + if (addrlen != 2) + return NULL; + + addr &= 0x03ff; + + if (len == 0) + return str; + + if (do_digit(str + pos, &area, 10, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &area, 1, &pos, len, &started)) + return str; + + if (pos == len) + return str; + + *(str + pos) = '.'; + pos++; + started = 0; + + if (do_digit(str + pos, &addr, 1000, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 100, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 10, &pos, len, &started)) + return str; + + if (do_digit(str + pos, &addr, 1, &pos, len, &started)) + return str; + + if (pos == len) + return str; + + *(str + pos) = 0; + + return str; +} + +static int dnet_num(const char *src, uint16_t * dst) +{ + int rv = 0; + int tmp; + *dst = 0; + + while ((tmp = *src++) != 0) { + tmp -= '0'; + if ((tmp < 0) || (tmp > 9)) + return rv; + + rv++; + (*dst) *= 10; + (*dst) += tmp; + } + + return rv; +} + +static inline int dnet_pton(const char *src, char *addrbuf) +{ + uint16_t area = 0; + uint16_t node = 0; + int pos; + + pos = dnet_num(src, &area); + if ((pos == 0) || (area > 63) || + ((*(src + pos) != '.') && (*(src + pos) != ','))) + return -EINVAL; + + pos = dnet_num(src + pos + 1, &node); + if ((pos == 0) || (node > 1023)) + return -EINVAL; + + *(uint16_t *)addrbuf = dn_ntohs((area << 10) | node); + + return 1; +} + +/** + * @name Creating Abstract Addresses + * @{ + */ + +/** + * Allocate new abstract address object. + * @arg maxsize Maximum size of the binary address. + * @return Newly allocated address object or NULL + */ +struct nl_addr *nl_addr_alloc(size_t maxsize) +{ + struct nl_addr *addr; + + addr = calloc(1, sizeof(*addr) + maxsize); + if (!addr) { + nl_errno(ENOMEM); + return NULL; + } + + addr->a_refcnt = 1; + addr->a_maxsize = maxsize; + + return addr; +} + +/** + * Allocate new abstract address object based on a binary address. + * @arg family Address family. + * @arg buf Buffer containing the binary address. + * @arg size Length of binary address buffer. + * @return Newly allocated address handle or NULL + */ +struct nl_addr *nl_addr_build(int family, void *buf, size_t size) +{ + struct nl_addr *addr; + + addr = nl_addr_alloc(size); + if (!addr) + return NULL; + + addr->a_family = family; + addr->a_len = size; + addr->a_prefixlen = size*8; + + if (size) + memcpy(addr->a_addr, buf, size); + + return addr; +} + +/** + * Allocate abstract address object based on a character string + * @arg addrstr Address represented as character string. + * @arg hint Address family hint or AF_UNSPEC. + * + * Regognizes the following address formats: + *@code + * Format Len Family + * ---------------------------------------------------------------- + * IPv6 address format 16 AF_INET6 + * ddd.ddd.ddd.ddd 4 AF_INET + * HH:HH:HH:HH:HH:HH 6 AF_LLC + * AA{.|,}NNNN 2 AF_DECnet + * HH:HH:HH:... variable AF_UNSPEC + * @endcode + * + * Special values: + * - none: All bits and length set to 0. + * - {default|all|any}: All bits set to 0, length based on hint or + * AF_INET if no hint is given. + * + * The prefix length may be appened at the end prefixed with a + * slash, e.g. 10.0.0.0/8. + * + * @return Newly allocated abstract address object or NULL. + */ +struct nl_addr *nl_addr_parse(const char *addrstr, int hint) +{ + int err, copy = 0, len = 0, family = AF_UNSPEC; + char *str, *prefix, buf[32]; + struct nl_addr *addr = NULL; /* gcc ain't that smart */ + + str = strdup(addrstr); + if (!str) { + err = nl_errno(ENOMEM); + goto errout; + } + + prefix = strchr(str, '/'); + if (prefix) + *prefix = '\0'; + + if (!strcasecmp(str, "none")) { + family = hint; + goto prefix; + } + + if (!strcasecmp(str, "default") || + !strcasecmp(str, "all") || + !strcasecmp(str, "any")) { + + switch (hint) { + case AF_INET: + case AF_UNSPEC: + /* Kind of a hack, we assume that if there is + * no hint given the user wants to have a IPv4 + * address given back. */ + family = AF_INET; + len = 4; + goto prefix; + + case AF_INET6: + family = AF_INET6; + len = 16; + goto prefix; + + case AF_LLC: + family = AF_LLC; + len = 6; + goto prefix; + + default: + err = nl_error(EINVAL, "Unsuported address" \ + "family for default address"); + goto errout; + } + } + + copy = 1; + + if (hint == AF_INET || hint == AF_UNSPEC) { + if (inet_pton(AF_INET, str, buf) > 0) { + family = AF_INET; + len = 4; + goto prefix; + } + if (hint == AF_INET) { + err = nl_error(EINVAL, "Invalid IPv4 address"); + goto errout; + } + } + + if (hint == AF_INET6 || hint == AF_UNSPEC) { + if (inet_pton(AF_INET6, str, buf) > 0) { + family = AF_INET6; + len = 16; + goto prefix; + } + if (hint == AF_INET6) { + err = nl_error(EINVAL, "Invalid IPv6 address"); + goto errout; + } + } + + if ((hint == AF_LLC || hint == AF_UNSPEC) && strchr(str, ':')) { + unsigned int a, b, c, d, e, f; + + if (sscanf(str, "%02x:%02x:%02x:%02x:%02x:%02x", + &a, &b, &c, &d, &e, &f) == 6) { + family = AF_LLC; + len = 6; + buf[0] = (unsigned char) a; + buf[1] = (unsigned char) b; + buf[2] = (unsigned char) c; + buf[3] = (unsigned char) d; + buf[4] = (unsigned char) e; + buf[5] = (unsigned char) f; + goto prefix; + } + + if (hint == AF_LLC) { + err = nl_error(EINVAL, "Invalid link layer address"); + goto errout; + } + } + + if ((hint == AF_DECnet || hint == AF_UNSPEC) && + (strchr(str, '.') || strchr(str, ','))) { + if (dnet_pton(str, buf) > 0) { + family = AF_DECnet; + len = 2; + goto prefix; + } + if (hint == AF_DECnet) { + err = nl_error(EINVAL, "Invalid DECnet address"); + goto errout; + } + } + + if (hint == AF_UNSPEC && strchr(str, ':')) { + int i = 0; + char *s = str, *p; + for (;;) { + long l = strtol(s, &p, 16); + + if (s == p || l > 0xff || i >= sizeof(buf)) { + err = -EINVAL; + goto errout; + } + + buf[i++] = (unsigned char) l; + if (*p == '\0') + break; + s = ++p; + } + + len = i; + family = AF_UNSPEC; + goto prefix; + } + + err = nl_error(EINVAL, "Invalid address"); + goto errout; + +prefix: + addr = nl_addr_alloc(len); + if (!addr) { + err = nl_errno(ENOMEM); + goto errout; + } + + nl_addr_set_family(addr, family); + + if (copy) + nl_addr_set_binary_addr(addr, buf, len); + + if (prefix) { + char *p; + long pl = strtol(++prefix, &p, 0); + if (p == prefix) { + nl_addr_destroy(addr); + err = -EINVAL; + goto errout; + } + nl_addr_set_prefixlen(addr, pl); + } else + nl_addr_set_prefixlen(addr, len * 8); + + err = 0; +errout: + free(str); + + return err ? NULL : addr; +} + +/** + * Clone existing abstract address object. + * @arg addr Abstract address object. + * @return Newly allocated abstract address object being a duplicate of the + * specified address object or NULL if a failure occured. + */ +struct nl_addr *nl_addr_clone(struct nl_addr *addr) +{ + struct nl_addr *new; + + new = nl_addr_build(addr->a_family, addr->a_addr, addr->a_len); + if (new) + new->a_prefixlen = addr->a_prefixlen; + + return new; +} + +/** @} */ + +/** + * @name Destroying Abstract Addresses + * @{ + */ + +/** + * Destroy abstract address object. + * @arg addr Abstract address object. + */ +void nl_addr_destroy(struct nl_addr *addr) +{ + if (!addr) + return; + + if (addr->a_refcnt != 1) + BUG(); + + free(addr); +} + +/** @} */ + +/** + * @name Managing Usage References + * @{ + */ + +struct nl_addr *nl_addr_get(struct nl_addr *addr) +{ + addr->a_refcnt++; + + return addr; +} + +void nl_addr_put(struct nl_addr *addr) +{ + if (!addr) + return; + + if (addr->a_refcnt == 1) + nl_addr_destroy(addr); + else + addr->a_refcnt--; +} + +/** + * Check whether an abstract address object is shared. + * @arg addr Abstract address object. + * @return Non-zero if the abstract address object is shared, otherwise 0. + */ +int nl_addr_shared(struct nl_addr *addr) +{ + return addr->a_refcnt > 1; +} + +/** @} */ + +/** + * @name Miscellaneous + * @{ + */ + +/** + * Compares two abstract address objects. + * @arg a A abstract address object. + * @arg b Another abstract address object. + * + * @return Integer less than, equal to or greather than zero if \c is found, + * respectively to be less than, to, or be greater than \c b. + */ +int nl_addr_cmp(struct nl_addr *a, struct nl_addr *b) +{ + int d = a->a_family - b->a_family; + + if (d == 0) { + d = a->a_len - b->a_len; + + if (a->a_len && d == 0) + return memcmp(a->a_addr, b->a_addr, a->a_len); + } + + return d; +} + +/** + * Compares the prefix of two abstract address objects. + * @arg a A abstract address object. + * @arg b Another abstract address object. + * + * @return Integer less than, equal to or greather than zero if \c is found, + * respectively to be less than, to, or be greater than \c b. + */ +int nl_addr_cmp_prefix(struct nl_addr *a, struct nl_addr *b) +{ + int d = a->a_family - b->a_family; + + if (d == 0) { + int len = min(a->a_prefixlen, b->a_prefixlen); + int bytes = len / 8; + + d = memcmp(a->a_addr, b->a_addr, bytes); + if (d == 0) { + int mask = (1UL << (len % 8)) - 1UL; + + d = (a->a_addr[bytes] & mask) - + (b->a_addr[bytes] & mask); + } + } + + return d; +} + +/** + * Check if an address matches a certain family. + * @arg addr Address represented as character string. + * @arg family Desired address family. + * + * @return 1 if the address is of the desired address family, + * otherwise 0 is returned. + */ +int nl_addr_valid(char *addr, int family) +{ + int ret; + char buf[32]; + + switch (family) { + case AF_INET: + case AF_INET6: + ret = inet_pton(family, addr, buf); + if (ret <= 0) + return 0; + break; + + case AF_DECnet: + ret = dnet_pton(addr, buf); + if (ret <= 0) + return 0; + break; + + case AF_LLC: + if (sscanf(addr, "%*02x:%*02x:%*02x:%*02x:%*02x:%*02x") != 6) + return 0; + break; + } + + return 1; +} + +/** + * Guess address family of an abstract address object based on address size. + * @arg addr Abstract address object. + * @return Address family or AF_UNSPEC if guessing wasn't successful. + */ +int nl_addr_guess_family(struct nl_addr *addr) +{ + switch (addr->a_len) { + case 4: + return AF_INET; + case 6: + return AF_LLC; + case 16: + return AF_INET6; + default: + return AF_UNSPEC; + } +} + +/** + * Fill out sockaddr structure with values from abstract address object. + * @arg addr Abstract address object. + * @arg sa Destination sockaddr structure buffer. + * @arg salen Length of sockaddr structure buffer. + * + * Fills out the specified sockaddr structure with the data found in the + * specified abstract address. The salen argument needs to be set to the + * size of sa but will be modified to the actual size used during before + * the function exits. + * + * @return 0 on success or a negative error code + */ +int nl_addr_fill_sockaddr(struct nl_addr *addr, struct sockaddr *sa, + socklen_t *salen) +{ + switch (addr->a_family) { + case AF_INET: { + struct sockaddr_in *sai = (struct sockaddr_in *) sa; + + if (*salen < sizeof(*sai)) + return -EINVAL; + + sai->sin_family = addr->a_family; + memcpy(&sai->sin_addr, addr->a_addr, 4); + *salen = sizeof(*sai); + } + break; + + case AF_INET6: { + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) sa; + + if (*salen < sizeof(*sa6)) + return -EINVAL; + + sa6->sin6_family = addr->a_family; + memcpy(&sa6->sin6_addr, addr->a_addr, 16); + *salen = sizeof(*sa6); + } + break; + + default: + return -EINVAL; + } + + return 0; +} + + +/** @} */ + +/** + * @name Getting Information About Addresses + * @{ + */ + +/** + * Call getaddrinfo() for an abstract address object. + * @arg addr Abstract address object. + * + * Calls getaddrinfo() for the specified abstract address in AI_NUMERICHOST + * mode. + * + * @note The caller is responsible for freeing the linked list using the + * interface provided by getaddrinfo(3). + * + * @return A linked list of addrinfo handles or NULL with an error message + * associated. + */ +struct addrinfo *nl_addr_info(struct nl_addr *addr) +{ + int err; + struct addrinfo *res; + char buf[INET6_ADDRSTRLEN+5]; + struct addrinfo hint = { + .ai_flags = AI_NUMERICHOST, + .ai_family = addr->a_family, + }; + + nl_addr2str(addr, buf, sizeof(buf)); + + err = getaddrinfo(buf, NULL, &hint, &res); + if (err != 0) { + nl_error(err, gai_strerror(err)); + return NULL; + } + + return res; +} + +/** + * Resolve abstract address object to a name using getnameinfo(). + * @arg addr Abstract address object. + * @arg host Destination buffer for host name. + * @arg hostlen Length of destination buffer. + * + * Resolves the abstract address to a name and writes the looked up result + * into the host buffer. getnameinfo() is used to perform the lookup and + * is put into NI_NAMEREQD mode so the function will fail if the lookup + * couldn't be performed. + * + * @return 0 on success or a negative error code. + */ +int nl_addr_resolve(struct nl_addr *addr, char *host, size_t hostlen) +{ + int err; + struct sockaddr_in6 buf; + socklen_t salen = sizeof(buf); + + err = nl_addr_fill_sockaddr(addr, (struct sockaddr *) &buf, &salen); + if (err < 0) + return err; + + return getnameinfo((struct sockaddr *) &buf, salen, + host, hostlen, NULL, 0, NI_NAMEREQD); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nl_addr_set_family(struct nl_addr *addr, int family) +{ + addr->a_family = family; +} + +int nl_addr_get_family(struct nl_addr *addr) +{ + return addr->a_family; +} + +/** + * Set binary address of abstract address object. + * @arg addr Abstract address object. + * @arg buf Buffer containing binary address. + * @arg len Length of buffer containing binary address. + */ +int nl_addr_set_binary_addr(struct nl_addr *addr, void *buf, size_t len) +{ + if (len > addr->a_maxsize) + return -ERANGE; + + addr->a_len = len; + memcpy(addr->a_addr, buf, len); + + return 0; +} + +/** + * Get binary address of abstract address object. + * @arg addr Abstract address object. + */ +void *nl_addr_get_binary_addr(struct nl_addr *addr) +{ + return addr->a_addr; +} + +/** + * Get length of binary address of abstract address object. + * @arg addr Abstract address object. + */ +unsigned int nl_addr_get_len(struct nl_addr *addr) +{ + return addr->a_len; +} + +void nl_addr_set_prefixlen(struct nl_addr *addr, int prefixlen) +{ + addr->a_prefixlen = prefixlen; +} + +/** + * Get prefix length of abstract address object. + * @arg addr Abstract address object. + */ +unsigned int nl_addr_get_prefixlen(struct nl_addr *addr) +{ + return addr->a_prefixlen; +} + +/** @} */ + +/** + * @name Translations to Strings + * @{ + */ + +/** + * Convert abstract address object to character string. + * @arg addr Abstract address object. + * @arg buf Destination buffer. + * @arg size Size of destination buffer. + * + * Converts an abstract address to a character string and stores + * the result in the specified destination buffer. + * + * @return Address represented in ASCII stored in destination buffer. + */ +char *nl_addr2str(struct nl_addr *addr, char *buf, size_t size) +{ + int i; + char tmp[16]; + + if (!addr->a_len) { + snprintf(buf, size, "none"); + goto prefix; + } + + switch (addr->a_family) { + case AF_INET: + inet_ntop(AF_INET, addr->a_addr, buf, size); + break; + + case AF_INET6: + inet_ntop(AF_INET6, addr->a_addr, buf, size); + break; + + case AF_DECnet: + dnet_ntop(addr->a_addr, addr->a_len, buf, size); + break; + + case AF_LLC: + default: + snprintf(buf, size, "%02x", + (unsigned char) addr->a_addr[0]); + for (i = 1; i < addr->a_len; i++) { + snprintf(tmp, sizeof(tmp), ":%02x", + (unsigned char) addr->a_addr[i]); + strncat(buf, tmp, size - strlen(buf) - 1); + } + break; + } + +prefix: + if (addr->a_prefixlen != (8 * addr->a_len)) { + snprintf(tmp, sizeof(tmp), "/%u", addr->a_prefixlen); + strncat(buf, tmp, size - strlen(buf) - 1); + } + + return buf; +} + +/** @} */ + +/** + * @name Address Family Transformations + * @{ + */ + +static struct trans_tbl afs[] = { + __ADD(AF_UNSPEC,unspec) + __ADD(AF_UNIX,unix) + __ADD(AF_LOCAL,local) + __ADD(AF_INET,inet) + __ADD(AF_AX25,ax25) + __ADD(AF_IPX,ipx) + __ADD(AF_APPLETALK,appletalk) + __ADD(AF_NETROM,netrom) + __ADD(AF_BRIDGE,bridge) + __ADD(AF_ATMPVC,atmpvc) + __ADD(AF_X25,x25) + __ADD(AF_INET6,inet6) + __ADD(AF_ROSE,rose) + __ADD(AF_DECnet,decnet) + __ADD(AF_NETBEUI,netbeui) + __ADD(AF_SECURITY,security) + __ADD(AF_KEY,key) + __ADD(AF_NETLINK,netlink) + __ADD(AF_ROUTE,route) + __ADD(AF_PACKET,packet) + __ADD(AF_ASH,ash) + __ADD(AF_ECONET,econet) + __ADD(AF_ATMSVC,atmsvc) + __ADD(AF_SNA,sna) + __ADD(AF_IRDA,irda) + __ADD(AF_PPPOX,pppox) + __ADD(AF_WANPIPE,wanpipe) + __ADD(AF_LLC,llc) + __ADD(AF_BLUETOOTH,bluetooth) +}; + +char *nl_af2str(int family, char *buf, size_t size) +{ + return __type2str(family, buf, size, afs, ARRAY_SIZE(afs)); +} + +int nl_str2af(const char *name) +{ + int fam = __str2type(name, afs, ARRAY_SIZE(afs)); + return fam >= 0 ? fam : AF_UNSPEC; +} + +/** @} */ + +/** @} */ diff --git a/lib/attr.c b/lib/attr.c new file mode 100644 index 0000000..fbce4ac --- /dev/null +++ b/lib/attr.c @@ -0,0 +1,787 @@ +/* + * lib/attr.c Netlink Attributes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * @ingroup msg + * @defgroup attr Attributes + * Netlink Attributes Construction/Parsing Interface + * @par 0) Introduction + * Netlink attributes are chained together following each other: + * @code + * <------- nla_total_size(payload) -------> + * <---- nla_attr_size(payload) -----> + * +----------+- - -+- - - - - - - - - +- - -+-------- - - + * | Header | Pad | Payload | Pad | Header + * +----------+- - -+- - - - - - - - - +- - -+-------- - - + * <- nla_len(nla) -> ^ + * nla_data(nla)----^ | + * nla_next(nla)-----------------------------' + * @endcode + * + * @par + * The attribute header and payload must be aligned properly: + * @code + * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | (struct nlattr) | ing | | ing | + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * <-------------- nlattr->nla_len --------------> + * @endcode + * + * @par Nested TLVs: + * Nested TLVs are an array of TLVs nested into another TLV. This can be useful + * to allow subsystems to have their own formatting rules without the need to + * make the underlying layer be aware of it. It can also be useful to transfer + * arrays, lists and flattened trees. + * \code + * <-------------------- NLA_ALIGN(...) -------------------> + * +---------------+- - - - - - - - - - - - - - - - - -+- - -+ + * | |+---------+---------+- - -+-------+| | + * | TLV Header || TLV 1 | TLV 2 | | TLV n || Pad | + * | |+---------+---------+- - -+-------+| | + * +---------------+- - - - - - - - - - - - - - - - - -+- - -+ + * <--------- nla_data(nla) ---------> + * \endcode + * + * @par 1) Constructing a message with attributes + * @code + * int param1 = 10; + * char *param2 = "parameter text"; + * struct nlmsghdr hdr = { + * .nlmsg_type = MY_ACTION, + * }; + * struct nl_msg *m = nlmsg_build(&hdr); + * nla_put_u32(m, 1, param1); + * nla_put_string(m, 2, param2); + * + * nl_send_auto_complete(handle, nl_msg_get(m)); + * nlmsg_free(m); + * @endcode + * + * @par 2) Constructing nested attributes + * @code + * struct nl_msg * nested_config(void) + * { + * int a = 5, int b = 10; + * struct nl_msg *n = nlmsg_build(NULL); + * nla_put_u32(n, 10, a); + * nla_put_u32(n, 20, b); + * return n; + * } + * + * ... + * struct nl_msg *m = nlmsg_build(&hdr); + * struct nl_msg *nest = nested_config(); + * nla_put_nested(m, 1, nest); + * + * nl_send_auto_complete(handle, nl_msg_get(m)); + * nlmsg_free(nest); + * nlmsg_free(m); + * @endcode + * @{ + */ + +/** + * @name Size Calculations + * @{ + */ + +/** + * length of attribute not including padding + * @arg payload length of payload + */ +int nla_attr_size(int payload) +{ + return NLA_HDRLEN + payload; +} + +/** + * total length of attribute including padding + * @arg payload length of payload + */ +int nla_total_size(int payload) +{ + return NLA_ALIGN(nla_attr_size(payload)); +} + +/** + * length of padding at the tail of the attribute + * @arg payload length of payload + */ +int nla_padlen(int payload) +{ + return nla_total_size(payload) - nla_attr_size(payload); +} + +/** @} */ + +/** + * @name Payload Access + * @{ + */ + +/** + * attribute type + * @arg nla netlink attribute + */ +int nla_type(const struct nlattr *nla) +{ + return nla->nla_type & NLA_TYPE_MASK; +} + +/** + * head of payload + * @arg nla netlink attribute + */ +void *nla_data(const struct nlattr *nla) +{ + return (char *) nla + NLA_HDRLEN; +} + +/** + * length of payload + * @arg nla netlink attribute + */ +int nla_len(const struct nlattr *nla) +{ + return nla->nla_len - NLA_HDRLEN; +} + +/** @} */ + +/** + * @name Attribute Parsing + * @{ + */ + +/** + * check if the netlink attribute fits into the remaining bytes + * @arg nla netlink attribute + * @arg remaining number of bytes remaining in attribute stream + */ +int nla_ok(const struct nlattr *nla, int remaining) +{ + return remaining >= sizeof(*nla) && + nla->nla_len >= sizeof(*nla) && + nla->nla_len <= remaining; +} + +/** + * next netlink attribte in attribute stream + * @arg nla netlink attribute + * @arg remaining number of bytes remaining in attribute stream + * + * @return the next netlink attribute in the attribute stream and + * decrements remaining by the size of the current attribute. + */ +struct nlattr *nla_next(const struct nlattr *nla, int *remaining) +{ + int totlen = NLA_ALIGN(nla->nla_len); + + *remaining -= totlen; + return (struct nlattr *) ((char *) nla + totlen); +} + +static uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = { + [NLA_U8] = sizeof(uint8_t), + [NLA_U16] = sizeof(uint16_t), + [NLA_U32] = sizeof(uint32_t), + [NLA_U64] = sizeof(uint64_t), + [NLA_STRING] = 1, + [NLA_NESTED] = NLA_HDRLEN, +}; + +static int validate_nla(struct nlattr *nla, int maxtype, + struct nla_policy *policy) +{ + struct nla_policy *pt; + int minlen = 0, type = nla_type(nla); + + if (type <= 0 || type > maxtype) + return 0; + + pt = &policy[type]; + + if (pt->type > NLA_TYPE_MAX) + BUG(); + + if (pt->minlen) + minlen = pt->minlen; + else if (pt->type != NLA_UNSPEC) + minlen = nla_attr_minlen[pt->type]; + + if (pt->type == NLA_FLAG && nla_len(nla) > 0) + return nl_errno(ERANGE); + + if (nla_len(nla) < minlen) + return nl_errno(ERANGE); + + if (pt->maxlen && nla_len(nla) > pt->maxlen) + return nl_errno(ERANGE); + + if (pt->type == NLA_STRING) { + char *data = nla_data(nla); + if (data[nla_len(nla) - 1] != '\0') + return nl_errno(EINVAL); + } + + return 0; +} + + +/** + * Parse a stream of attributes into a tb buffer + * @arg tb destination array with maxtype+1 elements + * @arg maxtype maximum attribute type to be expected + * @arg head head of attribute stream + * @arg len length of attribute stream + * @arg policy validation policy + * + * Parses a stream of attributes and stores a pointer to each attribute in + * the tb array accessable via the attribute type. Attributes with a type + * exceeding maxtype will be silently ignored for backwards compatibility + * reasons. policy may be set to NULL if no validation is required. + * + * @return 0 on success or a negative error code. + */ +int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len, + struct nla_policy *policy) +{ + struct nlattr *nla; + int rem, err; + + memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1)); + + nla_for_each_attr(nla, head, len, rem) { + int type = nla_type(nla); + + if (type == 0) { + fprintf(stderr, "Illegal nla->nla_type == 0\n"); + continue; + } + + if (type <= maxtype) { + if (policy) { + err = validate_nla(nla, maxtype, policy); + if (err < 0) + goto errout; + } + + tb[type] = nla; + } + } + + if (rem > 0) + fprintf(stderr, "netlink: %d bytes leftover after parsing " + "attributes.\n", rem); + + err = 0; +errout: + return err; +} + + +/** + * parse nested attributes + * @arg tb destination array with maxtype+1 elements + * @arg maxtype maximum attribute type to be expected + * @arg nla attribute containing the nested attributes + * @arg policy validation policy + * + * @see nla_parse() + */ +int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla, + struct nla_policy *policy) +{ + return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy); +} + +/** + * Validate a stream of attributes + * @arg head head of attribute stream + * @arg len length of attribute stream + * @arg maxtype maximum attribute type to be expected + * @arg policy validation policy + * + * Validates all attributes in the specified attribute stream + * against the specified policy. Attributes with a type exceeding + * maxtype will be ignored. See documenation of struct nla_policy + * for more details. + * + * @return 0 on success or a negative error code. + */ +int nla_validate(struct nlattr *head, int len, int maxtype, + struct nla_policy *policy) +{ + struct nlattr *nla; + int rem, err; + + nla_for_each_attr(nla, head, len, rem) { + err = validate_nla(nla, maxtype, policy); + if (err < 0) + goto errout; + } + + err = 0; +errout: + return err; +} + +/** + * Find a specific attribute in a stream of attributes + * @arg head head of attribute stream + * @arg len length of attribute stream + * @arg attrtype type of attribute to look for + * + * @return the first attribute in the stream matching the specified type. + */ +struct nlattr *nla_find(struct nlattr *head, int len, int attrtype) +{ + struct nlattr *nla; + int rem; + + nla_for_each_attr(nla, head, len, rem) + if (nla_type(nla) == attrtype) + return nla; + + return NULL; +} + +/** @} */ + +/** + * @name Utilities + * @{ + */ + +/** + * Copy a netlink attribute into another memory area + * @arg dest where to copy to memcpy + * @arg src netlink attribute to copy from + * @arg count size of the destination area + * + * Note: The number of bytes copied is limited by the length of + * attribute's payload. memcpy + * + * @return the number of bytes copied. + */ +int nla_memcpy(void *dest, struct nlattr *src, int count) +{ + int minlen; + + if (!src) + return 0; + + minlen = min_t(int, count, nla_len(src)); + memcpy(dest, nla_data(src), minlen); + + return minlen; +} + +/** + * Copy string attribute payload into a sized buffer + * @arg dst where to copy the string to + * @arg nla attribute to copy the string from + * @arg dstsize size of destination buffer + * + * Copies at most dstsize - 1 bytes into the destination buffer. + * The result is always a valid NUL-terminated string. Unlike + * strlcpy the destination buffer is always padded out. + * + * @return the length of the source buffer. + */ +size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize) +{ + size_t srclen = nla_len(nla); + char *src = nla_data(nla); + + if (srclen > 0 && src[srclen - 1] == '\0') + srclen--; + + if (dstsize > 0) { + size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen; + + memset(dst, 0, dstsize); + memcpy(dst, src, len); + } + + return srclen; +} + +/** + * Compare an attribute with sized memory area + * @arg nla netlink attribute + * @arg data memory area + * @arg size size of memory area + */ +int nla_memcmp(const struct nlattr *nla, const void *data, + size_t size) +{ + int d = nla_len(nla) - size; + + if (d == 0) + d = memcmp(nla_data(nla), data, size); + + return d; +} + +/** + * Compare a string attribute against a string + * @arg nla netlink string attribute + * @arg str another string + */ +int nla_strcmp(const struct nlattr *nla, const char *str) +{ + int len = strlen(str) + 1; + int d = nla_len(nla) - len; + + if (d == 0) + d = memcmp(nla_data(nla), str, len); + + return d; +} + +/** @} */ + +/** + * @name Attribute Construction + * @{ + */ + +/** + * reserve room for attribute on the skb + * @arg n netlink message + * @arg attrtype attribute type + * @arg attrlen length of attribute payload + * + * Adds a netlink attribute header to a netlink message and reserves + * room for the payload but does not copy it. + */ +struct nlattr *nla_reserve(struct nl_msg *n, int attrtype, int attrlen) +{ + struct nlattr *nla; + int tlen; + + tlen = NLMSG_ALIGN(n->nm_nlh->nlmsg_len) + nla_total_size(attrlen); + + n->nm_nlh = realloc(n->nm_nlh, tlen); + if (!n->nm_nlh) { + nl_errno(ENOMEM); + return NULL; + } + + nla = (struct nlattr *) nlmsg_tail(n->nm_nlh); + nla->nla_type = attrtype; + nla->nla_len = nla_attr_size(attrlen); + + memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen)); + n->nm_nlh->nlmsg_len = tlen; + + NL_DBG(2, "msg %p: Reserved %d bytes at offset +%d for attr %d " + "nlmsg_len=%d\n", n, attrlen, + (void *) nla - nlmsg_data(n->nm_nlh), + attrtype, n->nm_nlh->nlmsg_len); + + return nla; +} + +/** + * Add a netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg attrlen length of attribute payload + * @arg data head of attribute payload + * + * @return -1 if the tailroom of the skb is insufficient to store + * the attribute header and payload. + */ +int nla_put(struct nl_msg *n, int attrtype, int attrlen, const void *data) +{ + struct nlattr *nla; + + nla = nla_reserve(n, attrtype, attrlen); + if (!nla) + return nl_errno(ENOMEM); + + memcpy(nla_data(nla), data, attrlen); + NL_DBG(2, "msg %p: Wrote %d bytes at offset +%d for attr %d\n", + n, attrlen, (void *) nla - nlmsg_data(n->nm_nlh), attrtype); + + return 0; +} + +/** + * Add a nested netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg nested netlink attribute to nest + * + * @return -1 if the tailroom of the skb is insufficient to store + * the attribute header and payload. + */ +int nla_put_nested(struct nl_msg *n, int attrtype, struct nl_msg *nested) +{ + return nla_put(n, attrtype, nlmsg_len(nested->nm_nlh), + nlmsg_data(nested->nm_nlh)); +} + +/** + * Add a u16 netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg value numeric value + */ +int nla_put_u8(struct nl_msg *n, int attrtype, uint8_t value) +{ + return nla_put(n, attrtype, sizeof(uint8_t), &value); +} + +/** + * Add a u16 netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg value numeric value + */ +int nla_put_u16(struct nl_msg *n, int attrtype, uint16_t value) +{ + return nla_put(n, attrtype, sizeof(uint16_t), &value); +} + +/** + * Add a u32 netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg value numeric value + */ +int nla_put_u32(struct nl_msg *n, int attrtype, uint32_t value) +{ + return nla_put(n, attrtype, sizeof(uint32_t), &value); +} + +/** + * Add a u64 netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg value numeric value + */ +int nla_put_u64(struct nl_msg *n, int attrtype, uint64_t value) +{ + return nla_put(n, attrtype, sizeof(uint64_t), &value); +} + +/** + * Add a string netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg str NUL terminated string + */ +int nla_put_string(struct nl_msg *n, int attrtype, const char *str) +{ + return nla_put(n, attrtype, strlen(str) + 1, str); +} + +/** + * Add a flag netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + */ +int nla_put_flag(struct nl_msg *n, int attrtype) +{ + return nla_put(n, attrtype, 0, NULL); +} + +/** + * Add a msecs netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg msecs number of msecs + */ +int nla_put_msecs(struct nl_msg *n, int attrtype, unsigned long msecs) +{ + return nla_put_u64(n, attrtype, msecs); +} + +/** + * Add an abstract data netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg data abstract data + */ +int nla_put_data(struct nl_msg *n, int attrtype, struct nl_data *data) +{ + return nla_put(n, attrtype, nl_data_get_size(data), + nl_data_get(data)); +} + +/** + * Add an abstract address netlink attribute to a netlink message + * @arg n netlink message + * @arg attrtype attribute type + * @arg addr abstract address + */ +int nla_put_addr(struct nl_msg *n, int attrtype, struct nl_addr *addr) +{ + return nla_put(n, attrtype, nl_addr_get_len(addr), + nl_addr_get_binary_addr(addr)); +} + +/** @} */ + +/** + * @name Attribute Nesting + * @{ + */ + +/** + * Start a new level of nested attributes + * @arg n netlink message + * @arg attrtype attribute type of container + * + * @return the container attribute + */ +struct nlattr *nla_nest_start(struct nl_msg *n, int attrtype) +{ + struct nlattr *start = (struct nlattr *) nlmsg_tail(n->nm_nlh); + + if (nla_put(n, attrtype, 0, NULL) < 0) + return NULL; + + return start; +} + +/** + * Finalize nesting of attributes + * @arg n netlink message + * @arg start container attribute + * + * Corrects the container attribute header to include the all + * appeneded attributes. + * + * @return the total data length of the skb. + */ +int nla_nest_end(struct nl_msg *n, struct nlattr *start) +{ + start->nla_len = (unsigned char *) nlmsg_tail(n->nm_nlh) - + (unsigned char *) start; + return 0; +} + +/** @} */ + +/** + * @name Attribute Reading + * @{ + */ + +/** + * Return payload of u32 attribute + * @arg nla u32 netlink attribute + */ +uint32_t nla_get_u32(struct nlattr *nla) +{ + return *(uint32_t *) nla_data(nla); +} + +/** + * Return payload of u16 attribute + * @arg nla u16 netlink attribute + */ +uint16_t nla_get_u16(struct nlattr *nla) +{ + return *(uint16_t *) nla_data(nla); +} + +/** + * Return payload of u8 attribute + * @arg nla u8 netlink attribute + */ +uint8_t nla_get_u8(struct nlattr *nla) +{ + return *(uint8_t *) nla_data(nla); +} + +/** + * Return payload of u64 attribute + * @arg nla u64 netlink attribute + */ +uint64_t nla_get_u64(struct nlattr *nla) +{ + uint64_t tmp; + + nla_memcpy(&tmp, nla, sizeof(tmp)); + + return tmp; +} + +/** + * return payload of string attribute + * @arg nla string netlink attribute + */ +char *nla_get_string(struct nlattr *nla) +{ + return (char *) nla_data(nla); +} + +/** + * Return payload of flag attribute + * @arg nla flag netlink attribute + */ +int nla_get_flag(struct nlattr *nla) +{ + return !!nla; +} + +/** + * Return payload of msecs attribute + * @arg nla msecs netlink attribute + * + * @return the number of milliseconds. + */ +unsigned long nla_get_msecs(struct nlattr *nla) +{ + return nla_get_u64(nla); +} + +/** + * Return payload of address attribute + * @arg nla address netlink attribute + * @arg family address family + * + * @return Newly allocated address handle or NULL + */ +struct nl_addr *nla_get_addr(struct nlattr *nla, int family) +{ + return nl_addr_build(family, nla_data(nla), nla_len(nla)); +} + +/** + * Return payload of abstract data attribute + * @arg nla abstract data netlink attribute + * + * @return Newly allocated abstract data handle or NULL + */ +struct nl_data *nla_get_data(struct nlattr *nla) +{ + return nl_data_alloc(nla_data(nla), nla_len(nla)); +} + +/** @} */ + +/** @} */ diff --git a/lib/cache.c b/lib/cache.c new file mode 100644 index 0000000..577b2ba --- /dev/null +++ b/lib/cache.c @@ -0,0 +1,805 @@ +/* + * lib/cache.c Caching Module + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup cache_mngt + * @defgroup cache Cache + * + * @code + * Cache Management | | Type Specific Cache Operations + * + * | | +----------------+ +------------+ + * | request update | | msg_parser | + * | | +----------------+ +------------+ + * +- - - - -^- - - - - - - -^- -|- - - - + * nl_cache_update: | | | | + * 1) --------- co_request_update ------+ | | + * | | | + * 2) destroy old cache +----------- pp_cb ---------|---+ + * | | | + * 3) ---------- nl_recvmsgs ----------+ +- cb_valid -+ + * +--------------+ | | | | + * | nl_cache_add |<-----+ + - - -v- -|- - - - - - - - - - - + * +--------------+ | | +-------------+ + * | nl_recvmsgs | + * | | +-----|-^-----+ + * +---v-|---+ + * | | | nl_recv | + * +---------+ + * | | Core Netlink + * @endcode + * + * @{ + */ + +#include +#include +#include +#include +#include + +/** + * @name Access Functions + * @{ + */ + +/** + * Return the number of items in the cache + * @arg cache cache handle + */ +int nl_cache_nitems(struct nl_cache *cache) +{ + return cache->c_nitems; +} + +/** + * Return the number of items matching a filter in the cache + * @arg cache Cache object. + * @arg filter Filter object. + */ +int nl_cache_nitems_filter(struct nl_cache *cache, struct nl_object *filter) +{ + struct nl_object_ops *ops; + struct nl_object *obj; + int nitems = 0; + + if (cache->c_ops == NULL) + BUG(); + + ops = cache->c_ops->co_obj_ops; + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { + if (filter && !nl_object_match_filter(obj, filter)) + continue; + + nitems++; + } + + return nitems; +} + +/** + * Returns \b true if the cache is empty. + * @arg cache Cache to check + * @return \a true if the cache is empty, otherwise \b false is returned. + */ +int nl_cache_is_empty(struct nl_cache *cache) +{ + return nl_list_empty(&cache->c_items); +} + +/** + * Return the operations set of the cache + * @arg cache cache handle + */ +struct nl_cache_ops *nl_cache_get_ops(struct nl_cache *cache) +{ + return cache->c_ops; +} + +/** + * Return the first element in the cache + * @arg cache cache handle + */ +struct nl_object *nl_cache_get_first(struct nl_cache *cache) +{ + if (nl_list_empty(&cache->c_items)) + return NULL; + + return nl_list_entry(cache->c_items.next, + struct nl_object, ce_list); +} + +/** + * Return the last element in the cache + * @arg cache cache handle + */ +struct nl_object *nl_cache_get_last(struct nl_cache *cache) +{ + if (nl_list_empty(&cache->c_items)) + return NULL; + + return nl_list_entry(cache->c_items.prev, + struct nl_object, ce_list); +} + +/** + * Return the next element in the cache + * @arg obj current object + */ +struct nl_object *nl_cache_get_next(struct nl_object *obj) +{ + if (nl_list_at_tail(obj, &obj->ce_cache->c_items, ce_list)) + return NULL; + else + return nl_list_entry(obj->ce_list.next, + struct nl_object, ce_list); +} + +/** + * Return the previous element in the cache + * @arg obj current object + */ +struct nl_object *nl_cache_get_prev(struct nl_object *obj) +{ + if (nl_list_at_head(obj, &obj->ce_cache->c_items, ce_list)) + return NULL; + else + return nl_list_entry(obj->ce_list.prev, + struct nl_object, ce_list); +} + +/** @} */ + +/** + * @name Cache Creation/Deletion + * @{ + */ + +/** + * Allocate an empty cache + * @arg ops cache operations to base the cache on + * + * @return A newly allocated and initialized cache. + */ +struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops) +{ + struct nl_cache *cache; + + cache = calloc(1, sizeof(*cache)); + if (!cache) { + nl_errno(ENOMEM); + return NULL; + } + + nl_init_list_head(&cache->c_items); + cache->c_ops = ops; + + NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache)); + + return cache; +} + +/** + * Allocate an empty cache based on type name + * @arg kind Name of cache type + * @return A newly allocated and initialized cache. + */ +struct nl_cache *nl_cache_alloc_name(const char *kind) +{ + struct nl_cache_ops *ops; + + ops = nl_cache_ops_lookup(kind); + if (!ops) { + nl_error(ENOENT, "Unable to lookup cache \"%s\"", kind); + return NULL; + } + + return nl_cache_alloc(ops); +} + +/** + * Allocate a new cache containing a subset of a cache + * @arg orig Original cache to be based on + * @arg filter Filter defining the subset to be filled into new cache + * @return A newly allocated cache or NULL. + */ +struct nl_cache *nl_cache_subset(struct nl_cache *orig, + struct nl_object *filter) +{ + struct nl_cache *cache; + struct nl_object_ops *ops; + struct nl_object *obj; + + if (!filter) + BUG(); + + cache = nl_cache_alloc(orig->c_ops); + if (!cache) + return NULL; + + ops = orig->c_ops->co_obj_ops; + + nl_list_for_each_entry(obj, &orig->c_items, ce_list) { + if (!nl_object_match_filter(obj, filter)) + continue; + + nl_cache_add(cache, obj); + } + + return cache; +} + +/** + * Clear a cache. + * @arg cache cache to clear + * + * Removes all elements of a cache. + */ +void nl_cache_clear(struct nl_cache *cache) +{ + struct nl_object *obj, *tmp; + + NL_DBG(1, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache)); + + nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) + nl_cache_remove(obj); +} + +/** + * Free a cache. + * @arg cache Cache to free. + * + * Removes all elements of a cache and frees all memory. + * + * @note Use this function if you are working with allocated caches. + */ +void nl_cache_free(struct nl_cache *cache) +{ + nl_cache_clear(cache); + NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache)); + free(cache); +} + +/** @} */ + +/** + * @name Cache Modifications + * @{ + */ + +static int __cache_add(struct nl_cache *cache, struct nl_object *obj) +{ + obj->ce_cache = cache; + + nl_list_add_tail(&obj->ce_list, &cache->c_items); + cache->c_nitems++; + + NL_DBG(1, "Added %p to cache %p <%s>.\n", + obj, cache, nl_cache_name(cache)); + + return 0; +} + +/** + * Add object to a cache. + * @arg cache Cache to add object to + * @arg obj Object to be added to the cache + * + * Adds the given object to the specified cache. The object is cloned + * if it has been added to another cache already. + * + * @return 0 or a negative error code. + */ +int nl_cache_add(struct nl_cache *cache, struct nl_object *obj) +{ + struct nl_object *new; + + if (cache->c_ops->co_obj_ops != obj->ce_ops) + return nl_error(EINVAL, "Object mismatches cache type"); + + if (!nl_list_empty(&obj->ce_list)) { + new = nl_object_clone(obj); + if (!new) + return nl_errno(ENOMEM); + } else { + nl_object_get(obj); + new = obj; + } + + return __cache_add(cache, new); +} + +/** + * Move object from one cache to another + * @arg cache Cache to move object to. + * @arg obj Object subject to be moved + * + * Removes the given object from its associated cache if needed + * and adds it to the new cache. + * + * @return 0 on success or a negative error code. + */ +int nl_cache_move(struct nl_cache *cache, struct nl_object *obj) +{ + if (cache->c_ops->co_obj_ops != obj->ce_ops) + return nl_error(EINVAL, "Object mismatches cache type"); + + NL_DBG(3, "Moving object %p to cache %p\n", obj, cache); + + /* Acquire reference, if already in a cache this will be + * reverted during removal */ + nl_object_get(obj); + + if (!nl_list_empty(&obj->ce_list)) + nl_cache_remove(obj); + + return __cache_add(cache, obj); +} + +/** + * Removes an object from a cache. + * @arg obj Object to remove from its cache + * + * Removes the object \c obj from the cache it is assigned to, since + * an object can only be assigned to one cache at a time, the cache + * must ne be passed along with it. + */ +void nl_cache_remove(struct nl_object *obj) +{ + struct nl_cache *cache = obj->ce_cache; + + if (cache == NULL) + return; + + nl_list_del(&obj->ce_list); + obj->ce_cache = NULL; + nl_object_put(obj); + cache->c_nitems--; + + NL_DBG(1, "Deleted %p from cache %p <%s>.\n", + obj, cache, nl_cache_name(cache)); +} + +/** + * Search for an object in a cache + * @arg cache Cache to search in. + * @arg needle Object to look for. + * + * Iterates over the cache and looks for an object with identical + * identifiers as the needle. + * + * @return Reference to object or NULL if not found. + * @note The returned object must be returned via nl_object_put(). + */ +struct nl_object *nl_cache_search(struct nl_cache *cache, + struct nl_object *needle) +{ + struct nl_object *obj; + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { + if (nl_object_identical(obj, needle)) { + nl_object_get(obj); + return obj; + } + } + + return NULL; +} + + +/** @} */ + +/** + * @name Synchronization + * @{ + */ + +/** + * Request a full dump from the kernel to fill a cache + * @arg handle Netlink handle + * @arg cache Cache subjected to be filled. + * + * Send a dumping request to the kernel causing it to dump all objects + * related to the specified cache to the netlink socket. + * + * Use nl_cache_pickup() to read the objects from the socket and fill them + * into a cache. + */ +int nl_cache_request_full_dump(struct nl_handle *handle, struct nl_cache *cache) +{ + NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n", + cache, nl_cache_name(cache)); + + return cache->c_ops->co_request_update(cache, handle); +} + +/** @cond SKIP */ +struct update_xdata { + struct nl_cache_ops *ops; + struct nl_parser_param *params; +}; + +static int update_msg_parser(struct nl_msg *msg, void *arg) +{ + struct update_xdata *x = arg; + + return nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params); +} +/** @endcond */ + +int __cache_pickup(struct nl_handle *handle, struct nl_cache *cache, + struct nl_parser_param *param) +{ + int err; + struct nl_cb *cb; + struct update_xdata x = { + .ops = cache->c_ops, + .params = param, + }; + + NL_DBG(1, "Picking up answer for cache %p <%s>...\n", + cache, nl_cache_name(cache)); + + cb = nl_cb_clone(handle->h_cb); + if (cb == NULL) + return nl_get_errno(); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x); + + err = nl_recvmsgs(handle, cb); + if (err < 0) + NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned " \ + "%d: %s", cache, nl_cache_name(cache), + err, nl_geterror()); + + nl_cb_put(cb); + + return err; +} + +static int pickup_cb(struct nl_object *c, struct nl_parser_param *p) +{ + return nl_cache_add((struct nl_cache *) p->pp_arg, c); +} + +/** + * Pickup a netlink dump response and put it into a cache. + * @arg handle Netlink handle. + * @arg cache Cache to put items into. + * + * Waits for netlink messages to arrive, parses them and puts them into + * the specified cache. + * + * @return 0 on success or a negative error code. + */ +int nl_cache_pickup(struct nl_handle *handle, struct nl_cache *cache) +{ + struct nl_parser_param p = { + .pp_cb = pickup_cb, + .pp_arg = cache, + }; + + return __cache_pickup(handle, cache, &p); +} + +static int cache_include(struct nl_cache *cache, struct nl_object *obj, + struct nl_msgtype *type, change_func_t cb) +{ + struct nl_object *old; + + switch (type->mt_act) { + case NL_ACT_NEW: + case NL_ACT_DEL: + old = nl_cache_search(cache, obj); + if (old) { + nl_cache_remove(old); + if (type->mt_act == NL_ACT_DEL && cb) + cb(cache, old, NL_ACT_DEL); + } + + if (type->mt_act == NL_ACT_NEW) { + nl_cache_move(cache, obj); + if (old == NULL && cb) + cb(cache, obj, NL_ACT_NEW); + else if (old) { + if (nl_object_diff(old, obj) && cb) + cb(cache, obj, NL_ACT_CHANGE); + + nl_object_put(old); + } + } + break; + default: + NL_DBG(2, "Unknown action associated to object %p\n", obj); + return 0; + } + + return 0; +} + +int nl_cache_include(struct nl_cache *cache, struct nl_object *obj, + change_func_t change_cb) +{ + struct nl_cache_ops *ops = cache->c_ops; + int i; + + if (ops->co_obj_ops != obj->ce_ops) + return nl_error(EINVAL, "Object mismatches cache type"); + + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) + if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype) + return cache_include(cache, obj, &ops->co_msgtypes[i], + change_cb); + + return nl_errno(EINVAL); +} + +static int resync_cb(struct nl_object *c, struct nl_parser_param *p) +{ + struct nl_cache_assoc *ca = p->pp_arg; + + return nl_cache_include(ca->ca_cache, c, ca->ca_change); +} + +int nl_cache_resync(struct nl_handle *handle, struct nl_cache *cache, + change_func_t change_cb) +{ + struct nl_object *obj, *next; + struct nl_cache_assoc ca = { + .ca_cache = cache, + .ca_change = change_cb, + }; + struct nl_parser_param p = { + .pp_cb = resync_cb, + .pp_arg = &ca, + }; + int err; + + NL_DBG(1, "Resyncing cache %p <%s>...\n", cache, nl_cache_name(cache)); + + /* Mark all objects so we can see if some of them are obsolete */ + nl_cache_mark_all(cache); + + err = nl_cache_request_full_dump(handle, cache); + if (err < 0) + goto errout; + + err = __cache_pickup(handle, cache, &p); + if (err < 0) + goto errout; + + nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list) + if (nl_object_is_marked(obj)) + nl_cache_remove(obj); + + NL_DBG(1, "Finished resyncing %p <%s>\n", cache, nl_cache_name(cache)); + + err = 0; +errout: + return err; +} + +/** @} */ + +/** + * @name Parsing + * @{ + */ + +/** @cond SKIP */ +int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, struct nl_parser_param *params) +{ + int i, err; + + if (nlh->nlmsg_len < nlmsg_msg_size(ops->co_hdrsize)) { + err = nl_error(EINVAL, "netlink message too short to be " + "of kind %s", ops->co_name); + goto errout; + } + + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) { + if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) { + err = ops->co_msg_parser(ops, who, nlh, params); + if (err != -ENOENT) + goto errout; + } + } + + + err = nl_error(EINVAL, "Unsupported netlink message type %d", + nlh->nlmsg_type); +errout: + return err; +} +/** @endcond */ + +/** + * Parse a netlink message and add it to the cache. + * @arg cache cache to add element to + * @arg msg netlink message + * + * Parses a netlink message by calling the cache specific message parser + * and adds the new element to the cache. + * + * @return 0 or a negative error code. + */ +int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg) +{ + struct nl_parser_param p = { + .pp_cb = pickup_cb, + .pp_arg = cache, + }; + + return nl_cache_parse(cache->c_ops, NULL, nlmsg_hdr(msg), &p); +} + +/** + * (Re)fill a cache with the contents in the kernel. + * @arg handle netlink handle + * @arg cache cache to update + * + * Clears the specified cache and fills it with the current state in + * the kernel. + * + * @return 0 or a negative error code. + */ +int nl_cache_refill(struct nl_handle *handle, struct nl_cache *cache) +{ + int err; + + err = nl_cache_request_full_dump(handle, cache); + if (err < 0) + return err; + + NL_DBG(2, "Upading cache %p <%s>, request sent, waiting for dump...\n", + cache, nl_cache_name(cache)); + nl_cache_clear(cache); + + return nl_cache_pickup(handle, cache); +} + +/** @} */ + +/** + * @name Utillities + * @{ + */ + +/** + * Mark all objects in a cache + * @arg cache Cache to mark all objects in + */ +void nl_cache_mark_all(struct nl_cache *cache) +{ + struct nl_object *obj; + + NL_DBG(2, "Marking all objects in cache %p <%s>...\n", + cache, nl_cache_name(cache)); + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) + nl_object_mark(obj); +} + +/** @} */ + +/** + * @name Dumping + * @{ + */ + +/** + * Dump all elements of a cache. + * @arg cache cache to dump + * @arg params dumping parameters + * + * Dumps all elements of the \a cache to the file descriptor \a fd. + */ +void nl_cache_dump(struct nl_cache *cache, struct nl_dump_params *params) +{ + nl_cache_dump_filter(cache, params, NULL); +} + +/** + * Dump all elements of a cache (filtered). + * @arg cache cache to dump + * @arg params dumping parameters (optional) + * @arg filter filter object + * + * Dumps all elements of the \a cache to the file descriptor \a fd + * given they match the given filter \a filter. + */ +void nl_cache_dump_filter(struct nl_cache *cache, + struct nl_dump_params *params, + struct nl_object *filter) +{ + int type = params ? params->dp_type : NL_DUMP_FULL; + struct nl_object_ops *ops; + struct nl_object *obj; + + NL_DBG(2, "Dumping cache %p <%s> filter %p\n", + cache, nl_cache_name(cache), filter); + + if (type > NL_DUMP_MAX || type < 0) + BUG(); + + if (cache->c_ops == NULL) + BUG(); + + ops = cache->c_ops->co_obj_ops; + if (!ops->oo_dump[type]) + return; + + nl_list_for_each_entry(obj, &cache->c_items, ce_list) { + if (filter && !nl_object_match_filter(obj, filter)) + continue; + + NL_DBG(4, "Dumping object %p...\n", obj); + dump_from_ops(obj, params); + } +} + +/** @} */ + +/** + * @name Iterators + * @{ + */ + +/** + * Call a callback on each element of the cache. + * @arg cache cache to iterate on + * @arg cb callback function + * @arg arg argument passed to callback function + * + * Calls a callback function \a cb on each element of the \a cache. + * The argument \a arg is passed on the callback function. + */ +void nl_cache_foreach(struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + nl_cache_foreach_filter(cache, NULL, cb, arg); +} + +/** + * Call a callback on each element of the cache (filtered). + * @arg cache cache to iterate on + * @arg filter filter object + * @arg cb callback function + * @arg arg argument passed to callback function + * + * Calls a callback function \a cb on each element of the \a cache + * that matches the \a filter. The argument \a arg is passed on + * to the callback function. + */ +void nl_cache_foreach_filter(struct nl_cache *cache, struct nl_object *filter, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct nl_object *obj, *tmp; + struct nl_object_ops *ops; + + if (cache->c_ops == NULL) + BUG(); + + ops = cache->c_ops->co_obj_ops; + + nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) { + if (filter && !nl_object_match_filter(obj, filter)) + continue; + + cb(obj, arg); + } +} + +/** @} */ + +/** @} */ diff --git a/lib/cache_mngr.c b/lib/cache_mngr.c new file mode 100644 index 0000000..4e6b1a6 --- /dev/null +++ b/lib/cache_mngr.c @@ -0,0 +1,392 @@ +/* + * lib/cache_mngr.c Cache Manager + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2007 Thomas Graf + */ + +/** + * @ingroup cache_mngt + * @defgroup cache_mngr Manager + * @brief Helps keeping caches up to date. + * + * The purpose of a cache manager is to keep track of caches and + * automatically receive event notifications to keep the caches + * up to date with the kernel state. Each manager has exactly one + * netlink socket assigned which limits the scope of each manager + * to exactly one netlink family. Therefore all caches committed + * to a manager must be part of the same netlink family. Due to the + * nature of a manager, it is not possible to have a cache maintain + * two instances of the same cache type. The socket is subscribed + * to the event notification group of each cache and also put into + * non-blocking mode. Functions exist to poll() on the socket to + * wait for new events to be received. + * + * @code + * App libnl Kernel + * | | + * +-----------------+ [ notification, link change ] + * | | Cache Manager | | [ (IFF_UP | IFF_RUNNING) ] + * | | | + * | | +------------+| | | [ notification, new addr ] + * <-------|---| route/link |<-------(async)--+ [ 10.0.1.1/32 dev eth1 ] + * | | +------------+| | | + * | +------------+| | + * <---|---|---| route/addr |<------|-(async)--------------+ + * | +------------+| + * | | +------------+| | + * <-------|---| ... || + * | | +------------+| | + * +-----------------+ + * | | + * @endcode + * + * @par 1) Creating a new cache manager + * @code + * struct nl_cache_mngr *mngr; + * + * // Allocate a new cache manager for RTNETLINK and automatically + * // provide the caches added to the manager. + * mngr = nl_cache_mngr_alloc(NETLINK_ROUTE, NL_AUTO_PROVIDE); + * @endcode + * + * @par 2) Keep track of a cache + * @code + * struct nl_cache *cache; + * + * // Create a new cache for links/interfaces and ask the manager to + * // keep it up to date for us. This will trigger a full dump request + * // to initially fill the cache. + * cache = nl_cache_mngr_add(mngr, "route/link"); + * @endcode + * + * @par 3) Make the manager receive updates + * @code + * // Give the manager the ability to receive updates, will call poll() + * // with a timeout of 5 seconds. + * if (nl_cache_mngr_poll(mngr, 5000) > 0) { + * // Manager received at least one update, dump cache? + * nl_cache_dump(cache, ...); + * } + * @endcode + * + * @par 4) Release cache manager + * @code + * nl_cache_mngr_free(mngr); + * @endcode + * @{ + */ + +#include +#include +#include +#include + +static int include_cb(struct nl_object *obj, struct nl_parser_param *p) +{ + struct nl_cache_assoc *ca = p->pp_arg; + int err; + + NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache); +#ifdef NL_DEBUG + if (nl_debug >= 4) + nl_object_dump(obj, &nl_debug_dp); +#endif + err = nl_cache_include(ca->ca_cache, obj, ca->ca_change); + + nl_object_put(obj); + + return err; +} + +static int event_input(struct nl_msg *msg, void *arg) +{ + struct nl_cache_mngr *mngr = arg; + int protocol = nlmsg_get_proto(msg); + int type = nlmsg_hdr(msg)->nlmsg_type; + struct nl_cache_ops *ops; + int i, n; + struct nl_parser_param p = { + .pp_cb = include_cb, + }; + + NL_DBG(2, "Cache manager %p, handling new message %p as event\n", + mngr, msg); +#ifdef NL_DEBUG + if (nl_debug >= 4) + nl_msg_dump(msg, stderr); +#endif + + if (mngr->cm_protocol != protocol) + BUG(); + + for (i = 0; i < mngr->cm_nassocs; i++) { + if (mngr->cm_assocs[i].ca_cache) { + ops = mngr->cm_assocs[i].ca_cache->c_ops; + for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++) + if (ops->co_msgtypes[n].mt_id == type) + goto found; + } + } + + return NL_SKIP; + +found: + NL_DBG(2, "Associated message %p to cache %p\n", + msg, mngr->cm_assocs[i].ca_cache); + p.pp_arg = &mngr->cm_assocs[i]; + + return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); +} + +/** + * Allocate new cache manager + * @arg protocol Netlink Protocol this manager is used for + * @arg flags Flags + * + * @return Newly allocated cache manager or NULL on failure. + */ +struct nl_cache_mngr *nl_cache_mngr_alloc(struct nl_handle *handle, + int protocol, int flags) +{ + struct nl_cache_mngr *mngr; + + if (handle == NULL) + BUG(); + + mngr = calloc(1, sizeof(*mngr)); + if (!mngr) + goto enomem; + + mngr->cm_handle = handle; + mngr->cm_nassocs = 32; + mngr->cm_protocol = protocol; + mngr->cm_flags = flags; + mngr->cm_assocs = calloc(mngr->cm_nassocs, + sizeof(struct nl_cache_assoc)); + if (!mngr->cm_assocs) + goto enomem; + + + nl_socket_modify_cb(mngr->cm_handle, NL_CB_VALID, NL_CB_CUSTOM, + event_input, mngr); + + /* Required to receive async event notifications */ + nl_disable_sequence_check(mngr->cm_handle); + + if (nl_connect(mngr->cm_handle, protocol) < 0) + goto errout; + + if (nl_socket_set_nonblocking(mngr->cm_handle) < 0) + goto errout; + + NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n", + mngr, protocol, mngr->cm_nassocs); + + return mngr; + +enomem: + nl_errno(ENOMEM); +errout: + nl_cache_mngr_free(mngr); + return NULL; +} + +/** + * Add cache responsibility to cache manager + * @arg mngr Cache manager. + * @arg name Name of cache to keep track of + * + * Allocates a new cache of the specified type and adds it to the manager. + * The operation will trigger a full dump request from the kernel to + * initially fill the contents of the cache. The manager will subscribe + * to the notification group of the cache to keep track of any further + * changes. + * + * @return The newly allocated cache or NULL on failure. + */ +struct nl_cache *nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, + change_func_t cb) +{ + struct nl_cache_ops *ops; + struct nl_cache *cache; + struct nl_af_group *grp; + int err, i; + + ops = nl_cache_ops_lookup(name); + if (!ops) { + nl_error(ENOENT, "Unknown cache type"); + return NULL; + } + + if (ops->co_protocol != mngr->cm_protocol) { + nl_error(EINVAL, "Netlink protocol mismatch"); + return NULL; + } + + if (ops->co_groups == NULL) { + nl_error(EOPNOTSUPP, NULL); + return NULL; + } + + for (i = 0; i < mngr->cm_nassocs; i++) { + if (mngr->cm_assocs[i].ca_cache && + mngr->cm_assocs[i].ca_cache->c_ops == ops) { + nl_error(EEXIST, "Cache of this type already managed"); + return NULL; + } + } + +retry: + for (i = 0; i < mngr->cm_nassocs; i++) + if (!mngr->cm_assocs[i].ca_cache) + break; + + if (i >= mngr->cm_nassocs) { + mngr->cm_nassocs += 16; + mngr->cm_assocs = realloc(mngr->cm_assocs, + mngr->cm_nassocs * + sizeof(struct nl_cache_assoc)); + if (mngr->cm_assocs == NULL) { + nl_errno(ENOMEM); + return NULL; + } else { + NL_DBG(1, "Increased capacity of cache manager %p " \ + "to %d\n", mngr, mngr->cm_nassocs); + goto retry; + } + } + + cache = nl_cache_alloc(ops); + if (!cache) { + nl_errno(ENOMEM); + return NULL; + } + + for (grp = ops->co_groups; grp->ag_group; grp++) { + err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group); + if (err < 0) + goto errout_free_cache; + } + + err = nl_cache_refill(mngr->cm_handle, cache); + if (err < 0) + goto errout_drop_membership; + + mngr->cm_assocs[i].ca_cache = cache; + mngr->cm_assocs[i].ca_change = cb; + + if (mngr->cm_flags & NL_AUTO_PROVIDE) + nl_cache_mngt_provide(cache); + + NL_DBG(1, "Added cache %p <%s> to cache manager %p\n", + cache, nl_cache_name(cache), mngr); + + return cache; + +errout_drop_membership: + for (grp = ops->co_groups; grp->ag_group; grp++) + nl_socket_drop_membership(mngr->cm_handle, grp->ag_group); +errout_free_cache: + nl_cache_free(cache); + + return NULL; +} + +/** + * Get file descriptor + * @arg mngr Cache Manager + * + * Get the file descriptor of the socket associated to the manager. + * This can be used to change socket options or monitor activity + * using poll()/select(). + */ +int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr) +{ + return nl_socket_get_fd(mngr->cm_handle); +} + +/** + * Check for event notifications + * @arg mngr Cache Manager + * @arg timeout Upper limit poll() will block, in milliseconds. + * + * Causes poll() to be called to check for new event notifications + * being available. Automatically receives and handles available + * notifications. + * + * This functionally is ideally called regularly during an idle + * period. + * + * @return A positive value if at least one update was handled, 0 + * for none, or a negative error code. + */ +int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout) +{ + int ret; + struct pollfd fds = { + .fd = nl_socket_get_fd(mngr->cm_handle), + .events = POLLIN, + }; + + NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd); + ret = poll(&fds, 1, timeout); + NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret); + if (ret < 0) + return nl_errno(errno); + + if (ret == 0) + return 0; + + return nl_cache_mngr_data_ready(mngr); +} + +/** + * Receive available event notifications + * @arg mngr Cache manager + * + * This function can be called if the socket associated to the manager + * contains updates to be received. This function should not be used + * if nl_cache_mngr_poll() is used. + * + * @return A positive value if at least one update was handled, 0 + * for none, or a negative error code. + */ +int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr) +{ + int err; + + err = nl_recvmsgs_default(mngr->cm_handle); + if (err < 0) + return err; + + return 1; +} + +/** + * Free cache manager + * @arg mngr Cache manager + * + * Release all resources after usage of a cache manager. + */ +void nl_cache_mngr_free(struct nl_cache_mngr *mngr) +{ + if (!mngr) + return; + + if (mngr->cm_handle) { + nl_close(mngr->cm_handle); + nl_handle_destroy(mngr->cm_handle); + } + + free(mngr->cm_assocs); + free(mngr); + + NL_DBG(1, "Cache manager %p freed\n", mngr); +} + +/** @} */ diff --git a/lib/cache_mngt.c b/lib/cache_mngt.c new file mode 100644 index 0000000..7cf1a03 --- /dev/null +++ b/lib/cache_mngt.c @@ -0,0 +1,266 @@ +/* + * lib/cache_mngt.c Cache Management + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @defgroup cache_mngt Caching + * @{ + */ + +#include +#include +#include +#include + +static struct nl_cache_ops *cache_ops; + +/** + * Associate a message type to a set of cache operations + * @arg protocol netlink protocol + * @arg message_type netlink message type + * + * Associates the specified netlink message type with + * a registered set of cache operations. + * + * @return The cache operations or NULL if no association + * could be made. + */ +struct nl_cache_ops *nl_cache_mngt_associate(int protocol, int message_type) +{ + int i; + struct nl_cache_ops *ops; + + for (ops = cache_ops; ops; ops = ops->co_next) + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) + if (ops->co_msgtypes[i].mt_id == message_type && + ops->co_protocol == protocol) + return ops; + + return NULL; +} + +/** + * Convert message type to character string. + * @arg ops Cache operations. + * @arg protocol Netlink Protocol. + * @arg msgtype Message type. + * @arg buf Destination buffer. + * @arg len Size of destination buffer. + * + * Converts a message type to a character string and stores it in the + * provided buffer. + * + * @return The destination buffer or the message type encoded in + * hexidecimal form if no match was found. + */ +char *nl_cache_mngt_type2name(struct nl_cache_ops *ops, int protocol, + int msgtype, char *buf, size_t len) +{ + int i; + + for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) { + if (ops->co_msgtypes[i].mt_id == msgtype && + ops->co_protocol == protocol) { + snprintf(buf, len, "%s::%s", + ops->co_name, + ops->co_msgtypes[i].mt_name); + return buf; + } + } + + snprintf(buf, len, "%d:%s->0x%x()", protocol, ops->co_name, msgtype); + return buf; +} + +/** + * @name Cache Type Management + * @{ + */ + +/** + * Lookup the set cache operations of a certain cache type + * @arg name name of the cache type + * + * @return The cache operations or NULL if no operations + * have been registered under the specified name. + */ +struct nl_cache_ops *nl_cache_ops_lookup(const char *name) +{ + struct nl_cache_ops *ops; + + for (ops = cache_ops; ops; ops = ops->co_next) + if (!strcmp(ops->co_name, name)) + return ops; + + return NULL; +} + +/** + * Lookupt the set of cache operations responsible for a type of object + * @arg obj_ops Object operations + * + * @return The cache operations or NULL if not found. + */ +struct nl_cache_ops *nl_cache_ops_lookup_for_obj(struct nl_object_ops *obj_ops) +{ + struct nl_cache_ops *ops; + + for (ops = cache_ops; ops; ops = ops->co_next) + if (ops->co_obj_ops == obj_ops) + return ops; + + return NULL; + +} + +/** + * Call a function for each registered cache operation + * @arg cb Callback function to be called + * @arg arg User specific argument. + */ +void nl_cache_mngt_foreach(void (*cb)(struct nl_cache_ops *, void *), void *arg) +{ + struct nl_cache_ops *ops; + + for (ops = cache_ops; ops; ops = ops->co_next) + cb(ops, arg); +} + +/** + * Register a set of cache operations + * @arg ops cache operations + * + * Called by users of caches to announce the avaibility of + * a certain cache type. + * + * @return 0 on success or a negative error code. + */ +int nl_cache_mngt_register(struct nl_cache_ops *ops) +{ + if (!ops->co_name) + return nl_error(EINVAL, "No cache name specified"); + + if (!ops->co_obj_ops) + return nl_error(EINVAL, "No obj cache ops specified"); + + if (nl_cache_ops_lookup(ops->co_name)) + return nl_error(EEXIST, "Cache operations already exist"); + + ops->co_next = cache_ops; + cache_ops = ops; + + NL_DBG(1, "Registered cache operations %s\n", ops->co_name); + + return 0; +} + +/** + * Unregister a set of cache operations + * @arg ops cache operations + * + * Called by users of caches to announce a set of + * cache operations is no longer available. The + * specified cache operations must have been registered + * previously using nl_cache_mngt_register() + * + * @return 0 on success or a negative error code + */ +int nl_cache_mngt_unregister(struct nl_cache_ops *ops) +{ + struct nl_cache_ops *t, **tp; + + for (tp = &cache_ops; (t=*tp) != NULL; tp = &t->co_next) + if (t == ops) + break; + + if (!t) + return nl_error(ENOENT, "No such cache operations"); + + NL_DBG(1, "Unregistered cache operations %s\n", ops->co_name); + + *tp = t->co_next; + return 0; +} + +/** @} */ + +/** + * @name Global Cache Provisioning/Requiring + * @{ + */ + +/** + * Provide a cache for global use + * @arg cache cache to provide + * + * Offers the specified cache to be used by other modules. + * Only one cache per type may be shared at a time, + * a previsouly provided caches will be overwritten. + */ +void nl_cache_mngt_provide(struct nl_cache *cache) +{ + struct nl_cache_ops *ops; + + ops = nl_cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops); + if (!ops) + BUG(); + else + ops->co_major_cache = cache; +} + +/** + * Unprovide a cache for global use + * @arg cache cache to unprovide + * + * Cancels the offer to use a cache globally. The + * cache will no longer be returned via lookups but + * may still be in use. + */ +void nl_cache_mngt_unprovide(struct nl_cache *cache) +{ + struct nl_cache_ops *ops; + + ops = nl_cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops); + if (!ops) + BUG(); + else if (ops->co_major_cache == cache) + ops->co_major_cache = NULL; +} + +/** + * Demand the use of a global cache + * @arg name name of the required object type + * + * Trys to find a cache of the specified type for global + * use. + * + * @return A cache provided by another subsystem of the + * specified type marked to be available. + */ +struct nl_cache *nl_cache_mngt_require(const char *name) +{ + struct nl_cache_ops *ops; + + ops = nl_cache_ops_lookup(name); + if (!ops || !ops->co_major_cache) { + fprintf(stderr, "Application BUG: Your application must " + "call nl_cache_mngt_provide() and\nprovide a valid " + "%s cache to be used for internal lookups.\nSee the " + " API documentation for more details.\n", name); + + return NULL; + } + + return ops->co_major_cache; +} + +/** @} */ + +/** @} */ diff --git a/lib/data.c b/lib/data.c new file mode 100644 index 0000000..9399389 --- /dev/null +++ b/lib/data.c @@ -0,0 +1,172 @@ +/* + * lib/data.c Abstract Data + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup utils + * @defgroup data Abstract Data + * @{ + */ + +#include +#include +#include +#include + +/** + * @name General + * @{ + */ + +/** + * Allocate a new abstract data object. + * @arg buf Data buffer containing the actual data. + * @arg size Size of data buffer. + * + * Allocates a new abstract data and copies the specified data + * buffer into the new handle. + * + * @return Newly allocated data handle or NULL + */ +struct nl_data *nl_data_alloc(void *buf, size_t size) +{ + struct nl_data *data; + + data = calloc(1, sizeof(*data)); + if (!data) + goto errout; + + data->d_data = calloc(1, size); + if (!data->d_data) { + free(data); + goto errout; + } + + data->d_size = size; + + if (buf) + memcpy(data->d_data, buf, size); + + return data; +errout: + nl_errno(ENOMEM); + return NULL; +} + +/** + * Clone an abstract data object. + * @arg src Abstract data object + * + * @return Cloned object or NULL + */ +struct nl_data *nl_data_clone(struct nl_data *src) +{ + return nl_data_alloc(src->d_data, src->d_size); +} + +/** + * Append data to an abstract data object. + * @arg data Abstract data object. + * @arg buf Data buffer containing the data to be appended. + * @arg size Size of data to be apppended. + * + * Reallocates an abstract data and copies the specified data + * buffer into the new handle. + * + * @return 0 on success or a negative error code + */ +int nl_data_append(struct nl_data *data, void *buf, size_t size) +{ + if (size < 0) + BUG(); + + if (size > 0) { + data->d_data = realloc(data->d_data, data->d_size + size); + if (!data->d_data) + return nl_errno(ENOMEM); + + if (buf) + memcpy(data->d_data + data->d_size, buf, size); + else + memset(data->d_data + data->d_size, 0, size); + + data->d_size += size; + } + + return 0; +} + +/** + * Free an abstract data object. + * @arg data Abstract data object. + */ +void nl_data_free(struct nl_data *data) +{ + if (data) + free(data->d_data); + + free(data); +} + +/** @} */ + +/** + * @name Attribute Access + * @{ + */ + +/** + * Get data buffer of abstract data object. + * @arg data Abstract data object. + * @return Data buffer or NULL if empty. + */ +void *nl_data_get(struct nl_data *data) +{ + return data->d_size > 0 ? data->d_data : NULL; +} + +/** + * Get size of data buffer of abstract data object. + * @arg data Abstract data object. + * @return Size of data buffer. + */ +size_t nl_data_get_size(struct nl_data *data) +{ + return data->d_size; +} + +/** @} */ + +/** + * @name Misc + * @{ + */ + +/** + * Compare two abstract data objects. + * @arg a Abstract data object. + * @arg b Another abstract data object. + * @return An integer less than, equal to, or greater than zero if + * a is found, respectively, to be less than, to match, or + * be greater than b. + */ +int nl_data_cmp(struct nl_data *a, struct nl_data *b) +{ + void *a_ = nl_data_get(a); + void *b_ = nl_data_get(b); + + if (a_ && b_) + return memcmp(a_, b_, nl_data_get_size(a)); + else + return -1; +} + +/** @} */ +/** @} */ diff --git a/lib/defs.h b/lib/defs.h new file mode 100644 index 0000000..f079bdf --- /dev/null +++ b/lib/defs.h @@ -0,0 +1,29 @@ +/* lib/defs.h. Generated by configure. */ +/* lib/defs.h.in. Generated from configure.in by autoheader. */ + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "tgraf@suug.ch" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "libnl" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "libnl 1.0-pre6" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "libnl" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "1.0-pre6" + +/* verbose errors */ +/* #undef VERBOSE_ERRORS */ + +/* Define to empty if `const' does not conform to ANSI C. */ +/* #undef const */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif diff --git a/lib/defs.h.in b/lib/defs.h.in new file mode 100644 index 0000000..ef86caa --- /dev/null +++ b/lib/defs.h.in @@ -0,0 +1,28 @@ +/* lib/defs.h.in. Generated from configure.in by autoheader. */ + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* verbose errors */ +#undef VERBOSE_ERRORS + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +#undef inline +#endif diff --git a/lib/doc.c b/lib/doc.c new file mode 100644 index 0000000..8fee8c8 --- /dev/null +++ b/lib/doc.c @@ -0,0 +1,106 @@ +/* + * lib/doc.c Documentation Purpose + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @mainpage + * + * @section remarks Remarks + * + * @subsection cache_alloc Allocation of Caches + * + * Almost all subsystem provide a function to allocate a new cache + * of some form. The function usually looks like this: + * @code + * struct nl_cache *_alloc_cache(struct nl_handle *handle) + * @endcode + * + * These functions allocate a new cache for the own object type, + * initializes it properly and updates it to represent the current + * state of their master, e.g. a link cache would include all + * links currently configured in the kernel. + * + * Some of the allocation functions may take additional arguments + * to further specify what will be part of the cache. + * + * All such functions return a newly allocated cache or NULL + * in case of an error. + * + * @subsection addr Setting of Addresses + * @code + * int _set_addr(struct nl_object *, struct nl_addr *) + * @endcode + * + * All attribute functions avaiable for assigning addresses to objects + * take a struct nl_addr argument. The provided address object is + * validated against the address family of the object if known already. + * The assignment fails if the address families mismatch. In case the + * address family has not been specified yet, the address family of + * the new address is elected to be the new requirement. + * + * The function will acquire a new reference on the address object + * before assignment, the caller is NOT responsible for this. + * + * All functions return 0 on success or a negative error code. + * + * @subsection flags Flags to Character StringTranslations + * All functions converting a set of flags to a character string follow + * the same principles, therefore, the following information applies + * to all functions convertings flags to a character string and vice versa. + * + * @subsubsection flags2str Flags to Character String + * @code + * char *_flags2str(int flags, char *buf, size_t len) + * @endcode + * @arg flags Flags. + * @arg buf Destination buffer. + * @arg len Buffer length. + * + * Converts the specified flags to a character string separated by + * commas and stores it in the specified destination buffer. + * + * @return The destination buffer + * + * @subsubsection str2flags Character String to Flags + * @code + * int _str2flags(const char *name) + * @endcode + * @arg name Name of flag. + * + * Converts the provided character string specifying a flag + * to the corresponding numeric value. + * + * @return Link flag or a negative value if none was found. + * + * @subsubsection type2str Type to Character String + * @code + * char *_2str(int type, char *buf, size_t len) + * @endcode + * @arg type Type as numeric value + * @arg buf Destination buffer. + * @arg len Buffer length. + * + * Converts an identifier (type) to a character string and stores + * it in the specified destination buffer. + * + * @return The destination buffer or the type encoded in hexidecimal + * form if the identifier is unknown. + * + * @subsubsection str2type Character String to Type + * @code + * int _str2(const char *name) + * @endcode + * @arg name Name of identifier (type). + * + * Converts the provided character string specifying a identifier + * to the corresponding numeric value. + * + * @return Identifier as numeric value or a negative value if none was found. + */ diff --git a/lib/family.c b/lib/family.c new file mode 100644 index 0000000..ba1d65f --- /dev/null +++ b/lib/family.c @@ -0,0 +1,62 @@ +/* + * lib/family.c Netlink Family + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @defgroup nlfam Netlink Families + * @brief + * + * @{ + */ + +#include +#include +#include + +/** + * @name Netlink Family Name Translation + * @{ + */ + +static struct trans_tbl nlfamilies[] = { + __ADD(NETLINK_ROUTE,route) + __ADD(NETLINK_USERSOCK,usersock) + __ADD(NETLINK_FIREWALL,firewall) + __ADD(NETLINK_INET_DIAG,inetdiag) + __ADD(NETLINK_NFLOG,nflog) + __ADD(NETLINK_XFRM,xfrm) + __ADD(NETLINK_SELINUX,selinux) + __ADD(NETLINK_ISCSI,iscsi) + __ADD(NETLINK_AUDIT,audit) + __ADD(NETLINK_FIB_LOOKUP,fib_lookup) + __ADD(NETLINK_CONNECTOR,connector) + __ADD(NETLINK_NETFILTER,netfilter) + __ADD(NETLINK_IP6_FW,ip6_fw) + __ADD(NETLINK_DNRTMSG,dnrtmsg) + __ADD(NETLINK_KOBJECT_UEVENT,kobject_uevent) + __ADD(NETLINK_GENERIC,generic) + __ADD(NETLINK_SCSITRANSPORT,scsitransport) + __ADD(NETLINK_ECRYPTFS,ecryptfs) +}; + +char * nl_nlfamily2str(int family, char *buf, size_t size) +{ + return __type2str(family, buf, size, nlfamilies, + ARRAY_SIZE(nlfamilies)); +} + +int nl_str2nlfamily(const char *name) +{ + return __str2type(name, nlfamilies, ARRAY_SIZE(nlfamilies)); +} + +/** @} */ + +/** @} */ diff --git a/lib/fib_lookup/lookup.c b/lib/fib_lookup/lookup.c new file mode 100644 index 0000000..2b0070d --- /dev/null +++ b/lib/fib_lookup/lookup.c @@ -0,0 +1,353 @@ +/* + * lib/fib_lookup/lookup.c FIB Lookup + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup nlfam + * @defgroup fib_lookup FIB Lookup + * @brief + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +static struct nl_cache_ops fib_lookup_ops; +static struct nl_object_ops result_obj_ops; + +/* not exported so far */ +struct fib_result_nl { + uint32_t fl_addr; /* To be looked up*/ + uint32_t fl_fwmark; + unsigned char fl_tos; + unsigned char fl_scope; + unsigned char tb_id_in; + + unsigned char tb_id; /* Results */ + unsigned char prefixlen; + unsigned char nh_sel; + unsigned char type; + unsigned char scope; + int err; +}; +/** @endcond */ + +static void result_free_data(struct nl_object *obj) +{ + struct flnl_result *res = nl_object_priv(obj); + + if (res && res->fr_req) + nl_object_put(OBJ_CAST(res->fr_req)); +} + +static int result_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct flnl_result *dst = nl_object_priv(_dst); + struct flnl_result *src = nl_object_priv(_src); + + if (src->fr_req) + if (!(dst->fr_req = (struct flnl_request *) + nl_object_clone(OBJ_CAST(src->fr_req)))) + return nl_get_errno(); + + return 0; +} + +static int result_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + struct flnl_result *res; + struct fib_result_nl *fr; + struct nl_parser_param *pp = arg; + struct nl_addr *addr; + int err = -EINVAL; + + res = flnl_result_alloc(); + if (!res) + goto errout; + + res->ce_msgtype = n->nlmsg_type; + + res->fr_req = flnl_request_alloc(); + if (!res->fr_req) + goto errout; + + fr = nlmsg_data(n); + addr = nl_addr_build(AF_INET, &fr->fl_addr, 4); + if (!addr) + goto errout; + err = flnl_request_set_addr(res->fr_req, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + + flnl_request_set_fwmark(res->fr_req, fr->fl_fwmark); + flnl_request_set_tos(res->fr_req, fr->fl_tos); + flnl_request_set_scope(res->fr_req, fr->fl_scope); + flnl_request_set_table(res->fr_req, fr->tb_id_in); + + res->fr_table_id = fr->tb_id; + res->fr_prefixlen = fr->prefixlen; + res->fr_nh_sel = fr->nh_sel; + res->fr_type = fr->type; + res->fr_scope = fr->scope; + res->fr_error = fr->err; + + err = pp->pp_cb((struct nl_object *) res, pp); + if (err < 0) + goto errout; + + /* REAL HACK, fib_lookup doesn't support ACK nor does it + * send a DONE message, enforce end of message stream + * after just the first message */ + return NL_STOP; + +errout: + flnl_result_put(res); + return err; +} + +static int result_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +{ + struct flnl_result *res = (struct flnl_result *) obj; + char buf[128]; + int line = 1; + + dp_dump(p, "table %s prefixlen %u next-hop-selector %u\n", + rtnl_route_table2str(res->fr_table_id, buf, sizeof(buf)), + res->fr_prefixlen, res->fr_nh_sel); + dp_dump_line(p, line++, "type %s ", + nl_rtntype2str(res->fr_type, buf, sizeof(buf))); + dp_dump(p, "scope %s error %s (%d)\n", + rtnl_scope2str(res->fr_scope, buf, sizeof(buf)), + strerror(-res->fr_error), res->fr_error); + + return line; +} + +static int result_dump_full(struct nl_object *obj, struct nl_dump_params *p) +{ + return result_dump_brief(obj, p); +} + +static int result_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) +{ + return 0; +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct flnl_result *flnl_result_alloc(void) +{ + return (struct flnl_result *) nl_object_alloc(&result_obj_ops); +} + +void flnl_result_put(struct flnl_result *res) +{ + nl_object_put((struct nl_object *) res); +} + +/** @} */ + +/** + * @name Cache Management + * @{ + */ + +/** + * Allocate lookup result cache. + * + * Allocates a new lookup result cache and initializes it properly. + * + * @note Free the memory after usage using nl_cache_destroy_and_free(). + * @return Newly allocated cache or NULL if an error occured. + */ +struct nl_cache *flnl_result_alloc_cache(void) +{ + return nl_cache_alloc(&fib_lookup_ops); +} + +/** @} */ + +/** + * @name Lookup + * @{ + */ + +/** + * Builds a netlink request message to do a lookup + * @arg req Requested match. + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting a change of link attributes. + * The netlink message header isn't fully equipped with all relevant + * fields and must be sent out via nl_send_auto_complete() or + * supplemented as needed. + * \a old must point to a link currently configured in the kernel + * and \a tmpl must contain the attributes to be changed set via + * \c rtnl_link_set_* functions. + * + * @return New netlink message + * @note Not all attributes can be changed, see + * \ref link_changeable "Changeable Attributes" for more details. + */ +struct nl_msg *flnl_lookup_build_request(struct flnl_request *req, int flags) +{ + struct nl_msg *msg; + struct nl_addr *addr; + uint64_t fwmark; + int tos, scope, table; + struct fib_result_nl fr = {0}; + + fwmark = flnl_request_get_fwmark(req); + tos = flnl_request_get_tos(req); + scope = flnl_request_get_scope(req); + table = flnl_request_get_table(req); + + fr.fl_fwmark = fwmark != UINT_LEAST64_MAX ? fwmark : 0; + fr.fl_tos = tos >= 0 ? tos : 0; + fr.fl_scope = scope >= 0 ? scope : RT_SCOPE_UNIVERSE; + fr.tb_id_in = table >= 0 ? table : RT_TABLE_UNSPEC; + + addr = flnl_request_get_addr(req); + if (!addr) { + nl_error(EINVAL, "Request must specify the address"); + return NULL; + } + + fr.fl_addr = *(uint32_t *) nl_addr_get_binary_addr(addr); + + msg = nlmsg_alloc_simple(0, flags); + if (!msg) + goto errout; + + if (nlmsg_append(msg, &fr, sizeof(fr), NLMSG_ALIGNTO) < 0) + goto errout; + + return msg; + +errout: + nlmsg_free(msg); + return NULL; +} + +/** + * Perform FIB Lookup + * @arg handle Netlink handle. + * @arg req Lookup request object. + * @arg cache Cache for result. + * + * Builds a netlink message to request a FIB lookup, waits for the + * reply and adds the result to the specified cache. + * + * @return 0 on success or a negative error code. + */ +int flnl_lookup(struct nl_handle *handle, struct flnl_request *req, + struct nl_cache *cache) +{ + struct nl_msg *msg; + int err; + + msg = flnl_lookup_build_request(req, 0); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_cache_pickup(handle, cache); +} + +/** @} */ + +/** + * @name Attribute Access + * @{ + */ + +int flnl_result_get_table_id(struct flnl_result *res) +{ + return res->fr_table_id; +} + +int flnl_result_get_prefixlen(struct flnl_result *res) +{ + return res->fr_prefixlen; +} + +int flnl_result_get_nexthop_sel(struct flnl_result *res) +{ + return res->fr_nh_sel; +} + +int flnl_result_get_type(struct flnl_result *res) +{ + return res->fr_type; +} + +int flnl_result_get_scope(struct flnl_result *res) +{ + return res->fr_scope; +} + +int flnl_result_get_error(struct flnl_result *res) +{ + return res->fr_error; +} + +/** @} */ + +static struct nl_object_ops result_obj_ops = { + .oo_name = "fib_lookup/result", + .oo_size = sizeof(struct flnl_result), + .oo_free_data = result_free_data, + .oo_clone = result_clone, + .oo_dump[NL_DUMP_BRIEF] = result_dump_brief, + .oo_dump[NL_DUMP_FULL] = result_dump_full, + .oo_compare = result_compare, +}; + +static struct nl_cache_ops fib_lookup_ops = { + .co_name = "fib_lookup/fib_lookup", + .co_hdrsize = sizeof(struct fib_result_nl), + .co_msgtypes = { + { 0, NL_ACT_UNSPEC, "any" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_FIB_LOOKUP, + .co_msg_parser = result_msg_parser, + .co_obj_ops = &result_obj_ops, +}; + +static void __init fib_lookup_init(void) +{ + nl_cache_mngt_register(&fib_lookup_ops); +} + +static void __exit fib_lookup_exit(void) +{ + nl_cache_mngt_unregister(&fib_lookup_ops); +} + +/** @} */ diff --git a/lib/fib_lookup/request.c b/lib/fib_lookup/request.c new file mode 100644 index 0000000..8b00224 --- /dev/null +++ b/lib/fib_lookup/request.c @@ -0,0 +1,187 @@ +/* + * lib/fib_lookup/request.c FIB Lookup Request + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup fib_lookup + * @defgroup flreq Request + * @brief + * @{ + */ + +#include +#include +#include +#include +#include +#include + +static struct nl_object_ops request_obj_ops; + +/** @cond SKIP */ +#define REQUEST_ATTR_ADDR 0x01 +#define REQUEST_ATTR_FWMARK 0x02 +#define REQUEST_ATTR_TOS 0x04 +#define REQUEST_ATTR_SCOPE 0x08 +#define REQUEST_ATTR_TABLE 0x10 +/** @endcond */ + +static void request_free_data(struct nl_object *obj) +{ + struct flnl_request *req = REQUEST_CAST(obj); + + if (req) + nl_addr_put(req->lr_addr); +} + +static int request_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct flnl_request *dst = nl_object_priv(_dst); + struct flnl_request *src = nl_object_priv(_src); + + if (src->lr_addr) + if (!(dst->lr_addr = nl_addr_clone(src->lr_addr))) + goto errout; + + return 0; +errout: + return nl_get_errno(); +} + +static int request_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) +{ + struct flnl_request *a = (struct flnl_request *) _a; + struct flnl_request *b = (struct flnl_request *) _b; + int diff = 0; + +#define REQ_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, REQUEST_ATTR_##ATTR, a, b, EXPR) + + diff |= REQ_DIFF(FWMARK, a->lr_fwmark != b->lr_fwmark); + diff |= REQ_DIFF(TOS, a->lr_tos != b->lr_tos); + diff |= REQ_DIFF(SCOPE, a->lr_scope != b->lr_scope); + diff |= REQ_DIFF(TABLE, a->lr_table != b->lr_table); + diff |= REQ_DIFF(ADDR, nl_addr_cmp(a->lr_addr, b->lr_addr)); + +#undef REQ_DIFF + + return diff; +} + + +/** + * @name Lookup Request Creation/Deletion + * @{ + */ + +struct flnl_request *flnl_request_alloc(void) +{ + return REQUEST_CAST(nl_object_alloc(&request_obj_ops)); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void flnl_request_set_fwmark(struct flnl_request *req, uint64_t fwmark) +{ + req->lr_fwmark = fwmark; + req->ce_mask |= REQUEST_ATTR_FWMARK; +} + +uint64_t flnl_request_get_fwmark(struct flnl_request *req) +{ + if (req->ce_mask & REQUEST_ATTR_FWMARK) + return req->lr_fwmark; + else + return UINT_LEAST64_MAX; +} + +void flnl_request_set_tos(struct flnl_request *req, int tos) +{ + req->lr_tos = tos; + req->ce_mask |= REQUEST_ATTR_TOS; +} + +int flnl_request_get_tos(struct flnl_request *req) +{ + if (req->ce_mask & REQUEST_ATTR_TOS) + return req->lr_tos; + else + return -1; +} + +void flnl_request_set_scope(struct flnl_request *req, int scope) +{ + req->lr_scope = scope; + req->ce_mask |= REQUEST_ATTR_SCOPE; +} + +int flnl_request_get_scope(struct flnl_request *req) +{ + if (req->ce_mask & REQUEST_ATTR_SCOPE) + return req->lr_scope; + else + return -1; +} + +void flnl_request_set_table(struct flnl_request *req, int table) +{ + req->lr_table = table; + req->ce_mask |= REQUEST_ATTR_TABLE; +} + +int flnl_request_get_table(struct flnl_request *req) +{ + if (req->ce_mask & REQUEST_ATTR_TABLE) + return req->lr_table; + else + return -1; +} + +int flnl_request_set_addr(struct flnl_request *req, struct nl_addr *addr) +{ + if (addr->a_family != AF_INET) + return nl_error(EINVAL, "Address must be an IPv4 address"); + + if (req->lr_addr) + nl_addr_put(req->lr_addr); + + nl_addr_get(addr); + req->lr_addr = addr; + + req->ce_mask |= REQUEST_ATTR_ADDR; + + return 0; +} + +struct nl_addr *flnl_request_get_addr(struct flnl_request *req) +{ + if (req->ce_mask & REQUEST_ATTR_ADDR) + return req->lr_addr; + else + return NULL; +} + +/** @} */ + +static struct nl_object_ops request_obj_ops = { + .oo_name = "fib_lookup/request", + .oo_size = sizeof(struct flnl_request), + .oo_free_data = request_free_data, + .oo_clone = request_clone, + .oo_compare = request_compare, + .oo_id_attrs = ~0, +}; + +/** @} */ diff --git a/lib/genl/ctrl.c b/lib/genl/ctrl.c new file mode 100644 index 0000000..b4dc489 --- /dev/null +++ b/lib/genl/ctrl.c @@ -0,0 +1,319 @@ +/* + * lib/genl/ctrl.c Generic Netlink Controller + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup genl_mngt + * @defgroup ctrl Controller + * @brief + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define CTRL_VERSION 0x0001 + +static struct nl_cache_ops genl_ctrl_ops; +/** @endcond */ + +static int ctrl_request_update(struct nl_cache *c, struct nl_handle *h) +{ + return genl_send_simple(h, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, + CTRL_VERSION, NLM_F_DUMP); +} + +static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = { + [CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 }, + [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING, + .maxlen = GENL_NAMSIZ }, + [CTRL_ATTR_VERSION] = { .type = NLA_U32 }, + [CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 }, + [CTRL_ATTR_MAXATTR] = { .type = NLA_U32 }, + [CTRL_ATTR_OPS] = { .type = NLA_NESTED }, +}; + +static struct nla_policy family_op_policy[CTRL_ATTR_OP_MAX+1] = { + [CTRL_ATTR_OP_ID] = { .type = NLA_U32 }, + [CTRL_ATTR_OP_FLAGS] = { .type = NLA_U32 }, +}; + +static int ctrl_msg_parser(struct nl_cache_ops *ops, struct genl_cmd *cmd, + struct genl_info *info, void *arg) +{ + struct genl_family *family; + struct nl_parser_param *pp = arg; + int err; + + family = genl_family_alloc(); + if (family == NULL) { + err = nl_errno(ENOMEM); + goto errout; + } + + if (info->attrs[CTRL_ATTR_FAMILY_NAME] == NULL) { + err = nl_error(EINVAL, "Missing family name TLV"); + goto errout; + } + + if (info->attrs[CTRL_ATTR_FAMILY_ID] == NULL) { + err = nl_error(EINVAL, "Missing family id TLV"); + goto errout; + } + + family->ce_msgtype = info->nlh->nlmsg_type; + genl_family_set_id(family, + nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID])); + genl_family_set_name(family, + nla_get_string(info->attrs[CTRL_ATTR_FAMILY_NAME])); + + if (info->attrs[CTRL_ATTR_VERSION]) { + uint32_t version = nla_get_u32(info->attrs[CTRL_ATTR_VERSION]); + genl_family_set_version(family, version); + } + + if (info->attrs[CTRL_ATTR_HDRSIZE]) { + uint32_t hdrsize = nla_get_u32(info->attrs[CTRL_ATTR_HDRSIZE]); + genl_family_set_hdrsize(family, hdrsize); + } + + if (info->attrs[CTRL_ATTR_MAXATTR]) { + uint32_t maxattr = nla_get_u32(info->attrs[CTRL_ATTR_MAXATTR]); + genl_family_set_maxattr(family, maxattr); + } + + if (info->attrs[CTRL_ATTR_OPS]) { + struct nlattr *nla, *nla_ops; + int remaining; + + nla_ops = info->attrs[CTRL_ATTR_OPS]; + nla_for_each_nested(nla, nla_ops, remaining) { + struct nlattr *tb[CTRL_ATTR_OP_MAX+1]; + int flags = 0, id; + + err = nla_parse_nested(tb, CTRL_ATTR_OP_MAX, nla, + family_op_policy); + if (err < 0) + goto errout; + + if (tb[CTRL_ATTR_OP_ID] == NULL) { + err = nl_errno(EINVAL); + goto errout; + } + + id = nla_get_u32(tb[CTRL_ATTR_OP_ID]); + + if (tb[CTRL_ATTR_OP_FLAGS]) + flags = nla_get_u32(tb[CTRL_ATTR_OP_FLAGS]); + + err = genl_family_add_op(family, id, flags); + if (err < 0) + goto errout; + + } + } + + err = pp->pp_cb((struct nl_object *) family, pp); + if (err < 0) + goto errout; + + return P_ACCEPT; + +errout: + genl_family_put(family); + return err; +} + +/** + * @name Cache Management + * @{ + */ + +struct nl_cache *genl_ctrl_alloc_cache(struct nl_handle *handle) +{ + struct nl_cache * cache; + + cache = nl_cache_alloc(&genl_ctrl_ops); + if (cache == NULL) + return NULL; + + if (handle && nl_cache_refill(handle, cache) < 0) { + nl_cache_free(cache); + return NULL; + } + + return cache; +} + +/** + * Look up generic netlink family by id in the provided cache. + * @arg cache Generic netlink family cache. + * @arg id Family identifier. + * + * Searches through the cache looking for a registered family + * matching the specified identifier. The caller will own a + * reference on the returned object which needs to be given + * back after usage using genl_family_put(). + * + * @return Generic netlink family object or NULL if no match was found. + */ +struct genl_family *genl_ctrl_search(struct nl_cache *cache, int id) +{ + struct genl_family *fam; + + if (cache->c_ops != &genl_ctrl_ops) + BUG(); + + nl_list_for_each_entry(fam, &cache->c_items, ce_list) { + if (fam->gf_id == id) { + nl_object_get((struct nl_object *) fam); + return fam; + } + } + + return NULL; +} + +/** + * @name Resolver + * @{ + */ + +/** + * Look up generic netlink family by family name in the provided cache. + * @arg cache Generic netlink family cache. + * @arg name Family name. + * + * Searches through the cache looking for a registered family + * matching the specified name. The caller will own a reference + * on the returned object which needs to be given back after + * usage using genl_family_put(). + * + * @return Generic netlink family object or NULL if no match was found. + */ +struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, + const char *name) +{ + struct genl_family *fam; + + if (cache->c_ops != &genl_ctrl_ops) + BUG(); + + nl_list_for_each_entry(fam, &cache->c_items, ce_list) { + if (!strcmp(name, fam->gf_name)) { + nl_object_get((struct nl_object *) fam); + return fam; + } + } + + return NULL; +} + +/** @} */ + +/** + * Resolve generic netlink family name to its identifier + * @arg handle Netlink Handle + * @arg name Name of generic netlink family + * + * Resolves the generic netlink family name to its identifer and returns + * it. + * + * @return A positive identifier or a negative error code. + */ +int genl_ctrl_resolve(struct nl_handle *handle, const char *name) +{ + struct nl_cache *cache; + struct genl_family *family; + int err; + + cache = genl_ctrl_alloc_cache(handle); + if (cache == NULL) + return nl_get_errno(); + + family = genl_ctrl_search_by_name(cache, name); + if (family == NULL) { + err = nl_error(ENOENT, "Generic Netlink Family not found"); + goto errout; + } + + err = genl_family_get_id(family); + genl_family_put(family); +errout: + nl_cache_free(cache); + + return err; +} + +/** @} */ + +static struct genl_cmd genl_cmds[] = { + { + .c_id = CTRL_CMD_NEWFAMILY, + .c_name = "NEWFAMILY" , + .c_maxattr = CTRL_ATTR_MAX, + .c_attr_policy = ctrl_policy, + .c_msg_parser = ctrl_msg_parser, + }, + { + .c_id = CTRL_CMD_DELFAMILY, + .c_name = "DELFAMILY" , + }, + { + .c_id = CTRL_CMD_GETFAMILY, + .c_name = "GETFAMILY" , + }, + { + .c_id = CTRL_CMD_NEWOPS, + .c_name = "NEWOPS" , + }, + { + .c_id = CTRL_CMD_DELOPS, + .c_name = "DELOPS" , + }, +}; + +static struct genl_ops genl_ops = { + .o_cmds = genl_cmds, + .o_ncmds = ARRAY_SIZE(genl_cmds), +}; + +/** @cond SKIP */ +extern struct nl_object_ops genl_family_ops; +/** @endcond */ + +static struct nl_cache_ops genl_ctrl_ops = { + .co_name = "genl/family", + .co_hdrsize = GENL_HDRSIZE(0), + .co_msgtypes = GENL_FAMILY(GENL_ID_CTRL, "nlctrl"), + .co_genl = &genl_ops, + .co_protocol = NETLINK_GENERIC, + .co_request_update = ctrl_request_update, + .co_obj_ops = &genl_family_ops, +}; + +static void __init ctrl_init(void) +{ + genl_register(&genl_ctrl_ops); +} + +static void __exit ctrl_exit(void) +{ + genl_unregister(&genl_ctrl_ops); +} + +/** @} */ diff --git a/lib/genl/family.c b/lib/genl/family.c new file mode 100644 index 0000000..e05b52c --- /dev/null +++ b/lib/genl/family.c @@ -0,0 +1,285 @@ +/* + * lib/genl/family.c Generic Netlink Family + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup genl + * @defgroup genl_family Generic Netlink Family + * @brief + * + * @{ + */ + +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define FAMILY_ATTR_ID 0x01 +#define FAMILY_ATTR_NAME 0x02 +#define FAMILY_ATTR_VERSION 0x04 +#define FAMILY_ATTR_HDRSIZE 0x08 +#define FAMILY_ATTR_MAXATTR 0x10 +#define FAMILY_ATTR_OPS 0x20 + +struct nl_object_ops genl_family_ops; +/** @endcond */ + +static void family_constructor(struct nl_object *c) +{ + struct genl_family *family = (struct genl_family *) c; + + nl_init_list_head(&family->gf_ops); +} + +static void family_free_data(struct nl_object *c) +{ + struct genl_family *family = (struct genl_family *) c; + struct genl_family_op *ops, *tmp; + + if (family == NULL) + return; + + nl_list_for_each_entry_safe(ops, tmp, &family->gf_ops, o_list) { + nl_list_del(&ops->o_list); + free(ops); + } +} + +static int family_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct genl_family *dst = nl_object_priv(_dst); + struct genl_family *src = nl_object_priv(_src); + struct genl_family_op *ops; + int err; + + nl_list_for_each_entry(ops, &src->gf_ops, o_list) { + err = genl_family_add_op(dst, ops->o_id, ops->o_flags); + if (err < 0) + return err; + } + + return 0; +} + +static int family_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +{ + struct genl_family *family = (struct genl_family *) obj; + + dp_dump(p, "0x%04x %s version %u\n", + family->gf_id, family->gf_name, family->gf_version); + + return 1; +} + +static struct trans_tbl ops_flags[] = { + __ADD(GENL_ADMIN_PERM, admin-perm) + __ADD(GENL_CMD_CAP_DO, has-doit) + __ADD(GENL_CMD_CAP_DUMP, has-dump) + __ADD(GENL_CMD_CAP_HASPOL, has-policy) +}; + +static char *ops_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, ops_flags, ARRAY_SIZE(ops_flags)); +} + +static int family_dump_full(struct nl_object *obj, struct nl_dump_params *p) +{ + struct genl_family *family = (struct genl_family *) obj; + int line; + + line = family_dump_brief(obj, p); + dp_dump_line(p, line++, " hdrsize %u maxattr %u\n", + family->gf_hdrsize, family->gf_maxattr); + + if (family->ce_mask & FAMILY_ATTR_OPS) { + struct genl_family_op *op; + char buf[64]; + + nl_list_for_each_entry(op, &family->gf_ops, o_list) { + ops_flags2str(op->o_flags, buf, sizeof(buf)); + + genl_op2name(family->gf_id, op->o_id, buf, sizeof(buf)); + + dp_dump_line(p, line++, " op %s (0x%02x)", + buf, op->o_id); + + if (op->o_flags) + dp_dump(p, " <%s>", + ops_flags2str(op->o_flags, buf, + sizeof(buf))); + + dp_dump(p, "\n"); + } + } + + return line; +} + +static int family_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + return family_dump_full(obj, p); +} + +static int family_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) +{ + struct genl_family *a = (struct genl_family *) _a; + struct genl_family *b = (struct genl_family *) _b; + int diff = 0; + +#define FAM_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, FAMILY_ATTR_##ATTR, a, b, EXPR) + + diff |= FAM_DIFF(ID, a->gf_id != b->gf_id); + diff |= FAM_DIFF(VERSION, a->gf_version != b->gf_version); + diff |= FAM_DIFF(HDRSIZE, a->gf_hdrsize != b->gf_hdrsize); + diff |= FAM_DIFF(MAXATTR, a->gf_maxattr != b->gf_maxattr); + diff |= FAM_DIFF(NAME, strcmp(a->gf_name, b->gf_name)); + +#undef FAM_DIFF + + return diff; +} + + +/** + * @name Family Object + * @{ + */ + +struct genl_family *genl_family_alloc(void) +{ + return (struct genl_family *) nl_object_alloc(&genl_family_ops); +} + +void genl_family_put(struct genl_family *family) +{ + nl_object_put((struct nl_object *) family); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +unsigned int genl_family_get_id(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_ID) + return family->gf_id; + else + return GENL_ID_GENERATE; +} + +void genl_family_set_id(struct genl_family *family, unsigned int id) +{ + family->gf_id = id; + family->ce_mask |= FAMILY_ATTR_ID; +} + +char *genl_family_get_name(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_NAME) + return family->gf_name; + else + return NULL; +} + +void genl_family_set_name(struct genl_family *family, const char *name) +{ + strncpy(family->gf_name, name, GENL_NAMSIZ-1); + family->ce_mask |= FAMILY_ATTR_NAME; +} + +uint8_t genl_family_get_version(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_VERSION) + return family->gf_version; + else + return 0; +} + +void genl_family_set_version(struct genl_family *family, uint8_t version) +{ + family->gf_version = version; + family->ce_mask |= FAMILY_ATTR_VERSION; +} + +uint32_t genl_family_get_hdrsize(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_HDRSIZE) + return family->gf_hdrsize; + else + return 0; +} + +void genl_family_set_hdrsize(struct genl_family *family, uint32_t hdrsize) +{ + family->gf_hdrsize = hdrsize; + family->ce_mask |= FAMILY_ATTR_HDRSIZE; +} + +uint32_t genl_family_get_maxattr(struct genl_family *family) +{ + if (family->ce_mask & FAMILY_ATTR_MAXATTR) + return family->gf_maxattr; + else + return family->gf_maxattr; +} + +void genl_family_set_maxattr(struct genl_family *family, uint32_t maxattr) +{ + family->gf_maxattr = maxattr; + family->ce_mask |= FAMILY_ATTR_MAXATTR; +} + +int genl_family_add_op(struct genl_family *family, int id, int flags) +{ + struct genl_family_op *op; + + op = calloc(1, sizeof(*op)); + if (op == NULL) + return nl_errno(ENOMEM); + + op->o_id = id; + op->o_flags = flags; + + nl_list_add_tail(&op->o_list, &family->gf_ops); + family->ce_mask |= FAMILY_ATTR_OPS; + + return 0; +} + +/** @} */ + +/** @cond SKIP */ +struct nl_object_ops genl_family_ops = { + .oo_name = "genl/family", + .oo_size = sizeof(struct genl_family), + .oo_constructor = family_constructor, + .oo_free_data = family_free_data, + .oo_clone = family_clone, + .oo_dump[NL_DUMP_BRIEF] = family_dump_brief, + .oo_dump[NL_DUMP_FULL] = family_dump_full, + .oo_dump[NL_DUMP_STATS] = family_dump_stats, +#if 0 + .oo_dump[NL_DUMP_XML] = addr_dump_xml, + .oo_dump[NL_DUMP_ENV] = addr_dump_env, +#endif + .oo_compare = family_compare, + .oo_id_attrs = FAMILY_ATTR_ID, +}; +/** @endcond */ + +/** @} */ diff --git a/lib/genl/genl.c b/lib/genl/genl.c new file mode 100644 index 0000000..c600579 --- /dev/null +++ b/lib/genl/genl.c @@ -0,0 +1,210 @@ +/* + * lib/genl/genl.c Generic Netlink + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup nlfam + * @defgroup genl Generic Netlink + * + * @par Message Format + * @code + * <------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) ---> + * +----------------------------+- - -+- - - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | struct nlmsghdr | | | | + * +----------------------------+- - -+- - - - - - - - - - -+- - -+ + * @endcode + * @code + * <-------- GENL_HDRLEN -------> <--- hdrlen --> + * <------- genlmsg_len(ghdr) ------> + * +------------------------+- - -+---------------+- - -+------------+ + * | Generic Netlink Header | Pad | Family Header | Pad | Attributes | + * | struct genlmsghdr | | | | | + * +------------------------+- - -+---------------+- - -+------------+ + * genlmsg_data(ghdr)--------------^ ^ + * genlmsg_attrdata(ghdr, hdrlen)------------------------- + * @endcode + * + * @par 1) Creating a new generic netlink message + * @code + * struct nl_msg *msg; + * struct myhdr { + * int a; + * int b; + * } *hdr; + * + * // Create a new empty netlink message + * msg = nlmsg_alloc(); + * + * // Append the netlink and generic netlink message header, this + * // operation also reserves room for the family specific header. + * hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, sizeof(hdr), + * NLM_F_ECHO, MYOP, VERSION); + * + * // Fill out your own family specific header. + * hdr->a = 1; + * hdr->b = 2; + * + * // Append the optional attributes. + * nla_put_u32(msg, 1, 0x10); + * + * // Message is ready to be sent. + * nl_send_auto_complete(nl_handle, msg); + * + * // All done? Free the message. + * nlmsg_free(msg); + * @endcode + * + * @par 2) Sending of trivial messages + * @code + * // For trivial messages not requiring any family specific header or + * // attributes, genl_send_simple() may be used to send messages directly. + * genl_send_simple(nl_handle, family, MY_SIMPLE_CMD, VERSION, 0); + * @endcode + * @{ + */ + +#include +#include +#include +#include + +/** + * @name Socket Creating + * @{ + */ + +int genl_connect(struct nl_handle *handle) +{ + return nl_connect(handle, NETLINK_GENERIC); +} + +/** @} */ + +/** + * @name Sending + * @{ + */ + +/** + * Send trivial generic netlink message + * @arg handle Netlink handle. + * @arg family Generic netlink family + * @arg cmd Command + * @arg version Version + * @arg flags Additional netlink message flags. + * + * Fills out a routing netlink request message and sends it out + * using nl_send_simple(). + * + * @return 0 on success or a negative error code. + */ +int genl_send_simple(struct nl_handle *handle, int family, int cmd, + int version, int flags) +{ + struct genlmsghdr hdr = { + .cmd = cmd, + .version = version, + }; + + return nl_send_simple(handle, family, flags, &hdr, sizeof(hdr)); +} + +/** @} */ + + +/** + * @name Message Parsing + * @{ + */ + +/** + * Get head of message payload + * @arg gnlh genetlink messsage header + */ +void *genlmsg_data(const struct genlmsghdr *gnlh) +{ + return ((unsigned char *) gnlh + GENL_HDRLEN); +} + +/** + * Get lenght of message payload + * @arg gnlh genetlink message header + */ +int genlmsg_len(const struct genlmsghdr *gnlh) +{ + struct nlmsghdr *nlh = (struct nlmsghdr *)((unsigned char *)gnlh - + NLMSG_HDRLEN); + return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN); +} + +/** + * Get head of attribute data + * @arg gnlh generic netlink message header + * @arg hdrlen length of family specific header + */ +struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_data(gnlh) + NLMSG_ALIGN(hdrlen); +} + +/** + * Get length of attribute data + * @arg gnlh generic netlink message header + * @arg hdrlen length of family specific header + */ +int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen) +{ + return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen); +} + +/** @} */ + +/** + * @name Message Building + * @{ + */ + +/** + * Add generic netlink header to netlink message + * @arg msg netlink message + * @arg pid netlink process id or NL_AUTO_PID + * @arg seq sequence number of message or NL_AUTO_SEQ + * @arg family generic netlink family + * @arg hdrlen length of user specific header + * @arg flags message flags + * @arg cmd generic netlink command + * @arg version protocol version + * + * Returns pointer to user specific header. + */ +void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family, + int hdrlen, int flags, uint8_t cmd, uint8_t version) +{ + struct nlmsghdr *nlh; + struct genlmsghdr hdr = { + .cmd = cmd, + .version = version, + }; + + nlh = nlmsg_put(msg, pid, seq, family, GENL_HDRLEN + hdrlen, flags); + if (nlh == NULL) + return NULL; + + memcpy(nlmsg_data(nlh), &hdr, sizeof(hdr)); + NL_DBG(2, "msg %p: Added generic netlink header cmd=%d version=%d\n", + msg, cmd, version); + + return nlmsg_data(nlh) + GENL_HDRLEN; +} + +/** @} */ + +/** @} */ diff --git a/lib/genl/mngt.c b/lib/genl/mngt.c new file mode 100644 index 0000000..846cd7e --- /dev/null +++ b/lib/genl/mngt.c @@ -0,0 +1,285 @@ +/* + * lib/genl/mngt.c Generic Netlink Management + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup genl + * @defgroup genl_mngt Management + * + * @par 1) Registering a generic netlink module + * @code + * #include + * + * // First step is to define all the commands being used in + * // particular generic netlink family. The ID and name are + * // mandatory to be filled out. A callback function and + * // most the attribute policy that comes with it must be + * // defined for commands expected to be issued towards + * // userspace. + * static struct genl_cmd foo_cmds[] = { + * { + * .c_id = FOO_CMD_NEW, + * .c_name = "NEWFOO" , + * .c_maxattr = FOO_ATTR_MAX, + * .c_attr_policy = foo_policy, + * .c_msg_parser = foo_msg_parser, + * }, + * { + * .c_id = FOO_CMD_DEL, + * .c_name = "DELFOO" , + * }, + * }; + * + * // The list of commands must then be integrated into a + * // struct genl_ops serving as handle for this particular + * // family. + * static struct genl_ops my_genl_ops = { + * .o_cmds = foo_cmds, + * .o_ncmds = ARRAY_SIZE(foo_cmds), + * }; + * + * // Using the above struct genl_ops an arbitary number of + * // cache handles can be associated to it. + * // + * // The macro GENL_HDRSIZE() must be used to specify the + * // length of the header to automatically take headers on + * // generic layers into account. + * // + * // The macro GENL_FAMILY() is used to represent the generic + * // netlink family id. + * static struct nl_cache_ops genl_foo_ops = { + * .co_name = "genl/foo", + * .co_hdrsize = GENL_HDRSIZE(sizeof(struct my_hdr)), + * .co_msgtypes = GENL_FAMILY(GENL_ID_GENERATE, "foo"), + * .co_genl = &my_genl_ops, + * .co_protocol = NETLINK_GENERIC, + * .co_request_update = foo_request_update, + * .co_obj_ops = &genl_foo_ops, + * }; + * + * // Finally each cache handle for a generic netlink family + * // must be registered using genl_register(). + * static void __init foo_init(void) + * { + * genl_register(&genl_foo_ops); + * } + * + * // ... respectively unregsted again. + * static void __exit foo_exit(void) + * { + * genl_unregister(&genl_foo_ops); + * } + * @endcode + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +static NL_LIST_HEAD(genl_ops_list); + +static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, void *arg) +{ + int i, err; + struct genlmsghdr *ghdr; + struct genl_cmd *cmd; + + ghdr = nlmsg_data(nlh); + + if (ops->co_genl == NULL) + BUG(); + + for (i = 0; i < ops->co_genl->o_ncmds; i++) { + cmd = &ops->co_genl->o_cmds[i]; + if (cmd->c_id == ghdr->cmd) + goto found; + } + + err = nl_errno(ENOENT); + goto errout; + +found: + if (cmd->c_msg_parser == NULL) + err = nl_error(EOPNOTSUPP, "No message parser found."); + else { + struct nlattr *tb[cmd->c_maxattr + 1]; + struct genl_info info = { + .who = who, + .nlh = nlh, + .genlhdr = ghdr, + .userhdr = genlmsg_data(ghdr), + .attrs = tb, + }; + + err = nlmsg_parse(nlh, ops->co_hdrsize, tb, cmd->c_maxattr, + cmd->c_attr_policy); + if (err < 0) + goto errout; + + err = cmd->c_msg_parser(ops, cmd, &info, arg); + } +errout: + return err; + +} + +char *genl_op2name(int family, int op, char *buf, size_t len) +{ + struct genl_ops *ops; + int i; + + nl_list_for_each_entry(ops, &genl_ops_list, o_list) { + if (ops->o_family == family) { + for (i = 0; i < ops->o_ncmds; i++) { + struct genl_cmd *cmd; + cmd = &ops->o_cmds[i]; + + if (cmd->c_id == op) { + strncpy(buf, cmd->c_name, len - 1); + return buf; + } + } + } + } + + strncpy(buf, "unknown", len - 1); + return NULL; +} + + +/** + * @name Register/Unregister + * @{ + */ + +/** + * Register generic netlink operations + * @arg ops cache operations + */ +int genl_register(struct nl_cache_ops *ops) +{ + int err; + + if (ops->co_protocol != NETLINK_GENERIC) { + err = nl_error(EINVAL, "cache operations not for protocol " \ + "NETLINK_GENERIC (protocol=%s)", + ops->co_protocol); + goto errout; + } + + if (ops->co_hdrsize < GENL_HDRSIZE(0)) { + err = nl_error(EINVAL, "co_hdrsize too short, probably " \ + "not including genlmsghdr, minsize=%d", + GENL_HDRSIZE(0)); + goto errout; + } + + if (ops->co_genl == NULL) { + err = nl_error(EINVAL, "co_genl is NULL, must provide " \ + "valid genl operations"); + goto errout; + } + + ops->co_genl->o_cache_ops = ops; + ops->co_genl->o_name = ops->co_msgtypes[0].mt_name; + ops->co_genl->o_family = ops->co_msgtypes[0].mt_id; + ops->co_msg_parser = genl_msg_parser; + + /* FIXME: check for dup */ + + nl_list_add_tail(&ops->co_genl->o_list, &genl_ops_list); + + err = nl_cache_mngt_register(ops); +errout: + return err; +} + +/** + * Unregister generic netlink operations + * @arg ops cache operations + */ +void genl_unregister(struct nl_cache_ops *ops) +{ + nl_cache_mngt_unregister(ops); + nl_list_del(&ops->co_genl->o_list); +} + +/** @} */ + +/** + * @name Resolving ID/Name + * @{ + */ + +static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops) +{ + struct genl_family *family; + + family = genl_ctrl_search_by_name(ctrl, ops->o_name); + if (family != NULL) { + ops->o_id = genl_family_get_id(family); + genl_family_put(family); + + return 0; + } + + return nl_error(ENOENT, "Unable to find generic netlink family \"%s\"", + ops->o_name); +} + +int genl_ops_resolve(struct nl_handle *handle, struct genl_ops *ops) +{ + struct nl_cache *ctrl; + int err; + + ctrl = genl_ctrl_alloc_cache(handle); + if (ctrl == NULL) { + err = nl_get_errno(); + goto errout; + } + + err = __genl_ops_resolve(ctrl, ops); + + nl_cache_free(ctrl); +errout: + return err; +} + +int genl_mngt_resolve(struct nl_handle *handle) +{ + struct nl_cache *ctrl; + struct genl_ops *ops; + int err = 0; + + ctrl = genl_ctrl_alloc_cache(handle); + if (ctrl == NULL) { + err = nl_get_errno(); + goto errout; + } + + nl_list_for_each_entry(ops, &genl_ops_list, o_list) { + err = __genl_ops_resolve(ctrl, ops); + } + + nl_cache_free(ctrl); +errout: + return err; +} + +/** @} */ + + +/** @} */ diff --git a/lib/handlers.c b/lib/handlers.c new file mode 100644 index 0000000..1797e4f --- /dev/null +++ b/lib/handlers.c @@ -0,0 +1,447 @@ +/* + * lib/handlers.c default netlink message handlers + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup nl + * @defgroup cb Callbacks/Customization + * @brief + * + * Callbacks and overwriting capabilities are provided to take influence + * in various control flows inside the library. All callbacks are packed + * together in struct nl_cb which is then attached to a netlink socket or + * passed on to the respective functions directly. + * + * Callbacks can control the flow of the underlying layer by returning + * the appropriate error codes: + * @code + * Action ID | Description + * -----------------+------------------------------------------------------- + * NL_OK | Proceed with whatever comes next. + * NL_SKIP | Skip message currently being processed and continue + * | with next message. + * NL_STOP | Stop parsing and discard all remaining messages in + * | this set of messages. + * @endcode + * + * All callbacks are optional and a default action is performed if no + * application specific implementation is provided: + * + * @code + * Callback ID | Default Return Value + * ------------------+---------------------- + * NL_CB_VALID | NL_OK + * NL_CB_FINISH | NL_STOP + * NL_CB_OVERRUN | NL_STOP + * NL_CB_SKIPPED | NL_SKIP + * NL_CB_ACK | NL_STOP + * NL_CB_MSG_IN | NL_OK + * NL_CB_MSG_OUT | NL_OK + * NL_CB_INVALID | NL_STOP + * NL_CB_SEQ_CHECK | NL_OK + * NL_CB_SEND_ACK | NL_OK + * | + * Error Callback | NL_STOP + * @endcode + * + * In order to simplify typical usages of the library, different sets of + * default callback implementations exist: + * @code + * NL_CB_DEFAULT: No additional actions + * NL_CB_VERBOSE: Automatically print warning and error messages to a file + * descriptor as appropriate. This is useful for CLI based + * applications. + * NL_CB_DEBUG: Print informal debugging information for each message + * received. This will result in every message beint sent or + * received to be printed to the screen in a decoded, + * human-readable format. + * @endcode + * + * @par 1) Setting up a callback set + * @code + * // Allocate a callback set and initialize it to the verbose default set + * struct nl_cb *cb = nl_cb_alloc(NL_CB_VERBOSE); + * + * // Modify the set to call my_func() for all valid messages + * nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL); + * + * // Set the error message handler to the verbose default implementation + * // and direct it to print all errors to the given file descriptor. + * FILE *file = fopen(...); + * nl_cb_err(cb, NL_CB_VERBOSE, NULL, file); + * @endcode + * @{ + */ + +#include +#include +#include +#include +#include + +static void print_header_content(FILE *ofd, struct nlmsghdr *n) +{ + char flags[128]; + char type[32]; + + fprintf(ofd, "type=%s length=%u flags=<%s> sequence-nr=%u pid=%u", + nl_nlmsgtype2str(n->nlmsg_type, type, sizeof(type)), + n->nlmsg_len, nl_nlmsg_flags2str(n->nlmsg_flags, flags, + sizeof(flags)), n->nlmsg_seq, n->nlmsg_pid); +} + +static int nl_valid_handler_verbose(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stdout; + + fprintf(ofd, "-- Warning: unhandled valid message: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_OK; +} + +static int nl_invalid_handler_verbose(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Error: Invalid message: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_STOP; +} + +static int nl_overrun_handler_verbose(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Error: Netlink Overrun: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_STOP; +} + +static int nl_error_handler_verbose(struct sockaddr_nl *who, + struct nlmsgerr *e, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Error received: %s\n-- Original message: ", + strerror(-e->error)); + print_header_content(ofd, &e->msg); + fprintf(ofd, "\n"); + + return e->error; +} + +static int nl_valid_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: Unhandled Valid message: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_OK; +} + +static int nl_finish_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: End of multipart message block: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_STOP; +} + +static int nl_msg_in_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: Received Message:\n"); + nl_msg_dump(msg, ofd); + + return NL_OK; +} + +static int nl_msg_out_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: Sent Message:\n"); + nl_msg_dump(msg, ofd); + + return NL_OK; +} + +static int nl_skipped_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: Skipped message: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_SKIP; +} + +static int nl_ack_handler_debug(struct nl_msg *msg, void *arg) +{ + FILE *ofd = arg ? arg : stderr; + + fprintf(ofd, "-- Debug: ACK: "); + print_header_content(ofd, nlmsg_hdr(msg)); + fprintf(ofd, "\n"); + + return NL_STOP; +} + +static nl_recvmsg_msg_cb_t cb_def[NL_CB_TYPE_MAX+1][NL_CB_KIND_MAX+1] = { + [NL_CB_VALID] = { + [NL_CB_VERBOSE] = nl_valid_handler_verbose, + [NL_CB_DEBUG] = nl_valid_handler_debug, + }, + [NL_CB_FINISH] = { + [NL_CB_DEBUG] = nl_finish_handler_debug, + }, + [NL_CB_INVALID] = { + [NL_CB_VERBOSE] = nl_invalid_handler_verbose, + [NL_CB_DEBUG] = nl_invalid_handler_verbose, + }, + [NL_CB_MSG_IN] = { + [NL_CB_DEBUG] = nl_msg_in_handler_debug, + }, + [NL_CB_MSG_OUT] = { + [NL_CB_DEBUG] = nl_msg_out_handler_debug, + }, + [NL_CB_OVERRUN] = { + [NL_CB_VERBOSE] = nl_overrun_handler_verbose, + [NL_CB_DEBUG] = nl_overrun_handler_verbose, + }, + [NL_CB_SKIPPED] = { + [NL_CB_DEBUG] = nl_skipped_handler_debug, + }, + [NL_CB_ACK] = { + [NL_CB_DEBUG] = nl_ack_handler_debug, + }, +}; + +static nl_recvmsg_err_cb_t cb_err_def[NL_CB_KIND_MAX+1] = { + [NL_CB_VERBOSE] = nl_error_handler_verbose, + [NL_CB_DEBUG] = nl_error_handler_verbose, +}; + +/** + * @name Callback Handle Management + * @{ + */ + +/** + * Allocate a new callback handle + * @arg kind callback kind to be used for initialization + * @return Newly allocated callback handle or NULL + */ +struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind) +{ + int i; + struct nl_cb *cb; + + if (kind < 0 || kind > NL_CB_KIND_MAX) + return NULL; + + cb = calloc(1, sizeof(*cb)); + if (!cb) { + nl_errno(ENOMEM); + return NULL; + } + + cb->cb_refcnt = 1; + + for (i = 0; i <= NL_CB_TYPE_MAX; i++) + nl_cb_set(cb, i, kind, NULL, NULL); + + nl_cb_err(cb, kind, NULL, NULL); + + return cb; +} + +/** + * Clone an existing callback handle + * @arg orig original callback handle + * @return Newly allocated callback handle being a duplicate of + * orig or NULL + */ +struct nl_cb *nl_cb_clone(struct nl_cb *orig) +{ + struct nl_cb *cb; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) + return NULL; + + memcpy(cb, orig, sizeof(*orig)); + cb->cb_refcnt = 1; + + return cb; +} + +struct nl_cb *nl_cb_get(struct nl_cb *cb) +{ + cb->cb_refcnt++; + + return cb; +} + +void nl_cb_put(struct nl_cb *cb) +{ + if (!cb) + return; + + cb->cb_refcnt--; + + if (cb->cb_refcnt < 0) + BUG(); + + if (cb->cb_refcnt <= 0) + free(cb); +} + +/** @} */ + +/** + * @name Callback Setup + * @{ + */ + +/** + * Set up a callback + * @arg cb callback set + * @arg type callback to modify + * @arg kind kind of implementation + * @arg func callback function (NL_CB_CUSTOM) + * @arg arg argument passed to callback + * + * @return 0 on success or a negative error code + */ +int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind, + nl_recvmsg_msg_cb_t func, void *arg) +{ + if (type < 0 || type > NL_CB_TYPE_MAX) + return nl_error(ERANGE, "Callback type out of range"); + + if (kind < 0 || kind > NL_CB_KIND_MAX) + return nl_error(ERANGE, "Callback kind out of range"); + + if (kind == NL_CB_CUSTOM) { + cb->cb_set[type] = func; + cb->cb_args[type] = arg; + } else { + cb->cb_set[type] = cb_def[type][kind]; + cb->cb_args[type] = arg; + } + + return 0; +} + +/** + * Set up a all callbacks + * @arg cb callback set + * @arg kind kind of callback + * @arg func callback function + * @arg arg argument to be passwd to callback function + * + * @return 0 on success or a negative error code + */ +int nl_cb_set_all(struct nl_cb *cb, enum nl_cb_kind kind, + nl_recvmsg_msg_cb_t func, void *arg) +{ + int i, err; + + for (i = 0; i <= NL_CB_TYPE_MAX; i++) { + err = nl_cb_set(cb, i, kind, func, arg); + if (err < 0) + return err; + } + + return 0; +} + +/** + * Set up an error callback + * @arg cb callback set + * @arg kind kind of callback + * @arg func callback function + * @arg arg argument to be passed to callback function + */ +int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, + nl_recvmsg_err_cb_t func, void *arg) +{ + if (kind < 0 || kind > NL_CB_KIND_MAX) + return nl_error(ERANGE, "Callback kind out of range"); + + if (kind == NL_CB_CUSTOM) { + cb->cb_err = func; + cb->cb_err_arg = arg; + } else { + cb->cb_err = cb_err_def[kind]; + cb->cb_err_arg = arg; + } + + return 0; +} + +/** @} */ + +/** + * @name Overwriting + * @{ + */ + +/** + * Overwrite internal calls to nl_recvmsgs() + * @arg cb callback set + * @arg func replacement callback for nl_recvmsgs() + */ +void nl_cb_overwrite_recvmsgs(struct nl_cb *cb, + int (*func)(struct nl_handle *, struct nl_cb *)) +{ + cb->cb_recvmsgs_ow = func; +} + +/** + * Overwrite internal calls to nl_recv() + * @arg cb callback set + * @arg func replacement callback for nl_recv() + */ +void nl_cb_overwrite_recv(struct nl_cb *cb, + int (*func)(struct nl_handle *, struct sockaddr_nl *, + unsigned char **, struct ucred **)) +{ + cb->cb_recv_ow = func; +} + +/** + * Overwrite internal calls to nl_send() + * @arg cb callback set + * @arg func replacement callback for nl_send() + */ +void nl_cb_overwrite_send(struct nl_cb *cb, + int (*func)(struct nl_handle *, struct nl_msg *)) +{ + cb->cb_send_ow = func; +} + +/** @} */ + +/** @} */ diff --git a/lib/msg.c b/lib/msg.c new file mode 100644 index 0000000..caae744 --- /dev/null +++ b/lib/msg.c @@ -0,0 +1,931 @@ +/* + * lib/msg.c Netlink Messages Interface + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup nl + * @defgroup msg Messages + * Netlink Message Construction/Parsing Interface + * + * The following information is partly extracted from RFC3549 + * (ftp://ftp.rfc-editor.org/in-notes/rfc3549.txt) + * + * @par Message Format + * Netlink messages consist of a byte stream with one or multiple + * Netlink headers and an associated payload. If the payload is too big + * to fit into a single message it, can be split over multiple Netlink + * messages, collectively called a multipart message. For multipart + * messages, the first and all following headers have the \c NLM_F_MULTI + * Netlink header flag set, except for the last header which has the + * Netlink header type \c NLMSG_DONE. + * + * @par + * The Netlink message header (\link nlmsghdr struct nlmsghdr\endlink) is shown below. + * @code + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Flags | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Sequence Number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Process ID (PID) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * @endcode + * + * @par + * The netlink message header and payload must be aligned properly: + * @code + * <------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) ---> + * +----------------------------+- - -+- - - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | struct nlmsghdr | | | | + * +----------------------------+- - -+- - - - - - - - - - -+- - -+ + * @endcode + * @par + * Message Format: + * @code + * <--- nlmsg_total_size(payload) ---> + * <-- nlmsg_msg_size(payload) -> + * +----------+- - -+-------------+- - -+-------- - - + * | nlmsghdr | Pad | Payload | Pad | nlmsghdr + * +----------+- - -+-------------+- - -+-------- - - + * nlmsg_data(nlh)---^ ^ + * nlmsg_next(nlh)-----------------------+ + * @endcode + * @par + * The payload may consist of arbitary data but may have strict + * alignment and formatting rules depening on the specific netlink + * families. + * @par + * @code + * <---------------------- nlmsg_len(nlh) ---------------------> + * <------ hdrlen ------> <- nlmsg_attrlen(nlh, hdrlen) -> + * +----------------------+- - -+--------------------------------+ + * | Family Header | Pad | Attributes | + * +----------------------+- - -+--------------------------------+ + * nlmsg_attrdata(nlh, hdrlen)---^ + * @endcode + * @par The ACK Netlink Message + * This message is actually used to denote both an ACK and a NACK. + * Typically, the direction is from FEC to CPC (in response to an ACK + * request message). However, the CPC should be able to send ACKs back + * to FEC when requested. + * @code + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Netlink message header | + * | type = NLMSG_ERROR | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Error code | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | OLD Netlink message header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * @endcode + * + * @par 1) Creating a new netlink message + * @code + * // Netlink messages can be allocated in various ways, you may + * // allocate an empty netlink message by using nlmsg_alloc(): + * struct nl_msg *msg = nlmsg_alloc(); + * + * // Very often, the message type and message flags are known + * // at allocation time while the other fields are auto generated: + * struct nl_msg *msg = nlmsg_alloc_simple(MY_TYPE, MY_FLAGS); + * + * // Alternatively an existing netlink message header can be used + * // to inherit header values from: + * struct nlmsghdr hdr = { + * .nlmsg_type = MY_TYPE, + * .nlmsg_flags = MY_FLAGS, + * }; + * struct nl_msg *msg = nlmsg_inherit(&hdr); + * + * // Last but not least, netlink messages received from netlink sockets + * // can be converted into nl_msg objects using nlmsg_convert(): + * struct nl_msg *msg = nlmsg_convert(nlh_from_nl_sock); + * + * // The header can later be retrieved with nlmsg_hdr() and changed again: + * nlmsg_hdr(msg)->nlmsg_flags |= YET_ANOTHER_FLAG; + * @endcode + * + * @par 2) Appending data to the message + * @code + * // Payload may be added to the message via nlmsg_append(). The fourth + * // parameter specifies the number of alignment bytes the data should + * // be padding with at the end. Common values are 0 to disable it or + * // NLMSG_ALIGNTO to ensure proper netlink message padding. + * nlmsg_append(msg, &mydata, sizeof(mydata), 0); + * + * // Sometimes it may be necessary to reserve room for data but defer + * // the actual copying to a later point, nlmsg_reserve() can be used + * // for this purpose: + * void *data = nlmsg_reserve(msg, sizeof(mydata), NLMSG_ALIGNTO); + * @endcode + * + * @par 3) Cleaning up message construction + * @code + * // After successful use of the message, the memory must be freed + * // using nlmsg_free() + * nlmsg_free(msg); + * @endcode + * + * @par 4) Parsing messages + * @code + * int n; + * unsigned char *buf; + * struct nlmsghdr *hdr; + * + * n = nl_recv(handle, NULL, &buf); + * + * hdr = (struct nlmsghdr *) buf; + * while (nlmsg_ok(hdr, n)) { + * // Process message here... + * hdr = nlmsg_next(hdr, &n); + * } + * @endcode + * @{ + */ + +#include +#include +#include +#include +#include +#include + +/** + * @name Size Calculations + * @{ + */ + +/** + * length of netlink message not including padding + * @arg payload length of message payload + */ +int nlmsg_msg_size(int payload) +{ + return NLMSG_HDRLEN + payload; +} + +/** + * length of netlink message including padding + * @arg payload length of message payload + */ +int nlmsg_total_size(int payload) +{ + return NLMSG_ALIGN(nlmsg_msg_size(payload)); +} + +/** + * length of padding at the message's tail + * @arg payload length of message payload + */ +int nlmsg_padlen(int payload) +{ + return nlmsg_total_size(payload) - nlmsg_msg_size(payload); +} + +/** @} */ + +/** + * @name Payload Access + * @{ + */ + +/** + * head of message payload + * @arg nlh netlink messsage header + */ +void *nlmsg_data(const struct nlmsghdr *nlh) +{ + return (unsigned char *) nlh + NLMSG_HDRLEN; +} + +void *nlmsg_tail(const struct nlmsghdr *nlh) +{ + return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len); +} + +/** + * length of message payload + * @arg nlh netlink message header + */ +int nlmsg_len(const struct nlmsghdr *nlh) +{ + return nlh->nlmsg_len - NLMSG_HDRLEN; +} + +/** @} */ + +/** + * @name Attribute Access + * @{ + */ + +/** + * head of attributes data + * @arg nlh netlink message header + * @arg hdrlen length of family specific header + */ +struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen) +{ + unsigned char *data = nlmsg_data(nlh); + return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen)); +} + +/** + * length of attributes data + * @arg nlh netlink message header + * @arg hdrlen length of family specific header + */ +int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen) +{ + return nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen); +} + +/** @} */ + +/** + * @name Message Parsing + * @{ + */ + +/** + * check if the netlink message fits into the remaining bytes + * @arg nlh netlink message header + * @arg remaining number of bytes remaining in message stream + */ +int nlmsg_ok(const struct nlmsghdr *nlh, int remaining) +{ + return (remaining >= sizeof(struct nlmsghdr) && + nlh->nlmsg_len >= sizeof(struct nlmsghdr) && + nlh->nlmsg_len <= remaining); +} + +/** + * next netlink message in message stream + * @arg nlh netlink message header + * @arg remaining number of bytes remaining in message stream + * + * @returns the next netlink message in the message stream and + * decrements remaining by the size of the current message. + */ +struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining) +{ + int totlen = NLMSG_ALIGN(nlh->nlmsg_len); + + *remaining -= totlen; + + return (struct nlmsghdr *) ((unsigned char *) nlh + totlen); +} + +/** + * parse attributes of a netlink message + * @arg nlh netlink message header + * @arg hdrlen length of family specific header + * @arg tb destination array with maxtype+1 elements + * @arg maxtype maximum attribute type to be expected + * @arg policy validation policy + * + * See nla_parse() + */ +int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[], + int maxtype, struct nla_policy *policy) +{ + if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) + return nl_errno(EINVAL); + + return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), policy); +} + +/** + * nlmsg_find_attr - find a specific attribute in a netlink message + * @arg nlh netlink message header + * @arg hdrlen length of familiy specific header + * @arg attrtype type of attribute to look for + * + * Returns the first attribute which matches the specified type. + */ +struct nlattr *nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype) +{ + return nla_find(nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), attrtype); +} + +/** + * nlmsg_validate - validate a netlink message including attributes + * @arg nlh netlinket message header + * @arg hdrlen length of familiy specific header + * @arg maxtype maximum attribute type to be expected + * @arg policy validation policy + */ +int nlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype, + struct nla_policy *policy) +{ + if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen)) + return nl_errno(EINVAL); + + return nla_validate(nlmsg_attrdata(nlh, hdrlen), + nlmsg_attrlen(nlh, hdrlen), maxtype, policy); +} + +/** @} */ + +/** + * @name Message Building/Access + * @{ + */ + +static struct nl_msg *__nlmsg_alloc(size_t len) +{ + struct nl_msg *nm; + + nm = calloc(1, sizeof(*nm)); + if (!nm) + goto errout; + + nm->nm_nlh = calloc(1, len); + if (!nm->nm_nlh) + goto errout; + + nm->nm_protocol = -1; + nm->nm_nlh->nlmsg_len = len; + + NL_DBG(2, "msg %p: Allocated new message, nlmsg_len=%d\n", nm, len); + + return nm; +errout: + free(nm); + nl_errno(ENOMEM); + return NULL; +} + +/** + * Allocate a new netlink message + * + * Allocates a new netlink message without any further payload. + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg *nlmsg_alloc(void) +{ + return __nlmsg_alloc(nlmsg_total_size(0)); +} + +/** + * Allocate a new netlink message and inherit netlink message header + * @arg hdr Netlink message header template + * + * Allocates a new netlink message with a tailroom for the netlink + * message header. If \a hdr is not NULL it will be used as a + * template for the netlink message header, otherwise the header + * is left blank. + * + * @return Newly allocated netlink message or NULL + */ +struct nl_msg *nlmsg_inherit(struct nlmsghdr *hdr) +{ + struct nl_msg *nm; + + nm = nlmsg_alloc(); + if (nm && hdr) { + struct nlmsghdr *new = nm->nm_nlh; + + new->nlmsg_type = hdr->nlmsg_type; + new->nlmsg_flags = hdr->nlmsg_flags; + new->nlmsg_seq = hdr->nlmsg_seq; + new->nlmsg_pid = hdr->nlmsg_pid; + } + + return nm; +} + +/** + * Allocate a new netlink message + * @arg nlmsgtype Netlink message type + * @arg flags Message flags. + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg *nlmsg_alloc_simple(int nlmsgtype, int flags) +{ + struct nl_msg *msg; + struct nlmsghdr nlh = { + .nlmsg_type = nlmsgtype, + .nlmsg_flags = flags, + }; + + msg = nlmsg_inherit(&nlh); + if (msg) + NL_DBG(2, "msg %p: Allocated new simple message\n", msg); + + return msg; +} + +/** + * Convert a netlink message received from a netlink socket to a nl_msg + * @arg hdr Netlink message received from netlink socket. + * + * Allocates a new netlink message and copies all of the data pointed to + * by \a hdr into the new message object. + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg *nlmsg_convert(struct nlmsghdr *hdr) +{ + struct nl_msg *nm; + + nm = __nlmsg_alloc(NLMSG_ALIGN(hdr->nlmsg_len)); + if (!nm) + goto errout; + + memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len); + + return nm; +errout: + nlmsg_free(nm); + return NULL; +} + +/** + * Reserve room for additional data in a netlink message + * @arg n netlink message + * @arg len length of additional data to reserve room for + * @arg pad number of bytes to align data to + * + * Reserves room for additional data at the tail of the an + * existing netlink message. Eventual padding required will + * be zeroed out. + * + * @note All existing pointers into the old data section may have + * become obsolete and illegal to reference after this call. + * + * @return Pointer to start of additional data tailroom or NULL. + */ +void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad) +{ + void *tmp; + size_t tlen; + + tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len; + + tmp = realloc(n->nm_nlh, n->nm_nlh->nlmsg_len + tlen); + if (!tmp) { + nl_errno(ENOMEM); + return NULL; + } + + n->nm_nlh = tmp; + tmp += n->nm_nlh->nlmsg_len; + n->nm_nlh->nlmsg_len += tlen; + + if (tlen > len) + memset(tmp + len, 0, tlen - len); + + NL_DBG(2, "msg %p: Reserved %d bytes, pad=%d, nlmsg_len=%d\n", + n, len, pad, n->nm_nlh->nlmsg_len); + + return tmp; +} + +/** + * Append data to tail of a netlink message + * @arg n netlink message + * @arg data data to add + * @arg len length of data + * @arg pad Number of bytes to align data to. + * + * Extends the netlink message as needed and appends the data of given + * length to the message. + * + * @note All existing pointers into the old data section may have + * become obsolete and illegal to reference after this call. + * + * @return 0 on success or a negative error code + */ +int nlmsg_append(struct nl_msg *n, void *data, size_t len, int pad) +{ + void *tmp; + + tmp = nlmsg_reserve(n, len, pad); + if (tmp == NULL) + return nl_errno(ENOMEM); + + memcpy(tmp, data, len); + NL_DBG(2, "msg %p: Appended %d bytes with padding %d\n", n, len, pad); + + return 0; +} + +/** + * Add a netlink message header to a netlink message + * @arg n netlink message + * @arg pid netlink process id or NL_AUTO_PID + * @arg seq sequence number of message or NL_AUTO_SEQ + * @arg type message type + * @arg payload length of message payload + * @arg flags message flags + * + * Adds or overwrites the netlink message header in an existing message + * object. If \a payload is greater-than zero additional room will be + * reserved, f.e. for family specific headers. It can be accesed via + * nlmsg_data(). + * + * @return A pointer to the netlink message header or NULL. + */ +struct nlmsghdr *nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq, + int type, int payload, int flags) +{ + struct nlmsghdr *nlh; + + if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN) + BUG(); + + nlh = (struct nlmsghdr *) n->nm_nlh; + nlh->nlmsg_type = type; + nlh->nlmsg_flags = flags; + nlh->nlmsg_pid = pid; + nlh->nlmsg_seq = seq; + + NL_DBG(2, "msg %p: Added netlink header type=%d, flags=%d, pid=%d, " + "seq=%d\n", n, type, flags, pid, seq); + + if (payload > 0 && + nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL) + return NULL; + + return nlh; +} + +/** + * Return actual netlink message + * @arg n netlink message + * + * Returns the actual netlink message casted to the type of the netlink + * message header. + * + * @return A pointer to the netlink message. + */ +struct nlmsghdr *nlmsg_hdr(struct nl_msg *n) +{ + return n->nm_nlh; +} + +/** + * Free a netlink message + * @arg n netlink message + * + * Destroys a netlink message and frees up all used memory. + * + * @pre The message must be unused. + */ +void nlmsg_free(struct nl_msg *n) +{ + if (!n) + return; + + free(n->nm_nlh); + free(n); + NL_DBG(2, "msg %p: Freed\n", n); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nlmsg_set_proto(struct nl_msg *msg, int protocol) +{ + msg->nm_protocol = protocol; +} + +int nlmsg_get_proto(struct nl_msg *msg) +{ + return msg->nm_protocol; +} + +void nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr) +{ + memcpy(&msg->nm_src, addr, sizeof(*addr)); +} + +struct sockaddr_nl *nlmsg_get_src(struct nl_msg *msg) +{ + return &msg->nm_src; +} + +void nlmsg_set_dst(struct nl_msg *msg, struct sockaddr_nl *addr) +{ + memcpy(&msg->nm_dst, addr, sizeof(*addr)); +} + +struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *msg) +{ + return &msg->nm_dst; +} + +void nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds) +{ + memcpy(&msg->nm_creds, creds, sizeof(*creds)); + msg->nm_flags |= NL_MSG_CRED_PRESENT; +} + +struct ucred *nlmsg_get_creds(struct nl_msg *msg) +{ + if (msg->nm_flags & NL_MSG_CRED_PRESENT) + return &msg->nm_creds; + return NULL; +} + +/** @} */ + +/** + * @name Netlink Message Type Translations + * @{ + */ + +static struct trans_tbl nl_msgtypes[] = { + __ADD(NLMSG_NOOP,NOOP) + __ADD(NLMSG_ERROR,ERROR) + __ADD(NLMSG_DONE,DONE) + __ADD(NLMSG_OVERRUN,OVERRUN) +}; + +char *nl_nlmsgtype2str(int type, char *buf, size_t size) +{ + return __type2str(type, buf, size, nl_msgtypes, + ARRAY_SIZE(nl_msgtypes)); +} + +int nl_str2nlmsgtype(const char *name) +{ + return __str2type(name, nl_msgtypes, ARRAY_SIZE(nl_msgtypes)); +} + +/** @} */ + +/** + * @name Netlink Message Flags Translations + * @{ + */ + +char *nl_nlmsg_flags2str(int flags, char *buf, size_t len) +{ + memset(buf, 0, len); + +#define PRINT_FLAG(f) \ + if (flags & NLM_F_##f) { \ + flags &= ~NLM_F_##f; \ + strncat(buf, #f, len - strlen(buf) - 1); \ + if (flags) \ + strncat(buf, ",", len - strlen(buf) - 1); \ + } + + PRINT_FLAG(REQUEST); + PRINT_FLAG(MULTI); + PRINT_FLAG(ACK); + PRINT_FLAG(ECHO); + PRINT_FLAG(ROOT); + PRINT_FLAG(MATCH); + PRINT_FLAG(ATOMIC); + PRINT_FLAG(REPLACE); + PRINT_FLAG(EXCL); + PRINT_FLAG(CREATE); + PRINT_FLAG(APPEND); + + if (flags) { + char s[32]; + snprintf(s, sizeof(s), "0x%x", flags); + strncat(buf, s, len - strlen(buf) - 1); + } +#undef PRINT_FLAG + + return buf; +} + +/** @} */ + +/** + * @name Direct Parsing + * @{ + */ + +/** @cond SKIP */ +struct dp_xdata { + void (*cb)(struct nl_object *, void *); + void *arg; +}; +/** @endcond */ + +static int parse_cb(struct nl_object *obj, struct nl_parser_param *p) +{ + struct dp_xdata *x = p->pp_arg; + + x->cb(obj, x->arg); + nl_object_put(obj); + return 0; +} + +int nl_msg_parse(struct nl_msg *msg, void (*cb)(struct nl_object *, void *), + void *arg) +{ + struct nl_cache_ops *ops; + struct nl_parser_param p = { + .pp_cb = parse_cb + }; + struct dp_xdata x = { + .cb = cb, + .arg = arg, + }; + + ops = nl_cache_mngt_associate(nlmsg_get_proto(msg), + nlmsg_hdr(msg)->nlmsg_type); + if (ops == NULL) + return nl_error(ENOENT, "Unknown message type %d", + nlmsg_hdr(msg)->nlmsg_type); + p.pp_arg = &x; + + return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p); +} + +/** @} */ + +/** + * @name Dumping + * @{ + */ + +static void prefix_line(FILE *ofd, int prefix) +{ + int i; + + for (i = 0; i < prefix; i++) + fprintf(ofd, " "); +} + +static inline void dump_hex(FILE *ofd, char *start, int len, int prefix) +{ + int i, a, c, limit; + char ascii[21] = {0}; + + limit = 18 - (prefix * 2); + prefix_line(ofd, prefix); + fprintf(ofd, " "); + + for (i = 0, a = 0, c = 0; i < len; i++) { + int v = *(uint8_t *) (start + i); + + fprintf(ofd, "%02x ", v); + ascii[a++] = isprint(v) ? v : '.'; + + if (c == limit-1) { + fprintf(ofd, "%s\n", ascii); + if (i < (len - 1)) { + prefix_line(ofd, prefix); + fprintf(ofd, " "); + } + a = c = 0; + memset(ascii, 0, sizeof(ascii)); + } else + c++; + } + + if (c != 0) { + for (i = 0; i < (limit - c); i++) + fprintf(ofd, " "); + fprintf(ofd, "%s\n", ascii); + } +} + +static void print_hdr(FILE *ofd, struct nl_msg *msg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct nl_cache_ops *ops; + char buf[128]; + + fprintf(ofd, " .nlmsg_len = %d\n", nlh->nlmsg_len); + + ops = nl_cache_mngt_associate(nlmsg_get_proto(msg), nlh->nlmsg_type); + + fprintf(ofd, " .nlmsg_type = %d <%s>\n", nlh->nlmsg_type, + ops ? nl_cache_mngt_type2name(ops, msg->nm_protocol, + nlh->nlmsg_type, buf, sizeof(buf)) + : nl_nlmsgtype2str(nlh->nlmsg_type, buf, sizeof(buf))); + fprintf(ofd, " .nlmsg_flags = %d <%s>\n", nlh->nlmsg_flags, + nl_nlmsg_flags2str(nlh->nlmsg_flags, buf, sizeof(buf))); + fprintf(ofd, " .nlmsg_seq = %d\n", nlh->nlmsg_seq); + fprintf(ofd, " .nlmsg_pid = %d\n", nlh->nlmsg_pid); + +} + +static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen, + int prefix) +{ + int rem; + struct nlattr *nla; + + nla_for_each_attr(nla, attrs, attrlen, rem) { + int padlen, alen = nla_len(nla); + + prefix_line(ofd, prefix); + fprintf(ofd, " [ATTR %02d%s] %d octets\n", nla_type(nla), + nla->nla_type & NLA_F_NESTED ? " NESTED" : "", + alen); + + if (nla->nla_type & NLA_F_NESTED) + dump_attrs(ofd, nla_data(nla), alen, prefix+1); + else + dump_hex(ofd, nla_data(nla), alen, prefix); + + padlen = nla_padlen(alen); + if (padlen > 0) { + prefix_line(ofd, prefix); + fprintf(ofd, " [PADDING] %d octets\n", + padlen); + dump_hex(ofd, nla_data(nla) + alen, + padlen, prefix); + } + } + + if (rem) { + prefix_line(ofd, prefix); + fprintf(ofd, " [LEFTOVER] %d octets\n", rem); + } +} + +/** + * Dump message in human readable format to file descriptor + * @arg msg Message to print + * @arg ofd File descriptor. + */ +void nl_msg_dump(struct nl_msg *msg, FILE *ofd) +{ + struct nlmsghdr *hdr = nlmsg_hdr(msg); + + fprintf(ofd, + "-------------------------- BEGIN NETLINK MESSAGE " + "---------------------------\n"); + + fprintf(ofd, " [HEADER] %Zu octets\n", sizeof(struct nlmsghdr)); + print_hdr(ofd, msg); + + if (hdr->nlmsg_type == NLMSG_ERROR && + hdr->nlmsg_len >= nlmsg_msg_size(sizeof(struct nlmsgerr))) { + struct nl_msg *errmsg; + struct nlmsgerr *err = nlmsg_data(hdr); + + fprintf(ofd, " [ERRORMSG] %Zu octets\n", sizeof(*err)); + fprintf(ofd, " .error = %d \"%s\"\n", err->error, + strerror(-err->error)); + fprintf(ofd, " [ORIGINAL MESSAGE] %Zu octets\n", sizeof(*hdr)); + + errmsg = nlmsg_inherit(&err->msg); + print_hdr(ofd, errmsg); + nlmsg_free(errmsg); + } else if (nlmsg_len(hdr) > 0) { + struct nl_cache_ops *ops; + int payloadlen = nlmsg_len(hdr); + int attrlen = 0; + + ops = nl_cache_mngt_associate(nlmsg_get_proto(msg), + hdr->nlmsg_type); + if (ops) { + attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); + payloadlen -= attrlen; + } + + fprintf(ofd, " [PAYLOAD] %d octets\n", payloadlen); + dump_hex(ofd, nlmsg_data(hdr), payloadlen, 0); + + if (attrlen) { + struct nlattr *attrs; + int attrlen; + + attrs = nlmsg_attrdata(hdr, ops->co_hdrsize); + attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize); + dump_attrs(ofd, attrs, attrlen, 0); + } + } + + fprintf(ofd, + "--------------------------- END NETLINK MESSAGE " + "---------------------------\n"); +} + +/** @} */ + +/** @} */ diff --git a/lib/netfilter/ct.c b/lib/netfilter/ct.c new file mode 100644 index 0000000..24b67fd --- /dev/null +++ b/lib/netfilter/ct.c @@ -0,0 +1,465 @@ +/* + * lib/netfilter/ct.c Conntrack + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +/** + * @ingroup nfnl + * @defgroup ct Conntrack + * @brief + * @{ + */ + +#include +#include +#include + +#include +#include +#include +#include + +static struct nl_cache_ops nfnl_ct_ops; + +#if __BYTE_ORDER == __BIG_ENDIAN +static uint64_t ntohll(uint64_t x) +{ + return x; +} +#elif __BYTE_ORDER == __LITTLE_ENDIAN +static uint64_t ntohll(uint64_t x) +{ + return __bswap_64(x); +} +#endif + +static struct nla_policy ct_policy[CTA_MAX+1] = { + [CTA_TUPLE_ORIG] = { .type = NLA_NESTED }, + [CTA_TUPLE_REPLY] = { .type = NLA_NESTED }, + [CTA_STATUS] = { .type = NLA_U32 }, + [CTA_PROTOINFO] = { .type = NLA_NESTED }, + //[CTA_HELP] + //[CTA_NAT_SRC] + [CTA_TIMEOUT] = { .type = NLA_U32 }, + [CTA_MARK] = { .type = NLA_U32 }, + [CTA_COUNTERS_ORIG] = { .type = NLA_NESTED }, + [CTA_COUNTERS_REPLY] = { .type = NLA_NESTED }, + [CTA_USE] = { .type = NLA_U32 }, + [CTA_ID] = { .type = NLA_U32 }, + //[CTA_NAT_DST] +}; + +static struct nla_policy ct_tuple_policy[CTA_TUPLE_MAX+1] = { + [CTA_TUPLE_IP] = { .type = NLA_NESTED }, + [CTA_TUPLE_PROTO] = { .type = NLA_NESTED }, +}; + +static struct nla_policy ct_ip_policy[CTA_IP_MAX+1] = { + [CTA_IP_V4_SRC] = { .type = NLA_U32 }, + [CTA_IP_V4_DST] = { .type = NLA_U32 }, + [CTA_IP_V6_SRC] = { .minlen = 16 }, + [CTA_IP_V6_DST] = { .minlen = 16 }, +}; + +static struct nla_policy ct_proto_policy[CTA_PROTO_MAX+1] = { + [CTA_PROTO_NUM] = { .type = NLA_U8 }, + [CTA_PROTO_SRC_PORT] = { .type = NLA_U16 }, + [CTA_PROTO_DST_PORT] = { .type = NLA_U16 }, + [CTA_PROTO_ICMP_ID] = { .type = NLA_U16 }, + [CTA_PROTO_ICMP_TYPE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMP_CODE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMPV6_ID] = { .type = NLA_U16 }, + [CTA_PROTO_ICMPV6_TYPE] = { .type = NLA_U8 }, + [CTA_PROTO_ICMPV6_CODE] = { .type = NLA_U8 }, +}; + +static struct nla_policy ct_protoinfo_policy[CTA_PROTOINFO_MAX+1] = { + [CTA_PROTOINFO_TCP] = { .type = NLA_NESTED }, +}; + +static struct nla_policy ct_protoinfo_tcp_policy[CTA_PROTOINFO_TCP_MAX+1] = { + [CTA_PROTOINFO_TCP_STATE] = { .type = NLA_U8 }, + [CTA_PROTOINFO_TCP_WSCALE_ORIGINAL] = { .type = NLA_U8 }, + [CTA_PROTOINFO_TCP_WSCALE_REPLY] = { .type = NLA_U8 }, + [CTA_PROTOINFO_TCP_FLAGS_ORIGINAL] = { .minlen = 2 }, + [CTA_PROTOINFO_TCP_FLAGS_REPLY] = { .minlen = 2 }, + +}; + +static struct nla_policy ct_counters_policy[CTA_COUNTERS_MAX+1] = { + [CTA_COUNTERS_PACKETS] = { .type = NLA_U64 }, + [CTA_COUNTERS_BYTES] = { .type = NLA_U64 }, + [CTA_COUNTERS32_PACKETS]= { .type = NLA_U32 }, + [CTA_COUNTERS32_BYTES] = { .type = NLA_U32 }, +}; + +static int ct_parse_ip(struct nfnl_ct *ct, int repl, struct nlattr *attr) +{ + struct nlattr *tb[CTA_IP_MAX+1]; + struct nl_addr *addr; + int err; + + err = nla_parse_nested(tb, CTA_IP_MAX, attr, ct_ip_policy); + if (err < 0) + goto errout; + + if (tb[CTA_IP_V4_SRC]) { + addr = nla_get_addr(tb[CTA_IP_V4_SRC], AF_INET); + if (addr == NULL) + goto errout_errno; + err = nfnl_ct_set_src(ct, repl, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V4_DST]) { + addr = nla_get_addr(tb[CTA_IP_V4_DST], AF_INET); + if (addr == NULL) + goto errout_errno; + err = nfnl_ct_set_dst(ct, repl, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V6_SRC]) { + addr = nla_get_addr(tb[CTA_IP_V6_SRC], AF_INET6); + if (addr == NULL) + goto errout_errno; + err = nfnl_ct_set_src(ct, repl, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + if (tb[CTA_IP_V6_DST]) { + addr = nla_get_addr(tb[CTA_IP_V6_DST], AF_INET6); + if (addr == NULL) + goto errout_errno; + err = nfnl_ct_set_dst(ct, repl, addr); + nl_addr_put(addr); + if (err < 0) + goto errout; + } + + return 0; + +errout_errno: + return nl_get_errno(); +errout: + return err; +} + +static int ct_parse_proto(struct nfnl_ct *ct, int repl, struct nlattr *attr) +{ + struct nlattr *tb[CTA_PROTO_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_PROTO_MAX, attr, ct_proto_policy); + if (err < 0) + return err; + + if (!repl && tb[CTA_PROTO_NUM]) + nfnl_ct_set_proto(ct, nla_get_u8(tb[CTA_PROTO_NUM])); + if (tb[CTA_PROTO_SRC_PORT]) + nfnl_ct_set_src_port(ct, repl, + nla_get_u16(tb[CTA_PROTO_SRC_PORT])); + if (tb[CTA_PROTO_DST_PORT]) + nfnl_ct_set_dst_port(ct, repl, + nla_get_u16(tb[CTA_PROTO_DST_PORT])); + if (tb[CTA_PROTO_ICMP_ID]) + nfnl_ct_set_icmp_id(ct, repl, + nla_get_u16(tb[CTA_PROTO_ICMP_ID])); + if (tb[CTA_PROTO_ICMP_TYPE]) + nfnl_ct_set_icmp_type(ct, repl, + nla_get_u8(tb[CTA_PROTO_ICMP_TYPE])); + if (tb[CTA_PROTO_ICMP_CODE]) + nfnl_ct_set_icmp_code(ct, repl, + nla_get_u8(tb[CTA_PROTO_ICMP_CODE])); + + return 0; +} + +static int ct_parse_tuple(struct nfnl_ct *ct, int repl, struct nlattr *attr) +{ + struct nlattr *tb[CTA_TUPLE_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_TUPLE_MAX, attr, ct_tuple_policy); + if (err < 0) + return err; + + if (tb[CTA_TUPLE_IP]) { + err = ct_parse_ip(ct, repl, tb[CTA_TUPLE_IP]); + if (err < 0) + return err; + } + + if (tb[CTA_TUPLE_PROTO]) { + err = ct_parse_proto(ct, repl, tb[CTA_TUPLE_PROTO]); + if (err < 0) + return err; + } + + return 0; +} + +static int ct_parse_protoinfo_tcp(struct nfnl_ct *ct, struct nlattr *attr) +{ + struct nlattr *tb[CTA_PROTOINFO_TCP_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_PROTOINFO_TCP_MAX, attr, + ct_protoinfo_tcp_policy); + if (err < 0) + return err; + + if (tb[CTA_PROTOINFO_TCP_STATE]) + nfnl_ct_set_tcp_state(ct, + nla_get_u8(tb[CTA_PROTOINFO_TCP_STATE])); + + return 0; +} + +static int ct_parse_protoinfo(struct nfnl_ct *ct, struct nlattr *attr) +{ + struct nlattr *tb[CTA_PROTOINFO_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, + ct_protoinfo_policy); + if (err < 0) + return err; + + if (tb[CTA_PROTOINFO_TCP]) { + err = ct_parse_protoinfo_tcp(ct, tb[CTA_PROTOINFO_TCP]); + if (err < 0) + return err; + } + + return 0; +} + +static int ct_parse_counters(struct nfnl_ct *ct, int repl, struct nlattr *attr) +{ + struct nlattr *tb[CTA_COUNTERS_MAX+1]; + int err; + + err = nla_parse_nested(tb, CTA_COUNTERS_MAX, attr, ct_counters_policy); + if (err < 0) + return err; + + if (tb[CTA_COUNTERS_PACKETS]) + nfnl_ct_set_packets(ct, repl, + ntohll(nla_get_u64(tb[CTA_COUNTERS_PACKETS]))); + if (tb[CTA_COUNTERS32_PACKETS]) + nfnl_ct_set_packets(ct, repl, + ntohl(nla_get_u32(tb[CTA_COUNTERS32_PACKETS]))); + if (tb[CTA_COUNTERS_BYTES]) + nfnl_ct_set_bytes(ct, repl, + ntohll(nla_get_u64(tb[CTA_COUNTERS_BYTES]))); + if (tb[CTA_COUNTERS32_BYTES]) + nfnl_ct_set_bytes(ct, repl, + ntohl(nla_get_u32(tb[CTA_COUNTERS32_BYTES]))); + + return 0; +} + +int nfnlmsg_ct_group(struct nlmsghdr *nlh) +{ + switch (nfnlmsg_subtype(nlh)) { + case IPCTNL_MSG_CT_NEW: + if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL)) + return NFNLGRP_CONNTRACK_NEW; + else + return NFNLGRP_CONNTRACK_UPDATE; + case IPCTNL_MSG_CT_DELETE: + return NFNLGRP_CONNTRACK_DESTROY; + default: + return NFNLGRP_NONE; + } +} + +struct nfnl_ct *nfnlmsg_ct_parse(struct nlmsghdr *nlh) +{ + struct nfnl_ct *ct; + struct nlattr *tb[CTA_MAX+1]; + int err; + + ct = nfnl_ct_alloc(); + if (!ct) + return NULL; + + ct->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, CTA_MAX, + ct_policy); + if (err < 0) + goto errout; + + nfnl_ct_set_family(ct, nfnlmsg_family(nlh)); + + if (tb[CTA_TUPLE_ORIG]) { + err = ct_parse_tuple(ct, 0, tb[CTA_TUPLE_ORIG]); + if (err < 0) + goto errout; + } + if (tb[CTA_TUPLE_REPLY]) { + err = ct_parse_tuple(ct, 1, tb[CTA_TUPLE_REPLY]); + if (err < 0) + goto errout; + } + + if (tb[CTA_PROTOINFO]) { + err = ct_parse_protoinfo(ct, tb[CTA_PROTOINFO]); + if (err < 0) + goto errout; + } + + if (tb[CTA_STATUS]) + nfnl_ct_set_status(ct, ntohl(nla_get_u32(tb[CTA_STATUS]))); + if (tb[CTA_TIMEOUT]) + nfnl_ct_set_timeout(ct, ntohl(nla_get_u32(tb[CTA_TIMEOUT]))); + if (tb[CTA_MARK]) + nfnl_ct_set_mark(ct, ntohl(nla_get_u32(tb[CTA_MARK]))); + if (tb[CTA_USE]) + nfnl_ct_set_use(ct, ntohl(nla_get_u32(tb[CTA_USE]))); + if (tb[CTA_ID]) + nfnl_ct_set_id(ct, ntohl(nla_get_u32(tb[CTA_ID]))); + + if (tb[CTA_COUNTERS_ORIG]) { + err = ct_parse_counters(ct, 0, tb[CTA_COUNTERS_ORIG]); + if (err < 0) + goto errout; + } + + if (tb[CTA_COUNTERS_REPLY]) { + err = ct_parse_counters(ct, 1, tb[CTA_COUNTERS_REPLY]); + if (err < 0) + goto errout; + } + + return ct; + +errout: + nfnl_ct_put(ct); + return NULL; +} + +static int ct_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, void *arg) +{ + struct nl_parser_param *pp = arg; + struct nfnl_ct *ct; + int err; + + ct = nfnlmsg_ct_parse(nlh); + if (ct == NULL) + goto errout_errno; + + err = pp->pp_cb((struct nl_object *) ct, pp); + if (err < 0) + goto errout; + + return P_ACCEPT; + +errout_errno: + err = nl_get_errno(); +errout: + nfnl_ct_put(ct); + return err; +} + +int nfnl_ct_dump_request(struct nl_handle *h) +{ + return nfnl_send_simple(h, NFNL_SUBSYS_CTNETLINK, IPCTNL_MSG_CT_GET, + NLM_F_DUMP, AF_UNSPEC, 0); +} + +static int ct_request_update(struct nl_cache *c, struct nl_handle *h) +{ + return nfnl_ct_dump_request(h); +} + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a conntrack cache holding all conntrack currently in the kernel + * @arg handle netlink handle + * + * Allocates a new cache, initializes it properly and updates it to + * contain all conntracks currently in the kernel. + * + * @note The caller is responsible for destroying and freeing the + * cache after using it. + * @return The cache or NULL if an error has occured. + */ +struct nl_cache *nfnl_ct_alloc_cache(struct nl_handle *handle) +{ + struct nl_cache *cache; + + cache = nl_cache_alloc(&nfnl_ct_ops); + if (!cache) + return NULL; + + if (handle && nl_cache_refill(handle, cache) < 0) { + free(cache); + return NULL; + } + + return cache; +} + +/** @} */ + +/** + * @name Conntrack Addition + * @{ + */ + +/** @} */ + +static struct nl_af_group ct_groups[] = { + { AF_UNSPEC, NFNLGRP_CONNTRACK_NEW }, + { AF_UNSPEC, NFNLGRP_CONNTRACK_UPDATE }, + { AF_UNSPEC, NFNLGRP_CONNTRACK_DESTROY }, + { END_OF_GROUP_LIST }, +}; + +#define NFNLMSG_CT_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK, (type)) +static struct nl_cache_ops nfnl_ct_ops = { + .co_name = "netfilter/ct", + .co_hdrsize = NFNL_HDRLEN, + .co_msgtypes = { + { NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_NEW), NL_ACT_NEW, "new" }, + { NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_GET), NL_ACT_GET, "get" }, + { NFNLMSG_CT_TYPE(IPCTNL_MSG_CT_DELETE), NL_ACT_DEL, "del" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_NETFILTER, + .co_groups = ct_groups, + .co_request_update = ct_request_update, + .co_msg_parser = ct_msg_parser, + .co_obj_ops = &ct_obj_ops, +}; + +static void __init ct_init(void) +{ + nl_cache_mngt_register(&nfnl_ct_ops); +} + +static void __exit ct_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_ct_ops); +} + +/** @} */ diff --git a/lib/netfilter/ct_obj.c b/lib/netfilter/ct_obj.c new file mode 100644 index 0000000..1f4284e --- /dev/null +++ b/lib/netfilter/ct_obj.c @@ -0,0 +1,667 @@ +/* + * lib/netfilter/ct_obj.c Conntrack Object + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#include +#include +#include +#include + +#include +#include +#include + +/** @cond SKIP */ +#define CT_ATTR_FAMILY (1UL << 0) +#define CT_ATTR_PROTO (1UL << 1) + +#define CT_ATTR_TCP_STATE (1UL << 2) + +#define CT_ATTR_STATUS (1UL << 3) +#define CT_ATTR_TIMEOUT (1UL << 4) +#define CT_ATTR_MARK (1UL << 5) +#define CT_ATTR_USE (1UL << 6) +#define CT_ATTR_ID (1UL << 7) + +#define CT_ATTR_ORIG_SRC (1UL << 8) +#define CT_ATTR_ORIG_DST (1UL << 9) +#define CT_ATTR_ORIG_SRC_PORT (1UL << 10) +#define CT_ATTR_ORIG_DST_PORT (1UL << 11) +#define CT_ATTR_ORIG_ICMP_ID (1UL << 12) +#define CT_ATTR_ORIG_ICMP_TYPE (1UL << 13) +#define CT_ATTR_ORIG_ICMP_CODE (1UL << 14) +#define CT_ATTR_ORIG_PACKETS (1UL << 15) +#define CT_ATTR_ORIG_BYTES (1UL << 16) + +#define CT_ATTR_REPL_SRC (1UL << 17) +#define CT_ATTR_REPL_DST (1UL << 18) +#define CT_ATTR_REPL_SRC_PORT (1UL << 19) +#define CT_ATTR_REPL_DST_PORT (1UL << 20) +#define CT_ATTR_REPL_ICMP_ID (1UL << 21) +#define CT_ATTR_REPL_ICMP_TYPE (1UL << 22) +#define CT_ATTR_REPL_ICMP_CODE (1UL << 23) +#define CT_ATTR_REPL_PACKETS (1UL << 24) +#define CT_ATTR_REPL_BYTES (1UL << 25) +/** @endcond */ + +static void ct_free_data(struct nl_object *c) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) c; + + if (ct == NULL) + return; + + nl_addr_put(ct->ct_orig.src); + nl_addr_put(ct->ct_orig.dst); + nl_addr_put(ct->ct_repl.src); + nl_addr_put(ct->ct_repl.dst); +} + +static int ct_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct nfnl_ct *dst = (struct nfnl_ct *) _dst; + struct nfnl_ct *src = (struct nfnl_ct *) _src; + struct nl_addr *addr; + + if (src->ct_orig.src) { + addr = nl_addr_clone(src->ct_orig.src); + if (!addr) + goto errout; + dst->ct_orig.src = addr; + } + + if (src->ct_orig.dst) { + addr = nl_addr_clone(src->ct_orig.dst); + if (!addr) + goto errout; + dst->ct_orig.dst = addr; + } + + if (src->ct_repl.src) { + addr = nl_addr_clone(src->ct_repl.src); + if (!addr) + goto errout; + dst->ct_repl.src = addr; + } + + if (src->ct_repl.dst) { + addr = nl_addr_clone(src->ct_repl.dst); + if (!addr) + goto errout; + dst->ct_repl.dst = addr; + } + + return 0; +errout: + return nl_get_errno(); +} + +static void ct_dump_dir(struct nfnl_ct *ct, int repl, + struct nl_dump_params *p) +{ + struct nl_addr *addr; + char addrbuf[64]; + + addr = nfnl_ct_get_src(ct, repl); + if (addr) + dp_dump(p, "src=%s ", + nl_addr2str(addr, addrbuf, sizeof(addrbuf))); + + addr = nfnl_ct_get_dst(ct, repl); + if (addr) + dp_dump(p, "dst=%s ", + nl_addr2str(addr, addrbuf, sizeof(addrbuf))); + + if (nfnl_ct_test_src_port(ct, repl)) + dp_dump(p, "sport=%u ", ntohs(nfnl_ct_get_src_port(ct, repl))); + if (nfnl_ct_test_dst_port(ct, repl)) + dp_dump(p, "dport=%u ", ntohs(nfnl_ct_get_dst_port(ct, repl))); + + if (nfnl_ct_test_icmp_type(ct, repl)) + dp_dump(p, "type=%d ", nfnl_ct_get_icmp_type(ct, repl)); + if (nfnl_ct_test_icmp_type(ct, repl)) + dp_dump(p, "code=%d ", nfnl_ct_get_icmp_code(ct, repl)); + if (nfnl_ct_test_icmp_type(ct, repl)) + dp_dump(p, "id=%d ", ntohs(nfnl_ct_get_icmp_id(ct, repl))); + + if (nfnl_ct_test_packets(ct, repl)) + dp_dump(p, "packets=%llu ", nfnl_ct_get_packets(ct, repl)); + if (nfnl_ct_test_bytes(ct, repl)) + dp_dump(p, "bytes=%llu ", nfnl_ct_get_bytes(ct, repl)); +} + +/* Compatible with /proc/net/nf_conntrack */ +static int ct_dump(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) a; + char buf[64]; + uint32_t status; + uint8_t family; + uint8_t proto; + + family = nfnl_ct_get_family(ct); + dp_dump(p, "%-8s %u ", nl_af2str(family, buf, sizeof(buf)), family); + + if (nfnl_ct_test_proto(ct)) { + proto = nfnl_ct_get_proto(ct); + dp_dump(p, "%-8s %u ", + nl_ip_proto2str(proto, buf, sizeof(buf)), proto); + } + + if (nfnl_ct_test_timeout(ct)) + dp_dump(p, "%ld ", nfnl_ct_get_timeout(ct)); + + if (nfnl_ct_test_tcp_state(ct)) + dp_dump(p, "%s ", + nfnl_ct_tcp_state2str(nfnl_ct_get_tcp_state(ct), + buf, sizeof(buf))); + + ct_dump_dir(ct, 0, p); + + status = nfnl_ct_get_status(ct); + if (!(status & IPS_SEEN_REPLY)) + dp_dump(p, "[UNREPLIED] "); + + ct_dump_dir(ct, 1, p); + + if (status & IPS_ASSURED) + dp_dump(p, "[ASSURED] "); + + if (nfnl_ct_test_mark(ct)) + dp_dump(p, "mark=%u ", nfnl_ct_get_mark(ct)); + + if (nfnl_ct_test_use(ct)) + dp_dump(p, "use=%u ", nfnl_ct_get_use(ct)); + + dp_dump(p, "\n"); + + return 1; +} + +static int ct_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) +{ + struct nfnl_ct *a = (struct nfnl_ct *) _a; + struct nfnl_ct *b = (struct nfnl_ct *) _b; + int diff = 0; + +#define CT_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, CT_ATTR_##ATTR, a, b, EXPR) +#define CT_DIFF_VAL(ATTR, FIELD) CT_DIFF(ATTR, a->FIELD != b->FIELD) +#define CT_DIFF_ADDR(ATTR, FIELD) \ + ((flags & LOOSE_FLAG_COMPARISON) \ + ? CT_DIFF(ATTR, nl_addr_cmp_prefix(a->FIELD, b->FIELD)) \ + : CT_DIFF(ATTR, nl_addr_cmp(a->FIELD, b->FIELD))) + + diff |= CT_DIFF_VAL(FAMILY, ct_family); + diff |= CT_DIFF_VAL(PROTO, ct_proto); + diff |= CT_DIFF_VAL(TCP_STATE, ct_protoinfo.tcp.state); + diff |= CT_DIFF_VAL(STATUS, ct_status); + diff |= CT_DIFF_VAL(TIMEOUT, ct_timeout); + diff |= CT_DIFF_VAL(MARK, ct_mark); + diff |= CT_DIFF_VAL(USE, ct_use); + diff |= CT_DIFF_VAL(ID, ct_id); + diff |= CT_DIFF_ADDR(ORIG_SRC, ct_orig.src); + diff |= CT_DIFF_ADDR(ORIG_DST, ct_orig.dst); + diff |= CT_DIFF_VAL(ORIG_SRC_PORT, ct_orig.proto.port.src); + diff |= CT_DIFF_VAL(ORIG_DST_PORT, ct_orig.proto.port.dst); + diff |= CT_DIFF_VAL(ORIG_ICMP_ID, ct_orig.proto.icmp.id); + diff |= CT_DIFF_VAL(ORIG_ICMP_TYPE, ct_orig.proto.icmp.type); + diff |= CT_DIFF_VAL(ORIG_ICMP_CODE, ct_orig.proto.icmp.code); + diff |= CT_DIFF_VAL(ORIG_PACKETS, ct_orig.packets); + diff |= CT_DIFF_VAL(ORIG_BYTES, ct_orig.bytes); + diff |= CT_DIFF_ADDR(REPL_SRC, ct_repl.src); + diff |= CT_DIFF_ADDR(ORIG_DST, ct_repl.dst); + diff |= CT_DIFF_VAL(REPL_SRC_PORT, ct_repl.proto.port.src); + diff |= CT_DIFF_VAL(REPL_DST_PORT, ct_repl.proto.port.dst); + diff |= CT_DIFF_VAL(REPL_ICMP_ID, ct_repl.proto.icmp.id); + diff |= CT_DIFF_VAL(REPL_ICMP_TYPE, ct_repl.proto.icmp.type); + diff |= CT_DIFF_VAL(REPL_ICMP_CODE, ct_repl.proto.icmp.code); + diff |= CT_DIFF_VAL(REPL_PACKETS, ct_repl.packets); + diff |= CT_DIFF_VAL(REPL_BYTES, ct_repl.bytes); + +#undef CT_DIFF +#undef CT_DIFF_VAL +#undef CT_DIFF_ADDR + + return diff; +} + +static struct trans_tbl ct_attrs[] = { + __ADD(CT_ATTR_FAMILY, family) + __ADD(CT_ATTR_PROTO, proto) + __ADD(CT_ATTR_TCP_STATE, tcpstate) + __ADD(CT_ATTR_STATUS, status) + __ADD(CT_ATTR_TIMEOUT, timeout) + __ADD(CT_ATTR_MARK, mark) + __ADD(CT_ATTR_USE, use) + __ADD(CT_ATTR_ID, id) + __ADD(CT_ATTR_ORIG_SRC, origsrc) + __ADD(CT_ATTR_ORIG_DST, origdst) + __ADD(CT_ATTR_ORIG_SRC_PORT, origsrcport) + __ADD(CT_ATTR_ORIG_DST_PORT, origdstport) + __ADD(CT_ATTR_ORIG_ICMP_ID, origicmpid) + __ADD(CT_ATTR_ORIG_ICMP_TYPE, origicmptype) + __ADD(CT_ATTR_ORIG_ICMP_CODE, origicmpcode) + __ADD(CT_ATTR_ORIG_PACKETS, origpackets) + __ADD(CT_ATTR_ORIG_BYTES, origbytes) + __ADD(CT_ATTR_REPL_SRC, replysrc) + __ADD(CT_ATTR_REPL_DST, replydst) + __ADD(CT_ATTR_REPL_SRC_PORT, replysrcport) + __ADD(CT_ATTR_REPL_DST_PORT, replydstport) + __ADD(CT_ATTR_REPL_ICMP_ID, replyicmpid) + __ADD(CT_ATTR_REPL_ICMP_TYPE, replyicmptype) + __ADD(CT_ATTR_REPL_ICMP_CODE, replyicmpcode) + __ADD(CT_ATTR_REPL_PACKETS, replypackets) + __ADD(CT_ATTR_REPL_BYTES, replybytes) +}; + +static char *ct_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, ct_attrs, ARRAY_SIZE(ct_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_ct *nfnl_ct_alloc(void) +{ + return (struct nfnl_ct *) nl_object_alloc(&ct_obj_ops); +} + +void nfnl_ct_get(struct nfnl_ct *ct) +{ + nl_object_get((struct nl_object *) ct); +} + +void nfnl_ct_put(struct nfnl_ct *ct) +{ + nl_object_put((struct nl_object *) ct); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_ct_set_family(struct nfnl_ct *ct, uint8_t family) +{ + ct->ct_family = family; + ct->ce_mask |= CT_ATTR_FAMILY; +} + +uint8_t nfnl_ct_get_family(const struct nfnl_ct *ct) +{ + if (ct->ce_mask & CT_ATTR_FAMILY) + return ct->ct_family; + else + return AF_UNSPEC; +} + +void nfnl_ct_set_proto(struct nfnl_ct *ct, uint8_t proto) +{ + ct->ct_proto = proto; + ct->ce_mask |= CT_ATTR_PROTO; +} + +int nfnl_ct_test_proto(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_PROTO); +} + +uint8_t nfnl_ct_get_proto(const struct nfnl_ct *ct) +{ + return ct->ct_proto; +} + +void nfnl_ct_set_tcp_state(struct nfnl_ct *ct, uint8_t state) +{ + ct->ct_protoinfo.tcp.state = state; + ct->ce_mask |= CT_ATTR_TCP_STATE; +} + +int nfnl_ct_test_tcp_state(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_TCP_STATE); +} + +uint8_t nfnl_ct_get_tcp_state(const struct nfnl_ct *ct) +{ + return ct->ct_protoinfo.tcp.state; +} + +static struct trans_tbl tcp_states[] = { + __ADD(TCP_CONNTRACK_NONE,NONE) + __ADD(TCP_CONNTRACK_SYN_SENT,SYN_SENT) + __ADD(TCP_CONNTRACK_SYN_RECV,SYN_RECV) + __ADD(TCP_CONNTRACK_ESTABLISHED,ESTABLISHED) + __ADD(TCP_CONNTRACK_FIN_WAIT,FIN_WAIT) + __ADD(TCP_CONNTRACK_CLOSE_WAIT,CLOSE_WAIT) + __ADD(TCP_CONNTRACK_LAST_ACK,LAST_ACK) + __ADD(TCP_CONNTRACK_TIME_WAIT,TIME_WAIT) + __ADD(TCP_CONNTRACK_CLOSE,CLOSE) + __ADD(TCP_CONNTRACK_LISTEN,LISTEN) +}; + +char *nfnl_ct_tcp_state2str(uint8_t state, char *buf, size_t len) +{ + return __type2str(state, buf, len, tcp_states, ARRAY_SIZE(tcp_states)); +} + +int nfnl_ct_str2tcp_state(const char *name) +{ + return __str2type(name, tcp_states, ARRAY_SIZE(tcp_states)); +} + +void nfnl_ct_set_status(struct nfnl_ct *ct, uint32_t status) +{ + ct->ct_status = status; + ct->ce_mask |= CT_ATTR_STATUS; +} + +int nfnl_ct_test_status(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_STATUS); +} + +uint32_t nfnl_ct_get_status(const struct nfnl_ct *ct) +{ + return ct->ct_status; +} + +void nfnl_ct_set_timeout(struct nfnl_ct *ct, uint32_t timeout) +{ + ct->ct_timeout = timeout; + ct->ce_mask |= CT_ATTR_TIMEOUT; +} + +int nfnl_ct_test_timeout(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_TIMEOUT); +} + +uint32_t nfnl_ct_get_timeout(const struct nfnl_ct *ct) +{ + return ct->ct_timeout; +} + +void nfnl_ct_set_mark(struct nfnl_ct *ct, uint32_t mark) +{ + ct->ct_mark = mark; + ct->ce_mask |= CT_ATTR_MARK; +} + +int nfnl_ct_test_mark(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_MARK); +} + +uint32_t nfnl_ct_get_mark(const struct nfnl_ct *ct) +{ + return ct->ct_mark; +} + +void nfnl_ct_set_use(struct nfnl_ct *ct, uint32_t use) +{ + ct->ct_use = use; + ct->ce_mask |= CT_ATTR_USE; +} + +int nfnl_ct_test_use(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_USE); +} + +uint32_t nfnl_ct_get_use(const struct nfnl_ct *ct) +{ + return ct->ct_use; +} + +void nfnl_ct_set_id(struct nfnl_ct *ct, uint32_t id) +{ + ct->ct_id = id; + ct->ce_mask |= CT_ATTR_ID; +} + +int nfnl_ct_test_id(const struct nfnl_ct *ct) +{ + return !!(ct->ce_mask & CT_ATTR_ID); +} + +uint32_t nfnl_ct_get_id(const struct nfnl_ct *ct) +{ + return ct->ct_id; +} + +static int ct_set_addr(struct nfnl_ct *ct, struct nl_addr *addr, + int attr, struct nl_addr ** ct_addr) +{ + if (ct->ce_mask & CT_ATTR_FAMILY) { + if (addr->a_family != ct->ct_family) + return nl_error(EINVAL, "Address family mismatch"); + } else + nfnl_ct_set_family(ct, addr->a_family); + + if (*ct_addr) + nl_addr_put(*ct_addr); + + nl_addr_get(addr); + *ct_addr = addr; + ct->ce_mask |= attr; + + return 0; +} + +int nfnl_ct_set_src(struct nfnl_ct *ct, int repl, struct nl_addr *addr) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_SRC : CT_ATTR_ORIG_SRC; + return ct_set_addr(ct, addr, attr, &dir->src); +} + +int nfnl_ct_set_dst(struct nfnl_ct *ct, int repl, struct nl_addr *addr) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_DST : CT_ATTR_ORIG_DST; + return ct_set_addr(ct, addr, attr, &dir->dst); +} + +struct nl_addr *nfnl_ct_get_src(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_SRC : CT_ATTR_ORIG_SRC; + if (!(ct->ce_mask & attr)) + return NULL; + return dir->src; +} + +struct nl_addr *nfnl_ct_get_dst(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_DST : CT_ATTR_ORIG_DST; + if (!(ct->ce_mask & attr)) + return NULL; + return dir->dst; +} + +void nfnl_ct_set_src_port(struct nfnl_ct *ct, int repl, uint16_t port) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_SRC_PORT : CT_ATTR_ORIG_SRC_PORT; + + dir->proto.port.src = port; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_src_port(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_SRC_PORT : CT_ATTR_ORIG_SRC_PORT; + return !!(ct->ce_mask & attr); +} + +uint16_t nfnl_ct_get_src_port(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.port.src; +} + +void nfnl_ct_set_dst_port(struct nfnl_ct *ct, int repl, uint16_t port) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_DST_PORT : CT_ATTR_ORIG_DST_PORT; + + dir->proto.port.dst = port; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_dst_port(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_DST_PORT : CT_ATTR_ORIG_DST_PORT; + return !!(ct->ce_mask & attr); +} + +uint16_t nfnl_ct_get_dst_port(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.port.dst; +} + +void nfnl_ct_set_icmp_id(struct nfnl_ct *ct, int repl, uint16_t id) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_ICMP_ID : CT_ATTR_ORIG_ICMP_ID; + + dir->proto.icmp.id = id; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_icmp_id(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_ICMP_ID : CT_ATTR_ORIG_ICMP_ID; + return !!(ct->ce_mask & attr); +} + +uint16_t nfnl_ct_get_icmp_id(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.icmp.id; +} + +void nfnl_ct_set_icmp_type(struct nfnl_ct *ct, int repl, uint8_t type) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_ICMP_TYPE : CT_ATTR_ORIG_ICMP_TYPE; + + dir->proto.icmp.type = type; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_icmp_type(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_ICMP_TYPE : CT_ATTR_ORIG_ICMP_TYPE; + return !!(ct->ce_mask & attr); +} + +uint8_t nfnl_ct_get_icmp_type(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.icmp.type; +} + +void nfnl_ct_set_icmp_code(struct nfnl_ct *ct, int repl, uint8_t code) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_ICMP_CODE : CT_ATTR_ORIG_ICMP_CODE; + + dir->proto.icmp.code = code; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_icmp_code(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_ICMP_CODE : CT_ATTR_ORIG_ICMP_CODE; + return !!(ct->ce_mask & attr); +} + +uint8_t nfnl_ct_get_icmp_code(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->proto.icmp.code; +} + +void nfnl_ct_set_packets(struct nfnl_ct *ct, int repl, uint64_t packets) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_PACKETS : CT_ATTR_ORIG_PACKETS; + + dir->packets = packets; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_packets(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_PACKETS : CT_ATTR_ORIG_PACKETS; + return !!(ct->ce_mask & attr); +} + +uint64_t nfnl_ct_get_packets(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->packets; +} + +void nfnl_ct_set_bytes(struct nfnl_ct *ct, int repl, uint64_t bytes) +{ + struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + int attr = repl ? CT_ATTR_REPL_BYTES : CT_ATTR_ORIG_BYTES; + + dir->bytes = bytes; + ct->ce_mask |= attr; +} + +int nfnl_ct_test_bytes(const struct nfnl_ct *ct, int repl) +{ + int attr = repl ? CT_ATTR_REPL_BYTES : CT_ATTR_ORIG_BYTES; + return !!(ct->ce_mask & attr); +} + +uint64_t nfnl_ct_get_bytes(const struct nfnl_ct *ct, int repl) +{ + const struct nfnl_ct_dir *dir = repl ? &ct->ct_repl : &ct->ct_orig; + + return dir->bytes; +} + +/** @} */ + +struct nl_object_ops ct_obj_ops = { + .oo_name = "netfilter/ct", + .oo_size = sizeof(struct nfnl_ct), + .oo_free_data = ct_free_data, + .oo_clone = ct_clone, + .oo_dump[NL_DUMP_BRIEF] = ct_dump, + .oo_dump[NL_DUMP_FULL] = ct_dump, + .oo_dump[NL_DUMP_STATS] = ct_dump, + .oo_compare = ct_compare, + .oo_attrs2str = ct_attrs2str, +}; + +/** @} */ diff --git a/lib/netfilter/log.c b/lib/netfilter/log.c new file mode 100644 index 0000000..ccc90a8 --- /dev/null +++ b/lib/netfilter/log.c @@ -0,0 +1,349 @@ +/* + * lib/netfilter/log.c Netfilter Log + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +/** + * @ingroup nfnl + * @defgroup log Log + * @brief + * @{ + */ + +#include +#include + +#include +#include +#include +#include + +static struct nl_cache_ops nfnl_log_ops; + +#if __BYTE_ORDER == __BIG_ENDIAN +static uint64_t ntohll(uint64_t x) +{ + return x; +} +#elif __BYTE_ORDER == __LITTLE_ENDIAN +static uint64_t ntohll(uint64_t x) +{ + return __bswap_64(x); +} +#endif + +static struct nla_policy log_policy[NFULA_MAX+1] = { + [NFULA_PACKET_HDR] = { + .minlen = sizeof(struct nfulnl_msg_packet_hdr) + }, + [NFULA_MARK] = { .type = NLA_U32 }, + [NFULA_TIMESTAMP] = { + .minlen = sizeof(struct nfulnl_msg_packet_timestamp) + }, + [NFULA_IFINDEX_INDEV] = { .type = NLA_U32 }, + [NFULA_IFINDEX_OUTDEV] = { .type = NLA_U32 }, + [NFULA_IFINDEX_PHYSINDEV] = { .type = NLA_U32 }, + [NFULA_IFINDEX_PHYSOUTDEV] = { .type = NLA_U32 }, + [NFULA_HWADDR] = { + .minlen = sizeof(struct nfulnl_msg_packet_hw) + }, + //[NFULA_PAYLOAD] + [NFULA_PREFIX] = { .type = NLA_STRING, }, + [NFULA_UID] = { .type = NLA_U32 }, + [NFULA_SEQ] = { .type = NLA_U32 }, + [NFULA_SEQ_GLOBAL] = { .type = NLA_U32 }, +}; + +struct nfnl_log *nfnlmsg_log_parse(struct nlmsghdr *nlh) +{ + struct nfnl_log *log; + struct nlattr *tb[NFULA_MAX+1]; + struct nlattr *attr; + int err; + + log = nfnl_log_alloc(); + if (!log) + return NULL; + + log->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, NFULA_MAX, + log_policy); + if (err < 0) + goto errout; + + nfnl_log_set_family(log, nfnlmsg_family(nlh)); + + attr = tb[NFULA_PACKET_HDR]; + if (attr) { + struct nfulnl_msg_packet_hdr *hdr = nla_data(attr); + + nfnl_log_set_hwproto(log, hdr->hw_protocol); + nfnl_log_set_hook(log, hdr->hook); + } + + attr = tb[NFULA_MARK]; + if (attr) + nfnl_log_set_mark(log, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_TIMESTAMP]; + if (attr) { + struct nfulnl_msg_packet_timestamp *timestamp = nla_data(attr); + struct timeval tv; + + tv.tv_sec = ntohll(timestamp->sec); + tv.tv_usec = ntohll(timestamp->usec); + nfnl_log_set_timestamp(log, &tv); + } + + attr = tb[NFULA_IFINDEX_INDEV]; + if (attr) + nfnl_log_set_indev(log, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_IFINDEX_OUTDEV]; + if (attr) + nfnl_log_set_outdev(log, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_IFINDEX_PHYSINDEV]; + if (attr) + nfnl_log_set_physindev(log, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_IFINDEX_PHYSOUTDEV]; + if (attr) + nfnl_log_set_physoutdev(log, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_HWADDR]; + if (attr) { + struct nfulnl_msg_packet_hw *hw = nla_data(attr); + + nfnl_log_set_hwaddr(log, hw->hw_addr, ntohs(hw->hw_addrlen)); + } + + attr = tb[NFULA_PAYLOAD]; + if (attr) { + err = nfnl_log_set_payload(log, nla_data(attr), nla_len(attr)); + if (err < 0) + goto errout; + } + + attr = tb[NFULA_PREFIX]; + if (attr) { + err = nfnl_log_set_prefix(log, nla_data(attr)); + if (err < 0) + goto errout; + } + + attr = tb[NFULA_UID]; + if (attr) + nfnl_log_set_uid(log, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_SEQ]; + if (attr) + nfnl_log_set_seq(log, ntohl(nla_get_u32(attr))); + + attr = tb[NFULA_SEQ_GLOBAL]; + if (attr) + nfnl_log_set_seq_global(log, ntohl(nla_get_u32(attr))); + + return log; + +errout: + nfnl_log_put(log); + return NULL; +} + +static int log_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, void *arg) +{ + struct nl_parser_param *pp = arg; + struct nfnl_log *log; + int err; + + log = nfnlmsg_log_parse(nlh); + if (log == NULL) + goto errout_errno; + + err = pp->pp_cb((struct nl_object *) log, pp); + if (err < 0) + goto errout; + + return P_ACCEPT; + +errout_errno: + err = nl_get_errno(); +errout: + nfnl_log_put(log); + return err; +} + +/** + * @name Log Commands + * @{ + */ + +static struct nl_msg *build_log_cmd_msg(uint8_t family, uint16_t queuenum, + uint8_t command) +{ + struct nl_msg *msg; + struct nfulnl_msg_config_cmd cmd; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_ULOG, NFULNL_MSG_CONFIG, 0, + family, queuenum); + if (msg == NULL) + return NULL; + + cmd.command = command; + if (nla_put(msg, NFULA_CFG_CMD, sizeof(cmd), &cmd) < 0) + goto nla_put_failure; + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +static int send_log_msg(struct nl_handle *handle, struct nl_msg *msg) +{ + int err; + + err = nl_send_auto_complete(handle, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(handle); +} + +struct nl_msg *nfnl_log_build_bind(uint16_t queuenum) +{ + return build_log_cmd_msg(0, queuenum, NFULNL_CFG_CMD_BIND); +} + +int nfnl_log_bind(struct nl_handle *nlh, uint16_t queuenum) +{ + struct nl_msg *msg; + + msg = nfnl_log_build_bind(queuenum); + if (!msg) + return nl_get_errno(); + + return send_log_msg(nlh, msg); +} + +struct nl_msg *nfnl_log_build_unbind(uint16_t queuenum) +{ + return build_log_cmd_msg(0, queuenum, NFULNL_CFG_CMD_UNBIND); +} + +int nfnl_log_unbind(struct nl_handle *nlh, uint16_t queuenum) +{ + struct nl_msg *msg; + + msg = nfnl_log_build_bind(queuenum); + if (!msg) + return nl_get_errno(); + + return send_log_msg(nlh, msg); +} + +struct nl_msg *nfnl_log_build_pf_bind(uint8_t pf) +{ + return build_log_cmd_msg(pf, 0, NFULNL_CFG_CMD_PF_BIND); +} + +int nfnl_log_pf_bind(struct nl_handle *nlh, uint8_t pf) +{ + struct nl_msg *msg; + + msg = nfnl_log_build_pf_bind(pf); + if (!msg) + return nl_get_errno(); + + return send_log_msg(nlh, msg); +} + +struct nl_msg *nfnl_log_build_pf_unbind(uint8_t pf) +{ + return build_log_cmd_msg(pf, 0, NFULNL_CFG_CMD_PF_UNBIND); +} + +int nfnl_log_pf_unbind(struct nl_handle *nlh, uint8_t pf) +{ + struct nl_msg *msg; + + msg = nfnl_log_build_pf_unbind(pf); + if (!msg) + return nl_get_errno(); + + return send_log_msg(nlh, msg); +} + +struct nl_msg *nfnl_log_build_mode(uint16_t queuenum, uint8_t copy_mode, + uint32_t copy_range) +{ + struct nl_msg *msg; + struct nfulnl_msg_config_mode mode; + + msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_ULOG, NFULNL_MSG_CONFIG, 0, + 0, queuenum); + if (msg == NULL) + return NULL; + + mode.copy_mode = copy_mode; + mode.copy_range = htonl(copy_range); + if (nla_put(msg, NFULA_CFG_MODE, sizeof(mode), &mode) < 0) + goto nla_put_failure; + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +int nfnl_log_set_mode(struct nl_handle *nlh, uint16_t queuenum, + uint8_t copy_mode, uint32_t copy_range) +{ + struct nl_msg *msg; + + msg = nfnl_log_build_mode(queuenum, copy_mode, copy_range); + if (!msg) + return nl_get_errno(); + return send_log_msg(nlh, msg); +} + +/** @} */ + +#define NFNLMSG_LOG_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_ULOG, (type)) +static struct nl_cache_ops nfnl_log_ops = { + .co_name = "netfilter/log", + .co_hdrsize = NFNL_HDRLEN, + .co_msgtypes = { + { NFNLMSG_LOG_TYPE(NFULNL_MSG_PACKET), NL_ACT_NEW, "new" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_NETFILTER, + .co_msg_parser = log_msg_parser, + .co_obj_ops = &log_obj_ops, +}; + +static void __init log_init(void) +{ + nl_cache_mngt_register(&nfnl_log_ops); +} + +static void __exit log_exit(void) +{ + nl_cache_mngt_unregister(&nfnl_log_ops); +} + +/** @} */ diff --git a/lib/netfilter/log_obj.c b/lib/netfilter/log_obj.c new file mode 100644 index 0000000..c3adc51 --- /dev/null +++ b/lib/netfilter/log_obj.c @@ -0,0 +1,425 @@ +/* + * lib/netfilter/log_obj.c Netfilter Log Object + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#include +#include +#include + +/** @cond SKIP */ +#define LOG_ATTR_FAMILY (1UL << 0) +#define LOG_ATTR_HWPROTO (1UL << 1) +#define LOG_ATTR_HOOK (1UL << 2) +#define LOG_ATTR_MARK (1UL << 3) +#define LOG_ATTR_TIMESTAMP (1UL << 4) +#define LOG_ATTR_INDEV (1UL << 5) +#define LOG_ATTR_OUTDEV (1UL << 6) +#define LOG_ATTR_PHYSINDEV (1UL << 7) +#define LOG_ATTR_PHYSOUTDEV (1UL << 8) +#define LOG_ATTR_HWADDR (1UL << 9) +#define LOG_ATTR_PAYLOAD (1UL << 10) +#define LOG_ATTR_PREFIX (1UL << 11) +#define LOG_ATTR_UID (1UL << 12) +#define LOG_ATTR_SEQ (1UL << 13) +#define LOG_ATTR_SEQ_GLOBAL (1UL << 14) +/** @endcond */ + +static void log_free_data(struct nl_object *c) +{ + struct nfnl_log *log = (struct nfnl_log *) c; + + if (log == NULL) + return; + + free(log->log_payload); + free(log->log_prefix); +} + +static int log_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct nfnl_log *dst = (struct nfnl_log *) _dst; + struct nfnl_log *src = (struct nfnl_log *) _src; + int err; + + if (src->log_payload) { + err = nfnl_log_set_payload(dst, src->log_payload, + src->log_payload_len); + if (err < 0) + goto errout; + } + + if (src->log_prefix) { + err = nfnl_log_set_prefix(dst, src->log_prefix); + if (err < 0) + goto errout; + } + + return 0; +errout: + return err; +} + +static int log_dump(struct nl_object *a, struct nl_dump_params *p) +{ + struct nfnl_log *log = (struct nfnl_log *) a; + struct nl_cache *link_cache; + char buf[64]; + + link_cache = nl_cache_mngt_require("route/link"); + + if (log->ce_mask & LOG_ATTR_PREFIX) + dp_dump(p, "%s", log->log_prefix); + + if (log->ce_mask & LOG_ATTR_INDEV) { + if (link_cache) + dp_dump(p, "IN=%s ", + rtnl_link_i2name(link_cache, log->log_indev, + buf, sizeof(buf))); + else + dp_dump(p, "IN=%d ", log->log_indev); + } + + if (log->ce_mask & LOG_ATTR_PHYSINDEV) { + if (link_cache) + dp_dump(p, "PHYSIN=%s ", + rtnl_link_i2name(link_cache, log->log_physindev, + buf, sizeof(buf))); + else + dp_dump(p, "IN=%d ", log->log_physindev); + } + + if (log->ce_mask & LOG_ATTR_OUTDEV) { + if (link_cache) + dp_dump(p, "OUT=%s ", + rtnl_link_i2name(link_cache, log->log_outdev, + buf, sizeof(buf))); + else + dp_dump(p, "OUT=%d ", log->log_outdev); + } + + if (log->ce_mask & LOG_ATTR_PHYSOUTDEV) { + if (link_cache) + dp_dump(p, "PHYSOUT=%s ", + rtnl_link_i2name(link_cache,log->log_physoutdev, + buf, sizeof(buf))); + else + dp_dump(p, "PHYSOUT=%d ", log->log_physoutdev); + } + + if (log->ce_mask & LOG_ATTR_HWADDR) { + int i; + + dp_dump(p, "MAC"); + for (i = 0; i < log->log_hwaddr_len; i++) + dp_dump(p, "%c%02x", i?':':'=', log->log_hwaddr[i]); + dp_dump(p, " "); + } + + /* FIXME: parse the payload to get iptables LOG compatible format */ + + if (log->ce_mask & LOG_ATTR_FAMILY) + dp_dump(p, "FAMILY=%s ", + nl_af2str(log->log_family, buf, sizeof(buf))); + + if (log->ce_mask & LOG_ATTR_HWPROTO) + dp_dump(p, "HWPROTO=%s ", + nl_ether_proto2str(ntohs(log->log_hwproto), + buf, sizeof(buf))); + + if (log->ce_mask & LOG_ATTR_HOOK) + dp_dump(p, "HOOK=%d ", log->log_hook); + + if (log->ce_mask & LOG_ATTR_MARK) + dp_dump(p, "MARK=%d ", log->log_mark); + + if (log->ce_mask & LOG_ATTR_PAYLOAD) + dp_dump(p, "PAYLOADLEN=%d ", log->log_payload_len); + + if (log->ce_mask & LOG_ATTR_SEQ) + dp_dump(p, "SEQ=%d ", log->log_seq); + + if (log->ce_mask & LOG_ATTR_SEQ_GLOBAL) + dp_dump(p, "SEQGLOBAL=%d ", log->log_seq_global); + + dp_dump(p, "\n"); + + return 1; +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct nfnl_log *nfnl_log_alloc(void) +{ + return (struct nfnl_log *) nl_object_alloc(&log_obj_ops); +} + +void nfnl_log_get(struct nfnl_log *log) +{ + nl_object_get((struct nl_object *) log); +} + +void nfnl_log_put(struct nfnl_log *log) +{ + nl_object_put((struct nl_object *) log); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void nfnl_log_set_family(struct nfnl_log *log, uint8_t family) +{ + log->log_family = family; + log->ce_mask |= LOG_ATTR_FAMILY; +} + +uint8_t nfnl_log_get_family(const struct nfnl_log *log) +{ + if (log->ce_mask & LOG_ATTR_FAMILY) + return log->log_family; + else + return AF_UNSPEC; +} + +void nfnl_log_set_hwproto(struct nfnl_log *log, uint16_t hwproto) +{ + log->log_hwproto = hwproto; + log->ce_mask |= LOG_ATTR_HWPROTO; +} + +int nfnl_log_test_hwproto(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_HWPROTO); +} + +uint16_t nfnl_log_get_hwproto(const struct nfnl_log *log) +{ + return log->log_hwproto; +} + +void nfnl_log_set_hook(struct nfnl_log *log, uint8_t hook) +{ + log->log_hook = hook; + log->ce_mask |= LOG_ATTR_HOOK; +} + +int nfnl_log_test_hook(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_HOOK); +} + +uint8_t nfnl_log_get_hook(const struct nfnl_log *log) +{ + return log->log_hook; +} + +void nfnl_log_set_mark(struct nfnl_log *log, uint32_t mark) +{ + log->log_mark = mark; + log->ce_mask |= LOG_ATTR_MARK; +} + +int nfnl_log_test_mark(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_MARK); +} + +uint32_t nfnl_log_get_mark(const struct nfnl_log *log) +{ + return log->log_mark; +} + +void nfnl_log_set_timestamp(struct nfnl_log *log, struct timeval *tv) +{ + log->log_timestamp.tv_sec = tv->tv_sec; + log->log_timestamp.tv_usec = tv->tv_usec; + log->ce_mask |= LOG_ATTR_TIMESTAMP; +} + +const struct timeval *nfnl_log_get_timestamp(const struct nfnl_log *log) +{ + if (!(log->ce_mask & LOG_ATTR_TIMESTAMP)) + return NULL; + return &log->log_timestamp; +} + +void nfnl_log_set_indev(struct nfnl_log *log, uint32_t indev) +{ + log->log_indev = indev; + log->ce_mask |= LOG_ATTR_INDEV; +} + +uint32_t nfnl_log_get_indev(const struct nfnl_log *log) +{ + return log->log_indev; +} + +void nfnl_log_set_outdev(struct nfnl_log *log, uint32_t outdev) +{ + log->log_outdev = outdev; + log->ce_mask |= LOG_ATTR_OUTDEV; +} + +uint32_t nfnl_log_get_outdev(const struct nfnl_log *log) +{ + return log->log_outdev; +} + +void nfnl_log_set_physindev(struct nfnl_log *log, uint32_t physindev) +{ + log->log_physindev = physindev; + log->ce_mask |= LOG_ATTR_PHYSINDEV; +} + +uint32_t nfnl_log_get_physindev(const struct nfnl_log *log) +{ + return log->log_physindev; +} + +void nfnl_log_set_physoutdev(struct nfnl_log *log, uint32_t physoutdev) +{ + log->log_physoutdev = physoutdev; + log->ce_mask |= LOG_ATTR_PHYSOUTDEV; +} + +uint32_t nfnl_log_get_physoutdev(const struct nfnl_log *log) +{ + return log->log_physoutdev; +} + +void nfnl_log_set_hwaddr(struct nfnl_log *log, uint8_t *hwaddr, int len) +{ + if (len > sizeof(log->log_hwaddr)) + len = sizeof(log->log_hwaddr); + log->log_hwaddr_len = len; + memcpy(log->log_hwaddr, hwaddr, len); + log->ce_mask |= LOG_ATTR_HWADDR; +} + +const uint8_t *nfnl_log_get_hwaddr(const struct nfnl_log *log, int *len) +{ + if (!(log->ce_mask & LOG_ATTR_HWADDR)) { + *len = 0; + return NULL; + } + + *len = log->log_hwaddr_len; + return log->log_hwaddr; +} + +int nfnl_log_set_payload(struct nfnl_log *log, uint8_t *payload, int len) +{ + free(log->log_payload); + log->log_payload = malloc(len); + if (!log->log_payload) + return nl_errno(ENOMEM); + + memcpy(log->log_payload, payload, len); + log->log_payload_len = len; + log->ce_mask |= LOG_ATTR_PAYLOAD; + return 0; +} + +const void *nfnl_log_get_payload(const struct nfnl_log *log, int *len) +{ + if (!(log->ce_mask & LOG_ATTR_PAYLOAD)) { + *len = 0; + return NULL; + } + + *len = log->log_payload_len; + return log->log_payload; +} + +int nfnl_log_set_prefix(struct nfnl_log *log, void *prefix) +{ + free(log->log_prefix); + log->log_prefix = strdup(prefix); + if (!log->log_prefix) + return nl_errno(ENOMEM); + + log->ce_mask |= LOG_ATTR_PREFIX; + return 0; +} + +const char *nfnl_log_get_prefix(const struct nfnl_log *log) +{ + return log->log_prefix; +} + +void nfnl_log_set_uid(struct nfnl_log *log, uint32_t uid) +{ + log->log_uid = uid; + log->ce_mask |= LOG_ATTR_UID; +} + +int nfnl_log_test_uid(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_UID); +} + +uint32_t nfnl_log_get_uid(const struct nfnl_log *log) +{ + return log->log_uid; +} + +void nfnl_log_set_seq(struct nfnl_log *log, uint32_t seq) +{ + log->log_seq = seq; + log->ce_mask |= LOG_ATTR_SEQ; +} + +int nfnl_log_test_seq(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_SEQ); +} + +uint32_t nfnl_log_get_seq(const struct nfnl_log *log) +{ + return log->log_seq; +} + +void nfnl_log_set_seq_global(struct nfnl_log *log, uint32_t seq_global) +{ + log->log_seq_global = seq_global; + log->ce_mask |= LOG_ATTR_SEQ_GLOBAL; +} + +int nfnl_log_test_seq_global(const struct nfnl_log *log) +{ + return !!(log->ce_mask & LOG_ATTR_SEQ_GLOBAL); +} + +uint32_t nfnl_log_get_seq_global(const struct nfnl_log *log) +{ + return log->log_seq_global; +} + +/** @} */ + +struct nl_object_ops log_obj_ops = { + .oo_name = "netfilter/log", + .oo_size = sizeof(struct nfnl_log), + .oo_free_data = log_free_data, + .oo_clone = log_clone, + .oo_dump[NL_DUMP_BRIEF] = log_dump, + .oo_dump[NL_DUMP_FULL] = log_dump, + .oo_dump[NL_DUMP_STATS] = log_dump, +}; + +/** @} */ diff --git a/lib/netfilter/nfnl.c b/lib/netfilter/nfnl.c new file mode 100644 index 0000000..554e234 --- /dev/null +++ b/lib/netfilter/nfnl.c @@ -0,0 +1,246 @@ +/* + * lib/netfilter/nfnl.c Netfilter Netlink + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +/** + * @ingroup nlfam + * @defgroup nfnl Netfilter Netlink + * + * @par Message Format + * @code + * <------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) ---> + * +----------------------------+- - -+- - - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | struct nlmsghdr | | | | + * +----------------------------+- - -+- - - - - - - - - - -+- - -+ + * @endcode + * @code + * <-------- NFNL_HDRLEN ---------> + * +--------------------------+- - -+------------+ + * | Netfilter Netlink Header | Pad | Attributes | + * | struct nfgenmsg | | | + * +--------------------------+- - -+------------+ + * nfnlmsg_attrdata(nfg, hdrlen)-----^ + * @endcode + * + * @par 1) Creating a new netfilter netlink message + * @code + * struct nl_msg *msg; + * + * // Create a new empty netlink message + * msg = nlmsg_alloc(); + * + * // Append the netlink and netfilter netlink message header + * hdr = nfnlmsg_put(msg, PID, SEQ, SUBSYS, TYPE, NLM_F_ECHO, + * FAMILY, RES_ID); + * + * // Append the attributes. + * nla_put_u32(msg, 1, 0x10); + * + * // Message is ready to be sent. + * nl_send_auto_complete(nl_handle, msg); + * + * // All done? Free the message. + * nlmsg_free(msg); + * @endcode + * + * @par 2) Sending of trivial messages + * @code + * // For trivial messages not requiring any subsys specific header or + * // attributes, nfnl_send_simple() may be used to send messages directly. + * nfnl_send_simple(nl_handle, SUBSYS, TYPE, 0, FAMILY, RES_ID); + * @endcode + * @{ + */ + +#include +#include +#include + +/** + * @name Socket Creating + * @{ + */ + +/** + * Create and connect netfilter netlink socket. + * @arg handle Netlink handle. + * + * Creates a NETLINK_NETFILTER netlink socket, binds the socket and + * issues a connection attempt. + * + * @see nl_connect() + * + * @return 0 on success or a negative error code. + */ +int nfnl_connect(struct nl_handle *handle) +{ + return nl_connect(handle, NETLINK_NETFILTER); +} + +/** @} */ + +/** + * @name Sending + * @{ + */ + +/** + * Send trivial netfilter netlink message + * @arg handle Netlink handle. + * @arg subsys_id nfnetlink subsystem + * @arg type nfnetlink message type + * @arg flags message flags + * @arg family nfnetlink address family + * @arg res_id nfnetlink resource id + * + * @return Newly allocated netlink message or NULL. + */ +int nfnl_send_simple(struct nl_handle *handle, uint8_t subsys_id, uint8_t type, + int flags, uint8_t family, uint16_t res_id) +{ + struct nfgenmsg hdr = { + .nfgen_family = family, + .version = NFNETLINK_V0, + .res_id = htons(res_id), + }; + + return nl_send_simple(handle, NFNLMSG_TYPE(subsys_id, type), flags, + &hdr, sizeof(hdr)); +} + +/** @} */ + +/** + * @name Message Parsing + * @{ + */ + +/** + * Get netfilter subsystem id from message + * @arg nlh netlink messsage header + */ +uint8_t nfnlmsg_subsys(struct nlmsghdr *nlh) +{ + return NFNL_SUBSYS_ID(nlh->nlmsg_type); +} + +/** + * Get netfilter message type from message + * @arg nlh netlink messsage header + */ +uint8_t nfnlmsg_subtype(struct nlmsghdr *nlh) +{ + return NFNL_MSG_TYPE(nlh->nlmsg_type); +} + +/** + * Get netfilter family from message + * @arg nlh netlink messsage header + */ +uint8_t nfnlmsg_family(struct nlmsghdr *nlh) +{ + struct nfgenmsg *nfg = nlmsg_data(nlh); + + return nfg->nfgen_family; +} + +/** + * Get netfilter resource id from message + * @arg nlh netlink messsage header + */ +uint16_t nfnlmsg_res_id(struct nlmsghdr *nlh) +{ + struct nfgenmsg *nfg = nlmsg_data(nlh); + + return ntohs(nfg->res_id); +} + +/** @} */ + +/** + * @name Message Building + * @{ + */ + +static int nfnlmsg_append(struct nl_msg *msg, uint8_t family, uint16_t res_id) +{ + struct nfgenmsg *nfg; + + nfg = nlmsg_reserve(msg, sizeof(*nfg), NLMSG_ALIGNTO); + if (nfg == NULL) + return nl_errno(ENOMEM); + + nfg->nfgen_family = family; + nfg->version = NFNETLINK_V0; + nfg->res_id = htons(res_id); + NL_DBG(2, "msg %p: Added nfnetlink header family=%d res_id=%d\n", + msg, family, res_id); + return 0; +} + +/** + * Allocate a new netfilter netlink message + * @arg subsys_id nfnetlink subsystem + * @arg type nfnetlink message type + * @arg flags message flags + * @arg family nfnetlink address family + * @arg res_id nfnetlink resource id + * + * @return Newly allocated netlink message or NULL. + */ +struct nl_msg *nfnlmsg_alloc_simple(uint8_t subsys_id, uint8_t type, int flags, + uint8_t family, uint16_t res_id) +{ + struct nl_msg *msg; + + msg = nlmsg_alloc_simple(NFNLMSG_TYPE(subsys_id, type), flags); + if (msg == NULL) + return NULL; + + if (nfnlmsg_append(msg, family, res_id) < 0) + goto nla_put_failure; + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +/** + * Add netlink and netfilter netlink headers to netlink message + * @arg msg netlink message + * @arg pid netlink process id + * @arg seq sequence number of message + * @arg subsys_id nfnetlink subsystem + * @arg type nfnetlink message type + * @arg flags message flags + * @arg family nfnetlink address family + * @arg res_id nfnetlink resource id + */ +int nfnlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, + uint8_t subsys_id, uint8_t type, int flags, uint8_t family, + uint16_t res_id) +{ + struct nlmsghdr *nlh; + + nlh = nlmsg_put(msg, pid, seq, NFNLMSG_TYPE(subsys_id, type), 0, flags); + if (nlh == NULL) + return nl_get_errno(); + + return nfnlmsg_append(msg, family, res_id); +} + +/** @} */ + +/** @} */ diff --git a/lib/nl.c b/lib/nl.c new file mode 100644 index 0000000..3866846 --- /dev/null +++ b/lib/nl.c @@ -0,0 +1,798 @@ +/* + * lib/nl.c Core Netlink Interface + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @defgroup nl Core Netlink API + * @brief + * + * @par Receiving Semantics + * @code + * nl_recvmsgs_default(socket) + * | + * | cb = nl_socket_get_cb(socket) + * v + * nl_recvmsgs(socket, cb) + * | [Application provides nl_recvmsgs() replacement] + * |- - - - - - - - - - - - - - - v + * | cb->cb_recvmsgs_ow() + * | + * | [Application provides nl_recv() replacement] + * +-------------->|- - - - - - - - - - - - - - - v + * | nl_recv() cb->cb_recv_ow() + * | +----------->|<- - - - - - - - - - - - - - -+ + * | | v + * | | Parse Message + * | | |- - - - - - - - - - - - - - - v + * | | | NL_CB_MSG_IN() + * | | |<- - - - - - - - - - - - - - -+ + * | | | + * | | |- - - - - - - - - - - - - - - v + * | | Sequence Check NL_CB_SEQ_CHECK() + * | | |<- - - - - - - - - - - - - - -+ + * | | | + * | | |- - - - - - - - - - - - - - - v [ NLM_F_ACK is set ] + * | | | NL_CB_SEND_ACK() + * | | |<- - - - - - - - - - - - - - -+ + * | | | + * | | +-----+------+--------------+----------------+--------------+ + * | | v v v v v + * | | Valid Message ACK NOOP Message End of Multipart Error Message + * | | | | | | | + * | | v v v v v + * | |NL_CB_VALID() NL_CB_ACK() NL_CB_SKIPPED() NL_CB_FINISH() cb->cb_err() + * | | | | | | | + * | | +------------+--------------+----------------+ v + * | | | (FAILURE) + * | | | [Callback returned NL_SKIP] + * | | [More messages to be parsed] |<----------- + * | +----------------------------------| + * | | + * | [Multipart message] | + * +-------------------------------------| [Callback returned NL_STOP] + * |<----------- + * v + * (SUCCESS) + * + * At any time: + * Message Format Error + * |- - - - - - - - - - - - v + * v NL_CB_INVALID() + * (FAILURE) + * + * Message Overrun (Kernel Lost Data) + * |- - - - - - - - - - - - v + * v NL_CB_OVERRUN() + * (FAILURE) + * + * Callback returned negative error code + * (FAILURE) + * @endcode + * + * @par Sending Semantics + * @code + * nl_send_auto_complete() + * | + * | Automatically fill in PID and/or sequence number + * | + * | [Application provides nl_send() replacement] + * |- - - - - - - - - - - - - - - - - - - - v + * v cb->cb_send_ow() + * nl_send() + * | Add destination address and credentials + * v + * nl_sendmsg() + * | Set source address + * | + * |- - - - - - - - - - - - - - - - - - - - v + * | NL_CB_MSG_OUT() + * |<- - - - - - - - - - - - - - - - - - - -+ + * v + * sendmsg() + * @endcode + * + * @par 1) Connecting the socket + * @code + * // Bind and connect the socket to a protocol, NETLINK_ROUTE in this example. + * nl_connect(handle, NETLINK_ROUTE); + * @endcode + * + * @par 2) Sending data + * @code + * // The most rudimentary method is to use nl_sendto() simply pushing + * // a piece of data to the other netlink peer. This method is not + * // recommended. + * const char buf[] = { 0x01, 0x02, 0x03, 0x04 }; + * nl_sendto(handle, buf, sizeof(buf)); + * + * // A more comfortable interface is nl_send() taking a pointer to + * // a netlink message. + * struct nl_msg *msg = my_msg_builder(); + * nl_send(handle, nlmsg_hdr(msg)); + * + * // nl_sendmsg() provides additional control over the sendmsg() message + * // header in order to allow more specific addressing of multiple peers etc. + * struct msghdr hdr = { ... }; + * nl_sendmsg(handle, nlmsg_hdr(msg), &hdr); + * + * // You're probably too lazy to fill out the netlink pid, sequence number + * // and message flags all the time. nl_send_auto_complete() automatically + * // extends your message header as needed with an appropriate sequence + * // number, the netlink pid stored in the netlink handle and the message + * // flags NLM_F_REQUEST and NLM_F_ACK + * nl_send_auto_complete(handle, nlmsg_hdr(msg)); + * + * // Simple protocols don't require the complex message construction interface + * // and may favour nl_send_simple() to easly send a bunch of payload + * // encapsulated in a netlink message header. + * nl_send_simple(handle, MY_MSG_TYPE, 0, buf, sizeof(buf)); + * @endcode + * + * @par 3) Receiving data + * @code + * // nl_recv() receives a single message allocating a buffer for the message + * // content and gives back the pointer to you. + * struct sockaddr_nl peer; + * unsigned char *msg; + * nl_recv(handle, &peer, &msg); + * + * // nl_recvmsgs() receives a bunch of messages until the callback system + * // orders it to state, usually after receving a compolete multi part + * // message series. + * nl_recvmsgs(handle, my_callback_configuration); + * + * // nl_recvmsgs_default() acts just like nl_recvmsg() but uses the callback + * // configuration stored in the handle. + * nl_recvmsgs_default(handle); + * + * // In case you want to wait for the ACK to be recieved that you requested + * // with your latest message, you can call nl_wait_for_ack() + * nl_wait_for_ack(handle); + * @endcode + * + * @par 4) Closing + * @code + * // Close the socket first to release kernel memory + * nl_close(handle); + * @endcode + * + * @{ + */ + +#include +#include +#include +#include +#include +#include + +/** + * @name Connection Management + * @{ + */ + +/** + * Create and connect netlink socket. + * @arg handle Netlink handle. + * @arg protocol Netlink protocol to use. + * + * Creates a netlink socket using the specified protocol, binds the socket + * and issues a connection attempt. + * + * @return 0 on success or a negative error code. + */ +int nl_connect(struct nl_handle *handle, int protocol) +{ + int err; + socklen_t addrlen; + + handle->h_fd = socket(AF_NETLINK, SOCK_RAW, protocol); + if (handle->h_fd < 0) + return nl_error(1, "socket(AF_NETLINK, ...) failed"); + + if (!(handle->h_flags & NL_SOCK_BUFSIZE_SET)) { + err = nl_set_buffer_size(handle, 0, 0); + if (err < 0) + return err; + } + + err = bind(handle->h_fd, (struct sockaddr*) &handle->h_local, + sizeof(handle->h_local)); + if (err < 0) + return nl_error(1, "bind() failed"); + + addrlen = sizeof(handle->h_local); + err = getsockname(handle->h_fd, (struct sockaddr *) &handle->h_local, + &addrlen); + if (err < 0) + return nl_error(1, "getsockname failed"); + + if (addrlen != sizeof(handle->h_local)) + return nl_error(EADDRNOTAVAIL, "Invalid address length"); + + if (handle->h_local.nl_family != AF_NETLINK) + return nl_error(EPFNOSUPPORT, "Address format not supported"); + + handle->h_proto = protocol; + + return 0; +} + +/** + * Close/Disconnect netlink socket. + * @arg handle Netlink handle + */ +void nl_close(struct nl_handle *handle) +{ + if (handle->h_fd >= 0) { + close(handle->h_fd); + handle->h_fd = -1; + } + + handle->h_proto = 0; +} + +/** @} */ + +/** + * @name Send + * @{ + */ + +/** + * Send raw data over netlink socket. + * @arg handle Netlink handle. + * @arg buf Data buffer. + * @arg size Size of data buffer. + * @return Number of characters written on success or a negative error code. + */ +int nl_sendto(struct nl_handle *handle, void *buf, size_t size) +{ + int ret; + + ret = sendto(handle->h_fd, buf, size, 0, (struct sockaddr *) + &handle->h_peer, sizeof(handle->h_peer)); + if (ret < 0) + return nl_errno(errno); + + return ret; +} + +/** + * Send netlink message with control over sendmsg() message header. + * @arg handle Netlink handle. + * @arg msg Netlink message to be sent. + * @arg hdr Sendmsg() message header. + * @return Number of characters sent on sucess or a negative error code. + */ +int nl_sendmsg(struct nl_handle *handle, struct nl_msg *msg, struct msghdr *hdr) +{ + struct nl_cb *cb; + int ret; + + struct iovec iov = { + .iov_base = (void *) nlmsg_hdr(msg), + .iov_len = nlmsg_hdr(msg)->nlmsg_len, + }; + + hdr->msg_iov = &iov; + hdr->msg_iovlen = 1; + + nlmsg_set_src(msg, &handle->h_local); + + cb = handle->h_cb; + if (cb->cb_set[NL_CB_MSG_OUT]) + if (nl_cb_call(cb, NL_CB_MSG_OUT, msg) != NL_OK) + return 0; + + ret = sendmsg(handle->h_fd, hdr, 0); + if (ret < 0) + return nl_errno(errno); + + return ret; +} + + +/** + * Send netlink message. + * @arg handle Netlink handle + * @arg msg Netlink message to be sent. + * @see nl_sendmsg() + * @return Number of characters sent on success or a negative error code. + */ +int nl_send(struct nl_handle *handle, struct nl_msg *msg) +{ + struct sockaddr_nl *dst; + struct ucred *creds; + + struct msghdr hdr = { + .msg_name = (void *) &handle->h_peer, + .msg_namelen = sizeof(struct sockaddr_nl), + }; + + /* Overwrite destination if specified in the message itself, defaults + * to the peer address of the handle. + */ + dst = nlmsg_get_dst(msg); + if (dst->nl_family == AF_NETLINK) + hdr.msg_name = dst; + + /* Add credentials if present. */ + creds = nlmsg_get_creds(msg); + if (creds != NULL) { + char buf[CMSG_SPACE(sizeof(struct ucred))]; + struct cmsghdr *cmsg; + + hdr.msg_control = buf; + hdr.msg_controllen = sizeof(buf); + + cmsg = CMSG_FIRSTHDR(&hdr); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + memcpy(CMSG_DATA(cmsg), creds, sizeof(struct ucred)); + } + + return nl_sendmsg(handle, msg, &hdr); +} + +/** + * Send netlink message and check & extend header values as needed. + * @arg handle Netlink handle. + * @arg msg Netlink message to be sent. + * + * Checks the netlink message \c nlh for completness and extends it + * as required before sending it out. Checked fields include pid, + * sequence nr, and flags. + * + * @see nl_send() + * @return Number of characters sent or a negative error code. + */ +int nl_send_auto_complete(struct nl_handle *handle, struct nl_msg *msg) +{ + struct nlmsghdr *nlh; + struct nl_cb *cb = handle->h_cb; + + nlh = nlmsg_hdr(msg); + if (nlh->nlmsg_pid == 0) + nlh->nlmsg_pid = handle->h_local.nl_pid; + + if (nlh->nlmsg_seq == 0) + nlh->nlmsg_seq = handle->h_seq_next++; + + if (msg->nm_protocol == -1) + msg->nm_protocol = handle->h_proto; + + nlh->nlmsg_flags |= (NLM_F_REQUEST | NLM_F_ACK); + + if (cb->cb_send_ow) + return cb->cb_send_ow(handle, msg); + else + return nl_send(handle, msg); +} + +/** + * Send simple netlink message using nl_send_auto_complete() + * @arg handle Netlink handle. + * @arg type Netlink message type. + * @arg flags Netlink message flags. + * @arg buf Data buffer. + * @arg size Size of data buffer. + * + * Builds a netlink message with the specified type and flags and + * appends the specified data as payload to the message. + * + * @see nl_send_auto_complete() + * @return Number of characters sent on success or a negative error code. + */ +int nl_send_simple(struct nl_handle *handle, int type, int flags, void *buf, + size_t size) +{ + int err; + struct nl_msg *msg; + + msg = nlmsg_alloc_simple(type, flags); + if (!msg) + return nl_errno(ENOMEM); + + if (buf && size) + nlmsg_append(msg, buf, size, NLMSG_ALIGNTO); + + err = nl_send_auto_complete(handle, msg); + nlmsg_free(msg); + + return err; +} + +/** @} */ + +/** + * @name Receive + * @{ + */ + +/** + * Receive data from netlink socket + * @arg handle Netlink handle. + * @arg nla Destination pointer for peer's netlink address. + * @arg buf Destination pointer for message content. + * @arg creds Destination pointer for credentials. + * + * Receives a netlink message, allocates a buffer in \c *buf and + * stores the message content. The peer's netlink address is stored + * in \c *nla. The caller is responsible for freeing the buffer allocated + * in \c *buf if a positive value is returned. Interruped system calls + * are handled by repeating the read. The input buffer size is determined + * by peeking before the actual read is done. + * + * A non-blocking sockets causes the function to return immediately with + * a return value of 0 if no data is available. + * + * @return Number of octets read, 0 on EOF or a negative error code. + */ +int nl_recv(struct nl_handle *handle, struct sockaddr_nl *nla, + unsigned char **buf, struct ucred **creds) +{ + int n; + int flags = 0; + static int page_size = 0; + struct iovec iov; + struct msghdr msg = { + .msg_name = (void *) nla, + .msg_namelen = sizeof(struct sockaddr_nl), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = NULL, + .msg_controllen = 0, + .msg_flags = 0, + }; + struct cmsghdr *cmsg; + + if (handle->h_flags & NL_MSG_PEEK) + flags |= MSG_PEEK; + + if (page_size == 0) + page_size = getpagesize(); + + iov.iov_len = page_size; + iov.iov_base = *buf = calloc(1, iov.iov_len); + + if (handle->h_flags & NL_SOCK_PASSCRED) { + msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); + msg.msg_control = calloc(1, msg.msg_controllen); + } +retry: + + n = recvmsg(handle->h_fd, &msg, flags); + if (!n) + goto abort; + else if (n < 0) { + if (errno == EINTR) { + NL_DBG(3, "recvmsg() returned EINTR, retrying\n"); + goto retry; + } else if (errno == EAGAIN) { + NL_DBG(3, "recvmsg() returned EAGAIN, aborting\n"); + goto abort; + } else { + free(msg.msg_control); + free(*buf); + return nl_error(errno, "recvmsg failed"); + } + } + + if (iov.iov_len < n || + msg.msg_flags & MSG_TRUNC) { + /* Provided buffer is not long enough, enlarge it + * and try again. */ + iov.iov_len *= 2; + iov.iov_base = *buf = realloc(*buf, iov.iov_len); + goto retry; + } else if (msg.msg_flags & MSG_CTRUNC) { + msg.msg_controllen *= 2; + msg.msg_control = realloc(msg.msg_control, msg.msg_controllen); + goto retry; + } else if (flags != 0) { + /* Buffer is big enough, do the actual reading */ + flags = 0; + goto retry; + } + + if (msg.msg_namelen != sizeof(struct sockaddr_nl)) { + free(msg.msg_control); + free(*buf); + return nl_error(EADDRNOTAVAIL, "socket address size mismatch"); + } + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_CREDENTIALS) { + *creds = calloc(1, sizeof(struct ucred)); + memcpy(*creds, CMSG_DATA(cmsg), sizeof(struct ucred)); + break; + } + } + + free(msg.msg_control); + return n; + +abort: + free(msg.msg_control); + free(*buf); + return 0; +} + +#define NL_CB_CALL(cb, type, msg) \ +do { \ + err = nl_cb_call(cb, type, msg); \ + switch (err) { \ + case NL_OK: \ + err = 0; \ + break; \ + case NL_SKIP: \ + goto skip; \ + case NL_STOP: \ + goto stop; \ + default: \ + goto out; \ + } \ +} while (0) + +static int recvmsgs(struct nl_handle *handle, struct nl_cb *cb) +{ + int n, err = 0, multipart = 0; + unsigned char *buf = NULL; + struct nlmsghdr *hdr; + struct sockaddr_nl nla = {0}; + struct nl_msg *msg = NULL; + struct ucred *creds = NULL; + +continue_reading: + NL_DBG(3, "Attempting to read from %p\n", handle); + if (cb->cb_recv_ow) + n = cb->cb_recv_ow(handle, &nla, &buf, &creds); + else + n = nl_recv(handle, &nla, &buf, &creds); + + if (n <= 0) + return n; + + NL_DBG(3, "recvmsgs(%p): Read %d bytes\n", handle, n); + + hdr = (struct nlmsghdr *) buf; + while (nlmsg_ok(hdr, n)) { + NL_DBG(3, "recgmsgs(%p): Processing valid message...\n", + handle); + + nlmsg_free(msg); + msg = nlmsg_convert(hdr); + if (!msg) { + err = nl_errno(ENOMEM); + goto out; + } + + nlmsg_set_proto(msg, handle->h_proto); + nlmsg_set_src(msg, &nla); + if (creds) + nlmsg_set_creds(msg, creds); + + /* Raw callback is the first, it gives the most control + * to the user and he can do his very own parsing. */ + if (cb->cb_set[NL_CB_MSG_IN]) + NL_CB_CALL(cb, NL_CB_MSG_IN, msg); + + /* Sequence number checking. The check may be done by + * the user, otherwise a very simple check is applied + * enforcing strict ordering */ + if (cb->cb_set[NL_CB_SEQ_CHECK]) + NL_CB_CALL(cb, NL_CB_SEQ_CHECK, msg); + else if (hdr->nlmsg_seq != handle->h_seq_expect) { + if (cb->cb_set[NL_CB_INVALID]) + NL_CB_CALL(cb, NL_CB_INVALID, msg); + else { + err = nl_error(EINVAL, + "Sequence number mismatch"); + goto out; + } + } + + if (hdr->nlmsg_type == NLMSG_DONE || + hdr->nlmsg_type == NLMSG_ERROR || + hdr->nlmsg_type == NLMSG_NOOP || + hdr->nlmsg_type == NLMSG_OVERRUN) { + /* We can't check for !NLM_F_MULTI since some netlink + * users in the kernel are broken. */ + handle->h_seq_expect++; + NL_DBG(3, "recvmsgs(%p): Increased expected " \ + "sequence number to %d\n", + handle, handle->h_seq_expect); + } + + if (hdr->nlmsg_flags & NLM_F_MULTI) + multipart = 1; + + /* Other side wishes to see an ack for this message */ + if (hdr->nlmsg_flags & NLM_F_ACK) { + if (cb->cb_set[NL_CB_SEND_ACK]) + NL_CB_CALL(cb, NL_CB_SEND_ACK, msg); + else { + /* FIXME: implement */ + } + } + + /* messages terminates a multpart message, this is + * usually the end of a message and therefore we slip + * out of the loop by default. the user may overrule + * this action by skipping this packet. */ + if (hdr->nlmsg_type == NLMSG_DONE) { + multipart = 0; + if (cb->cb_set[NL_CB_FINISH]) + NL_CB_CALL(cb, NL_CB_FINISH, msg); + } + + /* Message to be ignored, the default action is to + * skip this message if no callback is specified. The + * user may overrule this action by returning + * NL_PROCEED. */ + else if (hdr->nlmsg_type == NLMSG_NOOP) { + if (cb->cb_set[NL_CB_SKIPPED]) + NL_CB_CALL(cb, NL_CB_SKIPPED, msg); + else + goto skip; + } + + /* Data got lost, report back to user. The default action is to + * quit parsing. The user may overrule this action by retuning + * NL_SKIP or NL_PROCEED (dangerous) */ + else if (hdr->nlmsg_type == NLMSG_OVERRUN) { + if (cb->cb_set[NL_CB_OVERRUN]) + NL_CB_CALL(cb, NL_CB_OVERRUN, msg); + else { + err = nl_error(EOVERFLOW, "Overrun"); + goto out; + } + } + + /* Message carries a nlmsgerr */ + else if (hdr->nlmsg_type == NLMSG_ERROR) { + struct nlmsgerr *e = nlmsg_data(hdr); + + if (hdr->nlmsg_len < nlmsg_msg_size(sizeof(*e))) { + /* Truncated error message, the default action + * is to stop parsing. The user may overrule + * this action by returning NL_SKIP or + * NL_PROCEED (dangerous) */ + if (cb->cb_set[NL_CB_INVALID]) + NL_CB_CALL(cb, NL_CB_INVALID, msg); + else { + err = nl_error(EINVAL, + "Truncated error message"); + goto out; + } + } else if (e->error) { + /* Error message reported back from kernel. */ + if (cb->cb_err) { + err = cb->cb_err(&nla, e, + cb->cb_err_arg); + if (err < 0) + goto out; + else if (err == NL_SKIP) + goto skip; + else if (err == NL_STOP) { + err = nl_error(-e->error, + "Netlink Error"); + goto out; + } + } else { + err = nl_error(-e->error, + "Netlink Error"); + goto out; + } + } else if (cb->cb_set[NL_CB_ACK]) + NL_CB_CALL(cb, NL_CB_ACK, msg); + } else { + /* Valid message (not checking for MULTIPART bit to + * get along with broken kernels. NL_SKIP has no + * effect on this. */ + if (cb->cb_set[NL_CB_VALID]) + NL_CB_CALL(cb, NL_CB_VALID, msg); + } +skip: + err = 0; + hdr = nlmsg_next(hdr, &n); + } + + nlmsg_free(msg); + free(buf); + free(creds); + buf = NULL; + msg = NULL; + creds = NULL; + + if (multipart) { + /* Multipart message not yet complete, continue reading */ + goto continue_reading; + } +stop: + err = 0; +out: + nlmsg_free(msg); + free(buf); + free(creds); + + return err; +} + +/** + * Receive a set of messages from a netlink socket. + * @arg handle netlink handle + * @arg cb set of callbacks to control behaviour. + * + * Repeatedly calls nl_recv() or the respective replacement if provided + * by the application (see nl_cb_overwrite_recv()) and parses the + * received data as netlink messages. Stops reading if one of the + * callbacks returns NL_STOP or nl_recv returns either 0 or a negative error code. + * + * A non-blocking sockets causes the function to return immediately if + * no data is available. + * + * @return 0 on success or a negative error code from nl_recv(). + */ +int nl_recvmsgs(struct nl_handle *handle, struct nl_cb *cb) +{ + if (cb->cb_recvmsgs_ow) + return cb->cb_recvmsgs_ow(handle, cb); + else + return recvmsgs(handle, cb); +} + +/** + * Receive a set of message from a netlink socket using handlers in nl_handle. + * @arg handle netlink handle + * + * Calls nl_recvmsgs() with the handlers configured in the netlink handle. + */ +int nl_recvmsgs_default(struct nl_handle *handle) +{ + return nl_recvmsgs(handle, handle->h_cb); + +} + +static int ack_wait_handler(struct nl_msg *msg, void *arg) +{ + return NL_STOP; +} + +/** + * Wait for ACK. + * @arg handle netlink handle + * @pre The netlink socket must be in blocking state. + * + * Waits until an ACK is received for the latest not yet acknowledged + * netlink message. + */ +int nl_wait_for_ack(struct nl_handle *handle) +{ + int err; + struct nl_cb *cb; + + cb = nl_cb_clone(handle->h_cb); + if (cb == NULL) + return nl_get_errno(); + + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, NULL); + err = nl_recvmsgs(handle, cb); + nl_cb_put(cb); + + return err; +} + +/** @} */ + +/** @} */ diff --git a/lib/object.c b/lib/object.c new file mode 100644 index 0000000..74f6e2d --- /dev/null +++ b/lib/object.c @@ -0,0 +1,386 @@ +/* + * lib/object.c Generic Cacheable Object + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup cache + * @defgroup object Object + * @{ + */ + +#include +#include +#include +#include +#include + +static inline struct nl_object_ops *obj_ops(struct nl_object *obj) +{ + if (!obj->ce_ops) + BUG(); + + return obj->ce_ops; +} + +/** + * @name Object Creation/Deletion + * @{ + */ + +/** + * Allocate a new object of kind specified by the operations handle + * @arg ops cache operations handle + * @return The new object or NULL + */ +struct nl_object *nl_object_alloc(struct nl_object_ops *ops) +{ + struct nl_object *new; + + if (ops->oo_size < sizeof(*new)) + BUG(); + + new = calloc(1, ops->oo_size); + if (!new) { + nl_errno(ENOMEM); + return NULL; + } + + new->ce_refcnt = 1; + nl_init_list_head(&new->ce_list); + + new->ce_ops = ops; + if (ops->oo_constructor) + ops->oo_constructor(new); + + NL_DBG(4, "Allocated new object %p\n", new); + + return new; +} + +/** + * Allocate a new object of kind specified by the name + * @arg kind name of object type + * @return The new object or nULL + */ +struct nl_object *nl_object_alloc_name(const char *kind) +{ + struct nl_cache_ops *ops; + + ops = nl_cache_ops_lookup(kind); + if (!ops) { + nl_error(ENOENT, "Unable to lookup cache kind \"%s\"", kind); + return NULL; + } + + return nl_object_alloc(ops->co_obj_ops); +} + +struct nl_derived_object { + NLHDR_COMMON + char data; +}; + +/** + * Allocate a new object and copy all data from an existing object + * @arg obj object to inherite data from + * @return The new object or NULL. + */ +struct nl_object *nl_object_clone(struct nl_object *obj) +{ + struct nl_object *new; + struct nl_object_ops *ops = obj_ops(obj); + int doff = offsetof(struct nl_derived_object, data); + int size; + + new = nl_object_alloc(ops); + if (!new) + return NULL; + + size = ops->oo_size - doff; + if (size < 0) + BUG(); + + new->ce_ops = obj->ce_ops; + new->ce_msgtype = obj->ce_msgtype; + + if (size) + memcpy((void *)new + doff, (void *)obj + doff, size); + + if (ops->oo_clone) { + if (ops->oo_clone(new, obj) < 0) { + nl_object_free(new); + return NULL; + } + } else if (size && ops->oo_free_data) + BUG(); + + return new; +} + +/** + * Free a cacheable object + * @arg obj object to free + * + * @return 0 or a negative error code. + */ +void nl_object_free(struct nl_object *obj) +{ + struct nl_object_ops *ops = obj_ops(obj); + + if (obj->ce_refcnt > 0) + NL_DBG(1, "Warning: Freeing object in use...\n"); + + if (obj->ce_cache) + nl_cache_remove(obj); + + if (ops->oo_free_data) + ops->oo_free_data(obj); + + free(obj); + + NL_DBG(4, "Freed object %p\n", obj); +} + +/** @} */ + +/** + * @name Reference Management + * @{ + */ + +/** + * Acquire a reference on a object + * @arg obj object to acquire reference from + */ +void nl_object_get(struct nl_object *obj) +{ + obj->ce_refcnt++; + NL_DBG(4, "New reference to object %p, total %d\n", + obj, obj->ce_refcnt); +} + +/** + * Release a reference from an object + * @arg obj object to release reference from + */ +void nl_object_put(struct nl_object *obj) +{ + if (!obj) + return; + + obj->ce_refcnt--; + NL_DBG(4, "Returned object reference %p, %d remaining\n", + obj, obj->ce_refcnt); + + if (obj->ce_refcnt < 0) + BUG(); + + if (obj->ce_refcnt <= 0) + nl_object_free(obj); +} + +/** + * Check whether this object is used by multiple users + * @arg obj object to check + * @return true or false + */ +int nl_object_shared(struct nl_object *obj) +{ + return obj->ce_refcnt > 1; +} + +/** @} */ + +/** + * @name Marks + * @{ + */ + +/** + * Add mark to object + * @arg obj Object to mark + */ +void nl_object_mark(struct nl_object *obj) +{ + obj->ce_flags |= NL_OBJ_MARK; +} + +/** + * Remove mark from object + * @arg obj Object to unmark + */ +void nl_object_unmark(struct nl_object *obj) +{ + obj->ce_flags &= ~NL_OBJ_MARK; +} + +/** + * Return true if object is marked + * @arg obj Object to check + * @return true if object is marked, otherwise false + */ +int nl_object_is_marked(struct nl_object *obj) +{ + return (obj->ce_flags & NL_OBJ_MARK); +} + +/** @} */ + +/** + * @name Utillities + * @{ + */ + +/** + * Dump this object according to the specified parameters + * @arg obj object to dump + * @arg params dumping parameters + */ +void nl_object_dump(struct nl_object *obj, struct nl_dump_params *params) +{ + dump_from_ops(obj, params); +} + +/** + * Check if the identifiers of two objects are identical + * @arg a an object + * @arg b another object of same type + * + * @return true if both objects have equal identifiers, otherwise false. + */ +int nl_object_identical(struct nl_object *a, struct nl_object *b) +{ + struct nl_object_ops *ops = obj_ops(a); + int req_attrs; + + /* Both objects must be of same type */ + if (ops != obj_ops(b)) + return 0; + + req_attrs = ops->oo_id_attrs; + + /* Both objects must provide all required attributes to uniquely + * identify an object */ + if ((a->ce_mask & req_attrs) != req_attrs || + (b->ce_mask & req_attrs) != req_attrs) + return 0; + + /* Can't judge unless we can compare */ + if (ops->oo_compare == NULL) + return 0; + + return !(ops->oo_compare(a, b, req_attrs, 0)); +} + +/** + * Compute bitmask representing difference in attribute values + * @arg a an object + * @arg b another object of same type + * + * The bitmask returned is specific to an object type, each bit set represents + * an attribute which mismatches in either of the two objects. Unavailability + * of an attribute in one object and presence in the other is regarded a + * mismatch as well. + * + * @return Bitmask describing differences or 0 if they are completely identical. + */ +uint32_t nl_object_diff(struct nl_object *a, struct nl_object *b) +{ + struct nl_object_ops *ops = obj_ops(a); + + if (ops != obj_ops(b) || ops->oo_compare == NULL) + return UINT_MAX; + + return ops->oo_compare(a, b, ~0, 0); +} + +/** + * Match a filter against an object + * @arg obj object to check + * @arg filter object of same type acting as filter + * + * @return 1 if the object matches the filter or 0 + * if no filter procedure is available or if the + * filter does not match. + */ +int nl_object_match_filter(struct nl_object *obj, struct nl_object *filter) +{ + struct nl_object_ops *ops = obj_ops(obj); + + if (ops != obj_ops(filter) || ops->oo_compare == NULL) + return 0; + + return !(ops->oo_compare(obj, filter, filter->ce_mask, + LOOSE_FLAG_COMPARISON)); +} + +/** + * Convert bitmask of attributes to a character string + * @arg obj object of same type as attribute bitmask + * @arg attrs bitmask of attribute types + * @arg buf destination buffer + * @arg len length of destination buffer + * + * Converts the bitmask of attribute types into a list of attribute + * names separated by comas. + * + * @return destination buffer. + */ +char *nl_object_attrs2str(struct nl_object *obj, uint32_t attrs, + char *buf, size_t len) +{ + struct nl_object_ops *ops = obj_ops(obj); + + if (ops->oo_attrs2str != NULL) + return ops->oo_attrs2str(attrs, buf, len); + else { + memset(buf, 0, len); + return buf; + } +} + +/** + * Return list of attributes present in an object + * @arg obj an object + * @arg buf destination buffer + * @arg len length of destination buffer + * + * @return destination buffer. + */ +char *nl_object_attr_list(struct nl_object *obj, char *buf, size_t len) +{ + return nl_object_attrs2str(obj, obj->ce_mask, buf, len); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +int nl_object_get_refcnt(struct nl_object *obj) +{ + return obj->ce_refcnt; +} + +struct nl_cache *nl_object_get_cache(struct nl_object *obj) +{ + return obj->ce_cache; +} + +inline void *nl_object_priv(struct nl_object *obj) +{ + return obj; +} + +/** @} */ + +/** @} */ diff --git a/lib/route/addr.c b/lib/route/addr.c new file mode 100644 index 0000000..1947b5a --- /dev/null +++ b/lib/route/addr.c @@ -0,0 +1,1130 @@ +/* + * lib/route/addr.c Addresses + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Baruch Even , + * Mediatrix Telecom, inc. + */ + +/** + * @ingroup rtnl + * @defgroup rtaddr Addresses + * @brief + * + * @note The maximum size of an address label is IFNAMSIZ. + * + * @note The address may not contain a prefix length if the peer address + * has been specified already. + * + * @par 1) Address Addition + * @code + * // Allocate an empty address object to be filled out with the attributes + * // of the new address. + * struct rtnl_addr *addr = rtnl_addr_alloc(); + * + * // Fill out the mandatory attributes of the new address. Setting the + * // local address will automatically set the address family and the + * // prefix length to the correct values. + * rtnl_addr_set_ifindex(addr, ifindex); + * rtnl_addr_set_local(addr, local_addr); + * + * // The label of the address can be specified, currently only supported + * // by IPv4 and DECnet. + * rtnl_addr_set_label(addr, "mylabel"); + * + * // The peer address can be specified if necessary, in either case a peer + * // address will be sent to the kernel in order to fullfil the interface + * // requirements. If none is set, it will equal the local address. + * // Note: Real peer addresses are only supported by IPv4 for now. + * rtnl_addr_set_peer(addr, peer_addr); + * + * // In case you want to have the address have a scope other than global + * // it may be overwritten using rtnl_addr_set_scope(). The scope currently + * // cannot be set for IPv6 addresses. + * rtnl_addr_set_scope(addr, rtnl_str2scope("site")); + * + * // Broadcast and anycast address may be specified using the relevant + * // functions, the address family will be verified if one of the other + * // addresses has been set already. Currently only works for IPv4. + * rtnl_addr_set_broadcast(addr, broadcast_addr); + * rtnl_addr_set_anycast(addr, anycast_addr); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_addr_build_add_request() to be + * // sent out using nl_send_auto_complete(). + * rtnl_addr_add(handle, addr, 0); + * + * // Free the memory + * rtnl_addr_put(addr); + * @endcode + * + * @par 2) Address Deletion + * @code + * // Allocate an empty address object to be filled out with the attributes + * // matching the address to be deleted. Alternatively a fully equipped + * // address object out of a cache can be used instead. + * struct rtnl_addr *addr = rtnl_addr_alloc(); + * + * // The only mandatory parameter besides the address family is the interface + * // index the address is on, i.e. leaving out all other parameters will + * // result in all addresses of the specified address family interface tuple + * // to be deleted. + * rtnl_addr_set_ifindex(addr, ifindex); + * + * // Specyfing the address family manually is only required if neither the + * // local nor peer address have been specified. + * rtnl_addr_set_family(addr, AF_INET); + * + * // Specyfing the local address is optional but the best choice to delete + * // specific addresses. + * rtnl_addr_set_local(addr, local_addr); + * + * // The label of the address can be specified, currently only supported + * // by IPv4 and DECnet. + * rtnl_addr_set_label(addr, "mylabel"); + * + * // The peer address can be specified if necessary, in either case a peer + * // address will be sent to the kernel in order to fullfil the interface + * // requirements. If none is set, it will equal the local address. + * // Note: Real peer addresses are only supported by IPv4 for now. + * rtnl_addr_set_peer(addr, peer_addr); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_addr_build_delete_request() + * // to be sent out using nl_send_auto_complete(). + * rtnl_addr_delete(handle, addr, 0); + * + * // Free the memory + * rtnl_addr_put(addr); + * @endcode + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define ADDR_ATTR_FAMILY 0x0001 +#define ADDR_ATTR_PREFIXLEN 0x0002 +#define ADDR_ATTR_FLAGS 0x0004 +#define ADDR_ATTR_SCOPE 0x0008 +#define ADDR_ATTR_IFINDEX 0x0010 +#define ADDR_ATTR_LABEL 0x0020 +#define ADDR_ATTR_CACHEINFO 0x0040 +#define ADDR_ATTR_PEER 0x0080 +#define ADDR_ATTR_LOCAL 0x0100 +#define ADDR_ATTR_BROADCAST 0x0200 +#define ADDR_ATTR_ANYCAST 0x0400 +#define ADDR_ATTR_MULTICAST 0x0800 + +static struct nl_cache_ops rtnl_addr_ops; +static struct nl_object_ops addr_obj_ops; +/** @endcond */ + +static void addr_free_data(struct nl_object *obj) +{ + struct rtnl_addr *addr = nl_object_priv(obj); + + if (!addr) + return; + + nl_addr_put(addr->a_peer); + nl_addr_put(addr->a_local); + nl_addr_put(addr->a_bcast); + nl_addr_put(addr->a_anycast); + nl_addr_put(addr->a_multicast); +} + +static int addr_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_addr *dst = nl_object_priv(_dst); + struct rtnl_addr *src = nl_object_priv(_src); + + if (src->a_peer) + if (!(dst->a_peer = nl_addr_clone(src->a_peer))) + goto errout; + + if (src->a_local) + if (!(dst->a_local = nl_addr_clone(src->a_local))) + goto errout; + + if (src->a_bcast) + if (!(dst->a_bcast = nl_addr_clone(src->a_bcast))) + goto errout; + + if (src->a_anycast) + if (!(dst->a_anycast = nl_addr_clone(src->a_anycast))) + goto errout; + + if (src->a_multicast) + if (!(dst->a_multicast = nl_addr_clone(src->a_multicast))) + goto errout; + + return 0; +errout: + return nl_get_errno(); +} + +static struct nla_policy addr_policy[IFA_MAX+1] = { + [IFA_LABEL] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [IFA_CACHEINFO] = { .minlen = sizeof(struct ifa_cacheinfo) }, +}; + +static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, void *arg) +{ + struct rtnl_addr *addr; + struct nl_parser_param *pp = arg; + struct ifaddrmsg *ifa; + struct nlattr *tb[IFA_MAX+1]; + int err = -ENOMEM, peer_prefix = 0; + + addr = rtnl_addr_alloc(); + if (!addr) { + err = nl_errno(ENOMEM); + goto errout; + } + addr->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(*ifa), tb, IFA_MAX, addr_policy); + if (err < 0) + goto errout_free; + + ifa = nlmsg_data(nlh); + addr->a_family = ifa->ifa_family; + addr->a_prefixlen = ifa->ifa_prefixlen; + addr->a_flags = ifa->ifa_flags; + addr->a_scope = ifa->ifa_scope; + addr->a_ifindex = ifa->ifa_index; + + addr->ce_mask = (ADDR_ATTR_FAMILY | ADDR_ATTR_PREFIXLEN | + ADDR_ATTR_FLAGS | ADDR_ATTR_SCOPE | ADDR_ATTR_IFINDEX); + + if (tb[IFA_LABEL]) { + nla_strlcpy(addr->a_label, tb[IFA_LABEL], IFNAMSIZ); + addr->ce_mask |= ADDR_ATTR_LABEL; + } + + if (tb[IFA_CACHEINFO]) { + struct ifa_cacheinfo *ca; + + ca = nla_data(tb[IFA_CACHEINFO]); + addr->a_cacheinfo.aci_prefered = ca->ifa_prefered; + addr->a_cacheinfo.aci_valid = ca->ifa_valid; + addr->a_cacheinfo.aci_cstamp = ca->cstamp; + addr->a_cacheinfo.aci_tstamp = ca->tstamp; + addr->ce_mask |= ADDR_ATTR_CACHEINFO; + } + + if (tb[IFA_LOCAL]) { + addr->a_local = nla_get_addr(tb[IFA_LOCAL], addr->a_family); + if (!addr->a_local) + goto errout_free; + addr->ce_mask |= ADDR_ATTR_LOCAL; + } + + if (tb[IFA_ADDRESS]) { + struct nl_addr *a; + + a = nla_get_addr(tb[IFA_ADDRESS], addr->a_family); + if (!a) + goto errout_free; + + /* IPv6 sends the local address as IFA_ADDRESS with + * no IFA_LOCAL, IPv4 sends both IFA_LOCAL and IFA_ADDRESS + * with IFA_ADDRESS being the peer address if they differ */ + if (!tb[IFA_LOCAL] || !nl_addr_cmp(a, addr->a_local)) { + nl_addr_put(addr->a_local); + addr->a_local = a; + addr->ce_mask |= ADDR_ATTR_LOCAL; + } else { + addr->a_peer = a; + addr->ce_mask |= ADDR_ATTR_PEER; + peer_prefix = 1; + } + } + + nl_addr_set_prefixlen(peer_prefix ? addr->a_peer : addr->a_local, + addr->a_prefixlen); + + if (tb[IFA_BROADCAST]) { + addr->a_bcast = nla_get_addr(tb[IFA_BROADCAST], addr->a_family); + if (!addr->a_bcast) + goto errout_free; + + addr->ce_mask |= ADDR_ATTR_BROADCAST; + } + + if (tb[IFA_ANYCAST]) { + addr->a_anycast = nla_get_addr(tb[IFA_ANYCAST], addr->a_family); + if (!addr->a_anycast) + goto errout_free; + + addr->ce_mask |= ADDR_ATTR_ANYCAST; + } + + if (tb[IFA_MULTICAST]) { + addr->a_multicast = nla_get_addr(tb[IFA_MULTICAST], + addr->a_family); + if (!addr->a_multicast) + goto errout_free; + + addr->ce_mask |= ADDR_ATTR_MULTICAST; + } + + err = pp->pp_cb((struct nl_object *) addr, pp); + if (err < 0) + goto errout_free; + + return P_ACCEPT; + +errout_free: + rtnl_addr_put(addr); +errout: + return err; +} + +static int addr_request_update(struct nl_cache *cache, struct nl_handle *handle) +{ + return nl_rtgen_request(handle, RTM_GETADDR, AF_UNSPEC, NLM_F_DUMP); +} + +static int addr_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_addr *addr = (struct rtnl_addr *) obj; + struct nl_cache *link_cache; + char buf[128]; + + link_cache = nl_cache_mngt_require("route/link"); + + if (addr->ce_mask & ADDR_ATTR_LOCAL) + dp_dump(p, "%s", + nl_addr2str(addr->a_local, buf, sizeof(buf))); + else + dp_dump(p, "none"); + + if (addr->ce_mask & ADDR_ATTR_PEER) + dp_dump(p, " peer %s", + nl_addr2str(addr->a_peer, buf, sizeof(buf))); + + dp_dump(p, " %s ", nl_af2str(addr->a_family, buf, sizeof(buf))); + + if (link_cache) + dp_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, addr->a_ifindex, + buf, sizeof(buf))); + else + dp_dump(p, "dev %d ", addr->a_ifindex); + + dp_dump(p, "scope %s", + rtnl_scope2str(addr->a_scope, buf, sizeof(buf))); + + rtnl_addr_flags2str(addr->a_flags, buf, sizeof(buf)); + if (buf[0]) + dp_dump(p, " <%s>", buf); + + dp_dump(p, "\n"); + + return 1; +} + +static int addr_dump_full(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_addr *addr = (struct rtnl_addr *) obj; + int line = addr_dump_brief(obj, p); + char buf[128]; + + if (addr->ce_mask & (ADDR_ATTR_LABEL | ADDR_ATTR_BROADCAST | + ADDR_ATTR_ANYCAST | ADDR_ATTR_MULTICAST)) { + dp_dump_line(p, line++, " "); + + if (addr->ce_mask & ADDR_ATTR_LABEL) + dp_dump(p, " label %s", addr->a_label); + + if (addr->ce_mask & ADDR_ATTR_BROADCAST) + dp_dump(p, " broadcast %s", + nl_addr2str(addr->a_bcast, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_ANYCAST) + dp_dump(p, " anycast %s", + nl_addr2str(addr->a_anycast, buf, + sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_MULTICAST) + dp_dump(p, " multicast %s", + nl_addr2str(addr->a_multicast, buf, + sizeof(buf))); + + dp_dump(p, "\n"); + } + + if (addr->ce_mask & ADDR_ATTR_CACHEINFO) { + struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo; + + dp_dump_line(p, line++, " valid-lifetime %s", + ci->aci_valid == 0xFFFFFFFFU ? "forever" : + nl_msec2str(ci->aci_valid * 1000, + buf, sizeof(buf))); + + dp_dump(p, " preferred-lifetime %s\n", + ci->aci_prefered == 0xFFFFFFFFU ? "forever" : + nl_msec2str(ci->aci_prefered * 1000, + buf, sizeof(buf))); + + dp_dump_line(p, line++, " created boot-time+%s ", + nl_msec2str(addr->a_cacheinfo.aci_cstamp * 10, + buf, sizeof(buf))); + + dp_dump(p, "last-updated boot-time+%s\n", + nl_msec2str(addr->a_cacheinfo.aci_tstamp * 10, + buf, sizeof(buf))); + } + + return line; +} + +static int addr_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + return addr_dump_full(obj, p); +} + +static int addr_dump_xml(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_addr *addr = (struct rtnl_addr *) obj; + struct nl_cache *link_cache; + char buf[128]; + int line = 0; + + dp_dump_line(p, line++, "
\n"); + dp_dump_line(p, line++, " %s\n", + nl_af2str(addr->a_family, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_LOCAL) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(addr->a_local, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_PEER) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(addr->a_peer, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_BROADCAST) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(addr->a_bcast, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_ANYCAST) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(addr->a_anycast, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_MULTICAST) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(addr->a_multicast, buf, + sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_PREFIXLEN) + dp_dump_line(p, line++, " %u\n", + addr->a_prefixlen); + link_cache = nl_cache_mngt_require("route/link"); + + if (link_cache) + dp_dump_line(p, line++, " %s\n", + rtnl_link_i2name(link_cache, addr->a_ifindex, + buf, sizeof(buf))); + else + dp_dump_line(p, line++, " %u\n", + addr->a_ifindex); + + if (addr->ce_mask & ADDR_ATTR_SCOPE) + dp_dump_line(p, line++, " %s\n", + rtnl_scope2str(addr->a_scope, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_LABEL) + dp_dump_line(p, line++, " \n", addr->a_label); + + rtnl_addr_flags2str(addr->a_flags, buf, sizeof(buf)); + if (buf[0]) + dp_dump_line(p, line++, " %s\n", buf); + + if (addr->ce_mask & ADDR_ATTR_CACHEINFO) { + struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo; + + dp_dump_line(p, line++, " \n"); + + dp_dump_line(p, line++, " %s\n", + ci->aci_valid == 0xFFFFFFFFU ? "forever" : + nl_msec2str(ci->aci_valid * 1000, + buf, sizeof(buf))); + + dp_dump_line(p, line++, " %s\n", + ci->aci_prefered == 0xFFFFFFFFU ? "forever" : + nl_msec2str(ci->aci_prefered * 1000, + buf, sizeof(buf))); + + dp_dump_line(p, line++, " %s\n", + nl_msec2str(addr->a_cacheinfo.aci_cstamp * 10, + buf, sizeof(buf))); + + dp_dump_line(p, line++, " %s\n", + nl_msec2str(addr->a_cacheinfo.aci_tstamp * 10, + buf, sizeof(buf))); + + dp_dump_line(p, line++, " \n"); + } + + dp_dump_line(p, line++, "
\n"); + + return line; +} + +static int addr_dump_env(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_addr *addr = (struct rtnl_addr *) obj; + struct nl_cache *link_cache; + char buf[128]; + int line = 0; + + dp_dump_line(p, line++, "ADDR_FAMILY=%s\n", + nl_af2str(addr->a_family, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_LOCAL) + dp_dump_line(p, line++, "ADDR_LOCAL=%s\n", + nl_addr2str(addr->a_local, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_PEER) + dp_dump_line(p, line++, "ADDR_PEER=%s\n", + nl_addr2str(addr->a_peer, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_BROADCAST) + dp_dump_line(p, line++, "ADDR_BROADCAST=%s\n", + nl_addr2str(addr->a_bcast, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_ANYCAST) + dp_dump_line(p, line++, "ADDR_ANYCAST=%s\n", + nl_addr2str(addr->a_anycast, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_MULTICAST) + dp_dump_line(p, line++, "ADDR_MULTICAST=%s\n", + nl_addr2str(addr->a_multicast, buf, + sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_PREFIXLEN) + dp_dump_line(p, line++, "ADDR_PREFIXLEN=%u\n", + addr->a_prefixlen); + link_cache = nl_cache_mngt_require("route/link"); + + dp_dump_line(p, line++, "ADDR_IFINDEX=%u\n", addr->a_ifindex); + if (link_cache) + dp_dump_line(p, line++, "ADDR_IFNAME=%s\n", + rtnl_link_i2name(link_cache, addr->a_ifindex, + buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_SCOPE) + dp_dump_line(p, line++, "ADDR_SCOPE=%s\n", + rtnl_scope2str(addr->a_scope, buf, sizeof(buf))); + + if (addr->ce_mask & ADDR_ATTR_LABEL) + dp_dump_line(p, line++, "ADDR_LABEL=%s\n", addr->a_label); + + rtnl_addr_flags2str(addr->a_flags, buf, sizeof(buf)); + if (buf[0]) + dp_dump_line(p, line++, "ADDR_FLAGS=%s\n", buf); + + if (addr->ce_mask & ADDR_ATTR_CACHEINFO) { + struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo; + + dp_dump_line(p, line++, "ADDR_CACHEINFO_VALID=%s\n", + ci->aci_valid == 0xFFFFFFFFU ? "forever" : + nl_msec2str(ci->aci_valid * 1000, + buf, sizeof(buf))); + + dp_dump_line(p, line++, "ADDR_CACHEINFO_PREFERED=%s\n", + ci->aci_prefered == 0xFFFFFFFFU ? "forever" : + nl_msec2str(ci->aci_prefered * 1000, + buf, sizeof(buf))); + + dp_dump_line(p, line++, "ADDR_CACHEINFO_CREATED=%s\n", + nl_msec2str(addr->a_cacheinfo.aci_cstamp * 10, + buf, sizeof(buf))); + + dp_dump_line(p, line++, "ADDR_CACHEINFO_LASTUPDATE=%s\n", + nl_msec2str(addr->a_cacheinfo.aci_tstamp * 10, + buf, sizeof(buf))); + } + + return line; +} + +static int addr_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) +{ + struct rtnl_addr *a = (struct rtnl_addr *) _a; + struct rtnl_addr *b = (struct rtnl_addr *) _b; + int diff = 0; + +#define ADDR_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ADDR_ATTR_##ATTR, a, b, EXPR) + + diff |= ADDR_DIFF(IFINDEX, a->a_ifindex != b->a_ifindex); + diff |= ADDR_DIFF(FAMILY, a->a_family != b->a_family); + diff |= ADDR_DIFF(SCOPE, a->a_scope != b->a_scope); + diff |= ADDR_DIFF(LABEL, strcmp(a->a_label, b->a_label)); + diff |= ADDR_DIFF(PEER, nl_addr_cmp(a->a_peer, b->a_peer)); + diff |= ADDR_DIFF(LOCAL, nl_addr_cmp(a->a_local, b->a_local)); + diff |= ADDR_DIFF(ANYCAST, nl_addr_cmp(a->a_anycast,b->a_anycast)); + diff |= ADDR_DIFF(MULTICAST, nl_addr_cmp(a->a_multicast, + b->a_multicast)); + diff |= ADDR_DIFF(BROADCAST, nl_addr_cmp(a->a_bcast, b->a_bcast)); + + if (flags & LOOSE_FLAG_COMPARISON) + diff |= ADDR_DIFF(FLAGS, + (a->a_flags ^ b->a_flags) & b->a_flag_mask); + else + diff |= ADDR_DIFF(FLAGS, a->a_flags != b->a_flags); + +#undef ADDR_DIFF + + return diff; +} + +static struct trans_tbl addr_attrs[] = { + __ADD(ADDR_ATTR_FAMILY, family) + __ADD(ADDR_ATTR_PREFIXLEN, prefixlen) + __ADD(ADDR_ATTR_FLAGS, flags) + __ADD(ADDR_ATTR_SCOPE, scope) + __ADD(ADDR_ATTR_IFINDEX, ifindex) + __ADD(ADDR_ATTR_LABEL, label) + __ADD(ADDR_ATTR_CACHEINFO, cacheinfo) + __ADD(ADDR_ATTR_PEER, peer) + __ADD(ADDR_ATTR_LOCAL, local) + __ADD(ADDR_ATTR_BROADCAST, broadcast) + __ADD(ADDR_ATTR_ANYCAST, anycast) + __ADD(ADDR_ATTR_MULTICAST, multicast) +}; + +static char *addr_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, addr_attrs, + ARRAY_SIZE(addr_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_addr *rtnl_addr_alloc(void) +{ + return (struct rtnl_addr *) nl_object_alloc(&addr_obj_ops); +} + +void rtnl_addr_put(struct rtnl_addr *addr) +{ + nl_object_put((struct nl_object *) addr); +} + +/** @} */ + +/** + * @name Cache Management + * @{ + */ + +struct nl_cache *rtnl_addr_alloc_cache(struct nl_handle *handle) +{ + struct nl_cache *cache; + + cache = nl_cache_alloc(&rtnl_addr_ops); + if (!cache) + return NULL; + + if (handle && nl_cache_refill(handle, cache) < 0) { + nl_cache_free(cache); + return NULL; + } + + return cache; +} + +/** @} */ + +static struct nl_msg *build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags) +{ + struct nl_msg *msg; + struct ifaddrmsg am = { + .ifa_family = tmpl->a_family, + .ifa_index = tmpl->a_ifindex, + .ifa_prefixlen = tmpl->a_prefixlen, + }; + + if (tmpl->ce_mask & ADDR_ATTR_SCOPE) + am.ifa_scope = tmpl->a_scope; + else { + /* compatibility hack */ + if (tmpl->a_family == AF_INET && + tmpl->ce_mask & ADDR_ATTR_LOCAL && + *((char *) nl_addr_get_binary_addr(tmpl->a_local)) == 127) + am.ifa_scope = RT_SCOPE_HOST; + else + am.ifa_scope = RT_SCOPE_UNIVERSE; + } + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + goto nla_put_failure; + + if (nlmsg_append(msg, &am, sizeof(am), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (tmpl->ce_mask & ADDR_ATTR_LOCAL) + NLA_PUT_ADDR(msg, IFA_LOCAL, tmpl->a_local); + + if (tmpl->ce_mask & ADDR_ATTR_PEER) + NLA_PUT_ADDR(msg, IFA_ADDRESS, tmpl->a_peer); + else + NLA_PUT_ADDR(msg, IFA_ADDRESS, tmpl->a_local); + + if (tmpl->ce_mask & ADDR_ATTR_LABEL) + NLA_PUT_STRING(msg, IFA_LABEL, tmpl->a_label); + + if (tmpl->ce_mask & ADDR_ATTR_BROADCAST) + NLA_PUT_ADDR(msg, IFA_BROADCAST, tmpl->a_bcast); + + if (tmpl->ce_mask & ADDR_ATTR_ANYCAST) + NLA_PUT_ADDR(msg, IFA_ANYCAST, tmpl->a_anycast); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +/** + * @name Addition + * @{ + */ + +/** + * Build netlink request message to request addition of new address + * @arg addr Address object representing the new address. + * @arg flags Additional netlink message flags. + * + * Builds a new netlink message requesting the addition of a new + * address. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * Minimal required attributes: + * - interface index (rtnl_addr_set_ifindex()) + * - local address (rtnl_addr_set_local()) + * + * The scope will default to universe except for loopback addresses in + * which case a host scope is used if not specified otherwise. + * + * @note Free the memory after usage using nlmsg_free(). + * @return Newly allocated netlink message or NULL if an error occured. + */ +struct nl_msg *rtnl_addr_build_add_request(struct rtnl_addr *addr, int flags) +{ + int required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY | + ADDR_ATTR_PREFIXLEN | ADDR_ATTR_LOCAL; + + if ((addr->ce_mask & required) != required) { + nl_error(EINVAL, "Missing mandatory attributes, required are: " + "ifindex, family, prefixlen, local address."); + return NULL; + } + + return build_addr_msg(addr, RTM_NEWADDR, NLM_F_CREATE | flags); +} + +/** + * Request addition of new address + * @arg handle Netlink handle. + * @arg addr Address object representing the new address. + * @arg flags Additional netlink message flags. + * + * Builds a netlink message by calling rtnl_addr_build_add_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @see rtnl_addr_build_add_request() + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_addr_add(struct nl_handle *handle, struct rtnl_addr *addr, int flags) +{ + struct nl_msg *msg; + int err; + + msg = rtnl_addr_build_add_request(addr, flags); + if (!msg) + return nl_get_errno(); + + err = nl_send_auto_complete(handle, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name Deletion + * @{ + */ + +/** + * Build a netlink request message to request deletion of an address + * @arg addr Address object to be deleteted. + * @arg flags Additional netlink message flags. + * + * Builds a new netlink message requesting a deletion of an address. + * The netlink message header isn't fully equipped with all relevant + * fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * Minimal required attributes: + * - interface index (rtnl_addr_set_ifindex()) + * - address family (rtnl_addr_set_family()) + * + * Optional attributes: + * - local address (rtnl_addr_set_local()) + * - label (rtnl_addr_set_label(), IPv4/DECnet only) + * - peer address (rtnl_addr_set_peer(), IPv4 only) + * + * @note Free the memory after usage using nlmsg_free(). + * @return Newly allocated netlink message or NULL if an error occured. + */ +struct nl_msg *rtnl_addr_build_delete_request(struct rtnl_addr *addr, int flags) +{ + int required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY; + + if ((addr->ce_mask & required) != required) { + nl_error(EINVAL, "Missing mandatory attributes, required are: " + "ifindex, family"); + return NULL; + } + + return build_addr_msg(addr, RTM_DELADDR, flags); +} + +/** + * Request deletion of an address + * @arg handle Netlink handle. + * @arg addr Address object to be deleted. + * @arg flags Additional netlink message flags. + * + * Builds a netlink message by calling rtnl_addr_build_delete_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @see rtnl_addr_build_delete_request(); + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_addr_delete(struct nl_handle *handle, struct rtnl_addr *addr, + int flags) +{ + struct nl_msg *msg; + int err; + + msg = rtnl_addr_build_delete_request(addr, flags); + if (!msg) + return nl_get_errno(); + + err = nl_send_auto_complete(handle, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void rtnl_addr_set_label(struct rtnl_addr *addr, const char *label) +{ + strncpy(addr->a_label, label, sizeof(addr->a_label) - 1); + addr->ce_mask |= ADDR_ATTR_LABEL; +} + +char *rtnl_addr_get_label(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_LABEL) + return addr->a_label; + else + return NULL; +} + +void rtnl_addr_set_ifindex(struct rtnl_addr *addr, int ifindex) +{ + addr->a_ifindex = ifindex; + addr->ce_mask |= ADDR_ATTR_IFINDEX; +} + +int rtnl_addr_get_ifindex(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_IFINDEX) + return addr->a_ifindex; + else + return RTNL_LINK_NOT_FOUND; +} + +void rtnl_addr_set_family(struct rtnl_addr *addr, int family) +{ + addr->a_family = family; + addr->ce_mask |= ADDR_ATTR_FAMILY; +} + +int rtnl_addr_get_family(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_FAMILY) + return addr->a_family; + else + return AF_UNSPEC; +} + +void rtnl_addr_set_prefixlen(struct rtnl_addr *addr, int prefix) +{ + addr->a_prefixlen = prefix; + addr->ce_mask |= ADDR_ATTR_PREFIXLEN; +} + +int rtnl_addr_get_prefixlen(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_PREFIXLEN) + return addr->a_prefixlen; + else + return -1; +} + +void rtnl_addr_set_scope(struct rtnl_addr *addr, int scope) +{ + addr->a_scope = scope; + addr->ce_mask |= ADDR_ATTR_SCOPE; +} + +int rtnl_addr_get_scope(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_SCOPE) + return addr->a_scope; + else + return -1; +} + +void rtnl_addr_set_flags(struct rtnl_addr *addr, unsigned int flags) +{ + addr->a_flag_mask |= flags; + addr->a_flags |= flags; + addr->ce_mask |= ADDR_ATTR_FLAGS; +} + +void rtnl_addr_unset_flags(struct rtnl_addr *addr, unsigned int flags) +{ + addr->a_flag_mask |= flags; + addr->a_flags &= ~flags; + addr->ce_mask |= ADDR_ATTR_FLAGS; +} + +unsigned int rtnl_addr_get_flags(struct rtnl_addr *addr) +{ + return addr->a_flags; +} + +static inline int __assign_addr(struct rtnl_addr *addr, struct nl_addr **pos, + struct nl_addr *new, int flag) +{ + if (addr->ce_mask & ADDR_ATTR_FAMILY) { + if (new->a_family != addr->a_family) + return nl_error(EINVAL, "Address family mismatch"); + } else + addr->a_family = new->a_family; + + if (*pos) + nl_addr_put(*pos); + + *pos = nl_addr_get(new); + addr->ce_mask |= (flag | ADDR_ATTR_FAMILY); + + return 0; +} + +int rtnl_addr_set_local(struct rtnl_addr *addr, struct nl_addr *local) +{ + int err; + + err = __assign_addr(addr, &addr->a_local, local, ADDR_ATTR_LOCAL); + if (err < 0) + return err; + + if (!(addr->ce_mask & ADDR_ATTR_PEER)) { + addr->a_prefixlen = nl_addr_get_prefixlen(addr->a_local); + addr->ce_mask |= ADDR_ATTR_PREFIXLEN; + } + + return 0; +} + +struct nl_addr *rtnl_addr_get_local(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_LOCAL) + return addr->a_local; + else + return NULL; +} + +int rtnl_addr_set_peer(struct rtnl_addr *addr, struct nl_addr *peer) +{ + return __assign_addr(addr, &addr->a_peer, peer, ADDR_ATTR_PEER); + + addr->a_prefixlen = nl_addr_get_prefixlen(addr->a_peer); + addr->ce_mask |= ADDR_ATTR_PREFIXLEN; + + return 0; +} + +struct nl_addr *rtnl_addr_get_peer(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_PEER) + return addr->a_peer; + else + return NULL; +} + +int rtnl_addr_set_broadcast(struct rtnl_addr *addr, struct nl_addr *bcast) +{ + return __assign_addr(addr, &addr->a_bcast, bcast, ADDR_ATTR_BROADCAST); +} + +struct nl_addr *rtnl_addr_get_broadcast(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_BROADCAST) + return addr->a_bcast; + else + return NULL; +} + +int rtnl_addr_set_anycast(struct rtnl_addr *addr, struct nl_addr *anycast) +{ + return __assign_addr(addr, &addr->a_anycast, anycast, + ADDR_ATTR_ANYCAST); +} + +struct nl_addr *rtnl_addr_get_anycast(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_ANYCAST) + return addr->a_anycast; + else + return NULL; +} + +int rtnl_addr_set_multicast(struct rtnl_addr *addr, struct nl_addr *multicast) +{ + return __assign_addr(addr, &addr->a_multicast, multicast, + ADDR_ATTR_MULTICAST); +} + +struct nl_addr *rtnl_addr_get_multicast(struct rtnl_addr *addr) +{ + if (addr->ce_mask & ADDR_ATTR_MULTICAST) + return addr->a_multicast; + else + return NULL; +} + +/** @} */ + +/** + * @name Flags Translations + * @{ + */ + +static struct trans_tbl addr_flags[] = { + __ADD(IFA_F_SECONDARY, secondary) + __ADD(IFA_F_DEPRECATED, deprecated) + __ADD(IFA_F_TENTATIVE, tentative) + __ADD(IFA_F_PERMANENT, permanent) +}; + +char *rtnl_addr_flags2str(int flags, char *buf, size_t size) +{ + return __flags2str(flags, buf, size, addr_flags, + ARRAY_SIZE(addr_flags)); +} + +int rtnl_addr_str2flags(const char *name) +{ + return __str2flags(name, addr_flags, ARRAY_SIZE(addr_flags)); +} + +/** @} */ + +static struct nl_object_ops addr_obj_ops = { + .oo_name = "route/addr", + .oo_size = sizeof(struct rtnl_addr), + .oo_free_data = addr_free_data, + .oo_clone = addr_clone, + .oo_dump[NL_DUMP_BRIEF] = addr_dump_brief, + .oo_dump[NL_DUMP_FULL] = addr_dump_full, + .oo_dump[NL_DUMP_STATS] = addr_dump_stats, + .oo_dump[NL_DUMP_XML] = addr_dump_xml, + .oo_dump[NL_DUMP_ENV] = addr_dump_env, + .oo_compare = addr_compare, + .oo_attrs2str = addr_attrs2str, + .oo_id_attrs = (ADDR_ATTR_FAMILY | ADDR_ATTR_IFINDEX | + ADDR_ATTR_LOCAL | ADDR_ATTR_PREFIXLEN | + ADDR_ATTR_PEER), +}; + +static struct nl_af_group addr_groups[] = { + { AF_INET, RTNLGRP_IPV4_IFADDR }, + { AF_INET6, RTNLGRP_IPV6_IFADDR }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_addr_ops = { + .co_name = "route/addr", + .co_hdrsize = sizeof(struct ifaddrmsg), + .co_msgtypes = { + { RTM_NEWADDR, NL_ACT_NEW, "new" }, + { RTM_DELADDR, NL_ACT_DEL, "del" }, + { RTM_GETADDR, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = addr_groups, + .co_request_update = addr_request_update, + .co_msg_parser = addr_msg_parser, + .co_obj_ops = &addr_obj_ops, +}; + +static void __init addr_init(void) +{ + nl_cache_mngt_register(&rtnl_addr_ops); +} + +static void __exit addr_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_addr_ops); +} + +/** @} */ diff --git a/lib/route/class.c b/lib/route/class.c new file mode 100644 index 0000000..7f2289e --- /dev/null +++ b/lib/route/class.c @@ -0,0 +1,230 @@ +/* + * lib/route/class.c Queueing Classes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup class Queueing Classes + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct nl_cache_ops rtnl_class_ops; + +static int class_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + int err; + struct nl_parser_param *pp = arg; + struct rtnl_class *class; + struct rtnl_class_ops *cops; + + class = rtnl_class_alloc(); + if (!class) { + err = nl_errno(ENOMEM); + goto errout; + } + class->ce_msgtype = n->nlmsg_type; + + err = tca_msg_parser(n, (struct rtnl_tca *) class); + if (err < 0) + goto errout_free; + + cops = rtnl_class_lookup_ops(class); + if (cops && cops->co_msg_parser) { + err = cops->co_msg_parser(class); + if (err < 0) + goto errout_free; + } + + err = pp->pp_cb((struct nl_object *) class, pp); + if (err < 0) + goto errout_free; + + return P_ACCEPT; + +errout_free: + rtnl_class_put(class); +errout: + return err; +} + +static int class_request_update(struct nl_cache *cache, + struct nl_handle *handle) +{ + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = cache->c_iarg1, + }; + + return nl_send_simple(handle, RTM_GETTCLASS, NLM_F_DUMP, &tchdr, + sizeof(tchdr)); +} + +/** + * @name Addition/Modification + * @{ + */ + +static struct nl_msg *class_build(struct rtnl_class *class, int type, int flags) +{ + struct rtnl_class_ops *cops; + struct nl_msg *msg; + int err; + + msg = tca_build_msg((struct rtnl_tca *) class, type, flags); + if (!msg) + goto errout; + + cops = rtnl_class_lookup_ops(class); + if (cops && cops->co_get_opts) { + struct nl_msg *opts; + + opts = cops->co_get_opts(class); + if (opts) { + err = nla_put_nested(msg, TCA_OPTIONS, opts); + nlmsg_free(opts); + if (err < 0) + goto errout; + } + } + + return msg; +errout: + nlmsg_free(msg); + return NULL; +} + +/** + * Build a netlink message to add a new class + * @arg class class to add + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting an addition of a class. + * The netlink message header isn't fully equipped with all relevant + * fields and must be sent out via nl_send_auto_complete() or + * supplemented as needed. + * + * Common message flags + * - NLM_F_REPLACE - replace possibly existing classes + * + * @return New netlink message + */ +struct nl_msg *rtnl_class_build_add_request(struct rtnl_class *class, int flags) +{ + return class_build(class, RTM_NEWTCLASS, NLM_F_CREATE | flags); +} + +/** + * Add a new class + * @arg handle netlink handle + * @arg class class to delete + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_qdisc_build_add_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been processed. + * + * Common message flags + * - NLM_F_REPLACE - replace possibly existing classes + * + * @return 0 on success or a negative error code + */ +int rtnl_class_add(struct nl_handle *handle, struct rtnl_class *class, + int flags) +{ + struct nl_msg *msg; + int err; + + msg = rtnl_class_build_add_request(class, flags); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a class cache including all classes attached to the specified interface + * @arg handle netlink handle + * @arg ifindex interface index of the link the classes are + * attached to. + * + * Allocates a new cache, initializes it properly and updates it to + * include all classes attached to the specified interface. + * + * @return The cache or NULL if an error has occured. + */ +struct nl_cache * rtnl_class_alloc_cache(struct nl_handle *handle, int ifindex) +{ + struct nl_cache * cache; + + cache = nl_cache_alloc(&rtnl_class_ops); + if (!cache) + return NULL; + + cache->c_iarg1 = ifindex; + + if (handle && nl_cache_refill(handle, cache) < 0) { + nl_cache_free(cache); + return NULL; + } + + return cache; +} + +/** @} */ + +static struct nl_cache_ops rtnl_class_ops = { + .co_name = "route/class", + .co_hdrsize = sizeof(struct tcmsg), + .co_msgtypes = { + { RTM_NEWTCLASS, NL_ACT_NEW, "new" }, + { RTM_DELTCLASS, NL_ACT_DEL, "del" }, + { RTM_GETTCLASS, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = &class_request_update, + .co_msg_parser = &class_msg_parser, + .co_obj_ops = &class_obj_ops, +}; + +static void __init class_init(void) +{ + nl_cache_mngt_register(&rtnl_class_ops); +} + +static void __exit class_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_class_ops); +} + +/** @} */ diff --git a/lib/route/class_api.c b/lib/route/class_api.c new file mode 100644 index 0000000..c814486 --- /dev/null +++ b/lib/route/class_api.c @@ -0,0 +1,102 @@ +/* + * lib/route/class_api.c Queueing Classes Module API + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup class + * @defgroup class_api Class Modules + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct rtnl_class_ops *class_ops_list; + +/** + * @name Module API + * @{ + */ + +/** + * Register a class module + * @arg cops class module operations + */ +int rtnl_class_register(struct rtnl_class_ops *cops) +{ + struct rtnl_class_ops *o, **op; + + if (!cops->co_kind[0]) + BUG(); + + for (op = &class_ops_list; (o = *op) != NULL; op = &o->co_next) + if (!strcasecmp(cops->co_kind, o->co_kind)) + return nl_errno(EEXIST); + + cops->co_next = NULL; + *op = cops; + + return 0; +} + +/** + * Unregister a class module + * @arg cops class module operations + */ +int rtnl_class_unregister(struct rtnl_class_ops *cops) +{ + struct rtnl_class_ops *o, **op; + + for (op = &class_ops_list; (o = *op) != NULL; op = &o->co_next) + if (!strcasecmp(cops->co_kind, o->co_kind)) + break; + + if (!o) + return nl_errno(ENOENT); + + *op = cops->co_next; + + return 0; +} + +struct rtnl_class_ops *__rtnl_class_lookup_ops(const char *kind) +{ + struct rtnl_class_ops *cops; + + for (cops = class_ops_list; cops; cops = cops->co_next) + if (!strcmp(kind, cops->co_kind)) + return cops; + + return NULL; +} + +/** + * Lookup class operations for a class object + * @arg class Class object. + * + * @return Class operations or NULL if not found. + */ +struct rtnl_class_ops *rtnl_class_lookup_ops(struct rtnl_class *class) +{ + if (!class->c_ops) + class->c_ops = __rtnl_class_lookup_ops(class->c_kind); + + return class->c_ops; +} + + +/** @} */ + +/** @} */ diff --git a/lib/route/class_obj.c b/lib/route/class_obj.c new file mode 100644 index 0000000..0601bdf --- /dev/null +++ b/lib/route/class_obj.c @@ -0,0 +1,287 @@ +/* + * lib/route/class.c Queueing Classes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup class + * @defgroup class_obj Class Object + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void class_free_data(struct nl_object *obj) +{ + struct rtnl_class *class = (struct rtnl_class *) obj; + struct rtnl_class_ops *cops; + + tca_free_data((struct rtnl_tca *) class); + + cops = rtnl_class_lookup_ops(class); + if (cops && cops->co_free_data) + cops->co_free_data(class); +} + +static int class_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_class *dst = nl_object_priv(_dst); + struct rtnl_class *src = nl_object_priv(_src); + struct rtnl_class_ops *cops; + int err; + + err = tca_clone((struct rtnl_tca *) dst, (struct rtnl_tca *) src); + if (err < 0) + goto errout; + + cops = rtnl_class_lookup_ops(src); + if (cops && cops->co_clone) + err = cops->co_clone(dst, src); +errout: + return err; +} + +static int class_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_class *class = (struct rtnl_class *) obj; + struct rtnl_class_ops *cops; + + int line = tca_dump_brief((struct rtnl_tca *) class, "class", p, 0); + + cops = rtnl_class_lookup_ops(class); + if (cops && cops->co_dump[NL_DUMP_BRIEF]) + line = cops->co_dump[NL_DUMP_BRIEF](class, p, line); + dp_dump(p, "\n"); + + return line; +} + +static int class_dump_full(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_class *class = (struct rtnl_class *) obj; + struct rtnl_class_ops *cops; + int line; + + line = class_dump_brief(obj, p); + line = tca_dump_full((struct rtnl_tca *) class, p, line); + + if (class->c_info) { + char buf[32]; + dp_dump(p, "child-qdisc %s ", + rtnl_tc_handle2str(class->c_info, buf, sizeof(buf))); + } + + cops = rtnl_class_lookup_ops(class); + if (cops && cops->co_dump[NL_DUMP_FULL]) + line = cops->co_dump[NL_DUMP_FULL](class, p, line); + else if (!class->c_info) + dp_dump(p, "noop (no leaf qdisc)"); + + dp_dump(p, "\n"); + + return line; +} + +static int class_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_class *class = (struct rtnl_class *) obj; + struct rtnl_class_ops *cops; + int line; + + line = class_dump_full(obj, p); + line = tca_dump_stats((struct rtnl_tca *) class, p, line); + dp_dump(p, "\n"); + + cops = rtnl_class_lookup_ops(class); + if (cops && cops->co_dump[NL_DUMP_STATS]) + line = cops->co_dump[NL_DUMP_STATS](class, p, line); + + return line; +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_class *rtnl_class_alloc(void) +{ + return (struct rtnl_class *) nl_object_alloc(&class_obj_ops); +} + +void rtnl_class_put(struct rtnl_class *class) +{ + nl_object_put((struct nl_object *) class); +} + +/** @} */ + +/** + * @name Leaf Qdisc + * @{ + */ + +/** + * Lookup the leaf qdisc of a class + * @arg class the parent class + * @arg cache a qdisc cache including at laest all qdiscs of the + * interface the specified class is attached to + * @return The qdisc from the cache or NULL if the class has no leaf qdisc + */ +struct rtnl_qdisc *rtnl_class_leaf_qdisc(struct rtnl_class *class, + struct nl_cache *cache) +{ + struct rtnl_qdisc *leaf; + + if (!class->c_info) + return NULL; + + leaf = rtnl_qdisc_get_by_parent(cache, class->c_ifindex, + class->c_handle); + if (!leaf || leaf->q_handle != class->c_info) + return NULL; + + return leaf; +} + +/** @} */ + + +/** + * @name Iterators + * @{ + */ + +/** + * Call a callback for each child of a class + * @arg class the parent class + * @arg cache a class cache including all classes of the interface + * the specified class is attached to + * @arg cb callback function + * @arg arg argument to be passed to callback function + */ +void rtnl_class_foreach_child(struct rtnl_class *class, struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct rtnl_class *filter; + + filter = rtnl_class_alloc(); + if (!filter) + return; + + rtnl_class_set_parent(filter, class->c_handle); + rtnl_class_set_ifindex(filter, class->c_ifindex); + rtnl_class_set_kind(filter, class->c_kind); + + nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg); + rtnl_class_put(filter); +} + +/** + * Call a callback for each classifier attached to the class + * @arg class the parent class + * @arg cache a filter cache including at least all the filters + * attached to the specified class + * @arg cb callback function + * @arg arg argument to be passed to callback function + */ +void rtnl_class_foreach_cls(struct rtnl_class *class, struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct rtnl_cls *filter; + + filter = rtnl_cls_alloc(); + if (!filter) + return; + + rtnl_cls_set_ifindex(filter, class->c_ifindex); + rtnl_cls_set_parent(filter, class->c_parent); + + nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg); + rtnl_cls_put(filter); +} + +/** @} */ + + +/** + * @name Attributes + * @{ + */ + +void rtnl_class_set_ifindex(struct rtnl_class *class, int ifindex) +{ + tca_set_ifindex((struct rtnl_tca *) class, ifindex); +} + +int rtnl_class_get_ifindex(struct rtnl_class *class) +{ + return tca_get_ifindex((struct rtnl_tca *) class); +} + +void rtnl_class_set_handle(struct rtnl_class *class, uint32_t handle) +{ + tca_set_handle((struct rtnl_tca *) class, handle); +} + +uint32_t rtnl_class_get_handle(struct rtnl_class *class) +{ + return tca_get_handle((struct rtnl_tca *) class); +} + +void rtnl_class_set_parent(struct rtnl_class *class, uint32_t parent) +{ + tca_set_parent((struct rtnl_tca *) class, parent); +} + +uint32_t rtnl_class_get_parent(struct rtnl_class *class) +{ + return tca_get_parent((struct rtnl_tca *) class); +} + +void rtnl_class_set_kind(struct rtnl_class *class, const char *name) +{ + tca_set_kind((struct rtnl_tca *) class, name); + class->c_ops = __rtnl_class_lookup_ops(name); +} + +char *rtnl_class_get_kind(struct rtnl_class *class) +{ + return tca_get_kind((struct rtnl_tca *) class); +} + +uint64_t rtnl_class_get_stat(struct rtnl_class *class, + enum rtnl_tc_stats_id id) +{ + return tca_get_stat((struct rtnl_tca *) class, id); +} + +/** @} */ + +struct nl_object_ops class_obj_ops = { + .oo_name = "route/class", + .oo_size = sizeof(struct rtnl_class), + .oo_free_data = class_free_data, + .oo_clone = class_clone, + .oo_dump[NL_DUMP_BRIEF] = class_dump_brief, + .oo_dump[NL_DUMP_FULL] = class_dump_full, + .oo_dump[NL_DUMP_STATS] = class_dump_stats, + .oo_compare = tca_compare, + .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), +}; + +/** @} */ diff --git a/lib/route/classifier.c b/lib/route/classifier.c new file mode 100644 index 0000000..7ad4722 --- /dev/null +++ b/lib/route/classifier.c @@ -0,0 +1,343 @@ +/* + * lib/route/classifier.c Classifier + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup cls Classifiers + * + * @par Classifier Identification + * - protocol + * - priority + * - parent + * - interface + * - kind + * - handle + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct nl_cache_ops rtnl_cls_ops; + +static int cls_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, void *arg) +{ + int err; + struct nl_parser_param *pp = arg; + struct rtnl_cls *cls; + struct rtnl_cls_ops *cops; + + cls = rtnl_cls_alloc(); + if (!cls) { + err = nl_errno(ENOMEM); + goto errout; + } + cls->ce_msgtype = nlh->nlmsg_type; + + err = tca_msg_parser(nlh, (struct rtnl_tca *) cls); + if (err < 0) + goto errout_free; + + cls->c_prio = TC_H_MAJ(cls->c_info) >> 16; + cls->c_protocol = ntohs(TC_H_MIN(cls->c_info)); + + cops = rtnl_cls_lookup_ops(cls); + if (cops && cops->co_msg_parser) { + err = cops->co_msg_parser(cls); + if (err < 0) + goto errout_free; + } + + err = pp->pp_cb((struct nl_object *) cls, pp); + if (err < 0) + goto errout_free; + + return P_ACCEPT; + +errout_free: + rtnl_cls_put(cls); +errout: + return err; +} + +static int cls_request_update(struct nl_cache *cache, struct nl_handle *handle) +{ + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = cache->c_iarg1, + .tcm_parent = cache->c_iarg2, + }; + + return nl_send_simple(handle, RTM_GETTFILTER, NLM_F_DUMP, &tchdr, + sizeof(tchdr)); +} + + +static struct nl_msg *cls_build(struct rtnl_cls *cls, int type, int flags) +{ + struct nl_msg *msg; + struct rtnl_cls_ops *cops; + int err, prio, proto; + struct tcmsg *tchdr; + + msg = tca_build_msg((struct rtnl_tca *) cls, type, flags); + if (!msg) + goto errout; + + tchdr = nlmsg_data(nlmsg_hdr(msg)); + prio = rtnl_cls_get_prio(cls); + proto = rtnl_cls_get_protocol(cls); + tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto)), + + cops = rtnl_cls_lookup_ops(cls); + if (cops && cops->co_get_opts) { + struct nl_msg *opts; + + opts = cops->co_get_opts(cls); + if (opts) { + err = nla_put_nested(msg, TCA_OPTIONS, opts); + nlmsg_free(opts); + if (err < 0) + goto errout; + } + } + + return msg; +errout: + nlmsg_free(msg); + return NULL; +} + +/** + * @name Classifier Addition/Modification/Deletion + * @{ + */ + +/** + * Build a netlink message to add a new classifier + * @arg cls classifier to add + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting an addition of a classifier + * The netlink message header isn't fully equipped with all relevant + * fields and must be sent out via nl_send_auto_complete() or + * supplemented as needed. \a classifier must contain the attributes of + * the new classifier set via \c rtnl_cls_set_* functions. \a opts + * may point to the clsasifier specific options. + * + * @return New netlink message + */ +struct nl_msg * rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags) +{ + return cls_build(cls, RTM_NEWTFILTER, NLM_F_CREATE | flags); +} + +/** + * Add a new classifier + * @arg handle netlink handle + * @arg cls classifier to add + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_cls_build_add_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been processed. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_cls_add(struct nl_handle *handle, struct rtnl_cls *cls, int flags) +{ + int err; + struct nl_msg *msg; + + msg = rtnl_cls_build_add_request(cls, flags); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** + * Build a netlink message to change classifier attributes + * @arg cls classifier to change + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting a change of a neigh + * attributes. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * @return The netlink message + */ +struct nl_msg *rtnl_cls_build_change_request(struct rtnl_cls *cls, int flags) +{ + return cls_build(cls, RTM_NEWTFILTER, NLM_F_REPLACE | flags); +} + +/** + * Change a classifier + * @arg handle netlink handle + * @arg cls classifier to change + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_cls_build_change_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been processed. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_cls_change(struct nl_handle *handle, struct rtnl_cls *cls, + int flags) +{ + int err; + struct nl_msg *msg; + + msg = rtnl_cls_build_change_request(cls, flags); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** + * Build a netlink request message to delete a classifier + * @arg cls classifier to delete + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting a deletion of a classifier. + * The netlink message header isn't fully equipped with all relevant + * fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * @return New netlink message + */ +struct nl_msg *rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags) +{ + return cls_build(cls, RTM_DELTFILTER, flags); +} + + +/** + * Delete a classifier + * @arg handle netlink handle + * @arg cls classifier to delete + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_cls_build_delete_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been processed. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_cls_delete(struct nl_handle *handle, struct rtnl_cls *cls, int flags) +{ + int err; + struct nl_msg *msg; + + msg = rtnl_cls_build_delete_request(cls, flags); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a classifier cache including all classifiers attached to the + * specified class/qdisc on eht specified interface. + * @arg handle netlink handle + * @arg ifindex interface index of the link the classes are + * attached to. + * @arg parent parent qdisc/class + * + * Allocates a new cache, initializes it properly and updates it to + * include all classes attached to the specified interface. + * + * @note The caller is responsible for destroying and freeing the + * cache after using it. + * @return The cache or NULL if an error has occured. + */ +struct nl_cache *rtnl_cls_alloc_cache(struct nl_handle *handle, + int ifindex, uint32_t parent) +{ + struct nl_cache * cache; + + cache = nl_cache_alloc(&rtnl_cls_ops); + if (cache == NULL) + return NULL; + + cache->c_iarg1 = ifindex; + cache->c_iarg2 = parent; + + if (handle && nl_cache_refill(handle, cache) < 0) { + nl_cache_free(cache); + return NULL; + } + + return cache; +} + +/** @} */ + +static struct nl_cache_ops rtnl_cls_ops = { + .co_name = "route/cls", + .co_hdrsize = sizeof(struct tcmsg), + .co_msgtypes = { + { RTM_NEWTFILTER, NL_ACT_NEW, "new" }, + { RTM_DELTFILTER, NL_ACT_DEL, "del" }, + { RTM_GETTFILTER, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = cls_request_update, + .co_msg_parser = cls_msg_parser, + .co_obj_ops = &cls_obj_ops, +}; + +static void __init cls_init(void) +{ + nl_cache_mngt_register(&rtnl_cls_ops); +} + +static void __exit cls_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_cls_ops); +} + +/** @} */ diff --git a/lib/route/cls/fw.c b/lib/route/cls/fw.c new file mode 100644 index 0000000..7ca7619 --- /dev/null +++ b/lib/route/cls/fw.c @@ -0,0 +1,251 @@ +/* + * lib/route/cls/fw.c fw classifier + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2006 Petr Gotthard + * Copyright (c) 2006 Siemens AG Oesterreich + */ + +/** + * @ingroup cls_api + * @defgroup fw Firewall Classifier + * + * @{ + */ + +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define FW_ATTR_CLASSID 0x001 +#define FW_ATTR_ACTION 0x002 +#define FW_ATTR_POLICE 0x004 +#define FW_ATTR_INDEV 0x008 +/** @endcond */ + +static inline struct rtnl_fw *fw_cls(struct rtnl_cls *cls) +{ + return (struct rtnl_fw *) cls->c_subdata; +} + +static inline struct rtnl_fw *fw_alloc(struct rtnl_cls *cls) +{ + if (!cls->c_subdata) + cls->c_subdata = calloc(1, sizeof(struct rtnl_fw)); + + return fw_cls(cls); +} + +static struct nla_policy fw_policy[TCA_FW_MAX+1] = { + [TCA_FW_CLASSID] = { .type = NLA_U32 }, + [TCA_FW_INDEV] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, +}; + +static int fw_msg_parser(struct rtnl_cls *cls) +{ + int err; + struct nlattr *tb[TCA_FW_MAX + 1]; + struct rtnl_fw *f; + + err = tca_parse(tb, TCA_FW_MAX, (struct rtnl_tca *) cls, fw_policy); + if (err < 0) + return err; + + f = fw_alloc(cls); + if (!f) + goto errout_nomem; + + if (tb[TCA_FW_CLASSID]) { + f->cf_classid = nla_get_u32(tb[TCA_FW_CLASSID]); + f->cf_mask |= FW_ATTR_CLASSID; + } + + if (tb[TCA_FW_ACT]) { + f->cf_act = nla_get_data(tb[TCA_FW_ACT]); + if (!f->cf_act) + goto errout_nomem; + f->cf_mask |= FW_ATTR_ACTION; + } + + if (tb[TCA_FW_POLICE]) { + f->cf_police = nla_get_data(tb[TCA_FW_POLICE]); + if (!f->cf_police) + goto errout_nomem; + f->cf_mask |= FW_ATTR_POLICE; + } + + if (tb[TCA_FW_INDEV]) { + nla_strlcpy(f->cf_indev, tb[TCA_FW_INDEV], IFNAMSIZ); + f->cf_mask |= FW_ATTR_INDEV; + } + + return 0; + +errout_nomem: + err = nl_errno(ENOMEM); + + return err; +} + +static void fw_free_data(struct rtnl_cls *cls) +{ + struct rtnl_fw *f = fw_cls(cls); + + if (!f) + return; + + nl_data_free(f->cf_act); + nl_data_free(f->cf_police); + + free(cls->c_subdata); +} + +static int fw_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src) +{ + struct rtnl_fw *dst, *src = fw_cls(_src); + + if (!src) + return 0; + + dst = fw_alloc(_dst); + if (!dst) + return nl_errno(ENOMEM); + + if (src->cf_act) + if (!(dst->cf_act = nl_data_clone(src->cf_act))) + goto errout; + + if (src->cf_police) + if (!(dst->cf_police = nl_data_clone(src->cf_police))) + goto errout; + + return 0; +errout: + return nl_get_errno(); +} + +static int fw_dump_brief(struct rtnl_cls *cls, struct nl_dump_params *p, + int line) +{ + struct rtnl_fw *f = fw_cls(cls); + char buf[32]; + + if (!f) + goto ignore; + + if (f->cf_mask & FW_ATTR_CLASSID) + dp_dump(p, " target %s", + rtnl_tc_handle2str(f->cf_classid, buf, sizeof(buf))); + +ignore: + return line; +} + +static int fw_dump_full(struct rtnl_cls *cls, struct nl_dump_params *p, + int line) +{ + struct rtnl_fw *f = fw_cls(cls); + + if (!f) + goto ignore; + + if (f->cf_mask & FW_ATTR_INDEV) + dp_dump(p, "indev %s ", f->cf_indev); + +ignore: + return line; +} + +static int fw_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p, + int line) +{ + struct rtnl_fw *f = fw_cls(cls); + + if (!f) + goto ignore; + +ignore: + return line; +} + +static struct nl_msg *fw_get_opts(struct rtnl_cls *cls) +{ + struct rtnl_fw *f; + struct nl_msg *msg; + + f = fw_cls(cls); + if (!f) + return NULL; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + if (f->cf_mask & FW_ATTR_CLASSID) + nla_put_u32(msg, TCA_FW_CLASSID, f->cf_classid); + + if (f->cf_mask & FW_ATTR_ACTION) + nla_put_data(msg, TCA_FW_ACT, f->cf_act); + + if (f->cf_mask & FW_ATTR_POLICE) + nla_put_data(msg, TCA_FW_POLICE, f->cf_police); + + if (f->cf_mask & FW_ATTR_INDEV) + nla_put_string(msg, TCA_FW_INDEV, f->cf_indev); + + return msg; +} + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_fw_set_classid(struct rtnl_cls *cls, uint32_t classid) +{ + struct rtnl_fw *f; + + f = fw_alloc(cls); + if (!f) + return nl_errno(ENOMEM); + + f->cf_classid = classid; + f->cf_mask |= FW_ATTR_CLASSID; + + return 0; +} + +/** @} */ + +static struct rtnl_cls_ops fw_ops = { + .co_kind = "fw", + .co_msg_parser = fw_msg_parser, + .co_free_data = fw_free_data, + .co_clone = fw_clone, + .co_get_opts = fw_get_opts, + .co_dump[NL_DUMP_BRIEF] = fw_dump_brief, + .co_dump[NL_DUMP_FULL] = fw_dump_full, + .co_dump[NL_DUMP_STATS] = fw_dump_stats, +}; + +static void __init fw_init(void) +{ + rtnl_cls_register(&fw_ops); +} + +static void __exit fw_exit(void) +{ + rtnl_cls_unregister(&fw_ops); +} + +/** @} */ diff --git a/lib/route/cls/police.c b/lib/route/cls/police.c new file mode 100644 index 0000000..051c6b2 --- /dev/null +++ b/lib/route/cls/police.c @@ -0,0 +1,67 @@ +/* + * lib/route/cls/police.c Policer + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @name Policer Type + * @{ + */ + +static struct trans_tbl police_types[] = { + __ADD(TC_POLICE_UNSPEC,unspec) + __ADD(TC_POLICE_OK,ok) + __ADD(TC_POLICE_RECLASSIFY,reclassify) + __ADD(TC_POLICE_SHOT,shot) +#ifdef TC_POLICE_PIPE + __ADD(TC_POLICE_PIPE,pipe) +#endif +}; + +/** + * Transform a policer type number into a character string (Reentrant). + * @arg type policer type + * @arg buf destination buffer + * @arg len buffer length + * + * Transforms a policer type number into a character string and stores + * it in the provided buffer. + * + * @return The destination buffer or the type encoded in hex if no match was found. + */ +char * nl_police2str(int type, char *buf, size_t len) +{ + return __type2str(type, buf, len, police_types, + ARRAY_SIZE(police_types)); +} + +/** + * Transform a character string into a policer type number + * @arg name policer type name + * + * Transform the provided character string specifying a policer + * type into the corresponding numeric value + * + * @return Policer type number or a negative value. + */ +int nl_str2police(const char *name) +{ + return __str2type(name, police_types, ARRAY_SIZE(police_types)); +} + +/** @} */ diff --git a/lib/route/cls/u32.c b/lib/route/cls/u32.c new file mode 100644 index 0000000..596e63f --- /dev/null +++ b/lib/route/cls/u32.c @@ -0,0 +1,602 @@ +/* + * lib/route/cls/u32.c u32 classifier + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2005-2006 Petr Gotthard + * Copyright (c) 2005-2006 Siemens AG Oesterreich + */ + +/** + * @ingroup cls_api + * @defgroup u32 Universal 32-bit Classifier + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define U32_ATTR_DIVISOR 0x001 +#define U32_ATTR_HASH 0x002 +#define U32_ATTR_CLASSID 0x004 +#define U32_ATTR_LINK 0x008 +#define U32_ATTR_PCNT 0x010 +#define U32_ATTR_SELECTOR 0x020 +#define U32_ATTR_ACTION 0x040 +#define U32_ATTR_POLICE 0x080 +#define U32_ATTR_INDEV 0x100 +/** @endcond */ + +static inline struct rtnl_u32 *u32_cls(struct rtnl_cls *cls) +{ + return (struct rtnl_u32 *) cls->c_subdata; +} + +static inline struct rtnl_u32 *u32_alloc(struct rtnl_cls *cls) +{ + if (!cls->c_subdata) + cls->c_subdata = calloc(1, sizeof(struct rtnl_u32)); + + return u32_cls(cls); +} + +static inline struct tc_u32_sel *u32_selector(struct rtnl_u32 *u) +{ + return (struct tc_u32_sel *) u->cu_selector->d_data; +} + +static inline struct tc_u32_sel *u32_selector_alloc(struct rtnl_u32 *u) +{ + if (!u->cu_selector) + u->cu_selector = nl_data_alloc(NULL, sizeof(struct tc_u32_sel)); + + return u32_selector(u); +} + +static struct nla_policy u32_policy[TCA_U32_MAX+1] = { + [TCA_U32_DIVISOR] = { .type = NLA_U32 }, + [TCA_U32_HASH] = { .type = NLA_U32 }, + [TCA_U32_CLASSID] = { .type = NLA_U32 }, + [TCA_U32_LINK] = { .type = NLA_U32 }, + [TCA_U32_INDEV] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [TCA_U32_SEL] = { .minlen = sizeof(struct tc_u32_sel) }, + [TCA_U32_PCNT] = { .minlen = sizeof(struct tc_u32_pcnt) }, +}; + +static int u32_msg_parser(struct rtnl_cls *cls) +{ + int err; + struct nlattr *tb[TCA_U32_MAX + 1]; + struct rtnl_u32 *u; + + err = tca_parse(tb, TCA_U32_MAX, (struct rtnl_tca *) cls, u32_policy); + if (err < 0) + return err; + + u = u32_alloc(cls); + if (!u) + goto errout_nomem; + + if (tb[TCA_U32_DIVISOR]) { + u->cu_divisor = nla_get_u32(tb[TCA_U32_DIVISOR]); + u->cu_mask |= U32_ATTR_DIVISOR; + } + + if (tb[TCA_U32_SEL]) { + u->cu_selector = nla_get_data(tb[TCA_U32_SEL]); + if (!u->cu_selector) + goto errout_nomem; + u->cu_mask |= U32_ATTR_SELECTOR; + } + + if (tb[TCA_U32_HASH]) { + u->cu_hash = nla_get_u32(tb[TCA_U32_HASH]); + u->cu_mask |= U32_ATTR_HASH; + } + + if (tb[TCA_U32_CLASSID]) { + u->cu_classid = nla_get_u32(tb[TCA_U32_CLASSID]); + u->cu_mask |= U32_ATTR_CLASSID; + } + + if (tb[TCA_U32_LINK]) { + u->cu_link = nla_get_u32(tb[TCA_U32_LINK]); + u->cu_mask |= U32_ATTR_LINK; + } + + if (tb[TCA_U32_ACT]) { + u->cu_act = nla_get_data(tb[TCA_U32_ACT]); + if (!u->cu_act) + goto errout_nomem; + u->cu_mask |= U32_ATTR_ACTION; + } + + if (tb[TCA_U32_POLICE]) { + u->cu_police = nla_get_data(tb[TCA_U32_POLICE]); + if (!u->cu_police) + goto errout_nomem; + u->cu_mask |= U32_ATTR_POLICE; + } + + if (tb[TCA_U32_PCNT]) { + struct tc_u32_sel *sel; + int pcnt_size; + + if (!tb[TCA_U32_SEL]) { + err = nl_error(EINVAL, "Missing TCA_U32_SEL required " + "for TCA_U32_PCNT"); + goto errout; + } + + sel = u->cu_selector->d_data; + pcnt_size = sizeof(struct tc_u32_pcnt) + + (sel->nkeys * sizeof(uint64_t)); + if (nla_len(tb[TCA_U32_PCNT]) < pcnt_size) { + err = nl_error(EINVAL, "Invalid size for TCA_U32_PCNT"); + goto errout; + } + + u->cu_pcnt = nla_get_data(tb[TCA_U32_PCNT]); + if (!u->cu_pcnt) + goto errout_nomem; + u->cu_mask |= U32_ATTR_PCNT; + } + + if (tb[TCA_U32_INDEV]) { + nla_strlcpy(u->cu_indev, tb[TCA_U32_INDEV], IFNAMSIZ); + u->cu_mask |= U32_ATTR_INDEV; + } + + return 0; + +errout_nomem: + err = nl_errno(ENOMEM); +errout: + return err; +} + +static void u32_free_data(struct rtnl_cls *cls) +{ + struct rtnl_u32 *u = u32_cls(cls); + + if (!u) + return; + + nl_data_free(u->cu_selector); + nl_data_free(u->cu_act); + nl_data_free(u->cu_police); + nl_data_free(u->cu_pcnt); + + free(cls->c_subdata); +} + +static int u32_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src) +{ + struct rtnl_u32 *dst, *src = u32_cls(_src); + + if (!src) + return 0; + + dst = u32_alloc(_dst); + if (!dst) + return nl_errno(ENOMEM); + + if (src->cu_selector) + if (!(dst->cu_selector = nl_data_clone(src->cu_selector))) + goto errout; + + if (src->cu_act) + if (!(dst->cu_act = nl_data_clone(src->cu_act))) + goto errout; + + if (src->cu_police) + if (!(dst->cu_police = nl_data_clone(src->cu_police))) + goto errout; + + if (src->cu_pcnt) + if (!(dst->cu_pcnt = nl_data_clone(src->cu_pcnt))) + goto errout; + + return 0; +errout: + return nl_get_errno(); +} + +static int u32_dump_brief(struct rtnl_cls *cls, struct nl_dump_params *p, + int line) +{ + struct rtnl_u32 *u = u32_cls(cls); + char buf[32]; + + if (!u) + goto ignore; + + if (u->cu_mask & U32_ATTR_DIVISOR) + dp_dump(p, " divisor %u", u->cu_divisor); + else if (u->cu_mask & U32_ATTR_CLASSID) + dp_dump(p, " target %s", + rtnl_tc_handle2str(u->cu_classid, buf, sizeof(buf))); + +ignore: + return line; +} + +static int print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel, + struct rtnl_cls *cls, struct rtnl_u32 *u, int line) +{ + int i; + struct tc_u32_key *key; + + if (sel->hmask || sel->hoff) { + /* I guess this will never be used since the kernel only + * exports the selector if no divisor is set but hash offset + * and hash mask make only sense in hash filters with divisor + * set */ + dp_dump(p, " hash at %u & 0x%x", sel->hoff, sel->hmask); + } + + if (sel->flags & (TC_U32_OFFSET | TC_U32_VAROFFSET)) { + dp_dump(p, " offset at %u", sel->off); + + if (sel->flags & TC_U32_VAROFFSET) + dp_dump(p, " variable (at %u & 0x%x) >> %u", + sel->offoff, ntohs(sel->offmask), sel->offshift); + } + + if (sel->flags) { + int flags = sel->flags; + dp_dump(p, " <"); + +#define PRINT_FLAG(f) if (flags & TC_U32_##f) { \ + flags &= ~TC_U32_##f; dp_dump(p, #f "%s", flags ? "," : ""); } + + PRINT_FLAG(TERMINAL); + PRINT_FLAG(OFFSET); + PRINT_FLAG(VAROFFSET); + PRINT_FLAG(EAT); +#undef PRINT_FLAG + + dp_dump(p, ">"); + } + + + for (i = 0; i < sel->nkeys; i++) { + key = (struct tc_u32_key *) ((char *) sel + sizeof(*sel)) + i; + + dp_dump(p, "\n"); + dp_dump_line(p, line++, " match key at %s%u ", + key->offmask ? "nexthdr+" : "", key->off); + + if (key->offmask) + dp_dump(p, "[0x%u] ", key->offmask); + + dp_dump(p, "& 0x%08x == 0x%08x", ntohl(key->mask), ntohl(key->val)); + + if (p->dp_type == NL_DUMP_STATS && + (u->cu_mask & U32_ATTR_PCNT)) { + struct tc_u32_pcnt *pcnt = u->cu_pcnt->d_data; + dp_dump(p, " successful %" PRIu64, pcnt->kcnts[i]); + } + } + + return line; +} + + +static int u32_dump_full(struct rtnl_cls *cls, struct nl_dump_params *p, + int line) +{ + struct rtnl_u32 *u = u32_cls(cls); + struct tc_u32_sel *s; + + if (!u) + goto ignore; + + if (!(u->cu_mask & U32_ATTR_SELECTOR)) { + dp_dump(p, "no-selector\n"); + return line; + } + + s = u->cu_selector->d_data; + + dp_dump(p, "nkeys %u ", s->nkeys); + + if (u->cu_mask & U32_ATTR_HASH) + dp_dump(p, "ht key 0x%x hash 0x%u", + TC_U32_USERHTID(u->cu_hash), TC_U32_HASH(u->cu_hash)); + + if (u->cu_mask & U32_ATTR_LINK) + dp_dump(p, "link %u ", u->cu_link); + + if (u->cu_mask & U32_ATTR_INDEV) + dp_dump(p, "indev %s ", u->cu_indev); + + line = print_selector(p, s, cls, u, line); + dp_dump(p, "\n"); + +ignore: + return line; + +#if 0 +#define U32_ATTR_ACTION 0x040 +#define U32_ATTR_POLICE 0x080 + + struct nl_data act; + struct nl_data police; +#endif +} + +static int u32_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p, + int line) +{ + struct rtnl_u32 *u = u32_cls(cls); + + if (!u) + goto ignore; + + if (u->cu_mask & U32_ATTR_PCNT) { + struct tc_u32_pcnt *pc = u->cu_pcnt->d_data; + dp_dump(p, "\n"); + dp_dump_line(p, line++, "%s successful hits\n"); + dp_dump_line(p, line++, "%s %8llu %8llu\n", + pc->rhit, pc->rcnt); + } + +ignore: + return line; +} + +static struct nl_msg *u32_get_opts(struct rtnl_cls *cls) +{ + struct rtnl_u32 *u; + struct nl_msg *msg; + + u = u32_cls(cls); + if (!u) + return NULL; + + msg = nlmsg_alloc(); + if (!msg) + return NULL; + + if (u->cu_mask & U32_ATTR_DIVISOR) + nla_put_u32(msg, TCA_U32_DIVISOR, u->cu_divisor); + + if (u->cu_mask & U32_ATTR_HASH) + nla_put_u32(msg, TCA_U32_HASH, u->cu_hash); + + if (u->cu_mask & U32_ATTR_CLASSID) + nla_put_u32(msg, TCA_U32_CLASSID, u->cu_classid); + + if (u->cu_mask & U32_ATTR_LINK) + nla_put_u32(msg, TCA_U32_LINK, u->cu_link); + + if (u->cu_mask & U32_ATTR_SELECTOR) + nla_put_data(msg, TCA_U32_SEL, u->cu_selector); + + if (u->cu_mask & U32_ATTR_ACTION) + nla_put_data(msg, TCA_U32_ACT, u->cu_act); + + if (u->cu_mask & U32_ATTR_POLICE) + nla_put_data(msg, TCA_U32_POLICE, u->cu_police); + + if (u->cu_mask & U32_ATTR_INDEV) + nla_put_string(msg, TCA_U32_INDEV, u->cu_indev); + + return msg; +} + +/** + * @name Attribute Modifications + * @{ + */ + +void rtnl_u32_set_handle(struct rtnl_cls *cls, int htid, int hash, + int nodeid) +{ + uint32_t handle = (htid << 20) | (hash << 12) | nodeid; + + tca_set_handle((struct rtnl_tca *) cls, handle ); +} + +int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid) +{ + struct rtnl_u32 *u; + + u = u32_alloc(cls); + if (!u) + return nl_errno(ENOMEM); + + u->cu_classid = classid; + u->cu_mask |= U32_ATTR_CLASSID; + + return 0; +} + +/** @} */ + +/** + * @name Selector Modifications + * @{ + */ + +int rtnl_u32_set_flags(struct rtnl_cls *cls, int flags) +{ + struct tc_u32_sel *sel; + struct rtnl_u32 *u; + + u = u32_alloc(cls); + if (!u) + return nl_errno(ENOMEM); + + sel = u32_selector_alloc(u); + if (!sel) + return nl_errno(ENOMEM); + + sel->flags |= flags; + u->cu_mask |= U32_ATTR_SELECTOR; + + return 0; +} + +/** + * Append new 32-bit key to the selector + * + * @arg cls classifier to be modifier + * @arg val value to be matched (network byte-order) + * @arg mask mask to be applied before matching (network byte-order) + * @arg off offset, in bytes, to start matching + * @arg offmask offset mask + * + * General selectors define the pattern, mask and offset the pattern will be + * matched to the packet contents. Using the general selectors you can match + * virtually any single bit in the IP (or upper layer) header. + * +*/ +int rtnl_u32_add_key(struct rtnl_cls *cls, uint32_t val, uint32_t mask, + int off, int offmask) +{ + struct tc_u32_sel *sel; + struct rtnl_u32 *u; + int err; + + u = u32_alloc(cls); + if (!u) + return nl_errno(ENOMEM); + + sel = u32_selector_alloc(u); + if (!sel) + return nl_errno(ENOMEM); + + err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key)); + if (err < 0) + return err; + + /* the selector might have been moved by realloc */ + sel = u32_selector(u); + + sel->keys[sel->nkeys].mask = mask; + sel->keys[sel->nkeys].val = val & mask; + sel->keys[sel->nkeys].off = off; + sel->keys[sel->nkeys].offmask = offmask; + sel->nkeys++; + u->cu_mask |= U32_ATTR_SELECTOR; + + return 0; +} + +int rtnl_u32_add_key_uint8(struct rtnl_cls *cls, uint8_t val, uint8_t mask, + int off, int offmask) +{ + int shift = 24 - 8 * (off & 3); + + return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift), + htonl((uint32_t)mask << shift), + off & ~3, offmask); +} + +/** + * Append new selector key to match a 16-bit number + * + * @arg cls classifier to be modified + * @arg val value to be matched (host byte-order) + * @arg mask mask to be applied before matching (host byte-order) + * @arg off offset, in bytes, to start matching + * @arg offmask offset mask +*/ +int rtnl_u32_add_key_uint16(struct rtnl_cls *cls, uint16_t val, uint16_t mask, + int off, int offmask) +{ + int shift = ((off & 3) == 0 ? 16 : 0); + if (off % 2) + return nl_error(EINVAL, "Invalid offset alignment"); + + return rtnl_u32_add_key(cls, htonl((uint32_t)val << shift), + htonl((uint32_t)mask << shift), + off & ~3, offmask); +} + +/** + * Append new selector key to match a 32-bit number + * + * @arg cls classifier to be modified + * @arg val value to be matched (host byte-order) + * @arg mask mask to be applied before matching (host byte-order) + * @arg off offset, in bytes, to start matching + * @arg offmask offset mask +*/ +int rtnl_u32_add_key_uint32(struct rtnl_cls *cls, uint32_t val, uint32_t mask, + int off, int offmask) +{ + return rtnl_u32_add_key(cls, htonl(val), htonl(mask), + off & ~3, offmask); +} + +int rtnl_u32_add_key_in_addr(struct rtnl_cls *cls, struct in_addr *addr, + uint8_t bitmask, int off, int offmask) +{ + uint32_t mask = 0xFFFFFFFF << (32 - bitmask); + return rtnl_u32_add_key(cls, addr->s_addr, htonl(mask), off, offmask); +} + +int rtnl_u32_add_key_in6_addr(struct rtnl_cls *cls, struct in6_addr *addr, + uint8_t bitmask, int off, int offmask) +{ + int i, err; + + for (i = 1; i <= 4; i++) { + if (32 * i - bitmask <= 0) { + if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1], + 0xFFFFFFFF, off+4*(i-1), offmask)) < 0) + return err; + } + else if (32 * i - bitmask < 32) { + uint32_t mask = 0xFFFFFFFF << (32 * i - bitmask); + if ((err = rtnl_u32_add_key(cls, addr->s6_addr32[i-1], + htonl(mask), off+4*(i-1), offmask)) < 0) + return err; + } + /* otherwise, if (32*i - bitmask >= 32) no key is generated */ + } + + return 0; +} + +/** @} */ + +static struct rtnl_cls_ops u32_ops = { + .co_kind = "u32", + .co_msg_parser = u32_msg_parser, + .co_free_data = u32_free_data, + .co_clone = u32_clone, + .co_get_opts = u32_get_opts, + .co_dump[NL_DUMP_BRIEF] = u32_dump_brief, + .co_dump[NL_DUMP_FULL] = u32_dump_full, + .co_dump[NL_DUMP_STATS] = u32_dump_stats, +}; + +static void __init u32_init(void) +{ + rtnl_cls_register(&u32_ops); +} + +static void __exit u32_exit(void) +{ + rtnl_cls_unregister(&u32_ops); +} + +/** @} */ diff --git a/lib/route/cls_api.c b/lib/route/cls_api.c new file mode 100644 index 0000000..f5a083a --- /dev/null +++ b/lib/route/cls_api.c @@ -0,0 +1,103 @@ +/* + * lib/route/cls_api.c Classifier Module API + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup cls + * @defgroup cls_api Classifier Modules + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct rtnl_cls_ops *cls_ops_list; + +/** + * @name Classifier Module API + * @{ + */ + +/** + * Register a classifier module + * @arg cops classifier module operations + */ +int rtnl_cls_register(struct rtnl_cls_ops *cops) +{ + struct rtnl_cls_ops *o, **op; + + if (!cops->co_kind) + BUG(); + + for (op = &cls_ops_list; (o = *op) != NULL; op = &o->co_next) + if (!strcasecmp(cops->co_kind, o->co_kind)) + return nl_errno(EEXIST); + + cops->co_next = NULL; + *op = cops; + + return 0; +} + +/** + * Unregister a classifier module + * @arg cops classifier module operations + */ +int rtnl_cls_unregister(struct rtnl_cls_ops *cops) +{ + struct rtnl_cls_ops *o, **op; + + for (op = &cls_ops_list; (o = *op) != NULL; op = &o->co_next) + if (!strcasecmp(cops->co_kind, o->co_kind)) + break; + + if (!o) + return nl_errno(ENOENT); + + *op = cops->co_next; + + return 0; +} + +struct rtnl_cls_ops *__rtnl_cls_lookup_ops(const char *kind) +{ + struct rtnl_cls_ops *cops; + + for (cops = cls_ops_list; cops; cops = cops->co_next) + if (!strcmp(kind, cops->co_kind)) + return cops; + + return NULL; +} + +/** + * Lookup classifier operations for a classifier object + * @arg cls Classifier object. + * + * @return Classifier operations or NULL if not found. + */ +struct rtnl_cls_ops *rtnl_cls_lookup_ops(struct rtnl_cls *cls) +{ + if (!cls->c_ops) + cls->c_ops = __rtnl_cls_lookup_ops(cls->c_kind); + + return cls->c_ops; +} + + +/** @} */ + +/** @} */ diff --git a/lib/route/cls_obj.c b/lib/route/cls_obj.c new file mode 100644 index 0000000..e12bc95 --- /dev/null +++ b/lib/route/cls_obj.c @@ -0,0 +1,203 @@ +/* + * lib/route/cls_api.c Classifier Object + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup cls + * @defgroup cls_obj Classifier Object + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define CLS_ATTR_PRIO (TCA_ATTR_MAX << 1) +#define CLS_ATTR_PROTOCOL (TCA_ATTR_MAX << 2) +/** @endcond */ + +static void cls_free_data(struct nl_object *obj) +{ + struct rtnl_cls *cls = (struct rtnl_cls *) obj; + struct rtnl_cls_ops *cops; + + tca_free_data((struct rtnl_tca *) cls); + + cops = rtnl_cls_lookup_ops(cls); + if (cops && cops->co_free_data) + cops->co_free_data(cls); +} + +static int cls_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_cls *dst = nl_object_priv(_dst); + struct rtnl_cls *src = nl_object_priv(_src); + struct rtnl_cls_ops *cops; + int err; + + err = tca_clone((struct rtnl_tca *) dst, (struct rtnl_tca *) src); + if (err < 0) + goto errout; + + cops = rtnl_cls_lookup_ops(src); + if (cops && cops->co_clone) + err = cops->co_clone(dst, src); +errout: + return err; +} + +static int cls_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +{ + char buf[32]; + struct rtnl_cls *cls = (struct rtnl_cls *) obj; + struct rtnl_cls_ops *cops; + int line; + + line = tca_dump_brief((struct rtnl_tca *) cls, "cls", p, 0); + + dp_dump(p, " prio %u protocol %s", cls->c_prio, + nl_ether_proto2str(cls->c_protocol, buf, sizeof(buf))); + + cops = rtnl_cls_lookup_ops(cls); + if (cops && cops->co_dump[NL_DUMP_BRIEF]) + line = cops->co_dump[NL_DUMP_BRIEF](cls, p, line); + dp_dump(p, "\n"); + + return line; +} + +static int cls_dump_full(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_cls *cls = (struct rtnl_cls *) obj; + struct rtnl_cls_ops *cops; + int line; + + line = cls_dump_brief(obj, p); + line = tca_dump_full((struct rtnl_tca *) cls, p, line); + + cops = rtnl_cls_lookup_ops(cls); + if (cops && cops->co_dump[NL_DUMP_FULL]) + line = cops->co_dump[NL_DUMP_FULL](cls, p, line); + else + dp_dump(p, "no options\n"); + + return line; +} + +static int cls_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_cls *cls = (struct rtnl_cls *) obj; + struct rtnl_cls_ops *cops; + int line; + + line = cls_dump_full(obj, p); + line = tca_dump_stats((struct rtnl_tca *) cls, p, line); + dp_dump(p, "\n"); + + cops = rtnl_cls_lookup_ops(cls); + if (cops && cops->co_dump[NL_DUMP_STATS]) + line = cops->co_dump[NL_DUMP_STATS](cls, p, line); + + return line; +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_cls *rtnl_cls_alloc(void) +{ + return (struct rtnl_cls *) nl_object_alloc(&cls_obj_ops); +} + +void rtnl_cls_put(struct rtnl_cls *cls) +{ + nl_object_put((struct nl_object *) cls); +} + +/** @} */ + + +/** + * @name Attributes + * @{ + */ + +void rtnl_cls_set_ifindex(struct rtnl_cls *f, int ifindex) +{ + tca_set_ifindex((struct rtnl_tca *) f, ifindex); +} + +void rtnl_cls_set_handle(struct rtnl_cls *f, uint32_t handle) +{ + tca_set_handle((struct rtnl_tca *) f, handle); +} + +void rtnl_cls_set_parent(struct rtnl_cls *f, uint32_t parent) +{ + tca_set_parent((struct rtnl_tca *) f, parent); +} + +void rtnl_cls_set_kind(struct rtnl_cls *f, const char *kind) +{ + tca_set_kind((struct rtnl_tca *) f, kind); + f->c_ops = __rtnl_cls_lookup_ops(kind); +} + +void rtnl_cls_set_prio(struct rtnl_cls *cls, int prio) +{ + cls->c_prio = prio; + cls->ce_mask |= CLS_ATTR_PRIO; +} + +int rtnl_cls_get_prio(struct rtnl_cls *cls) +{ + if (cls->ce_mask & CLS_ATTR_PRIO) + return cls->c_prio; + else + return 0; +} + +void rtnl_cls_set_protocol(struct rtnl_cls *cls, int protocol) +{ + cls->c_protocol = protocol; + cls->ce_mask |= CLS_ATTR_PROTOCOL; +} + +int rtnl_cls_get_protocol(struct rtnl_cls *cls) +{ + if (cls->ce_mask & CLS_ATTR_PROTOCOL) + return cls->c_protocol; + else + return ETH_P_ALL; +} + +/** @} */ + +struct nl_object_ops cls_obj_ops = { + .oo_name = "route/cls", + .oo_size = sizeof(struct rtnl_cls), + .oo_free_data = cls_free_data, + .oo_clone = cls_clone, + .oo_dump[NL_DUMP_BRIEF] = cls_dump_brief, + .oo_dump[NL_DUMP_FULL] = cls_dump_full, + .oo_dump[NL_DUMP_STATS] = cls_dump_stats, + .oo_compare = tca_compare, + .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), +}; + +/** @} */ diff --git a/lib/route/link.c b/lib/route/link.c new file mode 100644 index 0000000..7ecae12 --- /dev/null +++ b/lib/route/link.c @@ -0,0 +1,1313 @@ +/* + * lib/route/link.c Links (Interfaces) + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup link Links (Interfaces) + * @brief + * + * @par Link Identification + * A link can be identified by either its interface index or by its + * name. The kernel favours the interface index but falls back to the + * interface name if the interface index is lesser-than 0 for kernels + * >= 2.6.11. Therefore you can request changes without mapping a + * interface name to the corresponding index first. + * + * @par Changeable Attributes + * @anchor link_changeable + * - Link layer address + * - Link layer broadcast address + * - device mapping (ifmap) (>= 2.6.9) + * - MTU (>= 2.6.9) + * - Transmission queue length (>= 2.6.9) + * - Weight (>= 2.6.9) + * - Link name (only via access through interface index) (>= 2.6.9) + * - Flags (>= 2.6.9) + * - IFF_DEBUG + * - IFF_NOTRAILERS + * - IFF_NOARP + * - IFF_DYNAMIC + * - IFF_MULTICAST + * - IFF_PORTSEL + * - IFF_AUTOMEDIA + * - IFF_UP + * - IFF_PROMISC + * - IFF_ALLMULTI + * + * @par Link Flags (linux/if.h) + * @anchor link_flags + * @code + * IFF_UP Status of link (up|down) + * IFF_BROADCAST Indicates this link allows broadcasting + * IFF_MULTICAST Indicates this link allows multicasting + * IFF_ALLMULTI Indicates this link is doing multicast routing + * IFF_DEBUG Tell the driver to do debugging (currently unused) + * IFF_LOOPBACK This is the loopback link + * IFF_POINTOPOINT Point-to-point link + * IFF_NOARP Link is unable to perform ARP + * IFF_PROMISC Status of promiscious mode flag + * IFF_MASTER Used by teql + * IFF_SLAVE Used by teql + * IFF_PORTSEL Indicates this link allows port selection + * IFF_AUTOMEDIA Indicates this link selects port automatically + * IFF_DYNAMIC Indicates the address of this link is dynamic + * IFF_RUNNING Link is running and carrier is ok. + * IFF_NOTRAILERS Unused, BSD compat. + * @endcode + * + * @par Notes on IFF_PROMISC and IFF_ALLMULTI flags + * Although you can query the status of IFF_PROMISC and IFF_ALLMULTI + * they do not represent the actual state in the kernel but rather + * whether the flag has been enabled/disabled by userspace. The link + * may be in promiscious mode even if IFF_PROMISC is not set in a link + * dump request response because promiscity might be needed by the driver + * for a period of time. + * + * @note The unit of the transmission queue length depends on the + * link type, a common unit is \a packets. + * + * @par 1) Retrieving information about available links + * @code + * // The first step is to retrieve a list of all available interfaces within + * // the kernel and put them into a cache. + * struct nl_cache *cache = rtnl_link_alloc_cache(nl_handle); + * + * // In a second step, a specific link may be looked up by either interface + * // index or interface name. + * struct rtnl_link *link = rtnl_link_get_by_name(cache, "lo"); + * + * // rtnl_link_get_by_name() is the short version for translating the + * // interface name to an interface index first like this: + * int ifindex = rtnl_link_name2i(cache, "lo"); + * struct rtnl_link *link = rtnl_link_get(cache, ifindex); + * + * // After successful usage, the object must be given back to the cache + * rtnl_link_put(link); + * @endcode + * + * @par 2) Changing link attributes + * @code + * // In order to change any attributes of an existing link, we must allocate + * // a new link to hold the change requests: + * struct rtnl_link *request = rtnl_link_alloc(); + * + * // Now we can go on and specify the attributes we want to change: + * rtnl_link_set_weight(request, 300); + * rtnl_link_set_mtu(request, 1360); + * + * // We can also shut an interface down administratively + * rtnl_link_unset_flags(request, rtnl_link_str2flags("up")); + * + * // Actually, we should know which link to change, so let's look it up + * struct rtnl_link *old = rtnl_link_get(cache, "eth0"); + * + * // Two ways exist to commit this change request, the first one is to + * // build the required netlink message and send it out in one single + * // step: + * rtnl_link_change(nl_handle, old, request); + * + * // An alternative way is to build the netlink message and send it + * // out yourself using nl_send_auto_complete() + * struct nl_msg *msg = rtnl_link_build_change_request(old, request); + * nl_send_auto_complete(nl_handle, nlmsg_hdr(msg)); + * nlmsg_free(msg); + * + * // Don't forget to give back the link object ;-> + * rtnl_link_put(old); + * @endcode + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define LINK_ATTR_MTU 0x0001 +#define LINK_ATTR_LINK 0x0002 +#define LINK_ATTR_TXQLEN 0x0004 +#define LINK_ATTR_WEIGHT 0x0008 +#define LINK_ATTR_MASTER 0x0010 +#define LINK_ATTR_QDISC 0x0020 +#define LINK_ATTR_MAP 0x0040 +#define LINK_ATTR_ADDR 0x0080 +#define LINK_ATTR_BRD 0x0100 +#define LINK_ATTR_FLAGS 0x0200 +#define LINK_ATTR_IFNAME 0x0400 +#define LINK_ATTR_IFINDEX 0x0800 +#define LINK_ATTR_FAMILY 0x1000 +#define LINK_ATTR_ARPTYPE 0x2000 +#define LINK_ATTR_STATS 0x4000 +#define LINK_ATTR_CHANGE 0x8000 + +static struct nl_cache_ops rtnl_link_ops; +static struct nl_object_ops link_obj_ops; +/** @endcond */ + +static void link_free_data(struct nl_object *c) +{ + struct rtnl_link *link = nl_object_priv(c); + + if (link) { + nl_addr_put(link->l_addr); + nl_addr_put(link->l_bcast); + } +} + +static int link_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_link *dst = nl_object_priv(_dst); + struct rtnl_link *src = nl_object_priv(_src); + + if (src->l_addr) + if (!(dst->l_addr = nl_addr_clone(src->l_addr))) + goto errout; + + if (src->l_bcast) + if (!(dst->l_bcast = nl_addr_clone(src->l_bcast))) + goto errout; + + return 0; +errout: + return nl_get_errno(); +} + +static struct nla_policy link_policy[IFLA_MAX+1] = { + [IFLA_IFNAME] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [IFLA_MTU] = { .type = NLA_U32 }, + [IFLA_TXQLEN] = { .type = NLA_U32 }, + [IFLA_LINK] = { .type = NLA_U32 }, + [IFLA_WEIGHT] = { .type = NLA_U32 }, + [IFLA_MASTER] = { .type = NLA_U32 }, + [IFLA_QDISC] = { .type = NLA_STRING, + .maxlen = IFQDISCSIZ }, + [IFLA_STATS] = { .minlen = sizeof(struct rtnl_link_stats) }, + [IFLA_MAP] = { .minlen = sizeof(struct rtnl_link_ifmap) }, +}; + +static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + struct rtnl_link *link; + struct ifinfomsg *ifi; + struct nlattr *tb[IFLA_MAX+1]; + struct nl_parser_param *pp = arg; + int err; + + link = rtnl_link_alloc(); + if (link == NULL) { + err = nl_errno(ENOMEM); + goto errout; + } + + link->ce_msgtype = n->nlmsg_type; + + err = nlmsg_parse(n, sizeof(*ifi), tb, IFLA_MAX, link_policy); + if (err < 0) + goto errout; + + if (tb[IFLA_IFNAME] == NULL) { + err = nl_error(EINVAL, "Missing link name TLV"); + goto errout; + } + + nla_strlcpy(link->l_name, tb[IFLA_IFNAME], IFNAMSIZ); + + ifi = nlmsg_data(n); + link->l_family = ifi->ifi_family; + link->l_arptype = ifi->ifi_type; + link->l_index = ifi->ifi_index; + link->l_flags = ifi->ifi_flags; + link->l_change = ifi->ifi_change; + link->ce_mask = (LINK_ATTR_IFNAME | LINK_ATTR_FAMILY | + LINK_ATTR_ARPTYPE| LINK_ATTR_IFINDEX | + LINK_ATTR_FLAGS | LINK_ATTR_CHANGE); + + if (tb[IFLA_STATS]) { + struct rtnl_link_stats *st = nla_data(tb[IFLA_STATS]); + + link->l_stats[RTNL_LINK_RX_PACKETS] = st->rx_packets; + link->l_stats[RTNL_LINK_RX_BYTES] = st->rx_bytes; + link->l_stats[RTNL_LINK_RX_ERRORS] = st->rx_errors; + link->l_stats[RTNL_LINK_RX_DROPPED] = st->rx_dropped; + link->l_stats[RTNL_LINK_RX_COMPRESSED] = st->rx_compressed; + link->l_stats[RTNL_LINK_RX_FIFO_ERR] = st->rx_fifo_errors; + link->l_stats[RTNL_LINK_TX_PACKETS] = st->tx_packets; + link->l_stats[RTNL_LINK_TX_BYTES] = st->tx_bytes; + link->l_stats[RTNL_LINK_TX_ERRORS] = st->tx_errors; + link->l_stats[RTNL_LINK_TX_DROPPED] = st->tx_dropped; + link->l_stats[RTNL_LINK_TX_COMPRESSED] = st->tx_compressed; + link->l_stats[RTNL_LINK_TX_FIFO_ERR] = st->tx_fifo_errors; + link->l_stats[RTNL_LINK_RX_LEN_ERR] = st->rx_length_errors; + link->l_stats[RTNL_LINK_RX_OVER_ERR] = st->rx_over_errors; + link->l_stats[RTNL_LINK_RX_CRC_ERR] = st->rx_crc_errors; + link->l_stats[RTNL_LINK_RX_FRAME_ERR] = st->rx_frame_errors; + link->l_stats[RTNL_LINK_RX_MISSED_ERR] = st->rx_missed_errors; + link->l_stats[RTNL_LINK_TX_ABORT_ERR] = st->tx_aborted_errors; + link->l_stats[RTNL_LINK_TX_CARRIER_ERR] = st->tx_carrier_errors; + link->l_stats[RTNL_LINK_TX_HBEAT_ERR] = st->tx_heartbeat_errors; + link->l_stats[RTNL_LINK_TX_WIN_ERR] = st->tx_window_errors; + link->l_stats[RTNL_LINK_MULTICAST] = st->multicast; + + link->ce_mask |= LINK_ATTR_STATS; + } + + if (tb[IFLA_TXQLEN]) { + link->l_txqlen = nla_get_u32(tb[IFLA_TXQLEN]); + link->ce_mask |= LINK_ATTR_TXQLEN; + } + + if (tb[IFLA_MTU]) { + link->l_mtu = nla_get_u32(tb[IFLA_MTU]); + link->ce_mask |= LINK_ATTR_MTU; + } + + if (tb[IFLA_ADDRESS]) { + link->l_addr = nla_get_addr(tb[IFLA_ADDRESS], AF_UNSPEC); + if (link->l_addr == NULL) + goto errout; + nl_addr_set_family(link->l_addr, + nl_addr_guess_family(link->l_addr)); + link->ce_mask |= LINK_ATTR_ADDR; + } + + if (tb[IFLA_BROADCAST]) { + link->l_bcast = nla_get_addr(tb[IFLA_BROADCAST], AF_UNSPEC); + if (link->l_bcast == NULL) + goto errout; + nl_addr_set_family(link->l_bcast, + nl_addr_guess_family(link->l_bcast)); + link->ce_mask |= LINK_ATTR_BRD; + } + + if (tb[IFLA_LINK]) { + link->l_link = nla_get_u32(tb[IFLA_LINK]); + link->ce_mask |= LINK_ATTR_LINK; + } + + if (tb[IFLA_WEIGHT]) { + link->l_weight = nla_get_u32(tb[IFLA_WEIGHT]); + link->ce_mask |= LINK_ATTR_WEIGHT; + } + + if (tb[IFLA_QDISC]) { + nla_strlcpy(link->l_qdisc, tb[IFLA_QDISC], IFQDISCSIZ); + link->ce_mask |= LINK_ATTR_QDISC; + } + + if (tb[IFLA_MAP]) { + struct rtnl_link_ifmap *map = nla_data(tb[IFLA_MAP]); + link->l_map.lm_mem_start = map->mem_start; + link->l_map.lm_mem_end = map->mem_end; + link->l_map.lm_base_addr = map->base_addr; + link->l_map.lm_irq = map->irq; + link->l_map.lm_dma = map->dma; + link->l_map.lm_port = map->port; + link->ce_mask |= LINK_ATTR_MAP; + } + + if (tb[IFLA_MASTER]) { + link->l_master = nla_get_u32(tb[IFLA_MASTER]); + link->ce_mask |= LINK_ATTR_MASTER; + } + + err = pp->pp_cb((struct nl_object *) link, pp); + if (err < 0) + goto errout; + + return P_ACCEPT; + +errout: + rtnl_link_put(link); + return err; +} + +static int link_request_update(struct nl_cache *c, struct nl_handle *h) +{ + return nl_rtgen_request(h, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP); +} + +static int link_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +{ + char buf[128]; + struct nl_cache *cache = dp_cache(obj); + struct rtnl_link *link = (struct rtnl_link *) obj; + int line = 1; + + dp_dump(p, "%s ", link->l_name); + + if (link->ce_mask & LINK_ATTR_LINK) { + struct rtnl_link *ll = rtnl_link_get(cache, link->l_link); + dp_dump(p, "@%s", ll ? ll->l_name : "NONE"); + if (ll) + rtnl_link_put(ll); + } + + dp_dump(p, "%s ", nl_llproto2str(link->l_arptype, buf, sizeof(buf))); + dp_dump(p, "%s ", link->l_addr ? nl_addr2str(link->l_addr, buf, + sizeof(buf)) : "none"); + dp_dump(p, "mtu %u ", link->l_mtu); + + if (link->ce_mask & LINK_ATTR_MASTER) { + struct rtnl_link *master = rtnl_link_get(cache, link->l_master); + dp_dump(p, "master %s ", master ? master->l_name : "inv"); + if (master) + rtnl_link_put(master); + } + + rtnl_link_flags2str(link->l_flags, buf, sizeof(buf)); + if (buf[0]) + dp_dump(p, "<%s>", buf); + + dp_dump(p, "\n"); + + return line; +} + +static int link_dump_full(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + char buf[64]; + int line; + + line = link_dump_brief(obj, p); + dp_new_line(p, line++); + + dp_dump(p, " txqlen %u weight %u ", link->l_txqlen, link->l_weight); + + if (link->ce_mask & LINK_ATTR_QDISC) + dp_dump(p, "qdisc %s ", link->l_qdisc); + + if (link->ce_mask & LINK_ATTR_MAP && link->l_map.lm_irq) + dp_dump(p, "irq %u ", link->l_map.lm_irq); + + if (link->ce_mask & LINK_ATTR_IFINDEX) + dp_dump(p, "index %u ", link->l_index); + + if (link->ce_mask & LINK_ATTR_BRD) + dp_dump(p, "brd %s", nl_addr2str(link->l_bcast, buf, + sizeof(buf))); + + dp_dump(p, "\n"); + + return line; +} + +static int link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + char *unit, fmt[64]; + float res; + int line; + + line = link_dump_full(obj, p); + + dp_dump_line(p, line++, " Stats: bytes packets errors " + " dropped fifo-err compressed\n"); + + res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_RX_BYTES], &unit); + + strcpy(fmt, " RX %X.2f %s %10llu %10llu %10llu %10llu %10llu\n"); + fmt[9] = *unit == 'B' ? '9' : '7'; + + dp_dump_line(p, line++, fmt, + res, unit, + link->l_stats[RTNL_LINK_RX_PACKETS], + link->l_stats[RTNL_LINK_RX_ERRORS], + link->l_stats[RTNL_LINK_RX_DROPPED], + link->l_stats[RTNL_LINK_RX_FIFO_ERR], + link->l_stats[RTNL_LINK_RX_COMPRESSED]); + + res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_TX_BYTES], &unit); + + strcpy(fmt, " TX %X.2f %s %10llu %10llu %10llu %10llu %10llu\n"); + fmt[9] = *unit == 'B' ? '9' : '7'; + + dp_dump_line(p, line++, fmt, + res, unit, + link->l_stats[RTNL_LINK_TX_PACKETS], + link->l_stats[RTNL_LINK_TX_ERRORS], + link->l_stats[RTNL_LINK_TX_DROPPED], + link->l_stats[RTNL_LINK_TX_FIFO_ERR], + link->l_stats[RTNL_LINK_TX_COMPRESSED]); + + dp_dump_line(p, line++, " Errors: length over crc " + " frame missed multicast\n"); + + dp_dump_line(p, line++, " RX %10" PRIu64 " %10" PRIu64 " %10" + PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" + PRIu64 "\n", + link->l_stats[RTNL_LINK_RX_LEN_ERR], + link->l_stats[RTNL_LINK_RX_OVER_ERR], + link->l_stats[RTNL_LINK_RX_CRC_ERR], + link->l_stats[RTNL_LINK_RX_FRAME_ERR], + link->l_stats[RTNL_LINK_RX_MISSED_ERR], + link->l_stats[RTNL_LINK_MULTICAST]); + + dp_dump_line(p, line++, " Errors: aborted carrier heartbeat " + " window collision\n"); + + dp_dump_line(p, line++, " TX %10" PRIu64 " %10" PRIu64 " %10" + PRIu64 " %10" PRIu64 " %10" PRIu64 "\n", + link->l_stats[RTNL_LINK_TX_ABORT_ERR], + link->l_stats[RTNL_LINK_TX_CARRIER_ERR], + link->l_stats[RTNL_LINK_TX_HBEAT_ERR], + link->l_stats[RTNL_LINK_TX_WIN_ERR], + link->l_stats[RTNL_LINK_TX_COLLISIONS]); + + return line; +} + +static int link_dump_xml(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + struct nl_cache *cache = dp_cache(obj); + char buf[128]; + int i, line = 0; + + dp_dump_line(p, line++, "\n", + link->l_name, link->l_index); + dp_dump_line(p, line++, " %s\n", + nl_af2str(link->l_family, buf, sizeof(buf))); + dp_dump_line(p, line++, " %s\n", + nl_llproto2str(link->l_arptype, buf, sizeof(buf))); + dp_dump_line(p, line++, "
%s
\n", + nl_addr2str(link->l_addr, buf, sizeof(buf))); + dp_dump_line(p, line++, " %u\n", link->l_mtu); + dp_dump_line(p, line++, " %u\n", link->l_txqlen); + dp_dump_line(p, line++, " %u\n", link->l_weight); + + rtnl_link_flags2str(link->l_flags, buf, sizeof(buf)); + if (buf[0]) + dp_dump_line(p, line++, " %s\n", buf); + + if (link->ce_mask & LINK_ATTR_QDISC) + dp_dump_line(p, line++, " %s\n", link->l_qdisc); + + if (link->ce_mask & LINK_ATTR_LINK) { + struct rtnl_link *ll = rtnl_link_get(cache, link->l_link); + dp_dump_line(p, line++, " %s\n", + ll ? ll->l_name : "none"); + if (ll) + rtnl_link_put(ll); + } + + if (link->ce_mask & LINK_ATTR_MASTER) { + struct rtnl_link *master = rtnl_link_get(cache, link->l_master); + dp_dump_line(p, line++, " %s\n", + master ? master->l_name : "none"); + if (master) + rtnl_link_put(master); + } + + if (link->ce_mask & LINK_ATTR_BRD) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(link->l_bcast, buf, sizeof(buf))); + + if (link->ce_mask & LINK_ATTR_STATS) { + dp_dump_line(p, line++, " \n"); + for (i = 0; i <= RTNL_LINK_STATS_MAX; i++) { + rtnl_link_stat2str(i, buf, sizeof(buf)); + dp_dump_line(p, line++, + " <%s>%" PRIu64 "\n", + buf, link->l_stats[i], buf); + } + dp_dump_line(p, line++, " \n"); + } + + dp_dump_line(p, line++, "\n"); + +#if 0 + uint32_t l_change; /**< Change mask */ + struct rtnl_lifmap l_map; /**< Interface device mapping */ +#endif + + return line; +} + +static int link_dump_env(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + struct nl_cache *cache = dp_cache(obj); + char buf[128]; + int i, line = 0; + + dp_dump_line(p, line++, "LINK_NAME=%s\n", link->l_name); + dp_dump_line(p, line++, "LINK_IFINDEX=%u\n", link->l_index); + dp_dump_line(p, line++, "LINK_FAMILY=%s\n", + nl_af2str(link->l_family, buf, sizeof(buf))); + dp_dump_line(p, line++, "LINK_TYPE=%s\n", + nl_llproto2str(link->l_arptype, buf, sizeof(buf))); + if (link->ce_mask & LINK_ATTR_ADDR) + dp_dump_line(p, line++, "LINK_ADDRESS=%s\n", + nl_addr2str(link->l_addr, buf, sizeof(buf))); + dp_dump_line(p, line++, "LINK_MTU=%u\n", link->l_mtu); + dp_dump_line(p, line++, "LINK_TXQUEUELEN=%u\n", link->l_txqlen); + dp_dump_line(p, line++, "LINK_WEIGHT=%u\n", link->l_weight); + + rtnl_link_flags2str(link->l_flags & ~IFF_RUNNING, buf, sizeof(buf)); + if (buf[0]) + dp_dump_line(p, line++, "LINK_FLAGS=%s\n", buf); + + if (link->ce_mask & LINK_ATTR_QDISC) + dp_dump_line(p, line++, "LINK_QDISC=%s\n", link->l_qdisc); + + if (link->ce_mask & LINK_ATTR_LINK) { + struct rtnl_link *ll = rtnl_link_get(cache, link->l_link); + + dp_dump_line(p, line++, "LINK_LINK_IFINDEX=%d\n", link->l_link); + if (ll) { + dp_dump_line(p, line++, "LINK_LINK_IFNAME=%s\n", + ll->l_name); + rtnl_link_put(ll); + } + } + + if (link->ce_mask & LINK_ATTR_MASTER) { + struct rtnl_link *master = rtnl_link_get(cache, link->l_master); + dp_dump_line(p, line++, "LINK_MASTER=%s\n", + master ? master->l_name : "none"); + if (master) + rtnl_link_put(master); + } + + if (link->ce_mask & LINK_ATTR_BRD) + dp_dump_line(p, line++, "LINK_BROADCAST=%s\n", + nl_addr2str(link->l_bcast, buf, sizeof(buf))); + + if (link->ce_mask & LINK_ATTR_STATS) { + for (i = 0; i <= RTNL_LINK_STATS_MAX; i++) { + char *c = buf; + + sprintf(buf, "LINK_"); + rtnl_link_stat2str(i, buf + 5, sizeof(buf) - 5); + while (*c) { + *c = toupper(*c); + c++; + } + dp_dump_line(p, line++, + "%s=%" PRIu64 "\n", buf, link->l_stats[i]); + } + } + + return line; +} + +#if 0 +static int link_handle_event(struct nl_object *a, struct rtnl_link_event_cb *cb) +{ + struct rtnl_link *l = (struct rtnl_link *) a; + struct nl_cache *c = dp_cache(a); + int nevents = 0; + + if (l->l_change == ~0U) { + if (l->ce_msgtype == RTM_NEWLINK) + cb->le_register(l); + else + cb->le_unregister(l); + + return 1; + } + + if (l->l_change & IFF_SLAVE) { + if (l->l_flags & IFF_SLAVE) { + struct rtnl_link *m = rtnl_link_get(c, l->l_master); + cb->le_new_bonding(l, m); + if (m) + rtnl_link_put(m); + } else + cb->le_cancel_bonding(l); + } + +#if 0 + if (l->l_change & IFF_UP && l->l_change & IFF_RUNNING) + dp_dump_line(p, line++, "link %s changed state to %s.\n", + l->l_name, l->l_flags & IFF_UP ? "up" : "down"); + + if (l->l_change & IFF_PROMISC) { + dp_new_line(p, line++); + dp_dump(p, "link %s %s promiscuous mode.\n", + l->l_name, l->l_flags & IFF_PROMISC ? "entered" : "left"); + } + + if (line == 0) + dp_dump_line(p, line++, "link %s sent unknown event.\n", + l->l_name); +#endif + + return nevents; +} +#endif + +static int link_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) +{ + struct rtnl_link *a = (struct rtnl_link *) _a; + struct rtnl_link *b = (struct rtnl_link *) _b; + int diff = 0; + +#define LINK_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, LINK_ATTR_##ATTR, a, b, EXPR) + + diff |= LINK_DIFF(IFINDEX, a->l_index != b->l_index); + diff |= LINK_DIFF(MTU, a->l_mtu != b->l_mtu); + diff |= LINK_DIFF(LINK, a->l_link != b->l_link); + diff |= LINK_DIFF(TXQLEN, a->l_txqlen != b->l_txqlen); + diff |= LINK_DIFF(WEIGHT, a->l_weight != b->l_weight); + diff |= LINK_DIFF(MASTER, a->l_master != b->l_master); + diff |= LINK_DIFF(FAMILY, a->l_family != b->l_family); + diff |= LINK_DIFF(QDISC, strcmp(a->l_qdisc, b->l_qdisc)); + diff |= LINK_DIFF(IFNAME, strcmp(a->l_name, b->l_name)); + diff |= LINK_DIFF(ADDR, nl_addr_cmp(a->l_addr, b->l_addr)); + diff |= LINK_DIFF(BRD, nl_addr_cmp(a->l_bcast, b->l_bcast)); + + if (flags & LOOSE_FLAG_COMPARISON) + diff |= LINK_DIFF(FLAGS, + (a->l_flags ^ b->l_flags) & b->l_flag_mask); + else + diff |= LINK_DIFF(FLAGS, a->l_flags != b->l_flags); + +#undef LINK_DIFF + + return diff; +} + +static struct trans_tbl link_attrs[] = { + __ADD(LINK_ATTR_MTU, mtu) + __ADD(LINK_ATTR_LINK, link) + __ADD(LINK_ATTR_TXQLEN, txqlen) + __ADD(LINK_ATTR_WEIGHT, weight) + __ADD(LINK_ATTR_MASTER, master) + __ADD(LINK_ATTR_QDISC, qdisc) + __ADD(LINK_ATTR_MAP, map) + __ADD(LINK_ATTR_ADDR, address) + __ADD(LINK_ATTR_BRD, broadcast) + __ADD(LINK_ATTR_FLAGS, flags) + __ADD(LINK_ATTR_IFNAME, name) + __ADD(LINK_ATTR_IFINDEX, ifindex) + __ADD(LINK_ATTR_FAMILY, family) + __ADD(LINK_ATTR_ARPTYPE, arptype) + __ADD(LINK_ATTR_STATS, stats) + __ADD(LINK_ATTR_CHANGE, change) +}; + +static char *link_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, link_attrs, + ARRAY_SIZE(link_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_link *rtnl_link_alloc(void) +{ + return (struct rtnl_link *) nl_object_alloc(&link_obj_ops); +} + +void rtnl_link_put(struct rtnl_link *link) +{ + nl_object_put((struct nl_object *) link); +} + +/** @} */ + +/** + * @name Cache Management + * @{ + */ + + +/** + * Allocate link cache and fill in all configured links. + * @arg handle Netlink handle. + * + * Allocates a new link cache, initializes it properly and updates it + * to include all links currently configured in the kernel. + * + * @note Free the memory after usage. + * @return Newly allocated cache or NULL if an error occured. + */ +struct nl_cache *rtnl_link_alloc_cache(struct nl_handle *handle) +{ + struct nl_cache * cache; + + cache = nl_cache_alloc(&rtnl_link_ops); + if (cache == NULL) + return NULL; + + if (handle && nl_cache_refill(handle, cache) < 0) { + nl_cache_free(cache); + return NULL; + } + + return cache; +} + +/** + * Look up link by interface index in the provided cache + * @arg cache link cache + * @arg ifindex link interface index + * + * The caller owns a reference on the returned object and + * must give the object back via rtnl_link_put(). + * + * @return pointer to link inside the cache or NULL if no match was found. + */ +struct rtnl_link *rtnl_link_get(struct nl_cache *cache, int ifindex) +{ + struct rtnl_link *link; + + if (cache->c_ops != &rtnl_link_ops) + return NULL; + + nl_list_for_each_entry(link, &cache->c_items, ce_list) { + if (link->l_index == ifindex) { + nl_object_get((struct nl_object *) link); + return link; + } + } + + return NULL; +} + +/** + * Look up link by link name in the provided cache + * @arg cache link cache + * @arg name link name + * + * The caller owns a reference on the returned object and + * must give the object back via rtnl_link_put(). + * + * @return pointer to link inside the cache or NULL if no match was found. + */ +struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache, + const char *name) +{ + struct rtnl_link *link; + + if (cache->c_ops != &rtnl_link_ops) + return NULL; + + nl_list_for_each_entry(link, &cache->c_items, ce_list) { + if (!strcmp(name, link->l_name)) { + nl_object_get((struct nl_object *) link); + return link; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Link Modifications + * @{ + */ + +/** + * Builds a netlink change request message to change link attributes + * @arg old link to be changed + * @arg tmpl template with requested changes + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting a change of link attributes. + * The netlink message header isn't fully equipped with all relevant + * fields and must be sent out via nl_send_auto_complete() or + * supplemented as needed. + * \a old must point to a link currently configured in the kernel + * and \a tmpl must contain the attributes to be changed set via + * \c rtnl_link_set_* functions. + * + * @return New netlink message + * @note Not all attributes can be changed, see + * \ref link_changeable "Changeable Attributes" for more details. + */ +struct nl_msg * rtnl_link_build_change_request(struct rtnl_link *old, + struct rtnl_link *tmpl, + int flags) +{ + struct nl_msg *msg; + struct ifinfomsg ifi = { + .ifi_family = old->l_family, + .ifi_index = old->l_index, + }; + + if (tmpl->ce_mask & LINK_ATTR_FLAGS) { + ifi.ifi_flags = old->l_flags & ~tmpl->l_flag_mask; + ifi.ifi_flags |= tmpl->l_flags; + } + + msg = nlmsg_alloc_simple(RTM_SETLINK, flags); + if (!msg) + goto nla_put_failure; + + if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (tmpl->ce_mask & LINK_ATTR_ADDR) + NLA_PUT_ADDR(msg, IFLA_ADDRESS, tmpl->l_addr); + + if (tmpl->ce_mask & LINK_ATTR_BRD) + NLA_PUT_ADDR(msg, IFLA_BROADCAST, tmpl->l_bcast); + + if (tmpl->ce_mask & LINK_ATTR_MTU) + NLA_PUT_U32(msg, IFLA_MTU, tmpl->l_mtu); + + if (tmpl->ce_mask & LINK_ATTR_TXQLEN) + NLA_PUT_U32(msg, IFLA_TXQLEN, tmpl->l_txqlen); + + if (tmpl->ce_mask & LINK_ATTR_WEIGHT) + NLA_PUT_U32(msg, IFLA_WEIGHT, tmpl->l_weight); + + if (tmpl->ce_mask & LINK_ATTR_IFNAME) + NLA_PUT_STRING(msg, IFLA_IFNAME, tmpl->l_name); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +/** + * Change link attributes + * @arg handle netlink handle + * @arg old link to be changed + * @arg tmpl template with requested changes + * @arg flags additional netlink message flags + * + * Builds a new netlink message by calling rtnl_link_build_change_request(), + * sends the request to the kernel and waits for the next ACK to be + * received, i.e. blocks until the request has been processed. + * + * @return 0 on success or a negative error code + * @note Not all attributes can be changed, see + * \ref link_changeable "Changeable Attributes" for more details. + */ +int rtnl_link_change(struct nl_handle *handle, struct rtnl_link *old, + struct rtnl_link *tmpl, int flags) +{ + int err; + struct nl_msg *msg; + + msg = rtnl_link_build_change_request(old, tmpl, flags); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name Name <-> Index Translations + * @{ + */ + +/** + * Translate an interface index to the corresponding link name + * @arg cache link cache + * @arg ifindex link interface index + * @arg dst destination buffer + * @arg len length of destination buffer + * + * Translates the specified interface index to the corresponding + * link name and stores the name in the destination buffer. + * + * @return link name or NULL if no match was found. + */ +char * rtnl_link_i2name(struct nl_cache *cache, int ifindex, char *dst, + size_t len) +{ + struct rtnl_link *link = rtnl_link_get(cache, ifindex); + + if (link) { + strncpy(dst, link->l_name, len - 1); + rtnl_link_put(link); + return dst; + } + + return NULL; +} + +/** + * Translate a link name to the corresponding interface index + * @arg cache link cache + * @arg name link name + * + * @return interface index or RTNL_LINK_NOT_FOUND if no match was found. + */ +int rtnl_link_name2i(struct nl_cache *cache, const char *name) +{ + int ifindex = RTNL_LINK_NOT_FOUND; + struct rtnl_link *link; + + link = rtnl_link_get_by_name(cache, name); + if (link) { + ifindex = link->l_index; + rtnl_link_put(link); + } + + return ifindex; +} + +/** @} */ + +/** + * @name Link Flags Translations + * @{ + */ + +static struct trans_tbl link_flags[] = { + __ADD(IFF_LOOPBACK, loopback) + __ADD(IFF_BROADCAST, broadcast) + __ADD(IFF_POINTOPOINT, pointopoint) + __ADD(IFF_MULTICAST, multicast) + __ADD(IFF_NOARP, noarp) + __ADD(IFF_ALLMULTI, allmulti) + __ADD(IFF_PROMISC, promisc) + __ADD(IFF_MASTER, master) + __ADD(IFF_SLAVE, slave) + __ADD(IFF_DEBUG, debug) + __ADD(IFF_DYNAMIC, dynamic) + __ADD(IFF_AUTOMEDIA, automedia) + __ADD(IFF_PORTSEL, portsel) + __ADD(IFF_NOTRAILERS, notrailers) + __ADD(IFF_UP, up) + __ADD(IFF_RUNNING, running) + __ADD(IFF_LOWER_UP, lowerup) + __ADD(IFF_DORMANT, dormant) +}; + +char * rtnl_link_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, link_flags, + ARRAY_SIZE(link_flags)); +} + +int rtnl_link_str2flags(const char *name) +{ + return __str2flags(name, link_flags, ARRAY_SIZE(link_flags)); +} + +/** @} */ + +/** + * @name Link Statistics Translations + * @{ + */ + +static struct trans_tbl link_stats[] = { + __ADD(RTNL_LINK_RX_PACKETS, rx_packets) + __ADD(RTNL_LINK_TX_PACKETS, tx_packets) + __ADD(RTNL_LINK_RX_BYTES, rx_bytes) + __ADD(RTNL_LINK_TX_BYTES, tx_bytes) + __ADD(RTNL_LINK_RX_ERRORS, rx_errors) + __ADD(RTNL_LINK_TX_ERRORS, tx_errors) + __ADD(RTNL_LINK_RX_DROPPED, rx_dropped) + __ADD(RTNL_LINK_TX_DROPPED, tx_dropped) + __ADD(RTNL_LINK_RX_COMPRESSED, rx_compressed) + __ADD(RTNL_LINK_TX_COMPRESSED, tx_compressed) + __ADD(RTNL_LINK_RX_FIFO_ERR, rx_fifo_err) + __ADD(RTNL_LINK_TX_FIFO_ERR, tx_fifo_err) + __ADD(RTNL_LINK_RX_LEN_ERR, rx_len_err) + __ADD(RTNL_LINK_RX_OVER_ERR, rx_over_err) + __ADD(RTNL_LINK_RX_CRC_ERR, rx_crc_err) + __ADD(RTNL_LINK_RX_FRAME_ERR, rx_frame_err) + __ADD(RTNL_LINK_RX_MISSED_ERR, rx_missed_err) + __ADD(RTNL_LINK_TX_ABORT_ERR, tx_abort_err) + __ADD(RTNL_LINK_TX_CARRIER_ERR, tx_carrier_err) + __ADD(RTNL_LINK_TX_HBEAT_ERR, tx_hbeat_err) + __ADD(RTNL_LINK_TX_WIN_ERR, tx_win_err) + __ADD(RTNL_LINK_TX_COLLISIONS, tx_collision) + __ADD(RTNL_LINK_MULTICAST, multicast) +}; + +char *rtnl_link_stat2str(int st, char *buf, size_t len) +{ + return __type2str(st, buf, len, link_stats, ARRAY_SIZE(link_stats)); +} + +int rtnl_link_str2stat(const char *name) +{ + return __str2type(name, link_stats, ARRAY_SIZE(link_stats)); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void rtnl_link_set_qdisc(struct rtnl_link *link, const char *qdisc) +{ + strncpy(link->l_qdisc, qdisc, sizeof(link->l_qdisc) - 1); + link->ce_mask |= LINK_ATTR_QDISC; +} + +char *rtnl_link_get_qdisc(struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_QDISC) + return link->l_qdisc; + else + return NULL; +} + +void rtnl_link_set_name(struct rtnl_link *link, const char *name) +{ + strncpy(link->l_name, name, sizeof(link->l_name) - 1); + link->ce_mask |= LINK_ATTR_IFNAME; +} + +char *rtnl_link_get_name(struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_IFNAME) + return link->l_name; + else + return NULL; +} + +static inline void __assign_addr(struct rtnl_link *link, struct nl_addr **pos, + struct nl_addr *new, int flag) +{ + if (*pos) + nl_addr_put(*pos); + + nl_addr_get(new); + *pos = new; + + link->ce_mask |= flag; +} + +void rtnl_link_set_addr(struct rtnl_link *link, struct nl_addr *addr) +{ + __assign_addr(link, &link->l_addr, addr, LINK_ATTR_ADDR); +} + +struct nl_addr *rtnl_link_get_addr(struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_ADDR) + return link->l_addr; + else + return NULL; +} + +void rtnl_link_set_broadcast(struct rtnl_link *link, struct nl_addr *brd) +{ + __assign_addr(link, &link->l_bcast, brd, LINK_ATTR_BRD); +} + +struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_BRD) + return link->l_bcast; + else + return NULL; +} + +void rtnl_link_set_flags(struct rtnl_link *link, unsigned int flags) +{ + link->l_flag_mask |= flags; + link->l_flags |= flags; + link->ce_mask |= LINK_ATTR_FLAGS; +} + +void rtnl_link_unset_flags(struct rtnl_link *link, unsigned int flags) +{ + link->l_flag_mask |= flags; + link->l_flags &= ~flags; + link->ce_mask |= LINK_ATTR_FLAGS; +} + +unsigned int rtnl_link_get_flags(struct rtnl_link *link) +{ + return link->l_flags; +} + +void rtnl_link_set_family(struct rtnl_link *link, int family) +{ + link->l_family = family; + link->ce_mask |= LINK_ATTR_FAMILY; +} + +int rtnl_link_get_family(struct rtnl_link *link) +{ + if (link->l_family & LINK_ATTR_FAMILY) + return link->l_family; + else + return AF_UNSPEC; +} + +void rtnl_link_set_arptype(struct rtnl_link *link, unsigned int arptype) +{ + link->l_arptype = arptype; +} + +unsigned int rtnl_link_get_arptype(struct rtnl_link *link) +{ + return link->l_arptype; +} + +void rtnl_link_set_ifindex(struct rtnl_link *link, int ifindex) +{ + link->l_index = ifindex; + link->ce_mask |= LINK_ATTR_IFINDEX; +} + +int rtnl_link_get_ifindex(struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_IFINDEX) + return link->l_index; + else + return RTNL_LINK_NOT_FOUND; +} + +void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu) +{ + link->l_mtu = mtu; + link->ce_mask |= LINK_ATTR_MTU; +} + +unsigned int rtnl_link_get_mtu(struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_MTU) + return link->l_mtu; + else + return 0; +} + +void rtnl_link_set_txqlen(struct rtnl_link *link, unsigned int txqlen) +{ + link->l_txqlen = txqlen; + link->ce_mask |= LINK_ATTR_TXQLEN; +} + +unsigned int rtnl_link_get_txqlen(struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_TXQLEN) + return link->l_txqlen; + else + return UINT_MAX; +} + +void rtnl_link_set_weight(struct rtnl_link *link, unsigned int weight) +{ + link->l_weight = weight; + link->ce_mask |= LINK_ATTR_WEIGHT; +} + +unsigned int rtnl_link_get_weight(struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_WEIGHT) + return link->l_weight; + else + return UINT_MAX; +} + +void rtnl_link_set_link(struct rtnl_link *link, int ifindex) +{ + link->l_link = ifindex; + link->ce_mask |= LINK_ATTR_LINK; +} + +int rtnl_link_get_link(struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_LINK) + return link->l_link; + else + return RTNL_LINK_NOT_FOUND; +} + +void rtnl_link_set_master(struct rtnl_link *link, int ifindex) +{ + link->l_master = ifindex; + link->ce_mask |= LINK_ATTR_MASTER; +} + +int rtnl_link_get_master(struct rtnl_link *link) +{ + if (link->ce_mask & LINK_ATTR_MASTER) + return link->l_master; + else + return RTNL_LINK_NOT_FOUND; +} + +uint64_t rtnl_link_get_stat(struct rtnl_link *link, int id) +{ + if (id < 0 || id > RTNL_LINK_STATS_MAX) + return 0; + + return link->l_stats[id]; +} + +/** @} */ + +static struct nl_object_ops link_obj_ops = { + .oo_name = "route/link", + .oo_size = sizeof(struct rtnl_link), + .oo_free_data = link_free_data, + .oo_clone = link_clone, + .oo_dump[NL_DUMP_BRIEF] = link_dump_brief, + .oo_dump[NL_DUMP_FULL] = link_dump_full, + .oo_dump[NL_DUMP_STATS] = link_dump_stats, + .oo_dump[NL_DUMP_XML] = link_dump_xml, + .oo_dump[NL_DUMP_ENV] = link_dump_env, + .oo_compare = link_compare, + .oo_attrs2str = link_attrs2str, + .oo_id_attrs = LINK_ATTR_IFINDEX, +}; + +static struct nl_af_group link_groups[] = { + { AF_UNSPEC, RTNLGRP_LINK }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_link_ops = { + .co_name = "route/link", + .co_hdrsize = sizeof(struct ifinfomsg), + .co_msgtypes = { + { RTM_NEWLINK, NL_ACT_NEW, "new" }, + { RTM_DELLINK, NL_ACT_DEL, "del" }, + { RTM_GETLINK, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = link_groups, + .co_request_update = link_request_update, + .co_msg_parser = link_msg_parser, + .co_obj_ops = &link_obj_ops, +}; + +static void __init link_init(void) +{ + nl_cache_mngt_register(&rtnl_link_ops); +} + +static void __exit link_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_link_ops); +} + +/** @} */ diff --git a/lib/route/neigh.c b/lib/route/neigh.c new file mode 100644 index 0000000..aac4718 --- /dev/null +++ b/lib/route/neigh.c @@ -0,0 +1,1023 @@ +/* + * lib/route/neigh.c Neighbours + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup neigh Neighbours + * @brief + * + * The neighbour table establishes bindings between protocol addresses and + * link layer addresses for hosts sharing the same physical link. This + * module allows you to access and manipulate the content of these tables. + * + * @par Neighbour States + * @code + * NUD_INCOMPLETE + * NUD_REACHABLE + * NUD_STALE + * NUD_DELAY + * NUD_PROBE + * NUD_FAILED + * NUD_NOARP + * NUD_PERMANENT + * @endcode + * + * @par Neighbour Flags + * @code + * NTF_PROXY + * NTF_ROUTER + * @endcode + * + * @par Neighbour Identification + * A neighbour is uniquely identified by the attributes listed below, whenever + * you refer to an existing neighbour all of the attributes must be set. + * Neighbours from caches automatically have all required attributes set. + * - interface index (rtnl_neigh_set_ifindex()) + * - destination address (rtnl_neigh_set_dst()) + * + * @par Changeable Attributes + * \anchor neigh_changeable + * - state (rtnl_neigh_set_state()) + * - link layer address (rtnl_neigh_set_lladdr()) + * + * @par Required Caches for Dumping + * In order to dump neighbour attributes you must provide the following + * caches via nl_cache_provide() + * - link cache holding all links + * + * @par TODO + * - Document proxy settings + * - Document states and their influence + * + * @par 1) Retrieving information about configured neighbours + * @code + * // The first step is to retrieve a list of all available neighbour within + * // the kernel and put them into a cache. + * struct nl_cache *cache = rtnl_neigh_alloc_cache(handle); + * + * // Neighbours can then be looked up by the interface and destination + * // address: + * struct rtnl_neigh *neigh = rtnl_neigh_get(cache, ifindex, dst_addr); + * + * // After successful usage, the object must be given back to the cache + * rtnl_neigh_put(neigh); + * @endcode + * + * @par 2) Adding new neighbours + * @code + * // Allocate an empty neighbour handle to be filled out with the attributes + * // of the new neighbour. + * struct rtnl_neigh *neigh = rtnl_neigh_alloc(); + * + * // Fill out the attributes of the new neighbour + * rtnl_neigh_set_ifindex(neigh, ifindex); + * rtnl_neigh_set_dst(neigh, dst_addr); + * rtnl_neigh_set_state(neigh, rtnl_neigh_str2state("permanent")); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_neigh_build_add_request() + * // to be sent out using nl_send_auto_complete(). + * rtnl_neigh_add(nl_handle, neigh, NLM_F_REPLACE); + * + * // Free the memory + * rtnl_neigh_put(neigh); + * @endcode + * + * @par 3) Deleting an existing neighbour + * @code + * // Allocate an empty neighbour object to be filled out with the attributes + * // matching the neighbour to be deleted. Alternatively a fully equipped + * // neighbour object out of a cache can be used instead. + * struct rtnl_neigh *neigh = rtnl_neigh_alloc(); + * + * // Neighbours are uniquely identified by their interface index and + * // destination address, you may fill out other attributes but they + * // will have no influence. + * rtnl_neigh_set_ifindex(neigh, ifindex); + * rtnl_neigh_set_dst(neigh, dst_addr); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_neigh_build_delete_request() + * // to be sent out using nl_send_auto_complete(). + * rtnl_neigh_delete(handle, neigh, 0); + * + * // Free the memory + * rtnl_neigh_put(neigh); + * @endcode + * + * @par 4) Changing neighbour attributes + * @code + * // Allocate an empty neighbour object to be filled out with the attributes + * // matching the neighbour to be changed and the new parameters. Alternatively + * // a fully equipped modified neighbour object out of a cache can be used. + * struct rtnl_neigh *neigh = rtnl_neigh_alloc(); + * + * // Identify the neighbour to be changed by its interface index and + * // destination address + * rtnl_neigh_set_ifindex(neigh, ifindex); + * rtnl_neigh_set_dst(neigh, dst_addr); + * + * // The link layer address may be modified, if so it is wise to change + * // its state to "permanent" in order to avoid having it overwritten. + * rtnl_neigh_set_lladdr(neigh, lladdr); + * + * // Secondly the state can be modified allowing normal neighbours to be + * // converted into permanent entries or to manually confirm a neighbour. + * rtnl_neigh_set_state(neigh, state); + * + * // Build the netlink message and send it to the kernel, the operation will + * // block until the operation has been completed. Alternatively the required + * // netlink message can be built using rtnl_neigh_build_change_request() + * // to be sent out using nl_send_auto_complete(). + * rtnl_neigh_change(handle, neigh, 0); + * + * // Free the memory + * rtnl_neigh_put(neigh); + * @endcode + * @{ + */ + +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define NEIGH_ATTR_FLAGS 0x01 +#define NEIGH_ATTR_STATE 0x02 +#define NEIGH_ATTR_LLADDR 0x04 +#define NEIGH_ATTR_DST 0x08 +#define NEIGH_ATTR_CACHEINFO 0x10 +#define NEIGH_ATTR_IFINDEX 0x20 +#define NEIGH_ATTR_FAMILY 0x40 +#define NEIGH_ATTR_TYPE 0x80 +#define NEIGH_ATTR_PROBES 0x100 + +static struct nl_cache_ops rtnl_neigh_ops; +static struct nl_object_ops neigh_obj_ops; +/** @endcond */ + +static void neigh_free_data(struct nl_object *c) +{ + struct rtnl_neigh *neigh = nl_object_priv(c); + + if (!neigh) + return; + + nl_addr_put(neigh->n_lladdr); + nl_addr_put(neigh->n_dst); +} + +static int neigh_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_neigh *dst = nl_object_priv(_dst); + struct rtnl_neigh *src = nl_object_priv(_src); + + if (src->n_lladdr) + if (!(dst->n_lladdr = nl_addr_clone(src->n_lladdr))) + goto errout; + + if (src->n_dst) + if (!(dst->n_dst = nl_addr_clone(src->n_dst))) + goto errout; + + return 0; +errout: + return nl_get_errno(); +} + +static int neigh_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) +{ + struct rtnl_neigh *a = (struct rtnl_neigh *) _a; + struct rtnl_neigh *b = (struct rtnl_neigh *) _b; + int diff = 0; + +#define NEIGH_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NEIGH_ATTR_##ATTR, a, b, EXPR) + + diff |= NEIGH_DIFF(IFINDEX, a->n_ifindex != b->n_ifindex); + diff |= NEIGH_DIFF(FAMILY, a->n_family != b->n_family); + diff |= NEIGH_DIFF(TYPE, a->n_type != b->n_type); + diff |= NEIGH_DIFF(LLADDR, nl_addr_cmp(a->n_lladdr, b->n_lladdr)); + diff |= NEIGH_DIFF(DST, nl_addr_cmp(a->n_dst, b->n_dst)); + + if (flags & LOOSE_FLAG_COMPARISON) { + diff |= NEIGH_DIFF(STATE, + (a->n_state ^ b->n_state) & b->n_state_mask); + diff |= NEIGH_DIFF(FLAGS, + (a->n_flags ^ b->n_flags) & b->n_flag_mask); + } else { + diff |= NEIGH_DIFF(STATE, a->n_state != b->n_state); + diff |= NEIGH_DIFF(FLAGS, a->n_flags != b->n_flags); + } + +#undef NEIGH_DIFF + + return diff; +} + +static struct trans_tbl neigh_attrs[] = { + __ADD(NEIGH_ATTR_FLAGS, flags) + __ADD(NEIGH_ATTR_STATE, state) + __ADD(NEIGH_ATTR_LLADDR, lladdr) + __ADD(NEIGH_ATTR_DST, dst) + __ADD(NEIGH_ATTR_CACHEINFO, cacheinfo) + __ADD(NEIGH_ATTR_IFINDEX, ifindex) + __ADD(NEIGH_ATTR_FAMILY, family) + __ADD(NEIGH_ATTR_TYPE, type) + __ADD(NEIGH_ATTR_PROBES, probes) +}; + +static char *neigh_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, neigh_attrs, + ARRAY_SIZE(neigh_attrs)); +} + +static struct nla_policy neigh_policy[NDA_MAX+1] = { + [NDA_CACHEINFO] = { .minlen = sizeof(struct nda_cacheinfo) }, + [NDA_PROBES] = { .type = NLA_U32 }, +}; + +static int neigh_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + struct rtnl_neigh *neigh; + struct nlattr *tb[NDA_MAX + 1]; + struct nl_parser_param *pp = arg; + struct ndmsg *nm; + int err; + + neigh = rtnl_neigh_alloc(); + if (!neigh) { + err = nl_errno(ENOMEM); + goto errout; + } + + neigh->ce_msgtype = n->nlmsg_type; + nm = nlmsg_data(n); + + err = nlmsg_parse(n, sizeof(*nm), tb, NDA_MAX, neigh_policy); + if (err < 0) + goto errout; + + neigh->n_family = nm->ndm_family; + neigh->n_ifindex = nm->ndm_ifindex; + neigh->n_state = nm->ndm_state; + neigh->n_flags = nm->ndm_flags; + neigh->n_type = nm->ndm_type; + + neigh->ce_mask |= (NEIGH_ATTR_FAMILY | NEIGH_ATTR_IFINDEX | + NEIGH_ATTR_STATE | NEIGH_ATTR_FLAGS | + NEIGH_ATTR_TYPE); + + if (tb[NDA_LLADDR]) { + neigh->n_lladdr = nla_get_addr(tb[NDA_LLADDR], AF_UNSPEC); + if (!neigh->n_lladdr) + goto errout; + nl_addr_set_family(neigh->n_lladdr, + nl_addr_guess_family(neigh->n_lladdr)); + neigh->ce_mask |= NEIGH_ATTR_LLADDR; + } + + if (tb[NDA_DST]) { + neigh->n_dst = nla_get_addr(tb[NDA_DST], neigh->n_family); + if (!neigh->n_dst) + goto errout; + neigh->ce_mask |= NEIGH_ATTR_DST; + } + + if (tb[NDA_CACHEINFO]) { + struct nda_cacheinfo *ci = nla_data(tb[NDA_CACHEINFO]); + + neigh->n_cacheinfo.nci_confirmed = ci->ndm_confirmed; + neigh->n_cacheinfo.nci_used = ci->ndm_used; + neigh->n_cacheinfo.nci_updated = ci->ndm_updated; + neigh->n_cacheinfo.nci_refcnt = ci->ndm_refcnt; + + neigh->ce_mask |= NEIGH_ATTR_CACHEINFO; + } + + if (tb[NDA_PROBES]) { + neigh->n_probes = nla_get_u32(tb[NDA_PROBES]); + neigh->ce_mask |= NEIGH_ATTR_PROBES; + } + + err = pp->pp_cb((struct nl_object *) neigh, pp); + if (err < 0) + goto errout; + + return P_ACCEPT; + +errout: + rtnl_neigh_put(neigh); + return err; +} + +static int neigh_request_update(struct nl_cache *c, struct nl_handle *h) +{ + return nl_rtgen_request(h, RTM_GETNEIGH, AF_UNSPEC, NLM_F_DUMP); +} + + +static int neigh_dump_brief(struct nl_object *a, struct nl_dump_params *p) +{ + char dst[INET6_ADDRSTRLEN+5], lladdr[INET6_ADDRSTRLEN+5]; + struct rtnl_neigh *n = (struct rtnl_neigh *) a; + struct nl_cache *link_cache; + char state[128], flags[64]; + + link_cache = nl_cache_mngt_require("route/link"); + + dp_dump(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst))); + + if (link_cache) + dp_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, n->n_ifindex, + state, sizeof(state))); + else + dp_dump(p, "dev %d ", n->n_ifindex); + + if (n->ce_mask & NEIGH_ATTR_LLADDR) + dp_dump(p, "lladdr %s ", + nl_addr2str(n->n_lladdr, lladdr, sizeof(lladdr))); + + rtnl_neigh_state2str(n->n_state, state, sizeof(state)); + rtnl_neigh_flags2str(n->n_flags, flags, sizeof(flags)); + + if (state[0]) + dp_dump(p, "<%s", state); + if (flags[0]) + dp_dump(p, "%s%s", state[0] ? "," : "<", flags); + if (state[0] || flags[0]) + dp_dump(p, ">"); + dp_dump(p, "\n"); + + return 1; +} + +static int neigh_dump_full(struct nl_object *a, struct nl_dump_params *p) +{ + char rtn_type[32]; + struct rtnl_neigh *n = (struct rtnl_neigh *) a; + int hz = nl_get_hz(); + + int line = neigh_dump_brief(a, p); + + dp_dump_line(p, line++, " refcnt %u type %s confirmed %u used " + "%u updated %u\n", + n->n_cacheinfo.nci_refcnt, + nl_rtntype2str(n->n_type, rtn_type, sizeof(rtn_type)), + n->n_cacheinfo.nci_confirmed/hz, + n->n_cacheinfo.nci_used/hz, n->n_cacheinfo.nci_updated/hz); + + return line; +} + +static int neigh_dump_stats(struct nl_object *a, struct nl_dump_params *p) +{ + return neigh_dump_full(a, p); +} + +static int neigh_dump_xml(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_neigh *neigh = (struct rtnl_neigh *) obj; + char buf[128]; + int line = 0; + + dp_dump_line(p, line++, "\n"); + dp_dump_line(p, line++, " %s\n", + nl_af2str(neigh->n_family, buf, sizeof(buf))); + + if (neigh->ce_mask & NEIGH_ATTR_LLADDR) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(neigh->n_lladdr, buf, sizeof(buf))); + + if (neigh->ce_mask & NEIGH_ATTR_DST) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(neigh->n_dst, buf, sizeof(buf))); + + if (neigh->ce_mask & NEIGH_ATTR_IFINDEX) { + struct nl_cache *link_cache; + + link_cache = nl_cache_mngt_require("route/link"); + + if (link_cache) + dp_dump_line(p, line++, " %s\n", + rtnl_link_i2name(link_cache, + neigh->n_ifindex, + buf, sizeof(buf))); + else + dp_dump_line(p, line++, " %u\n", + neigh->n_ifindex); + } + + if (neigh->ce_mask & NEIGH_ATTR_PROBES) + dp_dump_line(p, line++, " %u\n", + neigh->n_probes); + + if (neigh->ce_mask & NEIGH_ATTR_TYPE) + dp_dump_line(p, line++, " %s\n", + nl_rtntype2str(neigh->n_type, buf, sizeof(buf))); + + rtnl_neigh_flags2str(neigh->n_flags, buf, sizeof(buf)); + if (buf[0]) + dp_dump_line(p, line++, " %s\n", buf); + + rtnl_neigh_state2str(neigh->n_state, buf, sizeof(buf)); + if (buf[0]) + dp_dump_line(p, line++, " %s\n", buf); + + dp_dump_line(p, line++, "\n"); + +#if 0 + struct rtnl_ncacheinfo n_cacheinfo; +#endif + + return line; +} + +static int neigh_dump_env(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_neigh *neigh = (struct rtnl_neigh *) obj; + char buf[128]; + int line = 0; + + dp_dump_line(p, line++, "NEIGH_FAMILY=%s\n", + nl_af2str(neigh->n_family, buf, sizeof(buf))); + + if (neigh->ce_mask & NEIGH_ATTR_LLADDR) + dp_dump_line(p, line++, "NEIGHT_LLADDR=%s\n", + nl_addr2str(neigh->n_lladdr, buf, sizeof(buf))); + + if (neigh->ce_mask & NEIGH_ATTR_DST) + dp_dump_line(p, line++, "NEIGH_DST=%s\n", + nl_addr2str(neigh->n_dst, buf, sizeof(buf))); + + if (neigh->ce_mask & NEIGH_ATTR_IFINDEX) { + struct nl_cache *link_cache; + + dp_dump_line(p, line++, "NEIGH_IFINDEX=%u\n", + neigh->n_ifindex); + + link_cache = nl_cache_mngt_require("route/link"); + if (link_cache) + dp_dump_line(p, line++, "NEIGH_IFNAME=%s\n", + rtnl_link_i2name(link_cache, + neigh->n_ifindex, + buf, sizeof(buf))); + } + + if (neigh->ce_mask & NEIGH_ATTR_PROBES) + dp_dump_line(p, line++, "NEIGH_PROBES=%u\n", + neigh->n_probes); + + if (neigh->ce_mask & NEIGH_ATTR_TYPE) + dp_dump_line(p, line++, "NEIGH_TYPE=%s\n", + nl_rtntype2str(neigh->n_type, buf, sizeof(buf))); + + rtnl_neigh_flags2str(neigh->n_flags, buf, sizeof(buf)); + if (buf[0]) + dp_dump_line(p, line++, "NEIGH_FLAGS=%s\n", buf); + + rtnl_neigh_state2str(neigh->n_state, buf, sizeof(buf)); + if (buf[0]) + dp_dump_line(p, line++, "NEIGH_STATE=%s\n", buf); + + return line; +} + +/** + * @name Neighbour Object Allocation/Freeage + * @{ + */ + +struct rtnl_neigh *rtnl_neigh_alloc(void) +{ + return (struct rtnl_neigh *) nl_object_alloc(&neigh_obj_ops); +} + +void rtnl_neigh_put(struct rtnl_neigh *neigh) +{ + nl_object_put((struct nl_object *) neigh); +} + +/** @} */ + +/** + * @name Neighbour Cache Managament + * @{ + */ + +/** + * Build a neighbour cache including all neighbours currently configured in the kernel. + * @arg handle netlink handle + * + * Allocates a new neighbour cache, initializes it properly and updates it + * to include all neighbours currently configured in the kernel. + * + * @note The caller is responsible for destroying and freeing the + * cache after using it. + * @return The new cache or NULL if an error occured. + */ +struct nl_cache *rtnl_neigh_alloc_cache(struct nl_handle *handle) +{ + struct nl_cache *cache; + + cache = nl_cache_alloc(&rtnl_neigh_ops); + if (cache == NULL) + return NULL; + + if (handle && nl_cache_refill(handle, cache) < 0) { + nl_cache_free(cache); + return NULL; + } + + NL_DBG(2, "Returning new cache %p\n", cache); + + return cache; +} + +/** + * Look up a neighbour by interface index and destination address + * @arg cache neighbour cache + * @arg ifindex interface index the neighbour is on + * @arg dst destination address of the neighbour + * @return neighbour handle or NULL if no match was found. + */ +struct rtnl_neigh * rtnl_neigh_get(struct nl_cache *cache, int ifindex, + struct nl_addr *dst) +{ + struct rtnl_neigh *neigh; + + nl_list_for_each_entry(neigh, &cache->c_items, ce_list) { + if (neigh->n_ifindex == ifindex && + !nl_addr_cmp(neigh->n_dst, dst)) { + nl_object_get((struct nl_object *) neigh); + return neigh; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Neighbour Addition + * @{ + */ + +static struct nl_msg * build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, + int flags) +{ + struct nl_msg *msg; + struct ndmsg nhdr = { + .ndm_ifindex = tmpl->n_ifindex, + .ndm_family = nl_addr_get_family(tmpl->n_dst), + .ndm_state = NUD_PERMANENT, + }; + + if (tmpl->ce_mask & NEIGH_ATTR_STATE) + nhdr.ndm_state = tmpl->n_state; + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + return NULL; + + if (nlmsg_append(msg, &nhdr, sizeof(nhdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + NLA_PUT_ADDR(msg, NDA_DST, tmpl->n_dst); + + if (tmpl->ce_mask & NEIGH_ATTR_LLADDR) + NLA_PUT_ADDR(msg, NDA_LLADDR, tmpl->n_lladdr); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +/** + * Build netlink request message to add a new neighbour + * @arg tmpl template with data of new neighbour + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting a addition of a new + * neighbour. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. \a tmpl must contain the attributes of the new + * neighbour set via \c rtnl_neigh_set_* functions. + * + * The following attributes must be set in the template: + * - Interface index (rtnl_neigh_set_ifindex()) + * - State (rtnl_neigh_set_state()) + * - Destination address (rtnl_neigh_set_dst()) + * - Link layer address (rtnl_neigh_set_lladdr()) + * + * @return The netlink message + */ +struct nl_msg * rtnl_neigh_build_add_request(struct rtnl_neigh *tmpl, int flags) +{ + return build_neigh_msg(tmpl, RTM_NEWNEIGH, NLM_F_CREATE | flags); +} + +/** + * Add a new neighbour + * @arg handle netlink handle + * @arg tmpl template with requested changes + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_neigh_build_add_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * The following attributes must be set in the template: + * - Interface index (rtnl_neigh_set_ifindex()) + * - State (rtnl_neigh_set_state()) + * - Destination address (rtnl_neigh_set_dst()) + * - Link layer address (rtnl_neigh_set_lladdr()) + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_neigh_add(struct nl_handle *handle, struct rtnl_neigh *tmpl, int flags) +{ + int err; + struct nl_msg *msg; + + msg = rtnl_neigh_build_add_request(tmpl, flags); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name Neighbour Deletion + * @{ + */ + +/** + * Build a netlink request message to delete a neighbour + * @arg neigh neighbour to delete + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting a deletion of a neighbour. + * The netlink message header isn't fully equipped with all relevant + * fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. \a neigh must point to an existing + * neighbour. + * + * @return The netlink message + */ +struct nl_msg *rtnl_neigh_build_delete_request(struct rtnl_neigh *neigh, + int flags) +{ + return build_neigh_msg(neigh, RTM_DELNEIGH, flags); +} + +/** + * Delete a neighbour + * @arg handle netlink handle + * @arg neigh neighbour to delete + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_neigh_build_delete_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_neigh_delete(struct nl_handle *handle, struct rtnl_neigh *neigh, + int flags) +{ + int err; + struct nl_msg *msg; + + msg = rtnl_neigh_build_delete_request(neigh, flags); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name Neighbour Modification + * @{ + */ + +/** + * Build a netlink request message to change neighbour attributes + * @arg neigh the neighbour to change + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting a change of a neigh + * attributes. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * @return The netlink message + * @note Not all attributes can be changed, see + * \ref neigh_changeable "Changeable Attributes" for a list. + */ +struct nl_msg *rtnl_neigh_build_change_request(struct rtnl_neigh *neigh, + int flags) +{ + return build_neigh_msg(neigh, RTM_NEWNEIGH, NLM_F_REPLACE | flags); +} + +/** + * Change neighbour attributes + * @arg handle netlink handle + * @arg neigh neighbour to be changed + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_neigh_build_change_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @return 0 on sucess or a negative error if an error occured. + * @note Not all attributes can be changed, see + * \ref neigh_changeable "Changeable Attributes" for a list. + */ +int rtnl_neigh_change(struct nl_handle *handle, struct rtnl_neigh *neigh, + int flags) +{ + int err; + struct nl_msg *msg; + + msg = rtnl_neigh_build_change_request(neigh, flags); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name Neighbour States Translations + * @{ + */ + +static struct trans_tbl neigh_states[] = { + __ADD(NUD_INCOMPLETE, incomplete) + __ADD(NUD_REACHABLE, reachable) + __ADD(NUD_STALE, stale) + __ADD(NUD_DELAY, delay) + __ADD(NUD_PROBE, probe) + __ADD(NUD_FAILED, failed) + __ADD(NUD_NOARP, norarp) + __ADD(NUD_PERMANENT, permanent) +}; + +char * rtnl_neigh_state2str(int state, char *buf, size_t len) +{ + return __flags2str(state, buf, len, neigh_states, + ARRAY_SIZE(neigh_states)); +} + +int rtnl_neigh_str2state(const char *name) +{ + return __str2type(name, neigh_states, ARRAY_SIZE(neigh_states)); +} + +/** @} */ + +/** + * @name Neighbour Flags Translations + * @{ + */ + +static struct trans_tbl neigh_flags[] = { + __ADD(NTF_PROXY, proxy) + __ADD(NTF_ROUTER, router) +}; + +char * rtnl_neigh_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, neigh_flags, + ARRAY_SIZE(neigh_flags)); +} + +int rtnl_neigh_str2flag(const char *name) +{ + return __str2type(name, neigh_flags, ARRAY_SIZE(neigh_flags)); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void rtnl_neigh_set_state(struct rtnl_neigh *neigh, int state) +{ + neigh->n_state_mask |= state; + neigh->n_state |= state; + neigh->ce_mask |= NEIGH_ATTR_STATE; +} + +int rtnl_neigh_get_state(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_STATE) + return neigh->n_state; + else + return -1; +} + +void rtnl_neigh_unset_state(struct rtnl_neigh *neigh, int state) +{ + neigh->n_state_mask |= state; + neigh->n_state &= ~state; + neigh->ce_mask |= NEIGH_ATTR_STATE; +} + +void rtnl_neigh_set_flags(struct rtnl_neigh *neigh, unsigned int flags) +{ + neigh->n_flag_mask |= flags; + neigh->n_flags |= flags; + neigh->ce_mask |= NEIGH_ATTR_FLAGS; +} + +unsigned int rtnl_neigh_get_flags(struct rtnl_neigh *neigh) +{ + return neigh->n_flags; +} + +void rtnl_neigh_unset_flags(struct rtnl_neigh *neigh, unsigned int flags) +{ + neigh->n_flag_mask |= flags; + neigh->n_flags &= ~flags; + neigh->ce_mask |= NEIGH_ATTR_FLAGS; +} + +void rtnl_neigh_set_ifindex(struct rtnl_neigh *neigh, int ifindex) +{ + neigh->n_ifindex = ifindex; + neigh->ce_mask |= NEIGH_ATTR_IFINDEX; +} + +int rtnl_neigh_get_ifindex(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_IFINDEX) + return neigh->n_ifindex; + else + return RTNL_LINK_NOT_FOUND; +} + +static inline int __assign_addr(struct rtnl_neigh *neigh, struct nl_addr **pos, + struct nl_addr *new, int flag, int nocheck) +{ + if (!nocheck) { + if (neigh->ce_mask & NEIGH_ATTR_FAMILY) { + if (new->a_family != neigh->n_family) + return nl_error(EINVAL, + "Address family mismatch"); + } else { + neigh->n_family = new->a_family; + neigh->ce_mask |= NEIGH_ATTR_FAMILY; + } + } + + if (*pos) + nl_addr_put(*pos); + + nl_addr_get(new); + *pos = new; + + neigh->ce_mask |= flag; + + return 0; +} + +void rtnl_neigh_set_lladdr(struct rtnl_neigh *neigh, struct nl_addr *addr) +{ + __assign_addr(neigh, &neigh->n_lladdr, addr, NEIGH_ATTR_LLADDR, 1); +} + +struct nl_addr *rtnl_neigh_get_lladdr(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_LLADDR) + return neigh->n_lladdr; + else + return NULL; +} + +int rtnl_neigh_set_dst(struct rtnl_neigh *neigh, struct nl_addr *addr) +{ + return __assign_addr(neigh, &neigh->n_dst, addr, + NEIGH_ATTR_DST, 0); +} + +struct nl_addr *rtnl_neigh_get_dst(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_DST) + return neigh->n_dst; + else + return NULL; +} + +void rtnl_neigh_set_family(struct rtnl_neigh *neigh, int family) +{ + neigh->n_family = family; + neigh->ce_mask |= NEIGH_ATTR_FAMILY; +} + +void rtnl_neigh_set_type(struct rtnl_neigh *neigh, int type) +{ + neigh->n_type = type; + neigh->ce_mask = NEIGH_ATTR_TYPE; +} + +int rtnl_neigh_get_type(struct rtnl_neigh *neigh) +{ + if (neigh->ce_mask & NEIGH_ATTR_TYPE) + return neigh->n_type; + else + return -1; +} + +/** @} */ + +static struct nl_object_ops neigh_obj_ops = { + .oo_name = "route/neigh", + .oo_size = sizeof(struct rtnl_neigh), + .oo_free_data = neigh_free_data, + .oo_clone = neigh_clone, + .oo_dump[NL_DUMP_BRIEF] = neigh_dump_brief, + .oo_dump[NL_DUMP_FULL] = neigh_dump_full, + .oo_dump[NL_DUMP_STATS] = neigh_dump_stats, + .oo_dump[NL_DUMP_XML] = neigh_dump_xml, + .oo_dump[NL_DUMP_ENV] = neigh_dump_env, + .oo_compare = neigh_compare, + .oo_attrs2str = neigh_attrs2str, + .oo_id_attrs = (NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY), +}; + +static struct nl_af_group neigh_groups[] = { + { AF_UNSPEC, RTNLGRP_NEIGH }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_neigh_ops = { + .co_name = "route/neigh", + .co_hdrsize = sizeof(struct ndmsg), + .co_msgtypes = { + { RTM_NEWNEIGH, NL_ACT_NEW, "new" }, + { RTM_DELNEIGH, NL_ACT_DEL, "del" }, + { RTM_GETNEIGH, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = neigh_groups, + .co_request_update = neigh_request_update, + .co_msg_parser = neigh_msg_parser, + .co_obj_ops = &neigh_obj_ops, +}; + +static void __init neigh_init(void) +{ + nl_cache_mngt_register(&rtnl_neigh_ops); +} + +static void __exit neigh_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_neigh_ops); +} + +/** @} */ diff --git a/lib/route/neightbl.c b/lib/route/neightbl.c new file mode 100644 index 0000000..afd64a6 --- /dev/null +++ b/lib/route/neightbl.c @@ -0,0 +1,825 @@ +/* + * lib/route/neightbl.c neighbour tables + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup neightbl Neighbour Tables + * @brief + * @{ + */ + +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define NEIGHTBL_ATTR_FAMILY 0x001 +#define NEIGHTBL_ATTR_STATS 0x002 +#define NEIGHTBL_ATTR_NAME 0x004 +#define NEIGHTBL_ATTR_THRESH1 0x008 +#define NEIGHTBL_ATTR_THRESH2 0x010 +#define NEIGHTBL_ATTR_THRESH3 0x020 +#define NEIGHTBL_ATTR_CONFIG 0x040 +#define NEIGHTBL_ATTR_PARMS 0x080 +#define NEIGHTBL_ATTR_GC_INTERVAL 0x100 + +#define NEIGHTBLPARM_ATTR_IFINDEX 0x0001 +#define NEIGHTBLPARM_ATTR_REFCNT 0x0002 +#define NEIGHTBLPARM_ATTR_QUEUE_LEN 0x0004 +#define NEIGHTBLPARM_ATTR_APP_PROBES 0x0008 +#define NEIGHTBLPARM_ATTR_UCAST_PROBES 0x0010 +#define NEIGHTBLPARM_ATTR_MCAST_PROBES 0x0020 +#define NEIGHTBLPARM_ATTR_PROXY_QLEN 0x0040 +#define NEIGHTBLPARM_ATTR_REACHABLE_TIME 0x0080 +#define NEIGHTBLPARM_ATTR_BASE_REACHABLE_TIME 0x0100 +#define NEIGHTBLPARM_ATTR_RETRANS_TIME 0x0200 +#define NEIGHTBLPARM_ATTR_GC_STALETIME 0x0400 +#define NEIGHTBLPARM_ATTR_DELAY_PROBE_TIME 0x0800 +#define NEIGHTBLPARM_ATTR_ANYCAST_DELAY 0x1000 +#define NEIGHTBLPARM_ATTR_PROXY_DELAY 0x2000 +#define NEIGHTBLPARM_ATTR_LOCKTIME 0x4000 + +static struct nl_cache_ops rtnl_neightbl_ops; +static struct nl_object_ops neightbl_obj_ops; +/** @endcond */ + +static int neightbl_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) +{ + struct rtnl_neightbl *a = (struct rtnl_neightbl *) _a; + struct rtnl_neightbl *b = (struct rtnl_neightbl *) _b; + int diff = 0; + +#define NT_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NEIGHTBL_ATTR_##ATTR, a, b, EXPR) + + diff |= NT_DIFF(FAMILY, a->nt_family != b->nt_family); + diff |= NT_DIFF(NAME, strcmp(a->nt_name, b->nt_name)); + diff |= NT_DIFF(THRESH1, a->nt_gc_thresh1 != b->nt_gc_thresh1); + diff |= NT_DIFF(THRESH2, a->nt_gc_thresh2 != b->nt_gc_thresh2); + diff |= NT_DIFF(THRESH3, a->nt_gc_thresh3 != b->nt_gc_thresh3); + diff |= NT_DIFF(GC_INTERVAL, a->nt_gc_interval != b->nt_gc_interval); + +#undef NT_DIFF + + if (!(a->ce_mask & NEIGHTBL_ATTR_PARMS) && + !(b->ce_mask & NEIGHTBL_ATTR_PARMS)) + return diff; + + /* XXX: FIXME: Compare parameter table */ + + +#if 0 +#define REQ(F) (fp->ntp_mask & NEIGHTBLPARM_ATTR_##F) +#define AVAIL(F) (op->ntp_mask & NEIGHTBLPARM_ATTR_##F) +#define _C(F, N) (REQ(F) && (!AVAIL(F) || (op->N != fp->N))) + if (_C(IFINDEX, ntp_ifindex) || + _C(QUEUE_LEN, ntp_queue_len) || + _C(APP_PROBES, ntp_app_probes) || + _C(UCAST_PROBES, ntp_ucast_probes) || + _C(MCAST_PROBES, ntp_mcast_probes) || + _C(PROXY_QLEN, ntp_proxy_qlen) || + _C(LOCKTIME, ntp_locktime) || + _C(RETRANS_TIME, ntp_retrans_time) || + _C(BASE_REACHABLE_TIME, ntp_base_reachable_time) || + _C(GC_STALETIME, ntp_gc_stale_time) || + _C(DELAY_PROBE_TIME, ntp_probe_delay) || + _C(ANYCAST_DELAY, ntp_anycast_delay) || + _C(PROXY_DELAY, ntp_proxy_delay)) + return 0; +#undef REQ +#undef AVAIL +#undef _C +#endif + + return diff; +} + + +static struct nla_policy neightbl_policy[NDTA_MAX+1] = { + [NDTA_NAME] = { .type = NLA_STRING, + .maxlen = NTBLNAMSIZ }, + [NDTA_THRESH1] = { .type = NLA_U32 }, + [NDTA_THRESH2] = { .type = NLA_U32 }, + [NDTA_THRESH3] = { .type = NLA_U32 }, + [NDTA_GC_INTERVAL] = { .type = NLA_U32 }, + [NDTA_CONFIG] = { .minlen = sizeof(struct ndt_config) }, + [NDTA_STATS] = { .minlen = sizeof(struct ndt_stats) }, + [NDTA_PARMS] = { .type = NLA_NESTED }, +}; + +static int neightbl_msg_parser(struct nl_cache_ops *ops, + struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) +{ + struct rtnl_neightbl *ntbl; + struct nlattr *tb[NDTA_MAX + 1]; + struct nl_parser_param *pp = arg; + struct rtgenmsg *rtmsg; + int err; + + ntbl = rtnl_neightbl_alloc(); + if (!ntbl) { + err = nl_errno(ENOMEM); + goto errout; + } + + ntbl->ce_msgtype = n->nlmsg_type; + rtmsg = nlmsg_data(n); + + err = nlmsg_parse(n, sizeof(*rtmsg), tb, NDTA_MAX, neightbl_policy); + if (err < 0) + goto errout; + + ntbl->nt_family = rtmsg->rtgen_family; + + if (tb[NDTA_NAME] == NULL) { + err = nl_error(EINVAL, "NDTA_NAME is missing"); + goto errout; + } + + nla_strlcpy(ntbl->nt_name, tb[NDTA_NAME], NTBLNAMSIZ); + ntbl->ce_mask |= NEIGHTBL_ATTR_NAME; + + if (tb[NDTA_THRESH1]) { + ntbl->nt_gc_thresh1 = nla_get_u32(tb[NDTA_THRESH1]); + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH1; + } + + if (tb[NDTA_THRESH2]) { + ntbl->nt_gc_thresh2 = nla_get_u32(tb[NDTA_THRESH2]); + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH2; + } + + if (tb[NDTA_THRESH3]) { + ntbl->nt_gc_thresh3 = nla_get_u32(tb[NDTA_THRESH3]); + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH3; + } + + if (tb[NDTA_GC_INTERVAL]) { + ntbl->nt_gc_interval = nla_get_u32(tb[NDTA_GC_INTERVAL]); + ntbl->ce_mask |= NEIGHTBL_ATTR_GC_INTERVAL; + } + + if (tb[NDTA_CONFIG]) { + nla_memcpy(&ntbl->nt_config, tb[NDTA_CONFIG], + sizeof(ntbl->nt_config)); + ntbl->ce_mask |= NEIGHTBL_ATTR_CONFIG; + } + + if (tb[NDTA_STATS]) { + nla_memcpy(&ntbl->nt_stats, tb[NDTA_STATS], + sizeof(ntbl->nt_stats)); + ntbl->ce_mask |= NEIGHTBL_ATTR_STATS; + } + + if (tb[NDTA_PARMS]) { + struct nlattr *tbp[NDTPA_MAX + 1]; + struct rtnl_neightbl_parms *p = &ntbl->nt_parms; + + err = nla_parse_nested(tbp, NDTPA_MAX, tb[NDTA_PARMS], NULL); + if (err < 0) + goto errout; + +#define COPY_ENTRY(name, var) \ + if (tbp[NDTPA_ ##name]) { \ + p->ntp_ ##var = nla_get_u32(tbp[NDTPA_ ##name]); \ + p->ntp_mask |= NEIGHTBLPARM_ATTR_ ##name; \ + } + + COPY_ENTRY(IFINDEX, ifindex); + COPY_ENTRY(REFCNT, refcnt); + COPY_ENTRY(QUEUE_LEN, queue_len); + COPY_ENTRY(APP_PROBES, app_probes); + COPY_ENTRY(UCAST_PROBES, ucast_probes); + COPY_ENTRY(MCAST_PROBES, mcast_probes); + COPY_ENTRY(PROXY_QLEN, proxy_qlen); + COPY_ENTRY(PROXY_DELAY, proxy_delay); + COPY_ENTRY(ANYCAST_DELAY, anycast_delay); + COPY_ENTRY(LOCKTIME, locktime); + COPY_ENTRY(REACHABLE_TIME, reachable_time); + COPY_ENTRY(BASE_REACHABLE_TIME, base_reachable_time); + COPY_ENTRY(RETRANS_TIME, retrans_time); + COPY_ENTRY(GC_STALETIME, gc_stale_time); + COPY_ENTRY(DELAY_PROBE_TIME, probe_delay); +#undef COPY_ENTRY + + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; + } + + err = pp->pp_cb((struct nl_object *) ntbl, pp); + if (err < 0) + goto errout; + + return P_ACCEPT; +errout: + rtnl_neightbl_put(ntbl); + return err; +} + +static int neightbl_request_update(struct nl_cache *c, struct nl_handle *h) +{ + return nl_rtgen_request(h, RTM_GETNEIGHTBL, AF_UNSPEC, NLM_F_DUMP); +} + + +static int neightbl_dump_brief(struct nl_object *arg, struct nl_dump_params *p) +{ + int line = 1; + struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *) arg; + + dp_dump(p, "%s", ntbl->nt_name); + + if (ntbl->nt_parms.ntp_mask & NEIGHTBLPARM_ATTR_IFINDEX) { + struct nl_cache *link_cache; + + link_cache = nl_cache_mngt_require("route/link"); + + if (link_cache) { + char buf[32]; + dp_dump(p, "<%s> ", + rtnl_link_i2name(link_cache, + ntbl->nt_parms.ntp_ifindex, + buf, sizeof(buf))); + } else + dp_dump(p, "<%u> ", ntbl->nt_parms.ntp_ifindex); + } else + dp_dump(p, " "); + + if (ntbl->ce_mask & NEIGHTBL_ATTR_CONFIG) + dp_dump(p, "entries %u ", ntbl->nt_config.ndtc_entries); + + if (ntbl->ce_mask & NEIGHTBL_ATTR_PARMS) { + char rt[32], rt2[32]; + struct rtnl_neightbl_parms *pa = &ntbl->nt_parms; + + dp_dump(p, "reachable-time %s retransmit-time %s", + nl_msec2str(pa->ntp_reachable_time, rt, sizeof(rt)), + nl_msec2str(pa->ntp_retrans_time, rt2, sizeof(rt2))); + } + + dp_dump(p, "\n"); + + return line; +} + +static int neightbl_dump_full(struct nl_object *arg, struct nl_dump_params *p) +{ + char x[32], y[32], z[32]; + struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *) arg; + + int line = neightbl_dump_brief(arg, p); + + if (ntbl->ce_mask & NEIGHTBL_ATTR_CONFIG) { + dp_new_line(p, line++); + dp_dump(p, " key-len %u entry-size %u last-flush %s\n", + ntbl->nt_config.ndtc_key_len, + ntbl->nt_config.ndtc_entry_size, + nl_msec2str(ntbl->nt_config.ndtc_last_flush, + x, sizeof(x))); + + dp_new_line(p, line++); + dp_dump(p, " gc threshold %u/%u/%u interval %s " \ + "chain-position %u\n", + ntbl->nt_gc_thresh1, ntbl->nt_gc_thresh2, + ntbl->nt_gc_thresh3, + nl_msec2str(ntbl->nt_gc_interval, x, sizeof(x)), + ntbl->nt_config.ndtc_hash_chain_gc); + + dp_new_line(p, line++); + dp_dump(p, " hash-rand 0x%08X/0x%08X last-rand %s\n", + ntbl->nt_config.ndtc_hash_rnd, + ntbl->nt_config.ndtc_hash_mask, + nl_msec2str(ntbl->nt_config.ndtc_last_rand, + x, sizeof(x))); + } + + if (ntbl->ce_mask & NEIGHTBL_ATTR_PARMS) { + struct rtnl_neightbl_parms *pa = &ntbl->nt_parms; + + dp_new_line(p, line++); + dp_dump(p, " refcnt %u pending-queue-limit %u " \ + "proxy-delayed-queue-limit %u\n", + pa->ntp_refcnt, + pa->ntp_queue_len, + pa->ntp_proxy_qlen); + + dp_new_line(p, line++); + dp_dump(p, " num-userspace-probes %u num-unicast-probes " \ + "%u num-multicast-probes %u\n", + pa->ntp_app_probes, + pa->ntp_ucast_probes, + pa->ntp_mcast_probes); + + dp_new_line(p, line++); + dp_dump(p, " min-age %s base-reachable-time %s " \ + "stale-check-interval %s\n", + nl_msec2str(pa->ntp_locktime, x, sizeof(x)), + nl_msec2str(pa->ntp_base_reachable_time, + y, sizeof(y)), + nl_msec2str(pa->ntp_gc_stale_time, z, sizeof(z))); + + dp_new_line(p, line++); + dp_dump(p, " initial-probe-delay %s answer-delay %s " \ + "proxy-answer-delay %s\n", + nl_msec2str(pa->ntp_probe_delay, x, sizeof(x)), + nl_msec2str(pa->ntp_anycast_delay, y, sizeof(y)), + nl_msec2str(pa->ntp_proxy_delay, z, sizeof(z))); + } + + return line; +} + +static int neightbl_dump_stats(struct nl_object *arg, struct nl_dump_params *p) +{ + struct rtnl_neightbl *ntbl = (struct rtnl_neightbl *) arg; + int line = neightbl_dump_full(arg, p); + + if (!(ntbl->ce_mask & NEIGHTBL_ATTR_STATS)) + return line; + + dp_new_line(p, line++); + dp_dump(p, " lookups %lld hits %lld failed %lld " \ + "allocations %lld destroys %lld\n", + ntbl->nt_stats.ndts_lookups, + ntbl->nt_stats.ndts_hits, + ntbl->nt_stats.ndts_res_failed, + ntbl->nt_stats.ndts_allocs, + ntbl->nt_stats.ndts_destroys); + + dp_new_line(p, line++); + dp_dump(p, " hash-grows %lld forced-gc-runs %lld " \ + "periodic-gc-runs %lld\n", + ntbl->nt_stats.ndts_hash_grows, + ntbl->nt_stats.ndts_forced_gc_runs, + ntbl->nt_stats.ndts_periodic_gc_runs); + + dp_dump(p, " rcv-unicast-probes %lld rcv-multicast-probes %lld\n", + ntbl->nt_stats.ndts_rcv_probes_ucast, + ntbl->nt_stats.ndts_rcv_probes_mcast); + + return line; +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_neightbl *rtnl_neightbl_alloc(void) +{ + return (struct rtnl_neightbl *) nl_object_alloc(&neightbl_obj_ops); +} + +void rtnl_neightbl_put(struct rtnl_neightbl *neightbl) +{ + nl_object_put((struct nl_object *) neightbl); +} + +/** @} */ + +/** + * @name Neighbour Table Cache Management + * @{ + */ + +/** + * Build a neighbour table cache including all neighbour tables currently configured in the kernel. + * @arg handle netlink handle + * + * Allocates a new neighbour table cache, initializes it properly and + * updates it to include all neighbour tables currently configured in + * the kernel. + * + * @note The caller is responsible for destroying and freeing the + * cache after using it. + * @return The new cache or NULL if an error occured. + */ +struct nl_cache * rtnl_neightbl_alloc_cache(struct nl_handle *handle) +{ + struct nl_cache * cache; + + cache = nl_cache_alloc(&rtnl_neightbl_ops); + if (cache == NULL) + return NULL; + + if (handle && nl_cache_refill(handle, cache) < 0) { + nl_cache_free(cache); + return NULL; + } + + return cache; +} + +/** + * Lookup neighbour table by name and optional interface index + * @arg cache neighbour table cache + * @arg name name of table + * @arg ifindex optional interface index + * + * Looks up the neighbour table matching the specified name and + * optionally the specified ifindex to retrieve device specific + * parameter sets. + * + * @return ptr to neighbour table inside the cache or NULL if no + * match was found. + */ +struct rtnl_neightbl *rtnl_neightbl_get(struct nl_cache *cache, + const char *name, int ifindex) +{ + struct rtnl_neightbl *nt; + + if (cache->c_ops != &rtnl_neightbl_ops) + return NULL; + + nl_list_for_each_entry(nt, &cache->c_items, ce_list) { + if (!strcasecmp(nt->nt_name, name) && + ((!ifindex && !nt->nt_parms.ntp_ifindex) || + (ifindex && ifindex == nt->nt_parms.ntp_ifindex))) { + nl_object_get((struct nl_object *) nt); + return nt; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Neighbour Table Modifications + * @{ + */ + +/** + * Builds a netlink change request message to change neighbour table attributes + * @arg old neighbour table to change + * @arg tmpl template with requested changes + * + * Builds a new netlink message requesting a change of neighbour table + * attributes. The netlink message header isn't fully equipped with all + * relevant fields and must be sent out via nl_send_auto_complete() or + * supplemented as needed. + * \a old must point to a neighbour table currently configured in the + * kernel and \a tmpl must contain the attributes to be changed set via + * \c rtnl_neightbl_set_* functions. + * + * @return New netlink message + */ +struct nl_msg * rtnl_neightbl_build_change_request(struct rtnl_neightbl *old, + struct rtnl_neightbl *tmpl) +{ + struct nl_msg *m; + struct ndtmsg ndt = { + .ndtm_family = old->nt_family, + }; + + m = nlmsg_alloc_simple(RTM_SETNEIGHTBL, 0); + nlmsg_append(m, &ndt, sizeof(ndt), NLMSG_ALIGNTO); + + nla_put_string(m, NDTA_NAME, old->nt_name); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH1) + nla_put_u32(m, NDTA_THRESH1, tmpl->nt_gc_thresh1); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH2) + nla_put_u32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_THRESH2) + nla_put_u32(m, NDTA_THRESH2, tmpl->nt_gc_thresh2); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_GC_INTERVAL) + nla_put_u64(m, NDTA_GC_INTERVAL, + tmpl->nt_gc_interval); + + if (tmpl->ce_mask & NEIGHTBL_ATTR_PARMS) { + struct rtnl_neightbl_parms *p = &tmpl->nt_parms; + struct nl_msg *parms = nlmsg_alloc(); + + if (old->nt_parms.ntp_mask & NEIGHTBLPARM_ATTR_IFINDEX) + nla_put_u32(parms, NDTPA_IFINDEX, + old->nt_parms.ntp_ifindex); + + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_QUEUE_LEN) + nla_put_u32(parms, NDTPA_QUEUE_LEN, p->ntp_queue_len); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_APP_PROBES) + nla_put_u32(parms, NDTPA_APP_PROBES, p->ntp_app_probes); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_UCAST_PROBES) + nla_put_u32(parms, NDTPA_UCAST_PROBES, + p->ntp_ucast_probes); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_MCAST_PROBES) + nla_put_u32(parms, NDTPA_MCAST_PROBES, + p->ntp_mcast_probes); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_PROXY_QLEN) + nla_put_u32(parms, NDTPA_PROXY_QLEN, + p->ntp_proxy_qlen); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_BASE_REACHABLE_TIME) + nla_put_u64(parms, NDTPA_BASE_REACHABLE_TIME, + p->ntp_base_reachable_time); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_RETRANS_TIME) + nla_put_u64(parms, NDTPA_RETRANS_TIME, + p->ntp_retrans_time); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_GC_STALETIME) + nla_put_u64(parms, NDTPA_GC_STALETIME, + p->ntp_gc_stale_time); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_DELAY_PROBE_TIME) + nla_put_u64(parms, NDTPA_DELAY_PROBE_TIME, + p->ntp_proxy_delay); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_ANYCAST_DELAY) + nla_put_u64(parms, NDTPA_ANYCAST_DELAY, + p->ntp_anycast_delay); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_PROXY_DELAY) + nla_put_u64(parms, NDTPA_PROXY_DELAY, + p->ntp_proxy_delay); + + if (p->ntp_mask & NEIGHTBLPARM_ATTR_LOCKTIME) + nla_put_u64(parms, NDTPA_LOCKTIME, p->ntp_locktime); + + nla_put_nested(m, NDTA_PARMS, parms); + nlmsg_free(parms); + } + + return m; +} + +/** + * Change neighbour table attributes + * @arg handle netlink handle + * @arg old neighbour table to be changed + * @arg tmpl template with requested changes + * + * Builds a new netlink message by calling + * rtnl_neightbl_build_change_request(), sends the request to the + * kernel and waits for the next ACK to be received, i.e. blocks + * until the request has been processed. + * + * @return 0 on success or a negative error code + */ +int rtnl_neightbl_change(struct nl_handle *handle, struct rtnl_neightbl *old, + struct rtnl_neightbl *tmpl) +{ + int err; + struct nl_msg *msg; + + msg = rtnl_neightbl_build_change_request(old, tmpl); + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name Attribute Modification + * @{ + */ + +void rtnl_neightbl_set_family(struct rtnl_neightbl *ntbl, int family) +{ + ntbl->nt_family = family; + ntbl->ce_mask |= NEIGHTBL_ATTR_FAMILY; +} + +void rtnl_neightbl_set_gc_interval(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_gc_interval = ms; + ntbl->ce_mask |= NEIGHTBL_ATTR_GC_INTERVAL; +} + +void rtnl_neightbl_set_gc_tresh1(struct rtnl_neightbl *ntbl, int thresh) +{ + ntbl->nt_gc_thresh1 = thresh; + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH1; +} + +void rtnl_neightbl_set_gc_tresh2(struct rtnl_neightbl *ntbl, int thresh) +{ + ntbl->nt_gc_thresh2 = thresh; + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH2; +} + +void rtnl_neightbl_set_gc_tresh3(struct rtnl_neightbl *ntbl, int thresh) +{ + ntbl->nt_gc_thresh3 = thresh; + ntbl->ce_mask |= NEIGHTBL_ATTR_THRESH3; +} + +void rtnl_neightbl_set_name(struct rtnl_neightbl *ntbl, const char *name) +{ + strncpy(ntbl->nt_name, name, sizeof(ntbl->nt_name) - 1); + ntbl->ce_mask |= NEIGHTBL_ATTR_NAME; +} + +void rtnl_neightbl_set_dev(struct rtnl_neightbl *ntbl, int ifindex) +{ + ntbl->nt_parms.ntp_ifindex = ifindex; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_IFINDEX; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the queue length for pending requests of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg len new queue len + */ +void rtnl_neightbl_set_queue_len(struct rtnl_neightbl *ntbl, int len) +{ + ntbl->nt_parms.ntp_queue_len = len; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_QUEUE_LEN; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the queue length for delay proxy arp requests of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg len new queue len + */ +void rtnl_neightbl_set_proxy_queue_len(struct rtnl_neightbl *ntbl, int len) +{ + ntbl->nt_parms.ntp_proxy_qlen = len; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_PROXY_QLEN; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the number of application probes of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg probes new probes value + */ +void rtnl_neightbl_set_app_probes(struct rtnl_neightbl *ntbl, int probes) +{ + ntbl->nt_parms.ntp_app_probes = probes; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_APP_PROBES; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the number of unicast probes of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg probes new probes value + */ +void rtnl_neightbl_set_ucast_probes(struct rtnl_neightbl *ntbl, int probes) +{ + ntbl->nt_parms.ntp_ucast_probes = probes; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_UCAST_PROBES; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the number of multicast probes of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg probes new probes value + */ +void rtnl_neightbl_set_mcast_probes(struct rtnl_neightbl *ntbl, int probes) +{ + ntbl->nt_parms.ntp_mcast_probes = probes; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_MCAST_PROBES; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the base reachable time of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new base reachable time in milliseconds + */ +void rtnl_neightbl_set_base_reachable_time(struct rtnl_neightbl *ntbl, + uint64_t ms) +{ + ntbl->nt_parms.ntp_base_reachable_time = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_BASE_REACHABLE_TIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the retransmit time of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new retransmit time + */ +void rtnl_neightbl_set_retrans_time(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_retrans_time = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_RETRANS_TIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the gc stale time of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new gc stale time in milliseconds + */ +void rtnl_neightbl_set_gc_stale_time(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_gc_stale_time = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_GC_STALETIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the first probe delay time of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new first probe delay time in milliseconds + */ +void rtnl_neightbl_set_delay_probe_time(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_probe_delay = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_DELAY_PROBE_TIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the anycast delay of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new anycast delay in milliseconds + */ +void rtnl_neightbl_set_anycast_delay(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_anycast_delay = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_ANYCAST_DELAY; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the proxy delay of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new proxy delay in milliseconds + */ +void rtnl_neightbl_set_proxy_delay(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_proxy_delay = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_PROXY_DELAY; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** + * Set the locktime of a neighbour table to the specified value + * @arg ntbl neighbour table to change + * @arg ms new locktime in milliseconds + */ +void rtnl_neightbl_set_locktime(struct rtnl_neightbl *ntbl, uint64_t ms) +{ + ntbl->nt_parms.ntp_locktime = ms; + ntbl->nt_parms.ntp_mask |= NEIGHTBLPARM_ATTR_LOCKTIME; + ntbl->ce_mask |= NEIGHTBL_ATTR_PARMS; +} + +/** @} */ + +static struct nl_object_ops neightbl_obj_ops = { + .oo_name = "route/neightbl", + .oo_size = sizeof(struct rtnl_neightbl), + .oo_dump[NL_DUMP_BRIEF] = neightbl_dump_brief, + .oo_dump[NL_DUMP_FULL] = neightbl_dump_full, + .oo_dump[NL_DUMP_STATS] = neightbl_dump_stats, + .oo_compare = neightbl_compare, +}; + +static struct nl_cache_ops rtnl_neightbl_ops = { + .co_name = "route/neightbl", + .co_hdrsize = sizeof(struct rtgenmsg), + .co_msgtypes = { + { RTM_NEWNEIGHTBL, NL_ACT_NEW, "new" }, + { RTM_SETNEIGHTBL, NL_ACT_SET, "set" }, + { RTM_GETNEIGHTBL, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = neightbl_request_update, + .co_msg_parser = neightbl_msg_parser, + .co_obj_ops = &neightbl_obj_ops, +}; + +static void __init neightbl_init(void) +{ + nl_cache_mngt_register(&rtnl_neightbl_ops); +} + +static void __exit neightbl_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_neightbl_ops); +} + +/** @} */ diff --git a/lib/route/nexthop.c b/lib/route/nexthop.c new file mode 100644 index 0000000..7486769 --- /dev/null +++ b/lib/route/nexthop.c @@ -0,0 +1,151 @@ +/* + * lib/route/nexthop.c Routing Nexthop + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup route_obj + * @defgroup nexthop Nexthop + * @{ + */ + +#include +#include +#include +#include +#include + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_nexthop *rtnl_route_nh_alloc(void) +{ + struct rtnl_nexthop *nh; + + nh = calloc(1, sizeof(*nh)); + if (!nh) { + nl_errno(ENOMEM); + return NULL; + } + + nl_init_list_head(&nh->rtnh_list); + + return nh; +} + +struct rtnl_nexthop *rtnl_route_nh_clone(struct rtnl_nexthop *src) +{ + struct rtnl_nexthop *nh; + + nh = rtnl_route_nh_alloc(); + if (!nh) + return NULL; + + nh->rtnh_flags = src->rtnh_flags; + nh->rtnh_flag_mask = src->rtnh_flag_mask; + nh->rtnh_weight = src->rtnh_weight; + nh->rtnh_ifindex = src->rtnh_ifindex; + nh->rtnh_mask = src->rtnh_mask; + + if (src->rtnh_gateway) { + nh->rtnh_gateway = nl_addr_clone(src->rtnh_gateway); + if (!nh->rtnh_gateway) { + free(nh); + return NULL; + } + } + + return nh; +} + +void rtnl_route_nh_free(struct rtnl_nexthop *nh) +{ + nl_addr_put(nh->rtnh_gateway); + free(nh); +} + +/** @} */ + +/** + * @name Attributes + */ + +void rtnl_route_nh_set_weight(struct rtnl_nexthop *nh, int weight) +{ + nh->rtnh_weight = weight; + nh->rtnh_mask |= NEXTHOP_HAS_WEIGHT; +} + +int rtnl_route_nh_get_weight(struct rtnl_nexthop *nh) +{ + if (nh->rtnh_mask & NEXTHOP_HAS_WEIGHT) + return nh->rtnh_weight; + else + return 0; +} + +void rtnl_route_nh_set_ifindex(struct rtnl_nexthop *nh, int ifindex) +{ + nh->rtnh_ifindex = ifindex; + nh->rtnh_mask |= NEXTHOP_HAS_IFINDEX; +} + +int rtnl_route_nh_get_ifindex(struct rtnl_nexthop *nh) +{ + if (nh->rtnh_mask & NEXTHOP_HAS_IFINDEX) + return nh->rtnh_ifindex; + else + return -1; +} + +void rtnl_route_nh_set_gateway(struct rtnl_nexthop *nh, struct nl_addr *addr) +{ + struct nl_addr *old = nh->rtnh_gateway; + + nh->rtnh_gateway = nl_addr_get(addr); + if (old) + nl_addr_put(old); + + nh->rtnh_mask |= NEXTHOP_HAS_GATEWAY; +} + +struct nl_addr *rtnl_route_nh_get_gateway(struct rtnl_nexthop *nh) +{ + if (nh->rtnh_mask & NEXTHOP_HAS_GATEWAY) + return nh->rtnh_gateway; + else + return NULL; +} + +void rtnl_route_nh_set_flags(struct rtnl_nexthop *nh, unsigned int flags) +{ + nh->rtnh_flag_mask |= flags; + nh->rtnh_flags |= flags; + nh->rtnh_mask |= NEXTHOP_HAS_FLAGS; +} + +void rtnl_route_nh_unset_flags(struct rtnl_nexthop *nh, unsigned int flags) +{ + nh->rtnh_flag_mask |= flags; + nh->rtnh_flags &= ~flags; + nh->rtnh_mask |= NEXTHOP_HAS_FLAGS; +} + +unsigned int rtnl_route_nh_get_flags(struct rtnl_nexthop *nh) +{ + if (nh->rtnh_mask & NEXTHOP_HAS_FLAGS) + return nh->rtnh_flags; + else + return 0; +} + +/** @} */ +/** @} */ diff --git a/lib/route/qdisc.c b/lib/route/qdisc.c new file mode 100644 index 0000000..818ca7b --- /dev/null +++ b/lib/route/qdisc.c @@ -0,0 +1,478 @@ +/* + * lib/route/qdisc.c Queueing Disciplines + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup tc + * @defgroup qdisc Queueing Disciplines + * + * @par Qdisc Handles + * In general, qdiscs are identified by the major part of a traffic control + * handle (the upper 16 bits). A few special values exist though: + * - \c TC_H_ROOT: root qdisc (directly attached to the device) + * - \c TC_H_INGRESS: ingress qdisc (directly attached to the device) + * - \c TC_H_UNSPEC: unspecified qdisc (no reference) + * + * @par 1) Adding a Qdisc + * @code + * // Allocate a new empty qdisc to be filled out + * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc(); + * + * // ... specify the kind of the Qdisc + * rtnl_qdisc_set_kind(qdisc, "pfifo"); + * + * // Specify the device the qdisc should be attached to + * rtnl_qdisc_set_ifindex(qdisc, ifindex); + * + * // ... specify the parent qdisc + * rtnl_qdisc_set_parent(qdisc, TC_H_ROOT); + * + * // Specifying the handle is not required but makes reidentifying easier + * // and may help to avoid adding a qdisc twice. + * rtnl_qdisc_set_handle(qdisc, 0x000A0000); + * + * // Now on to specify the qdisc specific options, see the relevant qdisc + * // modules for documentation, in this example we set the upper limit of + * // the packet fifo qdisc to 64 + * rtnl_qdisc_fifo_set_limit(qdisc, 64); + * + * rtnl_qdisc_add(handle, qdisc, NLM_R_REPLACE); + * + * // Free up the memory + * rtnl_qdisc_put(qdisc); + * @endcode + * + * @par 2) Deleting a Qdisc + * @code + * // Allocate a new empty qdisc to be filled out with the parameters + * // specifying the qdisc to be deleted. Alternatively a fully equiped + * // Qdisc object from a cache can be used. + * struct rtnl_qdisc *qdisc = rtnl_qdisc_alloc(); + * + * // The interface index of the device the qdisc is on and the parent handle + * // are the least required fields to be filled out. + * // Note: Specify TC_H_ROOT or TC_H_INGRESS as parent handle to delete the + * // root respectively root ingress qdisc. + * rtnl_qdisc_set_ifindex(qdisc, ifindex); + * rtnl_qdisc_set_parent(qdisc, parent_handle); + * + * // If required for identification, the handle can be specified as well. + * rtnl_qdisc_set_handle(qdisc, qdisc_handle); + * + * // Not required but maybe helpful as sanity check, the kind of the qdisc + * // can be specified to avoid mistakes. + * rtnl_qdisc_set_kind(qdisc, "pfifo"); + * + * // Finally delete the qdisc with rtnl_qdisc_delete(), alternatively + * // rtnl_qdisc_build_delete_request() can be invoked to generate an + * // appropritate netlink message to send out. + * rtnl_qdisc_delete(handle, qdisc); + * + * // Free up the memory + * rtnl_qdisc_put(qdisc); + * @endcode + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct nl_cache_ops rtnl_qdisc_ops; + +static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + int err = -ENOMEM; + struct nl_parser_param *pp = arg; + struct rtnl_qdisc *qdisc; + struct rtnl_qdisc_ops *qops; + + qdisc = rtnl_qdisc_alloc(); + if (!qdisc) { + err = nl_errno(ENOMEM); + goto errout; + } + + qdisc->ce_msgtype = n->nlmsg_type; + + err = tca_msg_parser(n, (struct rtnl_tca *) qdisc); + if (err < 0) + goto errout_free; + + qops = rtnl_qdisc_lookup_ops(qdisc); + if (qops && qops->qo_msg_parser) { + err = qops->qo_msg_parser(qdisc); + if (err < 0) + goto errout_free; + } + + err = pp->pp_cb((struct nl_object *) qdisc, pp); + if (err < 0) + goto errout_free; + + return P_ACCEPT; + +errout_free: + rtnl_qdisc_put(qdisc); +errout: + return err; +} + +static int qdisc_request_update(struct nl_cache *c, struct nl_handle *h) +{ + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = c->c_iarg1, + }; + + return nl_send_simple(h, RTM_GETQDISC, NLM_F_DUMP, &tchdr, + sizeof(tchdr)); +} + +/** + * @name QDisc Addition + * @{ + */ + +static struct nl_msg *qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags) +{ + struct rtnl_qdisc_ops *qops; + struct nl_msg *msg; + int err; + + msg = tca_build_msg((struct rtnl_tca *) qdisc, type, flags); + if (!msg) + goto errout; + + qops = rtnl_qdisc_lookup_ops(qdisc); + if (qops && qops->qo_get_opts) { + struct nl_msg *opts; + + opts = qops->qo_get_opts(qdisc); + if (opts) { + err = nla_put_nested(msg, TCA_OPTIONS, opts); + nlmsg_free(opts); + if (err < 0) + goto errout; + } + } + + return msg; +errout: + nlmsg_free(msg); + + return NULL; +} + +/** + * Build a netlink message to add a new qdisc + * @arg qdisc qdisc to add + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting an addition of a qdisc. + * The netlink message header isn't fully equipped with all relevant + * fields and must be sent out via nl_send_auto_complete() or + * supplemented as needed. + * + * Common message flags used: + * - NLM_F_REPLACE - replace a potential existing qdisc + * + * @return New netlink message + */ +struct nl_msg *rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, + int flags) +{ + struct nl_msg *msg; + + msg = qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags); + if (!msg) + nl_errno(ENOMEM); + + return msg; +} + +/** + * Add a new qdisc + * @arg handle netlink handle + * @arg qdisc qdisc to delete + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_qdisc_build_add_request(), + * sends the request to the kernel and waits for the ACK to be + * received and thus blocks until the request has been processed. + * + * Common message flags used: + * - NLM_F_REPLACE - replace a potential existing qdisc + * + * @return 0 on success or a negative error code + */ +int rtnl_qdisc_add(struct nl_handle *handle, struct rtnl_qdisc *qdisc, + int flags) +{ + struct nl_msg *msg; + int err; + + msg = rtnl_qdisc_build_add_request(qdisc, flags); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name QDisc Modification + * @{ + */ + +/** + * Build a netlink message to change attributes of a existing qdisc + * @arg qdisc qdisc to change + * @arg new new qdisc attributes + * + * Builds a new netlink message requesting an change of qdisc + * attributes. The netlink message header isn't fully equipped + * with all relevant fields and must be sent out via + * nl_send_auto_complete() or supplemented as needed. + * + * @return New netlink message + */ +struct nl_msg *rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new) +{ + return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE); +} + +/** + * Change attributes of a qdisc + * @arg handle netlink handle + * @arg qdisc qdisc to change + * @arg new new qdisc attributes + * + * Builds a netlink message by calling rtnl_qdisc_build_change_request(), + * sends the request to the kernel and waits for the ACK to be + * received and thus blocks until the request has been processed. + * + * @return 0 on success or a negative error code + */ +int rtnl_qdisc_change(struct nl_handle *handle, struct rtnl_qdisc *qdisc, + struct rtnl_qdisc *new) +{ + struct nl_msg *msg; + int err; + + msg = rtnl_qdisc_build_change_request(qdisc, new); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name QDisc Deletion + * @{ + */ + +/** + * Build a netlink request message to delete a qdisc + * @arg qdisc qdisc to delete + * + * Builds a new netlink message requesting a deletion of a qdisc. + * The netlink message header isn't fully equipped with all relevant + * fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. + * + * @return New netlink message + */ +struct nl_msg *rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc) +{ + struct nl_msg *msg; + struct tcmsg tchdr; + int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT; + + if ((qdisc->ce_mask & required) != required) + BUG(); + + msg = nlmsg_alloc_simple(RTM_DELQDISC, 0); + if (!msg) + return NULL; + + tchdr.tcm_family = AF_UNSPEC, + tchdr.tcm_handle = qdisc->q_handle, + tchdr.tcm_parent = qdisc->q_parent, + tchdr.tcm_ifindex = qdisc->q_ifindex, + nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO); + + return msg; +} + +/** + * Delete a qdisc + * @arg handle netlink handle + * @arg qdisc qdisc to delete + * + * Builds a netlink message by calling rtnl_qdisc_build_delete_request(), + * sends the request to the kernel and waits for the ACK to be + * received and thus blocks until the request has been processed. + * + * @return 0 on success or a negative error code + */ +int rtnl_qdisc_delete(struct nl_handle *handle, struct rtnl_qdisc *qdisc) +{ + struct nl_msg *msg; + int err; + + msg = rtnl_qdisc_build_delete_request(qdisc); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name Qdisc Cache Management + * @{ + */ + +/** + * Build a qdisc cache including all qdiscs currently configured in + * the kernel + * @arg handle netlink handle + * + * Allocates a new cache, initializes it properly and updates it to + * include all qdiscs currently configured in the kernel. + * + * @note The caller is responsible for destroying and freeing the + * cache after using it. + * @return The cache or NULL if an error has occured. + */ +struct nl_cache * rtnl_qdisc_alloc_cache(struct nl_handle *handle) +{ + struct nl_cache * cache; + + cache = nl_cache_alloc(&rtnl_qdisc_ops); + if (cache == NULL) + return NULL; + + if (handle && nl_cache_refill(handle, cache) < 0) { + nl_cache_free(cache); + return NULL; + } + + return cache; +} + +/** + * Look up qdisc by its parent in the provided cache + * @arg cache qdisc cache + * @arg ifindex interface the qdisc is attached to + * @arg parent parent handle + * @return pointer to qdisc inside the cache or NULL if no match was found. + */ +struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache, + int ifindex, uint32_t parent) +{ + struct rtnl_qdisc *q; + + if (cache->c_ops != &rtnl_qdisc_ops) + return NULL; + + nl_list_for_each_entry(q, &cache->c_items, ce_list) { + if (q->q_parent == parent && q->q_ifindex == ifindex) { + nl_object_get((struct nl_object *) q); + return q; + } + } + + return NULL; +} + +/** + * Look up qdisc by its handle in the provided cache + * @arg cache qdisc cache + * @arg ifindex interface the qdisc is attached to + * @arg handle qdisc handle + * @return pointer to qdisc inside the cache or NULL if no match was found. + */ +struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache, + int ifindex, uint32_t handle) +{ + struct rtnl_qdisc *q; + + if (cache->c_ops != &rtnl_qdisc_ops) + return NULL; + + nl_list_for_each_entry(q, &cache->c_items, ce_list) { + if (q->q_handle == handle && q->q_ifindex == ifindex) { + nl_object_get((struct nl_object *) q); + return q; + } + } + + return NULL; +} + +/** @} */ + +static struct nl_cache_ops rtnl_qdisc_ops = { + .co_name = "route/qdisc", + .co_hdrsize = sizeof(struct tcmsg), + .co_msgtypes = { + { RTM_NEWQDISC, NL_ACT_NEW, "new" }, + { RTM_DELQDISC, NL_ACT_DEL, "del" }, + { RTM_GETQDISC, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = qdisc_request_update, + .co_msg_parser = qdisc_msg_parser, + .co_obj_ops = &qdisc_obj_ops, +}; + +static void __init qdisc_init(void) +{ + nl_cache_mngt_register(&rtnl_qdisc_ops); +} + +static void __exit qdisc_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_qdisc_ops); +} + +/** @} */ diff --git a/lib/route/qdisc_api.c b/lib/route/qdisc_api.c new file mode 100644 index 0000000..ef4d07a --- /dev/null +++ b/lib/route/qdisc_api.c @@ -0,0 +1,98 @@ +/* + * lib/route/qdisc_api.c Queueing Discipline Module API + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_api Queueing Discipline Modules + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct rtnl_qdisc_ops *qdisc_ops_list; + +/** + * @name Module API + * @{ + */ + +/** + * Register a qdisc module + * @arg qops qdisc module operations + */ +int rtnl_qdisc_register(struct rtnl_qdisc_ops *qops) +{ + struct rtnl_qdisc_ops *o, **op; + + if (!qops->qo_kind[0]) + BUG(); + + for (op = &qdisc_ops_list; (o = *op) != NULL; op = &o->qo_next) + if (!strcasecmp(qops->qo_kind, o->qo_kind)) + return nl_errno(EEXIST); + + qops->qo_next = NULL; + *op = qops; + + return 0; +} + +/** + * Unregister a qdisc module + * @arg qops qdisc module operations + */ +int rtnl_qdisc_unregister(struct rtnl_qdisc_ops *qops) +{ + struct rtnl_qdisc_ops *o, **op; + + for (op = &qdisc_ops_list; (o = *op) != NULL; op = &o->qo_next) + if (!strcasecmp(qops->qo_kind, o->qo_kind)) + break; + + if (!o) + return nl_errno(ENOENT); + + *op = qops->qo_next; + + return 0; +} + +struct rtnl_qdisc_ops *__rtnl_qdisc_lookup_ops(const char *kind) +{ + struct rtnl_qdisc_ops *qops; + + for (qops = qdisc_ops_list; qops; qops = qops->qo_next) + if (!strcmp(kind, qops->qo_kind)) + return qops; + + return NULL; +} + +struct rtnl_qdisc_ops *rtnl_qdisc_lookup_ops(struct rtnl_qdisc *qdisc) +{ + if (!qdisc->q_ops) + qdisc->q_ops = __rtnl_qdisc_lookup_ops(qdisc->q_kind); + + return qdisc->q_ops; +} + +/** @} */ + +/** @} */ diff --git a/lib/route/qdisc_obj.c b/lib/route/qdisc_obj.c new file mode 100644 index 0000000..bbbb954 --- /dev/null +++ b/lib/route/qdisc_obj.c @@ -0,0 +1,273 @@ +/* + * lib/route/qdisc_obj.c Queueing Discipline Object + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup qdisc + * @defgroup qdisc_obj Queueing Discipline Object + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void qdisc_free_data(struct nl_object *obj) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj; + struct rtnl_qdisc_ops *qops; + + tca_free_data((struct rtnl_tca *) qdisc); + + qops = rtnl_qdisc_lookup_ops(qdisc); + if (qops && qops->qo_free_data) + qops->qo_free_data(qdisc); +} + +static int qdisc_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_qdisc *dst = (struct rtnl_qdisc *) _dst; + struct rtnl_qdisc *src = (struct rtnl_qdisc *) _src; + struct rtnl_qdisc_ops *qops; + int err; + + err = tca_clone((struct rtnl_tca *) dst, (struct rtnl_tca *) src); + if (err < 0) + goto errout; + + qops = rtnl_qdisc_lookup_ops(src); + if (qops && qops->qo_clone) + err = qops->qo_clone(dst, src); +errout: + return err; +} + +static int qdisc_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj; + struct rtnl_qdisc_ops *qops; + + int line = tca_dump_brief((struct rtnl_tca *) qdisc, "qdisc", p, 0); + + qops = rtnl_qdisc_lookup_ops(qdisc); + if (qops && qops->qo_dump[NL_DUMP_BRIEF]) + line = qops->qo_dump[NL_DUMP_BRIEF](qdisc, p, line); + + dp_dump(p, "\n"); + + return line; +} + +static int qdisc_dump_full(struct nl_object *arg, struct nl_dump_params *p) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) arg; + struct rtnl_qdisc_ops *qops; + + int line = qdisc_dump_brief(arg, p); + + line = tca_dump_full((struct rtnl_tca *) qdisc, p, line); + dp_dump(p, "refcnt %u ", qdisc->q_info); + + qops = rtnl_qdisc_lookup_ops(qdisc); + if (qops && qops->qo_dump[NL_DUMP_FULL]) + line = qops->qo_dump[NL_DUMP_FULL](qdisc, p, line); + + dp_dump(p, "\n"); + return line; +} + +static int qdisc_dump_stats(struct nl_object *arg, struct nl_dump_params *p) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) arg; + struct rtnl_qdisc_ops *qops; + + int line = qdisc_dump_full(arg, p); + line = tca_dump_stats((struct rtnl_tca *) qdisc, p, line ); + dp_dump(p, "\n"); + + qops = rtnl_qdisc_lookup_ops(qdisc); + if (qops && qops->qo_dump[NL_DUMP_STATS]) + line = qops->qo_dump[NL_DUMP_STATS](qdisc, p, line); + + return line; +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_qdisc *rtnl_qdisc_alloc(void) +{ + return (struct rtnl_qdisc *) nl_object_alloc(&qdisc_obj_ops); +} + +void rtnl_qdisc_put(struct rtnl_qdisc *qdisc) +{ + nl_object_put((struct nl_object *) qdisc); +} + +/** @} */ + +/** + * @name Iterators + * @{ + */ + +/** + * Call a callback for each child class of a qdisc + * @arg qdisc the parent qdisc + * @arg cache a class cache including all classes of the interface + * the specified qdisc is attached to + * @arg cb callback function + * @arg arg argument to be passed to callback function + */ +void rtnl_qdisc_foreach_child(struct rtnl_qdisc *qdisc, struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct rtnl_class *filter; + + filter = rtnl_class_alloc(); + if (!filter) + return; + + rtnl_class_set_parent(filter, qdisc->q_handle); + rtnl_class_set_ifindex(filter, qdisc->q_ifindex); + rtnl_class_set_kind(filter, qdisc->q_kind); + + nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg); + + rtnl_class_put(filter); +} + +/** + * Call a callback for each filter attached to the qdisc + * @arg qdisc the parent qdisc + * @arg cache a filter cache including at least all the filters + * attached to the specified qdisc + * @arg cb callback function + * @arg arg argument to be passed to callback function + */ +void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *qdisc, struct nl_cache *cache, + void (*cb)(struct nl_object *, void *), void *arg) +{ + struct rtnl_cls *filter; + + filter = rtnl_cls_alloc(); + if (!filter) + return; + + rtnl_cls_set_ifindex(filter, qdisc->q_ifindex); + rtnl_cls_set_parent(filter, qdisc->q_parent); + + nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg); + rtnl_cls_put(filter); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void rtnl_qdisc_set_ifindex(struct rtnl_qdisc *qdisc, int ifindex) +{ + tca_set_ifindex((struct rtnl_tca *) qdisc, ifindex); +} + +int rtnl_qdisc_get_ifindex(struct rtnl_qdisc *qdisc) +{ + return tca_get_ifindex((struct rtnl_tca *) qdisc); +} + +void rtnl_qdisc_set_handle(struct rtnl_qdisc *qdisc, uint32_t handle) +{ + tca_set_handle((struct rtnl_tca *) qdisc, handle); +} + +uint32_t rtnl_qdisc_get_handle(struct rtnl_qdisc *qdisc) +{ + return tca_get_handle((struct rtnl_tca *) qdisc); +} + +void rtnl_qdisc_set_parent(struct rtnl_qdisc *qdisc, uint32_t parent) +{ + tca_set_parent((struct rtnl_tca *) qdisc, parent); +} + +uint32_t rtnl_qdisc_get_parent(struct rtnl_qdisc *qdisc) +{ + return tca_get_parent((struct rtnl_tca *) qdisc); +} + +void rtnl_qdisc_set_kind(struct rtnl_qdisc *qdisc, const char *name) +{ + tca_set_kind((struct rtnl_tca *) qdisc, name); + qdisc->q_ops = __rtnl_qdisc_lookup_ops(name); +} + +char *rtnl_qdisc_get_kind(struct rtnl_qdisc *qdisc) +{ + return tca_get_kind((struct rtnl_tca *) qdisc); +} + +uint64_t rtnl_qdisc_get_stat(struct rtnl_qdisc *qdisc, + enum rtnl_tc_stats_id id) +{ + return tca_get_stat((struct rtnl_tca *) qdisc, id); +} + +/** @} */ + +/** + * @name Qdisc Specific Options + * @{ + */ + +/** + * Return qdisc specific options for use in TCA_OPTIONS + * @arg qdisc qdisc carrying the optiosn + * + * @return new headerless netlink message carrying the options as payload + */ +struct nl_msg *rtnl_qdisc_get_opts(struct rtnl_qdisc *qdisc) +{ + struct rtnl_qdisc_ops *ops; + + ops = rtnl_qdisc_lookup_ops(qdisc); + if (ops && ops->qo_get_opts) + return ops->qo_get_opts(qdisc); + + return NULL; +} + +/** @} */ + +struct nl_object_ops qdisc_obj_ops = { + .oo_name = "route/qdisc", + .oo_size = sizeof(struct rtnl_qdisc), + .oo_free_data = qdisc_free_data, + .oo_clone = qdisc_clone, + .oo_dump[NL_DUMP_BRIEF] = qdisc_dump_brief, + .oo_dump[NL_DUMP_FULL] = qdisc_dump_full, + .oo_dump[NL_DUMP_STATS] = qdisc_dump_stats, + .oo_compare = tca_compare, + .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE), +}; + +/** @} */ diff --git a/lib/route/route.c b/lib/route/route.c new file mode 100644 index 0000000..4382e19 --- /dev/null +++ b/lib/route/route.c @@ -0,0 +1,444 @@ +/* + * lib/route/route.c Routes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup route Routing + * @brief + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct nl_cache_ops rtnl_route_ops; + +static struct nla_policy route_policy[RTA_MAX+1] = { + [RTA_IIF] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ, }, + [RTA_OIF] = { .type = NLA_U32 }, + [RTA_PRIORITY] = { .type = NLA_U32 }, + [RTA_FLOW] = { .type = NLA_U32 }, + [RTA_MP_ALGO] = { .type = NLA_U32 }, + [RTA_CACHEINFO] = { .minlen = sizeof(struct rta_cacheinfo) }, + [RTA_METRICS] = { .type = NLA_NESTED }, + [RTA_MULTIPATH] = { .type = NLA_NESTED }, +}; + +static void copy_cacheinfo_into_route(struct rta_cacheinfo *ci, + struct rtnl_route *route) +{ + struct rtnl_rtcacheinfo nci = { + .rtci_clntref = ci->rta_clntref, + .rtci_last_use = ci->rta_lastuse, + .rtci_expires = ci->rta_expires, + .rtci_error = ci->rta_error, + .rtci_used = ci->rta_used, + .rtci_id = ci->rta_id, + .rtci_ts = ci->rta_ts, + .rtci_tsage = ci->rta_tsage, + }; + + rtnl_route_set_cacheinfo(route, &nci); +} + +static int route_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, void *arg) +{ + struct nl_parser_param *pp = arg; + struct rtmsg *rtm; + struct rtnl_route *route; + struct nlattr *tb[RTA_MAX + 1]; + struct nl_addr *src = NULL, *dst = NULL, *addr; + int err; + + route = rtnl_route_alloc(); + if (!route) { + err = nl_errno(ENOMEM); + goto errout; + } + + route->ce_msgtype = nlh->nlmsg_type; + + err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, + route_policy); + if (err < 0) + goto errout; + + rtm = nlmsg_data(nlh); + rtnl_route_set_family(route, rtm->rtm_family); + rtnl_route_set_tos(route, rtm->rtm_tos); + rtnl_route_set_table(route, rtm->rtm_table); + rtnl_route_set_type(route, rtm->rtm_type); + rtnl_route_set_scope(route, rtm->rtm_scope); + rtnl_route_set_protocol(route, rtm->rtm_protocol); + rtnl_route_set_flags(route, rtm->rtm_flags); + + if (tb[RTA_DST]) { + dst = nla_get_addr(tb[RTA_DST], rtm->rtm_family); + if (dst == NULL) + goto errout_errno; + } else if (rtm->rtm_dst_len) + dst = nl_addr_alloc(0); + + if (dst) { + nl_addr_set_prefixlen(dst, rtm->rtm_dst_len); + rtnl_route_set_dst(route, dst); + nl_addr_put(dst); + } + + if (tb[RTA_SRC]) { + src = nla_get_addr(tb[RTA_SRC], rtm->rtm_family); + if (src == NULL) + goto errout_errno; + } else if (rtm->rtm_src_len) + src = nl_addr_alloc(0); + + if (src) { + nl_addr_set_prefixlen(src, rtm->rtm_src_len); + rtnl_route_set_src(route, src); + nl_addr_put(src); + } + + if (tb[RTA_IIF]) + rtnl_route_set_iif(route, nla_get_string(tb[RTA_IIF])); + + if (tb[RTA_OIF]) + rtnl_route_set_oif(route, nla_get_u32(tb[RTA_OIF])); + + if (tb[RTA_GATEWAY]) { + addr = nla_get_addr(tb[RTA_GATEWAY], route->rt_family); + if (addr == NULL) + goto errout_errno; + rtnl_route_set_gateway(route, addr); + nl_addr_put(addr); + } + + if (tb[RTA_PRIORITY]) + rtnl_route_set_prio(route, nla_get_u32(tb[RTA_PRIORITY])); + + if (tb[RTA_PREFSRC]) { + addr = nla_get_addr(tb[RTA_PREFSRC], route->rt_family); + if (addr == NULL) + goto errout_errno; + rtnl_route_set_pref_src(route, addr); + nl_addr_put(addr); + } + + if (tb[RTA_METRICS]) { + struct nlattr *mtb[RTAX_MAX + 1]; + int i; + + err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL); + if (err < 0) + goto errout; + + for (i = 1; i <= RTAX_MAX; i++) { + if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) { + uint32_t m = nla_get_u32(mtb[i]); + if (rtnl_route_set_metric(route, i, m) < 0) + goto errout_errno; + } + } + } + + if (tb[RTA_MULTIPATH]) { + struct rtnl_nexthop *nh; + struct rtnexthop *rtnh = nla_data(tb[RTA_MULTIPATH]); + size_t tlen = nla_len(tb[RTA_MULTIPATH]); + + while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) { + nh = rtnl_route_nh_alloc(); + if (!nh) + goto errout; + + rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops); + rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex); + rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags); + + if (rtnh->rtnh_len > sizeof(*rtnh)) { + struct nlattr *ntb[RTA_MAX + 1]; + nla_parse(ntb, RTA_MAX, (struct nlattr *) + RTNH_DATA(rtnh), + rtnh->rtnh_len - sizeof(*rtnh), + route_policy); + + if (ntb[RTA_GATEWAY]) { + nh->rtnh_gateway = nla_get_addr( + ntb[RTA_GATEWAY], + route->rt_family); + nh->rtnh_mask = NEXTHOP_HAS_GATEWAY; + } + } + + rtnl_route_add_nexthop(route, nh); + tlen -= RTNH_ALIGN(rtnh->rtnh_len); + rtnh = RTNH_NEXT(rtnh); + } + } + + if (tb[RTA_FLOW]) + rtnl_route_set_realms(route, nla_get_u32(tb[RTA_FLOW])); + + if (tb[RTA_CACHEINFO]) + copy_cacheinfo_into_route(nla_data(tb[RTA_CACHEINFO]), route); + + if (tb[RTA_MP_ALGO]) + rtnl_route_set_mp_algo(route, nla_get_u32(tb[RTA_MP_ALGO])); + + err = pp->pp_cb((struct nl_object *) route, pp); + if (err < 0) + goto errout; + + return P_ACCEPT; + +errout_errno: + err = nl_get_errno(); +errout: + rtnl_route_put(route); + return err; + +} + +static int route_request_update(struct nl_cache *c, struct nl_handle *h) +{ + return nl_rtgen_request(h, RTM_GETROUTE, AF_UNSPEC, NLM_F_DUMP); +} + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a route cache holding all routes currently configured in the kernel + * @arg handle netlink handle + * + * Allocates a new cache, initializes it properly and updates it to + * contain all routes currently configured in the kernel. + * + * @note The caller is responsible for destroying and freeing the + * cache after using it. + * @return The cache or NULL if an error has occured. + */ +struct nl_cache *rtnl_route_alloc_cache(struct nl_handle *handle) +{ + struct nl_cache *cache; + + cache = nl_cache_alloc(&rtnl_route_ops); + if (!cache) + return NULL; + + if (handle && nl_cache_refill(handle, cache) < 0) { + free(cache); + return NULL; + } + + return cache; +} + +/** @} */ + +/** + * @name Route Addition + * @{ + */ + +static struct nl_msg *build_route_msg(struct rtnl_route *tmpl, int cmd, + int flags) +{ + struct nl_msg *msg; + struct nl_addr *addr; + int scope, i, oif, nmetrics = 0; + struct nlattr *metrics; + struct rtmsg rtmsg = { + .rtm_family = rtnl_route_get_family(tmpl), + .rtm_dst_len = rtnl_route_get_dst_len(tmpl), + .rtm_src_len = rtnl_route_get_src_len(tmpl), + .rtm_tos = rtnl_route_get_tos(tmpl), + .rtm_table = rtnl_route_get_table(tmpl), + .rtm_type = rtnl_route_get_type(tmpl), + .rtm_protocol = rtnl_route_get_protocol(tmpl), + .rtm_flags = rtnl_route_get_flags(tmpl), + }; + + if (rtmsg.rtm_family == AF_UNSPEC) { + nl_error(EINVAL, "Cannot build route message, address " \ + "family is unknown."); + return NULL; + } + + scope = rtnl_route_get_scope(tmpl); + if (scope == RT_SCOPE_NOWHERE) { + if (rtmsg.rtm_type == RTN_LOCAL) + scope = RT_SCOPE_HOST; + else { + /* XXX Change to UNIVERSE if gw || nexthops */ + scope = RT_SCOPE_LINK; + } + } + + rtmsg.rtm_scope = scope; + + msg = nlmsg_alloc_simple(cmd, flags); + if (msg == NULL) + return NULL; + + if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + addr = rtnl_route_get_dst(tmpl); + if (addr) + NLA_PUT_ADDR(msg, RTA_DST, addr); + + addr = rtnl_route_get_src(tmpl); + if (addr) + NLA_PUT_ADDR(msg, RTA_SRC, addr); + + addr = rtnl_route_get_gateway(tmpl); + if (addr) + NLA_PUT_ADDR(msg, RTA_GATEWAY, addr); + + addr = rtnl_route_get_pref_src(tmpl); + if (addr) + NLA_PUT_ADDR(msg, RTA_PREFSRC, addr); + + NLA_PUT_U32(msg, RTA_PRIORITY, rtnl_route_get_prio(tmpl)); + + oif = rtnl_route_get_oif(tmpl); + if (oif != RTNL_LINK_NOT_FOUND) + NLA_PUT_U32(msg, RTA_OIF, oif); + + for (i = 1; i <= RTAX_MAX; i++) + if (rtnl_route_get_metric(tmpl, i) != UINT_MAX) + nmetrics++; + + if (nmetrics > 0) { + unsigned int val; + + metrics = nla_nest_start(msg, RTA_METRICS); + if (metrics == NULL) + goto nla_put_failure; + + for (i = 1; i <= RTAX_MAX; i++) { + val = rtnl_route_get_metric(tmpl, i); + if (val != UINT_MAX) + NLA_PUT_U32(msg, i, val); + } + + nla_nest_end(msg, metrics); + } + +#if 0 + RTA_IIF, + RTA_MULTIPATH, + RTA_PROTOINFO, + RTA_FLOW, + RTA_CACHEINFO, + RTA_SESSION, + RTA_MP_ALGO, +#endif + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +struct nl_msg *rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags) +{ + return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags); +} + +int rtnl_route_add(struct nl_handle *handle, struct rtnl_route *route, + int flags) +{ + struct nl_msg *msg; + int err; + + msg = rtnl_route_build_add_request(route, flags); + if (!msg) + return nl_get_errno(); + + err = nl_send_auto_complete(handle, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(handle); +} + +struct nl_msg *rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags) +{ + return build_route_msg(tmpl, RTM_DELROUTE, flags); +} + +int rtnl_route_del(struct nl_handle *handle, struct rtnl_route *route, + int flags) +{ + struct nl_msg *msg; + int err; + + msg = rtnl_route_build_del_request(route, flags); + if (!msg) + return nl_get_errno(); + + err = nl_send_auto_complete(handle, msg); + nlmsg_free(msg); + if (err < 0) + return err; + + return nl_wait_for_ack(handle); +} + +/** @} */ + +static struct nl_af_group route_groups[] = { + { AF_INET, RTNLGRP_IPV4_ROUTE }, + { AF_INET6, RTNLGRP_IPV6_ROUTE }, + { AF_DECnet, RTNLGRP_DECnet_ROUTE }, + { END_OF_GROUP_LIST }, +}; + +static struct nl_cache_ops rtnl_route_ops = { + .co_name = "route/route", + .co_hdrsize = sizeof(struct rtmsg), + .co_msgtypes = { + { RTM_NEWROUTE, NL_ACT_NEW, "new" }, + { RTM_DELROUTE, NL_ACT_DEL, "del" }, + { RTM_GETROUTE, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_groups = route_groups, + .co_request_update = route_request_update, + .co_msg_parser = route_msg_parser, + .co_obj_ops = &route_obj_ops, +}; + +static void __init route_init(void) +{ + nl_cache_mngt_register(&rtnl_route_ops); +} + +static void __exit route_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_route_ops); +} + +/** @} */ diff --git a/lib/route/route_obj.c b/lib/route/route_obj.c new file mode 100644 index 0000000..7d954fc --- /dev/null +++ b/lib/route/route_obj.c @@ -0,0 +1,908 @@ +/* + * lib/route/route_obj.c Route Object + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup route + * @defgroup route_obj Route Object + * + * @par Attributes + * @code + * Name Default + * ------------------------------------------------------------- + * routing table RT_TABLE_MAIN + * scope RT_SCOPE_NOWHERE + * tos 0 + * realms 0 + * protocol RTPROT_STATIC + * prio 0 + * family AF_UNSPEC + * type RTN_UNICAST + * oif RTNL_LINK_NOT_FOUND + * iif NULL + * mpalgo IP_MP_ALG_NONE + * @endcode + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define ROUTE_ATTR_FAMILY 0x000001 +#define ROUTE_ATTR_TOS 0x000002 +#define ROUTE_ATTR_TABLE 0x000004 +#define ROUTE_ATTR_PROTOCOL 0x000008 +#define ROUTE_ATTR_SCOPE 0x000010 +#define ROUTE_ATTR_TYPE 0x000020 +#define ROUTE_ATTR_FLAGS 0x000040 +#define ROUTE_ATTR_DST 0x000080 +#define ROUTE_ATTR_SRC 0x000100 +#define ROUTE_ATTR_IIF 0x000200 +#define ROUTE_ATTR_OIF 0x000400 +#define ROUTE_ATTR_GATEWAY 0x000800 +#define ROUTE_ATTR_PRIO 0x001000 +#define ROUTE_ATTR_PREF_SRC 0x002000 +#define ROUTE_ATTR_METRICS 0x004000 +#define ROUTE_ATTR_MULTIPATH 0x008000 +#define ROUTE_ATTR_REALMS 0x010000 +#define ROUTE_ATTR_CACHEINFO 0x020000 +#define ROUTE_ATTR_MP_ALGO 0x040000 +/** @endcond */ + +static int route_dump_brief(struct nl_object *a, struct nl_dump_params *p); + +static void route_constructor(struct nl_object *c) +{ + struct rtnl_route *r = (struct rtnl_route *) c; + + nl_init_list_head(&r->rt_nexthops); +} + +static void route_free_data(struct nl_object *c) +{ + struct rtnl_route *r = (struct rtnl_route *) c; + struct rtnl_nexthop *nh, *tmp; + + if (r == NULL) + return; + + nl_addr_put(r->rt_dst); + nl_addr_put(r->rt_src); + nl_addr_put(r->rt_gateway); + nl_addr_put(r->rt_pref_src); + + nl_list_for_each_entry_safe(nh, tmp, &r->rt_nexthops, rtnh_list) { + rtnl_route_remove_nexthop(nh); + rtnl_route_nh_free(nh); + } +} + +static int route_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_route *dst = (struct rtnl_route *) _dst; + struct rtnl_route *src = (struct rtnl_route *) _src; + struct rtnl_nexthop *nh, *new; + + if (src->rt_dst) + if (!(dst->rt_dst = nl_addr_clone(src->rt_dst))) + goto errout; + + if (src->rt_src) + if (!(dst->rt_src = nl_addr_clone(src->rt_src))) + goto errout; + + if (src->rt_gateway) + if (!(dst->rt_gateway = nl_addr_clone(src->rt_gateway))) + goto errout; + + if (src->rt_pref_src) + if (!(dst->rt_pref_src = nl_addr_clone(src->rt_pref_src))) + goto errout; + + nl_init_list_head(&dst->rt_nexthops); + nl_list_for_each_entry(nh, &src->rt_nexthops, rtnh_list) { + new = rtnl_route_nh_clone(nh); + if (!new) + goto errout; + + rtnl_route_add_nexthop(dst, new); + } + + return 0; +errout: + return nl_get_errno(); +} + +static int route_dump_brief(struct nl_object *a, struct nl_dump_params *p) +{ + struct rtnl_route *r = (struct rtnl_route *) a; + struct nl_cache *link_cache; + char buf[64]; + + link_cache = nl_cache_mngt_require("route/link"); + + if (r->ce_mask & ROUTE_ATTR_DST) + dp_dump(p, "%s ", nl_addr2str(r->rt_dst, buf, sizeof(buf))); + else + dp_dump(p, "default "); + + if (r->ce_mask & ROUTE_ATTR_OIF) { + if (link_cache) + dp_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, r->rt_oif, + buf, sizeof(buf))); + else + dp_dump(p, "dev %d ", r->rt_oif); + } + + if (r->ce_mask & ROUTE_ATTR_GATEWAY) + dp_dump(p, "via %s ", nl_addr2str(r->rt_gateway, buf, + sizeof(buf))); + else if (r->ce_mask & ROUTE_ATTR_MULTIPATH) + dp_dump(p, "via nexthops "); + + if (r->ce_mask & ROUTE_ATTR_SCOPE) + dp_dump(p, "scope %s ", + rtnl_scope2str(r->rt_scope, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_FLAGS && r->rt_flags) { + int flags = r->rt_flags; + + dp_dump(p, "<"); + +#define PRINT_FLAG(f) if (flags & RTNH_F_##f) { \ + flags &= ~RTNH_F_##f; dp_dump(p, #f "%s", flags ? "," : ""); } + PRINT_FLAG(DEAD); + PRINT_FLAG(ONLINK); + PRINT_FLAG(PERVASIVE); +#undef PRINT_FLAG + +#define PRINT_FLAG(f) if (flags & RTM_F_##f) { \ + flags &= ~RTM_F_##f; dp_dump(p, #f "%s", flags ? "," : ""); } + PRINT_FLAG(NOTIFY); + PRINT_FLAG(CLONED); + PRINT_FLAG(EQUALIZE); + PRINT_FLAG(PREFIX); +#undef PRINT_FLAG + + dp_dump(p, ">"); + } + + dp_dump(p, "\n"); + + return 1; +} + +static int route_dump_full(struct nl_object *a, struct nl_dump_params *p) +{ + struct rtnl_route *r = (struct rtnl_route *) a; + struct nl_cache *link_cache; + char buf[128]; + int i, line; + + link_cache = nl_cache_mngt_require("route/link"); + line = route_dump_brief(a, p); + + if (r->ce_mask & ROUTE_ATTR_MULTIPATH) { + struct rtnl_nexthop *nh; + + nl_list_for_each_entry(nh, &r->rt_nexthops, rtnh_list) { + dp_dump_line(p, line++, " via "); + + if (nh->rtnh_mask & NEXTHOP_HAS_GATEWAY) + dp_dump(p, "%s ", + nl_addr2str(nh->rtnh_gateway, + buf, sizeof(buf))); + if (link_cache) { + dp_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, + nh->rtnh_ifindex, + buf, sizeof(buf))); + } else + dp_dump(p, "dev %d ", nh->rtnh_ifindex); + + dp_dump(p, "weight %u <%s>\n", nh->rtnh_weight, + rtnl_route_nh_flags2str(nh->rtnh_flags, + buf, sizeof(buf))); + } + } + + dp_dump_line(p, line++, " "); + + if (r->ce_mask & ROUTE_ATTR_PREF_SRC) + dp_dump(p, "preferred-src %s ", + nl_addr2str(r->rt_pref_src, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_TABLE) + dp_dump(p, "table %s ", + rtnl_route_table2str(r->rt_table, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_TYPE) + dp_dump(p, "type %s ", + nl_rtntype2str(r->rt_type, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_PRIO) + dp_dump(p, "metric %#x ", r->rt_prio); + + if (r->ce_mask & ROUTE_ATTR_FAMILY) + dp_dump(p, "family %s ", + nl_af2str(r->rt_family, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_PROTOCOL) + dp_dump(p, "protocol %s ", + rtnl_route_proto2str(r->rt_protocol, buf, sizeof(buf))); + + dp_dump(p, "\n"); + + if ((r->ce_mask & (ROUTE_ATTR_IIF | ROUTE_ATTR_SRC | ROUTE_ATTR_TOS | + ROUTE_ATTR_REALMS)) || + ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && + r->rt_cacheinfo.rtci_error)) { + dp_dump_line(p, line++, " "); + + if (r->ce_mask & ROUTE_ATTR_IIF) + dp_dump(p, "iif %s ", r->rt_iif); + + if (r->ce_mask & ROUTE_ATTR_SRC) + dp_dump(p, "src %s ", + nl_addr2str(r->rt_src, buf, sizeof(buf))); + + if (r->ce_mask & ROUTE_ATTR_TOS) + dp_dump(p, "tos %#x ", r->rt_tos); + + if (r->ce_mask & ROUTE_ATTR_REALMS) + dp_dump(p, "realm %04x:%04x ", + RTNL_REALM_FROM(r->rt_realms), + RTNL_REALM_TO(r->rt_realms)); + + if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && + r->rt_cacheinfo.rtci_error) + dp_dump(p, "error %d (%s) ", r->rt_cacheinfo.rtci_error, + strerror(-r->rt_cacheinfo.rtci_error)); + + dp_dump(p, "\n"); + } + + if (r->ce_mask & ROUTE_ATTR_METRICS) { + dp_dump_line(p, line++, " "); + for (i = 0; i < RTAX_MAX; i++) + if (r->rt_metrics_mask & (1 << i)) + dp_dump(p, "%s %u ", + rtnl_route_metric2str(i+1, + buf, sizeof(buf)), + r->rt_metrics[i]); + dp_dump(p, "\n"); + } + + return line; +} + +static int route_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_route *route = (struct rtnl_route *) obj; + int line; + + line = route_dump_full(obj, p); + + if (route->ce_mask & ROUTE_ATTR_CACHEINFO) { + struct rtnl_rtcacheinfo *ci = &route->rt_cacheinfo; + dp_dump_line(p, line++, " used %u refcnt %u ", + ci->rtci_used, ci->rtci_clntref); + dp_dump_line(p, line++, "last-use %us expires %us\n", + ci->rtci_last_use / nl_get_hz(), + ci->rtci_expires / nl_get_hz()); + } + + return line; +} + +static int route_dump_xml(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_route *route = (struct rtnl_route *) obj; + char buf[128]; + int line = 0; + + dp_dump_line(p, line++, "\n"); + dp_dump_line(p, line++, " %s\n", + nl_af2str(route->rt_family, buf, sizeof(buf))); + + if (route->ce_mask & ROUTE_ATTR_DST) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(route->rt_dst, buf, sizeof(buf))); + + if (route->ce_mask & ROUTE_ATTR_SRC) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(route->rt_src, buf, sizeof(buf))); + + if (route->ce_mask & ROUTE_ATTR_GATEWAY) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(route->rt_gateway, buf, sizeof(buf))); + + if (route->ce_mask & ROUTE_ATTR_PREF_SRC) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(route->rt_pref_src, buf, sizeof(buf))); + + if (route->ce_mask & ROUTE_ATTR_IIF) + dp_dump_line(p, line++, " %s\n", route->rt_iif); + + if (route->ce_mask & ROUTE_ATTR_REALMS) + dp_dump_line(p, line++, " %u\n", + route->rt_realms); + + if (route->ce_mask & ROUTE_ATTR_TOS) + dp_dump_line(p, line++, " %u\n", route->rt_tos); + + if (route->ce_mask & ROUTE_ATTR_TABLE) + dp_dump_line(p, line++, " %u
\n", + route->rt_table); + + if (route->ce_mask & ROUTE_ATTR_SCOPE) + dp_dump_line(p, line++, " %s\n", + rtnl_scope2str(route->rt_scope, buf, sizeof(buf))); + + if (route->ce_mask & ROUTE_ATTR_PRIO) + dp_dump_line(p, line++, " %u\n", + route->rt_prio); + + if (route->ce_mask & ROUTE_ATTR_OIF) { + struct nl_cache *link_cache; + + link_cache = nl_cache_mngt_require("route/link"); + if (link_cache) + dp_dump_line(p, line++, " %s\n", + rtnl_link_i2name(link_cache, + route->rt_oif, + buf, sizeof(buf))); + else + dp_dump_line(p, line++, " %u\n", + route->rt_oif); + } + + if (route->ce_mask & ROUTE_ATTR_TYPE) + dp_dump_line(p, line++, " %s\n", + nl_rtntype2str(route->rt_type, buf, sizeof(buf))); + + dp_dump_line(p, line++, "
\n"); + +#if 0 + uint8_t rt_protocol; + uint32_t rt_flags; + uint32_t rt_metrics[RTAX_MAX]; + uint32_t rt_metrics_mask; + struct rtnl_nexthop * rt_nexthops; + struct rtnl_rtcacheinfo rt_cacheinfo; + uint32_t rt_mp_algo; + +#endif + + return line; +} + +static int route_dump_env(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_route *route = (struct rtnl_route *) obj; + char buf[128]; + int line = 0; + + dp_dump_line(p, line++, "ROUTE_FAMILY=%s\n", + nl_af2str(route->rt_family, buf, sizeof(buf))); + + if (route->ce_mask & ROUTE_ATTR_DST) + dp_dump_line(p, line++, "ROUTE_DST=%s\n", + nl_addr2str(route->rt_dst, buf, sizeof(buf))); + + if (route->ce_mask & ROUTE_ATTR_SRC) + dp_dump_line(p, line++, "ROUTE_SRC=%s\n", + nl_addr2str(route->rt_src, buf, sizeof(buf))); + + if (route->ce_mask & ROUTE_ATTR_GATEWAY) + dp_dump_line(p, line++, "ROUTE_GATEWAY=%s\n", + nl_addr2str(route->rt_gateway, buf, sizeof(buf))); + + if (route->ce_mask & ROUTE_ATTR_PREF_SRC) + dp_dump_line(p, line++, "ROUTE_PREFSRC=%s\n", + nl_addr2str(route->rt_pref_src, buf, sizeof(buf))); + + if (route->ce_mask & ROUTE_ATTR_IIF) + dp_dump_line(p, line++, "ROUTE_IIF=%s\n", route->rt_iif); + + if (route->ce_mask & ROUTE_ATTR_REALMS) + dp_dump_line(p, line++, "ROUTE_REALM=%u\n", + route->rt_realms); + + if (route->ce_mask & ROUTE_ATTR_TOS) + dp_dump_line(p, line++, "ROUTE_TOS=%u\n", route->rt_tos); + + if (route->ce_mask & ROUTE_ATTR_TABLE) + dp_dump_line(p, line++, "ROUTE_TABLE=%u\n", + route->rt_table); + + if (route->ce_mask & ROUTE_ATTR_SCOPE) + dp_dump_line(p, line++, "ROUTE_SCOPE=%s\n", + rtnl_scope2str(route->rt_scope, buf, sizeof(buf))); + + if (route->ce_mask & ROUTE_ATTR_PRIO) + dp_dump_line(p, line++, "ROUTE_METRIC=%u\n", + route->rt_prio); + + if (route->ce_mask & ROUTE_ATTR_OIF) { + struct nl_cache *link_cache; + + dp_dump_line(p, line++, "ROUTE_OIF_IFINDEX=%u\n", + route->rt_oif); + + link_cache = nl_cache_mngt_require("route/link"); + if (link_cache) + dp_dump_line(p, line++, "ROUTE_OIF_IFNAME=%s\n", + rtnl_link_i2name(link_cache, + route->rt_oif, + buf, sizeof(buf))); + } + + if (route->ce_mask & ROUTE_ATTR_TYPE) + dp_dump_line(p, line++, "ROUTE_TYPE=%s\n", + nl_rtntype2str(route->rt_type, buf, sizeof(buf))); + + return line; +} + +static int route_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) +{ + struct rtnl_route *a = (struct rtnl_route *) _a; + struct rtnl_route *b = (struct rtnl_route *) _b; + int diff = 0; + +#define ROUTE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, ROUTE_ATTR_##ATTR, a, b, EXPR) + + diff |= ROUTE_DIFF(FAMILY, a->rt_family != b->rt_family); + diff |= ROUTE_DIFF(TOS, a->rt_tos != b->rt_tos); + diff |= ROUTE_DIFF(TABLE, a->rt_table != b->rt_table); + diff |= ROUTE_DIFF(PROTOCOL, a->rt_protocol != b->rt_protocol); + diff |= ROUTE_DIFF(SCOPE, a->rt_scope != b->rt_scope); + diff |= ROUTE_DIFF(TYPE, a->rt_type != b->rt_type); + diff |= ROUTE_DIFF(OIF, a->rt_oif != b->rt_oif); + diff |= ROUTE_DIFF(PRIO, a->rt_prio != b->rt_prio); + diff |= ROUTE_DIFF(REALMS, a->rt_realms != b->rt_realms); + diff |= ROUTE_DIFF(MP_ALGO, a->rt_mp_algo != b->rt_mp_algo); + diff |= ROUTE_DIFF(DST, nl_addr_cmp(a->rt_dst, b->rt_dst)); + diff |= ROUTE_DIFF(SRC, nl_addr_cmp(a->rt_src, b->rt_src)); + diff |= ROUTE_DIFF(IIF, strcmp(a->rt_iif, b->rt_iif)); + diff |= ROUTE_DIFF(PREF_SRC, nl_addr_cmp(a->rt_pref_src, + b->rt_pref_src)); + diff |= ROUTE_DIFF(GATEWAY, nl_addr_cmp(a->rt_gateway, + b->rt_gateway)); + + /* FIXME: Compare metrics, multipath config */ + + if (flags & LOOSE_FLAG_COMPARISON) + diff |= ROUTE_DIFF(FLAGS, + (a->rt_flags ^ b->rt_flags) & b->rt_flag_mask); + else + diff |= ROUTE_DIFF(FLAGS, a->rt_flags != b->rt_flags); + +#undef ROUTE_DIFF + + return diff; +} + +static struct trans_tbl route_attrs[] = { + __ADD(ROUTE_ATTR_FAMILY, family) + __ADD(ROUTE_ATTR_TOS, tos) + __ADD(ROUTE_ATTR_TABLE, table) + __ADD(ROUTE_ATTR_PROTOCOL, protocol) + __ADD(ROUTE_ATTR_SCOPE, scope) + __ADD(ROUTE_ATTR_TYPE, type) + __ADD(ROUTE_ATTR_FLAGS, flags) + __ADD(ROUTE_ATTR_DST, dst) + __ADD(ROUTE_ATTR_SRC, src) + __ADD(ROUTE_ATTR_IIF, iif) + __ADD(ROUTE_ATTR_OIF, oif) + __ADD(ROUTE_ATTR_GATEWAY, gateway) + __ADD(ROUTE_ATTR_PRIO, prio) + __ADD(ROUTE_ATTR_PREF_SRC, pref_src) + __ADD(ROUTE_ATTR_METRICS, metrics) + __ADD(ROUTE_ATTR_MULTIPATH, multipath) + __ADD(ROUTE_ATTR_REALMS, realms) + __ADD(ROUTE_ATTR_CACHEINFO, cacheinfo) + __ADD(ROUTE_ATTR_MP_ALGO, mp_algo) +}; + +static char *route_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, route_attrs, + ARRAY_SIZE(route_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_route *rtnl_route_alloc(void) +{ + return (struct rtnl_route *) nl_object_alloc(&route_obj_ops); +} + +void rtnl_route_get(struct rtnl_route *route) +{ + nl_object_get((struct nl_object *) route); +} + +void rtnl_route_put(struct rtnl_route *route) +{ + nl_object_put((struct nl_object *) route); +} + +/** @} */ + +/** + * @name Attributes + * @{ + */ + +void rtnl_route_set_table(struct rtnl_route *route, int table) +{ + route->rt_table = table; + route->ce_mask |= ROUTE_ATTR_TABLE; +} + +int rtnl_route_get_table(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_TABLE) + return route->rt_table; + else + return RT_TABLE_MAIN; +} + +void rtnl_route_set_scope(struct rtnl_route *route, int scope) +{ + route->rt_scope = scope; + route->ce_mask |= ROUTE_ATTR_SCOPE; +} + +int rtnl_route_get_scope(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_SCOPE) + return route->rt_scope; + else + return RT_SCOPE_NOWHERE; +} + +void rtnl_route_set_tos(struct rtnl_route *route, int tos) +{ + route->rt_tos = tos; + route->ce_mask |= ROUTE_ATTR_TOS; +} + +int rtnl_route_get_tos(struct rtnl_route *route) +{ + return route->rt_tos; +} + +void rtnl_route_set_realms(struct rtnl_route *route, realm_t realms) +{ + route->rt_realms = realms; + route->ce_mask |= ROUTE_ATTR_REALMS; +} + +realm_t rtnl_route_get_realms(struct rtnl_route *route) +{ + return route->rt_realms; +} + +void rtnl_route_set_protocol(struct rtnl_route *route, int proto) +{ + route->rt_protocol = proto; + route->ce_mask |= ROUTE_ATTR_PROTOCOL; +} + +int rtnl_route_get_protocol(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_PROTOCOL) + return route->rt_protocol; + else + return RTPROT_STATIC; +} + +void rtnl_route_set_prio(struct rtnl_route *route, int prio) +{ + route->rt_prio = prio; + route->ce_mask |= ROUTE_ATTR_PRIO; +} + +int rtnl_route_get_prio(struct rtnl_route *route) +{ + return route->rt_prio; +} + +void rtnl_route_set_family(struct rtnl_route *route, int family) +{ + route->rt_family = family; + route->ce_mask |= ROUTE_ATTR_FAMILY; +} + +int rtnl_route_get_family(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_FAMILY) + return route->rt_family; + else + return AF_UNSPEC; +} + +int rtnl_route_set_dst(struct rtnl_route *route, struct nl_addr *addr) +{ + if (route->ce_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return nl_error(EINVAL, "Address family mismatch"); + } else + route->rt_family = addr->a_family; + + if (route->rt_dst) + nl_addr_put(route->rt_dst); + + nl_addr_get(addr); + route->rt_dst = addr; + + route->ce_mask |= (ROUTE_ATTR_DST | ROUTE_ATTR_FAMILY); + + return 0; +} + +struct nl_addr *rtnl_route_get_dst(struct rtnl_route *route) +{ + return route->rt_dst; +} + +int rtnl_route_get_dst_len(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_DST) + return nl_addr_get_prefixlen(route->rt_dst); + else + return 0; +} + +int rtnl_route_set_src(struct rtnl_route *route, struct nl_addr *addr) +{ + if (route->ce_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return nl_error(EINVAL, "Address family mismatch"); + } else + route->rt_family = addr->a_family; + + if (route->rt_src) + nl_addr_put(route->rt_src); + + nl_addr_get(addr); + route->rt_src = addr; + route->ce_mask |= (ROUTE_ATTR_SRC | ROUTE_ATTR_FAMILY); + + return 0; +} + +struct nl_addr *rtnl_route_get_src(struct rtnl_route *route) +{ + return route->rt_src; +} + +int rtnl_route_get_src_len(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_SRC) + return nl_addr_get_prefixlen(route->rt_src); + else + return 0; +} + +int rtnl_route_set_gateway(struct rtnl_route *route, struct nl_addr *addr) +{ + if (route->ce_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return nl_error(EINVAL, "Address family mismatch"); + } else + route->rt_family = addr->a_family; + + if (route->rt_gateway) + nl_addr_put(route->rt_gateway); + + nl_addr_get(addr); + route->rt_gateway = addr; + route->ce_mask |= (ROUTE_ATTR_GATEWAY | ROUTE_ATTR_FAMILY); + + return 0; +} + +struct nl_addr *rtnl_route_get_gateway(struct rtnl_route *route) +{ + return route->rt_gateway; +} + +void rtnl_route_set_type(struct rtnl_route *route, int type) +{ + route->rt_type = type; + route->ce_mask |= ROUTE_ATTR_TYPE; +} + +int rtnl_route_get_type(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_TYPE) + return route->rt_type; + else + return RTN_UNICAST; +} + +void rtnl_route_set_flags(struct rtnl_route *route, unsigned int flags) +{ + route->rt_flag_mask |= flags; + route->rt_flags |= flags; + route->ce_mask |= ROUTE_ATTR_FLAGS; +} + +void rtnl_route_unset_flags(struct rtnl_route *route, unsigned int flags) +{ + route->rt_flag_mask |= flags; + route->rt_flags &= ~flags; + route->ce_mask |= ROUTE_ATTR_FLAGS; +} + +unsigned int rtnl_route_get_flags(struct rtnl_route *route) +{ + return route->rt_flags; +} + +int rtnl_route_set_metric(struct rtnl_route *route, int metric, uint32_t value) +{ + if (metric > RTAX_MAX || metric < 1) + return nl_error(EINVAL, "Metric out of range (1..%d)", + RTAX_MAX); + + route->rt_metrics[metric - 1] = value; + route->rt_metrics_mask |= (1 << (metric - 1)); + + return 0; +} + +int rtnl_route_unset_metric(struct rtnl_route *route, int metric) +{ + if (metric > RTAX_MAX || metric < 1) + return nl_error(EINVAL, "Metric out of range (1..%d)", + RTAX_MAX); + + route->rt_metrics_mask &= ~(1 << (metric - 1)); + + return 0; +} + +unsigned int rtnl_route_get_metric(struct rtnl_route *route, int metric) +{ + if (metric > RTAX_MAX || metric < 1) + return UINT_MAX; + + if (!(route->rt_metrics_mask & (1 << (metric - 1)))) + return UINT_MAX; + + return route->rt_metrics[metric - 1]; +} + +int rtnl_route_set_pref_src(struct rtnl_route *route, struct nl_addr *addr) +{ + if (route->ce_mask & ROUTE_ATTR_FAMILY) { + if (addr->a_family != route->rt_family) + return nl_error(EINVAL, "Address family mismatch"); + } else + route->rt_family = addr->a_family; + + if (route->rt_pref_src) + nl_addr_put(route->rt_pref_src); + + nl_addr_get(addr); + route->rt_pref_src = addr; + route->ce_mask |= (ROUTE_ATTR_PREF_SRC | ROUTE_ATTR_FAMILY); + + return 0; +} + +struct nl_addr *rtnl_route_get_pref_src(struct rtnl_route *route) +{ + return route->rt_pref_src; +} + +void rtnl_route_set_oif(struct rtnl_route *route, int ifindex) +{ + route->rt_oif = ifindex; + route->ce_mask |= ROUTE_ATTR_OIF; +} + +int rtnl_route_get_oif(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_OIF) + return route->rt_oif; + else + return RTNL_LINK_NOT_FOUND; +} + +void rtnl_route_set_iif(struct rtnl_route *route, const char *name) +{ + strncpy(route->rt_iif, name, sizeof(route->rt_iif) - 1); + route->ce_mask |= ROUTE_ATTR_IIF; +} + +char *rtnl_route_get_iif(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_IIF) + return route->rt_iif; + else + return NULL; +} + +void rtnl_route_add_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh) +{ + nl_list_add_tail(&nh->rtnh_list, &route->rt_nexthops); + route->ce_mask |= ROUTE_ATTR_MULTIPATH; +} + +void rtnl_route_remove_nexthop(struct rtnl_nexthop *nh) +{ + nl_list_del(&nh->rtnh_list); +} + +struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *route) +{ + return &route->rt_nexthops; +} + +void rtnl_route_set_cacheinfo(struct rtnl_route *route, + struct rtnl_rtcacheinfo *ci) +{ + memcpy(&route->rt_cacheinfo, ci, sizeof(*ci)); + route->ce_mask |= ROUTE_ATTR_CACHEINFO; +} + +uint32_t rtnl_route_get_mp_algo(struct rtnl_route *route) +{ + if (route->ce_mask & ROUTE_ATTR_MP_ALGO) + return route->rt_mp_algo; + else + return IP_MP_ALG_NONE; +} + +void rtnl_route_set_mp_algo(struct rtnl_route *route, uint32_t algo) +{ + route->rt_mp_algo = algo; + route->ce_mask |= ROUTE_ATTR_MP_ALGO; +} + +/** @} */ + +struct nl_object_ops route_obj_ops = { + .oo_name = "route/route", + .oo_size = sizeof(struct rtnl_route), + .oo_constructor = route_constructor, + .oo_free_data = route_free_data, + .oo_clone = route_clone, + .oo_dump[NL_DUMP_BRIEF] = route_dump_brief, + .oo_dump[NL_DUMP_FULL] = route_dump_full, + .oo_dump[NL_DUMP_STATS] = route_dump_stats, + .oo_dump[NL_DUMP_XML] = route_dump_xml, + .oo_dump[NL_DUMP_ENV] = route_dump_env, + .oo_compare = route_compare, + .oo_attrs2str = route_attrs2str, + .oo_id_attrs = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | + ROUTE_ATTR_TABLE | ROUTE_ATTR_DST), +}; + +/** @} */ diff --git a/lib/route/route_utils.c b/lib/route/route_utils.c new file mode 100644 index 0000000..a12d169 --- /dev/null +++ b/lib/route/route_utils.c @@ -0,0 +1,183 @@ +/* + * lib/route/route_utils.c Routing Utilities + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup route + * @defgroup route_utils Utilities + * @brief Routing Utility Functions + * + * + * @par 1) Translating Routing Table Names + * @code + * // libnl is only aware of the de facto standard routing table names. + * // Additional name <-> identifier associations have to be read in via + * // a configuration file, f.e. /etc/iproute2/rt_tables + * err = rtnl_route_read_table_names("/etc/iproute2/rt_tables"); + * + * // Translating a table name to its idenfier + * int table = rtnl_route_str2table("main"); + * + * // ... and the other way around. + * char buf[32]; + * printf("Name: %s\n", + * rtnl_route_table2str(table, buf, sizeof(buf))); + * @endcode + * + * + * + * + * @{ + */ + +#include +#include +#include +#include +#include + +/** + * @name Routing Table Identifier Translations + * @{ + */ + +static NL_LIST_HEAD(table_names); + +static int add_routing_table_name(long id, const char *name) +{ + return __trans_list_add(id, name, &table_names); +} + +static void __init init_routing_table_names(void) +{ + add_routing_table_name(RT_TABLE_UNSPEC, "unspec"); + add_routing_table_name(RT_TABLE_DEFAULT, "default"); + add_routing_table_name(RT_TABLE_MAIN, "main"); + add_routing_table_name(RT_TABLE_LOCAL, "local"); +}; + +int rtnl_route_read_table_names(const char *path) +{ + __trans_list_clear(&table_names); + + return __nl_read_num_str_file(path, &add_routing_table_name); +} + +char *rtnl_route_table2str(int table, char *buf, size_t size) +{ + return __list_type2str(table, buf, size, &table_names); +} + +int rtnl_route_str2table(const char *name) +{ + return __list_str2type(name, &table_names); +} + + +/** @} */ + +/** + * @name Routing Protocol Translations + * @{ + */ + +static NL_LIST_HEAD(proto_names); + +static int add_proto_name(long id, const char *name) +{ + return __trans_list_add(id, name, &proto_names); +} + +static void __init init_proto_names(void) +{ + add_proto_name(RTPROT_UNSPEC, "unspec"); + add_proto_name(RTPROT_REDIRECT, "redirect"); + add_proto_name(RTPROT_KERNEL, "kernel"); + add_proto_name(RTPROT_BOOT, "boot"); + add_proto_name(RTPROT_STATIC, "static"); +}; + +int rtnl_route_read_protocol_names(const char *path) +{ + __trans_list_clear(&proto_names); + + return __nl_read_num_str_file(path, &add_proto_name); +} + +char *rtnl_route_proto2str(int proto, char *buf, size_t size) +{ + return __list_type2str(proto, buf, size, &proto_names); +} + +int rtnl_route_str2proto(const char *name) +{ + return __list_str2type(name, &proto_names); +} + +/** @} */ + +/** + * @name Routing Metrices Translations + * @{ + */ + +static struct trans_tbl route_metrices[] = { + __ADD(RTAX_UNSPEC, unspec) + __ADD(RTAX_LOCK, lock) + __ADD(RTAX_MTU, mtu) + __ADD(RTAX_WINDOW, window) + __ADD(RTAX_RTT, rtt) + __ADD(RTAX_RTTVAR, rttvar) + __ADD(RTAX_SSTHRESH, ssthresh) + __ADD(RTAX_CWND, cwnd) + __ADD(RTAX_ADVMSS, advmss) + __ADD(RTAX_REORDERING, reordering) + __ADD(RTAX_HOPLIMIT, hoplimit) + __ADD(RTAX_INITCWND, initcwnd) + __ADD(RTAX_FEATURES, features) +}; + +char *rtnl_route_metric2str(int metric, char *buf, size_t size) +{ + return __type2str(metric, buf, size, route_metrices, + ARRAY_SIZE(route_metrices)); +} + +int rtnl_route_str2metric(const char *name) +{ + return __str2type(name, route_metrices, ARRAY_SIZE(route_metrices)); +} + +/** @} */ + +/** + * @name Nexthop Flags Translations + * @{ + */ + +static struct trans_tbl nh_flags[] = { + __ADD(RTNH_F_DEAD, dead) + __ADD(RTNH_F_PERVASIVE, pervasive) + __ADD(RTNH_F_ONLINK, onlink) +}; + +char * rtnl_route_nh_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, nh_flags, ARRAY_SIZE(nh_flags)); +} + +int rtnl_route_nh_str2flags(const char *name) +{ + return __str2flags(name, nh_flags, ARRAY_SIZE(nh_flags)); +} + +/** @} */ + +/** @} */ diff --git a/lib/route/rtnl.c b/lib/route/rtnl.c new file mode 100644 index 0000000..81ddf94 --- /dev/null +++ b/lib/route/rtnl.c @@ -0,0 +1,125 @@ +/* + * lib/route/rtnl.c Routing Netlink + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup nlfam + * @defgroup rtnl Routing Netlink + * @{ + */ + +#include +#include +#include +#include + +/** + * @name Sending + * @{ + */ + +/** + * Send routing netlink request message + * @arg handle Netlink handle. + * @arg type Netlink message type. + * @arg family Address family. + * @arg flags Additional netlink message flags. + * + * Fills out a routing netlink request message and sends it out + * using nl_send_simple(). + * + * @return 0 on success or a negative error code. + */ +int nl_rtgen_request(struct nl_handle *handle, int type, int family, int flags) +{ + struct rtgenmsg gmsg = { + .rtgen_family = family, + }; + + return nl_send_simple(handle, type, flags, &gmsg, sizeof(gmsg)); +} + +/** @} */ + +/** + * @name Routing Type Translations + * @{ + */ + +static struct trans_tbl rtntypes[] = { + __ADD(RTN_UNSPEC,unspec) + __ADD(RTN_UNICAST,unicast) + __ADD(RTN_LOCAL,local) + __ADD(RTN_BROADCAST,broadcast) + __ADD(RTN_ANYCAST,anycast) + __ADD(RTN_MULTICAST,multicast) + __ADD(RTN_BLACKHOLE,blackhole) + __ADD(RTN_UNREACHABLE,unreachable) + __ADD(RTN_PROHIBIT,prohibit) + __ADD(RTN_THROW,throw) + __ADD(RTN_NAT,nat) + __ADD(RTN_XRESOLVE,xresolve) +}; + +char *nl_rtntype2str(int type, char *buf, size_t size) +{ + return __type2str(type, buf, size, rtntypes, ARRAY_SIZE(rtntypes)); +} + +int nl_str2rtntype(const char *name) +{ + return __str2type(name, rtntypes, ARRAY_SIZE(rtntypes)); +} + +/** @} */ + +/** + * @name Scope Translations + * @{ + */ + +static struct trans_tbl scopes[] = { + __ADD(255,nowhere) + __ADD(254,host) + __ADD(253,link) + __ADD(200,site) + __ADD(0,universe) +}; + +char *rtnl_scope2str(int scope, char *buf, size_t size) +{ + return __type2str(scope, buf, size, scopes, ARRAY_SIZE(scopes)); +} + +int rtnl_str2scope(const char *name) +{ + return __str2type(name, scopes, ARRAY_SIZE(scopes)); +} + +/** @} */ + +/** + * @name Realms Translations + * @{ + */ + +char * rtnl_realms2str(uint32_t realms, char *buf, size_t len) +{ + int from = RTNL_REALM_FROM(realms); + int to = RTNL_REALM_TO(realms); + + snprintf(buf, len, "%d/%d", from, to); + + return buf; +} + +/** @} */ + +/** @} */ diff --git a/lib/route/rule.c b/lib/route/rule.c new file mode 100644 index 0000000..76e0060 --- /dev/null +++ b/lib/route/rule.c @@ -0,0 +1,900 @@ +/* + * lib/route/rule.c Routing Rules + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup rule Routing Rules + * @brief + * @{ + */ + +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define RULE_ATTR_FAMILY 0x0001 +#define RULE_ATTR_PRIO 0x0002 +#define RULE_ATTR_MARK 0x0004 +#define RULE_ATTR_IIF 0x0008 +#define RULE_ATTR_REALMS 0x0010 +#define RULE_ATTR_SRC 0x0020 +#define RULE_ATTR_DST 0x0040 +#define RULE_ATTR_DSFIELD 0x0080 +#define RULE_ATTR_TABLE 0x0100 +#define RULE_ATTR_TYPE 0x0200 +#define RULE_ATTR_SRC_LEN 0x0400 +#define RULE_ATTR_DST_LEN 0x0800 +#define RULE_ATTR_SRCMAP 0x1000 + +static struct nl_cache_ops rtnl_rule_ops; +static struct nl_object_ops rule_obj_ops; +/** @endcond */ + +static void rule_free_data(struct nl_object *c) +{ + struct rtnl_rule *rule = nl_object_priv(c); + + if (!rule) + return; + + nl_addr_put(rule->r_src); + nl_addr_put(rule->r_dst); +} + +static int rule_clone(struct nl_object *_dst, struct nl_object *_src) +{ + struct rtnl_rule *dst = nl_object_priv(_dst); + struct rtnl_rule *src = nl_object_priv(_src); + + if (src->r_src) + if (!(dst->r_src = nl_addr_clone(src->r_src))) + goto errout; + + if (src->r_dst) + if (!(dst->r_dst = nl_addr_clone(src->r_dst))) + goto errout; + + return 0; +errout: + return nl_get_errno(); +} + +static struct nla_policy rule_policy[RTA_MAX+1] = { + [RTA_PRIORITY] = { .type = NLA_U32 }, + [RTA_FLOW] = { .type = NLA_U32 }, + [RTA_PROTOINFO] = { .type = NLA_U32 }, + [RTA_IIF] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ, }, +}; + +static int rule_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *n, void *arg) +{ + struct rtnl_rule *rule; + struct rtmsg *r; + struct nlattr *tb[RTA_MAX+1]; + struct nl_parser_param *pp = arg; + int err = 1; + + rule = rtnl_rule_alloc(); + if (!rule) { + err = nl_errno(ENOMEM); + goto errout; + } + + rule->ce_msgtype = n->nlmsg_type; + r = nlmsg_data(n); + + err = nlmsg_parse(n, sizeof(*r), tb, RTA_MAX, rule_policy); + if (err < 0) + goto errout; + + rule->r_family = r->rtm_family; + rule->r_type = r->rtm_type; + rule->r_dsfield = r->rtm_tos; + rule->r_src_len = r->rtm_src_len; + rule->r_dst_len = r->rtm_dst_len; + rule->r_table = r->rtm_table; + rule->ce_mask = (RULE_ATTR_FAMILY | RULE_ATTR_TYPE | RULE_ATTR_DSFIELD | + RULE_ATTR_SRC_LEN | RULE_ATTR_DST_LEN |RULE_ATTR_TYPE); + + if (tb[RTA_PRIORITY]) { + rule->r_prio = nla_get_u32(tb[RTA_PRIORITY]); + rule->ce_mask |= RULE_ATTR_PRIO; + } + + if (tb[RTA_SRC]) { + rule->r_src = nla_get_addr(tb[RTA_SRC], r->rtm_family); + if (!rule->r_src) { + err = nl_errno(ENOMEM); + goto errout; + } + nl_addr_set_prefixlen(rule->r_src, r->rtm_src_len); + rule->ce_mask |= RULE_ATTR_SRC; + } + + if (tb[RTA_DST]) { + rule->r_dst = nla_get_addr(tb[RTA_DST], r->rtm_family); + if (!rule->r_dst) { + err = nl_errno(ENOMEM); + goto errout; + } + nl_addr_set_prefixlen(rule->r_dst, r->rtm_dst_len); + rule->ce_mask |= RULE_ATTR_DST; + } + + if (tb[RTA_PROTOINFO]) { + rule->r_mark = nla_get_u32(tb[RTA_PROTOINFO]); + rule->ce_mask |= RULE_ATTR_MARK; + } + + if (tb[RTA_IIF]) { + nla_strlcpy(rule->r_iif, tb[RTA_IIF], IFNAMSIZ); + rule->ce_mask |= RULE_ATTR_IIF; + } + + if (tb[RTA_FLOW]) { + rule->r_realms = nla_get_u32(tb[RTA_FLOW]); + rule->ce_mask |= RULE_ATTR_REALMS; + } + + if (tb[RTA_GATEWAY]) { + rule->r_srcmap = nla_get_addr(tb[RTA_GATEWAY], r->rtm_family); + if (!rule->r_srcmap) { + err = nl_errno(ENOMEM); + goto errout; + } + rule->ce_mask |= RULE_ATTR_SRCMAP; + } + + err = pp->pp_cb((struct nl_object *) rule, pp); + if (err < 0) + goto errout; + + return P_ACCEPT; + +errout: + rtnl_rule_put(rule); + return err; +} + +static int rule_request_update(struct nl_cache *c, struct nl_handle *h) +{ + return nl_rtgen_request(h, RTM_GETRULE, AF_UNSPEC, NLM_F_DUMP); +} + +static int rule_dump_brief(struct nl_object *o, struct nl_dump_params *p) +{ + struct rtnl_rule *r = (struct rtnl_rule *) o; + char buf[128]; + + if (r->ce_mask & RULE_ATTR_PRIO) + dp_dump(p, "%d:\t", r->r_prio); + else + dp_dump(p, "0:\t"); + + if (r->ce_mask & RULE_ATTR_SRC) + dp_dump(p, "from %s ", + nl_addr2str(r->r_src, buf, sizeof(buf))); + else if (r->ce_mask & RULE_ATTR_SRC_LEN && r->r_src_len) + dp_dump(p, "from 0/%d ", r->r_src_len); + + if (r->ce_mask & RULE_ATTR_DST) + dp_dump(p, "to %s ", + nl_addr2str(r->r_dst, buf, sizeof(buf))); + else if (r->ce_mask & RULE_ATTR_DST_LEN && r->r_dst_len) + dp_dump(p, "to 0/%d ", r->r_dst_len); + + if (r->ce_mask & RULE_ATTR_DSFIELD && r->r_dsfield) + dp_dump(p, "tos %d ", r->r_dsfield); + + if (r->ce_mask & RULE_ATTR_MARK) + dp_dump(p, "mark %" PRIx64 , r->r_mark); + + if (r->ce_mask & RULE_ATTR_IIF) + dp_dump(p, "iif %s ", r->r_iif); + + if (r->ce_mask & RULE_ATTR_TABLE) + dp_dump(p, "lookup %s ", + rtnl_route_table2str(r->r_table, buf, sizeof(buf))); + + if (r->ce_mask & RULE_ATTR_REALMS) + dp_dump(p, "realms %s ", + rtnl_realms2str(r->r_realms, buf, sizeof(buf))); + + dp_dump(p, "action %s\n", + nl_rtntype2str(r->r_type, buf, sizeof(buf))); + + return 1; +} + +static int rule_dump_full(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_rule *rule = (struct rtnl_rule *) obj; + char buf[128]; + int line; + + line = rule_dump_brief(obj, p); + + dp_dump_line(p, line++, " family %s", + nl_af2str(rule->r_family, buf, sizeof(buf))); + + if (rule->ce_mask & RULE_ATTR_SRCMAP) + dp_dump(p, " srcmap %s", + nl_addr2str(rule->r_srcmap, buf, sizeof(buf))); + + dp_dump(p, "\n"); + + return line; +} + +static int rule_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + return rule_dump_full(obj, p); +} + +static int rule_dump_xml(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_rule *rule = (struct rtnl_rule *) obj; + char buf[128]; + int line = 0; + + dp_dump_line(p, line++, "\n"); + + dp_dump_line(p, line++, " %u\n", + rule->r_prio); + dp_dump_line(p, line++, " %s\n", + nl_af2str(rule->r_family, buf, sizeof(buf))); + + if (rule->ce_mask & RULE_ATTR_DST) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(rule->r_dst, buf, sizeof(buf))); + + if (rule->ce_mask & RULE_ATTR_DST_LEN) + dp_dump_line(p, line++, " %u\n", + rule->r_dst_len); + + if (rule->ce_mask & RULE_ATTR_SRC) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(rule->r_src, buf, sizeof(buf))); + + if (rule->ce_mask & RULE_ATTR_SRC_LEN) + dp_dump_line(p, line++, " %u\n", + rule->r_src_len); + + if (rule->ce_mask & RULE_ATTR_IIF) + dp_dump_line(p, line++, " %s\n", rule->r_iif); + + if (rule->ce_mask & RULE_ATTR_TABLE) + dp_dump_line(p, line++, " %u
\n", + rule->r_table); + + if (rule->ce_mask & RULE_ATTR_REALMS) + dp_dump_line(p, line++, " %u\n", + rule->r_realms); + + if (rule->ce_mask & RULE_ATTR_MARK) + dp_dump_line(p, line++, " %" PRIx64 "\n", + rule->r_mark); + + if (rule->ce_mask & RULE_ATTR_DSFIELD) + dp_dump_line(p, line++, " %u\n", + rule->r_dsfield); + + if (rule->ce_mask & RULE_ATTR_TYPE) + dp_dump_line(p, line++, "%s\n", + nl_rtntype2str(rule->r_type, buf, sizeof(buf))); + + if (rule->ce_mask & RULE_ATTR_SRCMAP) + dp_dump_line(p, line++, "%s\n", + nl_addr2str(rule->r_srcmap, buf, sizeof(buf))); + + dp_dump_line(p, line++, "
\n"); + + return line; +} + +static int rule_dump_env(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_rule *rule = (struct rtnl_rule *) obj; + char buf[128]; + int line = 0; + + dp_dump_line(p, line++, "RULE_PRIORITY=%u\n", + rule->r_prio); + dp_dump_line(p, line++, "RULE_FAMILY=%s\n", + nl_af2str(rule->r_family, buf, sizeof(buf))); + + if (rule->ce_mask & RULE_ATTR_DST) + dp_dump_line(p, line++, "RULE_DST=%s\n", + nl_addr2str(rule->r_dst, buf, sizeof(buf))); + + if (rule->ce_mask & RULE_ATTR_DST_LEN) + dp_dump_line(p, line++, "RULE_DSTLEN=%u\n", + rule->r_dst_len); + + if (rule->ce_mask & RULE_ATTR_SRC) + dp_dump_line(p, line++, "RULE_SRC=%s\n", + nl_addr2str(rule->r_src, buf, sizeof(buf))); + + if (rule->ce_mask & RULE_ATTR_SRC_LEN) + dp_dump_line(p, line++, "RULE_SRCLEN=%u\n", + rule->r_src_len); + + if (rule->ce_mask & RULE_ATTR_IIF) + dp_dump_line(p, line++, "RULE_IIF=%s\n", rule->r_iif); + + if (rule->ce_mask & RULE_ATTR_TABLE) + dp_dump_line(p, line++, "RULE_TABLE=%u\n", + rule->r_table); + + if (rule->ce_mask & RULE_ATTR_REALMS) + dp_dump_line(p, line++, "RULE_REALM=%u\n", + rule->r_realms); + + if (rule->ce_mask & RULE_ATTR_MARK) + dp_dump_line(p, line++, "RULE_MARK=0x%" PRIx64 "\n", + rule->r_mark); + + if (rule->ce_mask & RULE_ATTR_DSFIELD) + dp_dump_line(p, line++, "RULE_DSFIELD=%u\n", + rule->r_dsfield); + + if (rule->ce_mask & RULE_ATTR_TYPE) + dp_dump_line(p, line++, "RULE_TYPE=%s\n", + nl_rtntype2str(rule->r_type, buf, sizeof(buf))); + + if (rule->ce_mask & RULE_ATTR_SRCMAP) + dp_dump_line(p, line++, "RULE_SRCMAP=%s\n", + nl_addr2str(rule->r_srcmap, buf, sizeof(buf))); + + return line; +} + +static int rule_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) +{ + struct rtnl_rule *a = (struct rtnl_rule *) _a; + struct rtnl_rule *b = (struct rtnl_rule *) _b; + int diff = 0; + +#define RULE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, RULE_ATTR_##ATTR, a, b, EXPR) + + diff |= RULE_DIFF(FAMILY, a->r_family != b->r_family); + diff |= RULE_DIFF(TABLE, a->r_table != b->r_table); + diff |= RULE_DIFF(REALMS, a->r_realms != b->r_realms); + diff |= RULE_DIFF(DSFIELD, a->r_dsfield != b->r_dsfield); + diff |= RULE_DIFF(TYPE, a->r_type != b->r_type); + diff |= RULE_DIFF(PRIO, a->r_prio != b->r_prio); + diff |= RULE_DIFF(MARK, a->r_mark != b->r_mark); + diff |= RULE_DIFF(SRC_LEN, a->r_src_len != b->r_src_len); + diff |= RULE_DIFF(DST_LEN, a->r_dst_len != b->r_dst_len); + diff |= RULE_DIFF(SRC, nl_addr_cmp(a->r_src, b->r_src)); + diff |= RULE_DIFF(DST, nl_addr_cmp(a->r_dst, b->r_dst)); + diff |= RULE_DIFF(IIF, strcmp(a->r_iif, b->r_iif)); + +#undef RULE_DIFF + + return diff; +} + +static struct trans_tbl rule_attrs[] = { + __ADD(RULE_ATTR_FAMILY, family) + __ADD(RULE_ATTR_PRIO, prio) + __ADD(RULE_ATTR_MARK, mark) + __ADD(RULE_ATTR_IIF, iif) + __ADD(RULE_ATTR_REALMS, realms) + __ADD(RULE_ATTR_SRC, src) + __ADD(RULE_ATTR_DST, dst) + __ADD(RULE_ATTR_DSFIELD, dsfield) + __ADD(RULE_ATTR_TABLE, table) + __ADD(RULE_ATTR_TYPE, type) + __ADD(RULE_ATTR_SRC_LEN, src_len) + __ADD(RULE_ATTR_DST_LEN, dst_len) + __ADD(RULE_ATTR_SRCMAP, srcmap) +}; + +static char *rule_attrs2str(int attrs, char *buf, size_t len) +{ + return __flags2str(attrs, buf, len, rule_attrs, + ARRAY_SIZE(rule_attrs)); +} + +/** + * @name Allocation/Freeing + * @{ + */ + +struct rtnl_rule *rtnl_rule_alloc(void) +{ + return (struct rtnl_rule *) nl_object_alloc(&rule_obj_ops); +} + +void rtnl_rule_put(struct rtnl_rule *rule) +{ + nl_object_put((struct nl_object *) rule); +} + +/** @} */ + +/** + * @name Cache Management + * @{ + */ + +/** + * Build a rule cache including all rules of the specified family currently configured in the kernel. + * @arg handle netlink handle + * @arg family address family + * + * Allocates a new rule cache, initializes it properly and updates it + * to include all rules of the specified address family currently + * configured in the kernel. + * + * @note The caller is responsible for destroying and freeing the + * cache after using it. (nl_cache_destroy_and_free()) + * @return The new cache or NULL if an error occured. + */ +struct nl_cache * rtnl_rule_alloc_cache_by_family(struct nl_handle *handle, + int family) +{ + struct nl_cache * cache; + + cache = nl_cache_alloc(&rtnl_rule_ops); + if (cache == NULL) + return NULL; + + /* XXX RULE_CACHE_FAMILY(cache) = family; */ + + if (handle && nl_cache_refill(handle, cache) < 0) { + free(cache); + return NULL; + } + + return cache; +} + +/** + * Build a rule cache including all rules currently configured in the kernel. + * @arg handle netlink handle + * + * Allocates a new rule cache, initializes it properly and updates it + * to include all rules currently configured in the kernel. + * + * @note The caller is responsible for destroying and freeing the + * cache after using it. (nl_cache_destroy_and_free()) + * @return The new cache or NULL if an error occured. + */ +struct nl_cache * rtnl_rule_alloc_cache(struct nl_handle *handle) +{ + return rtnl_rule_alloc_cache_by_family(handle, AF_UNSPEC); +} + +/** @} */ + +/** + * @name Rule Addition + * @{ + */ + +static struct nl_msg *build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags) +{ + struct nl_msg *msg; + struct rtmsg rtm = { + .rtm_type = RTN_UNSPEC + }; + + if (cmd == RTM_NEWRULE) + rtm.rtm_type = RTN_UNICAST; + + if (tmpl->ce_mask & RULE_ATTR_FAMILY) + rtm.rtm_family = tmpl->r_family; + + if (tmpl->ce_mask & RULE_ATTR_TABLE) + rtm.rtm_table = tmpl->r_table; + + if (tmpl->ce_mask & RULE_ATTR_DSFIELD) + rtm.rtm_tos = tmpl->r_dsfield; + + if (tmpl->ce_mask & RULE_ATTR_TYPE) + rtm.rtm_type = tmpl->r_type; + + if (tmpl->ce_mask & RULE_ATTR_SRC_LEN) + rtm.rtm_src_len = tmpl->r_src_len; + + if (tmpl->ce_mask & RULE_ATTR_DST_LEN) + rtm.rtm_dst_len = tmpl->r_dst_len; + + msg = nlmsg_alloc_simple(cmd, flags); + if (!msg) + goto nla_put_failure; + + if (nlmsg_append(msg, &rtm, sizeof(rtm), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (tmpl->ce_mask & RULE_ATTR_SRC) + NLA_PUT_ADDR(msg, RTA_SRC, tmpl->r_src); + + if (tmpl->ce_mask & RULE_ATTR_DST) + NLA_PUT_ADDR(msg, RTA_DST, tmpl->r_dst); + + if (tmpl->ce_mask & RULE_ATTR_PRIO) + NLA_PUT_U32(msg, RTA_PRIORITY, tmpl->r_prio); + + if (tmpl->ce_mask & RULE_ATTR_MARK) + NLA_PUT_U32(msg, RTA_PROTOINFO, tmpl->r_mark); + + if (tmpl->ce_mask & RULE_ATTR_REALMS) + NLA_PUT_U32(msg, RTA_FLOW, tmpl->r_realms); + + if (tmpl->ce_mask & RULE_ATTR_IIF) + NLA_PUT_STRING(msg, RTA_IIF, tmpl->r_iif); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +/** + * Build netlink request message to add a new rule + * @arg tmpl template with data of new rule + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting a addition of a new + * rule. The netlink message header isn't fully equipped with + * all relevant fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. \a tmpl must contain the attributes of the new + * address set via \c rtnl_rule_set_* functions. + * + * @return The netlink message + */ +struct nl_msg *rtnl_rule_build_add_request(struct rtnl_rule *tmpl, int flags) +{ + return build_rule_msg(tmpl, RTM_NEWRULE, NLM_F_CREATE | flags); +} + +/** + * Add a new rule + * @arg handle netlink handle + * @arg tmpl template with requested changes + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_rule_build_add_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_rule_add(struct nl_handle *handle, struct rtnl_rule *tmpl, int flags) +{ + int err; + struct nl_msg *msg; + + msg = rtnl_rule_build_add_request(tmpl, flags); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name Rule Deletion + * @{ + */ + +/** + * Build a netlink request message to delete a rule + * @arg rule rule to delete + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting a deletion of a rule. + * The netlink message header isn't fully equipped with all relevant + * fields and must thus be sent out via nl_send_auto_complete() + * or supplemented as needed. \a rule must point to an existing + * address. + * + * @return The netlink message + */ +struct nl_msg *rtnl_rule_build_delete_request(struct rtnl_rule *rule, int flags) +{ + return build_rule_msg(rule, RTM_DELRULE, flags); +} + +/** + * Delete a rule + * @arg handle netlink handle + * @arg rule rule to delete + * @arg flags additional netlink message flags + * + * Builds a netlink message by calling rtnl_rule_build_delete_request(), + * sends the request to the kernel and waits for the next ACK to be + * received and thus blocks until the request has been fullfilled. + * + * @return 0 on sucess or a negative error if an error occured. + */ +int rtnl_rule_delete(struct nl_handle *handle, struct rtnl_rule *rule, + int flags) +{ + int err; + struct nl_msg *msg; + + msg = rtnl_rule_build_delete_request(rule, flags); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name Attribute Modification + * @{ + */ + +void rtnl_rule_set_family(struct rtnl_rule *rule, int family) +{ + rule->r_family = family; + rule->ce_mask |= RULE_ATTR_FAMILY; +} + +int rtnl_rule_get_family(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_FAMILY) + return rule->r_family; + else + return AF_UNSPEC; +} + +void rtnl_rule_set_prio(struct rtnl_rule *rule, int prio) +{ + rule->r_prio = prio; + rule->ce_mask |= RULE_ATTR_PRIO; +} + +int rtnl_rule_get_prio(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_PRIO) + return rule->r_prio; + else + return -1; +} + +void rtnl_rule_set_mark(struct rtnl_rule *rule, uint64_t mark) +{ + rule->r_mark = mark; + rule->ce_mask |= RULE_ATTR_MARK; +} + +uint64_t rtnl_rule_get_mark(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_MARK) + return rule->r_mark; + else + return UINT_LEAST64_MAX; +} + +void rtnl_rule_set_table(struct rtnl_rule *rule, int table) +{ + rule->r_table = table; + rule->ce_mask |= RULE_ATTR_TABLE; +} + +int rtnl_rule_get_table(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_TABLE) + return rule->r_table; + else + return -1; +} + +void rtnl_rule_set_dsfield(struct rtnl_rule *rule, int dsfield) +{ + rule->r_dsfield = dsfield; + rule->ce_mask |= RULE_ATTR_DSFIELD; +} + +int rtnl_rule_get_dsfield(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_DSFIELD) + return rule->r_dsfield; + else + return -1; +} + +void rtnl_rule_set_src_len(struct rtnl_rule *rule, int len) +{ + rule->r_src_len = len; + if (rule->ce_mask & RULE_ATTR_SRC) + nl_addr_set_prefixlen(rule->r_src, len); + rule->ce_mask |= RULE_ATTR_SRC_LEN; +} + +int rtnl_rule_get_src_len(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_SRC_LEN) + return rule->r_src_len; + else + return -1; +} + +void rtnl_rule_set_dst_len(struct rtnl_rule *rule, int len) +{ + rule->r_dst_len = len; + if (rule->ce_mask & RULE_ATTR_DST) + nl_addr_set_prefixlen(rule->r_dst, len); + rule->ce_mask |= RULE_ATTR_DST_LEN; +} + +int rtnl_rule_get_dst_len(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_DST_LEN) + return rule->r_dst_len; + else + return -1; +} + +static inline int __assign_addr(struct rtnl_rule *rule, struct nl_addr **pos, + struct nl_addr *new, uint8_t *len, int flag) +{ + if (rule->ce_mask & RULE_ATTR_FAMILY) { + if (new->a_family != rule->r_family) + return nl_error(EINVAL, "Address family mismatch"); + } else + rule->r_family = new->a_family; + + if (*pos) + nl_addr_put(*pos); + + nl_addr_get(new); + *pos = new; + *len = nl_addr_get_prefixlen(new); + + rule->ce_mask |= (flag | RULE_ATTR_FAMILY); + + return 0; +} + +int rtnl_rule_set_src(struct rtnl_rule *rule, struct nl_addr *src) +{ + return __assign_addr(rule, &rule->r_src, src, &rule->r_src_len, + RULE_ATTR_SRC | RULE_ATTR_SRC_LEN); +} + +struct nl_addr *rtnl_rule_get_src(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_SRC) + return rule->r_src; + else + return NULL; +} + +int rtnl_rule_set_dst(struct rtnl_rule *rule, struct nl_addr *dst) +{ + return __assign_addr(rule, &rule->r_dst, dst, &rule->r_dst_len, + RULE_ATTR_DST | RULE_ATTR_DST_LEN); +} + +struct nl_addr *rtnl_rule_get_dst(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_DST) + return rule->r_dst; + else + return NULL; +} + +int rtnl_rule_set_iif(struct rtnl_rule *rule, const char *dev) +{ + if (strlen(dev) > IFNAMSIZ-1) + return nl_errno(ERANGE); + + strcpy(rule->r_iif, dev); + rule->ce_mask |= RULE_ATTR_IIF; + return 0; +} + +char *rtnl_rule_get_iif(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_IIF) + return rule->r_iif; + else + return NULL; +} + +void rtnl_rule_set_action(struct rtnl_rule *rule, int type) +{ + rule->r_type = type; + rule->ce_mask |= RULE_ATTR_TYPE; +} + +int rtnl_rule_get_action(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_TYPE) + return rule->r_type; + else + return nl_errno(ENOENT); +} + +void rtnl_rule_set_realms(struct rtnl_rule *rule, realm_t realms) +{ + rule->r_realms = realms; + rule->ce_mask |= RULE_ATTR_REALMS; +} + +realm_t rtnl_rule_get_realms(struct rtnl_rule *rule) +{ + if (rule->ce_mask & RULE_ATTR_REALMS) + return rule->r_realms; + else + return 0; +} + +/** @} */ + +static struct nl_object_ops rule_obj_ops = { + .oo_name = "route/rule", + .oo_size = sizeof(struct rtnl_rule), + .oo_free_data = rule_free_data, + .oo_clone = rule_clone, + .oo_dump[NL_DUMP_BRIEF] = rule_dump_brief, + .oo_dump[NL_DUMP_FULL] = rule_dump_full, + .oo_dump[NL_DUMP_STATS] = rule_dump_stats, + .oo_dump[NL_DUMP_XML] = rule_dump_xml, + .oo_dump[NL_DUMP_ENV] = rule_dump_env, + .oo_compare = rule_compare, + .oo_attrs2str = rule_attrs2str, + .oo_id_attrs = ~0, +}; + +static struct nl_cache_ops rtnl_rule_ops = { + .co_name = "route/rule", + .co_hdrsize = sizeof(struct rtmsg), + .co_msgtypes = { + { RTM_NEWRULE, NL_ACT_NEW, "new" }, + { RTM_DELRULE, NL_ACT_DEL, "del" }, + { RTM_GETRULE, NL_ACT_GET, "get" }, + END_OF_MSGTYPES_LIST, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = rule_request_update, + .co_msg_parser = rule_msg_parser, + .co_obj_ops = &rule_obj_ops, +}; + +static void __init rule_init(void) +{ + nl_cache_mngt_register(&rtnl_rule_ops); +} + +static void __exit rule_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_rule_ops); +} + +/** @} */ diff --git a/lib/route/sch/blackhole.c b/lib/route/sch/blackhole.c new file mode 100644 index 0000000..a30b693 --- /dev/null +++ b/lib/route/sch/blackhole.c @@ -0,0 +1,38 @@ +/* + * lib/route/sch/blackhole.c Blackhole Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup qdisc_api + * @defgroup blackhole Blackhole + * @{ + */ + +#include +#include +#include +#include +#include + +static struct rtnl_qdisc_ops blackhole_ops = { + .qo_kind = "blackhole", +}; + +static void __init blackhole_init(void) +{ + rtnl_qdisc_register(&blackhole_ops); +} + +static void __exit blackhole_exit(void) +{ + rtnl_qdisc_unregister(&blackhole_ops); +} + +/** @} */ diff --git a/lib/route/sch/cbq.c b/lib/route/sch/cbq.c new file mode 100644 index 0000000..9808509 --- /dev/null +++ b/lib/route/sch/cbq.c @@ -0,0 +1,306 @@ +/* + * lib/route/sch/cbq.c Class Based Queueing + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @ingroup qdisc_api + * @ingroup class_api + * @defgroup cbq Class Based Queueing (CBQ) + * @{ + */ + +static struct trans_tbl ovl_strategies[] = { + __ADD(TC_CBQ_OVL_CLASSIC,classic) + __ADD(TC_CBQ_OVL_DELAY,delay) + __ADD(TC_CBQ_OVL_LOWPRIO,lowprio) + __ADD(TC_CBQ_OVL_DROP,drop) + __ADD(TC_CBQ_OVL_RCLASSIC,rclassic) +}; + +/** + * Convert a CBQ OVL strategy to a character string + * @arg type CBQ OVL strategy + * @arg buf destination buffer + * @arg len length of destination buffer + * + * Converts a CBQ OVL strategy to a character string and stores in the + * provided buffer. Returns the destination buffer or the type + * encoded in hex if no match was found. + */ +char *nl_ovl_strategy2str(int type, char *buf, size_t len) +{ + return __type2str(type, buf, len, ovl_strategies, + ARRAY_SIZE(ovl_strategies)); +} + +/** + * Convert a string to a CBQ OVL strategy + * @arg name CBQ OVL stragegy name + * + * Converts a CBQ OVL stragegy name to it's corresponding CBQ OVL strategy + * type. Returns the type or -1 if none was found. + */ +int nl_str2ovl_strategy(const char *name) +{ + return __str2type(name, ovl_strategies, ARRAY_SIZE(ovl_strategies)); +} + +static struct nla_policy cbq_policy[TCA_CBQ_MAX+1] = { + [TCA_CBQ_LSSOPT] = { .minlen = sizeof(struct tc_cbq_lssopt) }, + [TCA_CBQ_RATE] = { .minlen = sizeof(struct tc_ratespec) }, + [TCA_CBQ_WRROPT] = { .minlen = sizeof(struct tc_cbq_wrropt) }, + [TCA_CBQ_OVL_STRATEGY] = { .minlen = sizeof(struct tc_cbq_ovl) }, + [TCA_CBQ_FOPT] = { .minlen = sizeof(struct tc_cbq_fopt) }, + [TCA_CBQ_POLICE] = { .minlen = sizeof(struct tc_cbq_police) }, +}; + +static inline struct rtnl_cbq *cbq_qdisc(struct rtnl_tca *tca) +{ + return (struct rtnl_cbq *) tca->tc_subdata; +} + +static inline struct rtnl_cbq *cbq_alloc(struct rtnl_tca *tca) +{ + if (!tca->tc_subdata) + tca->tc_subdata = calloc(1, sizeof(struct rtnl_qdisc)); + + return cbq_qdisc(tca); +} + + +static int cbq_msg_parser(struct rtnl_tca *tca) +{ + struct nlattr *tb[TCA_CBQ_MAX + 1]; + struct rtnl_cbq *cbq; + int err; + + err = tca_parse(tb, TCA_CBQ_MAX, tca, cbq_policy); + if (err < 0) + return err; + + cbq = cbq_alloc(tca); + if (!cbq) + return nl_errno(ENOMEM); + + nla_memcpy(&cbq->cbq_lss, tb[TCA_CBQ_LSSOPT], sizeof(cbq->cbq_lss)); + nla_memcpy(&cbq->cbq_rate, tb[TCA_CBQ_RATE], sizeof(cbq->cbq_rate)); + nla_memcpy(&cbq->cbq_wrr, tb[TCA_CBQ_WRROPT], sizeof(cbq->cbq_wrr)); + nla_memcpy(&cbq->cbq_fopt, tb[TCA_CBQ_FOPT], sizeof(cbq->cbq_fopt)); + nla_memcpy(&cbq->cbq_ovl, tb[TCA_CBQ_OVL_STRATEGY], + sizeof(cbq->cbq_ovl)); + nla_memcpy(&cbq->cbq_police, tb[TCA_CBQ_POLICE], + sizeof(cbq->cbq_police)); + + return 0; +} + +static int cbq_qdisc_msg_parser(struct rtnl_qdisc *qdisc) +{ + return cbq_msg_parser((struct rtnl_tca *) qdisc); +} + +static int cbq_class_msg_parser(struct rtnl_class *class) +{ + return cbq_msg_parser((struct rtnl_tca *) class); +} + +static void cbq_qdisc_free_data(struct rtnl_qdisc *qdisc) +{ + free(qdisc->q_subdata); +} + +static int cbq_clone(struct rtnl_tca *_dst, struct rtnl_tca *_src) +{ + struct rtnl_cbq *src = cbq_qdisc(_src); + + if (src && !cbq_alloc(_dst)) + return nl_errno(ENOMEM); + else + return 0; +} + +static int cbq_qdisc_clone(struct rtnl_qdisc *dst, struct rtnl_qdisc *src) +{ + return cbq_clone((struct rtnl_tca *) dst, (struct rtnl_tca *) src); +} + +static void cbq_class_free_data(struct rtnl_class *class) +{ + free(class->c_subdata); +} + +static int cbq_class_clone(struct rtnl_class *dst, struct rtnl_class *src) +{ + return cbq_clone((struct rtnl_tca *) dst, (struct rtnl_tca *) src); +} + +static int cbq_dump_brief(struct rtnl_tca *tca, struct nl_dump_params *p, + int line) +{ + struct rtnl_cbq *cbq; + double r, rbit; + char *ru, *rubit; + + cbq = cbq_qdisc(tca); + if (!cbq) + goto ignore; + + r = nl_cancel_down_bytes(cbq->cbq_rate.rate, &ru); + rbit = nl_cancel_down_bits(cbq->cbq_rate.rate * 8, &rubit); + + dp_dump(p, " rate %.2f%s/s (%.0f%s) prio %u", + r, ru, rbit, rubit, cbq->cbq_wrr.priority); + +ignore: + return line; +} + +static int cbq_qdisc_dump_brief(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p, int line) +{ + return cbq_dump_brief((struct rtnl_tca *) qdisc, p, line); +} + +static int cbq_class_dump_brief(struct rtnl_class *class, + struct nl_dump_params *p, int line) +{ + return cbq_dump_brief((struct rtnl_tca *) class, p, line); +} + +static int cbq_dump_full(struct rtnl_tca *tca, struct nl_dump_params *p, + int line) +{ + struct rtnl_cbq *cbq; + char *unit, buf[32]; + double w; + uint32_t el; + + cbq = cbq_qdisc(tca); + if (!cbq) + goto ignore; + + w = nl_cancel_down_bits(cbq->cbq_wrr.weight * 8, &unit); + + dp_dump(p, "avgpkt %u mpu %u cell %u allot %u weight %.0f%s\n", + cbq->cbq_lss.avpkt, + cbq->cbq_rate.mpu, + 1 << cbq->cbq_rate.cell_log, + cbq->cbq_wrr.allot, w, unit); + + el = cbq->cbq_lss.ewma_log; + dp_dump_line(p, line++, " minidle %uus maxidle %uus offtime " + "%uus level %u ewma_log %u\n", + nl_ticks2us(cbq->cbq_lss.minidle >> el), + nl_ticks2us(cbq->cbq_lss.maxidle >> el), + nl_ticks2us(cbq->cbq_lss.offtime >> el), + cbq->cbq_lss.level, + cbq->cbq_lss.ewma_log); + + dp_dump_line(p, line++, " penalty %uus strategy %s ", + nl_ticks2us(cbq->cbq_ovl.penalty), + nl_ovl_strategy2str(cbq->cbq_ovl.strategy, buf, sizeof(buf))); + + dp_dump(p, "split %s defmap 0x%08x ", + rtnl_tc_handle2str(cbq->cbq_fopt.split, buf, sizeof(buf)), + cbq->cbq_fopt.defmap); + + dp_dump(p, "police %s", + nl_police2str(cbq->cbq_police.police, buf, sizeof(buf))); + +ignore: + return line; +} + +static int cbq_qdisc_dump_full(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p, int line) +{ + return cbq_dump_full((struct rtnl_tca *) qdisc, p, line); +} + +static int cbq_class_dump_full(struct rtnl_class *class, + struct nl_dump_params *p, int line) +{ + return cbq_dump_full((struct rtnl_tca *) class, p, line); +} + +static int cbq_dump_with_stats(struct rtnl_tca *tca, struct nl_dump_params *p, + int line) +{ + struct tc_cbq_xstats *x = tca_xstats(tca); + + if (!x) + goto ignore; + + dp_dump_line(p, line++, " borrows overact " + " avgidle undertime\n"); + dp_dump_line(p, line++, " %10u %10u %10u %10u\n", + x->borrows, x->overactions, x->avgidle, x->undertime); + +ignore: + return line; +} + +static int cbq_qdisc_dump_with_stats(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p, int line) +{ + return cbq_dump_with_stats((struct rtnl_tca *) qdisc, p, line); +} + +static int cbq_class_dump_with_stats(struct rtnl_class *class, + struct nl_dump_params *p, int line) +{ + return cbq_dump_with_stats((struct rtnl_tca *) class, p, line); +} + +static struct rtnl_qdisc_ops cbq_qdisc_ops = { + .qo_kind = "cbq", + .qo_msg_parser = cbq_qdisc_msg_parser, + .qo_free_data = cbq_qdisc_free_data, + .qo_clone = cbq_qdisc_clone, + .qo_dump[NL_DUMP_BRIEF] = cbq_qdisc_dump_brief, + .qo_dump[NL_DUMP_FULL] = cbq_qdisc_dump_full, + .qo_dump[NL_DUMP_STATS] = cbq_qdisc_dump_with_stats, +}; + +static struct rtnl_class_ops cbq_class_ops = { + .co_kind = "cbq", + .co_msg_parser = cbq_class_msg_parser, + .co_free_data = cbq_class_free_data, + .co_clone = cbq_class_clone, + .co_dump[NL_DUMP_BRIEF] = cbq_class_dump_brief, + .co_dump[NL_DUMP_FULL] = cbq_class_dump_full, + .co_dump[NL_DUMP_STATS] = cbq_class_dump_with_stats, +}; + +static void __init cbq_init(void) +{ + rtnl_qdisc_register(&cbq_qdisc_ops); + rtnl_class_register(&cbq_class_ops); +} + +static void __exit cbq_exit(void) +{ + rtnl_qdisc_unregister(&cbq_qdisc_ops); + rtnl_class_unregister(&cbq_class_ops); +} + +/** @} */ diff --git a/lib/route/sch/dsmark.c b/lib/route/sch/dsmark.c new file mode 100644 index 0000000..5ba6b92 --- /dev/null +++ b/lib/route/sch/dsmark.c @@ -0,0 +1,459 @@ +/* + * lib/route/sch/dsmark.c DSMARK + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup qdisc_api + * @ingroup class_api + * @defgroup dsmark Differentiated Services Marker (DSMARK) + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_DSMARK_ATTR_INDICES 0x1 +#define SCH_DSMARK_ATTR_DEFAULT_INDEX 0x2 +#define SCH_DSMARK_ATTR_SET_TC_INDEX 0x4 + +#define SCH_DSMARK_ATTR_MASK 0x1 +#define SCH_DSMARK_ATTR_VALUE 0x2 +/** @endcond */ + +static inline struct rtnl_dsmark_qdisc *dsmark_qdisc(struct rtnl_qdisc *qdisc) +{ + return (struct rtnl_dsmark_qdisc *) qdisc->q_subdata; +} + +static inline struct rtnl_dsmark_qdisc * +dsmark_qdisc_alloc(struct rtnl_qdisc *qdisc) +{ + if (!qdisc->q_subdata) + qdisc->q_subdata = calloc(1, sizeof(struct rtnl_dsmark_qdisc)); + + return dsmark_qdisc(qdisc); +} + +static struct nla_policy dsmark_policy[TCA_DSMARK_MAX+1] = { + [TCA_DSMARK_INDICES] = { .type = NLA_U16 }, + [TCA_DSMARK_DEFAULT_INDEX] = { .type = NLA_U16 }, + [TCA_DSMARK_SET_TC_INDEX] = { .type = NLA_FLAG }, + [TCA_DSMARK_VALUE] = { .type = NLA_U8 }, + [TCA_DSMARK_MASK] = { .type = NLA_U8 }, +}; + +static int dsmark_qdisc_msg_parser(struct rtnl_qdisc *qdisc) +{ + int err; + struct nlattr *tb[TCA_DSMARK_MAX + 1]; + struct rtnl_dsmark_qdisc *dsmark; + + err = tca_parse(tb, TCA_DSMARK_MAX, (struct rtnl_tca *) qdisc, + dsmark_policy); + if (err < 0) + return err; + + dsmark = dsmark_qdisc_alloc(qdisc); + if (!dsmark) + return nl_errno(ENOMEM); + + if (tb[TCA_DSMARK_INDICES]) { + dsmark->qdm_indices = nla_get_u16(tb[TCA_DSMARK_INDICES]); + dsmark->qdm_mask |= SCH_DSMARK_ATTR_INDICES; + } + + if (tb[TCA_DSMARK_DEFAULT_INDEX]) { + dsmark->qdm_default_index = + nla_get_u16(tb[TCA_DSMARK_DEFAULT_INDEX]); + dsmark->qdm_mask |= SCH_DSMARK_ATTR_DEFAULT_INDEX; + } + + if (tb[TCA_DSMARK_SET_TC_INDEX]) { + dsmark->qdm_set_tc_index = 1; + dsmark->qdm_mask |= SCH_DSMARK_ATTR_SET_TC_INDEX; + } + + return 0; +} + +static inline struct rtnl_dsmark_class *dsmark_class(struct rtnl_class *class) +{ + return (struct rtnl_dsmark_class *) class->c_subdata; +} + +static inline struct rtnl_dsmark_class * +dsmark_class_alloc(struct rtnl_class *class) +{ + if (!class->c_subdata) + class->c_subdata = calloc(1, sizeof(struct rtnl_dsmark_class)); + + return dsmark_class(class); +} + +static int dsmark_class_msg_parser(struct rtnl_class *class) +{ + int err; + struct nlattr *tb[TCA_DSMARK_MAX + 1]; + struct rtnl_dsmark_class *dsmark; + + err = tca_parse(tb, TCA_DSMARK_MAX, (struct rtnl_tca *) class, + dsmark_policy); + if (err < 0) + return err; + + dsmark = dsmark_class_alloc(class); + if (!dsmark) + return nl_errno(ENOMEM); + + if (tb[TCA_DSMARK_MASK]) { + dsmark->cdm_bmask = nla_get_u8(tb[TCA_DSMARK_MASK]); + dsmark->cdm_mask |= SCH_DSMARK_ATTR_MASK; + } + + if (tb[TCA_DSMARK_VALUE]) { + dsmark->cdm_value = nla_get_u8(tb[TCA_DSMARK_VALUE]); + dsmark->cdm_mask |= SCH_DSMARK_ATTR_VALUE; + } + + return 0; +} + +static int dsmark_qdisc_dump_brief(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p, int line) +{ + struct rtnl_dsmark_qdisc *dsmark = dsmark_qdisc(qdisc); + + if (dsmark && (dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES)) + dp_dump(p, " indices 0x%04x", dsmark->qdm_indices); + + return line; +} + +static int dsmark_qdisc_dump_full(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p, int line) +{ + struct rtnl_dsmark_qdisc *dsmark = dsmark_qdisc(qdisc); + + if (!dsmark) + goto ignore; + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX) + dp_dump(p, " default index 0x%04x", dsmark->qdm_default_index); + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX) + dp_dump(p, " set-tc-index"); + +ignore: + return line; +} + +static int dsmark_class_dump_brief(struct rtnl_class *class, + struct nl_dump_params *p, int line) +{ + struct rtnl_dsmark_class *dsmark = dsmark_class(class); + + if (!dsmark) + goto ignore; + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE) + dp_dump(p, " value 0x%02x", dsmark->cdm_value); + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK) + dp_dump(p, " mask 0x%02x", dsmark->cdm_bmask); + +ignore: + return line; +} + +static struct nl_msg *dsmark_qdisc_get_opts(struct rtnl_qdisc *qdisc) +{ + struct rtnl_dsmark_qdisc *dsmark = dsmark_qdisc(qdisc); + struct nl_msg *msg; + + if (!dsmark) + return NULL; + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES) + NLA_PUT_U16(msg, TCA_DSMARK_INDICES, dsmark->qdm_indices); + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX) + NLA_PUT_U16(msg, TCA_DSMARK_DEFAULT_INDEX, + dsmark->qdm_default_index); + + if (dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX) + NLA_PUT_FLAG(msg, TCA_DSMARK_SET_TC_INDEX); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +static struct nl_msg *dsmark_class_get_opts(struct rtnl_class *class) +{ + struct rtnl_dsmark_class *dsmark = dsmark_class(class); + struct nl_msg *msg; + + if (!dsmark) + return NULL; + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK) + NLA_PUT_U8(msg, TCA_DSMARK_MASK, dsmark->cdm_bmask); + + if (dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE) + NLA_PUT_U8(msg, TCA_DSMARK_VALUE, dsmark->cdm_value); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +/** + * @name Class Attribute Access + * @{ + */ + +/** + * Set bitmask of DSMARK class. + * @arg class DSMARK class to be modified. + * @arg mask New bitmask. + * @return 0 on success or a negative error code. + */ +int rtnl_class_dsmark_set_bitmask(struct rtnl_class *class, uint8_t mask) +{ + struct rtnl_dsmark_class *dsmark; + + dsmark = dsmark_class(class); + if (!dsmark) + return nl_errno(ENOMEM); + + dsmark->cdm_bmask = mask; + dsmark->cdm_mask |= SCH_DSMARK_ATTR_MASK; + + return 0; +} + +/** + * Get bitmask of DSMARK class. + * @arg class DSMARK class. + * @return Bitmask or a negative error code. + */ +int rtnl_class_dsmark_get_bitmask(struct rtnl_class *class) +{ + struct rtnl_dsmark_class *dsmark; + + dsmark = dsmark_class(class); + if (dsmark && dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK) + return dsmark->cdm_bmask; + else + return nl_errno(ENOENT); +} + +/** + * Set value of DSMARK class. + * @arg class DSMARK class to be modified. + * @arg value New value. + * @return 0 on success or a negative errror code. + */ +int rtnl_class_dsmark_set_value(struct rtnl_class *class, uint8_t value) +{ + struct rtnl_dsmark_class *dsmark; + + dsmark = dsmark_class(class); + if (!dsmark) + return nl_errno(ENOMEM); + + dsmark->cdm_value = value; + dsmark->cdm_mask |= SCH_DSMARK_ATTR_VALUE; + + return 0; +} + +/** + * Get value of DSMARK class. + * @arg class DSMARK class. + * @return Value or a negative error code. + */ +int rtnl_class_dsmark_get_value(struct rtnl_class *class) +{ + struct rtnl_dsmark_class *dsmark; + + dsmark = dsmark_class(class); + if (dsmark && dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE) + return dsmark->cdm_value; + else + return nl_errno(ENOENT); +} + +/** @} */ + +/** + * @name Qdisc Attribute Access + * @{ + */ + +/** + * Set indices of DSMARK qdisc. + * @arg qdisc DSMARK qdisc to be modified. + * @arg indices New indices. + */ +int rtnl_qdisc_dsmark_set_indices(struct rtnl_qdisc *qdisc, uint16_t indices) +{ + struct rtnl_dsmark_qdisc *dsmark; + + dsmark = dsmark_qdisc(qdisc); + if (!dsmark) + return nl_errno(ENOMEM); + + dsmark->qdm_indices = indices; + dsmark->qdm_mask |= SCH_DSMARK_ATTR_INDICES; + + return 0; +} + +/** + * Get indices of DSMARK qdisc. + * @arg qdisc DSMARK qdisc. + * @return Indices or a negative error code. + */ +int rtnl_qdisc_dsmark_get_indices(struct rtnl_qdisc *qdisc) +{ + struct rtnl_dsmark_qdisc *dsmark; + + dsmark = dsmark_qdisc(qdisc); + if (dsmark && dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES) + return dsmark->qdm_indices; + else + return nl_errno(ENOENT); +} + +/** + * Set default index of DSMARK qdisc. + * @arg qdisc DSMARK qdisc to be modified. + * @arg default_index New default index. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_dsmark_set_default_index(struct rtnl_qdisc *qdisc, + uint16_t default_index) +{ + struct rtnl_dsmark_qdisc *dsmark; + + dsmark = dsmark_qdisc(qdisc); + if (!dsmark) + return nl_errno(ENOMEM); + + dsmark->qdm_default_index = default_index; + dsmark->qdm_mask |= SCH_DSMARK_ATTR_DEFAULT_INDEX; + + return 0; +} + +/** + * Get default index of DSMARK qdisc. + * @arg qdisc DSMARK qdisc. + * @return Default index or a negative error code. + */ +int rtnl_qdisc_dsmark_get_default_index(struct rtnl_qdisc *qdisc) +{ + struct rtnl_dsmark_qdisc *dsmark; + + dsmark = dsmark_qdisc(qdisc); + if (dsmark && dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX) + return dsmark->qdm_default_index; + else + return nl_errno(ENOENT); +} + +/** + * Set set-tc-index flag of DSMARK qdisc. + * @arg qdisc DSMARK qdisc to be modified. + * @arg flag Flag indicating whether to enable or disable. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_dsmark_set_set_tc_index(struct rtnl_qdisc *qdisc, int flag) +{ + struct rtnl_dsmark_qdisc *dsmark; + + dsmark = dsmark_qdisc(qdisc); + if (!dsmark) + return nl_errno(ENOMEM); + + dsmark->qdm_set_tc_index = !!flag; + dsmark->qdm_mask |= SCH_DSMARK_ATTR_SET_TC_INDEX; + + return 0; +} + +/** + * Get set-tc-index flag of DSMARK qdisc. + * @arg qdisc DSMARK qdisc to be modified. + * @return 1 or 0 to indicate wehther the flag is enabled or a negative + * error code. + */ +int rtnl_qdisc_dsmark_get_set_tc_index(struct rtnl_qdisc *qdisc) +{ + struct rtnl_dsmark_qdisc *dsmark; + + dsmark = dsmark_qdisc(qdisc); + if (dsmark && dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX) + return dsmark->qdm_set_tc_index; + else + return nl_errno(ENOENT); +} + +/** @} */ + +static struct rtnl_qdisc_ops dsmark_qdisc_ops = { + .qo_kind = "dsmark", + .qo_msg_parser = dsmark_qdisc_msg_parser, + .qo_dump[NL_DUMP_BRIEF] = dsmark_qdisc_dump_brief, + .qo_dump[NL_DUMP_FULL] = dsmark_qdisc_dump_full, + .qo_get_opts = dsmark_qdisc_get_opts, +}; + +static struct rtnl_class_ops dsmark_class_ops = { + .co_kind = "dsmark", + .co_msg_parser = dsmark_class_msg_parser, + .co_dump[NL_DUMP_BRIEF] = dsmark_class_dump_brief, + .co_get_opts = dsmark_class_get_opts, +}; + +static void __init dsmark_init(void) +{ + rtnl_qdisc_register(&dsmark_qdisc_ops); + rtnl_class_register(&dsmark_class_ops); +} + +static void __exit dsmark_exit(void) +{ + rtnl_qdisc_unregister(&dsmark_qdisc_ops); + rtnl_class_unregister(&dsmark_class_ops); +} + +/** @} */ diff --git a/lib/route/sch/fifo.c b/lib/route/sch/fifo.c new file mode 100644 index 0000000..4f8d202 --- /dev/null +++ b/lib/route/sch/fifo.c @@ -0,0 +1,205 @@ +/* + * lib/route/sch/fifo.c (p|b)fifo + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup qdisc_api + * @defgroup fifo Packet/Bytes FIFO (pfifo/bfifo) + * @brief + * + * The FIFO qdisc comes in two flavours: + * @par bfifo (Byte FIFO) + * Allows enqueuing until the currently queued volume in bytes exceeds + * the configured limit.backlog contains currently enqueued volume in bytes. + * + * @par pfifo (Packet FIFO) + * Allows enquueing until the currently queued number of packets + * exceeds the configured limit. + * + * The configuration is exactly the same, the decision which of + * the two variations is going to be used is made based on the + * kind of the qdisc (rtnl_qdisc_set_kind()). + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_FIFO_ATTR_LIMIT 1 +/** @endcond */ + +static inline struct rtnl_fifo *fifo_qdisc(struct rtnl_qdisc *qdisc) +{ + return (struct rtnl_fifo *) qdisc->q_subdata; +} + +static inline struct rtnl_fifo *fifo_alloc(struct rtnl_qdisc *qdisc) +{ + if (!qdisc->q_subdata) + qdisc->q_subdata = calloc(1, sizeof(struct rtnl_fifo)); + + return fifo_qdisc(qdisc); +} + +static int fifo_msg_parser(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fifo *fifo; + struct tc_fifo_qopt *opt; + + if (qdisc->q_opts->d_size < sizeof(struct tc_fifo_qopt)) + return nl_error(EINVAL, "FIFO options size mismatch"); + + fifo = fifo_alloc(qdisc); + if (!fifo) + return nl_errno(ENOMEM); + + opt = (struct tc_fifo_qopt *) qdisc->q_opts->d_data; + fifo->qf_limit = opt->limit; + fifo->qf_mask = SCH_FIFO_ATTR_LIMIT; + + return 0; +} + +static void fifo_free_data(struct rtnl_qdisc *qdisc) +{ + free(qdisc->q_subdata); +} + +static int pfifo_dump_brief(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p, int line) +{ + struct rtnl_fifo *fifo = fifo_qdisc(qdisc); + + if (fifo) + dp_dump(p, " limit %u packets", fifo->qf_limit); + + return line; +} + +static int bfifo_dump_brief(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p, int line) +{ + struct rtnl_fifo *fifo = fifo_qdisc(qdisc); + + if (fifo) { + char *unit; + double r; + + r = nl_cancel_down_bytes(fifo->qf_limit, &unit); + dp_dump(p, " limit %.1f%s", r, unit); + } + + return line; +} + +static struct nl_msg *fifo_get_opts(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fifo *fifo; + struct tc_fifo_qopt opts; + struct nl_msg *msg; + + fifo = fifo_qdisc(qdisc); + if (!fifo || !(fifo->qf_mask & SCH_FIFO_ATTR_LIMIT)) + return NULL; + + msg = nlmsg_alloc(); + if (!msg) + goto errout; + + memset(&opts, 0, sizeof(opts)); + opts.limit = fifo->qf_limit; + + if (nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD) < 0) + goto errout; + + return msg; +errout: + nlmsg_free(msg); + return NULL; +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set limit of FIFO qdisc. + * @arg qdisc FIFO qdisc to be modified. + * @arg limit New limit. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_fifo_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_fifo *fifo; + + fifo = fifo_alloc(qdisc); + if (!fifo) + return nl_errno(ENOMEM); + + fifo->qf_limit = limit; + fifo->qf_mask |= SCH_FIFO_ATTR_LIMIT; + + return 0; +} + +/** + * Get limit of a FIFO qdisc. + * @arg qdisc FIFO qdisc. + * @return Numeric limit or a negative error code. + */ +int rtnl_qdisc_fifo_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_fifo *fifo; + + fifo = fifo_qdisc(qdisc); + if (fifo && fifo->qf_mask & SCH_FIFO_ATTR_LIMIT) + return fifo->qf_limit; + else + return nl_errno(ENOMEM); +} + +/** @} */ + +static struct rtnl_qdisc_ops pfifo_ops = { + .qo_kind = "pfifo", + .qo_msg_parser = fifo_msg_parser, + .qo_free_data = fifo_free_data, + .qo_dump[NL_DUMP_BRIEF] = pfifo_dump_brief, + .qo_get_opts = fifo_get_opts, +}; + +static struct rtnl_qdisc_ops bfifo_ops = { + .qo_kind = "bfifo", + .qo_msg_parser = fifo_msg_parser, + .qo_free_data = fifo_free_data, + .qo_dump[NL_DUMP_BRIEF] = bfifo_dump_brief, + .qo_get_opts = fifo_get_opts, +}; + +static void __init fifo_init(void) +{ + rtnl_qdisc_register(&pfifo_ops); + rtnl_qdisc_register(&bfifo_ops); +} + +static void __exit fifo_exit(void) +{ + rtnl_qdisc_unregister(&pfifo_ops); + rtnl_qdisc_unregister(&bfifo_ops); +} + +/** @} */ diff --git a/lib/route/sch/htb.c b/lib/route/sch/htb.c new file mode 100644 index 0000000..6de87b3 --- /dev/null +++ b/lib/route/sch/htb.c @@ -0,0 +1,553 @@ +/* + * lib/route/sch/htb.c HTB Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2005-2006 Petr Gotthard + * Copyright (c) 2005-2006 Siemens AG Oesterreich + */ + +/** + * @ingroup qdisc_api + * @ingroup class_api + * @defgroup htb Hierachical Token Bucket (HTB) + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_HTB_HAS_RATE2QUANTUM 0x01 +#define SCH_HTB_HAS_DEFCLS 0x02 + +#define SCH_HTB_HAS_PRIO 0x001 +#define SCH_HTB_HAS_MTU 0x002 +#define SCH_HTB_HAS_RATE 0x004 +#define SCH_HTB_HAS_CEIL 0x008 +#define SCH_HTB_HAS_RBUFFER 0x010 +#define SCH_HTB_HAS_CBUFFER 0x020 +#define SCH_HTB_HAS_QUANTUM 0x040 +#define SCH_HTB_HAS_OVERHEAD 0x080 +#define SCH_HTB_HAS_MPU 0x100 +/** @endcond */ + +static inline struct rtnl_htb_qdisc *htb_qdisc(struct rtnl_qdisc *qdisc) +{ + if (qdisc->q_subdata == NULL) + qdisc->q_subdata = calloc(1, sizeof(struct rtnl_htb_qdisc)); + + return (struct rtnl_htb_qdisc *) qdisc->q_subdata; +} + +static struct nla_policy htb_policy[TCA_HTB_MAX+1] = { + [TCA_HTB_INIT] = { .minlen = sizeof(struct tc_htb_glob) }, + [TCA_HTB_PARMS] = { .minlen = sizeof(struct tc_htb_opt) }, +}; + +static int htb_qdisc_msg_parser(struct rtnl_qdisc *qdisc) +{ + int err; + struct nlattr *tb[TCA_HTB_MAX + 1]; + struct rtnl_htb_qdisc *d; + + err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) qdisc, htb_policy); + if (err < 0) + return err; + + d = htb_qdisc(qdisc); + + if (tb[TCA_HTB_INIT]) { + struct tc_htb_glob opts; + + nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts)); + d->qh_rate2quantum = opts.rate2quantum; + d->qh_defcls = opts.defcls; + + d->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS); + } + + return 0; +} + +static void htb_qdisc_free_data(struct rtnl_qdisc *qdisc) +{ + free(qdisc->q_subdata); +} + +static inline struct rtnl_htb_class *htb_class(struct rtnl_class *class) +{ + if (class->c_subdata == NULL) + class->c_subdata = calloc(1, sizeof(struct rtnl_htb_class)); + + return (struct rtnl_htb_class *) class->c_subdata; +} + +static int htb_class_msg_parser(struct rtnl_class *class) +{ + int err; + struct nlattr *tb[TCA_HTB_MAX + 1]; + struct rtnl_htb_class *d; + + err = tca_parse(tb, TCA_HTB_MAX, (struct rtnl_tca *) class, htb_policy); + if (err < 0) + return err; + + d = htb_class(class); + + if (tb[TCA_HTB_PARMS]) { + struct tc_htb_opt opts; + + nla_memcpy(&opts, tb[TCA_HTB_PARMS], sizeof(opts)); + d->ch_prio = opts.prio; + rtnl_copy_ratespec(&d->ch_rate, &opts.rate); + rtnl_copy_ratespec(&d->ch_ceil, &opts.ceil); + d->ch_rbuffer = rtnl_tc_calc_bufsize(opts.buffer, opts.rate.rate); + d->ch_cbuffer = rtnl_tc_calc_bufsize(opts.cbuffer, opts.ceil.rate); + d->ch_quantum = opts.quantum; + d->ch_overhead = (opts.rate.mpu >> 8) & 0xff; + d->ch_mpu = opts.rate.mpu & 0xff; + + d->ch_mask = (SCH_HTB_HAS_PRIO | SCH_HTB_HAS_RATE | + SCH_HTB_HAS_CEIL | SCH_HTB_HAS_RBUFFER | + SCH_HTB_HAS_CBUFFER | SCH_HTB_HAS_QUANTUM | + SCH_HTB_HAS_OVERHEAD | SCH_HTB_HAS_MPU); + } + + return 0; +} + +static void htb_class_free_data(struct rtnl_class *class) +{ + free(class->c_subdata); +} + +static int htb_qdisc_dump_brief(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p, int line) +{ + struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata; + + if (d == NULL) + goto ignore; + + if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) + dp_dump(p, " r2q %u", d->qh_rate2quantum); + + if (d->qh_mask & SCH_HTB_HAS_DEFCLS) { + char buf[32]; + dp_dump(p, " default %s", + rtnl_tc_handle2str(d->qh_defcls, buf, sizeof(buf))); + } + +ignore: + return line; +} + +static int htb_class_dump_brief(struct rtnl_class *class, + struct nl_dump_params *p, int line) +{ + struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; + + if (d == NULL) + goto ignore; + + if (d->ch_mask & SCH_HTB_HAS_RATE) { + double r, rbit; + char *ru, *rubit; + + r = nl_cancel_down_bytes(d->ch_rate.rs_rate, &ru); + rbit = nl_cancel_down_bits(d->ch_rate.rs_rate*8, &rubit); + + dp_dump(p, " rate %.2f%s/s (%.0f%s) log %u", + r, ru, rbit, rubit, 1<ch_rate.rs_cell_log); + } + +ignore: + return line; +} + +static int htb_class_dump_full(struct rtnl_class *class, + struct nl_dump_params *p, int line) +{ + struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; + + if (d == NULL) + goto ignore; + + /* line 1 */ + if (d->ch_mask & SCH_HTB_HAS_CEIL) { + double r, rbit; + char *ru, *rubit; + + r = nl_cancel_down_bytes(d->ch_ceil.rs_rate, &ru); + rbit = nl_cancel_down_bits(d->ch_ceil.rs_rate*8, &rubit); + + dp_dump(p, " ceil %.2f%s/s (%.0f%s) log %u", + r, ru, rbit, rubit, 1<ch_ceil.rs_cell_log); + } + + if (d->ch_mask & SCH_HTB_HAS_PRIO) + dp_dump(p, " prio %u", d->ch_prio); + + if (d->ch_mask & SCH_HTB_HAS_MTU) + dp_dump(p, " mtu %u", d->ch_mtu); + + if (d->ch_mask & SCH_HTB_HAS_RBUFFER) { + double b; + char *bu; + + b = nl_cancel_down_bytes(d->ch_rbuffer, &bu); + dp_dump(p, " rbuffer %.2f%s", b, bu); + } + + if (d->ch_mask & SCH_HTB_HAS_CBUFFER) { + double b; + char *bu; + + b = nl_cancel_down_bytes(d->ch_cbuffer, &bu); + dp_dump(p, " cbuffer %.2f%s", b, bu); + } + + if (d->ch_mask & SCH_HTB_HAS_QUANTUM) + dp_dump(p, " quantum %u", d->ch_quantum); + + if (d->ch_mask & SCH_HTB_HAS_OVERHEAD) + dp_dump(p, " overhead %u", d->ch_overhead); + + if (d->ch_mask & SCH_HTB_HAS_MPU) + dp_dump(p, " mpu %u", d->ch_mpu); + +ignore: + return line; +} + +static struct nl_msg *htb_qdisc_get_opts(struct rtnl_qdisc *qdisc) +{ + struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata; + struct tc_htb_glob opts; + struct nl_msg *msg; + + if (d == NULL) + return NULL; + + msg = nlmsg_alloc(); + if (msg == NULL) + return NULL; + + memset(&opts, 0, sizeof(opts)); + opts.version = TC_HTB_PROTOVER; + + if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM) + opts.rate2quantum = d->qh_rate2quantum; + if (d->qh_mask & SCH_HTB_HAS_DEFCLS) + opts.defcls = d->qh_defcls; + + nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts); + + return msg; +} + +static uint8_t compute_cell(uint32_t rate, uint32_t mtu) +{ + uint8_t cell_log = 0; + while (mtu > 255) { + mtu >>= 1; + cell_log++; + } + + return cell_log; +} + +static struct nl_msg *htb_class_get_opts(struct rtnl_class *class) +{ + struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata; + uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE]; + struct tc_htb_opt opts; + struct nl_msg *msg; + int buffer, cbuffer; + uint8_t overhead = 0, mpu = 0; + + if (d == NULL) + return NULL; + + msg = nlmsg_alloc(); + memset(&opts, 0, sizeof(opts)); + + /* if not set, zero (0) is used as priority */ + if (d->ch_mask & SCH_HTB_HAS_PRIO) + opts.prio = d->ch_prio; + + if (d->ch_mask & SCH_HTB_HAS_MTU) + mtu = d->ch_mtu; + else + mtu = 1600; /* eth packet len */ + + if (!(d->ch_mask & SCH_HTB_HAS_RATE)) + BUG(); + + rtnl_rcopy_ratespec(&opts.rate, &d->ch_rate); + /* if cell_log not set, compute default value */ + if (opts.rate.cell_log == UINT8_MAX) + opts.rate.cell_log = compute_cell(opts.rate.rate, mtu); + + /* if not set, configured rate is used as ceil, which implies no borrowing */ + if (d->ch_mask & SCH_HTB_HAS_CEIL) + rtnl_rcopy_ratespec(&opts.ceil, &d->ch_ceil); + else + memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec)); + /* if cell_log not set, compute default value */ + if (opts.ceil.cell_log == UINT8_MAX) + opts.ceil.cell_log = compute_cell(opts.ceil.rate, mtu); + + if (d->ch_mask & SCH_HTB_HAS_RBUFFER) + buffer = d->ch_rbuffer; + else + buffer = opts.rate.rate / nl_get_hz() + mtu; + + opts.buffer = rtnl_tc_calc_txtime(buffer, opts.rate.rate); + + if (d->ch_mask & SCH_HTB_HAS_CBUFFER) + cbuffer = d->ch_cbuffer; + else + cbuffer = opts.ceil.rate / nl_get_hz() + mtu; + + opts.cbuffer = rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate); + + if (d->ch_mask & SCH_HTB_HAS_QUANTUM) + opts.quantum = d->ch_quantum; + + if (d->ch_mask & SCH_HTB_HAS_OVERHEAD) + overhead = d->ch_overhead; + + if (d->ch_mask & SCH_HTB_HAS_MPU) + mpu = d->ch_mpu; + + opts.rate.mpu = mpu | (overhead << 8); + opts.ceil.mpu = mpu | (overhead << 8); + + nla_put(msg, TCA_HTB_PARMS, sizeof(opts), &opts); + + rtnl_tc_build_rate_table(rtable, mpu, overhead, + 1 << opts.rate.cell_log, + opts.rate.rate); + nla_put(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable); + + rtnl_tc_build_rate_table(ctable, mpu, overhead, + 1 << opts.ceil.cell_log, + opts.ceil.rate); + nla_put(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable); + + return msg; +} + +/** + * @name Attribute Modifications + * @{ + */ + +void rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum) +{ + struct rtnl_htb_qdisc *d = htb_qdisc(qdisc); + if (d == NULL) + return; + + d->qh_rate2quantum = rate2quantum; + d->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM; +} + +/** + * Set default class of the htb qdisc to the specified value + * @arg qdisc qdisc to change + * @arg defcls new default class + */ +void rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls) +{ + struct rtnl_htb_qdisc *d = htb_qdisc(qdisc); + if (d == NULL) + return; + + d->qh_defcls = defcls; + d->qh_mask |= SCH_HTB_HAS_DEFCLS; +} + +void rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio) +{ + struct rtnl_htb_class *d = htb_class(class); + if (d == NULL) + return; + + d->ch_prio = prio; + d->ch_mask |= SCH_HTB_HAS_PRIO; +} + +/** + * Set MTU of the data link. + * @arg class HTB class to be modified. + * @arg mtu New MTU in bytes. + * + * Sets MTU of the data link controlled by the HTB class. + * If not set, the Ethernet MTU (1600) is used. + */ +void rtnl_htb_set_mtu(struct rtnl_class *class, uint32_t mtu) +{ + struct rtnl_htb_class *d = htb_class(class); + if (d == NULL) + return; + + d->ch_mtu = mtu; + d->ch_mask |= SCH_HTB_HAS_MTU; +} + +/** + * Set rate of HTB class. + * @arg class HTB class to be modified. + * @arg rate New rate in bytes per second. + */ +void rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate) +{ + struct rtnl_htb_class *d = htb_class(class); + if (d == NULL) + return; + + d->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */ + d->ch_rate.rs_rate = rate; + d->ch_mask |= SCH_HTB_HAS_RATE; +} + +/** + * Set ceil of HTB class. + * @arg class HTB class to be modified. + * @arg ceil New ceil in bytes per second. + */ +void rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil) +{ + struct rtnl_htb_class *d = htb_class(class); + if (d == NULL) + return; + + d->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */ + d->ch_ceil.rs_rate = ceil; + d->ch_mask |= SCH_HTB_HAS_CEIL; +} + +/** + * Set size of the rate bucket of HTB class. + * @arg class HTB class to be modified. + * @arg rbuffer New size in bytes. + */ +void rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer) +{ + struct rtnl_htb_class *d = htb_class(class); + if (d == NULL) + return; + + d->ch_rbuffer = rbuffer; + d->ch_mask |= SCH_HTB_HAS_RBUFFER; +} + +/** + * Set size of the ceil bucket of HTB class. + * @arg class HTB class to be modified. + * @arg cbuffer New size in bytes. + */ +void rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer) +{ + struct rtnl_htb_class *d = htb_class(class); + if (d == NULL) + return; + + d->ch_cbuffer = cbuffer; + d->ch_mask |= SCH_HTB_HAS_CBUFFER; +} + +/** + * Set how much bytes to serve from leaf at once of HTB class {use r2q}. + * @arg class HTB class to be modified. + * @arg quantum New size in bytes. + */ +void rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum) +{ + struct rtnl_htb_class *d = htb_class(class); + if (d == NULL) + return; + + d->ch_quantum = quantum; + d->ch_mask |= SCH_HTB_HAS_QUANTUM; +} + +/** + * Set per-packet size overhead used in rate computations of HTB class. + * @arg class HTB class to be modified. + * @arg overhead Size in bytes. + */ +void rtnl_htb_set_overhead(struct rtnl_class *class, uint8_t overhead) +{ + struct rtnl_htb_class *d = htb_class(class); + if (d == NULL) + return; + + d->ch_overhead = overhead; + d->ch_mask |= SCH_HTB_HAS_OVERHEAD; +} + +/** + * Set the minimum packet size used in rate computations of HTB class. + * @arg class HTB class to be modified. + * @arg mpu Size in bytes. + */ +void rtnl_htb_set_mpu(struct rtnl_class *class, uint8_t mpu) +{ + struct rtnl_htb_class *d = htb_class(class); + if (d == NULL) + return; + + d->ch_mpu = mpu; + d->ch_mask |= SCH_HTB_HAS_MPU; +} + +/** @} */ + +static struct rtnl_qdisc_ops htb_qdisc_ops = { + .qo_kind = "htb", + .qo_msg_parser = htb_qdisc_msg_parser, + .qo_free_data = htb_qdisc_free_data, + .qo_dump[NL_DUMP_BRIEF] = htb_qdisc_dump_brief, + .qo_get_opts = htb_qdisc_get_opts, +}; + +static struct rtnl_class_ops htb_class_ops = { + .co_kind = "htb", + .co_msg_parser = htb_class_msg_parser, + .co_free_data = htb_class_free_data, + .co_dump[NL_DUMP_BRIEF] = htb_class_dump_brief, + .co_dump[NL_DUMP_FULL] = htb_class_dump_full, + .co_get_opts = htb_class_get_opts, +}; + +static void __init htb_init(void) +{ + rtnl_qdisc_register(&htb_qdisc_ops); + rtnl_class_register(&htb_class_ops); +} + +static void __exit htb_exit(void) +{ + rtnl_qdisc_unregister(&htb_qdisc_ops); + rtnl_class_unregister(&htb_class_ops); +} + +/** @} */ diff --git a/lib/route/sch/netem.c b/lib/route/sch/netem.c new file mode 100644 index 0000000..e8b8913 --- /dev/null +++ b/lib/route/sch/netem.c @@ -0,0 +1,603 @@ +/* + * lib/route/sch/netem.c Network Emulator Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup qdisc_api + * @defgroup netem Network Emulator + * @brief + * + * For further documentation see http://linux-net.osdl.org/index.php/Netem + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_NETEM_ATTR_LATENCY 0x001 +#define SCH_NETEM_ATTR_LIMIT 0x002 +#define SCH_NETEM_ATTR_LOSS 0x004 +#define SCH_NETEM_ATTR_GAP 0x008 +#define SCH_NETEM_ATTR_DUPLICATE 0x010 +#define SCH_NETEM_ATTR_JITTER 0x020 +#define SCH_NETEM_ATTR_DELAY_CORR 0x040 +#define SCH_NETEM_ATTR_LOSS_CORR 0x080 +#define SCH_NETEM_ATTR_DUP_CORR 0x100 +#define SCH_NETEM_ATTR_RO_PROB 0x200 +#define SCH_NETEM_ATTR_RO_CORR 0x400 +/** @endcond */ + +static inline struct rtnl_netem *netem_qdisc(struct rtnl_qdisc *qdisc) +{ + return (struct rtnl_netem *) qdisc->q_subdata; +} + +static inline struct rtnl_netem *netem_alloc(struct rtnl_qdisc *qdisc) +{ + if (!qdisc->q_subdata) + qdisc->q_subdata = calloc(1, sizeof(struct rtnl_netem)); + + return netem_qdisc(qdisc); +} + +static struct nla_policy netem_policy[TCA_NETEM_MAX+1] = { + [TCA_NETEM_CORR] = { .minlen = sizeof(struct tc_netem_corr) }, + [TCA_NETEM_REORDER] = { .minlen = sizeof(struct tc_netem_reorder) }, +}; + +static int netem_msg_parser(struct rtnl_qdisc *qdisc) +{ + int len, err = 0; + struct rtnl_netem *netem; + struct tc_netem_qopt *opts; + + if (qdisc->q_opts->d_size < sizeof(*opts)) + return nl_error(EINVAL, "Netem specific options size mismatch"); + + netem = netem_alloc(qdisc); + if (!netem) + return nl_errno(ENOMEM); + + opts = (struct tc_netem_qopt *) qdisc->q_opts->d_data; + netem->qnm_latency = opts->latency; + netem->qnm_limit = opts->limit; + netem->qnm_loss = opts->loss; + netem->qnm_gap = opts->gap; + netem->qnm_duplicate = opts->duplicate; + netem->qnm_jitter = opts->jitter; + + netem->qnm_mask = (SCH_NETEM_ATTR_LATENCY | SCH_NETEM_ATTR_LIMIT | + SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP | + SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER); + + len = qdisc->q_opts->d_size - sizeof(*opts); + + if (len > 0) { + struct nlattr *tb[TCA_NETEM_MAX+1]; + + err = nla_parse(tb, TCA_NETEM_MAX, (struct nlattr *) + qdisc->q_opts->d_data + sizeof(*opts), + len, netem_policy); + if (err < 0) { + free(netem); + return err; + } + + if (tb[TCA_NETEM_CORR]) { + struct tc_netem_corr cor; + + nla_memcpy(&cor, tb[TCA_NETEM_CORR], sizeof(cor)); + netem->qnm_corr.nmc_delay = cor.delay_corr; + netem->qnm_corr.nmc_loss = cor.loss_corr; + netem->qnm_corr.nmc_duplicate = cor.dup_corr; + + netem->qnm_mask |= (SCH_NETEM_ATTR_DELAY_CORR | + SCH_NETEM_ATTR_LOSS_CORR | + SCH_NETEM_ATTR_DELAY_CORR); + } + + if (tb[TCA_NETEM_REORDER]) { + struct tc_netem_reorder ro; + + nla_memcpy(&ro, tb[TCA_NETEM_REORDER], sizeof(ro)); + netem->qnm_ro.nmro_probability = ro.probability; + netem->qnm_ro.nmro_correlation = ro.correlation; + + netem->qnm_mask |= (SCH_NETEM_ATTR_RO_PROB | + SCH_NETEM_ATTR_RO_CORR); + } + } + + return 0; +} + +static void netem_free_data(struct rtnl_qdisc *qdisc) +{ + free(qdisc->q_subdata); +} + +static int netem_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, + int line) +{ + struct rtnl_netem *netem = netem_qdisc(qdisc); + + if (netem) + dp_dump(p, "limit %d", netem->qnm_limit); + + return line; +} + +static int netem_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, + int line) +{ + return line; +} + +static struct nl_msg *netem_get_opts(struct rtnl_qdisc *qdisc) +{ + return NULL; +} + +/** + * @name Queue Limit + * @{ + */ + +/** + * Set limit of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg limit New limit in bytes. + * @return 0 on success or a negative error code. + */ +int rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return nl_errno(ENOMEM); + + netem->qnm_limit = limit; + netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT; + + return 0; +} + +/** + * Get limit of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Limit in bytes or a negative error code. + */ +int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT)) + return netem->qnm_limit; + else + return nl_errno(ENOENT); +} + +/** @} */ + +/** + * @name Packet Re-ordering + * @{ + */ + +/** + * Set re-ordering gap of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg gap New gap in number of packets. + * @return 0 on success or a negative error code. + */ +int rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap) +{ + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return nl_errno(ENOMEM); + + netem->qnm_gap = gap; + netem->qnm_mask |= SCH_NETEM_ATTR_GAP; + + return 0; +} + +/** + * Get re-ordering gap of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Re-ordering gap in packets or a negative error code. + */ +int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_GAP)) + return netem->qnm_gap; + else + return nl_errno(ENOENT); +} + +/** + * Set re-ordering probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New re-ordering probability. + * @return 0 on success or a negative error code. + */ +int rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return nl_errno(ENOMEM); + + netem->qnm_ro.nmro_probability = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB; + + return 0; +} + +/** + * Get re-ordering probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Re-ordering probability or a negative error code. + */ +int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB)) + return netem->qnm_ro.nmro_probability; + else + return nl_errno(ENOENT); +} + +/** + * Set re-order correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New re-ordering correlation probability. + * @return 0 on success or a negative error code. + */ +int rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return nl_errno(ENOMEM); + + netem->qnm_ro.nmro_correlation = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR; + + return 0; +} + +/** + * Get re-ordering correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Re-ordering correlation probability or a negative error code. + */ +int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR)) + return netem->qnm_ro.nmro_correlation; + else + return nl_errno(ENOENT); +} + +/** @} */ + +/** + * @name Packet Loss + * @{ + */ + +/** + * Set packet loss probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet loss probability. + * @return 0 on success or a negative error code. + */ +int rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return nl_errno(ENOMEM); + + netem->qnm_loss = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_LOSS; + + return 0; +} + +/** + * Get packet loss probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet loss probability or a negative error code. + */ +int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS)) + return netem->qnm_loss; + else + return nl_errno(ENOENT); +} + +/** + * Set packet loss correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet loss correlation. + * @return 0 on success or a negative error code. + */ +int rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return nl_errno(ENOMEM); + + netem->qnm_corr.nmc_loss = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR; + + return 0; +} + +/** + * Get packet loss correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet loss correlation probability or a negative error code. + */ +int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR)) + return netem->qnm_corr.nmc_loss; + else + return nl_errno(ENOENT); +} + +/** @} */ + +/** + * @name Packet Duplication + * @{ + */ + +/** + * Set packet duplication probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet duplication probability. + * @return 0 on success or a negative error code. + */ +int rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return nl_errno(ENOMEM); + + netem->qnm_duplicate = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE; + + return 0; +} + +/** + * Get packet duplication probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet duplication probability or a negative error code. + */ +int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE)) + return netem->qnm_duplicate; + else + return nl_errno(ENOENT); +} + +/** + * Set packet duplication correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet duplication correlation probability. + * @return 0 on sucess or a negative error code. + */ +int rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return nl_errno(ENOMEM); + + netem->qnm_corr.nmc_duplicate = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR; + + return 0; +} + +/** + * Get packet duplication correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet duplication correlation probability or a negative error code. + */ +int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR)) + return netem->qnm_corr.nmc_duplicate; + else + return nl_errno(ENOENT); +} + +/** @} */ + +/** + * @name Packet Delay + * @{ + */ + +/** + * Set packet delay of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg delay New packet delay in micro seconds. + * @return 0 on success or a negative error code. + */ +int rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay) +{ + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return nl_errno(ENOMEM); + + netem->qnm_latency = nl_us2ticks(delay); + netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY; + + return 0; +} + +/** + * Get packet delay of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet delay in micro seconds or a negative error code. + */ +int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY)) + return nl_ticks2us(netem->qnm_latency); + else + return nl_errno(ENOENT); +} + +/** + * Set packet delay jitter of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg jitter New packet delay jitter in micro seconds. + * @return 0 on success or a negative error code. + */ +int rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter) +{ + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return nl_errno(ENOMEM); + + netem->qnm_jitter = nl_us2ticks(jitter); + netem->qnm_mask |= SCH_NETEM_ATTR_JITTER; + + return 0; +} + +/** + * Get packet delay jitter of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet delay jitter in micro seconds or a negative error code. + */ +int rtnl_netem_get_jitter(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_JITTER)) + return nl_ticks2us(netem->qnm_jitter); + else + return nl_errno(ENOENT); +} + +/** + * Set packet delay correlation probability of netem qdisc. + * @arg qdisc Netem qdisc to be modified. + * @arg prob New packet delay correlation probability. + */ +int rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob) +{ + struct rtnl_netem *netem; + + netem = netem_alloc(qdisc); + if (!netem) + return nl_errno(ENOMEM); + + netem->qnm_corr.nmc_delay = prob; + netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR; + + return 0; +} + +/** + * Get packet delay correlation probability of netem qdisc. + * @arg qdisc Netem qdisc. + * @return Packet delay correlation probability or a negative error code. + */ +int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *qdisc) +{ + struct rtnl_netem *netem; + + netem = netem_qdisc(qdisc); + if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR)) + return netem->qnm_corr.nmc_delay; + else + return nl_errno(ENOENT); +} + +/** @} */ + +static struct rtnl_qdisc_ops netem_ops = { + .qo_kind = "netem", + .qo_msg_parser = netem_msg_parser, + .qo_free_data = netem_free_data, + .qo_dump[NL_DUMP_BRIEF] = netem_dump_brief, + .qo_dump[NL_DUMP_FULL] = netem_dump_full, + .qo_get_opts = netem_get_opts, +}; + +static void __init netem_init(void) +{ + rtnl_qdisc_register(&netem_ops); +} + +static void __exit netem_exit(void) +{ + rtnl_qdisc_unregister(&netem_ops); +} + +/** @} */ diff --git a/lib/route/sch/prio.c b/lib/route/sch/prio.c new file mode 100644 index 0000000..4e3d624 --- /dev/null +++ b/lib/route/sch/prio.c @@ -0,0 +1,332 @@ +/* + * lib/route/sch/prio.c PRIO Qdisc/Class + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup qdisc_api + * @defgroup prio (Fast) Prio + * @brief + * + * @par 1) Typical PRIO configuration + * @code + * // Specify the maximal number of bands to be used for this PRIO qdisc. + * rtnl_qdisc_prio_set_bands(qdisc, QDISC_PRIO_DEFAULT_BANDS); + * + * // Provide a map assigning each priority to a band number. + * uint8_t map[] = QDISC_PRIO_DEFAULT_PRIOMAP; + * rtnl_qdisc_prio_set_priomap(qdisc, map, sizeof(map)); + * @endcode + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_PRIO_ATTR_BANDS 1 +#define SCH_PRIO_ATTR_PRIOMAP 2 +/** @endcond */ + +static inline struct rtnl_prio *prio_qdisc(struct rtnl_qdisc *qdisc) +{ + return (struct rtnl_prio *) qdisc->q_subdata; +} + +static inline struct rtnl_prio *prio_alloc(struct rtnl_qdisc *qdisc) +{ + if (!qdisc->q_subdata) + qdisc->q_subdata = calloc(1, sizeof(struct rtnl_prio)); + + return prio_qdisc(qdisc); +} + +static int prio_msg_parser(struct rtnl_qdisc *qdisc) +{ + struct rtnl_prio *prio; + struct tc_prio_qopt *opt; + + if (qdisc->q_opts->d_size < sizeof(*opt)) + return nl_error(EINVAL, "prio specific option size mismatch"); + + prio = prio_alloc(qdisc); + if (!prio) + return nl_errno(ENOMEM); + + opt = (struct tc_prio_qopt *) qdisc->q_opts->d_data; + prio->qp_bands = opt->bands; + memcpy(prio->qp_priomap, opt->priomap, sizeof(prio->qp_priomap)); + prio->qp_mask = (SCH_PRIO_ATTR_BANDS | SCH_PRIO_ATTR_PRIOMAP); + + return 0; +} + +static void prio_free_data(struct rtnl_qdisc *qdisc) +{ + free(qdisc->q_subdata); +} + +static int prio_dump_brief(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p, int line) +{ + struct rtnl_prio *prio = prio_qdisc(qdisc); + + if (prio) + dp_dump(p, " bands %u", prio->qp_bands); + + return line; +} + +static int prio_dump_full(struct rtnl_qdisc *qdisc, + struct nl_dump_params *p, int line) +{ + struct rtnl_prio *prio = prio_qdisc(qdisc); + int i, hp; + + if (!prio) + goto ignore; + + dp_dump(p, "priomap ["); + + for (i = 0; i <= TC_PRIO_MAX; i++) + dp_dump(p, "%u%s", prio->qp_priomap[i], + i < TC_PRIO_MAX ? " " : ""); + + dp_dump(p, "]\n"); + dp_new_line(p, line++); + + hp = (((TC_PRIO_MAX/2) + 1) & ~1); + + for (i = 0; i < hp; i++) { + char a[32]; + dp_dump(p, " %18s => %u", + rtnl_prio2str(i, a, sizeof(a)), + prio->qp_priomap[i]); + if (hp+i <= TC_PRIO_MAX) { + dp_dump(p, " %18s => %u", + rtnl_prio2str(hp+i, a, sizeof(a)), + prio->qp_priomap[hp+i]); + if (i < (hp - 1)) { + dp_dump(p, "\n"); + dp_new_line(p, line++); + } + } + } + +ignore: + return line; +} + +static struct nl_msg *prio_get_opts(struct rtnl_qdisc *qdisc) +{ + struct rtnl_prio *prio; + struct tc_prio_qopt opts; + struct nl_msg *msg; + + prio = prio_qdisc(qdisc); + if (!prio || + !(prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP)) + goto errout; + + opts.bands = prio->qp_bands; + memcpy(opts.priomap, prio->qp_priomap, sizeof(opts.priomap)); + + msg = nlmsg_alloc(); + if (!msg) + goto errout; + + if (nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD) < 0) { + nlmsg_free(msg); + goto errout; + } + + return msg; +errout: + return NULL; +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set number of bands of PRIO qdisc. + * @arg qdisc PRIO qdisc to be modified. + * @arg bands New number of bands. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *qdisc, int bands) +{ + struct rtnl_prio *prio; + + prio = prio_alloc(qdisc); + if (!prio) + return nl_errno(ENOMEM); + + prio->qp_bands = bands; + prio->qp_mask |= SCH_PRIO_ATTR_BANDS; + + return 0; +} + +/** + * Get number of bands of PRIO qdisc. + * @arg qdisc PRIO qdisc. + * @return Number of bands or a negative error code. + */ +int rtnl_qdisc_prio_get_bands(struct rtnl_qdisc *qdisc) +{ + struct rtnl_prio *prio; + + prio = prio_qdisc(qdisc); + if (prio && prio->qp_mask & SCH_PRIO_ATTR_BANDS) + return prio->qp_bands; + else + return nl_errno(ENOMEM); +} + +/** + * Set priomap of the PRIO qdisc. + * @arg qdisc PRIO qdisc to be modified. + * @arg priomap New priority mapping. + * @arg len Length of priomap (# of elements). + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[], + int len) +{ + struct rtnl_prio *prio; + int i; + + prio = prio_alloc(qdisc); + if (!prio) + return nl_errno(ENOMEM); + + if (!(prio->qp_mask & SCH_PRIO_ATTR_BANDS)) + return nl_error(EINVAL, "Set number of bands first"); + + if ((len / sizeof(uint8_t)) > (TC_PRIO_MAX+1)) + return nl_error(ERANGE, "priomap length out of bounds"); + + for (i = 0; i <= TC_PRIO_MAX; i++) { + if (priomap[i] > prio->qp_bands) + return nl_error(ERANGE, "priomap element %d " \ + "out of bounds, increase bands number"); + } + + memcpy(prio->qp_priomap, priomap, len); + prio->qp_mask |= SCH_PRIO_ATTR_PRIOMAP; + + return 0; +} + +/** + * Get priomap of a PRIO qdisc. + * @arg qdisc PRIO qdisc. + * @return Priority mapping as array of size TC_PRIO_MAX+1 + * or NULL if an error occured. + */ +uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *qdisc) +{ + struct rtnl_prio *prio; + + prio = prio_qdisc(qdisc); + if (prio && prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP) + return prio->qp_priomap; + else { + nl_errno(ENOENT); + return NULL; + } +} + +/** @} */ + +/** + * @name Priority Band Translations + * @{ + */ + +static struct trans_tbl prios[] = { + __ADD(TC_PRIO_BESTEFFORT,besteffort) + __ADD(TC_PRIO_FILLER,filler) + __ADD(TC_PRIO_BULK,bulk) + __ADD(TC_PRIO_INTERACTIVE_BULK,interactive_bulk) + __ADD(TC_PRIO_INTERACTIVE,interactive) + __ADD(TC_PRIO_CONTROL,control) +}; + +/** + * Convert priority to character string. + * @arg prio Priority. + * @arg buf Destination buffer + * @arg size Size of destination buffer. + * + * Converts a priority to a character string and stores the result in + * the specified destination buffer. + * + * @return Name of priority as character string. + */ +char * rtnl_prio2str(int prio, char *buf, size_t size) +{ + return __type2str(prio, buf, size, prios, ARRAY_SIZE(prios)); +} + +/** + * Convert character string to priority. + * @arg name Name of priority. + * + * Converts the provided character string specifying a priority + * to the corresponding numeric value. + * + * @return Numeric priority or a negative value if no match was found. + */ +int rtnl_str2prio(const char *name) +{ + return __str2type(name, prios, ARRAY_SIZE(prios)); +} + +/** @} */ + +static struct rtnl_qdisc_ops prio_ops = { + .qo_kind = "prio", + .qo_msg_parser = prio_msg_parser, + .qo_free_data = prio_free_data, + .qo_dump[NL_DUMP_BRIEF] = prio_dump_brief, + .qo_dump[NL_DUMP_FULL] = prio_dump_full, + .qo_get_opts = prio_get_opts, +}; + +static struct rtnl_qdisc_ops pfifo_fast_ops = { + .qo_kind = "pfifo_fast", + .qo_msg_parser = prio_msg_parser, + .qo_free_data = prio_free_data, + .qo_dump[NL_DUMP_BRIEF] = prio_dump_brief, + .qo_dump[NL_DUMP_FULL] = prio_dump_full, + .qo_get_opts = prio_get_opts, +}; + +static void __init prio_init(void) +{ + rtnl_qdisc_register(&prio_ops); + rtnl_qdisc_register(&pfifo_fast_ops); +} + +static void __exit prio_exit(void) +{ + rtnl_qdisc_unregister(&prio_ops); + rtnl_qdisc_unregister(&pfifo_fast_ops); +} + +/** @} */ diff --git a/lib/route/sch/red.c b/lib/route/sch/red.c new file mode 100644 index 0000000..a31c358 --- /dev/null +++ b/lib/route/sch/red.c @@ -0,0 +1,219 @@ +/* + * lib/route/sch/red.c RED Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup qdisc_api + * @defgroup red Random Early Detection (RED) + * @brief + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define RED_ATTR_LIMIT 0x01 +#define RED_ATTR_QTH_MIN 0x02 +#define RED_ATTR_QTH_MAX 0x04 +#define RED_ATTR_FLAGS 0x08 +#define RED_ATTR_WLOG 0x10 +#define RED_ATTR_PLOG 0x20 +#define RED_ATTR_SCELL_LOG 0x40 +/** @endcond */ + +static inline struct rtnl_red *red_qdisc(struct rtnl_qdisc *qdisc) +{ + return (struct rtnl_red *) qdisc->q_subdata; +} + +static inline struct rtnl_red *red_alloc(struct rtnl_qdisc *qdisc) +{ + if (!qdisc->q_subdata) + qdisc->q_subdata = calloc(1, sizeof(struct rtnl_red)); + + return red_qdisc(qdisc); +} + +static struct nla_policy red_policy[TCA_RED_MAX+1] = { + [TCA_RED_PARMS] = { .minlen = sizeof(struct tc_red_qopt) }, +}; + +static int red_msg_parser(struct rtnl_qdisc *qdisc) +{ + struct nlattr *tb[TCA_RED_MAX+1]; + struct rtnl_red *red; + struct tc_red_qopt *opts; + int err; + + if (!(qdisc->ce_mask & TCA_ATTR_OPTS)) + return 0; + + err = tca_parse(tb, TCA_RED_MAX, (struct rtnl_tca *) qdisc, red_policy); + if (err < 0) + return err; + + if (!tb[TCA_RED_PARMS]) + return nl_error(EINVAL, "Missing TCA_RED_PARMS"); + + red = red_alloc(qdisc); + if (!red) + return nl_errno(ENOMEM); + + opts = nla_data(tb[TCA_RED_PARMS]); + + red->qr_limit = opts->limit; + red->qr_qth_min = opts->qth_min; + red->qr_qth_max = opts->qth_max; + red->qr_flags = opts->flags; + red->qr_wlog = opts->Wlog; + red->qr_plog = opts->Plog; + red->qr_scell_log = opts->Scell_log; + + red->qr_mask = (RED_ATTR_LIMIT | RED_ATTR_QTH_MIN | RED_ATTR_QTH_MAX | + RED_ATTR_FLAGS | RED_ATTR_WLOG | RED_ATTR_PLOG | + RED_ATTR_SCELL_LOG); + + return 0; +} + +static int red_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, + int line) +{ + struct rtnl_red *red = red_qdisc(qdisc); + + if (red) { + /* XXX: limit, min, max, flags */ + } + + return line; +} + +static int red_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, + int line) +{ + struct rtnl_red *red = red_qdisc(qdisc); + + if (red) { + /* XXX: wlog, plog, scell_log */ + } + + return line; +} + +static int red_dump_stats(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, + int line) +{ + struct rtnl_red *red = red_qdisc(qdisc); + + if (red) { + /* XXX: xstats */ + } + + return line; +} + +static struct nl_msg *red_get_opts(struct rtnl_qdisc *qdisc) +{ + struct rtnl_red *red; + struct nl_msg *msg; + + red = red_qdisc(qdisc); + if (!red) + return NULL; + + msg = nlmsg_alloc(); + if (!msg) + goto errout; + +#if 0 + memset(&opts, 0, sizeof(opts)); + opts.quantum = sfq->qs_quantum; + opts.perturb_period = sfq->qs_perturb; + opts.limit = sfq->qs_limit; + + if (nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD) < 0) + goto errout; +#endif + + return msg; +errout: + nlmsg_free(msg); + return NULL; +} + +/** + * @name Attribute Access + * @{ + */ + +/** + * Set limit of RED qdisc. + * @arg qdisc RED qdisc to be modified. + * @arg limit New limit in number of packets. + * @return 0 on success or a negative error code. + */ +int rtnl_red_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_red *red; + + red = red_alloc(qdisc); + if (!red) + return nl_errno(ENOMEM); + + red->qr_limit = limit; + red->qr_mask |= RED_ATTR_LIMIT; + + return 0; +} + +/** + * Get limit of RED qdisc. + * @arg qdisc RED qdisc. + * @return Limit or a negative error code. + */ +int rtnl_red_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_red *red; + + red = red_qdisc(qdisc); + if (red && (red->qr_mask & RED_ATTR_LIMIT)) + return red->qr_limit; + else + return nl_errno(ENOENT); +} + +/** @} */ + +static struct rtnl_qdisc_ops red_ops = { + .qo_kind = "red", + .qo_msg_parser = red_msg_parser, + .qo_dump[NL_DUMP_BRIEF] = red_dump_brief, + .qo_dump[NL_DUMP_FULL] = red_dump_full, + .qo_dump[NL_DUMP_STATS] = red_dump_stats, + .qo_get_opts = red_get_opts, +}; + +static void __init red_init(void) +{ + rtnl_qdisc_register(&red_ops); +} + +static void __exit red_exit(void) +{ + rtnl_qdisc_unregister(&red_ops); +} + +/** @} */ diff --git a/lib/route/sch/sfq.c b/lib/route/sch/sfq.c new file mode 100644 index 0000000..d530c0f --- /dev/null +++ b/lib/route/sch/sfq.c @@ -0,0 +1,294 @@ +/* + * lib/route/sch/sfq.c SFQ Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup qdisc_api + * @defgroup sfq Stochastic Fairness Queueing (SFQ) + * @brief + * + * @par Parameter Description + * - \b Quantum: Number of bytes to send out per slot and round. + * - \b Perturbation: Timer period between changing the hash function. + * - \b Limit: Upper limit of queue in number of packets before SFQ starts + * dropping packets. + * - \b Divisor: Hash table divisor, i.e. size of hash table. + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define SCH_SFQ_ATTR_QUANTUM 0x01 +#define SCH_SFQ_ATTR_PERTURB 0x02 +#define SCH_SFQ_ATTR_LIMIT 0x04 +#define SCH_SFQ_ATTR_DIVISOR 0x08 +#define SCH_SFQ_ATTR_FLOWS 0x10 +/** @endcond */ + +static inline struct rtnl_sfq *sfq_qdisc(struct rtnl_qdisc *qdisc) +{ + return (struct rtnl_sfq *) qdisc->q_subdata; +} + +static inline struct rtnl_sfq *sfq_alloc(struct rtnl_qdisc *qdisc) +{ + if (!qdisc->q_subdata) + qdisc->q_subdata = calloc(1, sizeof(struct rtnl_sfq)); + + return sfq_qdisc(qdisc); +} + +static int sfq_msg_parser(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + struct tc_sfq_qopt *opts; + + if (!(qdisc->ce_mask & TCA_ATTR_OPTS)) + return 0; + + if (qdisc->q_opts->d_size < sizeof(*opts)) + return nl_error(EINVAL, "SFQ specific options size mismatch"); + + sfq = sfq_alloc(qdisc); + if (!sfq) + return nl_errno(ENOMEM); + + opts = (struct tc_sfq_qopt *) qdisc->q_opts->d_data; + + sfq->qs_quantum = opts->quantum; + sfq->qs_perturb = opts->perturb_period; + sfq->qs_limit = opts->limit; + sfq->qs_divisor = opts->divisor; + sfq->qs_flows = opts->flows; + + sfq->qs_mask = (SCH_SFQ_ATTR_QUANTUM | SCH_SFQ_ATTR_PERTURB | + SCH_SFQ_ATTR_LIMIT | SCH_SFQ_ATTR_DIVISOR | + SCH_SFQ_ATTR_FLOWS); + + return 0; +} + +static void sfq_free_data(struct rtnl_qdisc *qdisc) +{ + free(qdisc->q_subdata); +} + +static int sfq_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, + int line) +{ + struct rtnl_sfq *sfq = sfq_qdisc(qdisc); + + if (sfq) + dp_dump(p, " quantum %u perturb %us", + sfq->qs_quantum, + nl_ticks2us(sfq->qs_perturb * nl_get_hz())); + + return line; +} + +static int sfq_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, + int line) +{ + struct rtnl_sfq *sfq = sfq_qdisc(qdisc); + + if (sfq) + dp_dump(p, "limit %u divisor %u", + sfq->qs_limit, sfq->qs_divisor); + + return line; +} + +static struct nl_msg *sfq_get_opts(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + struct tc_sfq_qopt opts; + struct nl_msg *msg; + + sfq = sfq_qdisc(qdisc); + if (!sfq) + return NULL; + + msg = nlmsg_alloc(); + if (!msg) + goto errout; + + memset(&opts, 0, sizeof(opts)); + opts.quantum = sfq->qs_quantum; + opts.perturb_period = sfq->qs_perturb; + opts.limit = sfq->qs_limit; + + if (nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD) < 0) + goto errout; + + return msg; +errout: + nlmsg_free(msg); + return NULL; +} + +/** + * @name Attribute Access + * @{ + */ + +/** + * Set quantum of SFQ qdisc. + * @arg qdisc SFQ qdisc to be modified. + * @arg quantum New quantum in bytes. + * @return 0 on success or a negative error code. + */ +int rtnl_sfq_set_quantum(struct rtnl_qdisc *qdisc, int quantum) +{ + struct rtnl_sfq *sfq; + + sfq = sfq_alloc(qdisc); + if (!sfq) + return nl_errno(ENOMEM); + + sfq->qs_quantum = quantum; + sfq->qs_mask |= SCH_SFQ_ATTR_QUANTUM; + + return 0; +} + +/** + * Get quantum of SFQ qdisc. + * @arg qdisc SFQ qdisc. + * @return Quantum in bytes or a negative error code. + */ +int rtnl_sfq_get_quantum(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + + sfq = sfq_qdisc(qdisc); + if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_QUANTUM) + return sfq->qs_quantum; + else + return nl_errno(ENOENT); +} + +/** + * Set limit of SFQ qdisc. + * @arg qdisc SFQ qdisc to be modified. + * @arg limit New limit in number of packets. + * @return 0 on success or a negative error code. + */ +int rtnl_sfq_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_sfq *sfq; + + sfq = sfq_alloc(qdisc); + if (!sfq) + return nl_errno(ENOMEM); + + sfq->qs_limit = limit; + sfq->qs_mask |= SCH_SFQ_ATTR_LIMIT; + + return 0; +} + +/** + * Get limit of SFQ qdisc. + * @arg qdisc SFQ qdisc. + * @return Limit or a negative error code. + */ +int rtnl_sfq_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + + sfq = sfq_qdisc(qdisc); + if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_LIMIT) + return sfq->qs_limit; + else + return nl_errno(ENOENT); +} + +/** + * Set perturbation interval of SFQ qdisc. + * @arg qdisc SFQ qdisc to be modified. + * @arg perturb New perturbation interval in seconds. + * @note A value of 0 disables perturbation altogether. + * @return 0 on success or a negative error code. + */ +int rtnl_sfq_set_perturb(struct rtnl_qdisc *qdisc, int perturb) +{ + struct rtnl_sfq *sfq; + + sfq = sfq_alloc(qdisc); + if (!sfq) + return nl_errno(ENOMEM); + + sfq->qs_perturb = perturb; + sfq->qs_mask |= SCH_SFQ_ATTR_PERTURB; + + return 0; +} + +/** + * Get perturbation interval of SFQ qdisc. + * @arg qdisc SFQ qdisc. + * @return Perturbation interval in seconds or a negative error code. + */ +int rtnl_sfq_get_perturb(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + + sfq = sfq_qdisc(qdisc); + if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_PERTURB) + return sfq->qs_perturb; + else + return nl_errno(ENOENT); +} + +/** + * Get divisor of SFQ qdisc. + * @arg qdisc SFQ qdisc. + * @return Divisor in number of entries or a negative error code. + */ +int rtnl_sfq_get_divisor(struct rtnl_qdisc *qdisc) +{ + struct rtnl_sfq *sfq; + + sfq = sfq_qdisc(qdisc); + if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_DIVISOR) + return sfq->qs_divisor; + else + return nl_errno(ENOENT); +} + +/** @} */ + +static struct rtnl_qdisc_ops sfq_ops = { + .qo_kind = "sfq", + .qo_msg_parser = sfq_msg_parser, + .qo_free_data = sfq_free_data, + .qo_dump[NL_DUMP_BRIEF] = sfq_dump_brief, + .qo_dump[NL_DUMP_FULL] = sfq_dump_full, + .qo_get_opts = sfq_get_opts, +}; + +static void __init sfq_init(void) +{ + rtnl_qdisc_register(&sfq_ops); +} + +static void __exit sfq_exit(void) +{ + rtnl_qdisc_unregister(&sfq_ops); +} + +/** @} */ diff --git a/lib/route/sch/tbf.c b/lib/route/sch/tbf.c new file mode 100644 index 0000000..04d1689 --- /dev/null +++ b/lib/route/sch/tbf.c @@ -0,0 +1,540 @@ +/* + * lib/route/sch/tbf.c TBF Qdisc + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup qdisc_api + * @defgroup tbf Token Bucket Filter (TBF) + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define TBF_ATTR_LIMIT 0x01 +#define TBF_ATTR_RATE 0x02 +#define TBF_ATTR_PEAKRATE 0x10 +#define TBF_ATTR_MPU 0x80 +/** @endcond */ + +static inline struct rtnl_tbf *tbf_qdisc(struct rtnl_qdisc *qdisc) +{ + return (struct rtnl_tbf *) qdisc->q_subdata; +} + +static inline struct rtnl_tbf *tbf_alloc(struct rtnl_qdisc *qdisc) +{ + if (!qdisc->q_subdata) + qdisc->q_subdata = calloc(1, sizeof(struct rtnl_tbf)); + + return tbf_qdisc(qdisc); +} + +static struct nla_policy tbf_policy[TCA_TBF_MAX+1] = { + [TCA_TBF_PARMS] = { .minlen = sizeof(struct tc_tbf_qopt) }, +}; + +static int tbf_msg_parser(struct rtnl_qdisc *q) +{ + int err; + struct nlattr *tb[TCA_TBF_MAX + 1]; + struct rtnl_tbf *tbf; + + err = tca_parse(tb, TCA_TBF_MAX, (struct rtnl_tca *) q, tbf_policy); + if (err < 0) + return err; + + tbf = tbf_qdisc(q); + if (!tbf) + return nl_errno(ENOMEM); + + if (tb[TCA_TBF_PARMS]) { + struct tc_tbf_qopt opts; + int bufsize; + + nla_memcpy(&opts, tb[TCA_TBF_PARMS], sizeof(opts)); + tbf->qt_limit = opts.limit; + tbf->qt_mpu = opts.rate.mpu; + + rtnl_copy_ratespec(&tbf->qt_rate, &opts.rate); + tbf->qt_rate_txtime = opts.buffer; + bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer), + opts.rate.rate); + tbf->qt_rate_bucket = bufsize; + + rtnl_copy_ratespec(&tbf->qt_peakrate, &opts.peakrate); + tbf->qt_peakrate_txtime = opts.mtu; + bufsize = rtnl_tc_calc_bufsize(nl_ticks2us(opts.mtu), + opts.peakrate.rate); + tbf->qt_peakrate_bucket = bufsize; + + tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_MPU | TBF_ATTR_RATE | + TBF_ATTR_PEAKRATE); + } + + return 0; +} + +static int tbf_dump_brief(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, + int line) +{ + double r, rbit, lim; + char *ru, *rubit, *limu; + struct rtnl_tbf *tbf = tbf_qdisc(qdisc); + + if (!tbf) + goto ignore; + + r = nl_cancel_down_bytes(tbf->qt_rate.rs_rate, &ru); + rbit = nl_cancel_down_bits(tbf->qt_rate.rs_rate*8, &rubit); + lim = nl_cancel_down_bytes(tbf->qt_limit, &limu); + + dp_dump(p, " rate %.2f%s/s (%.0f%s) limit %.2f%s", + r, ru, rbit, rubit, lim, limu); + +ignore: + return line; +} + +static int tbf_dump_full(struct rtnl_qdisc *qdisc, struct nl_dump_params *p, + int line) +{ + struct rtnl_tbf *tbf = tbf_qdisc(qdisc); + + if (!tbf) + goto ignore; + + if (1) { + char *bu, *cu; + double bs = nl_cancel_down_bytes(tbf->qt_rate_bucket, &bu); + double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log, + &cu); + + dp_dump(p, "mpu %u rate-bucket-size %1.f%s " + "rate-cell-size %.1f%s\n", + tbf->qt_mpu, bs, bu, cl, cu); + + } + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { + char *pru, *prbu, *bsu, *clu; + double pr, prb, bs, cl; + + pr = nl_cancel_down_bytes(tbf->qt_peakrate.rs_rate, &pru); + prb = nl_cancel_down_bits(tbf->qt_peakrate.rs_rate * 8, &prbu); + bs = nl_cancel_down_bits(tbf->qt_peakrate_bucket, &bsu); + cl = nl_cancel_down_bits(1 << tbf->qt_peakrate.rs_cell_log, + &clu); + + dp_dump_line(p, line++, " peak-rate %.2f%s/s (%.0f%s) " + "bucket-size %.1f%s cell-size %.1f%s", + "latency %.1f%s", + pr, pru, prb, prbu, bs, bsu, cl, clu); + } + +ignore: + return line; +} + +static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc) +{ + struct tc_tbf_qopt opts; + struct rtnl_tbf *tbf; + struct nl_msg *msg; + uint32_t rtab[RTNL_TC_RTABLE_SIZE]; + uint32_t ptab[RTNL_TC_RTABLE_SIZE]; + int required = TBF_ATTR_RATE | TBF_ATTR_LIMIT; + + memset(&opts, 0, sizeof(opts)); + + tbf = tbf_qdisc(qdisc); + if (!tbf) + return NULL; + + if (!(tbf->qt_mask & required) != required) + return NULL; + + opts.limit = tbf->qt_limit; + opts.buffer = tbf->qt_rate_txtime; + tbf->qt_rate.rs_mpu = tbf->qt_mpu; + rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate); + + rtnl_tc_build_rate_table(rtab, tbf->qt_mpu & 0xff, tbf->qt_mpu >> 8, + 1 << tbf->qt_rate.rs_cell_log, + tbf->qt_rate.rs_rate); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { + opts.mtu = tbf->qt_peakrate_txtime; + tbf->qt_peakrate.rs_mpu = tbf->qt_mpu; + rtnl_rcopy_ratespec(&opts.peakrate, &tbf->qt_peakrate); + + rtnl_tc_build_rate_table(ptab, tbf->qt_mpu & 0xff, + tbf->qt_mpu >> 8, + 1 << tbf->qt_peakrate.rs_cell_log, + tbf->qt_peakrate.rs_rate); + } + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + NLA_PUT(msg, TCA_TBF_PARMS, sizeof(opts), &opts); + NLA_PUT(msg, TCA_TBF_RTAB, sizeof(rtab), rtab); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) + NLA_PUT(msg, TCA_TBF_PTAB, sizeof(ptab), ptab); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +/** + * @name Attribute Access + * @{ + */ + +/** + * Set limit of TBF qdisc. + * @arg qdisc TBF qdisc to be modified. + * @arg limit New limit in bytes. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit) +{ + struct rtnl_tbf *tbf; + + tbf = tbf_alloc(qdisc); + if (!tbf) + return nl_errno(ENOMEM); + + tbf->qt_limit = limit; + tbf->qt_mask |= TBF_ATTR_LIMIT; + + return 0; +} + +static inline double calc_limit(struct rtnl_ratespec *spec, int latency, + int bucket) +{ + double limit; + + limit = (double) spec->rs_rate * ((double) latency / 1000000.); + limit += bucket; + + return limit; +} + +/** + * Set limit of TBF qdisc by latency. + * @arg qdisc TBF qdisc to be modified. + * @arg latency Latency in micro seconds. + * + * Calculates and sets the limit based on the desired latency and the + * configured rate and peak rate. In order for this operation to succeed, + * the rate and if required the peak rate must have been set in advance. + * + * @f[ + * limit_n = \frac{{rate_n} \times {latency}}{10^6}+{bucketsize}_n + * @f] + * @f[ + * limit = min(limit_{rate},limit_{peak}) + * @f] + * + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency) +{ + struct rtnl_tbf *tbf; + double limit, limit2; + + tbf = tbf_alloc(qdisc); + if (!tbf) + return nl_errno(ENOMEM); + + if (!(tbf->qt_mask & TBF_ATTR_RATE)) + return nl_error(EINVAL, "The rate must be specified before " + "limit can be calculated based on latency."); + + limit = calc_limit(&tbf->qt_rate, latency, tbf->qt_rate_bucket); + + if (tbf->qt_mask & TBF_ATTR_PEAKRATE) { + limit2 = calc_limit(&tbf->qt_peakrate, latency, + tbf->qt_peakrate_bucket); + + if (limit2 < limit) + limit = limit2; + } + + return rtnl_qdisc_tbf_set_limit(qdisc, (int) limit); +} + +/** + * Get limit of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Limit in bytes or a negative error code. + */ +int rtnl_qdisc_tbf_get_limit(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + tbf = tbf_qdisc(qdisc); + if (tbf && (tbf->qt_mask & TBF_ATTR_LIMIT)) + return tbf->qt_limit; + return + nl_errno(ENOENT); +} + +/** + * Set MPU of TBF qdisc. + * @arg qdisc TBF qdisc to be modified. + * @arg mpu New MPU in bytes. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_tbf_set_mpu(struct rtnl_qdisc *qdisc, int mpu) +{ + struct rtnl_tbf *tbf; + + tbf = tbf_alloc(qdisc); + if (!tbf) + return nl_errno(ENOMEM); + + tbf->qt_mpu = mpu; + tbf->qt_mask |= TBF_ATTR_MPU; + + return 0; +} + +/** + * Get MPU of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return MPU in bytes or a negative error code. + */ +int rtnl_qdisc_tbf_get_mpu(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + tbf = tbf_qdisc(qdisc); + if (tbf && (tbf->qt_mask & TBF_ATTR_MPU)) + return tbf->qt_mpu; + return + nl_errno(ENOENT); +} + +static inline int calc_cell_log(int cell, int bucket) +{ + if (cell > 0) + cell = rtnl_tc_calc_cell_log(cell); + else { + cell = 0; + + if (!bucket) + bucket = 2047; /* defaults to cell_log=3 */ + + while ((bucket >> cell) > 255) + cell++; + } + + return cell; +} + +/** + * Set rate of TBF qdisc. + * @arg qdisc TBF qdisc to be modified. + * @arg rate New rate in bytes per second. + * @arg bucket Size of bucket in bytes. + * @arg cell Size of a rate cell or 0 to get default value. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_tbf_set_rate(struct rtnl_qdisc *qdisc, int rate, int bucket, + int cell) +{ + struct rtnl_tbf *tbf; + int cell_log; + + tbf = tbf_alloc(qdisc); + if (!tbf) + return nl_errno(ENOMEM); + + cell_log = calc_cell_log(cell, bucket); + if (cell_log < 0) + return cell_log; + + tbf->qt_rate.rs_rate = rate; + tbf->qt_rate_bucket = bucket; + tbf->qt_rate.rs_cell_log = cell_log; + tbf->qt_rate_txtime = rtnl_tc_calc_txtime(bucket, rate); + tbf->qt_mask |= TBF_ATTR_RATE; + + return 0; +} + +/** + * Get rate of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Rate in bytes per seconds or a negative error code. + */ +int rtnl_qdisc_tbf_get_rate(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + tbf = tbf_qdisc(qdisc); + if (tbf && (tbf->qt_mask & TBF_ATTR_RATE)) + return tbf->qt_rate.rs_rate; + else + return -1; +} + +/** + * Get rate bucket size of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Size of rate bucket or a negative error code. + */ +int rtnl_qdisc_tbf_get_rate_bucket(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + tbf = tbf_qdisc(qdisc); + if (tbf && (tbf->qt_mask & TBF_ATTR_RATE)) + return tbf->qt_rate_bucket; + else + return -1; +} + +/** + * Get rate cell size of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Size of rate cell in bytes or a negative error code. + */ +int rtnl_qdisc_tbf_get_rate_cell(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + tbf = tbf_qdisc(qdisc); + if (tbf && (tbf->qt_mask & TBF_ATTR_RATE)) + return (1 << tbf->qt_rate.rs_cell_log); + else + return -1; +} + +/** + * Set peak rate of TBF qdisc. + * @arg qdisc TBF qdisc to be modified. + * @arg rate New peak rate in bytes per second. + * @arg bucket Size of peakrate bucket. + * @arg cell Size of a peakrate cell or 0 to get default value. + * @return 0 on success or a negative error code. + */ +int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket, + int cell) +{ + struct rtnl_tbf *tbf; + int cell_log; + + tbf = tbf_alloc(qdisc); + if (!tbf) + return nl_errno(ENOMEM); + + cell_log = calc_cell_log(cell, bucket); + if (cell_log < 0) + return cell_log; + + tbf->qt_peakrate.rs_rate = rate; + tbf->qt_peakrate_bucket = bucket; + tbf->qt_peakrate.rs_cell_log = cell_log; + tbf->qt_peakrate_txtime = rtnl_tc_calc_txtime(bucket, rate); + + tbf->qt_mask |= TBF_ATTR_PEAKRATE; + + return 0; +} + +/** + * Get peak rate of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Peak rate in bytes per seconds or a negative error code. + */ +int rtnl_qdisc_tbf_get_peakrate(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + tbf = tbf_qdisc(qdisc); + if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE)) + return tbf->qt_peakrate.rs_rate; + else + return -1; +} + +/** + * Get peak rate bucket size of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Size of peak rate bucket or a negative error code. + */ +int rtnl_qdisc_tbf_get_peakrate_bucket(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + tbf = tbf_qdisc(qdisc); + if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE)) + return tbf->qt_peakrate_bucket; + else + return -1; +} + +/** + * Get peak rate cell size of TBF qdisc. + * @arg qdisc TBF qdisc. + * @return Size of peak rate cell in bytes or a negative error code. + */ +int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc) +{ + struct rtnl_tbf *tbf; + + tbf = tbf_qdisc(qdisc); + if (tbf && (tbf->qt_mask & TBF_ATTR_PEAKRATE)) + return (1 << tbf->qt_peakrate.rs_cell_log); + else + return -1; +} + +/** @} */ + +static struct rtnl_qdisc_ops tbf_qdisc_ops = { + .qo_kind = "tbf", + .qo_msg_parser = tbf_msg_parser, + .qo_dump[NL_DUMP_BRIEF] = tbf_dump_brief, + .qo_dump[NL_DUMP_FULL] = tbf_dump_full, + .qo_get_opts = tbf_get_opts, +}; + +static void __init tbf_init(void) +{ + rtnl_qdisc_register(&tbf_qdisc_ops); +} + +static void __exit tbf_exit(void) +{ + rtnl_qdisc_unregister(&tbf_qdisc_ops); +} + +/** @} */ diff --git a/lib/route/tc.c b/lib/route/tc.c new file mode 100644 index 0000000..1351fa2 --- /dev/null +++ b/lib/route/tc.c @@ -0,0 +1,585 @@ +/* + * lib/route/tc.c Traffic Control + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup rtnl + * @defgroup tc Traffic Control + * @brief + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ + +static struct nla_policy tc_policy[TCA_MAX+1] = { + [TCA_KIND] = { .type = NLA_STRING, + .maxlen = TCKINDSIZ }, + [TCA_STATS] = { .minlen = sizeof(struct tc_stats) }, + [TCA_STATS2] = { .type = NLA_NESTED }, +}; + +int tca_parse(struct nlattr **tb, int maxattr, struct rtnl_tca *g, + struct nla_policy *policy) +{ + + if (g->ce_mask & TCA_ATTR_OPTS) + return nla_parse(tb, maxattr, + (struct nlattr *) g->tc_opts->d_data, + g->tc_opts->d_size, policy); + else { + /* Ugly but tb[] must be in a defined state even if no + * attributes can be found. */ + memset(tb, 0, sizeof(struct nlattr *) * (maxattr + 1)); + return 0; + } +} + +static struct nla_policy tc_stats2_policy[TCA_STATS_MAX+1] = { + [TCA_STATS_BASIC] = { .minlen = sizeof(struct gnet_stats_basic) }, + [TCA_STATS_RATE_EST] = { .minlen = sizeof(struct gnet_stats_rate_est) }, + [TCA_STATS_QUEUE] = { .minlen = sizeof(struct gnet_stats_queue) }, +}; + +int tca_msg_parser(struct nlmsghdr *n, struct rtnl_tca *g) +{ + struct nlattr *tb[TCA_MAX + 1]; + struct tcmsg *tm; + int err; + + err = nlmsg_parse(n, sizeof(*tm), tb, TCA_MAX, tc_policy); + if (err < 0) + return err; + + if (tb[TCA_KIND] == NULL) + return nl_error(EINVAL, "Missing tca kind TLV"); + + nla_strlcpy(g->tc_kind, tb[TCA_KIND], TCKINDSIZ); + + tm = nlmsg_data(n); + g->tc_family = tm->tcm_family; + g->tc_ifindex = tm->tcm_ifindex; + g->tc_handle = tm->tcm_handle; + g->tc_parent = tm->tcm_parent; + g->tc_info = tm->tcm_info; + + g->ce_mask = (TCA_ATTR_FAMILY | TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE | + TCA_ATTR_PARENT | TCA_ATTR_INFO | TCA_ATTR_KIND); + + if (tb[TCA_OPTIONS]) { + g->tc_opts = nla_get_data(tb[TCA_OPTIONS]); + if (!g->tc_opts) + return nl_errno(ENOMEM); + g->ce_mask |= TCA_ATTR_OPTS; + } + + + if (tb[TCA_STATS2]) { + struct nlattr *tbs[TCA_STATS_MAX + 1]; + + err = nla_parse_nested(tbs, TCA_STATS_MAX, tb[TCA_STATS2], + tc_stats2_policy); + if (err < 0) + return err; + + if (tbs[TCA_STATS_BASIC]) { + struct gnet_stats_basic *bs; + + bs = nla_data(tbs[TCA_STATS_BASIC]); + g->tc_stats[RTNL_TC_BYTES] = bs->bytes; + g->tc_stats[RTNL_TC_PACKETS] = bs->packets; + } + + if (tbs[TCA_STATS_RATE_EST]) { + struct gnet_stats_rate_est *re; + + re = nla_data(tbs[TCA_STATS_RATE_EST]); + g->tc_stats[RTNL_TC_RATE_BPS] = re->bps; + g->tc_stats[RTNL_TC_RATE_PPS] = re->pps; + } + + if (tbs[TCA_STATS_QUEUE]) { + struct gnet_stats_queue *q; + + q = nla_data(tbs[TCA_STATS_QUEUE]); + g->tc_stats[RTNL_TC_QLEN] = q->qlen; + g->tc_stats[RTNL_TC_BACKLOG] = q->backlog; + g->tc_stats[RTNL_TC_DROPS] = q->drops; + g->tc_stats[RTNL_TC_REQUEUES] = q->requeues; + g->tc_stats[RTNL_TC_OVERLIMITS] = q->overlimits; + } + + g->ce_mask |= TCA_ATTR_STATS; + + if (tbs[TCA_STATS_APP]) { + g->tc_xstats = nla_get_data(tbs[TCA_STATS_APP]); + if (g->tc_xstats == NULL) + return -ENOMEM; + } else + goto compat_xstats; + } else { + if (tb[TCA_STATS]) { + struct tc_stats *st = nla_data(tb[TCA_STATS]); + + g->tc_stats[RTNL_TC_BYTES] = st->bytes; + g->tc_stats[RTNL_TC_PACKETS] = st->packets; + g->tc_stats[RTNL_TC_RATE_BPS] = st->bps; + g->tc_stats[RTNL_TC_RATE_PPS] = st->pps; + g->tc_stats[RTNL_TC_QLEN] = st->qlen; + g->tc_stats[RTNL_TC_BACKLOG] = st->backlog; + g->tc_stats[RTNL_TC_DROPS] = st->drops; + g->tc_stats[RTNL_TC_OVERLIMITS] = st->overlimits; + + g->ce_mask |= TCA_ATTR_STATS; + } + +compat_xstats: + if (tb[TCA_XSTATS]) { + g->tc_xstats = nla_get_data(tb[TCA_XSTATS]); + if (g->tc_xstats == NULL) + return -ENOMEM; + g->ce_mask |= TCA_ATTR_XSTATS; + } + } + + + return 0; +} + +void tca_free_data(struct rtnl_tca *tca) +{ + nl_data_free(tca->tc_opts); + nl_data_free(tca->tc_xstats); +} + +int tca_clone(struct rtnl_tca *dst, struct rtnl_tca *src) +{ + if (src->tc_opts) { + dst->tc_opts = nl_data_clone(src->tc_opts); + if (!dst->tc_opts) + goto errout; + } + + if (src->tc_xstats) { + dst->tc_xstats = nl_data_clone(src->tc_xstats); + if (!dst->tc_xstats) + goto errout; + } + + return 0; +errout: + return nl_get_errno(); +} + +int tca_dump_brief(struct rtnl_tca *g, const char *type, + struct nl_dump_params *p, int line) +{ + char handle[32], parent[32]; + struct nl_cache *link_cache; + + link_cache = nl_cache_mngt_require("route/link"); + + dp_dump(p, "%s %s ", g->tc_kind, type); + + if (link_cache) { + char buf[32]; + dp_dump(p, "dev %s ", + rtnl_link_i2name(link_cache, g->tc_ifindex, + buf, sizeof(buf))); + } else + dp_dump(p, "dev %u ", g->tc_ifindex); + + dp_dump(p, "handle %s parent %s", + rtnl_tc_handle2str(g->tc_handle, handle, sizeof(handle)), + rtnl_tc_handle2str(g->tc_parent, parent, sizeof(parent))); + + return 1; +} + +int tca_dump_full(struct rtnl_tca *g, struct nl_dump_params *p, int line) +{ + dp_dump_line(p, line++, " "); + return line; +} + +int tca_dump_stats(struct rtnl_tca *g, struct nl_dump_params *p, int line) +{ + char *unit, fmt[64]; + float res; + strcpy(fmt, " %7.2f %s %10u %10u %10u %10u %10u\n"); + + dp_dump_line(p, line++, + " Stats: bytes packets drops overlimits" \ + " qlen backlog\n"); + + res = nl_cancel_down_bytes(g->tc_stats[RTNL_TC_BYTES], &unit); + if (*unit == 'B') + fmt[11] = '9'; + + dp_dump_line(p, line++, fmt, res, unit, + g->tc_stats[RTNL_TC_PACKETS], + g->tc_stats[RTNL_TC_DROPS], + g->tc_stats[RTNL_TC_OVERLIMITS], + g->tc_stats[RTNL_TC_QLEN], + g->tc_stats[RTNL_TC_BACKLOG]); + + res = nl_cancel_down_bytes(g->tc_stats[RTNL_TC_RATE_BPS], &unit); + + strcpy(fmt, " %7.2f %s/s%9u pps"); + + if (*unit == 'B') + fmt[11] = '9'; + + dp_dump_line(p, line++, fmt, res, unit, g->tc_stats[RTNL_TC_RATE_PPS]); + + return line; +} + +int tca_compare(struct nl_object *_a, struct nl_object *_b, + uint32_t attrs, int flags) +{ + struct rtnl_tca *a = (struct rtnl_tca *) _a; + struct rtnl_tca *b = (struct rtnl_tca *) _b; + int diff = 0; + +#define TC_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, TCA_ATTR_##ATTR, a, b, EXPR) + + diff |= TC_DIFF(HANDLE, a->tc_handle != b->tc_handle); + diff |= TC_DIFF(PARENT, a->tc_parent != b->tc_parent); + diff |= TC_DIFF(IFINDEX, a->tc_ifindex != b->tc_ifindex); + diff |= TC_DIFF(KIND, strcmp(a->tc_kind, b->tc_kind)); + +#undef TC_DIFF + + return diff; +} + +void tca_set_ifindex(struct rtnl_tca *t, int ifindex) +{ + t->tc_ifindex = ifindex; + t->ce_mask |= TCA_ATTR_IFINDEX; +} + +int tca_get_ifindex(struct rtnl_tca *t) +{ + if (t->ce_mask & TCA_ATTR_IFINDEX) + return t->tc_ifindex; + else + return RTNL_LINK_NOT_FOUND; +} + +void tca_set_handle(struct rtnl_tca *t, uint32_t handle) +{ + t->tc_handle = handle; + t->ce_mask |= TCA_ATTR_HANDLE; +} + +uint32_t tca_get_handle(struct rtnl_tca *t) +{ + if (t->ce_mask & TCA_ATTR_HANDLE) + return t->tc_handle; + else + return 0; +} + +void tca_set_parent(struct rtnl_tca *t, uint32_t parent) +{ + t->tc_parent = parent; + t->ce_mask |= TCA_ATTR_PARENT; +} + +uint32_t tca_get_parent(struct rtnl_tca *t) +{ + if (t->ce_mask & TCA_ATTR_PARENT) + return t->tc_parent; + else + return 0; +} + +void tca_set_kind(struct rtnl_tca *t, const char *kind) +{ + strncpy(t->tc_kind, kind, sizeof(t->tc_kind) - 1); + t->ce_mask |= TCA_ATTR_KIND; +} + +char *tca_get_kind(struct rtnl_tca *t) +{ + if (t->ce_mask & TCA_ATTR_KIND) + return t->tc_kind; + else + return NULL; +} + +uint64_t tca_get_stat(struct rtnl_tca *t, int id) +{ + if (id < 0 || id > RTNL_TC_STATS_MAX) + return 0; + + return t->tc_stats[id]; +} + +struct nl_msg *tca_build_msg(struct rtnl_tca *tca, int type, int flags) +{ + struct nl_msg *msg; + struct tcmsg tchdr = { + .tcm_family = AF_UNSPEC, + .tcm_ifindex = tca->tc_ifindex, + .tcm_handle = tca->tc_handle, + .tcm_parent = tca->tc_parent, + }; + + msg = nlmsg_alloc_simple(type, flags); + if (!msg) + goto nla_put_failure; + + if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) + goto nla_put_failure; + + if (tca->ce_mask & TCA_ATTR_KIND) + NLA_PUT_STRING(msg, TCA_KIND, tca->tc_kind); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +/** @endcond */ + +/** + * @name Utilities + * @{ + */ + +/** + * Calculate time required to transmit buffer at a specific rate + * @arg bufsize Size of buffer to be transmited in bytes. + * @arg rate Transmit rate in bytes per second. + * + * Calculates the number of micro seconds required to transmit a + * specific buffer at a specific transmit rate. + * + * @f[ + * txtime=\frac{bufsize}{rate}10^6 + * @f] + * + * @return Required transmit time in micro seconds. + */ +int rtnl_tc_calc_txtime(int bufsize, int rate) +{ + double tx_time_secs; + + tx_time_secs = (double) bufsize / (double) rate; + + return tx_time_secs * 1000000.; +} + +/** + * Calculate buffer size able to transmit in a specific time and rate. + * @arg txtime Available transmit time in micro seconds. + * @arg rate Transmit rate in bytes per second. + * + * Calculates the size of the buffer that can be transmitted in a + * specific time period at a specific transmit rate. + * + * @f[ + * bufsize=\frac{{txtime} \times {rate}}{10^6} + * @f] + * + * @return Size of buffer in bytes. + */ +int rtnl_tc_calc_bufsize(int txtime, int rate) +{ + double bufsize; + + bufsize = (double) txtime * (double) rate; + + return bufsize / 1000000.; +} + +/** + * Calculate the binary logarithm for a specific cell size + * @arg cell_size Size of cell, must be a power of two. + * @return Binary logirhtm of cell size or a negative error code. + */ +int rtnl_tc_calc_cell_log(int cell_size) +{ + int i; + + for (i = 0; i < 32; i++) + if ((1 << i) == cell_size) + return i; + + return nl_errno(EINVAL); +} + + +/** @} */ + +/** + * @name Rate Tables + * @{ + */ + +/** + * Compute a transmission time lookup table + * @arg dst Destination buffer of RTNL_TC_RTABLE_SIZE uint32_t[]. + * @arg mpu Minimal size of a packet at all times. + * @arg overhead Overhead to be added to each packet. + * @arg cell Size of cell, i.e. size of step between entries in bytes. + * @arg rate Rate in bytes per second. + * + * Computes a table of RTNL_TC_RTABLE_SIZE entries specyfing the + * transmission times for various packet sizes, e.g. the transmission + * time for a packet of size \c pktsize could be looked up: + * @code + * txtime = table[pktsize >> log2(cell)]; + * @endcode + */ +int rtnl_tc_build_rate_table(uint32_t *dst, uint8_t mpu, uint8_t overhead, + int cell, int rate) +{ + int i, size, cell_log; + + cell_log = rtnl_tc_calc_cell_log(cell); + if (cell_log < 0) + return cell_log; + + for (i = 0; i < RTNL_TC_RTABLE_SIZE; i++) { + size = (i << cell_log) + overhead; + if (size < mpu) + size = mpu; + + dst[i] = rtnl_tc_calc_txtime(size, rate); + } + + return 0; +} + +/** @} */ + +/** + * @name Traffic Control Handle Translations + * @{ + */ + +/** + * Convert a traffic control handle to a character string (Reentrant). + * @arg handle traffic control handle + * @arg buf destination buffer + * @arg len buffer length + * + * Converts a tarffic control handle to a character string in the + * form of \c MAJ:MIN and stores it in the specified destination buffer. + * + * @return The destination buffer or the type encoded in hexidecimal + * form if no match was found. + */ +char * rtnl_tc_handle2str(uint32_t handle, char *buf, size_t len) +{ + if (TC_H_ROOT == handle) + snprintf(buf, len, "root"); + else if (TC_H_UNSPEC == handle) + snprintf(buf, len, "none"); + else if (0 == TC_H_MAJ(handle)) + snprintf(buf, len, ":%02x", TC_H_MIN(handle)); + else if (0 == TC_H_MIN(handle)) + snprintf(buf, len, "%02x:", TC_H_MAJ(handle) >> 16); + else + snprintf(buf, len, "%02x:%02x", + TC_H_MAJ(handle) >> 16, TC_H_MIN(handle)); + + return buf; +} + +/** + * Convert a charactering strint to a traffic control handle + * @arg name traffic control handle as character string + * @arg res destination buffer + * + * Converts the provided character string specifying a traffic + * control handle to the corresponding numeric value. + * + * The handle must be provided in one of the following formats: + * - root + * - none + * - XXXX: + * - :YYYY + * - XXXX:YYYY + * - XXXXYYYY + * + * @return 0 on success or a negative error code + */ +int rtnl_tc_str2handle(const char *name, uint32_t *res) +{ + char *colon, *end; + uint32_t h; + + if (!strcasecmp(name, "root")) { + *res = TC_H_ROOT; + return 0; + } + + if (!strcasecmp(name, "none")) { + *res = TC_H_UNSPEC; + return 0; + } + + h = strtoul(name, &colon, 16); + + if (colon == name) { + /* :YYYY */ + h = 0; + if (':' != *colon) + return -EINVAL; + } + + if (':' == *colon) { + /* check if we would lose bits */ + if (TC_H_MAJ(h)) + return -ERANGE; + h <<= 16; + + if ('\0' == colon[1]) { + /* XXXX: */ + *res = h; + } else { + /* XXXX:YYYY */ + uint32_t l = strtoul(colon+1, &end, 16); + + /* check if we overlap with major part */ + if (TC_H_MAJ(l)) + return -ERANGE; + + if ('\0' != *end) + return -EINVAL; + + *res = (h | l); + } + } else if ('\0' == *colon) { + /* XXXXYYYY */ + *res = h; + } else + return -EINVAL; + + return 0; +} + +/** @} */ + +/** @} */ diff --git a/lib/socket.c b/lib/socket.c new file mode 100644 index 0000000..f68e8cf --- /dev/null +++ b/lib/socket.c @@ -0,0 +1,548 @@ +/* + * lib/socket.c Netlink Socket Handle + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @ingroup nl + * @defgroup socket Socket + * @brief Handle representing a netlink socket. + * + * The socket is represented in a structure called the netlink handle, + * besides the socket, it stores various settings and values related + * to the socket. Every socket handle has a mandatory association with + * a set of callbacks which can be used to modify the behaviour when + * sending/receiving data from the socket. + * + * @par Socket Attributes + * - \b Local \b Port: The local port is a netlink port identifying the + * local endpoint. It is used as source address for outgoing messages + * and will be addressed in replies. It must therefore be unique among + * all userspace applications. When the socket handle is allocated, a + * unique port number is generated automatically in the form of 22 bits + * Process Identifier + 10 bits Arbitary Number. Therefore the library + * is capable of generating 1024 unique local port numbers for every + * process. If more sockets are required, the application has to manage + * port numbers itself using nl_socket_set_local_port(). + * - \b Group \b Subscriptions: A socket can subscribe to any number of + * multicast groups. It will then receive a copy of all messages sent + * to one of the groups. This method is mainly used for event notification. + * Prior to kernel 2.6.14, the group subscription was done via bitmask + * which limited to a total number of groups of 32. With 2.6.14 a new + * method was added based on continous identifiers which supports an + * arbitary number of groups. Both methods are supported, see + * nl_join_groups() respectively nl_socket_add_membership() and + * nl_socket_drop_membership(). + * - \b Peer \b Port: The peer port is a netlink port identifying the + * peer's endpoint. If no peer port is specified, the kernel will try to + * autobind to a socket of the specified netlink family automatically. + * This is very common as typically only one listening socket exists + * on the kernel side. The peer port can be modified using + * nl_socket_set_peer_port(). + * - \b Peer \b Groups: + * - \b File \b Descriptor: The file descriptor of the socket, it can be + * accessed via nl_socket_get_fd() to change socket options or monitor + * activity using poll()/select(). + * - \b Protocol: Once connected, the socket is bound to stick to one + * netlink family. This field is invisible, it is maintained automatically. + * (See nl_connect()) + * - \b Next \b Sequence \b Number: Next available sequence number to be used + * for the next message being sent out. (Initial value: UNIX time when the + * socket was allocated.) Sequence numbers can be used via + * nl_socket_use_seq(). + * - \b Expected \b Sequence \b Number: Expected sequence number in the next + * message received from the socket. (Initial value: Equal to next sequence + * number.) + * - \b Callbacks \b Configuration: + * + * @par 1) Creating the netlink handle + * @code + * struct nl_handle *handle; + * + * // Allocate and initialize a new netlink handle + * handle = nl_handle_alloc(); + * + * // Use nl_socket_get_fd() to fetch the file description, for example to + * // put a socket into non-blocking i/o mode. + * fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK); + * @endcode + * + * @par 2) Group Subscriptions + * @code + * // Event notifications are typically sent to multicast addresses which + * // represented by groups. Join a group to f.e. receive link notifications. + * nl_socket_add_membership(handle, RTNLGRP_LINK); + * @endcode + * + * @par 6) Cleaning up + * @code + * // Finally destroy the netlink handle + * nl_handle_destroy(handle); + * @endcode + * + * @{ + */ + +#include +#include +#include +#include +#include +#include + +static uint32_t used_ports_map[32]; + +static uint32_t generate_local_port(void) +{ + int i, n; + uint32_t pid = getpid() & 0x3FFFFF; + + for (i = 0; i < 32; i++) { + if (used_ports_map[i] == 0xFFFFFFFF) + continue; + + for (n = 0; n < 32; n++) { + if (1UL & (used_ports_map[i] >> n)) + continue; + + used_ports_map[i] |= (1UL << n); + n += (i * 32); + + /* PID_MAX_LIMIT is currently at 2^22, leaving 10 bit + * to, i.e. 1024 unique ports per application. */ + return pid + (n << 22); + + } + } + + /* Out of sockets in our own PID namespace, what to do? FIXME */ + return UINT_MAX; +} + +static void release_local_port(uint32_t port) +{ + int nr = port >> 22; + + used_ports_map[nr / 32] &= ~(nr % 32); +} + +/** + * @name Allocation + * @{ + */ + +static struct nl_handle *__alloc_handle(struct nl_cb *cb) +{ + struct nl_handle *handle; + + handle = calloc(1, sizeof(*handle)); + if (!handle) { + nl_errno(ENOMEM); + return NULL; + } + + handle->h_cb = cb; + handle->h_local.nl_family = AF_NETLINK; + handle->h_local.nl_pid = generate_local_port(); + handle->h_peer.nl_family = AF_NETLINK; + handle->h_seq_expect = handle->h_seq_next = time(0); + + return handle; +} + +/** + * Allocate new netlink socket handle. + * + * @return Newly allocated netlink socket handle or NULL. + */ +struct nl_handle *nl_handle_alloc(void) +{ + struct nl_cb *cb; + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) { + nl_errno(ENOMEM); + return NULL; + } + + return __alloc_handle(cb); +} + +/** + * Allocate new socket handle with custom callbacks + * @arg cb Callback handler + * + * The reference to the callback handler is taken into account + * automatically, it is released again upon calling nl_handle_destroy(). + * + *@return Newly allocted socket handle or NULL. + */ +struct nl_handle *nl_handle_alloc_cb(struct nl_cb *cb) +{ + if (cb == NULL) + BUG(); + + return __alloc_handle(nl_cb_get(cb)); +} + +/** + * Destroy netlink handle. + * @arg handle Netlink handle. + */ +void nl_handle_destroy(struct nl_handle *handle) +{ + if (!handle) + return; + + if (!(handle->h_flags & NL_OWN_PORT)) + release_local_port(handle->h_local.nl_pid); + + nl_cb_put(handle->h_cb); + free(handle); +} + +/** @} */ + +/** + * @name Sequence Numbers + * @{ + */ + +static int noop_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + + +/** + * Disable sequence number checking. + * @arg handle Netlink handle. + * + * Disables checking of sequence numbers on the netlink handle. This is + * required to allow messages to be processed which were not requested by + * a preceding request message, e.g. netlink events. + * + * @note This function modifies the NL_CB_SEQ_CHECK configuration in + * the callback handle associated with the socket. + */ +void nl_disable_sequence_check(struct nl_handle *handle) +{ + nl_cb_set(handle->h_cb, NL_CB_SEQ_CHECK, + NL_CB_CUSTOM, noop_seq_check, NULL); +} + +/** + * Use next sequence number + * @arg handle Netlink handle + * + * Uses the next available sequence number and increases the counter + * by one for subsequent calls. + * + * @return Unique serial sequence number + */ +unsigned int nl_socket_use_seq(struct nl_handle *handle) +{ + return handle->h_seq_next++; +} + +/** @} */ + +/** + * @name Source Idenficiation + * @{ + */ + +uint32_t nl_socket_get_local_port(struct nl_handle *handle) +{ + return handle->h_local.nl_pid; +} + +/** + * Set local port of socket + * @arg handle Netlink handle + * @arg port Local port identifier + * + * Assigns a local port identifier to the socket. If port is 0 + * a unique port identifier will be generated automatically. + */ +void nl_socket_set_local_port(struct nl_handle *handle, uint32_t port) +{ + if (port == 0) { + port = generate_local_port(); + handle->h_flags &= ~NL_OWN_PORT; + } else { + if (!(handle->h_flags & NL_OWN_PORT)) + release_local_port(handle->h_local.nl_pid); + handle->h_flags |= NL_OWN_PORT; + } + + handle->h_local.nl_pid = port; +} + +/** @} */ + +/** + * @name Group Subscriptions + * @{ + */ + +/** + * Join a group + * @arg handle Netlink handle + * @arg group Group identifier + * + * Joins the specified group using the modern socket option which + * is available since kernel version 2.6.14. It allows joining an + * almost arbitary number of groups without limitation. + * + * Make sure to use the correct group definitions as the older + * bitmask definitions for nl_join_groups() are likely to still + * be present for backward compatibility reasons. + * + * @return 0 on sucess or a negative error code. + */ +int nl_socket_add_membership(struct nl_handle *handle, int group) +{ + int err; + + err = setsockopt(handle->h_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, + &group, sizeof(group)); + if (err < 0) + return nl_error(errno, "setsockopt(NETLINK_ADD_MEMBERSHIP) " + "failed"); + + return 0; +} + +/** + * Leave a group + * @arg handle Netlink handle + * @arg group Group identifier + * + * Leaves the specified group using the modern socket option + * which is available since kernel version 2.6.14. + * + * @see nl_socket_add_membership + * @return 0 on success or a negative error code. + */ +int nl_socket_drop_membership(struct nl_handle *handle, int group) +{ + int err; + + err = setsockopt(handle->h_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, + &group, sizeof(group)); + if (err < 0) + return nl_error(errno, "setsockopt(NETLINK_DROP_MEMBERSHIP) " + "failed"); + + return 0; +} + +/** + * Join multicast groups (deprecated) + * @arg handle Netlink handle. + * @arg groups Bitmask of groups to join. + * + * This function defines the old way of joining multicast group which + * has to be done prior to calling nl_connect(). It works on any kernel + * version but is very limited as only 32 groups can be joined. + */ +void nl_join_groups(struct nl_handle *handle, int groups) +{ + handle->h_local.nl_groups |= groups; +} + + +/** @} */ + +/** + * @name Peer Identfication + * @{ + */ + +uint32_t nl_socket_get_peer_port(struct nl_handle *handle) +{ + return handle->h_peer.nl_pid; +} + +void nl_socket_set_peer_port(struct nl_handle *handle, uint32_t port) +{ + handle->h_peer.nl_pid = port; +} + +/** @} */ + +/** + * @name File Descriptor + * @{ + */ + +int nl_socket_get_fd(struct nl_handle *handle) +{ + return handle->h_fd; +} + +/** + * Set file descriptor of socket handle to non-blocking state + * @arg handle Netlink socket + * + * @return 0 on success or a negative error code. + */ +int nl_socket_set_nonblocking(struct nl_handle *handle) +{ + if (fcntl(handle->h_fd, F_SETFL, O_NONBLOCK) < 0) + return nl_error(errno, "fcntl(F_SETFL, O_NONBLOCK) failed"); + + return 0; +} + +/** + * Enable use of MSG_PEEK when reading from socket + * @arg handle Netlink socket + */ +void nl_socket_enable_msg_peek(struct nl_handle *handle) +{ + handle->h_flags |= NL_MSG_PEEK; +} + +/** + * Disable use of MSG_PEEK when reading from socket + * @arg handle Netlink socket + */ +void nl_socket_disable_msg_peek(struct nl_handle *handle) +{ + handle->h_flags &= ~NL_MSG_PEEK; +} + +/** @} */ + +/** + * @name Callback Handler + * @{ + */ + +struct nl_cb *nl_socket_get_cb(struct nl_handle *handle) +{ + return nl_cb_get(handle->h_cb); +} + +void nl_socket_set_cb(struct nl_handle *handle, struct nl_cb *cb) +{ + nl_cb_put(handle->h_cb); + handle->h_cb = nl_cb_get(cb); +} + +/** + * Modify the callback handler associated to the socket + * @arg handle netlink handle + * @arg type which type callback to set + * @arg kind kind of callback + * @arg func callback function + * @arg arg argument to be passwd to callback function + * + * @see nl_cb_set + */ +int nl_socket_modify_cb(struct nl_handle *handle, enum nl_cb_type type, + enum nl_cb_kind kind, nl_recvmsg_msg_cb_t func, + void *arg) +{ + return nl_cb_set(handle->h_cb, type, kind, func, arg); +} + +/** @} */ + +/** + * @name Utilities + * @{ + */ + +/** + * Set socket buffer size of netlink handle. + * @arg handle Netlink handle. + * @arg rxbuf New receive socket buffer size in bytes. + * @arg txbuf New transmit socket buffer size in bytes. + * + * Sets the socket buffer size of a netlink handle to the specified + * values \c rxbuf and \c txbuf. Providing a value of \c 0 assumes a + * good default value. + * + * @note It is not required to call this function prior to nl_connect(). + * @return 0 on sucess or a negative error code. + */ +int nl_set_buffer_size(struct nl_handle *handle, int rxbuf, int txbuf) +{ + int err; + + if (rxbuf <= 0) + rxbuf = 32768; + + if (txbuf <= 0) + txbuf = 32768; + + err = setsockopt(handle->h_fd, SOL_SOCKET, SO_SNDBUF, + &txbuf, sizeof(txbuf)); + if (err < 0) + return nl_error(errno, "setsockopt(SO_SNDBUF) failed"); + + err = setsockopt(handle->h_fd, SOL_SOCKET, SO_RCVBUF, + &rxbuf, sizeof(rxbuf)); + if (err < 0) + return nl_error(errno, "setsockopt(SO_RCVBUF) failed"); + + handle->h_flags |= NL_SOCK_BUFSIZE_SET; + + return 0; +} + +/** + * Enable/disable credential passing on netlink handle. + * @arg handle Netlink handle + * @arg state New state (0 - disabled, 1 - enabled) + * + * @return 0 on success or a negative error code + */ +int nl_set_passcred(struct nl_handle *handle, int state) +{ + int err; + + err = setsockopt(handle->h_fd, SOL_SOCKET, SO_PASSCRED, + &state, sizeof(state)); + if (err < 0) + return nl_error(errno, "setsockopt(SO_PASSCRED) failed"); + + if (state) + handle->h_flags |= NL_SOCK_PASSCRED; + else + handle->h_flags &= ~NL_SOCK_PASSCRED; + + return 0; +} + +/** + * Enable/disable receival of additional packet information + * @arg handle Netlink handle + * @arg state New state (0 - disabled, 1 - enabled) + * + * @return 0 on success or a negative error code + */ +int nl_socket_recv_pktinfo(struct nl_handle *handle, int state) +{ + int err; + + err = setsockopt(handle->h_fd, SOL_NETLINK, NETLINK_PKTINFO, + &state, sizeof(state)); + if (err < 0) + return nl_error(errno, "setsockopt(NETLINK_PKTINFO) failed"); + + return 0; +} + +/** @} */ + +/** @} */ diff --git a/lib/utils.c b/lib/utils.c new file mode 100644 index 0000000..8b86921 --- /dev/null +++ b/lib/utils.c @@ -0,0 +1,701 @@ +/* + * lib/utils.c Utility Functions + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +/** + * @defgroup utils Utilities + * @{ + */ + +#include +#include +#include +#include + +/** + * Debug level + */ +int nl_debug = 0; + +struct nl_dump_params nl_debug_dp = { + .dp_type = NL_DUMP_FULL, +}; + +static void __init nl_debug_dp_init(void) +{ + nl_debug_dp.dp_fd = stderr; +} + +/** + * @name Error Code Helpers + * @{ + */ + +static char *errbuf; +static int nlerrno; + +/** @cond SKIP */ +int __nl_error(int err, const char *file, unsigned int line, const char *func, + const char *fmt, ...) +{ + char *user_err; + va_list args; + + if (errbuf) { + free(errbuf); + errbuf = NULL; + } + + nlerrno = err; + + if (fmt) { + va_start(args, fmt); + vasprintf(&user_err, fmt, args); + va_end(args); + } + +#ifdef VERBOSE_ERRORS + asprintf(&errbuf, "%s:%u:%s: %s (errno = %s)", + file, line, func, fmt ? user_err : "", strerror(err)); +#else + asprintf(&errbuf, "%s (errno = %s)", + fmt ? user_err : "", strerror(err)); +#endif + + if (fmt) + free(user_err); + + return -err; +} + +int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *)) +{ + FILE *fd; + char buf[128]; + + fd = fopen(path, "r"); + if (fd == NULL) + return nl_error(errno, "Unable to open file %s for reading", + path); + + while (fgets(buf, sizeof(buf), fd)) { + int goodlen, err; + long num; + char *end; + + if (*buf == '#' || *buf == '\n' || *buf == '\r') + continue; + + num = strtol(buf, &end, 0); + if (end == buf) + return nl_error(EINVAL, "Parsing error"); + + if (num == LONG_MIN || num == LONG_MAX) + return nl_error(errno, "Number of out range"); + + while (*end == ' ' || *end == '\t') + end++; + + goodlen = strcspn(end, "#\r\n\t "); + if (goodlen == 0) + return nl_error(EINVAL, "Empty string"); + + end[goodlen] = '\0'; + + err = cb(num, end); + if (err < 0) + return err; + } + + fclose(fd); + + return 0; +} + +/** @endcond */ + +int nl_get_errno(void) +{ + return nlerrno; +} + + +/** + * Return error message for an error code + * @return error message + */ +char *nl_geterror(void) +{ + if (errbuf) + return errbuf; + + if (nlerrno) + return strerror(nlerrno); + + return "Sucess\n"; +} + +/** + * Print a libnl error message + * @arg s error message prefix + * + * Prints the error message of the call that failed last. + * + * If s is not NULL and *s is not a null byte the argument + * string is printed, followed by a colon and a blank. Then + * the error message and a new-line. + */ +void nl_perror(const char *s) +{ + if (s && *s) + fprintf(stderr, "%s: %s\n", s, nl_geterror()); + else + fprintf(stderr, "%s\n", nl_geterror()); +} + +/** @} */ + +/** + * @name Unit Pretty-Printing + * @{ + */ + +/** + * Cancel down a byte counter + * @arg l byte counter + * @arg unit destination unit pointer + * + * Cancels down a byte counter until it reaches a reasonable + * unit. The chosen unit is assigned to \a unit. + * + * @return The cancelled down byte counter in the new unit. + */ +double nl_cancel_down_bytes(unsigned long long l, char **unit) +{ + if (l >= 1099511627776LL) { + *unit = "TiB"; + return ((double) l) / 1099511627776LL; + } else if (l >= 1073741824) { + *unit = "GiB"; + return ((double) l) / 1073741824; + } else if (l >= 1048576) { + *unit = "MiB"; + return ((double) l) / 1048576; + } else if (l >= 1024) { + *unit = "KiB"; + return ((double) l) / 1024; + } else { + *unit = "B"; + return (double) l; + } +} + +/** + * Cancel down a bit counter + * @arg l bit counter + * @arg unit destination unit pointer + * + * Cancels downa bit counter until it reaches a reasonable + * unit. The chosen unit is assigned to \a unit. + * + * @return The cancelled down bit counter in the new unit. + */ +double nl_cancel_down_bits(unsigned long long l, char **unit) +{ + if (l >= 1099511627776ULL) { + *unit = "Tbit"; + return ((double) l) / 1099511627776ULL; + } else if (l >= 1073741824) { + *unit = "Gbit"; + return ((double) l) / 1073741824; + } else if (l >= 1048576) { + *unit = "Mbit"; + return ((double) l) / 1048576; + } else if (l >= 1024) { + *unit = "Kbit"; + return ((double) l) / 1024; + } else { + *unit = "bit"; + return (double) l; + } + +} + +/** + * Cancel down a micro second value + * @arg l micro seconds + * @arg unit destination unit pointer + * + * Cancels down a microsecond counter until it reaches a + * reasonable unit. The chosen unit is assigned to \a unit. + * + * @return The cancelled down microsecond in the new unit + */ +double nl_cancel_down_us(uint32_t l, char **unit) +{ + if (l >= 1000000) { + *unit = "s"; + return ((double) l) / 1000000; + } else if (l >= 1000) { + *unit = "ms"; + return ((double) l) / 1000; + } else { + *unit = "us"; + return (double) l; + } +} + +/** @} */ + +/** + * @name Generic Unit Translations + * @{ + */ + +/** + * Convert a character string to a size + * @arg str size encoded as character string + * + * Converts the specified size as character to the corresponding + * number of bytes. + * + * Supported formats are: + * - b,kb/k,m/mb,gb/g for bytes + * - bit,kbit/mbit/gbit + * + * @return The number of bytes or -1 if the string is unparseable + */ +long nl_size2int(const char *str) +{ + char *p; + long l = strtol(str, &p, 0); + if (p == str) + return -1; + + if (*p) { + if (!strcasecmp(p, "kb") || !strcasecmp(p, "k")) + l *= 1024; + else if (!strcasecmp(p, "gb") || !strcasecmp(p, "g")) + l *= 1024*1024*1024; + else if (!strcasecmp(p, "gbit")) + l *= 1024*1024*1024/8; + else if (!strcasecmp(p, "mb") || !strcasecmp(p, "m")) + l *= 1024*1024; + else if (!strcasecmp(p, "mbit")) + l *= 1024*1024/8; + else if (!strcasecmp(p, "kbit")) + l *= 1024/8; + else if (!strcasecmp(p, "bit")) + l /= 8; + else if (strcasecmp(p, "b") != 0) + return -1; + } + + return l; +} + +/** + * Convert a character string to a probability + * @arg str probability encoded as character string + * + * Converts the specified probability as character to the + * corresponding probability number. + * + * Supported formats are: + * - 0.0-1.0 + * - 0%-100% + * + * @return The probability relative to NL_PROB_MIN and NL_PROB_MAX + */ +long nl_prob2int(const char *str) +{ + char *p; + double d = strtod(str, &p); + + if (p == str) + return -1; + + if (d > 1.0) + d /= 100.0f; + + if (d > 1.0f || d < 0.0f) + return -1; + + if (*p && strcmp(p, "%") != 0) + return -1; + + return rint(d * NL_PROB_MAX); +} + +/** @} */ + +/** + * @name Time Translations + * @{ + */ + +#ifdef USER_HZ +static uint32_t user_hz = USER_HZ; +#else +static uint32_t user_hz = 100; +#endif + +static double ticks_per_usec = 1.0f; + +/* Retrieves the configured HZ and ticks/us value in the kernel. + * The value is cached. Supported ways of getting it: + * + * 1) environment variable + * 2) /proc/net/psched and sysconf + * + * Supports the environment variables: + * PROC_NET_PSCHED - may point to psched file in /proc + * PROC_ROOT - may point to /proc fs */ +static void __init get_psched_settings(void) +{ + char name[FILENAME_MAX]; + FILE *fd; + int got_hz = 0, got_tick = 0; + + if (getenv("HZ")) { + long hz = strtol(getenv("HZ"), NULL, 0); + + if (LONG_MIN != hz && LONG_MAX != hz) { + user_hz = hz; + got_hz = 1; + } + } + + if (!got_hz) + user_hz = sysconf(_SC_CLK_TCK); + + if (getenv("TICKS_PER_USEC")) { + double t = strtod(getenv("TICKS_PER_USEC"), NULL); + + ticks_per_usec = t; + got_tick = 1; + } + + + if (getenv("PROC_NET_PSCHED")) + snprintf(name, sizeof(name), "%s", getenv("PROC_NET_PSCHED")); + else if (getenv("PROC_ROOT")) + snprintf(name, sizeof(name), "%s/net/psched", + getenv("PROC_ROOT")); + else + strncpy(name, "/proc/net/psched", sizeof(name) - 1); + + if ((fd = fopen(name, "r"))) { + uint32_t tick, us, nom; + int r = fscanf(fd, "%08x%08x%08x%*08x", &tick, &us, &nom); + + if (4 == r && nom == 1000000 && !got_tick) + ticks_per_usec = (double)tick/(double)us; + + fclose(fd); + } +} + + +/** + * Return the value of HZ + */ +int nl_get_hz(void) +{ + return user_hz; +} + + +/** + * Convert micro seconds to ticks + * @arg us micro seconds + * @return number of ticks + */ +uint32_t nl_us2ticks(uint32_t us) +{ + return us * ticks_per_usec; +} + + +/** + * Convert ticks to micro seconds + * @arg ticks number of ticks + * @return microseconds + */ +uint32_t nl_ticks2us(uint32_t ticks) +{ + return ticks / ticks_per_usec; +} + +long nl_time2int(const char *str) +{ + char *p; + long l = strtol(str, &p, 0); + if (p == str) + return -1; + + if (*p) { + if (!strcasecmp(p, "min") == 0 || !strcasecmp(p, "m")) + l *= 60; + else if (!strcasecmp(p, "hour") || !strcasecmp(p, "h")) + l *= 60*60; + else if (!strcasecmp(p, "day") || !strcasecmp(p, "d")) + l *= 60*60*24; + else if (strcasecmp(p, "s") != 0) + return -1; + } + + return l; +} + +/** + * Convert milliseconds to a character string + * @arg msec number of milliseconds + * @arg buf destination buffer + * @arg len buffer length + * + * Converts milliseconds to a character string split up in days, hours, + * minutes, seconds, and milliseconds and stores it in the specified + * destination buffer. + * + * @return The destination buffer. + */ +char * nl_msec2str(uint64_t msec, char *buf, size_t len) +{ + int i, split[5]; + char *units[] = {"d", "h", "m", "s", "msec"}; + +#define _SPLIT(idx, unit) if ((split[idx] = msec / unit) > 0) msec %= unit + _SPLIT(0, 86400000); /* days */ + _SPLIT(1, 3600000); /* hours */ + _SPLIT(2, 60000); /* minutes */ + _SPLIT(3, 1000); /* seconds */ +#undef _SPLIT + split[4] = msec; + + memset(buf, 0, len); + + for (i = 0; i < ARRAY_SIZE(split); i++) { + if (split[i] > 0) { + char t[64]; + snprintf(t, sizeof(t), "%s%d%s", + strlen(buf) ? " " : "", split[i], units[i]); + strncat(buf, t, len - strlen(buf) - 1); + } + } + + return buf; +} + +/** @} */ + +/** + * @name Link Layer Protocol Translations + * @{ + */ + +static struct trans_tbl llprotos[] = { + {0, "generic"}, + __ADD(ARPHRD_ETHER,ether) + __ADD(ARPHRD_EETHER,eether) + __ADD(ARPHRD_AX25,ax25) + __ADD(ARPHRD_PRONET,pronet) + __ADD(ARPHRD_CHAOS,chaos) + __ADD(ARPHRD_IEEE802,ieee802) + __ADD(ARPHRD_ARCNET,arcnet) + __ADD(ARPHRD_APPLETLK,atalk) + __ADD(ARPHRD_DLCI,dlci) + __ADD(ARPHRD_ATM,atm) + __ADD(ARPHRD_METRICOM,metricom) + __ADD(ARPHRD_IEEE1394,ieee1394) +#ifdef ARPHRD_EUI64 + __ADD(ARPHRD_EUI64,eui64) +#endif + __ADD(ARPHRD_INFINIBAND,infiniband) + __ADD(ARPHRD_SLIP,slip) + __ADD(ARPHRD_CSLIP,cslip) + __ADD(ARPHRD_SLIP6,slip6) + __ADD(ARPHRD_CSLIP6,cslip6) + __ADD(ARPHRD_RSRVD,rsrvd) + __ADD(ARPHRD_ADAPT,adapt) + __ADD(ARPHRD_ROSE,rose) + __ADD(ARPHRD_X25,x25) +#ifdef ARPHRD_HWX25 + __ADD(ARPHRD_HWX25,hwx25) +#endif + __ADD(ARPHRD_PPP,ppp) + __ADD(ARPHRD_HDLC,hdlc) + __ADD(ARPHRD_LAPB,lapb) + __ADD(ARPHRD_DDCMP,ddcmp) + __ADD(ARPHRD_RAWHDLC,rawhdlc) + __ADD(ARPHRD_TUNNEL,ipip) + __ADD(ARPHRD_TUNNEL6,tunnel6) + __ADD(ARPHRD_FRAD,frad) + __ADD(ARPHRD_SKIP,skip) + __ADD(ARPHRD_LOOPBACK,loopback) + __ADD(ARPHRD_LOCALTLK,localtlk) + __ADD(ARPHRD_FDDI,fddi) + __ADD(ARPHRD_BIF,bif) + __ADD(ARPHRD_SIT,sit) + __ADD(ARPHRD_IPDDP,ip/ddp) + __ADD(ARPHRD_IPGRE,gre) + __ADD(ARPHRD_PIMREG,pimreg) + __ADD(ARPHRD_HIPPI,hippi) + __ADD(ARPHRD_ASH,ash) + __ADD(ARPHRD_ECONET,econet) + __ADD(ARPHRD_IRDA,irda) + __ADD(ARPHRD_FCPP,fcpp) + __ADD(ARPHRD_FCAL,fcal) + __ADD(ARPHRD_FCPL,fcpl) + __ADD(ARPHRD_FCFABRIC,fcfb_0) + __ADD(ARPHRD_FCFABRIC+1,fcfb_1) + __ADD(ARPHRD_FCFABRIC+2,fcfb_2) + __ADD(ARPHRD_FCFABRIC+3,fcfb_3) + __ADD(ARPHRD_FCFABRIC+4,fcfb_4) + __ADD(ARPHRD_FCFABRIC+5,fcfb_5) + __ADD(ARPHRD_FCFABRIC+6,fcfb_6) + __ADD(ARPHRD_FCFABRIC+7,fcfb_7) + __ADD(ARPHRD_FCFABRIC+8,fcfb_8) + __ADD(ARPHRD_FCFABRIC+9,fcfb_9) + __ADD(ARPHRD_FCFABRIC+10,fcfb_10) + __ADD(ARPHRD_FCFABRIC+11,fcfb_11) + __ADD(ARPHRD_FCFABRIC+12,fcfb_12) + __ADD(ARPHRD_IEEE802_TR,tr) + __ADD(ARPHRD_IEEE80211,ieee802.11) +#ifdef ARPHRD_IEEE80211_PRISM + __ADD(ARPHRD_IEEE80211_PRISM, ieee802.11_prism) +#endif +#ifdef ARPHRD_VOID + __ADD(ARPHRD_VOID,void) +#endif +}; + +char * nl_llproto2str(int llproto, char *buf, size_t len) +{ + return __type2str(llproto, buf, len, llprotos, ARRAY_SIZE(llprotos)); +} + +int nl_str2llproto(const char *name) +{ + return __str2type(name, llprotos, ARRAY_SIZE(llprotos)); +} + +/** @} */ + + +/** + * @name Ethernet Protocol Translations + * @{ + */ + +static struct trans_tbl ether_protos[] = { + __ADD(ETH_P_LOOP,loop) + __ADD(ETH_P_PUP,pup) + __ADD(ETH_P_PUPAT,pupat) + __ADD(ETH_P_IP,ip) + __ADD(ETH_P_X25,x25) + __ADD(ETH_P_ARP,arp) + __ADD(ETH_P_BPQ,bpq) + __ADD(ETH_P_IEEEPUP,ieeepup) + __ADD(ETH_P_IEEEPUPAT,ieeepupat) + __ADD(ETH_P_DEC,dec) + __ADD(ETH_P_DNA_DL,dna_dl) + __ADD(ETH_P_DNA_RC,dna_rc) + __ADD(ETH_P_DNA_RT,dna_rt) + __ADD(ETH_P_LAT,lat) + __ADD(ETH_P_DIAG,diag) + __ADD(ETH_P_CUST,cust) + __ADD(ETH_P_SCA,sca) + __ADD(ETH_P_RARP,rarp) + __ADD(ETH_P_ATALK,atalk) + __ADD(ETH_P_AARP,aarp) +#ifdef ETH_P_8021Q + __ADD(ETH_P_8021Q,802.1q) +#endif + __ADD(ETH_P_IPX,ipx) + __ADD(ETH_P_IPV6,ipv6) +#ifdef ETH_P_WCCP + __ADD(ETH_P_WCCP,wccp) +#endif + __ADD(ETH_P_PPP_DISC,ppp_disc) + __ADD(ETH_P_PPP_SES,ppp_ses) + __ADD(ETH_P_MPLS_UC,mpls_uc) + __ADD(ETH_P_MPLS_MC,mpls_mc) + __ADD(ETH_P_ATMMPOA,atmmpoa) + __ADD(ETH_P_ATMFATE,atmfate) + __ADD(ETH_P_EDP2,edp2) + __ADD(ETH_P_802_3,802.3) + __ADD(ETH_P_AX25,ax25) + __ADD(ETH_P_ALL,all) + __ADD(ETH_P_802_2,802.2) + __ADD(ETH_P_SNAP,snap) + __ADD(ETH_P_DDCMP,ddcmp) + __ADD(ETH_P_WAN_PPP,wan_ppp) + __ADD(ETH_P_PPP_MP,ppp_mp) + __ADD(ETH_P_LOCALTALK,localtalk) + __ADD(ETH_P_PPPTALK,ppptalk) + __ADD(ETH_P_TR_802_2,tr_802.2) + __ADD(ETH_P_MOBITEX,mobitex) + __ADD(ETH_P_CONTROL,control) + __ADD(ETH_P_IRDA,irda) + __ADD(ETH_P_ECONET,econet) + __ADD(ETH_P_HDLC,hdlc) +}; + +char *nl_ether_proto2str(int eproto, char *buf, size_t len) +{ + return __type2str(eproto, buf, len, ether_protos, + ARRAY_SIZE(ether_protos)); +} + +int nl_str2ether_proto(const char *name) +{ + return __str2type(name, ether_protos, ARRAY_SIZE(ether_protos)); +} + +/** @} */ + +/** + * @name IP Protocol Translations + * @{ + */ + +char *nl_ip_proto2str(int proto, char *buf, size_t len) +{ + struct protoent *p = getprotobynumber(proto); + + if (p) { + snprintf(buf, len, "%s", p->p_name); + return buf; + } + + snprintf(buf, len, "0x%x", proto); + return buf; +} + +int nl_str2ip_proto(const char *name) +{ + struct protoent *p = getprotobyname(name); + unsigned long l; + char *end; + + if (p) + return p->p_proto; + + l = strtoul(name, &end, 0); + if (l == ULONG_MAX || *end != '\0') + return -1; + + return (int) l; +} + +/** @} */ + +/** @} */ diff --git a/libnl-1.pc.in b/libnl-1.pc.in new file mode 100644 index 0000000..7ac8413 --- /dev/null +++ b/libnl-1.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@prefix@ +libdir=@libdir@ +includedir=@prefix@/include + +Name: libnl +Description: Convenience library for netlink sockets +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lnl +Cflags: diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..ddbe29a --- /dev/null +++ b/src/Makefile @@ -0,0 +1,45 @@ +# +# src/Makefile +# +# 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 version 2.1 +# of the License. +# +# Copyright (c) 2003-2006 Thomas Graf +# + +ifeq ($(shell [ ! -r ../Makefile.opts ] && echo 1),) + include ../Makefile.opts +endif + +LDFLAGS += -L../lib -lnl utils.o +CIN := $(wildcard nl-*.c) $(wildcard genl-*.c) $(wildcard nf-*.c) +TOOLS := $(CIN:%.c=%) + +all: $(TOOLS) + +$(TOOLS): utils.o + +nl-%: nl-%.c + @echo " LD $@"; \ + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) + +genl-%: genl-%.c + @echo " LD $@"; \ + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) + +nf-%: nf-%.c + @echo " LD $@"; \ + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) + +clean: + @echo " CLEAN src"; \ + rm -f $(TOOLS) utils.o + +distclean: clean + +install: + @true + +include ../Makefile.rules diff --git a/src/f_addr.c b/src/f_addr.c new file mode 100644 index 0000000..491d88f --- /dev/null +++ b/src/f_addr.c @@ -0,0 +1,107 @@ +/* + * src/f_addr.c Address Filter + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +static void get_filter(struct rtnl_addr *addr, int argc, char **argv, int idx, + struct nl_cache *link_cache) +{ + struct nl_addr *a; + + while (argc > idx) { + if (arg_match("dev")) { + if (argc > ++idx) { + int ifindex = rtnl_link_name2i(link_cache, argv[idx++]); + if (ifindex == RTNL_LINK_NOT_FOUND) + goto err_notfound; + rtnl_addr_set_ifindex(addr, ifindex); + } + } else if (arg_match("family")) { + if (argc > ++idx) { + int family = nl_str2af(argv[idx++]); + if (family == AF_UNSPEC) + goto err_invaf; + rtnl_addr_set_family(addr, family); + } + } else if (arg_match("label")) { + if (argc > ++idx) + rtnl_addr_set_label(addr, argv[idx++]); + } else if (arg_match("scope")) { + if (argc > ++idx) { + int scope = rtnl_str2scope(argv[idx++]); + if (scope < 0) + goto err_invscope; + rtnl_addr_set_scope(addr, scope); + } + } else if (arg_match("local")) { + if (argc > ++idx) { + a = nl_addr_parse(argv[idx++], + rtnl_addr_get_family(addr)); + if (!a) + goto err_invaddr; + rtnl_addr_set_local(addr, a); + nl_addr_put(a); + } + } else if (arg_match("peer")) { + if (argc > ++idx) { + a = nl_addr_parse(argv[idx++], + rtnl_addr_get_family(addr)); + if (!a) + goto err_invaddr; + rtnl_addr_set_peer(addr, a); + nl_addr_put(a); + } + } else if (arg_match("broadcast")) { + if (argc > ++idx) { + a = nl_addr_parse(argv[idx++], + rtnl_addr_get_family(addr)); + if (!a) + goto err_invaddr; + rtnl_addr_set_broadcast(addr, a); + nl_addr_put(a); + } + } else if (arg_match("multicast")) { + if (argc > ++idx) { + a = nl_addr_parse(argv[idx++], + rtnl_addr_get_family(addr)); + if (!a) + goto err_invaddr; + rtnl_addr_set_multicast(addr, a); + nl_addr_put(a); + } + } else if (arg_match("anycast")) { + if (argc > ++idx) { + a = nl_addr_parse(argv[idx++], + rtnl_addr_get_family(addr)); + if (!a) + goto err_invaddr; + rtnl_addr_set_anycast(addr, a); + nl_addr_put(a); + } + } else { + fprintf(stderr, "What is '%s'?\n", argv[idx]); + exit(1); + } + } + + return; + +err_notfound: + fprintf(stderr, "Unknown link %s\n", argv[idx-1]); + exit(1); +err_invscope: + fprintf(stderr, "Invalid scope name \"%s\".\n", argv[idx-1]); + exit(1); +err_invaf: + fprintf(stderr, "Invalid address family \"%s\"\n", argv[idx-1]); + exit(1); +err_invaddr: + fprintf(stderr, "Invalid address \"%s\": %s\n", argv[idx-1], nl_geterror()); + exit(1); +} diff --git a/src/f_ct.c b/src/f_ct.c new file mode 100644 index 0000000..e45b393 --- /dev/null +++ b/src/f_ct.c @@ -0,0 +1,153 @@ +/* + * src/f_ct.c Conntrack Filter + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +static void get_filter(struct nfnl_ct *ct, int argc, char **argv, int idx) +{ + struct nl_addr *a; + + while (argc > idx) { + if (arg_match("family")) { + if (argc > ++idx) { + int family = nl_str2af(argv[idx++]); + if (family == AF_UNSPEC) + goto err_invaf; + nfnl_ct_set_family(ct, family); + } + } else if (arg_match("proto")) { + if (argc > ++idx) { + int proto = nl_str2ip_proto(argv[idx++]); + if (proto < 0) + goto err_invproto; + nfnl_ct_set_proto(ct, proto); + } + } else if (arg_match("tcpstate")) { + if (argc > ++idx) { + int state = nfnl_ct_str2tcp_state(argv[idx++]); + if (state < 0) + goto err_invtcpstate; + nfnl_ct_set_tcp_state(ct, state); + } + } else if (arg_match("status")) { + if (argc > ++idx) + nfnl_ct_set_status(ct, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("timeout")) { + if (argc > ++idx) + nfnl_ct_set_timeout(ct, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("mark")) { + if (argc > ++idx) + nfnl_ct_set_mark(ct, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("use")) { + if (argc > ++idx) + nfnl_ct_set_use(ct, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("id")) { + if (argc > ++idx) + nfnl_ct_set_id(ct, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("origsrc")) { + if (argc > ++idx) { + a = nl_addr_parse(argv[idx++], + nfnl_ct_get_family(ct)); + if (!a) + goto err_invaddr; + nfnl_ct_set_src(ct, 0, a); + nl_addr_put(a); + } + } else if (arg_match("origdst")) { + if (argc > ++idx) { + a = nl_addr_parse(argv[idx++], + nfnl_ct_get_family(ct)); + if (!a) + goto err_invaddr; + nfnl_ct_set_dst(ct, 0, a); + nl_addr_put(a); + } + } else if (arg_match("origsrcport")) { + if (argc > ++idx) + nfnl_ct_set_src_port(ct, 0, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("origdstport")) { + if (argc > ++idx) + nfnl_ct_set_dst_port(ct, 0, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("origicmpid")) { + if (argc > ++idx) + nfnl_ct_set_icmp_id(ct, 0, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("origicmptype")) { + if (argc > ++idx) + nfnl_ct_set_icmp_type(ct, 0, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("origicmpcode")) { + if (argc > ++idx) + nfnl_ct_set_icmp_code(ct, 0, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("origpackets")) { + if (argc > ++idx) + nfnl_ct_set_packets(ct, 0, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("origbytes")) { + if (argc > ++idx) + nfnl_ct_set_bytes(ct, 0, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("replysrc")) { + if (argc > ++idx) { + a = nl_addr_parse(argv[idx++], + nfnl_ct_get_family(ct)); + if (!a) + goto err_invaddr; + nfnl_ct_set_src(ct, 1, a); + nl_addr_put(a); + } + } else if (arg_match("replydst")) { + if (argc > ++idx) { + a = nl_addr_parse(argv[idx++], + nfnl_ct_get_family(ct)); + if (!a) + goto err_invaddr; + nfnl_ct_set_dst(ct, 1, a); + nl_addr_put(a); + } + } else if (arg_match("replysrcport")) { + if (argc > ++idx) + nfnl_ct_set_src_port(ct, 1, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("replydstport")) { + if (argc > ++idx) + nfnl_ct_set_dst_port(ct, 1, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("replyicmpid")) { + if (argc > ++idx) + nfnl_ct_set_icmp_id(ct, 1, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("replyicmptype")) { + if (argc > ++idx) + nfnl_ct_set_icmp_type(ct, 1, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("replyicmpcode")) { + if (argc > ++idx) + nfnl_ct_set_icmp_code(ct, 1, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("replypackets")) { + if (argc > ++idx) + nfnl_ct_set_packets(ct, 1, strtoul(argv[idx++], NULL, 0)); + } else if (arg_match("replybytes")) { + if (argc > ++idx) + nfnl_ct_set_bytes(ct, 1, strtoul(argv[idx++], NULL, 0)); + } else { + fprintf(stderr, "What is '%s'?\n", argv[idx]); + exit(1); + } + } + + return; + +err_invproto: + fprintf(stderr, "Invalid IP protocol \"%s\".\n", argv[idx-1]); + exit(1); +err_invtcpstate: + fprintf(stderr, "Invalid TCP state \"%s\".\n", argv[idx-1]); + exit(1); +err_invaf: + fprintf(stderr, "Invalid address family \"%s\"\n", argv[idx-1]); + exit(1); +err_invaddr: + fprintf(stderr, "Invalid address \"%s\": %s\n", argv[idx-1], nl_geterror()); + exit(1); +} diff --git a/src/f_link.c b/src/f_link.c new file mode 100644 index 0000000..3c1cb93 --- /dev/null +++ b/src/f_link.c @@ -0,0 +1,106 @@ +/* + * src/f_link.c Link Filter + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include + +static void get_filter(struct rtnl_link *l, int ac, char **av, int idx, + struct nl_cache *cache) +{ + while (ac > idx) { + if (!strcasecmp(av[idx], "dev")) { + if (ac > ++idx) { + int ifindex = rtnl_link_name2i(cache, av[idx++]); + if (ifindex == RTNL_LINK_NOT_FOUND) + goto err_notfound; + rtnl_link_set_ifindex(l, ifindex); + } + } else if (!strcasecmp(av[idx], "mtu")) { + if (ac > ++idx) + rtnl_link_set_mtu(l, strtoul(av[idx++], NULL, 0)); + } else if (!strcasecmp(av[idx], "txqlen")) { + if (ac > ++idx) + rtnl_link_set_txqlen(l, strtoul(av[idx++], NULL, 0)); + } else if (!strcasecmp(av[idx], "weight")) { + if (ac > ++idx) + rtnl_link_set_weight(l, strtoul(av[idx++], NULL, 0)); + } else if (!strcasecmp(av[idx], "link")) { + if (ac > ++idx) { + int ifindex = rtnl_link_name2i(cache, av[idx++]); + if (ifindex == RTNL_LINK_NOT_FOUND) + goto err_notfound; + rtnl_link_set_link(l, ifindex); + } + } else if (!strcasecmp(av[idx], "master")) { + if (ac > ++idx) { + int ifindex = rtnl_link_name2i(cache, av[idx++]); + if (ifindex == RTNL_LINK_NOT_FOUND) + goto err_notfound; + rtnl_link_set_master(l, ifindex); + } + } else if (!strcasecmp(av[idx], "qdisc")) { + if (ac > ++idx) + rtnl_link_set_qdisc(l, av[idx++]); + } else if (!strcasecmp(av[idx], "name")) { + if (ac > ++idx) + rtnl_link_set_name(l, av[idx++]); + } else if (!strcasecmp(av[idx], "addr")) { + if (ac > ++idx) { + struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC); + if (a == NULL) + goto err; + rtnl_link_set_addr(l, a); + nl_addr_put(a); + } + } else if (!strcasecmp(av[idx], "broadcast")) { + if (ac > ++idx) { + struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC); + if (a == NULL) + goto err; + rtnl_link_set_broadcast(l, a); + nl_addr_put(a); + } + } +#define MFLAG(STR, FLAG) \ + else if (!strcasecmp(av[idx], STR)) { \ + rtnl_link_set_flags(l, FLAG); idx++; } +#define MNOFLAG(STR, FLAG) \ + else if (!strcasecmp(av[idx], STR)) { \ + rtnl_link_unset_flags(l, FLAG); idx++; } + + MFLAG("up", IFF_UP) + MNOFLAG("down", IFF_UP) + MFLAG("noarp", IFF_NOARP) + MNOFLAG("arp", IFF_NOARP) + MFLAG("promisc", IFF_PROMISC) + MNOFLAG("nopromisc", IFF_PROMISC) + MFLAG("dynamic", IFF_DYNAMIC) + MNOFLAG("nodynamic", IFF_DYNAMIC) + MFLAG("multicast", IFF_MULTICAST) + MNOFLAG("nomulticast", IFF_MULTICAST) + MFLAG("allmulticast", IFF_ALLMULTI) + MNOFLAG("noallmulticast", IFF_ALLMULTI) +#undef MFLAG +#undef MNOFLAG + else { + fprintf(stderr, "What is '%s'?\n", av[idx]); + exit(1); + } + } + + return; + +err_notfound: + fprintf(stderr, "Unknown link %s\n", av[idx-1]); + exit(1); +err: + fprintf(stderr, "%s\n", nl_geterror()); + exit(1); +} diff --git a/src/f_neigh.c b/src/f_neigh.c new file mode 100644 index 0000000..ac9355c --- /dev/null +++ b/src/f_neigh.c @@ -0,0 +1,51 @@ +/* + * src/f_neigh.c Neighbour Filter + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +static void get_filter(struct rtnl_neigh *n, int ac, char **av, int idx, + struct nl_cache *cache) +{ + struct nl_cache *lc = nl_cache_mngt_require("route/link"); + + while (ac > idx) { + if (!strcasecmp(av[idx], "dev")) { + if (ac > ++idx) { + int ifindex = rtnl_link_name2i(lc, av[idx++]); + if (ifindex == RTNL_LINK_NOT_FOUND) + goto err_notfound; + rtnl_neigh_set_ifindex(n, ifindex); + } + } else if (!strcasecmp(av[idx], "dst")) { + if (ac > ++idx) { + struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC); + if (a == NULL) + goto err; + rtnl_neigh_set_dst(n, a); + nl_addr_put(a); + } + } else if (!strcasecmp(av[idx], "lladdr")) { + if (ac > ++idx) { + struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC); + if (a == NULL) + goto err; + rtnl_neigh_set_lladdr(n, a); + nl_addr_put(a); + } + } + } + + return; +err_notfound: + fprintf(stderr, "Unable to find interface %s\n", av[idx-1]); + exit(1); +err: + fprintf(stderr, "%s\n", nl_geterror()); + exit(1); +} diff --git a/src/f_route.c b/src/f_route.c new file mode 100644 index 0000000..581ff65 --- /dev/null +++ b/src/f_route.c @@ -0,0 +1,81 @@ +/* + * src/f_route.c Routes Filter + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +static void get_filter(struct rtnl_route *r, int ac, char **av, int idx, + struct nl_cache *cache, struct nl_cache *link_cache) +{ + while (ac > idx) { + if (!strcasecmp(av[idx], "src")) { + if (ac > ++idx) { + struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC); + if (!a) + goto err; + rtnl_route_set_pref_src(r, a); + nl_addr_put(a); + } + } else if (!strcasecmp(av[idx], "dst")) { + if (ac > ++idx) { + struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC); + if (!a) + goto err; + rtnl_route_set_dst(r, a); + nl_addr_put(a); + } + } else if (!strcasecmp(av[idx], "via")) { + if (ac > ++idx) { + struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC); + if (!a) + goto err; + rtnl_route_set_gateway(r, a); + nl_addr_put(a); + } + } else if (!strcasecmp(av[idx], "from")) { + if (ac > ++idx) { + struct nl_addr *a = nl_addr_parse(av[idx++], AF_UNSPEC); + if (!a) + goto err; + rtnl_route_set_src(r, a); + nl_addr_put(a); + } + } else if (!strcasecmp(av[idx], "tos")) { + if (ac > ++idx) + rtnl_route_set_tos(r, strtoul(av[idx++], NULL, 0)); + } else if (!strcasecmp(av[idx], "prio")) { + if (ac > ++idx) + rtnl_route_set_prio(r, strtoul(av[idx++], NULL, 0)); + } else if (!strcasecmp(av[idx], "scope")) { + if (ac > ++idx) + rtnl_route_set_scope(r, rtnl_str2scope(av[idx++])); + } else if (!strcasecmp(av[idx], "dev")) { + if (ac > ++idx) { + int ifindex = rtnl_link_name2i(link_cache, av[idx++]); + if (ifindex == RTNL_LINK_NOT_FOUND) + goto err_notfound; + rtnl_route_set_oif(r, ifindex); + } + } else if (!strcasecmp(av[idx], "table")) { + if (ac > ++idx) + rtnl_route_set_table(r, strtoul(av[idx++], NULL, 0)); + } else { + fprintf(stderr, "What is '%s'?\n", av[idx]); + exit(1); + } + } + + return; + +err_notfound: + fprintf(stderr, "Unable to find device \"%s\"\n", av[idx-1]); + exit(1); +err: + fprintf(stderr, "%s\n", nl_geterror()); + exit(1); +} diff --git a/src/genl-ctrl-dump.c b/src/genl-ctrl-dump.c new file mode 100644 index 0000000..98a26a1 --- /dev/null +++ b/src/genl-ctrl-dump.c @@ -0,0 +1,65 @@ +/* + * src/genl-ctrl-dump.c Dump Generic Netlink Controller + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf( + "Usage: genl-ctrl-dump []\n" + " mode := { brief | detailed | stats }\n" + " filter := \n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *family_cache; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_BRIEF + }; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 2 || !strcmp(argv[1], "-h")) + print_usage(); + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + if (genl_connect(nlh) < 0) { + fprintf(stderr, "Unable to connect generic netlink socket%s\n", + nl_geterror()); + goto errout; + } + + family_cache = nltool_alloc_genl_family_cache(nlh); + if (!family_cache) + goto errout; + + params.dp_type = nltool_parse_dumptype(argv[1]); + if (params.dp_type < 0) + goto errout; + + //get_filter(link, argc, argv, 2, link_cache); + nl_cache_dump(family_cache, ¶ms); + nl_cache_free(family_cache); + err = 0; +errout: + nl_close(nlh); + nl_handle_destroy(nlh); + return err; +} diff --git a/src/nf-ct-dump.c b/src/nf-ct-dump.c new file mode 100644 index 0000000..774c91b --- /dev/null +++ b/src/nf-ct-dump.c @@ -0,0 +1,88 @@ +/* + * src/nf-ct-dump.c Dump conntrack attributes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#include "utils.h" +#include + +#include "f_ct.c" + +static void print_usage(void) +{ + printf( + "Usage: nf-ct-dump []\n" + " mode := { brief | detailed | stats | xml }\n" + " filter := [family FAMILY] [proto PROTO] [tcpstate TCPSTATE]\n" + " [status STATUS] [timeout TIMEOUT] [mark MARK] [use USE] [id ID]\n" + " [origsrc ADDR] [origdst ADDR] [origsrcport PORT] [origdstport PORT]\n" + " [origicmpid ID] [origicmptype TYPE] [origicmpcode CODE]\n" + " [origpackets PACKETS] [origbytes BYTES]\n" + " [replysrc ADDR] [replydst ADDR] [replysrcport PORT] [replydstport PORT]\n" + " [replyicmpid ID] [replyicmptype TYPE] [replyicmpcode CODE]\n" + " [replypackets PACKETS] [replybytes BYTES]\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *ct_cache; + struct nfnl_ct *ct; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_BRIEF + }; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 2 || !strcmp(argv[1], "-h")) + print_usage(); + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + ct = nfnl_ct_alloc(); + if (!ct) + goto errout; + + if (nltool_connect(nlh, NETLINK_NETFILTER) < 0) + goto errout_free; + + ct_cache = nfnl_ct_alloc_cache(nlh); + if (!ct_cache) { + fprintf(stderr, "Unable to retrieve ct cache: %s\n", + nl_geterror()); + goto errout_close; + } + nl_cache_mngt_provide(ct_cache); + + params.dp_type = nltool_parse_dumptype(argv[1]); + if (params.dp_type < 0) + goto errout_ct_cache; + + get_filter(ct, argc, argv, 2); + nl_cache_dump_filter(ct_cache, ¶ms, (struct nl_object *) ct); + + err = 0; + +errout_ct_cache: + nl_cache_free(ct_cache); +errout_close: + nl_close(nlh); +errout_free: + nfnl_ct_put(ct); +errout: + return err; +} diff --git a/src/nf-log.c b/src/nf-log.c new file mode 100644 index 0000000..2c100f8 --- /dev/null +++ b/src/nf-log.c @@ -0,0 +1,142 @@ +/* + * src/nf-log.c Monitor netfilter log events + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#include +#include + +#include "utils.h" +#include +#include + +static void obj_input(struct nl_object *obj, void *arg) +{ + struct nl_dump_params dp = { + .dp_type = NL_DUMP_STATS, + .dp_fd = stdout, + .dp_dump_msgtype = 1, + }; + + nl_object_dump(obj, &dp); +} + +static int event_input(struct nl_msg *msg, void *arg) +{ + if (nl_msg_parse(msg, &obj_input, NULL) < 0) + fprintf(stderr, "<> Unknown message type\n"); + + /* Exit nl_recvmsgs_def() and return to the main select() */ + return NL_STOP; +} + +int main(int argc, char *argv[]) +{ + struct nl_handle *nfnlh; + struct nl_handle *rtnlh; + struct nl_cache *link_cache; + int err = 1; + int family, group; + + if (nltool_init(argc, argv) < 0) + return -1; + + nfnlh = nltool_alloc_handle(); + if (nfnlh == NULL) + return -1; + + nl_disable_sequence_check(nfnlh); + + nl_socket_modify_cb(nfnlh, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL); + + if ((argc > 1 && !strcasecmp(argv[1], "-h")) || argc < 3) { + printf("Usage: nf-log family group\n"); + return 2; + } + + if (nfnl_connect(nfnlh) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout; + } + + family = nl_str2af(argv[1]); + if (family == AF_UNSPEC) { + fprintf(stderr, "Unknown family: %s\n", argv[1]); + goto errout; + } + if (nfnl_log_pf_unbind(nfnlh, family) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout; + } + if (nfnl_log_pf_bind(nfnlh, family) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout; + } + + group = nl_str2af(argv[2]); + if (nfnl_log_bind(nfnlh, group) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout; + } + + if (nfnl_log_set_mode(nfnlh, 0, NFULNL_COPY_PACKET, 0xffff) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout; + } + + rtnlh = nltool_alloc_handle(); + if (rtnlh == NULL) { + goto errout_close; + } + + if (nl_connect(rtnlh, NETLINK_ROUTE) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout; + } + + if ((link_cache = rtnl_link_alloc_cache(rtnlh)) == NULL) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout_close; + } + + nl_cache_mngt_provide(link_cache); + + while (1) { + fd_set rfds; + int nffd, rtfd, maxfd, retval; + + FD_ZERO(&rfds); + + maxfd = nffd = nl_socket_get_fd(nfnlh); + FD_SET(nffd, &rfds); + + rtfd = nl_socket_get_fd(rtnlh); + FD_SET(rtfd, &rfds); + if (maxfd < rtfd) + maxfd = rtfd; + + /* wait for an incoming message on the netlink socket */ + retval = select(maxfd+1, &rfds, NULL, NULL, NULL); + + if (retval) { + if (FD_ISSET(nffd, &rfds)) + nl_recvmsgs_default(nfnlh); + if (FD_ISSET(rtfd, &rfds)) + nl_recvmsgs_default(rtnlh); + } + } + + nl_close(rtnlh); +errout_close: + nl_close(nfnlh); +errout: + return err; +} diff --git a/src/nf-monitor.c b/src/nf-monitor.c new file mode 100644 index 0000000..2bc58c9 --- /dev/null +++ b/src/nf-monitor.c @@ -0,0 +1,115 @@ +/* + * src/nf-monitor.c Monitor netfilter events + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + * Copyright (c) 2007 Philip Craig + * Copyright (c) 2007 Secure Computing Corporation + */ + +#include "utils.h" +#include + +static void obj_input(struct nl_object *obj, void *arg) +{ + struct nl_dump_params dp = { + .dp_type = NL_DUMP_STATS, + .dp_fd = stdout, + .dp_dump_msgtype = 1, + }; + + nl_object_dump(obj, &dp); +} + +static int event_input(struct nl_msg *msg, void *arg) +{ + if (nl_msg_parse(msg, &obj_input, NULL) < 0) + fprintf(stderr, "<> Unknown message type\n"); + + /* Exit nl_recvmsgs_def() and return to the main select() */ + return NL_STOP; +} + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + int err = 1; + int i, idx; + + static const struct { + enum nfnetlink_groups gr_id; + const char* gr_name; + } known_groups[] = { + { NFNLGRP_CONNTRACK_NEW, "ct-new" }, + { NFNLGRP_CONNTRACK_UPDATE, "ct-update" }, + { NFNLGRP_CONNTRACK_DESTROY, "ct-destroy" }, + { NFNLGRP_NONE, NULL } + }; + + if (nltool_init(argc, argv) < 0) + return -1; + + nlh = nltool_alloc_handle(); + if (nlh == NULL) + return -1; + + nl_disable_sequence_check(nlh); + + nl_socket_modify_cb(nlh, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL); + + if (argc > 1 && !strcasecmp(argv[1], "-h")) { + printf("Usage: nf-monitor []\n"); + + printf("Known groups:"); + for (i = 0; known_groups[i].gr_id != NFNLGRP_NONE; i++) + printf(" %s", known_groups[i].gr_name); + printf("\n"); + return 2; + } + + if (nfnl_connect(nlh) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout; + } + + for (idx = 1; argc > idx; idx++) { + for (i = 0; known_groups[i].gr_id != NFNLGRP_NONE; i++) { + if (!strcmp(argv[idx], known_groups[i].gr_name)) { + + if (nl_socket_add_membership(nlh, known_groups[i].gr_id) < 0) { + fprintf(stderr, "%s: %s\n", argv[idx], nl_geterror()); + goto errout; + } + + break; + } + } + if (known_groups[i].gr_id == NFNLGRP_NONE) + fprintf(stderr, "Warning: Unknown group: %s\n", argv[idx]); + } + + while (1) { + fd_set rfds; + int fd, retval; + + fd = nl_socket_get_fd(nlh); + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + /* wait for an incoming message on the netlink socket */ + retval = select(fd+1, &rfds, NULL, NULL, NULL); + + if (retval) { + /* FD_ISSET(fd, &rfds) will be true */ + nl_recvmsgs_default(nlh); + } + } + + nl_close(nlh); +errout: + return err; +} diff --git a/src/nl-addr-add.c b/src/nl-addr-add.c new file mode 100644 index 0000000..de5ddcd --- /dev/null +++ b/src/nl-addr-add.c @@ -0,0 +1,68 @@ +/* + * src/nl-addr-add.c Add addresses + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct rtnl_addr *addr; + struct nl_addr *local; + int err = 1; + + if (argc < 3 || !strcmp(argv[1], "-h")) { + printf("Usage: nl-addr-add \n"); + goto errout; + } + + if (nltool_init(argc, argv) < 0) + goto errout; + + nlh = nltool_alloc_handle(); + if (!nlh) + goto errout; + + addr = rtnl_addr_alloc(); + if (!addr) + goto errout_free_handle; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_free_addr; + + local = nltool_addr_parse(argv[1]); + if (!local) + goto errout_close; + + if (rtnl_addr_set_local(addr, local) < 0) { + fprintf(stderr, "Unable to set local address: %s\n", + nl_geterror()); + goto errout_put_addr; + } + + rtnl_addr_set_ifindex(addr, strtoul(argv[2], NULL, 0)); + + if (rtnl_addr_add(nlh, addr, 0) < 0) { + fprintf(stderr, "Unable to add address: %s\n", nl_geterror()); + goto errout_close; + } + + err = 0; +errout_put_addr: + nl_addr_put(local); +errout_close: + nl_close(nlh); +errout_free_addr: + rtnl_addr_put(addr); +errout_free_handle: + nl_handle_destroy(nlh); +errout: + return err; +} diff --git a/src/nl-addr-delete.c b/src/nl-addr-delete.c new file mode 100644 index 0000000..19ee6c5 --- /dev/null +++ b/src/nl-addr-delete.c @@ -0,0 +1,70 @@ +/* + * src/nl-addr-delete.c Delete addresses + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct rtnl_addr *addr; + struct nl_addr *local; + int err = 1; + + if (argc < 3 || !strcmp(argv[1], "-h")) { + printf("Usage: nl-addr-delete \n"); + goto errout; + } + + if (nltool_init(argc, argv) < 0) + goto errout; + + nlh = nltool_alloc_handle(); + if (!nlh) + goto errout; + + addr = rtnl_addr_alloc(); + if (!addr) + goto errout_free_handle; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_free_addr; + + local = nltool_addr_parse(argv[1]); + if (!local) + goto errout_close; + + if (rtnl_addr_set_local(addr, local) < 0) { + fprintf(stderr, "Unable to set local address: %s\n", + nl_geterror()); + goto errout_addr_put; + } + + rtnl_addr_set_ifindex(addr, strtoul(argv[2], NULL, 0)); + + if (rtnl_addr_delete(nlh, addr, 0) < 0) { + fprintf(stderr, "Unable to delete address: %s\n", + nl_geterror()); + goto errout_addr_put; + } + + err = 0; + +errout_addr_put: + nl_addr_put(local); +errout_close: + nl_close(nlh); +errout_free_addr: + rtnl_addr_put(addr); +errout_free_handle: + nl_handle_destroy(nlh); +errout: + return err; +} diff --git a/src/nl-addr-dump.c b/src/nl-addr-dump.c new file mode 100644 index 0000000..5dcd53b --- /dev/null +++ b/src/nl-addr-dump.c @@ -0,0 +1,82 @@ +/* + * src/nl-addr-dump.c Dump address attributes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf( + "Usage: nl-addr-dump []\n" + " mode := { brief | detailed | stats | xml }\n" + " filter := [dev DEV] [label LABEL] [family FAMILY] [scope SCOPE]\n" + " [local ADDR] [peer ADDR] [broadcast ADDR] [anycast ADDR]\n" + " [multicast ADDR]\n"); + exit(1); +} + +#include "f_addr.c" + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache, *addr_cache; + struct rtnl_addr *addr; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_BRIEF + }; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + addr = rtnl_addr_alloc(); + if (!addr) + goto errout; + + if (argc < 2 || !strcmp(argv[1], "-h")) + print_usage(); + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_free; + + link_cache = nltool_alloc_link_cache(nlh); + if (!link_cache) + goto errout_close; + + addr_cache = nltool_alloc_addr_cache(nlh); + if (!addr_cache) + goto errout_link_cache; + + params.dp_type = nltool_parse_dumptype(argv[1]); + if (params.dp_type < 0) + goto errout_addr_cache; + get_filter(addr, argc, argv, 2, link_cache); + + nl_cache_dump_filter(addr_cache, ¶ms, (struct nl_object *) addr); + + err = 0; + +errout_addr_cache: + nl_cache_free(addr_cache); +errout_link_cache: + nl_cache_free(link_cache); +errout_close: + nl_close(nlh); +errout_free: + rtnl_addr_put(addr); +errout: + return err; +} diff --git a/src/nl-fib-lookup.c b/src/nl-fib-lookup.c new file mode 100644 index 0000000..5bbf91e --- /dev/null +++ b/src/nl-fib-lookup.c @@ -0,0 +1,130 @@ +/* + * src/nl-fib-lookup.c FIB Route Lookup + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf( + "Usage: nl-fib-lookup [options] \n" + "Options:\n" + " -t, --table Table id\n" + " -f, --fwmark Firewall mark\n" + " -s, --scope Routing scope\n" + " -T, --tos Type of Service\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *result; + struct flnl_request *request; + struct nl_addr *addr; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_FULL, + }; + int table = RT_TABLE_UNSPEC, scope = RT_SCOPE_UNIVERSE; + int tos = 0, err = 1; + uint64_t fwmark = 0; + + if (nltool_init(argc, argv) < 0) + return -1; + + while (1) { + static struct option long_opts[] = { + {"table", 1, 0, 't'}, + {"fwmark", 1, 0, 'f'}, + {"scope", 1, 0, 's'}, + {"tos", 1, 0, 'T'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0}, + }; + int c, idx = 0; + + c = getopt_long(argc, argv, "t:f:s:T:h", long_opts, &idx); + if (c == -1) + break; + + switch (c) { + case 't': + table = strtoul(optarg, NULL, 0); + break; + case 'f': + fwmark = strtoul(optarg, NULL, 0); + break; + case 's': + scope = strtoul(optarg, NULL, 0); + break; + case 'T': + tos = strtoul(optarg, NULL, 0); + break; + default: + print_usage(); + } + } + + if (optind >= argc) + print_usage(); + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + addr = nl_addr_parse(argv[optind], AF_INET); + if (!addr) { + fprintf(stderr, "Unable to parse address \"%s\": %s\n", + argv[optind], nl_geterror()); + goto errout; + } + + result = flnl_result_alloc_cache(); + if (!result) + goto errout_addr; + + request = flnl_request_alloc(); + if (!request) + goto errout_result; + + flnl_request_set_table(request, table); + flnl_request_set_fwmark(request, fwmark); + flnl_request_set_scope(request, scope); + flnl_request_set_tos(request, tos); + + err = flnl_request_set_addr(request, addr); + nl_addr_put(addr); + if (err < 0) + goto errout_put; + + if (nltool_connect(nlh, NETLINK_FIB_LOOKUP) < 0) + goto errout_put; + + err = flnl_lookup(nlh, request, result); + if (err < 0) { + fprintf(stderr, "Unable to lookup: %s\n", nl_geterror()); + goto errout_put; + } + + nl_cache_dump(result, ¶ms); + + err = 0; +errout_put: + nl_object_put(OBJ_CAST(request)); +errout_result: + nl_cache_free(result); +errout_addr: + nl_addr_put(addr); +errout: + nl_close(nlh); + nl_handle_destroy(nlh); + return err; +} diff --git a/src/nl-link-dump.c b/src/nl-link-dump.c new file mode 100644 index 0000000..0214025 --- /dev/null +++ b/src/nl-link-dump.c @@ -0,0 +1,75 @@ +/* + * src/nl-link-dump.c Dump link attributes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf( + "Usage: nl-link-dump []\n" + " mode := { brief | detailed | stats | xml }\n" + " filter := [dev DEV] [mtu MTU] [txqlen TXQLEN] [weight WEIGHT] [link LINK]\n" + " [master MASTER] [qdisc QDISC] [addr ADDR] [broadcast BRD]\n" + " [{ up | down }] [{ arp | noarp }] [{ promisc | nopromisc }]\n" + " [{ dynamic | nodynamic }] [{ multicast | nomulticast }]\n" + " [{ trailers | notrailers }] [{ allmulticast | noallmulticast }]\n"); + exit(1); +} + +#include "f_link.c" + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache; + struct rtnl_link *link; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_BRIEF + }; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 2 || !strcmp(argv[1], "-h")) + print_usage(); + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + link = rtnl_link_alloc(); + if (!link) + goto errout; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_put; + + link_cache = nltool_alloc_link_cache(nlh); + if (!link_cache) + goto errout_put; + + params.dp_type = nltool_parse_dumptype(argv[1]); + if (params.dp_type < 0) + goto errout_put; + + get_filter(link, argc, argv, 2, link_cache); + nl_cache_dump_filter(link_cache, ¶ms, (struct nl_object *) link); + nl_cache_free(link_cache); + err = 0; +errout_put: + rtnl_link_put(link); +errout: + nl_close(nlh); + nl_handle_destroy(nlh); + return err; +} diff --git a/src/nl-link-ifindex2name.c b/src/nl-link-ifindex2name.c new file mode 100644 index 0000000..e1043fb --- /dev/null +++ b/src/nl-link-ifindex2name.c @@ -0,0 +1,55 @@ +/* + * src/nl-link-ifindex2name.c Transform a interface index to its name + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +int main(int argc, char **argv) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache; + int err = -1, ifindex; + char dst[32] = {0}; + const char *name; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 2 || !strcmp(argv[1], "-h")) { + fprintf(stderr, "Usage: nl-link-ifindex2name \n"); + return -1; + } + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout; + + link_cache = nltool_alloc_link_cache(nlh); + if (!link_cache) + goto errout; + + ifindex = strtoul(argv[1], NULL, 0); + + if (!(name = rtnl_link_i2name(link_cache, ifindex, dst, sizeof(dst)))) + fprintf(stderr, "Interface index %d does not exist\n", ifindex); + else + printf("%s\n", name); + + nl_cache_free(link_cache); + err = 0; +errout: + nl_close(nlh); + nl_handle_destroy(nlh); + + return err; +} diff --git a/src/nl-link-name2ifindex.c b/src/nl-link-name2ifindex.c new file mode 100644 index 0000000..993397d --- /dev/null +++ b/src/nl-link-name2ifindex.c @@ -0,0 +1,51 @@ +/* + * src/nl-link-name2ifindex.c Transform a interface name to its index + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache; + int err = -1, ifindex; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 2 || !strcmp(argv[1], "-h")) { + printf("Usage: nl-link-name2ifindex \n"); + return -1; + } + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout; + + link_cache = nltool_alloc_link_cache(nlh); + if (!link_cache) + goto errout; + + if ((ifindex = rtnl_link_name2i(link_cache, argv[1])) == RTNL_LINK_NOT_FOUND) + fprintf(stderr, "Interface %s does not exist\n", argv[1]); + else + printf("%d\n", ifindex); + + nl_cache_free(link_cache); + err = 0; +errout: + nl_close(nlh); + nl_handle_destroy(nlh); + + return err; +} diff --git a/src/nl-link-set.c b/src/nl-link-set.c new file mode 100644 index 0000000..1872301 --- /dev/null +++ b/src/nl-link-set.c @@ -0,0 +1,83 @@ +/* + * src/nl-link-set.c Set link attributes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf( + "Usage: nl-link-set \n" + " changes := [dev DEV] [mtu MTU] [txqlen TXQLEN] [weight WEIGHT] [link LINK]\n" + " [master MASTER] [qdisc QDISC] [addr ADDR] [broadcast BRD]\n" + " [{ up | down }] [{ arp | noarp }] [{ promisc | nopromisc }]\n" + " [{ dynamic | nodynamic }] [{ multicast | nomulticast }]\n" + " [{ trailers | notrailers }] [{ allmulticast | noallmulticast }]\n"); + exit(1); +} + +#include "f_link.c" + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache; + struct rtnl_link *link, *orig; + int err = 1, ifindex; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 2 || !strcmp(argv[1], "-h")) + print_usage(); + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + link = rtnl_link_alloc(); + if (!link) + goto errout; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_free; + + link_cache = nltool_alloc_link_cache(nlh); + if (!link_cache) + goto errout_close; + + ifindex = strtoul(argv[1], NULL, 0); + + if (!(orig = rtnl_link_get(link_cache, ifindex))) { + fprintf(stderr, "Interface index %d does not exist\n", ifindex); + goto errout_cache; + } + + get_filter(link, argc, argv, 2, link_cache); + + if (rtnl_link_change(nlh, orig, link, 0) < 0) { + fprintf(stderr, "Unable to change link: %s\n", nl_geterror()); + goto errout_put; + } + + err = 0; + +errout_put: + rtnl_link_put(orig); +errout_cache: + nl_cache_free(link_cache); +errout_close: + nl_close(nlh); +errout_free: + rtnl_link_put(link); +errout: + nl_handle_destroy(nlh); + return err; +} diff --git a/src/nl-link-stats.c b/src/nl-link-stats.c new file mode 100644 index 0000000..8a9200e --- /dev/null +++ b/src/nl-link-stats.c @@ -0,0 +1,109 @@ +/* + * src/nl-link-stats.c Retrieve link statistics + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf( +"Usage: nl-link-stats [ ...]\n" +" ifindex := { all | number }\n" +" statistic := { (rx|tx)_packets | (rx|tx)_bytes | (rx|tx)_errors |\n" +" (rx|tx)_dropped | (rx|tx)_compressed | (rx|tx)_fifo_err |\n" \ +" rx_len_err | rx_over_err | rx_crc_err | rx_frame_err |\n" +" rx_missed_err | tx_abort_err | tx_carrier_err |\n" +" tx_hbeat_err | tx_win_err | tx_collision | multicast }\n"); + exit(1); +} + +static char **gargv; +static int gargc; + +static void dump_stat(struct rtnl_link *link, int id) +{ + uint64_t st = rtnl_link_get_stat(link, id); + char buf[62]; + + printf("%s.%s %" PRIu64 "\n", rtnl_link_get_name(link), + rtnl_link_stat2str(id, buf, sizeof(buf)), st); +} + +static void dump_stats(struct nl_object *obj, void *arg) +{ + int i; + struct rtnl_link *link = (struct rtnl_link *) obj; + + if (!strcasecmp(gargv[0], "all")) { + for (i = 0; i < RTNL_LINK_STATS_MAX; i++) + dump_stat(link, i); + } else { + for (i = 0; i < gargc; i++) { + int id = rtnl_link_str2stat(gargv[i]); + + if (id < 0) + fprintf(stderr, "Warning: Unknown statistic " + "\"%s\"\n", gargv[i]); + else + dump_stat(link, id); + } + } +} + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 3 || !strcmp(argv[1], "-h")) + print_usage(); + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout; + + link_cache = nltool_alloc_link_cache(nlh); + if (!link_cache) + goto errout_close; + + gargv = &argv[2]; + gargc = argc - 2; + + if (!strcasecmp(argv[1], "all")) + nl_cache_foreach(link_cache, dump_stats, NULL); + else { + int ifindex = strtoul(argv[1], NULL, 0); + struct rtnl_link *link = rtnl_link_get(link_cache, ifindex); + + if (!link) { + fprintf(stderr, "Could not find ifindex %d\n", ifindex); + goto errout_link_cache; + } + + dump_stats((struct nl_object *) link, NULL); + rtnl_link_put(link); + } + + err = 0; +errout_link_cache: + nl_cache_free(link_cache); +errout_close: + nl_close(nlh); +errout: + nl_handle_destroy(nlh); + return err; +} diff --git a/src/nl-list-caches.c b/src/nl-list-caches.c new file mode 100644 index 0000000..db71d6d --- /dev/null +++ b/src/nl-list-caches.c @@ -0,0 +1,119 @@ +/* + * nl-list-caches.c List registered cache types + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + fprintf(stderr, "Usage: nl-list-caches\n"); + exit(1); +} + +static char *id_attr_list(struct nl_object_ops *ops, char *buf, size_t len) +{ + if (ops->oo_attrs2str != NULL) + return ops->oo_attrs2str(ops->oo_id_attrs, buf, len); + else { + memset(buf, 0, len); + return buf; + } +} + +static void print(struct nl_cache_ops *ops, void *arg) +{ + char buf[64]; + + printf("%s:\n" \ + " hdrsize: %d bytes\n" \ + " protocol: %s\n" \ + " request-update: %s\n" \ + " msg-parser: %s\n", + ops->co_name, ops->co_hdrsize, + nl_nlfamily2str(ops->co_protocol, buf, sizeof(buf)), + ops->co_request_update ? "yes" : "no", + ops->co_msg_parser ? "yes" : "no"); + + if (ops->co_obj_ops) { + struct nl_object_ops *obj_ops = ops->co_obj_ops; + const char *dump_names[NL_DUMP_MAX+1] = { + "brief", + "detailed", + "stats", + "xml", + "env", + "events" + }; + int i; + + printf(" cacheable object:\n" \ + " name: %s:\n" \ + " size: %d bytes\n" \ + " constructor: %s\n" \ + " free-data: %s\n" \ + " clone: %s\n" \ + " compare: %s\n" \ + " id attributes: %s\n" \ + " dump: ", + obj_ops->oo_name, obj_ops->oo_size, + obj_ops->oo_constructor ? "yes" : "no", + obj_ops->oo_free_data ? "yes" : "no", + obj_ops->oo_clone ? "yes" : "no", + obj_ops->oo_compare ? "yes" : "no", + id_attr_list(obj_ops, buf, sizeof(buf))); + + for (i = 0; i <= NL_DUMP_MAX; i++) + if (obj_ops->oo_dump[i]) + printf("%s%s", + i == 0 ? "" : ", ", + dump_names[i]); + + printf("\n"); + } + + if (ops->co_genl) { + struct genl_ops *genl_ops = ops->co_genl; + + printf(" genl:\n" \ + " name: %s\n" \ + " family: %d\n" \ + " id: %d\n", + genl_ops->o_name, genl_ops->o_family, genl_ops->o_id); + + if (genl_ops->o_ncmds) { + int i; + + printf(" cmds:\n"); + + for (i = 0; i < genl_ops->o_ncmds; i++) { + struct genl_cmd *cmd = &genl_ops->o_cmds[i]; + + printf(" %s:\n" + " id: %d\n" \ + " maxattr: %d\n" \ + " msg-parser: %s\n" \ + " attr-policy: %s\n", + cmd->c_name, cmd->c_id, cmd->c_maxattr, + cmd->c_msg_parser ? "yes" : "no", + cmd->c_attr_policy ? "yes" : "no"); + } + } + } +} + +int main(int argc, char *argv[]) +{ + if (argc > 1 && !strcasecmp(argv[1], "-h")) + print_usage(); + + nl_cache_mngt_foreach(print, NULL); + + return 0; +} diff --git a/src/nl-list-sockets.c b/src/nl-list-sockets.c new file mode 100644 index 0000000..9a1333c --- /dev/null +++ b/src/nl-list-sockets.c @@ -0,0 +1,58 @@ +/* + * nl-list-sockets.c Pretty-print /proc/net/netlink + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +#define PROC_NETLINK "/proc/net/netlink" + +static void print_usage(void) +{ + fprintf(stderr, "Usage: nl-list-sockets []\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + FILE *fd; + char buf[2048], p[64]; + + if (argc > 1 && !strcasecmp(argv[1], "-h")) + print_usage(); + + fd = fopen(PROC_NETLINK, "r"); + if (fd == NULL) { + perror("fopen"); + return -1; + } + + printf("Address Family PID Groups rmem wmem " \ + "CB refcnt\n"); + + while (fgets(buf, sizeof(buf), fd)) { + unsigned long sk, cb; + int ret, proto, pid, rmem, wmem, refcnt; + uint32_t groups; + + ret = sscanf(buf, "%lx %d %d %08x %d %d %lx %d\n", + &sk, &proto, &pid, &groups, &rmem, &wmem, + &cb, &refcnt); + if (ret != 8) + continue; + + printf("0x%08lx %-16s %-6d %08x %-6d %-6d 0x%08lx %d\n", + sk, nl_nlfamily2str(proto, p, sizeof(p)), pid, + groups, rmem, wmem, cb, refcnt); + } + + fclose(fd); + + return 0; +} diff --git a/src/nl-monitor.c b/src/nl-monitor.c new file mode 100644 index 0000000..9f21dbb --- /dev/null +++ b/src/nl-monitor.c @@ -0,0 +1,135 @@ +/* + * src/nl-monitor.c Monitor events + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" +#include +#include + +static void obj_input(struct nl_object *obj, void *arg) +{ + struct nl_dump_params dp = { + .dp_type = NL_DUMP_STATS, + .dp_fd = stdout, + .dp_dump_msgtype = 1, + }; + + nl_object_dump(obj, &dp); +} + +static int event_input(struct nl_msg *msg, void *arg) +{ + if (nl_msg_parse(msg, &obj_input, NULL) < 0) + fprintf(stderr, "<> Unknown message type\n"); + + /* Exit nl_recvmsgs_def() and return to the main select() */ + return NL_STOP; +} + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache; + int err = 1; + int i, idx; + + static const struct { + enum rtnetlink_groups gr_id; + const char* gr_name; + } known_groups[] = { + { RTNLGRP_LINK, "link" }, + { RTNLGRP_NOTIFY, "notify" }, + { RTNLGRP_NEIGH, "neigh" }, + { RTNLGRP_TC, "tc" }, + { RTNLGRP_IPV4_IFADDR, "ipv4-ifaddr" }, + { RTNLGRP_IPV4_MROUTE, "ipv4-mroute" }, + { RTNLGRP_IPV4_ROUTE, "ipv4-route" }, + { RTNLGRP_IPV6_IFADDR, "ipv6-ifaddr" }, + { RTNLGRP_IPV6_MROUTE, "ipv6-mroute" }, + { RTNLGRP_IPV6_ROUTE, "ipv6-route" }, + { RTNLGRP_IPV6_IFINFO, "ipv6-ifinfo" }, + { RTNLGRP_DECnet_IFADDR, "decnet-ifaddr" }, + { RTNLGRP_DECnet_ROUTE, "decnet-route" }, + { RTNLGRP_IPV6_PREFIX, "ipv6-prefix" }, + { RTNLGRP_NONE, NULL } + }; + + if (nltool_init(argc, argv) < 0) + return -1; + + nlh = nltool_alloc_handle(); + if (nlh == NULL) + return -1; + + nl_disable_sequence_check(nlh); + + nl_socket_modify_cb(nlh, NL_CB_VALID, NL_CB_CUSTOM, event_input, NULL); + + if (argc > 1 && !strcasecmp(argv[1], "-h")) { + printf("Usage: nl-monitor []\n"); + + printf("Known groups:"); + for (i = 0; known_groups[i].gr_id != RTNLGRP_NONE; i++) + printf(" %s", known_groups[i].gr_name); + printf("\n"); + return 2; + } + + if (nl_connect(nlh, NETLINK_ROUTE) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout; + } + + for (idx = 1; argc > idx; idx++) { + for (i = 0; known_groups[i].gr_id != RTNLGRP_NONE; i++) { + if (!strcmp(argv[idx], known_groups[i].gr_name)) { + + if (nl_socket_add_membership(nlh, known_groups[i].gr_id) < 0) { + fprintf(stderr, "%s: %s\n", argv[idx], nl_geterror()); + goto errout; + } + + break; + } + } + if (known_groups[i].gr_id == RTNLGRP_NONE) + fprintf(stderr, "Warning: Unknown group: %s\n", argv[idx]); + } + + if ((link_cache = rtnl_link_alloc_cache(nlh)) == NULL) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout_close; + } + + nl_cache_mngt_provide(link_cache); + + while (1) { + fd_set rfds; + int fd, retval; + + fd = nl_socket_get_fd(nlh); + + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + /* wait for an incoming message on the netlink socket */ + retval = select(fd+1, &rfds, NULL, NULL, NULL); + + if (retval) { + /* FD_ISSET(fd, &rfds) will be true */ + nl_recvmsgs_default(nlh); + } + } + + nl_cache_free(link_cache); +errout_close: + nl_close(nlh); +errout: + return err; +} diff --git a/src/nl-neigh-add.c b/src/nl-neigh-add.c new file mode 100644 index 0000000..14b99de --- /dev/null +++ b/src/nl-neigh-add.c @@ -0,0 +1,79 @@ +/* + * src/ nl-neigh-add.c Add a neighbour + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct rtnl_neigh *neigh; + struct nl_addr *addr; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 4 || !strcmp(argv[1], "-h")) { + printf("Usage: nl-neigh-add " + " []\n"); + return 1; + } + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + neigh = rtnl_neigh_alloc(); + if (!neigh) + goto errout; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_free; + + addr = nltool_addr_parse(argv[1]); + if (!addr) + goto errout_close; + rtnl_neigh_set_dst(neigh, addr); + nl_addr_put(addr); + + addr = nltool_addr_parse(argv[2]); + if (!addr) + goto errout_close; + rtnl_neigh_set_lladdr(neigh, addr); + nl_addr_put(addr); + + rtnl_neigh_set_ifindex(neigh, strtoul(argv[3], NULL, 0)); + + if (argc > 4) { + int state = rtnl_neigh_str2state(argv[4]); + if (state < 0) { + fprintf(stderr, "Unknown state \"%s\"\n", argv[4]); + goto errout_close; + } + rtnl_neigh_set_state(neigh, state); + } else + rtnl_neigh_set_state(neigh, NUD_PERMANENT); + + if (rtnl_neigh_add(nlh, neigh, 0) < 0) { + fprintf(stderr, "Unable to add address: %s\n", nl_geterror()); + goto errout_close; + } + + err = 0; + +errout_close: + nl_close(nlh); +errout_free: + rtnl_neigh_put(neigh); +errout: + nl_handle_destroy(nlh); + return err; +} diff --git a/src/nl-neigh-delete.c b/src/nl-neigh-delete.c new file mode 100644 index 0000000..7829d34 --- /dev/null +++ b/src/nl-neigh-delete.c @@ -0,0 +1,66 @@ +/* + * src/nl-neigh-delete.c Delete a neighbour + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct rtnl_neigh *neigh; + struct nl_addr *addr; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 3 || !strcmp(argv[1], "-h")) { + printf("Usage: nl-neigh-delete \n"); + return 2; + } + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + neigh = rtnl_neigh_alloc(); + if (neigh == NULL) + goto errout; + + if (nl_connect(nlh, NETLINK_ROUTE) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout_free; + } + + addr = nl_addr_parse(argv[1], AF_UNSPEC); + if (addr == NULL) { + fprintf(stderr, "Invalid address \"%s\"\n", argv[1]); + goto errout_close; + } + rtnl_neigh_set_dst(neigh, addr); + nl_addr_put(addr); + + rtnl_neigh_set_ifindex(neigh, strtoul(argv[2], NULL, 0)); + + if (rtnl_neigh_delete(nlh, neigh, 0) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout_close; + } + + err = 0; + +errout_close: + nl_close(nlh); +errout_free: + rtnl_neigh_put(neigh); +errout: + nl_handle_destroy(nlh); + return err; +} diff --git a/src/nl-neigh-dump.c b/src/nl-neigh-dump.c new file mode 100644 index 0000000..4553f2e --- /dev/null +++ b/src/nl-neigh-dump.c @@ -0,0 +1,82 @@ +/* + * src/nl-neigh-dump.c Dump neighbour attributes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf( + "Usage: nl-neigh-dump []\n" + " mode := { brief | detailed | stats | xml }\n" + " filter := [dev DEV] [dst ADDR] [lladdr ADDR]\n"); + exit(1); +} + +#include "f_neigh.c" + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache, *neigh_cache; + struct rtnl_neigh *neigh; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_BRIEF + }; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 2 || !strcmp(argv[1], "-h")) + print_usage(); + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + neigh = rtnl_neigh_alloc(); + if (neigh == NULL) + goto errout; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_free; + + link_cache = nltool_alloc_link_cache(nlh); + if (!link_cache) + goto errout_close; + + neigh_cache = nltool_alloc_neigh_cache(nlh); + if (!neigh_cache) + goto errout_link_cache; + + params.dp_type = nltool_parse_dumptype(argv[1]); + if (params.dp_type < 0) + goto errout_neigh_cache; + + get_filter(neigh, argc, argv, 2, neigh_cache); + + nl_cache_dump_filter(neigh_cache, ¶ms, (struct nl_object *) neigh); + + err = 0; + +errout_neigh_cache: + nl_cache_free(neigh_cache); +errout_link_cache: + nl_cache_free(link_cache); +errout_close: + nl_close(nlh); +errout_free: + rtnl_neigh_put(neigh); +errout: + nl_handle_destroy(nlh); + return err; +} diff --git a/src/nl-neightbl-dump.c b/src/nl-neightbl-dump.c new file mode 100644 index 0000000..0d79711 --- /dev/null +++ b/src/nl-neightbl-dump.c @@ -0,0 +1,70 @@ +/* + * src/nl-neightbl-dump.c Dump neighbour tables + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf( + "Usage: nl-neightbl-dump []\n" + " mode := { brief | detailed | stats | xml }\n" + " filter :=\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + int err = -1; + struct nl_handle *nlh; + struct nl_cache *ntc, *lc; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_BRIEF, + }; + + if (argc < 2) + print_usage(); + + if (nltool_init(argc, argv) < 0) + return -1; + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout; + + ntc = nltool_alloc_neightbl_cache(nlh); + if (!ntc) + goto errout_close; + + lc = nltool_alloc_link_cache(nlh); + if (!lc) + goto errout_ntbl_cache; + + params.dp_type = nltool_parse_dumptype(argv[1]); + if (params.dp_type < 0) + goto errout_link_cache; + + nl_cache_dump(ntc, ¶ms); + err = 0; + +errout_link_cache: + nl_cache_free(lc); +errout_ntbl_cache: + nl_cache_free(ntc); +errout_close: + nl_close(nlh); +errout: + nl_handle_destroy(nlh); + return err; +} diff --git a/src/nl-qdisc-add.c b/src/nl-qdisc-add.c new file mode 100644 index 0000000..be19f9b --- /dev/null +++ b/src/nl-qdisc-add.c @@ -0,0 +1,196 @@ +/* + * src/nl-qdisc-dump.c Dump qdisc attributes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" +#include +#include + +static void print_usage(void) +{ + printf( +"Usage: nl-qdisc-add \n"); + exit(1); +} + +static int parse_blackhole_opts(struct rtnl_qdisc *qdisc, char *argv[], + int argc) +{ + return 0; +} + +static int parse_pfifo_opts(struct rtnl_qdisc *qdisc, char *argv[], int argc) +{ + int err, limit; + + if (argc > 0) { + if (argc != 2 || strcasecmp(argv[0], "limit")) { + fprintf(stderr, "Usage: ... pfifo limit \n"); + return -1; + } + + limit = strtoul(argv[1], NULL, 0); + err = rtnl_qdisc_fifo_set_limit(qdisc, limit); + if (err < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + return -1; + } + } + + return 0; +} + +static int parse_bfifo_opts(struct rtnl_qdisc *qdisc, char *argv[], int argc) +{ + int err, limit; + + if (argc > 0) { + if (argc != 2 || strcasecmp(argv[0], "limit")) { + fprintf(stderr, "Usage: ... bfifo limit \n"); + return -1; + } + + limit = nl_size2int(argv[1]); + if (limit < 0) { + fprintf(stderr, "Invalid value for limit.\n"); + return -1; + } + + err = rtnl_qdisc_fifo_set_limit(qdisc, limit); + if (err < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + return -1; + } + } + + return 0; +} + +static int parse_prio_opts(struct rtnl_qdisc *qdisc, char *argv[], int argc) +{ + int i, err, bands; + uint8_t map[] = QDISC_PRIO_DEFAULT_PRIOMAP; + + if (argc > 0) { + if (argc < 2 || strcasecmp(argv[0], "bands")) + goto usage; + + bands = strtoul(argv[1], NULL, 0); + err = rtnl_qdisc_prio_set_bands(qdisc, bands); + if (err < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + return -1; + } + } + + if (argc > 2) { + if (argc < 5 || strcasecmp(argv[2], "map")) + goto usage; + + for (i = 3; i < (argc & ~1U); i += 2) { + int prio, band; + + prio = rtnl_str2prio(argv[i]); + if (prio < 0 || prio > sizeof(map)/sizeof(map[0])) { + fprintf(stderr, "Invalid priority \"%s\"\n", + argv[i]); + return -1; + } + + band = strtoul(argv[i+1], NULL, 0); + + map[prio] = band; + } + } + + err = rtnl_qdisc_prio_set_priomap(qdisc, map, sizeof(map)); + if (err < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + return -1; + } + + return 0; +usage: + fprintf(stderr, "Usage: ... prio bands map MAP\n" + "MAP := \n"); + return -1; +} + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct rtnl_qdisc *qdisc; + uint32_t handle, parent; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 5 || !strcmp(argv[1], "-h")) + print_usage(); + + nlh = nltool_alloc_handle(); + if (!nlh) + goto errout; + + qdisc = rtnl_qdisc_alloc(); + if (!qdisc) + goto errout_free_handle; + + rtnl_qdisc_set_ifindex(qdisc, strtoul(argv[1], NULL, 0)); + + if (rtnl_tc_str2handle(argv[2], &handle) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout_free_qdisc; + } + + if (rtnl_tc_str2handle(argv[3], &parent) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout_free_qdisc; + } + + rtnl_qdisc_set_handle(qdisc, handle); + rtnl_qdisc_set_parent(qdisc, parent); + rtnl_qdisc_set_kind(qdisc, argv[4]); + + if (!strcasecmp(argv[4], "blackhole")) + err = parse_blackhole_opts(qdisc, &argv[5], argc-5); + else if (!strcasecmp(argv[4], "pfifo")) + err = parse_pfifo_opts(qdisc, &argv[5], argc-5); + else if (!strcasecmp(argv[4], "bfifo")) + err = parse_bfifo_opts(qdisc, &argv[5], argc-5); + else if (!strcasecmp(argv[4], "prio")) + err = parse_prio_opts(qdisc, &argv[5], argc-5); + else { + fprintf(stderr, "Unknown qdisc \"%s\"\n", argv[4]); + goto errout_free_qdisc; + } + + if (err < 0) + goto errout_free_qdisc; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_free_qdisc; + + if (rtnl_qdisc_add(nlh, qdisc, NLM_F_REPLACE) < 0) { + fprintf(stderr, "Unable to add Qdisc: %s\n", nl_geterror()); + goto errout_close; + } + + err = 0; +errout_close: + nl_close(nlh); +errout_free_qdisc: + rtnl_qdisc_put(qdisc); +errout_free_handle: + nl_handle_destroy(nlh); +errout: + return err; +} diff --git a/src/nl-qdisc-delete.c b/src/nl-qdisc-delete.c new file mode 100644 index 0000000..b8a17ca --- /dev/null +++ b/src/nl-qdisc-delete.c @@ -0,0 +1,76 @@ +/* + * src/nl-qdisc-delete.c Delete Qdiscs + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf("Usage: nl-qdisc-delete \n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct rtnl_qdisc *qdisc; + uint32_t handle, parent; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 3 || !strcmp(argv[1], "-h")) + print_usage(); + + nlh = nltool_alloc_handle(); + if (!nlh) + goto errout; + + qdisc = rtnl_qdisc_alloc(); + if (!qdisc) + goto errout_free_handle; + + rtnl_qdisc_set_ifindex(qdisc, strtoul(argv[1], NULL, 0)); + + if (rtnl_tc_str2handle(argv[2], &parent) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout_free_qdisc; + } + + if (argc > 3) { + if (rtnl_tc_str2handle(argv[3], &handle) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout_free_qdisc; + } + + rtnl_qdisc_set_handle(qdisc, handle); + } + + rtnl_qdisc_set_parent(qdisc, parent); + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_free_qdisc; + + if (rtnl_qdisc_delete(nlh, qdisc) < 0) { + fprintf(stderr, "Unable to delete Qdisc: %s\n", nl_geterror()); + goto errout_close; + } + + err = 0; +errout_close: + nl_close(nlh); +errout_free_qdisc: + rtnl_qdisc_put(qdisc); +errout_free_handle: + nl_handle_destroy(nlh); +errout: + return err; +} diff --git a/src/nl-qdisc-dump.c b/src/nl-qdisc-dump.c new file mode 100644 index 0000000..167dc7f --- /dev/null +++ b/src/nl-qdisc-dump.c @@ -0,0 +1,74 @@ +/* + * src/nl-qdisc-dump.c Dump qdisc attributes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf( +"Usage: nl-qdisc-dump \n" +" mode := { brief | detailed | stats | xml }\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache, *qdisc_cache; + struct rtnl_qdisc *qdisc; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_BRIEF + }; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 2 || !strcmp(argv[1], "-h")) + print_usage(); + + params.dp_type = nltool_parse_dumptype(argv[1]); + if (params.dp_type < 0) + return -1; + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + qdisc = rtnl_qdisc_alloc(); + if (!qdisc) + goto errout_no_put; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout; + + link_cache = nltool_alloc_link_cache(nlh); + if (!link_cache) + goto errout; + + qdisc_cache = nltool_alloc_qdisc_cache(nlh); + if (!qdisc_cache) + goto errout_link_cache; + + nl_cache_dump_filter(qdisc_cache, ¶ms, (struct nl_object *) qdisc); + nl_cache_free(qdisc_cache); + err = 0; + +errout_link_cache: + nl_cache_free(link_cache); +errout: + rtnl_qdisc_put(qdisc); +errout_no_put: + nl_close(nlh); + nl_handle_destroy(nlh); + return err; +} diff --git a/src/nl-route-add.c b/src/nl-route-add.c new file mode 100644 index 0000000..2686397 --- /dev/null +++ b/src/nl-route-add.c @@ -0,0 +1,76 @@ +/* + * src/nl-route-dump.c Dump route attributes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf( + "Usage: nl-route-add []\n"); + exit(1); +} + +#include "f_route.c" + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache, *route_cache; + struct rtnl_route *route; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 2 || !strcmp(argv[1], "-h")) + print_usage(); + + nlh = nltool_alloc_handle(); + if (!nlh) + goto errout; + + route = rtnl_route_alloc(); + if (!route) + goto errout; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_free; + + link_cache = nltool_alloc_link_cache(nlh); + if (!link_cache) + goto errout_close; + + route_cache = nltool_alloc_route_cache(nlh); + if (!route_cache) + goto errout_link_cache; + + get_filter(route, argc, argv, 1, route_cache, link_cache); + + if (rtnl_route_add(nlh, route, 0) < 0) { + fprintf(stderr, "rtnl_route_add failed: %s\n", + nl_geterror()); + goto errout_route_cache; + } + + err = 0; + +errout_route_cache: + nl_cache_free(route_cache); +errout_link_cache: + nl_cache_free(link_cache); +errout_close: + nl_close(nlh); +errout_free: + rtnl_route_put(route); +errout: + nl_handle_destroy(nlh); + return err; +} diff --git a/src/nl-route-del.c b/src/nl-route-del.c new file mode 100644 index 0000000..9d912e1 --- /dev/null +++ b/src/nl-route-del.c @@ -0,0 +1,76 @@ +/* + * src/nl-route-del.c Delete Routes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf( + "Usage: nl-route-del []\n"); + exit(1); +} + +#include "f_route.c" + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache, *route_cache; + struct rtnl_route *route; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 2 || !strcmp(argv[1], "-h")) + print_usage(); + + nlh = nltool_alloc_handle(); + if (!nlh) + goto errout; + + route = rtnl_route_alloc(); + if (!route) + goto errout; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_free; + + link_cache = nltool_alloc_link_cache(nlh); + if (!link_cache) + goto errout_close; + + route_cache = nltool_alloc_route_cache(nlh); + if (!route_cache) + goto errout_link_cache; + + get_filter(route, argc, argv, 1, route_cache, link_cache); + + if (rtnl_route_del(nlh, route, 0) < 0) { + fprintf(stderr, "rtnl_route_del failed: %s\n", + nl_geterror()); + goto errout_route_cache; + } + + err = 0; + +errout_route_cache: + nl_cache_free(route_cache); +errout_link_cache: + nl_cache_free(link_cache); +errout_close: + nl_close(nlh); +errout_free: + rtnl_route_put(route); +errout: + nl_handle_destroy(nlh); + return err; +} diff --git a/src/nl-route-dump.c b/src/nl-route-dump.c new file mode 100644 index 0000000..aed9bd2 --- /dev/null +++ b/src/nl-route-dump.c @@ -0,0 +1,81 @@ +/* + * src/nl-route-dump.c Dump route attributes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf( + "Usage: nl-route-dump []\n" + " mode := { brief | detailed | stats | xml }\n"); + exit(1); +} + +#include "f_route.c" + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache, *route_cache; + struct rtnl_route *route; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_BRIEF + }; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 2 || !strcmp(argv[1], "-h")) + print_usage(); + + nlh = nltool_alloc_handle(); + if (!nlh) + goto errout; + + route = rtnl_route_alloc(); + if (!route) + goto errout; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_free; + + link_cache = nltool_alloc_link_cache(nlh); + if (!link_cache) + goto errout_close; + + route_cache = nltool_alloc_route_cache(nlh); + if (!route_cache) + goto errout_link_cache; + + params.dp_type = nltool_parse_dumptype(argv[1]); + if (params.dp_type < 0) + goto errout_route_cache; + + get_filter(route, argc, argv, 2, route_cache, link_cache); + + nl_cache_dump_filter(route_cache, ¶ms, (struct nl_object *) route); + + err = 0; + +errout_route_cache: + nl_cache_free(route_cache); +errout_link_cache: + nl_cache_free(link_cache); +errout_close: + nl_close(nlh); +errout_free: + rtnl_route_put(route); +errout: + nl_handle_destroy(nlh); + return err; +} diff --git a/src/nl-route-get.c b/src/nl-route-get.c new file mode 100644 index 0000000..fd7a503 --- /dev/null +++ b/src/nl-route-get.c @@ -0,0 +1,106 @@ +/* + * src/nl-route-get.c Get Route Attributes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf("Usage: nl-route-get \n"); + exit(1); +} + +static int cb(struct nl_msg *msg, void *arg) +{ + nl_cache_parse_and_add(arg, msg); + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache, *route_cache; + struct nl_addr *dst; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_BRIEF + }; + int err = 1; + + if (argc < 2 || !strcmp(argv[1], "-h")) + print_usage(); + + if (nltool_init(argc, argv) < 0) + goto errout; + + nlh = nltool_alloc_handle(); + if (!nlh) + goto errout; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_free_handle; + + link_cache = nltool_alloc_link_cache(nlh); + if (!link_cache) + goto errout_close; + + dst = nltool_addr_parse(argv[1]); + if (!dst) + goto errout_link_cache; + + route_cache = nltool_alloc_route_cache(nlh); + if (!route_cache) + goto errout_addr_put; + + { + struct nl_msg *m; + struct rtmsg rmsg = { + .rtm_family = nl_addr_get_family(dst), + .rtm_dst_len = nl_addr_get_prefixlen(dst), + }; + + m = nlmsg_alloc_simple(RTM_GETROUTE, 0); + nlmsg_append(m, &rmsg, sizeof(rmsg), NLMSG_ALIGNTO); + nla_put_addr(m, RTA_DST, dst); + + if ((err = nl_send_auto_complete(nlh, m)) < 0) { + nlmsg_free(m); + fprintf(stderr, "%s\n", nl_geterror()); + goto errout_route_cache; + } + + nlmsg_free(m); + + nl_socket_modify_cb(nlh, NL_CB_VALID, NL_CB_CUSTOM, cb, + route_cache); + + if (nl_recvmsgs_default(nlh) < 0) { + fprintf(stderr, "%s\n", nl_geterror()); + goto errout_route_cache; + } + } + + nl_cache_dump(route_cache, ¶ms); + + err = 0; +errout_route_cache: + nl_cache_free(route_cache); +errout_addr_put: + nl_addr_put(dst); +errout_link_cache: + nl_cache_free(link_cache); +errout_close: + nl_close(nlh); +errout_free_handle: + nl_handle_destroy(nlh); +errout: + return err; +} diff --git a/src/nl-rule-dump.c b/src/nl-rule-dump.c new file mode 100644 index 0000000..f9e483a --- /dev/null +++ b/src/nl-rule-dump.c @@ -0,0 +1,78 @@ +/* + * src/nl-rule-dump.c Dump rule attributes + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +static void print_usage(void) +{ + printf( + "Usage: nl-rule-dump []\n" + " mode := { brief | detailed | stats | xml }\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + struct nl_handle *nlh; + struct nl_cache *link_cache, *rule_cache; + struct rtnl_rule *rule; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_BRIEF + }; + int err = 1; + + if (nltool_init(argc, argv) < 0) + return -1; + + if (argc < 2 || !strcmp(argv[1], "-h")) + print_usage(); + + nlh = nltool_alloc_handle(); + if (!nlh) + return -1; + + rule = rtnl_rule_alloc(); + if (!rule) + goto errout; + + if (nltool_connect(nlh, NETLINK_ROUTE) < 0) + goto errout_free; + + link_cache = nltool_alloc_link_cache(nlh); + if (!link_cache) + goto errout_close; + + rule_cache = nltool_alloc_rule_cache(nlh); + if (!rule_cache) + goto errout_link_cache; + + params.dp_type = nltool_parse_dumptype(argv[1]); + if (params.dp_type < 0) + goto errout_rule_cache; + + //get_filter(route, argc, argv, 2, route_cache); + + nl_cache_dump_filter(rule_cache, ¶ms, (struct nl_object *) rule); + + err = 0; + +errout_rule_cache: + nl_cache_free(rule_cache); +errout_link_cache: + nl_cache_free(link_cache); +errout_close: + nl_close(nlh); +errout_free: + nl_object_put((struct nl_object *) rule); +errout: + return err; +} diff --git a/src/nl-tctree-dump.c b/src/nl-tctree-dump.c new file mode 100644 index 0000000..193686f --- /dev/null +++ b/src/nl-tctree-dump.c @@ -0,0 +1,144 @@ +/* + * src/nl-tctree-dump.c Dump Traffic Control Tree + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" +#include + +static struct nl_handle *nl_handle; +static struct nl_cache *qdisc_cache, *class_cache; +static struct nl_dump_params dump_params = { + .dp_type = NL_DUMP_FULL, +}; + +static int ifindex; +static void print_qdisc(struct nl_object *, void *); + +static void print_class(struct nl_object *obj, void *arg) +{ + struct rtnl_qdisc *leaf; + struct rtnl_class *class = (struct rtnl_class *) obj; + struct nl_cache *cls_cache; + uint32_t parent = rtnl_class_get_handle(class); + + dump_params.dp_prefix = (int) arg; + nl_object_dump(obj, &dump_params); + + leaf = rtnl_class_leaf_qdisc(class, qdisc_cache); + if (leaf) + print_qdisc((struct nl_object *) leaf, arg + 2); + + rtnl_class_foreach_child(class, class_cache, &print_class, arg + 2); + + cls_cache = rtnl_cls_alloc_cache(nl_handle, ifindex, parent); + if (!cls_cache) + return; + + dump_params.dp_prefix = (int) arg + 2; + nl_cache_dump(cls_cache, &dump_params); + nl_cache_free(cls_cache); +} + +static void print_qdisc(struct nl_object *obj, void *arg) +{ + struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj; + struct nl_cache *cls_cache; + uint32_t parent = rtnl_qdisc_get_handle(qdisc); + + dump_params.dp_prefix = (int) arg; + nl_object_dump(obj, &dump_params); + + rtnl_qdisc_foreach_child(qdisc, class_cache, &print_class, arg + 2); + + cls_cache = rtnl_cls_alloc_cache(nl_handle, ifindex, parent); + if (!cls_cache) + return; + + dump_params.dp_prefix = (int) arg + 2; + nl_cache_dump(cls_cache, &dump_params); + nl_cache_free(cls_cache); +} + +static void print_link(struct nl_object *obj, void *arg) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + struct rtnl_qdisc *qdisc; + + ifindex = rtnl_link_get_ifindex(link); + dump_params.dp_prefix = 0; + nl_object_dump(obj, &dump_params); + + class_cache = rtnl_class_alloc_cache(nl_handle, ifindex); + if (!class_cache) + return; + + qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_ROOT); + if (qdisc) { + print_qdisc((struct nl_object *) qdisc, (void *) 2); + rtnl_qdisc_put(qdisc); + } + + qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, 0); + if (qdisc) { + print_qdisc((struct nl_object *) qdisc, (void *) 2); + rtnl_qdisc_put(qdisc); + } + + qdisc = rtnl_qdisc_get_by_parent(qdisc_cache, ifindex, TC_H_INGRESS); + if (qdisc) { + print_qdisc((struct nl_object *) qdisc, (void *) 2); + rtnl_qdisc_put(qdisc); + } + + nl_cache_free(class_cache); +} + +int main(int argc, char *argv[]) +{ + struct nl_cache *link_cache; + + if (nltool_init(argc, argv) < 0) + return -1; + + dump_params.dp_fd = stdout; + + if (argc > 1) { + if (!strcasecmp(argv[1], "brief")) + dump_params.dp_type = NL_DUMP_BRIEF; + else if (!strcasecmp(argv[1], "full")) + dump_params.dp_type = NL_DUMP_FULL; + else if (!strcasecmp(argv[1], "stats")) + dump_params.dp_type = NL_DUMP_STATS; + } + + nl_handle = nltool_alloc_handle(); + if (!nl_handle) + return 1; + + if (nltool_connect(nl_handle, NETLINK_ROUTE) < 0) + return 1; + + link_cache = nltool_alloc_link_cache(nl_handle); + if (!link_cache) + return 1; + + qdisc_cache = nltool_alloc_qdisc_cache(nl_handle); + if (!qdisc_cache) + return 1; + + nl_cache_foreach(link_cache, &print_link, NULL); + + nl_cache_free(qdisc_cache); + nl_cache_free(link_cache); + + nl_close(nl_handle); + nl_handle_destroy(nl_handle); + return 0; +} diff --git a/src/nl-util-addr.c b/src/nl-util-addr.c new file mode 100644 index 0000000..9f12795 --- /dev/null +++ b/src/nl-util-addr.c @@ -0,0 +1,41 @@ +/* + * src/nl-util-addr.c Address Helper + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +int main(int argc, char *argv[]) +{ + int err; + char host[256]; + struct nl_addr *a; + + if (argc < 2) { + fprintf(stderr, "Usage: nl-util-addr
\n"); + return -1; + } + + a = nl_addr_parse(argv[1], AF_UNSPEC); + if (a == NULL) { + fprintf(stderr, "Cannot parse address \"%s\"\n", argv[1]); + return -1; + } + + err = nl_addr_resolve(a, host, sizeof(host)); + if (err != 0) { + fprintf(stderr, "Cannot resolve address \"%s\": %d\n", + argv[1], err); + return -1; + } + + printf("%s\n", host); + + return 0; +} diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..308aacb --- /dev/null +++ b/src/utils.c @@ -0,0 +1,218 @@ +/* + * src/utils.c Utilities + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#include "utils.h" + +#include + +static struct nl_cb *nltool_cb; + +int nltool_init(int argc, char *argv[]) +{ + char *nlcb = getenv("NLCB"); + char *nldbg = getenv("NLDBG"); + int cbset = NL_CB_VERBOSE; + + if (nlcb) { + if (!strcasecmp(nlcb, "default")) + cbset = NL_CB_DEFAULT; + else if (!strcasecmp(nlcb, "verbose")) + cbset = NL_CB_VERBOSE; + else if (!strcasecmp(nlcb, "debug")) + cbset = NL_CB_DEBUG; + else { + fprintf(stderr, "Unknown value for NLCB, valid values: " + "{default | verbose | debug}\n"); + goto errout; + } + } + + nltool_cb = nl_cb_alloc(cbset); + if (nltool_cb == NULL) { + fprintf(stderr, "Cannot allocate callback handle\n"); + goto errout; + } + + if (nldbg) { + long dbg = strtol(nldbg, NULL, 0); + + if (dbg == LONG_MIN || dbg == LONG_MAX) { + fprintf(stderr, "Invalid value for NLDBG.\n"); + goto errout; + } + + nl_debug = dbg; + } + + return 0; + +errout: + return -1; +} + +int nltool_connect(struct nl_handle *nlh, int protocol) +{ + int err; + + err = nl_connect(nlh, protocol); + if (err < 0) + fprintf(stderr, "Unable to connect netlink socket%s\n", + nl_geterror()); + + return err; +} + +struct nl_handle *nltool_alloc_handle(void) +{ + return nl_handle_alloc_cb(nltool_cb); +} + +struct nl_addr *nltool_addr_parse(const char *str) +{ + struct nl_addr *addr; + + addr = nl_addr_parse(str, AF_UNSPEC); + if (!addr) + fprintf(stderr, "Unable to parse address \"%s\": %s\n", + str, nl_geterror()); + + return addr; +} + +int nltool_parse_dumptype(const char *str) +{ + if (!strcasecmp(str, "brief")) + return NL_DUMP_BRIEF; + else if (!strcasecmp(str, "detailed")) + return NL_DUMP_FULL; + else if (!strcasecmp(str, "stats")) + return NL_DUMP_STATS; + else if (!strcasecmp(str, "xml")) + return NL_DUMP_XML; + else if (!strcasecmp(str, "env")) + return NL_DUMP_ENV; + else { + fprintf(stderr, "Invalid dump type \"%s\".\n", str); + return -1; + } +} + +struct nl_cache *nltool_alloc_link_cache(struct nl_handle *nlh) +{ + struct nl_cache *cache; + + cache = rtnl_link_alloc_cache(nlh); + if (!cache) + fprintf(stderr, "Unable to retrieve link cache: %s\n", + nl_geterror()); + else + nl_cache_mngt_provide(cache); + + return cache; +} + +struct nl_cache *nltool_alloc_addr_cache(struct nl_handle *nlh) +{ + struct nl_cache *cache; + + cache = rtnl_addr_alloc_cache(nlh); + if (!cache) + fprintf(stderr, "Unable to retrieve address cache: %s\n", + nl_geterror()); + else + nl_cache_mngt_provide(cache); + + return cache; +} + +struct nl_cache *nltool_alloc_neigh_cache(struct nl_handle *nlh) +{ + struct nl_cache *cache; + + cache = rtnl_neigh_alloc_cache(nlh); + if (!cache) + fprintf(stderr, "Unable to retrieve neighbour cache: %s\n", + nl_geterror()); + else + nl_cache_mngt_provide(cache); + + return cache; +} + +struct nl_cache *nltool_alloc_neightbl_cache(struct nl_handle *nlh) +{ + struct nl_cache *cache; + + cache = rtnl_neightbl_alloc_cache(nlh); + if (!cache) + fprintf(stderr, "Unable to retrieve neighbour table " + "cache: %s\n", nl_geterror()); + else + nl_cache_mngt_provide(cache); + + return cache; +} + +struct nl_cache *nltool_alloc_route_cache(struct nl_handle *nlh) +{ + struct nl_cache *cache; + + cache = rtnl_route_alloc_cache(nlh); + if (!cache) + fprintf(stderr, "Unable to retrieve route cache: %s\n", + nl_geterror()); + else + nl_cache_mngt_provide(cache); + + return cache; +} + +struct nl_cache *nltool_alloc_rule_cache(struct nl_handle *nlh) +{ + struct nl_cache *cache; + + cache = rtnl_rule_alloc_cache(nlh); + if (!cache) + fprintf(stderr, "Unable to retrieve rule cache: %s\n", + nl_geterror()); + else + nl_cache_mngt_provide(cache); + + return cache; +} + +struct nl_cache *nltool_alloc_qdisc_cache(struct nl_handle *nlh) +{ + struct nl_cache *cache; + + cache = rtnl_qdisc_alloc_cache(nlh); + if (!cache) + fprintf(stderr, "Unable to retrieve qdisc cache: %s\n", + nl_geterror()); + else + nl_cache_mngt_provide(cache); + + return cache; +} + +struct nl_cache *nltool_alloc_genl_family_cache(struct nl_handle *nlh) +{ + struct nl_cache *cache; + + cache = genl_ctrl_alloc_cache(nlh); + if (!cache) + fprintf(stderr, "Unable to retrieve genl family cache: %s\n", + nl_geterror()); + else + nl_cache_mngt_provide(cache); + + return cache; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..d738ce7 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,63 @@ +/* + * src/utils.h Utilities + * + * 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 version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef __SRC_UTILS_H_ +#define __SRC_UTILS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int nltool_init(int argc, char *argv[]); +extern int nltool_connect(struct nl_handle *nlh, int protocol); +extern struct nl_addr *nltool_addr_parse(const char *str); +extern int nltool_parse_dumptype(const char *str); +extern struct nl_handle *nltool_alloc_handle(void); + +extern struct nl_cache *nltool_alloc_link_cache(struct nl_handle *nlh); +extern struct nl_cache *nltool_alloc_addr_cache(struct nl_handle *nlh); +extern struct nl_cache *nltool_alloc_neigh_cache(struct nl_handle *nlh); +extern struct nl_cache *nltool_alloc_neightbl_cache(struct nl_handle *nlh); +extern struct nl_cache *nltool_alloc_route_cache(struct nl_handle *nlh); +extern struct nl_cache *nltool_alloc_rule_cache(struct nl_handle *nlh); +extern struct nl_cache *nltool_alloc_qdisc_cache(struct nl_handle *nlh); +extern struct nl_cache *nltool_alloc_genl_family_cache(struct nl_handle *nlh); + +#define arg_match(str) !strcasecmp(argv[idx], str) + +#endif diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..b5cec34 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,37 @@ +# +# src/Makefile +# +# 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 version 2.1 +# of the License. +# +# Copyright (c) 2003-2006 Thomas Graf +# + +ifeq ($(shell [ ! -r ../Makefile.opts ] && echo 1),) + include ../Makefile.opts +endif + +LDFLAGS += -L../lib -lnl ../src/utils.o +CIN := $(wildcard test-*.c) +TOOLS := $(CIN:%.c=%) + +all: $(TOOLS) + +$(TOOLS): ../src/utils.o + +test-%: test-%.c + @echo " LD $@"; \ + $(CC) $(CFLAGS) -o $@ $< $(LDFLAGS) + +clean: + @echo " CLEAN src"; \ + rm -f $(TOOLS) + +distclean: clean + +install: + @true + +include ../Makefile.rules diff --git a/tests/test-cache-mngr.c b/tests/test-cache-mngr.c new file mode 100644 index 0000000..4d70e31 --- /dev/null +++ b/tests/test-cache-mngr.c @@ -0,0 +1,73 @@ +#include "../src/utils.h" + +static void change_cb(struct nl_cache *cache, struct nl_object *obj, + int action) +{ + struct nl_dump_params dp = { + .dp_type = NL_DUMP_BRIEF, + .dp_fd = stdout, + }; + + if (action == NL_ACT_NEW) + printf("NEW "); + else if (action == NL_ACT_DEL) + printf("DEL "); + else if (action == NL_ACT_CHANGE) + printf("CHANGE "); + + nl_object_dump(obj, &dp); +} + +int main(int argc, char *argv[]) +{ + struct nl_cache_mngr *mngr; + struct nl_cache *lc, *nc, *ac, *rc; + struct nl_handle *handle; + + nltool_init(argc, argv); + + handle = nltool_alloc_handle(); + + mngr = nl_cache_mngr_alloc(handle, NETLINK_ROUTE, NL_AUTO_PROVIDE); + if (!mngr) { + nl_perror("nl_cache_mngr_alloc"); + return -1; + } + + lc = nl_cache_mngr_add(mngr, "route/link", &change_cb); + if (lc == NULL) { + nl_perror("nl_cache_mngr_add(route/link"); + return -1; + } + + nc = nl_cache_mngr_add(mngr, "route/neigh", &change_cb); + if (nc == NULL) { + nl_perror("nl_cache_mngr_add(route/neigh"); + return -1; + } + + ac = nl_cache_mngr_add(mngr, "route/addr", &change_cb); + if (ac == NULL) { + nl_perror("nl_cache_mngr_add(route/addr"); + return -1; + } + + rc = nl_cache_mngr_add(mngr, "route/route", &change_cb); + if (rc == NULL) { + nl_perror("nl_cache_mngr_add(route/route"); + return -1; + } + + for (;;) { + int err = nl_cache_mngr_poll(mngr, 5000); + if (err < 0) { + nl_perror("nl_cache_mngr_poll()"); + return -1; + } + + } + + nl_cache_mngr_free(mngr); + + return 0; +} diff --git a/tests/test-genl.c b/tests/test-genl.c new file mode 100644 index 0000000..e44b3fb --- /dev/null +++ b/tests/test-genl.c @@ -0,0 +1,56 @@ +#include "../src/utils.h" + +int main(int argc, char *argv[]) +{ + struct nl_handle *h; + struct nl_msg *msg; + void *hdr; + + if (nltool_init(argc, argv) < 0) + return -1; + + h = nltool_alloc_handle(); + if (!h) { + nl_perror("nl_handle_alloc"); + return -1; + } + + if (genl_connect(h) < 0) { + nl_perror("genl_connect"); + return -1; + } + + msg = nlmsg_alloc(); + if (msg == NULL) { + nl_perror("nlmsg_alloc"); + return -1; + } + + hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, GENL_ID_CTRL, + 0, 0, CTRL_CMD_GETFAMILY, 1); + if (hdr == NULL) { + nl_perror("genlmsg_put"); + return -1; + } + + if (nla_put_u32(msg, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL) < 0) { + nl_perror("nla_put_u32(CTRL_ATTR_FAMILY_ID)"); + return -1; + } + + if (nl_send_auto_complete(h, msg) < 0) { + nl_perror("nl_send_auto_complete"); + return -1; + } + + if (nl_recvmsgs_default(h) < 0) { + nl_perror("nl_recvmsgs_def"); + return -1; + } + + nlmsg_free(msg); + + nl_close(h); + + return 0; +} diff --git a/tests/test-nf-cache-mngr.c b/tests/test-nf-cache-mngr.c new file mode 100644 index 0000000..86cbabb --- /dev/null +++ b/tests/test-nf-cache-mngr.c @@ -0,0 +1,58 @@ +#include "../src/utils.h" + +static void change_cb(struct nl_cache *cache, struct nl_object *obj, + int action) +{ + struct nfnl_ct *ct = (struct nfnl_ct *) obj; + static struct nl_addr *hack = NULL; + + if (!hack) + hack = nl_addr_parse("194.88.212.233", AF_INET); + + if (!nl_addr_cmp(hack, nfnl_ct_get_src(ct, 1)) || + !nl_addr_cmp(hack, nfnl_ct_get_dst(ct, 1))) { + struct nl_dump_params dp = { + .dp_type = NL_DUMP_BRIEF, + .dp_fd = stdout, + }; + + printf("UPDATE "); + nl_object_dump(obj, &dp); + } +} + +int main(int argc, char *argv[]) +{ + struct nl_cache_mngr *mngr; + struct nl_handle *handle; + struct nl_cache *ct; + + nltool_init(argc, argv); + + handle = nltool_alloc_handle(); + + mngr = nl_cache_mngr_alloc(handle, NETLINK_NETFILTER, NL_AUTO_PROVIDE); + if (!mngr) { + nl_perror("nl_cache_mngr_alloc"); + return -1; + } + + ct = nl_cache_mngr_add(mngr, "netfilter/ct", &change_cb); + if (ct == NULL) { + nl_perror("nl_cache_mngr_add(netfilter/ct)"); + return -1; + } + + for (;;) { + int err = nl_cache_mngr_poll(mngr, 5000); + if (err < 0) { + nl_perror("nl_cache_mngr_poll()"); + return -1; + } + + } + + nl_cache_mngr_free(mngr); + + return 0; +} diff --git a/tests/test-socket-creation.c b/tests/test-socket-creation.c new file mode 100644 index 0000000..5a06661 --- /dev/null +++ b/tests/test-socket-creation.c @@ -0,0 +1,15 @@ +#include "../src/utils.h" + +int main(int argc, char *argv[]) +{ + struct nl_handle *h; + int i; + + for (i = 0; i < 1025; i++) { + h = nl_handle_alloc(); + printf("Created handle with port 0x%x\n", + nl_socket_get_local_port(h)); + } + + return 0; +} -- cgit v0.12