summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Stewart <pstew@google.com>2016-11-04 10:50:31 -0700
committerPaul Stewart <pstew@google.com>2016-11-11 12:31:50 -0800
commit054c80d775f2ae9b8f50260bdfcb821e99c0da2a (patch)
tree9749e9fc8313b0745b2b96e0102d41a63b2a2db4
parent3f563dcf99d53a43799e5bd75d7761fcd9716c5a (diff)
downloadlibnl-054c80d775f2ae9b8f50260bdfcb821e99c0da2a.tar.gz
Update libnl to 3.2.25
Commits: 2dbc1ca Generic Netlink multicast groups support 3229b32 - Prepare for 2.1.x tree - Bump interface number, we will break API in the development tree c0cd587 nl-qdisc-add tool fa89403 HTB: Append TCA_OPTIONS even if no options are set 757592e classid database 23c6191 remove obsolete nl-qdisc-add code 1884809 pfifo/bfifo qdisc support for cli libs 27883b0 nl-class-add tool a670ee5 make nl-qdisc-list installable 420438c Remove NL_DUMP_ENV code cefe7db Make nl-qdisc-delete installable 8699ba8 nl-class-delete tool 2a9c3ef nl-class-list tool 3a96527 nl-qdisc-add(8), nl-qdisc-delete(8), nl-qdisc-list(8) b9d965b Update include/linux header copies 4c6d1c5 Unified TC attributes interface b57a697 nl-cls-* tools 65e386c Packet location updates 0fe5b29 Extended pktloc to support nbyte locations for ipv6, etc. d0e5645 Avoid memcpy()/memset() when reserving space for nested attributes e1eacd6 Fix use of uninitialized data at the end of netlink message d7a561a Tons of ematch work 2f86768 basic: Only add ematches to message if available 691905b cgroup classifier improvements - enabled again - ematch support - cli tools module 897828d src/nf-queue: revert nonsensical change ee88c71 routing rules: mark support 6862c65 Include RTA_MARK in routing rule messages 6d43441 Updated arp and ethernet codes 873a64e Support for 64bit link counters IFLA_STATS64 2e6d497 Support neighbour flag NTF_USE 48cdb1f Support RT_TABLE_COMPAT 8585276 Revert "Include RTA_MARK in routing rule messages" 1eccb7b Revert "routing rules: mark support" f703192 routing rules: adapt to unified routing rules cc22992 Improved debugging messages while constructing messages/attributes 93f992e attr: Add padding if nested data does not end at an alignment boundry c7a6737 text ematch support 7903d6a Support for meta match 4267d8f classid auto generation if provided tc name does not exist 4e48d90 Correctly parse and generate classids 54e2539 nl-qdisc-list: --recursive - print tc objects recursively df36c25 nl-classid-lookup: Added --raw option to print classid without pretty printing it d95a419 ematch/meta: id definitions 738ee7f nl-qdisc-delete: Do not attempt to delete default qdiscs d283c8e Correctly state the process of sending a netlink message. A message passes through nl_send_iovec() before hitting nl_sendmsg() 59880cb pktloc: support to specify a shift operator for packet locations 8970c5c link: Support IFLA_IFALIAS attribute fd857ee link: Support IFLA_NUM_VF attribute - parses attribute if available - provides API to access value rtnl_link_get_num_vf(link, num_vf) 3fa6a6b link: API for address family specific link data 280d457 link/api: Check for null pointer in rtnl_link_af_ops_put() 800013b link: Support for AF_BRIDGE address family 67aeb74 link/api: Convert link info ops to use nl_list_head 12b82e4 link/api: Improve API documentation. 2847cf0 Make nl_str2af return a negative value if parsing fails 7ff4dee Extend rtnl_link_alloc_cache() to support address families f64ebab Add all libraries for libtool to improve linkage 407e9eb Add local copies of linux/ipv6.h and linux/snmp.h e69efad Add support for per interface ICMPv6 statistics a4efc65 link: Add support for IPv6 specific link data - parses IFLA_PROTINFO - dumps flags, cacheinfo, devconf and all statistics 2e3ca4d link: Support for IFLA_AF_SPEC 53015f8 link: AF_INET link module b6592cb link: Fix typo, use rtnl_link_af_alloc() to make sure data buffer is allocated e209971 link/inet: Fix array access 2575d03 link: Add missing IFLA_AF_SPEC container attribute ee57cc7 link: define RTM_SETLINK to allow pretty printing 01bc3c5 link/inet: documentation: add examples a3ea8a6 Make syntax of nl-link-list consistent and install it f56317f update Doxyfile to latest version 4d28cc5 link/inet: pretty printing, make output more readable 3a95620 sfq: perturb period is in seconds, not jiffies 10424b2 Rename nl_get_hz() to nl_get_user_hz() to indicate it's not the in-kernel HZ value d8eeb0a New function nl_size2str() 98ffede link/inet6: pretty printing cf5577d constify struct trans_tbl c32c3f3 socket: constify interface 09daef3 Don't build doc.c, there is no real code in it 063a2eb only perform automatic sequence checking if auto-ack mode is enabled 8a365db rename nl_send_auto_complete() -> nl_send_auto(), nl_auto_complete -> nl_complete_msg() 5a08c8f Inherit return code of NL_CB_MSG_OUT in nl_sendmsg() 6545206 Documentation updates - moved documentation from lib/doc.c to doc/src/ - splitted pages into separate files - worked over core sections, explaining the receiving and sending behaviour in much more details including the the available mechanisms to customize the behaviour - updated the html stylesheet file for increased readability, probably needs some doxygen patching to achieve what I am looking for. - enabled call graphs, not expanded by default e52a09c nl: rename nlmsg_msg_size() to nlmsg_size(), nlmsg_len() -> nlmsg_datalen() 9fbdf6c nl_recv(): Make passing creds pointer optional 4fb528b nl_recv(): Zero out sockaddr in case the caller forgot 9513d4c Rename nlmsg_for_each_msg() to nlmsg_for_each() c158d06 rename NL_AUTO_PID to NL_AUTO_PORT 7105aea addr: hide nl_addr_destroy() 5644578 Tons of documentation 82fe785 use linux/netlink.h instead of netlink/netlink-kernel.h 4b0126b prefix ipv6 link statistics identifiers with IP6 2d1626b Include <linux/pkt_cls.h> and <linux/pkt_sched.h> in <netlink/route/tc.h> for convenience 5f3dbf8 Fix function nfnl_ct_del name b5c474e allowing silent rules in build d8d67c0 Provide numeric version defines 59c3474 Add missing argument in rtnl_link_change example ae5dfb1 Make struct nl_object public, NLHDR_COMMON has been public anyway 5a9f50b __nlmsg_alloc(): Guarantee minimal message size of at least the header 2c75886 Fix off-by-one when reading IFLA_INET6_CONF and IFLA_INET6_STATS 33e9403 Print debugging info while iterating a cache based on a filter 552c85c Hold reference to obj while calling callback of cache iterator 5dc897d provide function to retrieve htb rate 8eb5b55 Unified TC API 45941f9 rename sch -> qdisc 722a227 fix module parent references 55f803c libnl-3.0 c1073d6 Documentation updates f0603a4 add missing checks for ROUTE_ATTR_MULTIPATH 38db636 add missing nl_cache_search in cache.h a0fe7a1 Omit empty nested attributes 913579b Fix out-of-tree build. 9f1abdd Fix "make distcheck". 14fa557 Set default MTU to 1500 93b6c11 Add NLE_NODEV error a62bfdb Check if all mandatory attributes are present in rtnl_tc_msg_build() 8d54934 Cleanup <netlink/route/qdisc.h> - remove dead prototypes - reformat f523f29 Allow NLSYSCONFDIR environment variable to overwrite built-in sysconfdir e4b507e Deprecate rtnl_qdisc_foreach_child() and rtnl_qdisc_foreach_cls() 7e9d5f6 correctly handle the object not found case 23845e9 Add nl_send_sync() 747b892 Qdisc API improvements and documentation 7c62050 trafic class/classifer API improvements and documentation 350b15f Move to asciidoc 475dffa Provide TC_HANDLE(maj, min) macro to generate tc handles 2dbe7d7 Initialize dump buffer in case caller missed it ef327ff Provide nl_object_dump_buf() to easily dump to buffers 23c27b4 Provide nl_cache_set_arg{1,2}() to specify cache args 0893aaf link: Make return and argument type match 41fb241 link: Provide rtnl_link_delete() to delete virtual links 48d543c API to issue direct GET requests to the kernel b5918b5 fix line removed by mistake 96bc6d6 Improve rtnl_link_change() behaviour e81814a Support for rtnl_link_add() 5a66101 dummy interface support 8ffab45 export rtnl_link_add() and rtnl_link_build_add_request() 3d70697 Add support for ARPHRD_NONE 36b0474 Support link info types with no payload 4c6dd3a Expose <netlink/route/link/inet.h> fac4885 Improve readability of classid string representation c18730d Set tc->tc_link if link cache is available 83f1411 Fix clone() of AF specific link data daefa76 Provide rtnl_tc_get_link() and fix link refcnt cc33b09 Add missing declaration of rtnl_tc_get_link() cb6a089 Adding rule with "From" option doesn't work d44c31d addr: store link object and provide rtnl_addr_get() 023c662 Use name "global" instead of "universe" for largest scope f443be6 python interface to netlink protocols 4cb1666 Provide silent variation of nl_cache_require() ed69b2a Add rtnl_tc_data_check() 58e0e1e Add nl_rate2str() aba3689 Provide documentation for rtnl_tc_data() and rtnl_tc_data_check() 053c93f Update local copies of include/linux 09210d9 HTB: Add support for level and direct pkt stats, complete access functions e56eb06 Work on libnl-python 4806c5c Don't redefine offsetof when already defined by e.g. stddef.h c881908 neigh: include ndm flags while building message ca0fc75 socket: Set SOCK_CLOEXEC if available 21d52ea Support for NLM_F_INTR 63548f5 documentation updates 7d48455 Documentation updates dea6de4 Include IFLA_LINK in link messages 5007473 more documentation updates - improved stylesheets for both doxygen and asciidoc - use of xml doxygen layout - python script to resolve <<foo>> asciidoc references to <<foo, title>> based on the target caption - graphics for netlink and netlink error headers - more link documentation 4d23836 bonding link module c79ab52 ignore various generated files 49d29e9 ignore python build directory 16d38a4 Use 'link type' instead of 'link info type' 8219cc7 VLAN: rtnl_link_is_vlan() function and API documentation bf1b5d2 removed autogenerated swig interface files from git tree 20e9797 use rtnl_link_set/get_type() f3ee216 include <linux/if.h> from <netlink/route/link.h> to export IFF_* flags 915a23f Hack doxygen CSS to avoid stupid margins in modules listing 70c9371 Updated link documentation f1c8d5b 3.1 release f999383 Remove write-only variables in lib/cache.c 7701c85 Make some functions and global variables static bbe5e94 Put "break" inside the "if" block in route_compare() d886de5 Allow building documentation out of the tree b5d081d Avoid freeing memory if vasprintf() failed 569bec5 make port map thread safe 7a46ef0 Include all files necessary to build documentation in distribution d8d96bb link: fix unaligned access to 64bit link stats 23333e5 python: Include python/ in distribution and provide a README on how to build & install cc9ae9b doc: need to include @srcdir@/src not @top_srcdir@/src 4be7adb htb: fix misplaced memset() overwriting already set htb prio option b367024 Local port leak on nl_socket_alloc/nl_socket_set_local_port(, 0) eabb753 nl-link-name2ifindex: fix usage text 6faeffe socket: introduce nl_socket_modify_err_cb 17781e4 socket: fix two typos 7adaad7 genl: genl_ctrl_grp_by_name: fix retval in case group id not found f1d9e9d 64bit unaligned access b4b853e Do not require python and swig to be present db6de56 fix license of lib/route/pktloc.c 7ac948c Remove GPL-3.0 license file. 80569bb remove dist lines for non existing files 2a37ab5 prepare for 3.2 release 5a59cf8 asciidoc xhtml stylesheet 7b0d063 lib: Use @MAJ_VERSION and @MAJ_MINOR@ for -version-info 226b387 Install headers in ${includedir}/libnl3 6e5332b Inform users about changed include location at end of configure script a73cb2f build: always install files into /etc/libnl 4c210ad Switch to libtool versioning system b50195c addr: Add missing header to <netlink/route/addr.h> 6d93b83 route: Remove dead link_cache variable 109ea68 tools: Use LDADD and link against .la files instead of LDFLAGS and -llib 67bd56f Fix a73cb2f26 fallout to allow building in separate directory 5151cbc link: Eat ACK followed by RTM_NEWLINK when requesting single link 96f17ce bonding: API to create/enslave/release 15b13dd bonding: Install <netlink/route/link/bonding.h> 076909a Bonding: Fix header guard of <netlink/route/link/bonding.h> 0d9958e Ingnore src/nl-link-enslave and nl-link-release 1c9b175 Provide micro version in <netlink/version.h> d3bb7c9 3.2.1 release f90dc63 Add libnl-cli-3.0 to pkg-config tool d17379d Fix rtnl_link object memory leak when freeing rtnl_addr objects. 6c70cf7 Don't install CLI header files when --disable-cli has been configured. 790966d Only use the MULTIPATH attribute when adding routes with more than one next hop. 30d3f8c utils: Initialize list head after freeing translation list 2bcd8ec cache: event_filter() cache operation to filter notifications 7f20c57 link: Ignore bridging notifications in link cache manager 9c7593c python: Link against nl-3 and nl-route-3 8104b52 Fix typo in debug message 659b6d6 bump to 3.2.2 03f3a58 link: generic link enslaving API bd70009 add missing IFLA_MASTER fillup 32f30b7 release 3.2.3 88940b7 build: simplify optional dirs/files in Makefile.am processing ef75c4e link: allow to add/get linkinfo of unknown type 847e269 rtnl_link_bond_add: allow to allocate bond name in case NULL is given 4a7791e dsmark: Add missing declarations for rtnl_class_dsmark_(get|set)_bitmask() 9819717 avoid dangling co_major_cache reference to NL_AUTO_PROVIDE caches d7222e5 3.2.4 release 794ac78 nl_addr_cmp(): handle prefix length during address comparison 4035e5b link: fix regression in link message parser 9015d11 3.2.5 release d111b1d cli: Use -avoid-version to link cli modules c857625 Update COPYING to fix FSF address 83ab0ae man: Remove bogus .LO macro in manpages 4cc1daa 3.2.6 release 9cbcbca 3.2.7 release a39bb56 add new function to provide neighbour event parsing a17970b Support plug qdisc - queue traffic until explicit release 0b40364 Add new nl_cache_clone() function. f7d0661 doc: Fix typos in autoconf example 965fd78 missing extended Table attribute for lib/route/ 7d47666 doc: Fix incorrect nl_socket_add_memberships() example f54ac3d Memory leak in classid.c 073ef6a genl-ctrl-list: Introduce -d|--details as a shortcut for --format=details 5399b3d genl-ctrl-list: Provide manual page 4a2962d genl-ctrl-list: fix copyright and summary 5f4d4d3 genl-ctrl-list: Mark for installation 653ea34 cache_mngr: document uncommon error codes 9af5469 tests: Convert tests/Makefile to use automake e78975a tests: fix test programs to compile again b320112 cache_mngr: Don't modify callback setup of socket c55acc4 cache_manager: Move documentation to doc/core.txt e34ed95 cache_mngr: Automatically allocate socket if needed a143037 test-cache-mngr: Let the cache manager allocate the socket adbc568 cache_mngr: Fix memory corruption after resizing e048279 nl: Make nl_recvmsgs() return the number of netlink messages processed a518a31 cache_mngr: Let nl_cache_mngr_data_ready() read multiple messages 743756f cache_mngr: API doc updates 5165366 cache_mngr: Provide nl_cache_mngr_info() to pring cache manager details 2ed371e test-cache-mngr: Allow for management of arbitary caches via argument string 2e93940 cache_mngr: Make providing the result pointer to nl_cache_mngr_add() optional 8bbcd22 genl: Make genl_unregister() a NOP if NULL pointer is passed 2e23491 cache: improve documentation of co_event_filter bd1e4d0 cache: Add co_include_event allowing caches to provide their own nl_cache_include() implementation ff3e9e3 object: Add functions to access the object type, cache and object ops ad5d2b7 3.2.8 release d726ecd Fix two bugs in 3.2.8/doc/ 6f156a7 nl: Fix return value of nl_recvmsgs() f8b4f92 3.2.9 release beb40e2 u32: add support for hashing 7b503a1 u32: example/test code for u32 hashing with HTB 183e869 doc: Check documentation generation requirements with autoconf 2fbab02 doc: add section about addressing 100403a route: Add FIXME to rtnl_route_nh_set_gateway() to fix return value 32057bc __str2flags fix fec10a2 doc: documentation restructuring 996b502 FTBFS with musl libc: Missing includes 6627ec3 tc: fix included headers 24d577c u32: fix various u32 hashing related warnings ca883b6 Fix for dumping objects to a buffer instead of file descriptor 9bb30a5 add fwmark mask support 970f5d0 correct HTB rtable/HZ calculations d733f8a use MSG_TRUNC flag to get recv message size at once 0b70de5 genl: updates to API reference documentation 71b442b genl: Add genlmsg_hdr() 3656b6f genl: Add genlmsg_user_hdr(), genlmsg_user_data(), and genlmsg_user_datalen() faef2fa genl: Support registration of families without depending on caches 8fad2e3 genl: Export genl_ops_resolve() and genl_mngt_resolve() in header 43eab46 genl: Update genl-ctrl-list(8) 0c408aa genl: modify genl_ctrl_resolve and friends to allow for module auto-loading 405d168 libnl 3.2.10 2275bb0 Fix compilation with clang d3dcde2 rtnl_netem_set_delay_distribution: fix possible segfault d8a25e4 netem: Use ARRAY_SIZE() bf54d6d Fixed address deletion 9d60ef0 Removed generated .pyc files from repository 1e75bd0 Make syntax highlighters happy f55ea7f Fix typo in still unused function that generate colored message 4be1ae2 Introduce Python's absolute_imports b369d22 Fix whitespaces at EOL 38fefc5 Fixed various str-related logick c1547d9 Flags properties description and implementation fixed fb890a5 Code cleanups 83f06bf Fix indentation (spaces vs tabs) 8959d95 Using only single quotes now and multi-line lists 454ea4a pylint's first review and trivial fixes 482c602 Refine some places 08e8b35 Remove unnecessary comments 139e3d3 nl_pickup removed from python binding 87d3709 netlink.nlattr re-implemented in more pythonic way dca358c rtnl_link_(get|set)_weight is deprecated in libnl. ffa461d Fixed memory leak in Cache destructor 3c53265 Add 'ingress' to the list of recognized TC handles. 84037be Correct missing fwmark mask proto. 69da6af genl: Wait for ACK after successful ctrl reply 49fe936 genl: cleanup genl_ctrl_resolve() 4f93364 link: rtnl_link_get_kernel() should only wait for ACK if AUTO-ACK is on 2bdcde7 Fix types-related warnings based on clang diagnostics 941ba95 libnl 3.2.11 9426d03 tbf: fix false missing attr 052a131 nl_cli_route_parse_table fixed typo in code a0f1c0e ct_dump_stats: detect when stats are not available 25d640d lib/utils.c: One kilobit now is a 1000bits (instead of 1024) 8222519 Fix the always false if (a->rt_nr_nh != a->rt_nr_nh) test 376a0e2 Fix build warning after const char ** convert 97d2460 route_clone : fix segmentation fault using nl_cache_subset to filter routes de28daf nl_addr_parse handling of 'default', 'any', and 'all' b62e019 single nexthop flags bug d10d963 Added lex.yy.c to .gitignore 582a324 Run-time version information is now available ab15d06 "%llu" replaced with "%" PRIu64 8cd2f57 Fix typo in textual description in ct_dump_stats() a2b23ff Fix warning "not checking return value of fscanf" in lib/utils.c: get_psched_settings 5eee974 Prevent potential socket file descriptor leak 717fabc configure: Check for graphviz and source-highlight before building documentation b377ab1 route: Document ROUTE_CACHE_CONTENT flag 5eeb3d3 doc: Update Doxyfile config to latest release 929bd01 configure: Check for pygmentize when building docs 3bf8712 3.2.12 release 8f2ce4d More clean NL_AUTO_PORT and NL_AUTO_SEQ usage in nl_complete_msg 0026125 Address comparison bug fixed 49c94c3 genl_ctrl_probe_by_name: fix checking of genlmsg_put() return value 49be8cd doc: Update Doxyfile.in to latest syntax b1e0a0c Remove auto-generated Doxyfile from git a820222 asprintf related fixed in yy parser fddba71 libnl-3.2.12 - ./configure --disable-doc: error: conditional "LINK_DOC" was never defined. \ Usually this means the macro was only invoked conditionally. ce72837 3.2.13 release 72c2cb9 Enabled the use of Links as context managers. e4192ff nl: Provide API to specify the default buffer size when receiving netlink messages 6ac0717 link: Support IFLA_PROMISCUITY link attribute d2876f8 link: correctly set LINK_ATTR_PROMISCUITY 36ed882 link: Support IFLA_NUM_TX_QUEUES and IFLA_NUM_RX_QUEUES 7f6880c link: Only print "promisc-mode" if users > 0 f2e2e7f link: Include IFLA_NUM_TX_QUEUES and IFLA_NUM_RX_QUEUES when building messages 36139cf doc: Provide documentation for link promis counter and rx/tx queues a35287a link: Support link grouping c3376e7 Conntrack Dump ICMP 2d674fe doc: mark route.txt as WIP db13843 doc: Split doc/ into separate packages 65c3919 Only include doc/ in dist 1fa61d1 3.2.14 release 5535f59 Don't include doc/ in toplevel dist 787f14d genl/family flags can be damaged during the auto-indentation. fedb862 ROUTE_DIFF result was not used in some place in route_compare e1b67fb Clang diagnostics e7ec197 nf-log example: correct copy-range parsing 9d6b104 nl_recv(): "else if" logick simplified and refined 6946851 nl_recv(): Memory allocation errors are handled properly now 2249eae nl_recv(): EWOULDBLOCK return value also checked da694e6 nl_recv(): small code cleanups 420e462 nl_recv(): work with credentials only if "creds" given and NL_SOCK_PASSCRED set 3c1ab42 add missing '}' in __cplusplus namespaces 40457db Exp checkpoint 20035ce Checkpoint: compare function e8b3356 "checkpoint" c675bf0 Checkpoint before compilation attempt f111efd Successful compilation of libnl-nf with expectation 4a702e6 Starting CLI work daf5f93 Compile CLI 453492c Expectation get cli tool 3cb581d update gitignore d3bec59 bugfixes 547c8f6 reinit port numers on tuple dump 4164595 Bugfixes 0741865 define advanced attributes out 2d70751 Updated nfnetlink includes; removed ifdefs; added delete exp program d2fff93 Source cleanup for upstream a8ef352 Add configure option to disable pthreads support 503270c Remove unreachable code 16e54c4 Add missing va_end() calls 3750e2a Set err and free ntbl when leaving neightbl_msg_parser 690264a Add new object op oo_id_attrs_get bc7c822 Add support for updating objects in the cache ceb8fb9 Document buffer size limitations of nl_recv() 6d52ae6 nl_recv: Don't update *buf in error path fb42f19 nl_recv: improve function documentation eb36066 nl_recv: return NLE_INVAL if buf is NULL 220d8e1 nl_recv: return NLE_INVAL if socket address pointer is NULL 65f97de nl_recv: fix indent style b25f26b socket: document nl_socket_get_fd() 8f47501 nl: improve nl_sendto() docs and error checks a721c1d doc: increase dot max graph nodes to 100 b132ee7 nl: improve API doc of transmit functions fd6f205 nl: Allow to overwrite nl_send() b28c25e nl: Improve API doc of nl_connect() and nl_close() 665464c nl: Improve API doc of nl_send_simple() a8741fa Add hash function c6f89ed Add nl hashtable structures and access functions 55c0e03 Add hash support in cache mngr e16e8fd Add hash support to link cache 6f24cf1 Add hash support to neigh cache a2207c7 Add hash support to route cache 5641c0e Hash: Properly prefix hash functions ae1e236 hashtable: Add API reference documentation 59a6b00 hashtable: Fix reference leak in nl_hashtable_free() b66d267 hashtable: remove doc section crashing doxygen 7e075c6 doc: update Doxygen layout file to latest format 5a04760 addr: Return -NLE_AF_NOSUPPORT when trying to set unsupported attributes 3132db5 addr: rtnl_addr_set_peer() is limited to IPv4 c83ecb3 doc: Revert to default stylesheet dd8a87d Add support for per cache flags 30d8626 New cache manager add cache api 0bd14aa Add NL_CACHE_AF_ITER support during refill and resync ce72565 cache: Move NL_CACHE_AF_ITER to <netlink/cache.h> 5734011 cache: Fix typo in API doc of nl_cache_set_flags() 8ffa257 cache: Move nl_cache_ops_set_flags() to cache_mngt.c ea79a76 addr: Support setting local/peer/anycast/multicast/broadcast address to NULL 125119a Add AF_BRIDGE support to link cache 64fcb47 Add AF_BRIDGE support to neigh cache cb25338 Add master support to rtnl_neigh for AF_BRIDGE objects c658a6e cache: Add reference counter to caches 3ed1f9a cache: Hold cache reference while a cache is being provided 20efa14 lock abstraction layer f5af5c5 cache: rwlock accesses to cache operations 74926f9 link: Protect registration of af and link ops with rwlock 1a2c3e3 cache: Add reference counter to cache operations 2b3912a cache: Provide safe versions of nl_cache_ops_associate() and nl_cache_ops_lookup() cb82c2a use safe cache lookup variants internally 235aa7f cache: hold a reference to the cache ops while a cache is provided over it 23c4ef6 Use NL_DBG() instead of printing warnings and errors to stderr 00132b4 cache: provide safe variant of nl_cache_mngt_require() and use it 4d94ed5 Bug Fix: Add new fill socket to cache manager a2c4bd8 Updated configure.in to make sure bison and lex are installed. 0a9d5fc configure: Replace broken AM_PROG_LEX with AC_CHECK_PROGS 3163fa7 cache: reserve room in cache_ops to avoid breaking module ABI too frequently ed3d706 Include the newly added header files in next release 05a6723 3.2.15 release 4149154 cache: only continue iterating over co_groups if it is available f535de3 3.2.16 release a96a3b6 build: rename configure.in -> .ac 9d92564 build: resolve automake-1.12 warnings f3cd3fe build: use AC_CONFIG_AUX_DIR 0ba9e99 build: use foreign mode ab7f42e build: resolve lex failure 5251188 link: basic socket-CAN support c86088f link: socket-CAN helper functions 68967eb Fix python detection code de14136 Fix make check when using --disable-cli 913a689 can: provide local copy of <linux/can/netlink.h> 7c85b8a netfilter: update local header files 89e1b30 Included defs.h so DISABLE_PTHREADS is visible. 25c407c Re-adding #defines for __aligned_be64 for older kernels 29b7137 route cache: Fix handling of ipv6 multipath routes 20a0512 Bug Fix: cache_include: Fix object ref release after successful object update 91ab1be make: automake provides a cscope target these days 96bb7c9 cache pickup: Avoid duplicates during cache pickup de21332 cache: Take cache_ops lock when modifying cache ops flags 6971932 Fix file descriptor leak on error 5d53626 nlmsg_ok comparison between signed and unsigned 6369e1f 3.2.17 release 00a5879 Add <netlink/route/link/can.h> to dist target ba38f39 cache: make sure the user has specified a callback de5744f build: Fix distribution to include 'nfnetlink_conntrack.h' 383ff94 fix include/Makefile.am f123795 3.2.18 release 3540e44 link: add carrier support b1ebda9 cache: Add new nl_cache_find api 09213ee link af ops: Add new ao_compare op to compare link af_data 310ec86 Add ao_compare support to bridge family af_data 6b4a2cb 3.2.19 release 99399ca Add new rtnl_link_af_data_compare function to compare af_data 5c3f2f0 link: Add af data compare to link objects 5291af6 link: fix reference leak in rtnl_link_af_data_compare() 7559157 Undo soname bump f20bbe1 No longer install module API headers 2b96c33 Remove obsolete release notes at end of configure 1481f97 route cache: This patch adds route priority to route object oo_id_attrs 26e22d8 3.2.20 release 1419851 fix includes after removing some headers 9680f91 Move private header files to <netlink-private/*> 1b9de9a Provide compat headers for removed private API c6abb44 Fix test-cache-mngr test c4f8033 Fix build warnings of nl-(qdisc|class|cls)-add 776cde0 Remove obsolete warning from <netlink/object-api.h> as it breaks NM build b6afd0b Provide better help text when bumping CURRENT,AGE,REVISION 83c762d 3.2.21 release 56352ab netfilter: expectation NAT direction is 32 bit attribute 87244f7 netfilter: keep 8-bit API for 32-bit NAT dir attribute 6cc5fdc can: add helper function to get CAN bus state b06c23a link cache: remove AF_UNSPEC check in rtnl_get_link and rtnl_get_link_by_name 4f088d3 netfilter: correct error in construction of NAT tuple 87458ab Fix mask calculation in nl_addr_cmp_prefix() 2005c2e cache: Make NL_ACT_* and nl_cache_ops_(get|put)() available 8571f58 neigh: Remove check for AF_UNSPEC in rtnl_neigh_get() 1c24480 can: Include "linux/can/netlink.h" in the distribution 506020a can: Fix nested message creation in can_put_attrs() 8b8e26b link: Fix af_ops leak on ENOMEM 2d36371 link: Keep reference to af_ops during lifetime of link object e576768 link: Modify link policy on the stack 4a793a4 link: Hold af_ops reference for each AF_SPEC block during lifetime of link object 8026fe2 link: Free and realloc af specific data upon rtnl_link_set_family() 9f8548b attr: Provide nla_is_nested() function fd19dae bridge: Support the new bridging attributes 5a5aa73 bridge: Provide rtnl_link_bridge_alloc() bb9911b netlink: Forward declare frequent libnl types to ease inclusion deps ee4122a vlan: Provide rtnl_link_vlan_alloc() 1ecf98a bond: Provide rtnl_link_bond_alloc() 8f151fa link: move af_data_compare to the end ded2048 link: Fix rtnl_link_af_data_compare return value 4d7680c Use thread-safe strerror_r() instead of strerror() 9e6cdbf attr: Add nla_nest_cancel() to remove partially added nested attributes 60b370d attr: Do not enforce maximum length for NLA_FLAG attributes 64315f7 attr: No longer warn about attribute of type 0 1395c69 attr: Warn application if nla_parse() observes same attribute multiple times c608b4d msg: Pretty print padding attributes in nl_msg_dump() f72bfc7 msg: Pretty print error message header even if incomplete f0f33c3 addr: Reset unused portion of binary address in nl_addr_set_binary_addr() 8852753 addr: improve API reference documentation for nl_addr_*() 780a042 addr: Update to latest address familiy definition for translation e09e7f1 tests: Add check based unit test system 549d26d rtnl-addr: Inherit prefix length to nl_addr objs in rtnl_addr_set_prefixlen() b39c9f7 rtnl-addr: Fix invalid call to nl_addr_set_prefixlen() if neither local or peer address are present 4db1151 Remove superfluous declaration of rtnl_route_put() 23e26e9 Remove rtnl_link_free() declaration from the header file. 6c9be5a nl-route-add: Add NLM_F_EXCL flag to route add aad041c genl: Provide internal function to resolve name to id ad545f2 genl: Update mt_id of cache ops when resolving genl id f9241d5 cache: Improve debugging messages of cache operations ff56710 nl: Print file:line:func in debugging messages and provide --disable-debug to disable debugging 18152ca ct: add ICMPv6 type,code and ID df66b0f genl: Fix cb reference leak in genl_ctrl_probe_by_name() 375a629 nl: Return -NLE_AGAIN if non-blocking socket would block 56eb22f msg: Pretty print generic netlink header in nl_msg_dump() ea43644 Perform no operation on nl_object_free(NULL). d3cf89e addr: only translate more recent address family names and ARP types if defined d505165 autoconf: Use PKG_CHECK_MODULES() instead of AM_PATH_CHECK() ead4cde tests: Make unit test building optional c07a6a3 attr: nla_is_nested() must access nla_type directly 59db7fb dump_attrs: "NLA_F_NESTED" => nla_is_nested(nla) 33396fa Fix leak of cb if nl_socket_alloc_cb() failed to allocate socket 3a6d256 attr: Fix typo in nla_is_nested() 979ea33 Wrong calcultation in nla_reserve ffc0ee3 configure: Convert ENABLE_UNIT_TESTS to a mere AM conditional 4e9aa6a tests: Add basic attribute unit tests ed1f4cb tests: Include util.h in dist 183052d Prepare for 3.2.22-rc1 release 8983fa9 rtnl_link_af_unregister: fix locking 807fddc nl: Increase receive buffer size to 4 pages c4d846f 3.2.22 release c76393e Add macvlan support 87bbfb6 Default to comparing all attributes if no oo_id_attrs defined 53ac502 Handle -NLE_AGAIN in nl_cache_mngr_data_ready 34a96ba netfilter/queue: generalize nfnl_queue_msg_build_verdict() d612180 netfilter/queue: introduce nfnl_queue_msg_send_verdict_batch() 01cfa9c msg: Avoid returning a negative value for nlmsg_attrlen() cd1c1e1 tests: use AM_CFLAGS instead of CFLAGS directly 2d0810e socket: Warn via debug message if local port namespace is exhausted 408a1b8 cache: Return -NLE_PROTO_MISMATCH if socket provided mismatches cache protocol c08aacc handle the case where 0 is passed to nl_size2str e77ea93 add support functions for attributes and callback handlers 7dc033f add python module for generic netlink library 89b0011 add generic netlink functions to swig 81d2b1d Add support for inet diag Netlink protocol. b3fb89f Handle the case where nl_msec2str is passed 0 msecs 162c906 Add VXLAN support. 57f1d9f link: add support for IFLA_PHYS_PORT_ID c97c8c2 Add idiag-socket-details 2ef91da More safely parse vegas info and mem info. 0eb665c Enable linker versioning 6f37b43 fix double free caused by freeing link af_data in rtnl_link_set_family() c31467e Priority of the '<' operator is higher than that of the '=' operator 8a4f16b Call to_msg_fill_raw when defined to_msg_fill is NULL 56d2bbe netfilter/ct: support optional CTA_TIMESTAMP attribute db08ef4 netfilter/ct: support optional CTA_ZONE attribute 9828326 add python examples using swig provided api 9346269 nl_cb: store nl_cb_type in struct nl_cb 86207d4 python: rework netlink callback handling 2d55872 use Callback object constructing Socket 522cf98 python: allow callback function to be a class method fa23414 python: remove unnecessary callback type definitions 6726895 python: fix typo in Socket::__str__() function b9284bc python: add send and receive functions to Socket class ae7f8cc python: remove use of PyArg_ParseTuple() for callback result f2e6f50 python: fixup VLANLink() initialization a4e588d Fixed ObjIterator for python3, fixed output of _color and added missing parameter to nl_cache_resync 1daa48c nl: Fix comment typo on recvmsgs 27019c5 nl-link-set: Add --state option fdd1ba2 route/link: fixup link->l_af_ops must be set for some kind of links cb034e1 test: add bridge creation sample defebe3 python: add basic netlink protocol bridge interface support 07669b0 test: add python bridge testing sample 978bb85 python: add bridge flags API d976e2e obj: Check for NULL pointer in nl_object_clone() 549010c rtnl-addr: set ifa_flags when creating/updating address 74db300 libnl-3.2.23-rc1 release 72bf363 doxygen-link: Be python3 compatible 3151eda doc: Add instructions on how to build docs 7f066d0 Fix up -rc1 versioning to not be included in libnl_micro_version 71ad234 include: Provide <netlink/route/link/vxlan.h> in distribution f44b82f libnl-3.2.23-rc2 release ac344b9 tests: fix make test-create-vxlan 60ceeef 3.2.23 release 43e9438 add ingress qdisc 1f3511c factor out rtnl_link_fill_info() 4263106 add veth link support 760bfab add link netns support 678753b factor out rtnl_link_info_parse() 2ae5298 python: Include all files in distribution 2d20d09 Remove extra memset from __nlmsg_alloc 4f075a4 do not compile some address families when not available d6cfd04 add initial tc action support 9073aaf add mirred tc action 6cdc32d add u32 action support 2e90ed5 add u32 action test 24e8b52 basic: add action support ff94045 add veth.h into include/Makefile.am 0404011 link: Fall back to global provisioned link cache if object is not a cache resident 3ddecb3 fix linux/pkt_act.h inclusion d969a19 add fq_codel qdisc ae19ac0 add fq_codel cli module bda0f66 build: separate compiler and linker flags 6a8d90f attr: Allow attribute type 0 b50a36b The commit 6a8d90f5fec4 "attr: Allow attribute type 0" intended to allow the parsing of {netlink,packet,unix}_diag, even if they are using type 0 for valid attributes. 8b87ae5 fix rtnl_link_info_parse() and rtnl_link_fill_info() aa1c562 add a parameter to rtnl_link_veth_add() 2b7d1d5 add declaration for rtnl_cls_change() b5309e1 fix a bug in rtnl_act_fill() 017c971 rename rtnl_mirred_set_index() to rtnl_mirred_set_ifindex() ffbfe92 add rtnl_mirred_get* functions 507897a u32: add action removal API efdde42 basic: add action removal API 4b84836 basic: Add missing declaration for rtnl_basic_add_action() 438e3ff fix some typo in lib/route/act.c a858d99 basic: Declare rtnl_act in basic.h to silence warning ea0eec9 add an assertion in rtnl_tc_set_link() 7744a5a Revert "basic: Declare rtnl_act in basic.h to silence warning" 85c0192 Revert "basic: Add missing declaration for rtnl_basic_add_action()" c855e4f add declaration for rtnl_basic_add_action() bc717a9 cache: Add missing declaration for nl_cache_find() d7aca17 u32: add rtnl_u32_get_key() 02c9c25 build: fix build failure when using different build dir (make distcheck) 62c37fa libnl-3.2.24-rc1 release 2cf635b route: add libnl name prefix to global variable link_policy 42c4133 add support for IFA_FLAGS nl attribute dcc0baa addr: add address flag IFA_F_MANAGETEMPADDR 8dabf9f bridge: add rtnl_link_add_bridge() function aa8877d act: remove rtnl_act_alloc_cache() b203c89 addr: add address flag IFA_F_NOPREFIXROUTE 815e192 python: fix build error for missing library_path in setup.py 894acf7 doc: fix typo in documentation 1a510c5 libnl-3.2.24 release 4c7a307 build: fix error when running `configure --disable-doc` b701746 route: fix return value of nl_rtgen_request() 7dbf4d5 remove the obsolete src/cls directory b3b8d72 remove nl_cache_lookup() from cache.h 3fb0aae utils: fix nl_msec2str() which always returned '0msec' for whole second durations 85ec9c7 route: rtnl_route_build_msg() should not overwrite the route scope 20aa732 basic: make BASIC_ATTR_TARGET optional 59547d2 fix comments for rtnl_act_add() cdfdfb1 remove some useless code in lib/route/cls/u32.c a126d66 fill prio and protocol attr bits when parsing cls message 68d6bd7 utils: add nl_has_capability() function 015c4ee utils: indicate capability NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE 67a7046 link: document sk == NULL case for rtnl_link_alloc_cache() c0a5b39 u32: add const to rtnl_u32_add_key_in*_addr() dfd0a80 route: don't enforce minlen in inet6_parse_protinfo() (IFLA_PROTINFO) and inet_parse_af() (IFLA_AF_SPEC) 3584a7a route: detect missing cfgid in rtnl_link_inet_get_conf() 5981a39 route: update kernel header snmp.h and fix inet6_parse_protinfo() after kernel API breakage 1048a61 route: rename internal copy of kernel header file 'inetdevice.h' to 'ip.h' a593803 route: update copy of kernel header 'ip.h' baa2cad route: fix off-by-one in rtnl_act_parse() 34bfce6 link: Catch missing io_free() implementations 5206c05 route/addr: only sent IFA_FLAGS when needed to workaround picky older kernels 690545a act: fix policy range check ab55ea8 link: call rtnl_link_set_type() in link_msg_parser() 12bd035 veth: implement ->io_alloc 6c8f67b veth: implement ->io_free 3700bf5 veth: use nl_object_clone() to deep copy rtnl_link object 65f218b add NLM_F_EXCL to rtnl_link_veth_add() cb319e2 python: fix wrongly passing argument to function in ObjIterator.next() 8ff1999 netfilter/ct: expand CT parameters that can be used in add/delete operations 69f4a03 gitignore: ignore ctags file 6722853 doc/core: reply message should send from kernel to app f55ef93 gitignore: ignore patch files 0ba7e66 veth: grab a reference for rtnl_link_veth_get_peer() e5d9b82 act: grab a reference when adding an action to a filter a5917da gitignore: ignore 'nf-ct-add' (netfilter/ct) 6d70d83 docs: add code comment to explain the meaning of the fields in rtnl_addr_cacheinfo b8d90d9 act: fix a pointer in rtnl_act_msg_parse() 35f4473 act: fix memory leak in rtnl_act_parse() a1c9915 doc: fix wrong name in documentation for rtnl_link_bridge_alloc() 0482cdc gitignore: ignore test binaries and artifacts in "tests/" 3a95fad introduce ipip tunnel support 57bdc4f introduce gre tunnel support d715b8a introduce sit tunnel support 737d5f0 ipgre: fix attribute IPGRE_ATTR_OKEY in ipgre_put_attrs() adb9f5d ipgre: rename new public API to avoid confusion 8f63014 ipvti: introduce vti tunnel support 657e257 gitignore: ignore test binaries and artifacts in "tests/" 0fd510b lib/socket: use proper typed constant UINT32_MAX for uint32_t typed port 0271578 lib/socket: don't fail if no more local ports can be assigned in nl_socket_alloc 4dd5fdd lib/socket: retry generate local port in nl_connect on ADDRINUSE 1f734a8 lib/socket: randomize the generated local port c797542 tunnel: add a copy of include/linux/if_tunnel.h 430eb40 vlan: add support for IFLA_VLAN_PROTOCOL 4dc7246 Explicitly create output directories for flex/yacc output. 425d3d6 Use paths relative to srcdir in setup.py. 1087eb5 obj: Fix dereference before NULL check 872544c msg: Remove unnecessary call of nlmsg_free on known NULL pointer 6608cd5 act: fix the logic of parsing actions bb44548 route/link: pass proper type to sizeof() for calloc() in vlan_parse() 29a3894 lib: reorder free() after printf("%p") statements d50758c route: remove unnecessary non-null check in pktloc and ematch 87682a0 cli: add error checking to nl-route-get about out-of-memory 8532ac5 cls: fix array overrun in rtnl_ematch_opnd2txt() dae0a23 cache: fix crash in nl_cache_dump_filter() when omitting the params argument d7a9e74 route/link: fix dangling pointer after rtnl_link_get_ifalias(link, NULL) 90cfeee netfilter/ct: fix calling nfnl_*_put() on uninitialized memory after nfnlmsg_*_parse() 3d5e488 route/pktloc: read_pktlocs() always returned success status 11f9cc0 ip6tnl: introduce ip6 tunnel support f8144b1 build: fix error in include/Makefile.am feda705 cli: Fix typo in error message 8f82270 cache_mngr: Return proper error code if nl_socket_alloc() fails b6cadfe cache_mngr: Fix assignment of error code in nl_cache_mngr_alloc() e4c94ea doc: fix error in core documentation for nl_cache_mngr_alloc() 2ca01af u32: prevent memcpy from NULL 8e052f5 attr: prevent garbage return value for NULL param a8b352a attr: fix compile warning in headers 853c045 cache: add missing declaration of nl_cache_move() in include/netlink/cache.h 940e5d5 doc: fix doxygen-link.py by skipping invalid entries in libnl.dict 558df52 attr: nla_get_u64() should return 0 if the attribute does not fully contain 64 bit 0446731 libnl-3.2.25-rc1 release 9e0960c doc: state in documentation that libnl3 is supposed to work with 3.x kernels 9c2dbdf ip6tnl: Use <netinet/in.h> for in6_addr and stuff c62cda9 build: let autogen.sh script change into top source directory 94e1345 build: embed the git commit id of HEAD in the configure script c4d7000 tc: add co_groups for tc qdisc and filter dcc5375 libnl-3.2.25 release Change-Id: I5c46e7d2b808ad7baf037a49523ad69191450cbf Bug: 32255299 Test: Compile (fails)
-rw-r--r--.gitignore15
-rw-r--r--Android.mk9
-rw-r--r--COPYING85
-rw-r--r--ChangeLog2
-rw-r--r--Makefile.am22
-rwxr-xr-xautogen.sh13
-rw-r--r--configure.ac184
-rw-r--r--configure.in42
-rw-r--r--doc/.gitignore8
-rw-r--r--doc/AUTHORS1
-rw-r--r--doc/COPYING674
-rw-r--r--doc/Doxyfile.in1714
-rw-r--r--doc/DoxygenLayout.xml187
-rw-r--r--doc/Makefile.am72
-rw-r--r--doc/README13
-rw-r--r--doc/api/.gitignore8
-rwxr-xr-xdoc/autogen.sh4
-rw-r--r--doc/configure.ac107
-rw-r--r--doc/core.txt3017
-rwxr-xr-xdoc/doxygen-link.py43
-rwxr-xr-xdoc/gen-tags.sh14
-rw-r--r--doc/html/.gitignore1
-rw-r--r--doc/images/.gitignore3
-rw-r--r--doc/images/addressing.pngbin0 -> 13682 bytes
-rw-r--r--doc/images/attribute_hdr.pngbin0 -> 14996 bytes
-rw-r--r--doc/images/classful_qdisc.pngbin0 -> 31849 bytes
-rw-r--r--doc/images/classless_qdisc.pngbin0 -> 15443 bytes
-rw-r--r--doc/images/classless_qdisc_nbands.pngbin0 -> 17727 bytes
-rw-r--r--doc/images/icons/README5
-rw-r--r--doc/images/icons/callouts/1.pngbin0 -> 329 bytes
-rw-r--r--doc/images/icons/callouts/10.pngbin0 -> 361 bytes
-rw-r--r--doc/images/icons/callouts/11.pngbin0 -> 565 bytes
-rw-r--r--doc/images/icons/callouts/12.pngbin0 -> 617 bytes
-rw-r--r--doc/images/icons/callouts/13.pngbin0 -> 623 bytes
-rw-r--r--doc/images/icons/callouts/14.pngbin0 -> 411 bytes
-rw-r--r--doc/images/icons/callouts/15.pngbin0 -> 640 bytes
-rw-r--r--doc/images/icons/callouts/2.pngbin0 -> 353 bytes
-rw-r--r--doc/images/icons/callouts/3.pngbin0 -> 350 bytes
-rw-r--r--doc/images/icons/callouts/4.pngbin0 -> 345 bytes
-rw-r--r--doc/images/icons/callouts/5.pngbin0 -> 348 bytes
-rw-r--r--doc/images/icons/callouts/6.pngbin0 -> 355 bytes
-rw-r--r--doc/images/icons/callouts/7.pngbin0 -> 344 bytes
-rw-r--r--doc/images/icons/callouts/8.pngbin0 -> 357 bytes
-rw-r--r--doc/images/icons/callouts/9.pngbin0 -> 357 bytes
-rw-r--r--doc/images/icons/caution.pngbin0 -> 2734 bytes
-rw-r--r--doc/images/icons/example.pngbin0 -> 2599 bytes
-rw-r--r--doc/images/icons/home.pngbin0 -> 1340 bytes
-rw-r--r--doc/images/icons/important.pngbin0 -> 2980 bytes
-rw-r--r--doc/images/icons/next.pngbin0 -> 1302 bytes
-rw-r--r--doc/images/icons/note.pngbin0 -> 2494 bytes
-rw-r--r--doc/images/icons/prev.pngbin0 -> 1348 bytes
-rw-r--r--doc/images/icons/tip.pngbin0 -> 2718 bytes
-rw-r--r--doc/images/icons/up.pngbin0 -> 1320 bytes
-rw-r--r--doc/images/icons/warning.pngbin0 -> 3214 bytes
-rw-r--r--doc/images/ifinfomsg.pngbin0 -> 44814 bytes
-rw-r--r--doc/images/library_overview.pngbin0 -> 23870 bytes
-rw-r--r--doc/images/nlmsgerr.pngbin0 -> 43810 bytes
-rw-r--r--doc/images/nlmsghdr.pngbin0 -> 19553 bytes
-rw-r--r--doc/images/qdisc_default.pngbin0 -> 14571 bytes
-rw-r--r--doc/images/qdisc_mq.pngbin0 -> 26121 bytes
-rw-r--r--doc/images/tc_obj.pngbin0 -> 30876 bytes
-rw-r--r--doc/images/tc_overview.pngbin0 -> 48129 bytes
-rw-r--r--doc/index.txt22
-rw-r--r--doc/libnl.css1407
-rw-r--r--doc/m4/ax_python.m497
-rwxr-xr-xdoc/resolve-asciidoc-refs.py25
-rw-r--r--doc/route.txt1889
-rw-r--r--doc/src/hidden.c42
-rw-r--r--doc/src/toc.c54
-rw-r--r--doc/stylesheets/asciidoc-manpage.css18
-rw-r--r--doc/stylesheets/asciidoc.css526
-rw-r--r--doc/stylesheets/docbook-xsl.css322
-rw-r--r--doc/stylesheets/flask-manpage.css1
-rw-r--r--doc/stylesheets/flask.css584
-rw-r--r--doc/stylesheets/pygments.css66
-rw-r--r--doc/stylesheets/slidy.css445
-rw-r--r--doc/stylesheets/toc2.css34
-rw-r--r--doc/stylesheets/volnitsky-manpage.css1
-rw-r--r--doc/stylesheets/volnitsky.css435
-rw-r--r--doc/stylesheets/xhtml11-quirks.css43
-rw-r--r--doc/stylesheets/xhtml11.css333
-rw-r--r--etc/classid45
-rw-r--r--etc/pktloc50
-rw-r--r--include/Makefile.am122
-rw-r--r--include/defs.h86
-rw-r--r--include/linux/can/netlink.h122
-rw-r--r--include/linux/fib_rules.h69
-rw-r--r--include/linux/gen_stats.h23
-rw-r--r--include/linux/genetlink.h1
-rw-r--r--include/linux/if_addr.h23
-rw-r--r--include/linux/if_arp.h17
-rw-r--r--include/linux/if_bad.h25
-rw-r--r--include/linux/if_bridge.h185
-rw-r--r--include/linux/if_ether.h31
-rw-r--r--include/linux/if_link.h319
-rw-r--r--include/linux/if_tunnel.h116
-rw-r--r--include/linux/if_vlan.h5
-rw-r--r--include/linux/ip.h172
-rw-r--r--include/linux/ipv6.h146
-rw-r--r--include/linux/neighbour.h20
-rw-r--r--include/linux/netfilter.h24
-rw-r--r--include/linux/netfilter/nf_conntrack_common.h117
-rw-r--r--include/linux/netfilter/nfnetlink.h16
-rw-r--r--include/linux/netfilter/nfnetlink_compat.h63
-rw-r--r--include/linux/netfilter/nfnetlink_conntrack.h120
-rw-r--r--include/linux/netfilter/nfnetlink_log.h44
-rw-r--r--include/linux/netfilter/nfnetlink_queue.h43
-rw-r--r--include/linux/netlink.h21
-rw-r--r--include/linux/pkt_cls.h85
-rw-r--r--include/linux/pkt_sched.h318
-rw-r--r--include/linux/rtnetlink.h83
-rw-r--r--include/linux/snmp.h299
-rw-r--r--include/linux/tc_act/tc_mirred.h27
-rw-r--r--include/linux/tc_ematch/tc_em_meta.h89
-rw-r--r--include/netlink-local.h183
-rw-r--r--include/netlink-private/cache-api.h270
-rw-r--r--include/netlink-private/genl.h (renamed from include/netlink-generic.h)8
-rw-r--r--include/netlink-private/netlink.h276
-rw-r--r--include/netlink-private/object-api.h376
-rw-r--r--include/netlink-private/route/link/api.h153
-rw-r--r--include/netlink-private/route/tc-api.h134
-rw-r--r--include/netlink-private/socket.h31
-rw-r--r--include/netlink-private/tc.h57
-rw-r--r--include/netlink-private/types.h (renamed from include/netlink-types.h)314
-rw-r--r--include/netlink-tc.h69
-rw-r--r--include/netlink/addr.h7
-rw-r--r--include/netlink/attr.h20
-rw-r--r--include/netlink/cache-api.h189
-rw-r--r--include/netlink/cache.h47
-rw-r--r--include/netlink/cli/class.h21
-rw-r--r--include/netlink/cli/cls.h24
-rw-r--r--include/netlink/cli/ct.h1
-rw-r--r--include/netlink/cli/exp.h42
-rw-r--r--include/netlink/cli/link.h8
-rw-r--r--include/netlink/cli/qdisc.h7
-rw-r--r--include/netlink/cli/tc.h41
-rw-r--r--include/netlink/cli/utils.h2
-rw-r--r--include/netlink/errno.h6
-rw-r--r--include/netlink/genl/ctrl.h5
-rw-r--r--include/netlink/genl/family.h23
-rw-r--r--include/netlink/genl/genl.h9
-rw-r--r--include/netlink/genl/mngt.h117
-rw-r--r--include/netlink/handlers.h4
-rw-r--r--include/netlink/hash.h69
-rw-r--r--include/netlink/hashtable.h52
-rw-r--r--include/netlink/idiag/idiagnl.h126
-rw-r--r--include/netlink/idiag/meminfo.h41
-rw-r--r--include/netlink/idiag/msg.h83
-rw-r--r--include/netlink/idiag/req.h50
-rw-r--r--include/netlink/idiag/vegasinfo.h43
-rw-r--r--include/netlink/msg.h37
-rw-r--r--include/netlink/netfilter/ct.h16
-rw-r--r--include/netlink/netfilter/exp.h129
-rw-r--r--include/netlink/netfilter/queue_msg.h2
-rw-r--r--include/netlink/netlink-kernel.h145
-rw-r--r--include/netlink/netlink.h21
-rw-r--r--include/netlink/object-api.h333
-rw-r--r--include/netlink/object.h13
-rw-r--r--include/netlink/route/act/mirred.h35
-rw-r--r--include/netlink/route/action.h46
-rw-r--r--include/netlink/route/addr.h9
-rw-r--r--include/netlink/route/class-modules.h73
-rw-r--r--include/netlink/route/class.h43
-rw-r--r--include/netlink/route/classifier-modules.h78
-rw-r--r--include/netlink/route/classifier.h46
-rw-r--r--include/netlink/route/cls/basic.h19
-rw-r--r--include/netlink/route/cls/cgroup.h11
-rw-r--r--include/netlink/route/cls/ematch.h100
-rw-r--r--include/netlink/route/cls/ematch/cmp.h3
-rw-r--r--include/netlink/route/cls/ematch/meta.h41
-rw-r--r--include/netlink/route/cls/ematch/nbyte.h36
-rw-r--r--include/netlink/route/cls/ematch/text.h42
-rw-r--r--include/netlink/route/cls/fw.h2
-rw-r--r--include/netlink/route/cls/u32.h15
-rw-r--r--include/netlink/route/link.h185
-rw-r--r--include/netlink/route/link/api.h20
-rw-r--r--include/netlink/route/link/bonding.h39
-rw-r--r--include/netlink/route/link/bridge.h60
-rw-r--r--include/netlink/route/link/can.h60
-rw-r--r--include/netlink/route/link/inet.h33
-rw-r--r--include/netlink/route/link/info-api.h63
-rw-r--r--include/netlink/route/link/ip6tnl.h56
-rw-r--r--include/netlink/route/link/ipgre.h59
-rw-r--r--include/netlink/route/link/ipip.h47
-rw-r--r--include/netlink/route/link/ipvti.h43
-rw-r--r--include/netlink/route/link/macvlan.h46
-rw-r--r--include/netlink/route/link/sit.h53
-rw-r--r--include/netlink/route/link/veth.h36
-rw-r--r--include/netlink/route/link/vlan.h13
-rw-r--r--include/netlink/route/link/vxlan.h86
-rw-r--r--include/netlink/route/neighbour.h2
-rw-r--r--include/netlink/route/pktloc.h15
-rw-r--r--include/netlink/route/qdisc-modules.h75
-rw-r--r--include/netlink/route/qdisc.h60
-rw-r--r--include/netlink/route/qdisc/cbq.h (renamed from include/netlink/route/sch/cbq.h)0
-rw-r--r--include/netlink/route/qdisc/dsmark.h (renamed from include/netlink/route/sch/dsmark.h)8
-rw-r--r--include/netlink/route/qdisc/fifo.h (renamed from include/netlink/route/sch/fifo.h)1
-rw-r--r--include/netlink/route/qdisc/fq_codel.h44
-rw-r--r--include/netlink/route/qdisc/htb.h49
-rw-r--r--include/netlink/route/qdisc/netem.h (renamed from include/netlink/route/sch/netem.h)27
-rw-r--r--include/netlink/route/qdisc/plug.h31
-rw-r--r--include/netlink/route/qdisc/prio.h (renamed from include/netlink/route/sch/prio.h)5
-rw-r--r--include/netlink/route/qdisc/red.h (renamed from include/netlink/route/sch/red.h)0
-rw-r--r--include/netlink/route/qdisc/sfq.h (renamed from include/netlink/route/sch/sfq.h)9
-rw-r--r--include/netlink/route/qdisc/tbf.h (renamed from include/netlink/route/sch/tbf.h)10
-rw-r--r--include/netlink/route/route.h10
-rw-r--r--include/netlink/route/rule.h39
-rw-r--r--include/netlink/route/sch/htb.h41
-rw-r--r--include/netlink/route/tc-api.h21
-rw-r--r--include/netlink/route/tc.h96
-rw-r--r--include/netlink/socket.h17
-rw-r--r--include/netlink/types.h9
-rw-r--r--include/netlink/utils.h53
-rw-r--r--include/netlink/version.h27
-rw-r--r--include/netlink/version.h.in23
-rw-r--r--lib/.gitignore1
-rw-r--r--lib/Makefile.am135
-rw-r--r--lib/addr.c338
-rw-r--r--lib/attr.c497
-rw-r--r--lib/cache.c618
-rw-r--r--lib/cache_mngr.c413
-rw-r--r--lib/cache_mngt.c270
-rw-r--r--lib/cli/cls/basic.c93
-rw-r--r--lib/cli/cls/cgroup.c75
-rw-r--r--lib/cli/qdisc/bfifo.c83
-rw-r--r--lib/cli/qdisc/blackhole.c64
-rw-r--r--lib/cli/qdisc/fq_codel.c112
-rw-r--r--lib/cli/qdisc/htb.c203
-rw-r--r--lib/cli/qdisc/ingress.c65
-rw-r--r--lib/cli/qdisc/pfifo.c77
-rw-r--r--lib/cli/qdisc/plug.c113
-rw-r--r--lib/data.c21
-rw-r--r--lib/doc.c484
-rw-r--r--lib/error.c8
-rw-r--r--lib/fib_lookup/lookup.c14
-rw-r--r--lib/fib_lookup/request.c2
-rw-r--r--lib/genl/ctrl.c335
-rw-r--r--lib/genl/family.c153
-rw-r--r--lib/genl/genl.c359
-rw-r--r--lib/genl/mngt.c346
-rw-r--r--lib/handlers.c50
-rw-r--r--lib/hash.c482
-rw-r--r--lib/hashtable.c197
-rw-r--r--lib/idiag/idiag.c274
-rw-r--r--lib/idiag/idiag_meminfo_obj.c100
-rw-r--r--lib/idiag/idiag_msg_obj.c729
-rw-r--r--lib/idiag/idiag_req_obj.c255
-rw-r--r--lib/idiag/idiag_vegasinfo_obj.c104
-rw-r--r--lib/msg.c422
-rw-r--r--lib/netfilter/ct.c132
-rw-r--r--lib/netfilter/ct_obj.c93
-rw-r--r--lib/netfilter/exp.c620
-rw-r--r--lib/netfilter/exp_obj.c888
-rw-r--r--lib/netfilter/log.c2
-rw-r--r--lib/netfilter/log_msg.c8
-rw-r--r--lib/netfilter/log_msg_obj.c7
-rw-r--r--lib/netfilter/log_obj.c8
-rw-r--r--lib/netfilter/netfilter.c6
-rw-r--r--lib/netfilter/nfnl.c6
-rw-r--r--lib/netfilter/queue.c2
-rw-r--r--lib/netfilter/queue_msg.c51
-rw-r--r--lib/netfilter/queue_msg_obj.c9
-rw-r--r--lib/netfilter/queue_obj.c6
-rw-r--r--lib/nl.c852
-rw-r--r--lib/object.c191
-rw-r--r--lib/route/act.c580
-rw-r--r--lib/route/act/mirred.c242
-rw-r--r--lib/route/addr.c302
-rw-r--r--lib/route/class.c400
-rw-r--r--lib/route/class_api.c102
-rw-r--r--lib/route/class_obj.c281
-rw-r--r--lib/route/classid.c456
-rw-r--r--lib/route/cls.c391
-rw-r--r--lib/route/cls/.gitignore2
-rw-r--r--lib/route/cls/basic.c203
-rw-r--r--lib/route/cls/cgroup.c136
-rw-r--r--lib/route/cls/ematch.c414
-rw-r--r--lib/route/cls/ematch/cmp.c99
-rw-r--r--lib/route/cls/ematch/container.c28
-rw-r--r--lib/route/cls/ematch/meta.c334
-rw-r--r--lib/route/cls/ematch/nbyte.c139
-rw-r--r--lib/route/cls/ematch/text.c183
-rw-r--r--lib/route/cls/ematch_grammar.l162
-rw-r--r--lib/route/cls/ematch_syntax.y501
-rw-r--r--lib/route/cls/fw.c111
-rw-r--r--lib/route/cls/police.c11
-rw-r--r--lib/route/cls/u32.c304
-rw-r--r--lib/route/cls_api.c103
-rw-r--r--lib/route/cls_obj.c253
-rw-r--r--lib/route/link.c2564
-rw-r--r--lib/route/link/api.c345
-rw-r--r--lib/route/link/bonding.c227
-rw-r--r--lib/route/link/bridge.c526
-rw-r--r--lib/route/link/can.c784
-rw-r--r--lib/route/link/dummy.c40
-rw-r--r--lib/route/link/inet.c295
-rw-r--r--lib/route/link/inet6.c484
-rw-r--r--lib/route/link/ip6tnl.c688
-rw-r--r--lib/route/link/ipgre.c727
-rw-r--r--lib/route/link/ipip.c528
-rw-r--r--lib/route/link/ipvti.c477
-rw-r--r--lib/route/link/macvlan.c367
-rw-r--r--lib/route/link/sit.c624
-rw-r--r--lib/route/link/veth.c308
-rw-r--r--lib/route/link/vlan.c224
-rw-r--r--lib/route/link/vxlan.c1157
-rw-r--r--lib/route/neigh.c195
-rw-r--r--lib/route/neightbl.c28
-rw-r--r--lib/route/nexthop.c55
-rw-r--r--lib/route/pktloc.c163
-rw-r--r--lib/route/pktloc_grammar.l17
-rw-r--r--lib/route/pktloc_syntax.y69
-rw-r--r--lib/route/qdisc.c608
-rw-r--r--lib/route/qdisc/blackhole.c37
-rw-r--r--lib/route/qdisc/cbq.c (renamed from lib/route/sch/cbq.c)186
-rw-r--r--lib/route/qdisc/dsmark.c (renamed from lib/route/sch/dsmark.c)198
-rw-r--r--lib/route/qdisc/fifo.c169
-rw-r--r--lib/route/qdisc/fq_codel.c377
-rw-r--r--lib/route/qdisc/htb.c643
-rw-r--r--lib/route/qdisc/ingress.c64
-rw-r--r--lib/route/qdisc/netem.c (renamed from lib/route/sch/netem.c)325
-rw-r--r--lib/route/qdisc/plug.c177
-rw-r--r--lib/route/qdisc/prio.c (renamed from lib/route/sch/prio.c)144
-rw-r--r--lib/route/qdisc/red.c (renamed from lib/route/sch/red.c)104
-rw-r--r--lib/route/qdisc/sfq.c (renamed from lib/route/sch/sfq.c)155
-rw-r--r--lib/route/qdisc/tbf.c (renamed from lib/route/sch/tbf.c)261
-rw-r--r--lib/route/qdisc_api.c98
-rw-r--r--lib/route/qdisc_obj.c270
-rw-r--r--lib/route/route.c9
-rw-r--r--lib/route/route_obj.c278
-rw-r--r--lib/route/route_utils.c5
-rw-r--r--lib/route/rtnl.c21
-rw-r--r--lib/route/rule.c494
-rw-r--r--lib/route/sch/blackhole.c38
-rw-r--r--lib/route/sch/fifo.c199
-rw-r--r--lib/route/sch/htb.c546
-rw-r--r--lib/route/tc.c1040
-rw-r--r--lib/socket.c286
-rw-r--r--lib/utils.c371
-rw-r--r--lib/version.c36
-rw-r--r--libnl-3.0.pc.in (renamed from libnl-2.0.pc.in)4
-rw-r--r--libnl-cli-3.0.pc.in11
-rw-r--r--libnl-genl-3.0.pc.in11
-rw-r--r--libnl-idiag-3.0.pc.in12
-rw-r--r--libnl-nf-3.0.pc.in11
-rw-r--r--libnl-route-3.0.pc.in11
-rw-r--r--libnl.sym.in9
-rw-r--r--m4/ax_pkg_swig.m4135
-rw-r--r--m4/ax_python.m497
-rw-r--r--m4/ax_python_devel.m4325
-rw-r--r--m4/ax_swig_python.m464
-rw-r--r--make.log355
-rw-r--r--man/Makefile.am7
-rw-r--r--man/genl-ctrl-list.8104
-rw-r--r--man/nl-classid-lookup.851
-rw-r--r--man/nl-pktloc-lookup.848
-rw-r--r--man/nl-qdisc-add.8118
-rw-r--r--man/nl-qdisc-delete.81
-rw-r--r--man/nl-qdisc-list.81
-rw-r--r--python/.gitignore4
-rw-r--r--python/Makefile.am3
-rw-r--r--python/README12
-rw-r--r--python/doc/Makefile.am8
-rw-r--r--python/doc/conf.py216
-rw-r--r--python/doc/core.rst215
-rw-r--r--python/doc/index.rst24
-rw-r--r--python/doc/route.rst3
-rw-r--r--python/doc/route_addr.rst47
-rw-r--r--python/examples/Makefile.am6
-rw-r--r--python/examples/iface.py93
-rw-r--r--python/examples/nl80211.py1577
-rw-r--r--python/examples/wiphy.py141
-rw-r--r--python/netlink/Makefile.am11
-rw-r--r--python/netlink/__init__.py0
-rw-r--r--python/netlink/capi.i964
-rw-r--r--python/netlink/core.py793
-rw-r--r--python/netlink/fixes.h1
-rw-r--r--python/netlink/genl/Makefile.am5
-rw-r--r--python/netlink/genl/__init__.py0
-rw-r--r--python/netlink/genl/capi.i108
-rw-r--r--python/netlink/route/Makefile.am14
-rw-r--r--python/netlink/route/__init__.py0
-rw-r--r--python/netlink/route/address.py367
-rw-r--r--python/netlink/route/capi.i507
-rw-r--r--python/netlink/route/link.py551
-rw-r--r--python/netlink/route/links/__init__.py0
-rw-r--r--python/netlink/route/links/bridge.py119
-rw-r--r--python/netlink/route/links/dummy.py26
-rw-r--r--python/netlink/route/links/inet.py158
-rw-r--r--python/netlink/route/links/vlan.py71
-rw-r--r--python/netlink/route/qdisc/__init__.py0
-rw-r--r--python/netlink/route/qdisc/htb.py145
-rw-r--r--python/netlink/route/tc.py595
-rw-r--r--python/netlink/util.py175
-rw-r--r--python/netlink/utils.h41
-rw-r--r--python/setup.py.in42
-rw-r--r--python/tests/Makefile.am5
-rw-r--r--python/tests/test-create-bridge.py28
-rw-r--r--src/.gitignore15
-rw-r--r--src/Makefile.am81
-rw-r--r--src/cls/basic.c90
-rw-r--r--src/cls/cgroup.c78
-rw-r--r--src/cls/utils.c105
-rw-r--r--src/cls/utils.h51
-rw-r--r--src/disabled-nl-qdisc-add.c196
-rw-r--r--src/genl-ctrl-list.c12
-rw-r--r--src/idiag-socket-details.c90
-rw-r--r--src/lib/Makefile.am26
-rw-r--r--src/lib/class.c45
-rw-r--r--src/lib/cls.c71
-rw-r--r--src/lib/ct.c6
-rw-r--r--src/lib/exp.c165
-rw-r--r--src/lib/link.c36
-rw-r--r--src/lib/neigh.c2
-rw-r--r--src/lib/qdisc.c46
-rw-r--r--src/lib/route.c16
-rw-r--r--src/lib/tc.c165
-rw-r--r--src/lib/utils.c77
-rw-r--r--src/nf-ct-add.c142
-rw-r--r--src/nf-exp-add.c187
-rw-r--r--src/nf-exp-delete.c165
-rw-r--r--src/nf-exp-list.c137
-rw-r--r--src/nf-log.c2
-rw-r--r--src/nf-queue.c25
-rw-r--r--src/nl-addr-list.c7
-rw-r--r--src/nl-class-add.c155
-rw-r--r--src/nl-class-delete.c128
-rw-r--r--src/nl-class-list.c117
-rw-r--r--src/nl-classid-lookup.c87
-rw-r--r--src/nl-cls-add.c136
-rw-r--r--src/nl-cls-delete.c142
-rw-r--r--src/nl-cls-list.c124
-rw-r--r--src/nl-link-enslave.c50
-rw-r--r--src/nl-link-list.c58
-rw-r--r--src/nl-link-name2ifindex.c2
-rw-r--r--src/nl-link-release.c45
-rw-r--r--src/nl-link-set.c23
-rw-r--r--src/nl-list-caches.c7
-rw-r--r--src/nl-list-sockets.c2
-rw-r--r--src/nl-neigh-delete.c2
-rw-r--r--src/nl-pktloc-lookup.c129
-rw-r--r--src/nl-qdisc-add.c146
-rw-r--r--src/nl-qdisc-delete.c68
-rw-r--r--src/nl-qdisc-list.c147
-rw-r--r--src/nl-route-add.c2
-rw-r--r--src/nl-route-get.c8
-rw-r--r--src/nl-tctree-list.c24
-rw-r--r--tests/.gitignore27
-rw-r--r--tests/Makefile37
-rw-r--r--tests/Makefile.am67
-rw-r--r--tests/check-addr.c212
-rw-r--r--tests/check-all.c44
-rw-r--r--tests/check-attr.c88
-rw-r--r--tests/test-cache-mngr.c61
-rw-r--r--tests/test-complex-HTB-with-hash-filters.c761
-rw-r--r--tests/test-create-bond.c29
-rw-r--r--tests/test-create-bridge.c80
-rw-r--r--tests/test-create-ip6tnl.c55
-rw-r--r--tests/test-create-ipgre.c56
-rw-r--r--tests/test-create-ipip.c56
-rw-r--r--tests/test-create-ipvti.c55
-rw-r--r--tests/test-create-macvlan.c48
-rw-r--r--tests/test-create-sit.c56
-rw-r--r--tests/test-create-veth.c42
-rw-r--r--tests/test-create-vlan.c43
-rw-r--r--tests/test-create-vxlan.c47
-rw-r--r--tests/test-delete-link.c28
-rw-r--r--tests/test-genl.c106
-rw-r--r--tests/test-nf-cache-mngr.c23
-rw-r--r--tests/test-socket-creation.c15
-rw-r--r--tests/test-u32-filter-with-actions.c400
-rw-r--r--tests/util.h5
472 files changed, 58559 insertions, 11400 deletions
diff --git a/.gitignore b/.gitignore
index 17a67a02..5f5f1ccb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,28 +1,27 @@
.deps
.libs
.dirstamp
-*.in
*.la
*.lo
*.o
*.swp
+*.patch
Makefile
+Makefile.in
+defs.h.in
+defs.h.in~
/lib/stamp-h1
/libnl-1.pc
-/doc/Doxyfile
/lib/defs.h
cscope.*
+/tags
/aclocal.m4
/autom4te.cache
-/compile
+/build-aux/
/config.*
/configure
-/depcomp
/libtool
-/ltmain.sh
-/install-sh
-/missing
-
/*.pc
+/libnl.sym
diff --git a/Android.mk b/Android.mk
index 96c13f42..5e59bf51 100644
--- a/Android.mk
+++ b/Android.mk
@@ -5,7 +5,6 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := lib/cache.c \
lib/data.c \
lib/nl.c \
- lib/doc.c \
lib/cache_mngr.c \
lib/addr.c \
lib/socket.c \
@@ -24,11 +23,15 @@ LOCAL_SRC_FILES := lib/cache.c \
lib/route/rtnl.c \
lib/route/route_utils.c \
lib/netfilter/nfnl.c \
- lib/error.c
+ lib/error.c \
+ lib/version.c \
+ lib/hash.c \
+ lib/hashtable.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS += -D_BSD_SOURCE -Wno-unused-parameter
+LOCAL_CFLAGS += -D_BSD_SOURCE -Wno-unused-parameter \
+ -UNDEBUG -DSYSCONFDIR="\"/etc/libnl\""
LOCAL_MODULE := libnl
include $(BUILD_STATIC_LIBRARY)
diff --git a/COPYING b/COPYING
index 371ec204..4362b491 100644
--- a/COPYING
+++ b/COPYING
@@ -1,9 +1,8 @@
-
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
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -23,8 +22,7 @@ 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.
+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
@@ -89,9 +87,9 @@ 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
+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.
@@ -138,8 +136,8 @@ 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.
+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
@@ -305,10 +303,10 @@ of these things:
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.
+ 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
@@ -386,10 +384,9 @@ 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.
+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
@@ -407,11 +404,11 @@ 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.
+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.
@@ -459,3 +456,47 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/ChangeLog b/ChangeLog
index 6dc334bb..cdd78a86 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,5 @@
ChangeLog discontinued, git history can be found here:
-http://git.kernel.org/?p=libs/netlink/libnl.git
+http://git.infradead.org/users/tgr/libnl.git
Summary of Changes from 1.0-pre6 to 1.0-pre7
================================================
diff --git a/Makefile.am b/Makefile.am
index 8f9438c0..bc4266de 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,19 +2,21 @@
ACLOCAL_AMFLAGS = -I m4
-OPT_DIRS =
+SUBDIRS = include lib man python tests
+
+pkgconfig_DATA = libnl-3.0.pc \
+ libnl-route-3.0.pc \
+ libnl-genl-3.0.pc \
+ libnl-nf-3.0.pc
if ENABLE_CLI
-OPT_DIRS += src
+SUBDIRS += src
+pkgconfig_DATA += libnl-cli-3.0.pc
endif
-SUBDIRS = include lib doc $(OPT_DIRS)
-
-pkgconfig_DATA = libnl-2.0.pc
-sysconfdir = @sysconfdir@/libnl
-sysconf_DATA = etc/pktloc
+pkgsysconfdir = ${sysconfdir}/libnl
+pkgsysconf_DATA = etc/pktloc etc/classid
-.PHONY: cscope
-cscope:
- cscope -b -q -R -Iinclude -slib -ssrc;
+EXTRA_DIST = \
+ $(pkgsysconf_DATA)
diff --git a/autogen.sh b/autogen.sh
index a5696148..e498fae1 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -1,4 +1,15 @@
#!/bin/bash
-autoreconf -fi;
+die() {
+ echo "$@" >&2
+ exit 1
+}
+
+BASEDIR="$(dirname "$0")"
+
+cd "$BASEDIR" || die "Could not change into base directory $BASEDIR"
+
+autoreconf -fi || die "Error during autoreconf"
rm -Rf autom4te.cache;
+
+doc/autogen.sh || die "Error during doc/autogen.sh"
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 00000000..c6f064e6
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,184 @@
+#
+# 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-2013 Thomas Graf <tgraf@suug.ch>
+#
+
+
+# copied from glib
+m4_define([libnl_major_version], [3])
+m4_define([libnl_minor_version], [2])
+m4_define([libnl_micro_version], [25])
+m4_define([libnl_git_sha], [m4_esyscmd([ ( [ -d ./.git/ ] && [ "$(readlink -f ./.git/)" = "$(readlink -f "$(git rev-parse --git-dir 2>/dev/null)" 2>/dev/null)" ] && git rev-parse --verify -q HEAD 2>/dev/null ) || true ])])
+
+
+# The following explanation may help to understand the above rules a bit
+# better: consider that there are three possible kinds of reactions from
+# users of your library to changes in a shared library:
+#
+# 1. Programs using the previous version may use the new version as drop-in
+# replacement, and programs using the new version can also work with the
+# previous one. In other words, no recompiling nor relinking is needed.
+# In this case, bump revision only, don't touch current nor age.
+#
+# 2. Programs using the previous version may use the new version as drop-in
+# replacement, but programs using the new version may use APIs not
+# present in the previous one. In other words, a program linking against
+# the new version may fail with “unresolved symbols” if linking against
+# the old version at runtime: set revision to 0, bump current and age.
+#
+# 3. Programs may need to be changed, recompiled, relinked in order to use
+# the new version. Bump current, set revision and age to 0.
+
+m4_define([libnl_lt_current], [220])
+m4_define([libnl_lt_revision], [0])
+m4_define([libnl_lt_age], [20])
+
+m4_define([libnl_version],
+ [libnl_major_version.libnl_minor_version.libnl_micro_version])
+
+AC_INIT(libnl, [libnl_version], [], [], [http://www.infradead.org/~tgr/libnl/])
+AC_CONFIG_HEADERS([lib/defs.h])
+AC_CONFIG_AUX_DIR([build-aux])
+AC_CONFIG_MACRO_DIR([m4])
+AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)], [])
+m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
+
+MAJ_VERSION=libnl_major_version
+AC_SUBST(MAJ_VERSION)
+MIN_VERSION=libnl_minor_version
+AC_SUBST(MIN_VERSION)
+MIC_VERSION=libnl_micro_version
+AC_SUBST(MIC_VERSION)
+LIBNL_GIT_SHA=libnl_git_sha
+LIBNL_VERSION=libnl_version
+AC_SUBST(LIBNL_VERSION)
+
+LT_CURRENT=libnl_lt_current
+AC_SUBST(LT_CURRENT)
+LT_REVISION=libnl_lt_revision
+AC_SUBST(LT_REVISION)
+LT_AGE=libnl_lt_age
+AC_SUBST(LT_AGE)
+
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_INSTALL
+AM_PROG_LIBTOOL
+AC_PROG_MKDIR_P
+AC_CHECK_PROGS(FLEX, 'flex')
+AC_CHECK_PROGS(YACC, 'bison -y')
+
+AC_C_CONST
+AC_C_INLINE
+
+PKG_CHECK_MODULES([CHECK], [check >= 0.9.0],
+ [enable_unit_tests="yes"],
+ [AC_MSG_WARN([*** Disabling building of unit tests])
+ enable_unit_tests="no"])
+
+AM_CONDITIONAL([ENABLE_UNIT_TESTS], [test "$enable_unit_tests" = "yes"])
+
+AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH],
+ [Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]),
+ [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig'])
+AC_SUBST([pkgconfigdir])
+
+AC_ARG_ENABLE([cli],
+ AS_HELP_STRING([--disable-cli], [Do not build command line interface utils]),
+ [enable_cli="$enableval"], [enable_cli="yes"])
+AM_CONDITIONAL([ENABLE_CLI], [test "$enable_cli" = "yes"])
+
+AC_ARG_ENABLE([pthreads],
+ AS_HELP_STRING([--disable-pthreads], [Disable pthreads support]),
+ [enable_pthreads="$enableval"], [enable_pthreads="yes"])
+AM_CONDITIONAL([DISABLE_PTHREADS], [test "$enable_pthreads" = "no"])
+
+AC_ARG_ENABLE([debug],
+ AS_HELP_STRING([--disable-debug], [Do not include debugging statements]),
+ [enable_debug="$enableval"], [enable_debug="yes"])
+AM_CONDITIONAL([ENABLE_DEBUG], [test "$enable_debug" = "no" ])
+
+AC_CHECK_LIB([m], [pow], [], AC_MSG_ERROR([libm is required]))
+
+if test "x$enable_pthreads" = "xno"; then
+ AC_DEFINE([DISABLE_PTHREADS], [1], [Define to 1 to disable pthreads])
+else
+ AC_CHECK_LIB([pthread], [pthread_mutex_lock], [], AC_MSG_ERROR([libpthread is required]))
+fi
+
+if test "x$enable_debug" = "xyes"; then
+ AC_DEFINE([NL_DEBUG], [1], [Define to 1 to enable debugging])
+fi
+
+AC_CONFIG_SUBDIRS([doc])
+
+AC_CONFIG_FILES([
+Makefile
+libnl.sym
+libnl-3.0.pc
+libnl-route-3.0.pc
+libnl-genl-3.0.pc
+libnl-nf-3.0.pc
+libnl-cli-3.0.pc
+lib/Makefile
+include/Makefile
+src/Makefile
+src/lib/Makefile
+tests/Makefile
+man/Makefile
+python/Makefile
+python/setup.py
+python/doc/Makefile
+python/examples/Makefile
+python/netlink/Makefile
+python/netlink/genl/Makefile
+python/netlink/route/Makefile
+python/tests/Makefile
+include/netlink/version.h
+])
+
+ac_errcount=0
+if test -z "$YACC"; then
+ AC_MSG_WARN(bison not found. Please install before continuing.)
+ ac_errcount=$((ac_errcount + 1))
+fi
+if test -z "$FLEX"; then
+ AC_MSG_WARN(flex not found. Please install before continuing.)
+ ac_errcount=$((ac_errcount + 1))
+fi
+if test $ac_errcount -gt 0; then
+ AC_MSG_ERROR(Required packages are missing. Please install them and rerun ./configure)
+fi
+
+AC_OUTPUT
+
+echo "-------------------------------------------------------------------------------"
+echo " NOTE"
+echo ""
+echo " There have been some changes starting with 3.2 regarding where and how libnl"
+echo " is being installed on the system in order to allow multiple libnl versions"
+echo " to be installed in parallel:"
+echo ""
+echo " - Headers will be installed in ${includedir}/libnl${MAJ_VERSION}, therefore"
+echo " you will need to add \"-I/usr/include/libnl${MAJ_VERSION}\" to CFLAGS"
+echo ""
+echo " - The library basename was renamed to libnl-${MAJ_VERSION}, i.e. the SO names become"
+echo " libnl-${MAJ_VERSION}.so., libnl-route-${MAJ_VERSION}.so, etc."
+echo ""
+echo " - libtool versioning was assumed, to ease detection of compatible library"
+echo " versions."
+echo ""
+echo " If you are using pkg-config for detecting and linking against the library "
+echo " things will continue magically as if nothing every happened. If you are "
+echo " linking manually you need to adapt your Makefiles or switch to using "
+echo " pkg-config files."
+echo ""
+echo "-------------------------------------------------------------------------------"
+
diff --git a/configure.in b/configure.in
deleted file mode 100644
index 18d27168..00000000
--- a/configure.in
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# 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-2010 Thomas Graf <tgraf@suug.ch>
-#
-
-AC_INIT(libnl, 2.0, tgraf@suug.ch)
-AC_CONFIG_HEADERS([lib/defs.h])
-AC_CONFIG_MACRO_DIR([m4])
-AM_INIT_AUTOMAKE([-Wall foreign subdir-objects])
-
-AC_PROG_CC
-AM_PROG_CC_C_O
-AC_PROG_INSTALL
-AM_PROG_LIBTOOL
-AM_PROG_LEX
-AC_PROG_YACC
-
-AC_C_CONST
-AC_C_INLINE
-
-AC_ARG_WITH([pkgconfigdir], AS_HELP_STRING([--with-pkgconfigdir=PATH],
- [Path to the pkgconfig directory [[LIBDIR/pkgconfig]]]),
- [pkgconfigdir="$withval"], [pkgconfigdir='${libdir}/pkgconfig'])
-AC_SUBST([pkgconfigdir])
-
-AC_ARG_ENABLE([cli],
- AS_HELP_STRING([--disable-cli], [Do not build command line interface utils]),
- [enable_cli="$enableval"], [enable_cli="yes"])
-AM_CONDITIONAL([ENABLE_CLI], [test "$enable_cli" = "yes"])
-
-AC_CHECK_LIB([m], [pow], [], AC_MSG_ERROR([libm is required]))
-
-AC_CONFIG_FILES([Makefile doc/Doxyfile doc/Makefile lib/Makefile
- include/Makefile src/Makefile src/lib/Makefile \
- libnl-2.0.pc include/netlink/version.h])
-AC_OUTPUT
diff --git a/doc/.gitignore b/doc/.gitignore
new file mode 100644
index 00000000..f7cb70da
--- /dev/null
+++ b/doc/.gitignore
@@ -0,0 +1,8 @@
+*.html
+libnl.dict
+Doxyfile
+/aclocal.m4
+/autom4te.cache/
+/build-aux/
+/config.*
+/configure
diff --git a/doc/AUTHORS b/doc/AUTHORS
new file mode 100644
index 00000000..26e3cb40
--- /dev/null
+++ b/doc/AUTHORS
@@ -0,0 +1 @@
+Thomas Graf <tgraf@suug.ch>
diff --git a/doc/COPYING b/doc/COPYING
new file mode 100644
index 00000000..94a9ed02
--- /dev/null
+++ b/doc/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index 8e311e3b..50e8f0c9 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -1,203 +1,268 @@
-# Doxyfile 1.5.2
+# Doxyfile 1.8.1.1
# This file describes the settings to be used by the documentation system
-# doxygen (www.doxygen.org) for a project
+# doxygen (www.doxygen.org) for a project.
#
-# All text after a hash (#) is considered a comment and will be ignored
+# 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 (" ")
+# 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.
+# 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.
+# The PROJECT_NAME tag is a single word (or sequence of words) that should
+# identify the project. Note that if you do not use Doxywizard you need
+# to put quotes around the project name if it contains spaces.
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
+# 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 = @PACKAGE_VERSION@
-# 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
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer
+# a quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify an logo or icon that is
+# included in the documentation. The maximum height of the logo should not
+# exceed 55 pixels and the maximum width should not exceed 200 pixels.
+# Doxygen will copy the logo to the output directory.
+
+PROJECT_LOGO =
+
+# 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
+# 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.
+# 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, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
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).
+# 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
+# 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
+REPEAT_BRIEF = YES
-# 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"
+# 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 =
+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
+# 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
+ALWAYS_DETAILED_SEC = NO
-# 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
+# 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
+# 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
+# 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 =
+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
+# 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 =
+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
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful if your file system
# 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.
+# 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 regular 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.
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
-MULTILINE_CPP_IS_BRIEF = NO
+QT_AUTOBRIEF = 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.
+# 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.
-DETAILS_AT_TOP = YES
+MULTILINE_CPP_IS_BRIEF = NO
-# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
-# member inherits the documentation from any documented member that it
+# 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
+# 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.
+# 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:".
+# 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
+ALIASES = arg=\param \
+ "ref_asciidoc{3}=<a href=\"../\1.html#\2\"><b>\3</b></a>" \
+ "ref_core{2}=\ref_asciidoc{core,\1,\2 (Netlink Core Library Development Guide)}" \
+ "ref_route{2}=\ref_asciidoc{route,\1,\2 (Netlink Routing Development Guide)}" \
+ "ref_idiagnl{2}=\ref_asciidoc{idiag,\1,\2 (Netlink Inet Diag Development Guide)}" \
+ "core_doc{2}=\ref_core{\1,\2}" \
+ "route_doc{2}=\ref_route{\1,\2}" \
+ "idiagnl_doc{2}=\ref_idiagnl{\1,\2}" \
+ "callback=\par Callback Invocation:\n" \
+ "lowlevel=\copydoc low_level_api"
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding
+# "class=itcl::class" will allow you to use the command class in the
+# itcl::class meaning.
+
+TCL_SUBST =
+
+# 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.
+# 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
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this
+# tag. The format is ext=language, where ext is a file extension, and language
+# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C,
+# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make
+# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C
+# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions
+# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all
+# comments according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you
+# can mix doxygen, HTML, and XML commands with Markdown formatting.
+# Disable only in case of backward compatibilities issues.
+
+MARKDOWN_SUPPORT = YES
+
+# 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 makes the inheritance and collaboration
# diagrams that involve STL classes more complete and accurate.
BUILTIN_STL_SUPPORT = NO
@@ -207,396 +272,551 @@ BUILTIN_STL_SUPPORT = NO
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
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_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
+# 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
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and
+# unions are shown inside the group in which they are included (e.g. using
+# @ingroup) instead of on a separate page (for HTML and Man pages) or
+# section (for LaTeX and RTF).
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and
+# unions with only public data fields will be shown inline in the documentation
+# of the scope in which they are defined (i.e. file, namespace, or group
+# documentation), provided this scope is documented. If set to NO (the default),
+# structs, classes, and unions are shown on a separate page (for HTML and Man
+# pages) or section (for LaTeX and RTF).
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+SYMBOL_CACHE_SIZE = 0
+
+# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be
+# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given
+# their name and scope. Since this can be an expensive process and often the
+# same symbol appear multiple times in the code, doxygen keeps a cache of
+# pre-resolved symbols. If the cache is too small doxygen will become slower.
+# If the cache is too large, memory is wasted. The cache size is given by this
+# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols.
+
+LOOKUP_CACHE_SIZE = 0
+
#---------------------------------------------------------------------------
# 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
+# 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
+# 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
+# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation.
+
+EXTRACT_PACKAGE = NO
+
+# 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 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.
+# 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.
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespaces are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# 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
+# 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
+# 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
+# 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.
+# 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
+# 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
+# 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
+# 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]
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen
+# will list include files with double quotes in the documentation
+# rather than with sharp brackets.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# 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
+# 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
+# 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.
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen
+# will sort the (brief and detailed) documentation of class members so that
+# constructors and destructors are listed first. If set to NO (the default)
+# the constructors will appear in the respective orders defined by
+# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS.
+# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO
+# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = YES
+
+# 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
+# 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
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to
+# do proper type resolution of all parameters of a function it will reject a
+# match between the prototype and the implementation of a member function even
+# if there is only one candidate or it is obvious which candidate to choose
+# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen
+# will still accept a match between prototype and implementation in such cases.
+
+STRICT_PROTO_MATCHING = 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
+# 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
+# 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
+GENERATE_BUGLIST = YES
-# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
-# disable (NO) the deprecated list. This list is created by putting
+# 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
+GENERATE_DEPRECATEDLIST= YES
-# The ENABLED_SECTIONS tag can be used to enable conditional
+# The ENABLED_SECTIONS tag can be used to enable conditional
# documentation sections, marked by \if sectionname ... \endif.
-ENABLED_SECTIONS =
+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
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or macro 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 macros 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
+# 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.
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = NO
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
-SHOW_DIRECTORIES = NO
+SHOW_NAMESPACES = YES
-# 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 <command> <input-file>, where <command> is the value of
-# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
-# provided by doxygen. Whatever the program writes to standard output
+# 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 <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> 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 =
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option.
+# You can optionally specify a file name after the option, if omitted
+# DoxygenLayout.xml will be used as the name of the layout file.
+
+LAYOUT_FILE = DoxygenLayout.xml
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files
+# containing the references data. This must be a list of .bib files. The
+# .bib extension is automatically appended if omitted. Using this command
+# requires the bibtex tool to be installed. See also
+# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style
+# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this
+# feature you need bibtex and perl available in the search path.
+
+CITE_BIB_FILES =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
-# The QUIET tag can be used to turn on/off the messages that are generated
+# 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
+# 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
+# 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
+# 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
+# The WARN_NO_PARAMDOC option can be enabled 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
+# 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
+# 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 =
+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
+# 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 \
- ../src/lib \
- ../include/netlink
+INPUT = @top_srcdir@/../lib \
+ @top_srcdir@/../src/lib \
+ @top_srcdir@/../include/netlink \
+ @top_srcdir@/../src \
+ @top_srcdir@/../doc/src
-# 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.
+# 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
+# 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++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh
+# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py
+# *.f90 *.f *.for *.vhd *.vhdl
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.
+# 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
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# 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.
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
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
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system 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
+# 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 =
+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
+# 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 =
+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 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 =
+EXAMPLE_PATH = @top_srcdir@/src
-# 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
+# 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 =
+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.
+# 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_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 =
+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 <filter> <input-file>, where <filter>
-# is the value of the INPUT_FILTER tag, and <input-file> 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
+# 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 <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> 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 =
+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.
+# 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 or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
-FILTER_PATTERNS =
+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
+# 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
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
#---------------------------------------------------------------------------
# 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
+# 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
+# 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.
+# 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, C++ and Fortran 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
+# If the REFERENCED_BY_RELATION tag is set to YES
+# 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
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
# called/used by that function will be listed.
REFERENCES_RELATION = YES
@@ -604,43 +824,44 @@ 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.
+# link to the source code.
+# Otherwise they will link to the documentation.
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
+# 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
+# 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
+VERBATIM_HEADERS = YES
#---------------------------------------------------------------------------
# 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
+# 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
+ALPHABETICAL_INDEX = YES
-# 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
+# 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
+# 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_
@@ -649,256 +870,524 @@ IGNORE_PREFIX = nl_
# configuration options related to the HTML output
#---------------------------------------------------------------------------
-# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# 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
+# 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
+HTML_OUTPUT = api
-# 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
+# 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.
+# 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. Note that when using a custom header you are responsible
+# for the proper inclusion of any scripts and style sheets that doxygen
+# needs, which is dependent on the configuration options used.
+# It is advised to generate a default header using "doxygen -w html
+# header.html footer.html stylesheet.css YourConfigFile" and then modify
+# that header. Note that the header is subject to change so you typically
+# have to redo this when upgrading to a newer version of doxygen or when
+# changing the value of configuration settings such as GENERATE_TREEVIEW!
+
+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_HEADER =
+HTML_FOOTER =
-# 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.
+# 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
+# style sheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET = @srcdir@/libnl.css
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that
+# the files will be copied as-is; there are no commands or markers available.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output.
+# Doxygen will adjust the colors in the style sheet and background images
+# according to this color. Hue is specified as an angle on a colorwheel,
+# see http://en.wikipedia.org/wiki/Hue for more information.
+# For instance the value 0 represents red, 60 is yellow, 120 is green,
+# 180 is cyan, 240 is blue, 300 purple, and 360 is red again.
+# The allowed range is 0 to 359.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of
+# the colors in the HTML output. For a value of 0 the output will use
+# grayscales only. A value of 255 will produce the most vivid colors.
-HTML_FOOTER =
+HTML_COLORSTYLE_SAT = 100
-# 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!
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to
+# the luminance component of the colors in the HTML output. Values below
+# 100 gradually make the output lighter, whereas values above 100 make
+# the output darker. The value divided by 100 is the actual gamma applied,
+# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2,
+# and 100 does not change the gamma.
-HTML_STYLESHEET =
+HTML_COLORSTYLE_GAMMA = 80
-# 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.
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting
+# this to NO can help when comparing the output of multiple runs.
-HTML_ALIGN_MEMBERS = YES
+HTML_TIMESTAMP = 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)
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+
+HTML_DYNAMIC_SECTIONS = YES
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of
+# entries shown in the various tree structured indices initially; the user
+# can expand and collapse entries dynamically later on. Doxygen will expand
+# the tree to such a level that at most the specified number of entries are
+# visible (unless a fully collapsed tree already exceeds this amount).
+# So setting the number of entries 1 will produce a full collapsed tree by
+# default. 0 is a special value representing an infinite number of entries
+# and will result in a full expanded tree by default.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# 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 compiled 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
+# 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 =
+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
+# 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 =
+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
+# 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
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# 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
+# 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.
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.infradead.libnl
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
+# at top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it. Since the tabs have the same information as the
+# navigation tree you can set this option to NO if you already set
+# GENERATE_TREEVIEW to YES.
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.
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value 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 (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+# Since the tree basically has the same information as the tab index you
+# could consider to set DISABLE_INDEX to NO when enabling this option.
-ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = YES
-# 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.
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values
+# (range [0,1..20]) that doxygen will group on one line in the generated HTML
+# documentation. Note that a value of 0 will completely suppress the enum
+# values from appearing in the overview section.
-GENERATE_TREEVIEW = NO
+ENUM_VALUES_PER_LINE = 1
-# 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
+# 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
+TREEVIEW_WIDTH = 205
+
+# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open
+# links to external symbols imported via tag files in a separate window.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are
+# not supported properly for IE 6.0, but are supported on all modern browsers.
+# Note that when changing this option you need to delete any form_*.png files
+# in the HTML output before the changes have effect.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax
+# (see http://www.mathjax.org) which uses client side Javascript for the
+# rendering instead of using prerendered bitmaps. Use this if you do not
+# have LaTeX installed or if you want to formulas look prettier in the HTML
+# output. When enabled you may also need to install MathJax separately and
+# configure the path to it using the MATHJAX_RELPATH option.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you need to specify the location relative to the
+# HTML output directory using the MATHJAX_RELPATH option. The destination
+# directory should contain the MathJax.js script. For instance, if the mathjax
+# directory is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to
+# the MathJax Content Delivery Network so you can quickly see the result without
+# installing MathJax.
+# However, it is strongly recommended to install a local
+# copy of MathJax from http://www.mathjax.org before deployment.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension
+# names that should be enabled during MathJax rendering.
+
+MATHJAX_EXTENSIONS =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box
+# for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using
+# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets
+# (GENERATE_DOCSET) there is already a search function so this one should
+# typically be disabled. For large projects the javascript based search engine
+# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution.
+
+SEARCHENGINE = NO
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
-# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# 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
+# 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
+# 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.
+# Note that when enabling USE_PDFLATEX this option is only used for
+# generating bitmaps for formulas in the HTML output, but not in the
+# Makefile that is written to the output directory.
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
+# 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
+# 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
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, 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
+# 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 =
+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
+# 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 =
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for
+# the generated latex document. The footer should contain everything after
+# the last chapter. If it is left blank doxygen will generate a
+# standard footer. Notice: only use this tag if you know what you are doing!
-# 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
+LATEX_FOOTER =
+
+# 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
+# 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.
+# 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.)
+# 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
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include
+# source code with syntax highlighting in the LaTeX output.
+# Note that which sources are shown also depends on other settings
+# such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See
+# http://en.wikipedia.org/wiki/BibTeX for more info.
+
+LATEX_BIB_STYLE = plain
+
#---------------------------------------------------------------------------
# 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
+# 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
+# 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
+# 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.
+# 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
+# Load style sheet 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 =
+RTF_STYLESHEET_FILE =
-# Set optional variables used in the generation of an rtf document.
+# Set optional variables used in the generation of an rtf document.
# Syntax is similar to doxygen's config file.
-RTF_EXTENSIONS_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
+# 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
+# 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 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
+# 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
@@ -907,33 +1396,33 @@ 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
+# 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
+# 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
+# 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 =
+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
+# 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 =
+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
+# 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
@@ -942,10 +1431,10 @@ 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
+# 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
@@ -954,307 +1443,364 @@ 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
+# 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
+# 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
+# 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
+# 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 =
+PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
-# Configuration options related to the preprocessor
+# 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
+# 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
+# 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
+# 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.
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# pointed to by INCLUDE_PATH will be searched when 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 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 =
+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
+# 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 =
+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
+# 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 =
+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.
+# 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 that
+# overrules the definition found in the source code.
-EXPAND_AS_DEFINED =
+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.
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all references to function-like macros
+# that are alone on a line, have an all uppercase name, and do not end with a
+# semicolon, because these will confuse the parser if not removed.
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
-# Configuration::additions related to external references
+# 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
+# The TAGFILES option can be used to specify one or more tagfiles. For each
+# tag file the location of the external documentation should be added. 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. 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 =
+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
+# 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
+# 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
+# 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
+# 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.
+# 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 also works with HAVE_DOT disabled, but 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.
+# 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 =
+MSCGEN_PATH =
-# If set to YES, the inheritance and collaboration graphs will hide
-# inheritance and usage relations if the target is undocumented
+# 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
+# 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.
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is
+# allowed to run in parallel. When set to 0 (the default) doxygen will
+# base this on the number of processors available in the system. You can set it
+# explicitly to a value larger than 0 to get control over the balance
+# between CPU load and processing speed.
+
+DOT_NUM_THREADS = 0
+
+# By default doxygen will use the Helvetica font for all dot files that
+# doxygen generates. When you want a differently looking font you can specify
+# the font name using DOT_FONTNAME. You need to make sure dot is able to find
+# the font, which can be done by putting it in a standard location or by setting
+# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
+# directory containing the font.
+
+DOT_FONTNAME = FreeSans.ttf
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the Helvetica font.
+# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to
+# set the path where dot can find it.
+
+DOT_FONTPATH =
+
+# 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
+# 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
+# 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
+# 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
+# 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
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside
+# the class node. If there are many fields or methods and many nodes the
+# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS
+# threshold limits the number of items for each type to make the size more
+# managable. Set this to 0 for no limit. Note that the threshold may be
+# exceeded by 50% before the limit is enforced.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# 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
+# 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
+# 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.
+# If the CALL_GRAPH and HAVE_DOT options 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
+CALL_GRAPH = YES
-# 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.
+# 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
+CALLER_GRAPH = YES
-# 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.
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will generate a 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
+# If the DIRECTORY_GRAPH 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
+DIRECTORY_GRAPH = YES
-# 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.
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are svg, png, jpg, or gif.
+# If left blank png will be used. If you choose svg you need to set
+# HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible in IE 9+ (other browsers do not have this requirement).
DOT_IMAGE_FORMAT = png
-# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+# Note that this requires a modern browser other than Internet Explorer.
+# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you
+# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files
+# visible. Older versions of IE do not have SVG support.
+
+INTERACTIVE_SVG = NO
+
+# 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 =
+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
+# 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 =
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_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.
+# The 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 if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
-DOT_GRAPH_MAX_NODES = 50
+DOT_GRAPH_MAX_NODES = 100
-# 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).
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. 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)
+# 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
+# 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
+# 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/DoxygenLayout.xml b/doc/DoxygenLayout.xml
new file mode 100644
index 00000000..589d0f10
--- /dev/null
+++ b/doc/DoxygenLayout.xml
@@ -0,0 +1,187 @@
+<doxygenlayout version="1.0">
+ <!-- Navigation index tabs for HTML output -->
+ <navindex>
+ <tab type="mainpage" visible="yes" title=""/>
+ <tab type="pages" visible="yes" title="" intro=""/>
+ <tab type="modules" visible="yes" title="" intro=""/>
+ <tab type="namespaces" visible="yes" title="">
+ <tab type="namespacelist" visible="yes" title="" intro=""/>
+ <tab type="namespacemembers" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="classes" visible="yes" title="">
+ <tab type="classlist" visible="yes" title="" intro=""/>
+ <tab type="classindex" visible="$ALPHABETICAL_INDEX" title=""/>
+ <tab type="hierarchy" visible="yes" title="" intro=""/>
+ <tab type="classmembers" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="files" visible="yes" title="">
+ <tab type="filelist" visible="yes" title="" intro=""/>
+ <tab type="globals" visible="yes" title="" intro=""/>
+ </tab>
+ <tab type="examples" visible="yes" title="" intro=""/>
+ </navindex>
+
+ <!-- Layout definition for a class page -->
+ <class>
+ <briefdescription visible="yes"/>
+ <includes visible="$SHOW_INCLUDE_FILES"/>
+ <inheritancegraph visible="$CLASS_GRAPH"/>
+ <collaborationgraph visible="$COLLABORATION_GRAPH"/>
+ <allmemberslink visible="yes"/>
+ <memberdecl>
+ <nestedclasses visible="yes" title=""/>
+ <publictypes title=""/>
+ <publicslots title=""/>
+ <signals title=""/>
+ <publicmethods title=""/>
+ <publicstaticmethods title=""/>
+ <publicattributes title=""/>
+ <publicstaticattributes title=""/>
+ <protectedtypes title=""/>
+ <protectedslots title=""/>
+ <protectedmethods title=""/>
+ <protectedstaticmethods title=""/>
+ <protectedattributes title=""/>
+ <protectedstaticattributes title=""/>
+ <packagetypes title=""/>
+ <packagemethods title=""/>
+ <packagestaticmethods title=""/>
+ <packageattributes title=""/>
+ <packagestaticattributes title=""/>
+ <properties title=""/>
+ <events title=""/>
+ <privatetypes title=""/>
+ <privateslots title=""/>
+ <privatemethods title=""/>
+ <privatestaticmethods title=""/>
+ <privateattributes title=""/>
+ <privatestaticattributes title=""/>
+ <friends title=""/>
+ <related title="" subtitle=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <inlineclasses title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <constructors title=""/>
+ <functions title=""/>
+ <related title=""/>
+ <variables title=""/>
+ <properties title=""/>
+ <events title=""/>
+ </memberdef>
+ <usedfiles visible="$SHOW_USED_FILES"/>
+ <authorsection visible="yes"/>
+ </class>
+
+ <!-- Layout definition for a namespace page -->
+ <namespace>
+ <briefdescription visible="yes"/>
+ <memberdecl>
+ <nestednamespaces visible="yes" title=""/>
+ <classes visible="yes" title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <inlineclasses title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ </memberdef>
+ <authorsection visible="yes"/>
+ </namespace>
+
+ <!-- Layout definition for a file page -->
+ <file>
+ <briefdescription visible="yes"/>
+ <includes visible="$SHOW_INCLUDE_FILES"/>
+ <includegraph visible="$INCLUDE_GRAPH"/>
+ <includedbygraph visible="$INCLUDED_BY_GRAPH"/>
+ <sourcelink visible="yes"/>
+ <memberdecl>
+ <classes visible="yes" title=""/>
+ <namespaces visible="yes" title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <inlineclasses title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ </memberdef>
+ <authorsection/>
+ </file>
+
+ <!-- Layout definition for a group page -->
+ <group>
+ <briefdescription visible="yes"/>
+ <groupgraph visible="$GROUP_GRAPHS"/>
+ <memberdecl>
+ <nestedgroups visible="yes" title=""/>
+ <dirs visible="yes" title=""/>
+ <files visible="yes" title=""/>
+ <namespaces visible="yes" title=""/>
+ <classes visible="yes" title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <enumvalues title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <signals title=""/>
+ <publicslots title=""/>
+ <protectedslots title=""/>
+ <privateslots title=""/>
+ <events title=""/>
+ <properties title=""/>
+ <friends title=""/>
+ <membergroups visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ <memberdef>
+ <pagedocs/>
+ <inlineclasses title=""/>
+ <defines title=""/>
+ <typedefs title=""/>
+ <enums title=""/>
+ <enumvalues title=""/>
+ <functions title=""/>
+ <variables title=""/>
+ <signals title=""/>
+ <publicslots title=""/>
+ <protectedslots title=""/>
+ <privateslots title=""/>
+ <events title=""/>
+ <properties title=""/>
+ <friends title=""/>
+ </memberdef>
+ <authorsection visible="yes"/>
+ </group>
+
+ <!-- Layout definition for a directory page -->
+ <directory>
+ <briefdescription visible="yes"/>
+ <directorygraph visible="yes"/>
+ <memberdecl>
+ <dirs visible="yes"/>
+ <files visible="yes"/>
+ </memberdecl>
+ <detaileddescription title=""/>
+ </directory>
+</doxygenlayout>
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 040ff877..338f0772 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -1,9 +1,73 @@
# -*- Makefile -*-
-.PHONY: gendoc
+.PHONY: gendoc api_ref asciidoc
-gendoc:
+ASCIIDOCOPTS=-a pygments -a language=c -a icons \
+ -a toc2 \
+ -a numbered \
+ -a imagesdir="./images/" \
+ -a iconsdir="./images/icons" \
+ -a stylesdir="${abs_srcdir}/stylesheets/"
+
+EXTRA_DIST = \
+ core.txt \
+ core.html \
+ route.txt \
+ route.html \
+ index.txt \
+ index.html \
+ libnl.css \
+ stylesheets \
+ images \
+ api
+
+dist-hook:
+ rm -f $(distdir)/aclocal.m4
+ rm -f $(distdir)/configure
+ rm -f $(distdir)/configure.in
+ rm -rf $(distdir)/m4
+ rm -f $(distdir)/README
+ rm -f $(distdir)/missing
+ rm -f $(distdir)/Doxyfile.in
+ rm -f $(distdir)/Makefile.am
+ rm -f $(distdir)/Makefile.in
+
+link_doc:
+if LINK_DOC
+ ./gen-tags.sh > libnl.dict
+else
+ @echo "Warning: Linking to API reference is disabled, check configure output"
+endif
+
+
+%.html: %.txt link_doc
+ ./resolve-asciidoc-refs.py $< > asciidoc.tmp
+ asciidoc $(ASCIIDOCOPTS) -o $@ asciidoc.tmp
+if LINK_DOC
+ ./doxygen-link.py libnl.dict $@ > asciidoc.tmp
+ mv asciidoc.tmp $@
+endif
+
+asciidoc: core.html route.html index.html
+
+api_ref:
doxygen Doxyfile;
-distclean-local:
- rm -f html/*;
+gendoc:
+if GENERATE_DOC
+if HAVE_DOXYGEN
+ $(MAKE) api_ref
+else
+ @echo "Warning: Building of API reference (doxygen) is disabled, check autoconf logs"
+endif
+if HAVE_ASCIIDOC
+ $(MAKE) asciidoc
+else
+ @echo "Warning: Building of asciidoc files is disabled, check autoconf logs"
+endif
+else
+ @echo "Warning: Building of documentation disabled by user or autoconf"
+endif
+
+clean-local:
+ rm -f api/* libnl.dict *.html;
diff --git a/doc/README b/doc/README
new file mode 100644
index 00000000..ddcdf14f
--- /dev/null
+++ b/doc/README
@@ -0,0 +1,13 @@
+Requirements to build documentation
+
+mscgen
+ http://www.mcternan.me.uk/mscgen/
+
+mscgen-filter-1.2
+ http://code.google.com/p/asciidoc-mscgen-filter/
+
+asciidoc > 8.6.x
+doxygen > 1.8.0
+
+Building the documentation:
+make gendoc
diff --git a/doc/api/.gitignore b/doc/api/.gitignore
new file mode 100644
index 00000000..e57ca889
--- /dev/null
+++ b/doc/api/.gitignore
@@ -0,0 +1,8 @@
+*.html
+*.png
+*.css
+*.map
+*.md5
+*.js
+formula.repository
+jquery.js
diff --git a/doc/autogen.sh b/doc/autogen.sh
new file mode 100755
index 00000000..a5696148
--- /dev/null
+++ b/doc/autogen.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+autoreconf -fi;
+rm -Rf autom4te.cache;
diff --git a/doc/configure.ac b/doc/configure.ac
new file mode 100644
index 00000000..d4cda857
--- /dev/null
+++ b/doc/configure.ac
@@ -0,0 +1,107 @@
+#
+# 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-2013 Thomas Graf <tgraf@suug.ch>
+#
+
+AC_INIT(libnl-doc, [3.2.25], [http://www.infradead.org/~tgr/libnl/])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_AUX_DIR([build-aux])
+AM_INIT_AUTOMAKE([foreign])
+m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES(yes)], [])
+
+m4_include([m4/ax_python.m4])
+
+#
+# Generating the documentation
+#
+AC_ARG_ENABLE([doc],
+ AS_HELP_STRING([--disable-doc], [Do not generate documentation]),
+ [generate_doc="$enableval"], [generate_doc=auto])
+
+AX_PYTHON
+
+if test "x$generate_doc" != "xno"; then
+ AC_PROG_SED
+ AC_PROG_EGREP
+
+ AC_CHECK_PROG(HAVE_DOXYGEN, [doxygen], yes, no)
+ if test "x$HAVE_DOXYGEN" = "xno" -a "x$generate_doc" = "xyes"; then
+ AC_MSG_ERROR([*** doxygen package required to generate documentation])
+ fi
+
+ AC_CHECK_PROG(HAVE_DOT, [dot], yes, no)
+ if test "x$HAVE_DOT" = "xno"; then
+ if test "x$generate_doc" = "xyes"; then
+ AC_MSG_ERROR([*** graphviz package required to generate documentation])
+ else
+ AC_MSG_WARN([*** graphviz not found, disabling building of API reference])
+ HAVE_DOXYGEN=no
+ fi
+ fi
+
+ AC_CHECK_PROG(HAVE_ASCIIDOC, [asciidoc], yes, no)
+ if test "x$HAVE_ASCIIDOC" = "xno"; then
+ if test "x$generate_doc" = "xyes"; then
+ AC_MSG_ERROR([*** asciidoc package required to generate documentation])
+ else
+ AC_MSG_WARN([*** asciidoc not found, disabling building of guides])
+ fi
+ fi
+
+ AC_CHECK_PROG(HAVE_SOURCE_HIGHLIGHT, [source-highlight], yes, no)
+ if test "x$HAVE_SOURCE_HIGHLIGHT" = "xno"; then
+ if test "x$generate_doc" = "xyes"; then
+ AC_MSG_ERROR([*** source-highlight required to generate documentation])
+ else
+ AC_MSG_WARN([*** source-highlight not found, disabling building of guides])
+ HAVE_ASCIIDOC=no
+ fi
+ fi
+
+ AC_CHECK_PROG(HAVE_MSCGEN, [mscgen], yes, no)
+ if test "x$HAVE_MSCGEN" = "xno"; then
+ AC_MSG_WARN([*** mscgen not found, get it at http://www.mcternan.me.uk/mscgen/])
+ if test "x$generate_doc" = "xyes"; then
+ AC_MSG_ERROR([*** mscgen package required to generate documentation])
+ else
+ AC_MSG_WARN([*** Disabling building of guides])
+ HAVE_ASCIIDOC=no
+ HAVE_DOXYGEN=no
+ fi
+ fi
+
+ AC_CHECK_PROG(HAVE_PYGMENTIZE, [pygmentize], yes, no)
+ if test "x$HAVE_PYGMENTIZE" = "xno"; then
+ if test "x$generate_doc" = "xyes"; then
+ AC_MSG_ERROR([*** pygmentize package required to generate documentation])
+ else
+ AC_MSG_WARN([*** Disabling building of guides])
+ HAVE_ASCIIDOC=no
+ fi
+ fi
+
+ link_doc=yes
+ if test "x$HAVE_DOXYGEN" = "xno"; then
+ AC_MSG_WARN([*** Disabling API linking due to missing doxygen package])
+ link_doc=no
+ fi
+fi
+
+AM_CONDITIONAL([LINK_DOC], [test "x$link_doc" = "xyes"])
+AM_CONDITIONAL([HAVE_DOXYGEN], [test "x$HAVE_DOXYGEN" = "xyes"])
+AM_CONDITIONAL([HAVE_ASCIIDOC], [test "x$HAVE_ASCIIDOC" = "xyes"])
+
+AM_CONDITIONAL([GENERATE_DOC], [test "x$generate_doc" != "xno"])
+
+AC_CONFIG_FILES([
+Doxyfile
+Makefile
+])
+
+AC_OUTPUT
diff --git a/doc/core.txt b/doc/core.txt
new file mode 100644
index 00000000..042369d3
--- /dev/null
+++ b/doc/core.txt
@@ -0,0 +1,3017 @@
+////
+ vim.syntax: asciidoc
+
+ Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+////
+
+Netlink Library (libnl)
+=======================
+Thomas Graf <tgraf@suug.ch>
+3.2, May 9 2011:
+:numbered:
+
+== Introduction
+
+The core library contains the fundamentals required to communicate
+over netlink sockets. It deals with connecting and disconnectng of
+sockets, sending and receiving of data, construction and parsing of
+messages, provides a customizeable receiving state machine, and
+provides a abstract data type framework which eases the implementation
+of object based netlink protocols where objects are added, removed, or
+modified using a netlink based protocol.
+
+.Library Hierarchy
+
+The suite is split into multiple libraries:
+
+image:library_overview.png["Library Hierarchy"]
+
+link:core.html[Netlink Library] (libnl)::
+Socket handling, sending and receiving, message construction and parsing, ...
+
+link:route.html[Routing Family Library] (libnl-route)::
+Adresses, links, neighbours, routing, traffic control, neighbour tables, ...
+
+Netfilter Library (libnl-nf)::
+Connection tracking, logging, queueing
+
+Generic Netlink Library (libnl-genl)::
+Controller API, family and command registration
+
+
+=== How To Read This Documentation
+
+The libraries provide a broad set of APIs of which most applications only
+require a small subset of it. Depending on the type of application, some
+users may only be interested in the low level netlink messaging API while
+others wish to make heavy use of the high level API.
+
+In any case it is recommended to get familiar with the netlink protocol
+first.
+
+- <<core_netlink_fundamentals>>
+
+The low level APIs are described in:
+
+- <<core_sockets>>
+- <<core_send_recv>>
+
+
+=== Linking to this Library
+
+.Checking the presence of the library using autoconf
+
+Projects using autoconf may use +PKG_CHECK_MODULES()+ to check if
+a specific version of libnl is available on the system. The example
+below also shows how to retrieve the +CFLAGS+ and linking dependencies
+required to link against the library.
+
+The following example shows how to check for a specific version of libnl. If
+found, it extends the `CFLAGS` and `LIBS` variable appropriately:
+
+[source]
+----
+PKG_CHECK_MODULES(LIBNL3, libnl-3.0 >= 3.1, [have_libnl3=yes], [have_libnl3=no])
+if (test "${have_libnl3}" = "yes"); then
+ CFLAGS+="$LIBNL3_CFLAGS"
+ LIBS+="$LIBNL3_LIBS"
+fi
+----
+
+NOTE: The pkgconfig file is named +libnl-3.0.pc+ for historic reasons, it also
+ covers library versions >= 3.1.
+
+.Header Files
+
+The main header file is `<netlink/netlink.h>`. Additional headers may need to
+be included in your sources depending on the subsystems and components your
+program makes use of.
+
+[source,c]
+-----
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/route/link.h>
+-----
+
+.Version Dependent Code
+
+If your code wishes to be capable to link against multiple versions of libnl
+you may have direct the compiler to only include portions on the code depending
+on the version of libnl that it is compiled against.
+
+[source,c]
+-----
+#include <netlink/version.h>
+
+#if LIBNL_VER_NUM >= LIBNL_VER(3,1)
+ /* include code if compiled with libnl version >= 3.1 */
+#endif
+-----
+
+.Linking
+-----
+$ gcc myprogram.c -o myprogram $(pkgconfig --cflags --libs libnl-3.0)
+-----
+
+=== Debugging
+
+The library has been compiled with debugging statements enabled it will
+print debug information to +stderr+ if the environment variable +NLDBG+
+is set to > 0.
+
+-----
+$ NLDBG=2 ./myprogram
+-----
+
+.Debugging Levels
+[options="header", width="80%", cols="1,5", align="center"]
+|===============================================================
+| Level | Description
+| 0 | Debugging disabled (default)
+| 1 | Warnings, important events and notifications
+| 2 | More or less important debugging messages
+| 3 | Repetitive events causing a flood of debugging messages
+| 4 | Even less important messages
+|===============================================================
+
+.Debugging the Netlink Protocol
+
+It is often useful to peek into the stream of netlink messages exchanged
+with other sockets. Setting the environment variable +NLCB=debug+ will
+cause the debugging message handlers to be used which in turn print the
+netlink messages exchanged in a human readable format to to +stderr+:
+
+-----
+$ NLCB=debug ./myprogram
+-- Debug: Sent Message:
+-------------------------- BEGIN NETLINK MESSAGE ---------------------------
+ [HEADER] 16 octets
+ .nlmsg_len = 20
+ .nlmsg_type = 18 <route/link::get>
+ .nlmsg_flags = 773 <REQUEST,ACK,ROOT,MATCH>
+ .nlmsg_seq = 1301410712
+ .nlmsg_pid = 20014
+ [PAYLOAD] 16 octets
+ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
+--------------------------- END NETLINK MESSAGE ---------------------------
+-- Debug: Received Message:
+-------------------------- BEGIN NETLINK MESSAGE ---------------------------
+ [HEADER] 16 octets
+ .nlmsg_len = 996
+ .nlmsg_type = 16 <route/link::new>
+ .nlmsg_flags = 2 <MULTI>
+ .nlmsg_seq = 1301410712
+ .nlmsg_pid = 20014
+ [PAYLOAD] 16 octets
+ 00 00 04 03 01 00 00 00 49 00 01 00 00 00 00 00 ........I.......
+ [ATTR 03] 3 octets
+ 6c 6f 00 lo.
+ [PADDING] 1 octets
+ 00 .
+ [ATTR 13] 4 octets
+ 00 00 00 00 ....
+ [ATTR 16] 1 octets
+ 00 .
+ [PADDING] 3 octets
+ 00 00 00 ...
+ [ATTR 17] 1 octets
+ 00 .
+ [...]
+--------------------------- END NETLINK MESSAGE ---------------------------
+
+-----
+
+[[core_netlink_fundamentals]]
+== Netlink Protocol Fundamentals
+
+The netlink protocol is a socket based IPC mechanism used for
+communication between userspace processes and the kernel or between
+userspace processes themselves. The netlink protocol is based on BSD
+sockets and uses the +AF_NETLINK+ address family. Every netlink
+protocol uses its own protocol number (e.g. +NETLINK_ROUTE+,
++NETLINK_NETFILTER+, etc). Its addressing schema is based on a 32 bit
+port number, formerly referred to as PID, which uniquely identifies
+each peer.
+
+[[core_addressing]]
+=== Addressing
+
+The netlink address (port) consists of a 32bit integer. Port 0 (zero)
+is reserved for the kernel and refers to the kernel side socket of each
+netlink protocol family. Other port numbers usually refer to user space
+owned sockets, although this is not enforced.
+
+NOTE: In the beginning, it was common practice to use the process
+ identifier (PID) as the local port number. This became unpractical
+ with the introduction of threaded netlink applications and
+ applications requiring multiple sockets. Therefore libnl generates
+ unique port numbers based on the process identifier and adds an
+ offset to it allowing for multiple sockets to be used. The initial
+ socket will still equal to the process identifier for backwards
+ compatibility reasons.
+
+image:addressing.png["Addressing Example"]
+
+The above figure illustrates three applications and the kernel side
+exposing two kernel side sockets. It shows the common netlink use
+cases:
+
+ * User space to kernel
+ * User space to user space
+ * Listening to kernel multicast notifications
+
+.User Space to Kernel
+
+The most common form of netlink usage is for a user space application
+to send requests to the kernel and process the reply which is either
+an error message or a success notification.
+
+["mscgen"]
+--------
+msc {
+ App1,App2,Kernel;
+ App1=>Kernel [label="request (src=11, dst=0)"];
+ App1<=Kernel [label="reply (src=0, dst=11)"];
+ ...;
+ App2=>Kernel [label="request (src=21, dst=0)"];
+ App2<=Kernel [label="reply (src=0, dst=21)"];
+}
+--------
+
+.User Space to User Space
+
+Netlink may also be used as an IPC mechanism to communicate between user
+space applications directly. Communication is not limited to two peers,
+any number of peers may communicate with each other and multicasting
+capabilities allow to reach multiple peers with a single message.
+
+In order for the sockets to be visible to each other, both sockets must
+be created for the same netlink protocol family.
+
+["mscgen"]
+--------
+msc {
+ App2,App3;
+ App2=>App3 [label="request (src=22, dst=31)"];
+ App2<=App3 [label="reply (src=31, dst=22)"];
+ ...;
+}
+--------
+
+.User space listening to kernel notifications
+
+This form of netlink communication is typically found in user space
+daemons that need to act on certain kernel events. Such daemons will
+typically maintain a netlink socket subscribed to a multicast group that
+is used by the kernel to notify interested user space parties about
+specific events.
+
+["mscgen"]
+--------
+msc {
+ Kernel,App3;
+ Kernel=>App3 [label="notification (src=0, group=foo)"];
+ ...;
+}
+--------
+
+Use of multicasting is preferred over direct addressing due to the
+flexibility in exchanging the user space component at any time without
+the kernel noticing.
+
+[[core_msg_format]]
+=== Message Format
+
+A netlink protocol is typically based on messages and consists of the
+netlink message header (+struct nlmsghdr+) plus the payload attached
+to it. The payload can consist of arbitrary data but usually contains
+a fixed size protocol specific header followed by a stream of
+attributes.
+
+.Netlink message header (struct nlmsghdr)
+
+image:nlmsghdr.png[align="center", alt="Netlink Message Header"]
+
+Total Length (32bit)::
+Total length of the message in bytes including the netlink message header.
+
+Message Type (16bit)::
+The message type specifies the type of payload the message is carrying.
+Several standard message types are defined by the netlink protocol.
+Additional message types may be defined by each protocol family. See
+<<core_msg_types>> for additional information.
+
+Message Flags (16bit)::
+The message flags may be used to modify the behaviour of a message type.
+See section <<core_msg_flags>> for a list of standard message flags.
+
+Sequence Number (32bit)::
+The sequence number is optional and may be used to allow referring to
+a previous message, e.g. an error message can refer to the original
+request causing the error.
+
+Port Number (32bit)::
+The port number specifies the peer to which the message should be delivered
+to. If not specified, the message will be delivered to the first matching
+kernel side socket of the same protocol family.
+
+[[core_msg_types]]
+=== Message Types
+
+Netlink differs between requests, notifications, and replies. Requests
+are messages which have the +NLM_F_REQUEST+ flag set and are meant to
+request an action from the receiver. A request is typically sent from
+a userspace process to the kernel. While not strictly enforced, requests
+should carry a sequence number incremented for each request sent.
+
+Depending on the nature of the request, the receiver may reply to the
+request with another netlink message. The sequence number of a reply
+must match the sequence number of the request it relates to.
+
+Notifications are of informal nature and no reply is expected, therefore
+the sequence number is typically set to 0.
+
+["mscgen"]
+--------
+msc {
+ A,B;
+ A=>B [label="GET (seq=1, NLM_F_REQUEST)"];
+ A<=B [label="PUT (seq=1)"];
+ ...;
+ A<=B [label="NOTIFY (seq=0)"];
+}
+--------
+
+
+The type of message is primarly identified by its 16 bit message type set
+in the message header. The following standard message types are defined:
+
+- +NLMSG_NOOP+ - No operation, message must be discarded
+- +NLMSG_ERROR+ - Error message or ACK, see <<core_errmsg>>
+ respectively <<core_msg_ack>>
+- +NLMSG_DONE+ - End of multipart sequence, see <<core_multipart>>
+- +NLMSG_OVERRUN+ - Overrun notification (Error)
+
+Every netlink protocol is free to define own message types. Note that
+message type values +< NLMSG_MIN_TYPE (0x10)+ are reserved and may
+not be used.
+
+It is common practice to use own message types to implement RPC schemas.
+Suppose the goal of the netlink protocol you are implementing is allow
+configuration of a particular network device, therefore you want to
+provide read/write access to various configuration options. The typical
+"netlink way" of doing this would be to define two message types
++MSG_SETCFG+, +MSG_GETCFG+:
+
+[source,c]
+--------
+#define MSG_SETCFG 0x11
+#define MSG_GETCFG 0x12
+--------
+
+Sending a +MSG_GETCFG+ request message will typically trigger a reply
+with the message type +MSG_SETCFG+ containing the current configuration.
+In object oriented terms one would describe this as "the kernel sets
+the local copy of the configuration in userspace".
+
+["mscgen"]
+--------
+msc {
+ A,B;
+ A=>B [label="MSG_GETCFG (seq=1, NLM_F_REQUEST)"];
+ A<=B [label="MSG_SETCFG (seq=1)"];
+}
+--------
+
+The configuration may be changed by sending a +MSG_SETCFG+ which will
+be responded to with either a ACK (see <<core_msg_ack>>)
+or a error message (see <<core_errmsg>>).
+
+["mscgen"]
+--------
+msc {
+ A,B;
+ A=>B [label="MSG_SETCFG (seq=1, NLM_F_REQUEST, NLM_F_ACK)"];
+ A<=B [label="ACK (seq=1)"];
+}
+--------
+
+Optionally, the kernel may send out notifications for configuration
+changes allowing userspace to listen for changes instead of polling
+frequently. Notifications typically reuse an existing message type
+and rely on the application using a separate socket to differ between
+requests and notifications but you may also specify a separate message
+type.
+
+["mscgen"]
+--------
+msc {
+ A,B;
+ A<=B [label="MSG_SETCFG (seq=0)"];
+}
+--------
+
+[[core_multipart]]
+==== Multipart Messages
+
+Although in theory a netlink message can be up to 4GiB in size. The socket
+buffers are very likely not large enough to hold message of such sizes.
+Therefore it is common to limit messages to one page size (PAGE_SIZE) and
+use the multipart mechanism to split large pieces of data into several
+messages. A multipart message has the flag +NLM_F_MULTI+ set and the
+receiver is expected to continue receiving and parsing until the special
+message type +NLMSG_DONE+ is received.
+
+Multipart messages unlike fragmented ip packets must not be reassmbled
+even though it is perfectly legal to do so if the protocols wishes to
+work this way. Often multipart message are used to send lists or trees
+of objects were each multipart message simply carries multiple objects
+allow for each message to be parsed independently.
+
+["mscgen"]
+--------
+msc {
+ A,B;
+ A=>B [label="GET (seq=1, NLM_F_REQUEST)"];
+ A<=B [label="PUT (seq=1, NLM_F_MULTI)"];
+ ...;
+ A<=B [label="PUT (seq=1, NLM_F_MULTI)"];
+ A<=B [label="NLMSG_DONE (seq=1)"];
+}
+--------
+
+[[core_errmsg]]
+==== Error Message
+
+Error messages can be sent in response to a request. Error messages must
+use the standard message type +NLMSG_ERROR+. The payload consists of a
+error code and the original netlink mesage header of the request.
+
+image:nlmsgerr.png["Netlink Errror Message header"]
+
+Error messages should set the sequence number to the sequence number
+of the request which caused the error.
+
+["mscgen"]
+--------
+msc {
+ A,B;
+ A=>B [label="GET (seq=1, NLM_F_REQUEST)"];
+ A<=B [label="NLMSG_ERROR code=EINVAL (seq=1)"];
+}
+--------
+
+[[core_msg_ack]]
+==== ACKs
+
+A sender can request an ACK message to be sent back for each request
+processed by setting the +NLM_F_ACK+ flag in the request. This is typically
+used to allow the sender to synchronize further processing until the
+request has been processed by the receiver.
+
+["mscgen"]
+--------
+msc {
+ A,B;
+ A=>B [label="GET (seq=1, NLM_F_REQUEST | NLM_F_ACK)"];
+ A<=B [label="ACK (seq=1)"];
+}
+--------
+
+ACK messages also use the message type +NLMSG_ERROR+ and payload
+format but the error code is set to 0.
+
+[[core_msg_flags]]
+==== Message Flags
+
+The following standard flags are defined
+
+[source,c]
+--------
+#define NLM_F_REQUEST 1
+#define NLM_F_MULTI 2
+#define NLM_F_ACK 4
+#define NLM_F_ECHO 8
+--------
+
+- `NLM_F_REQUEST` - Message is a request, see <<core_msg_types>>.
+- `NLM_F_MULTI` - Multipart message, see <<core_multipart>>
+- `NLM_F_ACK` - ACK message requested, see <<core_msg_ack>>.
+- `NLM_F_ECHO` - Request to echo the request.
+
+The flag +NLM_F_ECHO+ is similar to the `NLM_F_ACK` flag. It can be
+used in combination with `NLM_F_REQUEST` and causes a notification
+which is sent as a result of a request to also be sent to the sender
+regardless of whether the sender has subscribed to the corresponding
+multicast group or not. See <<core_multicast>>
+
+Additional universal message flags are defined which only apply for
++GET+ requests:
+
+[source,c]
+--------
+#define NLM_F_ROOT 0x100
+#define NLM_F_MATCH 0x200
+#define NLM_F_ATOMIC 0x400
+#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
+--------
+
+- `NLM_F_ROOT` - Return based on root of tree.
+- `NLM_F_MATCH` - Return all matching entries.
+- `NLM_F_ATOMIC` - Obsoleted, once used to request an atomic operation.
+- `NLM_F_DUMP` - Return a list of all objects
+ (`NLM_F_ROOT`|`NLM_F_MATCH`).
+
+Use of these flags is completely optional and many netlink protocols only
+make use of the `NLM_F_DUMP` flag which typically requests the receiver
+to send a list of all objects in the context of the message type as a
+sequence of multipart messages (see <<core_multipart>>).
+
+Another set of flags exist related to `NEW` or `SET` requests. These
+flags are mutually exclusive to the `GET` flags:
+
+[source,c]
+--------
+#define NLM_F_REPLACE 0x100
+#define NLM_F_EXCL 0x200
+#define NLM_F_CREATE 0x400
+#define NLM_F_APPEND 0x800
+--------
+
+- `NLM_F_REPLACE` - Replace an existing object if it exists.
+- `NLM_F_EXCL` - Do not update object if it exists already.
+- `NLM_F_CREATE` - Create object if it does not exist yet.
+- `NLM_F_APPEND` - Add object at end of list.
+
+Behaviour of these flags may differ slightly between different netlink
+protocols.
+
+[[core_seq_num]]
+=== Sequence Numbers
+
+Netlink allows the use of sequence numbers to help relate replies to
+requests. It should be noted that unlike in protocols such as TCP
+there is no strict enforcment of the sequence number. The sole purpose
+of sequence numbers is to assist a sender in relating replies to the
+corresponding requests. See <<core_msg_types>> for more information.
+
+Sequence numbers are managed on a per socket basis, see
+<<core_sk_seq_num>> for more information on how to use sequence numbers.
+
+[[core_multicast]]
+=== Multicast Groups
+
+TODO
+
+See <<core_sk_multicast>>
+
+[[core_sockets]]
+== Netlink Sockets
+
+In order to use the netlink protocol, a netlink socket is required.
+Each socket defines an independent context for sending and receiving of
+messages. An application may make use multiple sockets, e.g. a socket to
+send requests and receive the replies and another socket subscribed to a
+multicast group to receive notifications.
+
+=== Socket structure (struct nl_sock)
+
+The netlink socket and all related attributes including the actual file
+descriptor are represented by +struct nl_sock+.
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+struct nl_sock *nl_socket_alloc(void)
+void nl_socket_free(struct nl_sock *sk)
+--------
+
+The application must allocate an instance of +struct nl_sock+ for each
+netlink socket it wishes to use.
+
+[[core_sk_seq_num]]
+=== Sequence Numbers
+
+The library will automatically take care of sequence number handling
+for the application. A sequence number counter is stored in the
+socket structure which is used and incremented automatically when a
+message needs to be sent which is expected to generate a reply such as
+an error or any other message type that needs to be related to the
+original message.
+
+Alternatively, the counter can be used directly via the function
+nl_socket_use_seq(). It will return the current value of the counter
+and increment it by one afterwards.
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+unsigned int nl_socket_use_seq(struct nl_sock *sk);
+--------
+
+Most applications will not want to deal with sequence number handling
+themselves though. When using nl_send_auto() the sequence number is
+filled in automatically and matched again when a reply is received. See
+section <<core_send_recv>> for more information.
+
+This behaviour can and must be disabled if the netlink protocol
+implemented does not use a request/reply model, e.g. when a socket is
+used to receive notification messages.
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+void nl_socket_disable_seq_check(struct nl_sock *sk);
+--------
+
+For more information on the theory behind netlink sequence numbers,
+see section <<core_seq_num>>.
+
+[[core_sk_multicast]]
+=== Multicast Group Subscriptions
+
+Each socket can subscribe to any number of multicast groups of the
+netlink protocol it is connected to. The socket will then receive a
+copy of each message sent to any of the groups. Multicast groups are
+commonly used to implement event notifications.
+
+Prior to kernel 2.6.14 the group subscription was performed using a
+bitmask which limited the number of groups per protocol family to 32.
+This outdated interface can still be accessed via the function
+nl_join_groups() even though it is not recommended for new code.
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+void nl_join_groups(struct nl_sock *sk, int bitmask);
+--------
+
+Starting with 2.6.14 a new method was introduced which supports subscribing
+to an almost infinite number of multicast groups.
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+int nl_socket_add_memberships(struct nl_sock *sk, int group, ...);
+int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...);
+--------
+
+==== Multicast Example
+
+[source,c]
+--------
+#include <netlink/netlink.h>
+#include <netlink/socket.h>
+#include <netlink/msg.h>
+
+/*
+ * This function will be called for each valid netlink message received
+ * in nl_recvmsgs_default()
+ */
+static int my_func(struct nl_msg *msg, void *arg)
+{
+ return 0;
+}
+
+struct nl_sock *sk;
+
+/* Allocate a new socket */
+sk = nl_socket_alloc();
+
+/*
+ * Notifications do not use sequence numbers, disable sequence number
+ * checking.
+ */
+nl_socket_disable_seq_check(sk);
+
+/*
+ * Define a callback function, which will be called for each notification
+ * received
+ */
+nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL);
+
+/* Connect to routing netlink protocol */
+nl_connect(sk, NETLINK_ROUTE);
+
+/* Subscribe to link notifications group */
+nl_socket_add_memberships(sk, RTNLGRP_LINK, 0);
+
+/*
+ * Start receiving messages. The function nl_recvmsgs_default() will block
+ * until one or more netlink messages (notification) are received which
+ * will be passed on to my_func().
+ */
+while (1)
+ nl_recvmsgs_default(sock);
+--------
+
+[[core_sk_cb]]
+=== Modifiying Socket Callback Configuration
+
+See <<core_cb>> for more information on
+callback hooks and overwriting capabilities.
+
+Each socket is assigned a callback configuration which controls the
+behaviour of the socket. This is f.e. required to have a separate
+message receive function per socket. It is perfectly legal to share
+callback configurations between sockets though.
+
+The following functions can be used to access and set the callback
+configuration of a socket:
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk);
+void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb);
+--------
+
+Additionaly a shortcut exists to modify the callback configuration
+assigned to a socket directly:
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type, enum nl_cb_kind kind,
+ nl_recvmsg_msg_cb_t func, void *arg);
+--------
+
+.Example:
+[source,c]
+--------
+#include <netlink/socket.h>
+
+// Call my_input() for all valid messages received in socket sk
+nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, my_input, NULL);
+--------
+
+=== Socket Attributes
+
+.Local Port
+
+The local port number uniquely identifies the socket and is used to
+address it. A unique local port is generated automatically when the
+socket is allocated. It will consist of the Process ID (22 bits) and a
+random number (10 bits) thus allowing up to 1024 sockets per process.
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+uint32_t nl_socket_get_local_port(const struct nl_sock *sk);
+void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port);
+--------
+
+See section <<core_addressing>> for more information on port numbers.
+
+CAUTION: Overwriting the local port is possible but you have to ensure
+that the provided value is unique and no other socket in any other
+application is using the same value.
+
+.Peer Port
+
+A peer port can be assigned to the socket which will result in all
+unicast messages sent over the socket to be addresses to the peer. If
+no peer is specified, the message is sent to the kernel which will try
+to automatically bind the socket to a kernel side socket of the same
+netlink protocol family. It is common practice not to bind the socket
+to a peer port as typically only one kernel side socket exists per
+netlink protocol family.
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+uint32_t nl_socket_get_peer_port(const struct nl_sock *sk);
+void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port);
+--------
+
+See section <<core_addressing>> for more information on port numbers.
+
+.File Descriptor
+
+Netlink uses the BSD socket interface, therefore a file descriptor is
+behind each socket and you may use it directly.
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+int nl_socket_get_fd(const struct nl_sock *sk);
+--------
+
+If a socket is used to only receive notifications it usually is best
+to put the socket in non-blocking mode and periodically poll for new
+notifications.
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+int nl_socket_set_nonblocking(const struct nl_sock *sk);
+--------
+
+.Send/Receive Buffer Size
+
+The socket buffer is used to queue netlink messages between sender and
+receiver. The size of these buffers specifies the maximum size you
+will be able to write() to a netlink socket, i.e. it will indirectly
+define the maximum message size. The default is 32KiB.
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+int nl_socket_set_buffer_size(struct nl_sock *sk, int rx, int tx);
+--------
+
+[[core_sk_cred]]
+.Enable/Disable Credentials
+
+TODO
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+int nl_socket_set_passcred(struct nl_sock *sk, int state);
+--------
+
+.Enable/Disable Auto-ACK Mode
+
+The following functions allow to enable/disable Auto-ACK mode on a socket.
+See <<core_auto_ack>> for more information on what implications that has.
+Auto-ACK mode is enabled by default.
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+void nl_socket_enable_auto_ack(struct nl_sock *sk);
+void nl_socket_disable_auto_ack(struct nl_sock *sk);
+--------
+
+.Enable/Disable Message Peeking
+
+If enabled, message peeking causes nl_recv() to try and use MSG_PEEK
+to retrieve the size of the next message received and allocate a
+buffer of that size. Message peeking is enabled by default but can be
+disabled using the following function:
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+void nl_socket_enable_msg_peek(struct nl_sock *sk);
+void nl_socket_disable_msg_peek(struct nl_sock *sk);
+--------
+
+.Enable/Disable Receival of Packet Information
+
+If enabled, each received netlink message from the kernel will include
+an additional struct nl_pktinfo in the control message. The following
+function can be used to enable/disable receival of packet information.
+
+[source,c]
+--------
+#include <netlink/socket.h>
+
+int nl_socket_recv_pktinfo(struct nl_sock *sk, int state);
+--------
+
+CAUTION: Processing of NETLINK_PKTINFO has not been implemented yet.
+
+[[core_send_recv]]
+== Sending and Receiving of Messages / Data
+
+[[core_send]]
+=== Sending Messages
+
+The standard method of sending a netlink message over a netlink socket
+is to use the function nl_send_auto(). It will automatically complete
+the netlink message by filling the missing bits and pieces in the
+netlink message header and will deal with addressing based on the
+options and address set in the netlink socket. The message is then
+passed on to nl_send().
+
+If the default sending semantics implemented by nl_send() do not suit
+the application, it may overwrite the sending function nl_send() by
+specifying an own implementation using the function
+nl_cb_overwrite_send().
+
+[source,c]
+--------
+ nl_send_auto(sk, msg)
+ |
+ |-----> nl_complete_msg(sk, msg)
+ |
+ |
+ | Own send function specified via nl_cb_overwrite_send()
+ |- - - - - - - - - - - - - - - - - - - -
+ v v
+ nl_send(sk, msg) send_func()
+--------
+
+.Using nl_send()
+
+If you do not require any of the automatic message completion
+functionality you may use nl_send() directly but beware that any
+internal calls to nl_send_auto() by the library to send netlink
+messages will still use nl_send(). Therefore if you wish to use any
+higher level interfaces and the behaviour of nl_send() is to your
+dislike then you must overwrite the nl_send() function via
+nl_cb_overwrite_send()
+
+The purpose of nl_send() is to embed the netlink message into a iovec
+structure and pass it on to nl_send_iovec().
+
+[source,c]
+--------
+ nl_send(sk, msg)
+ |
+ v
+ nl_send_iovec(sk, msg, iov, iovlen)
+--------
+
+.Using nl_send_iovec()
+
+nl_send_iovec() expects a finalized netlink message and fills out the
+struct msghdr used for addressing. It will first check if the struct
+nl_msg is addressed to a specific peer (see nlmsg_set_dst()). If not,
+it will try to fall back to the peer address specified in the socket
+(see nl_socket_set_peer_port(). Otherwise the message will be sent
+unaddressed and it is left to the kernel to find the correct peer.
+
+nl_send_iovec() also adds credentials if present and enabled
+(see <<core_sk_cred>>).
+
+The message is then passed on to nl_sendmsg().
+
+[source,c]
+--------
+ nl_send_iovec(sk, msg, iov, iovlen)
+ |
+ v
+ nl_sendmsg(sk, msg, msghdr)
+--------
+
+.Using nl_sendmsg()
+
+nl_sendmsg() expects a finalized netlink message and an optional
+struct msghdr containing the peer address. It will copy the local
+address as defined in the socket (see nl_socket_set_local_port()) into
+the netlink message header.
+
+At this point, construction of the message finished and it is ready to
+be sent.
+
+[source,c]
+--------
+ nl_sendmsg(sk, msg, msghdr)
+ |- - - - - - - - - - - - - - - - - - - - v
+ | NL_CB_MSG_OUT()
+ |<- - - - - - - - - - - - - - - - - - - -+
+ v
+ sendmsg()
+--------
+
+Before sending the application has one last chance to modify the
+message. It is passed to the NL_CB_MSG_OUT callback function which
+may inspect or modify the message and return an error code. If this
+error code is NL_OK the message is sent using sendmsg() resulting in
+the number of bytes written being returned. Otherwise the message
+sending process is aborted and the error code specified by the
+callback function is returned. See <<core_sk_cb>> for more information
+on how to set callbacks.
+
+.Sending Raw Data with nl_sendto()
+
+If you wish to send raw data over a netlink socket, the following
+function will pass on any buffer provided to it directly to sendto():
+
+[source,c]
+--------
+#include <netlink/netlink.h>
+
+int nl_sendto(struct nl_sock *sk, void *buf, size_t size);
+--------
+
+.Sending of Simple Messages
+
+A special interface exists for sending of trivial messages. The function
+expects the netlink message type, optional netlink message flags, and an
+optional data buffer and data length.
+[source,c]
+--------
+#include <netlink/netlink.h>
+
+int nl_send_simple(struct nl_sock *sk, int type, int flags,
+ void *buf, size_t size);
+--------
+
+The function will construct a netlink message header based on the message
+type and flags provided and append the data buffer as message payload. The
+newly constructed message is sent with nl_send_auto().
+
+The following example will send a netlink request message causing the
+kernel to dump a list of all network links to userspace:
+
+[source,c]
+--------
+#include <netlink/netlink.h>
+
+struct nl_sock *sk;
+struct rtgenmsg rt_hdr = {
+ .rtgen_family = AF_UNSPEC,
+};
+
+sk = nl_socket_alloc();
+nl_connect(sk, NETLINK_ROUTE);
+
+nl_send_simple(sock, RTM_GETLINK, NLM_F_DUMP, &rt_hdr, sizeof(rt_hdr));
+--------
+
+[[core_recv]]
+=== Receiving Messages
+
+The easiest method to receive netlink messages is to call nl_recvmsgs_default().
+It will receive messages based on the semantics defined in the socket. The
+application may customize these in detail although the default behaviour will
+probably suit most applications.
+
+nl_recvmsgs_default() will also be called internally by the library whenever
+it needs to receive and parse a netlink message.
+
+The function will fetch the callback configuration stored in the socket and
+call nl_recvmsgs():
+
+[source,c]
+--------
+ nl_recvmsgs_default(sk)
+ |
+ | cb = nl_socket_get_cb(sk)
+ v
+ nl_recvmsgs(sk, cb)
+--------
+
+.Using nl_recvmsgs()
+
+nl_recvmsgs() implements the actual receiving loop, it blocks until a
+netlink message has been received unless the socket has been put into
+non-blocking mode.
+
+For the unlikely scenario that certain required receive characteristics
+can not be achieved by fine tuning the internal recvmsgs function using
+the callback configuration (see <<core_sk_cb>>) the application may provide
+a complete own implementation of it and overwrite all calls to nl_recvmsgs()
+with the function nl_cb_overwrite_recvmsgs().
+
+[source,c]
+--------
+ nl_recvmsgs(sk, cb)
+ |
+ | Own recvmsgs function specified via nl_cb_overwrite_recvmsgs()
+ |- - - - - - - - - - - - - - - - - - - -
+ v v
+ internal_recvmsgs() my_recvmsgs()
+--------
+
+[[core_recv_character]]
+.Receive Characteristics
+
+If the application does not provide its own recvmsgs() implementation
+with the function nl_cb_overwrite_recvmsgs() the following characteristics
+apply while receiving data from a netlink socket:
+
+[source,c]
+--------
+ internal_recvmsgs()
+ |
++-------------->| Own recv function specified with nl_cb_overwrite_recv()
+| |- - - - - - - - - - - - - - - -
+| v v
+| nl_recv() my_recv()
+| |<- - - - - - - - - - - - - - -+
+| |<-------------+
+| v | More data to parse? (nlmsg_next())
+| Parse Message |
+| |--------------+
+| v
++------- NLM_F_MULTI set?
+ |
+ v
+ (SUCCESS)
+--------
+
+The function nl_recv() is invoked first to receive data from the
+netlink socket. This function may be overwritten by the application
+by an own implementation using the function nl_cb_overwrite_recv().
+This may be useful if the netlink byte stream is in fact not received
+from a socket directly but is read from a file or another source.
+
+If data has been read, it will be attemped to parse the data. This
+will be done repeately until the parser returns NL_STOP, an error was
+returned or all data has been parsed.
+
+In case the last message parsed successfully was a multipart message
+(see <<core_multipart>>) and the parser did not
+quit due to either an error or NL_STOP nl_recv() respectively the
+applications own implementation will be called again and the parser
+starts all over.
+
+See <<core_parse_character>> for information on how to extract valid
+netlink messages from the parser and on how to control the behaviour
+of it.
+
+[[core_parse_character]]
+.Parsing Characteristics
+
+The internal parser is invoked for each netlink message received from
+a netlink socket. It is typically fed by nl_recv() (see
+<<core_recv_character>>).
+
+The parser will first ensure that the length of the data stream
+provided is sufficient to contain a netlink message header and that
+the message length as specified in the message header does not exceed
+it.
+
+If this criteria is met, a new struct nl_msg is allocated and the
+message is passed on to the the callback function NL_CB_MSG_IN if one
+is set. Like any other callback function, it may return NL_SKIP to
+skip the current message but continue parsing the next message or
+NL_STOP to stop parsing completely.
+
+The next step is to check the sequence number of the message against
+the currently expected sequence number. The application may provide
+its own sequence number checking algorithm by setting the callback
+function NL_CB_SEQ_CHECK to its own implementation. In fact, calling
+nl_socket_disable_seq_check() to disable sequence number checking will
+do nothing more than set the NL_CB_SEQ_CHECK hook to a function which
+always returns NL_OK.
+
+Another callback hook NL_CB_SEND_ACK exists which is called if the
+message has the NLM_F_ACK flag set. Although I am not aware of any
+userspace netlink socket doing this, the application may want to send
+an ACK message back to the sender (see <<core_msg_ack>>).
+
+[source,c]
+--------
+ parse()
+ |
+ v
+ nlmsg_ok() --> Ignore
+ |
+ |- - - - - - - - - - - - - - - v
+ | NL_CB_MSG_IN()
+ |<- - - - - - - - - - - - - - -+
+ |
+ |- - - - - - - - - - - - - - - v
+ Sequence Check NL_CB_SEQ_CHECK()
+ |<- - - - - - - - - - - - - - -+
+ |
+ | Message has NLM_F_ACK set
+ |- - - - - - - - - - - - - - - v
+ | NL_CB_SEND_ACK()
+ |<- - - - - - - - - - - - - - -+
+ |
+ Handle Message Type
+--------
+
+[[core_auto_ack]]
+=== Auto-ACK Mode
+
+TODO
+
+== Message Parsing & Construction
+
+=== Message Format
+
+See <<core_netlink_fundamentals>> for an introduction to the netlink
+protocol and its message format.
+
+.Alignment
+
+Most netlink protocols enforce a strict alignment policy for all
+boundries. The alignment value is defined by NLMSG_ALIGNTO and is
+fixed to 4 bytes. Therefore all netlink message headers, begin of
+payload sections, protocol specific headers, and attribute sections
+must start at an offset which is a multiple of NLMSG_ALIGNTO.
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+int nlmsg_size(int payloadlen);
+int nlmsg_total_size(int payloadlen);
+--------
+
+The library provides a set of function to handle alignment
+requirements automatically. The function nlmsg_total_size() returns
+the total size of a netlink message including the padding to ensure
+the next message header is aligned correctly.
+
+[source,c]
+--------
+ <----------- nlmsg_total_size(len) ------------>
+ <----------- nlmsg_size(len) ------------>
+ +-------------------+- - -+- - - - - - - - +- - -+-------------------+- - -
+ | struct nlmsghdr | Pad | Payload | Pad | struct nlsmghdr |
+ +-------------------+- - -+- - - - - - - - +- - -+-------------------+- - -
+ <---- NLMSG_HDRLEN -----> <- NLMSG_ALIGN(len) -> <---- NLMSG_HDRLEN ---
+--------
+
+If you need to know if padding needs to be added at the end of a
+message, nlmsg_padlen() returns the number of padding bytes that need
+to be added for a specific payload length.
+
+[source,c]
+--------
+#include <netlink/msg.h>
+int nlmsg_padlen(int payloadlen);
+--------
+
+=== Parsing a Message
+
+The library offers two different methods of parsing netlink messages.
+It offers a low level interface for applications which want to do all
+the parsing manually. This method is described below. Alternatively
+the library also offers an interface to implement a parser as part of
+a cache operations set which is especially useful when your protocol
+deals with objects of any sort such as network links, routes, etc.
+This high level interface is described in <<core_cache>>.
+
+.Splitting a byte stream into separate messages
+
+What you receive from a netlink socket is typically a stream of
+messages. You will be given a buffer and its length, the buffer may
+contain any number of netlink messages.
+
+The first message header starts at the beginning of message stream.
+Any subsequent message headers are access by calling nlmsg_next() on
+the previous header.
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+struct nlmsghdr *nlmsg_next(struct nlmsghdr *hdr, int *remaining);
+--------
+
+The function nlmsg_next() will automatically substract the size of the
+previous message from the remaining number of bytes.
+
+Please note, there is no indication in the previous message whether
+another message follows or not. You must assume that more messages
+follow until all bytes of the message stream have been processed.
+
+To simplify this, the function nlmsg_ok() exists which returns true if
+another message fits into the remaining number of bytes in the message
+stream. nlmsg_valid_hdr() is similar, it checks whether a specific
+netlink message contains at least a minimum of payload.
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+int nlmsg_valid_hdr(const struct nlmsghdr *hdr, int payloadlen);
+int nlmsg_ok(const struct nlmsghdr *hdr, int remaining);
+--------
+
+A typical use of these functions looks like this:
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+void my_parse(void *stream, int length)
+{
+ struct nlmsghdr *hdr = stream;
+
+ while (nlmsg_ok(hdr, length)) {
+ // Parse message here
+ hdr = nlmsg_next(hdr, &length);
+ }
+}
+--------
+
+CAUTION: nlmsg_ok() only returns true if the *complete* message including
+ the message payload fits into the remaining buffer length. It will
+ return false if only a part of it fits.
+
+The above can also be written using the iterator nlmsg_for_each():
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+struct nlmsghdr *hdr;
+
+nlmsg_for_each(hdr, stream, length) {
+ /* do something with message */
+}
+--------
+
+.Message Payload
+
+The message payload is appended to the message header and is guranteed
+to start at a multiple of +NLMSG_ALIGNTO+. Padding at the end of the
+message header is added if necessary to ensure this. The function
+nlmsg_data() will calculate the necessary offset based on the message
+and returns a pointer to the start of the message payload.
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+void *nlmsg_data(const struct nlmsghdr *nlh);
+void *nlmsg_tail(const struct nlmsghdr *nlh);
+int nlmsg_datalen(const struct nlmsghdr *nlh);
+--------
+
+The length of the message payload is returned by nlmsg_datalen().
+
+[source,c]
+--------
+ <--- nlmsg_datalen(nlh) --->
+ +-------------------+- - -+----------------------------+- - -+
+ | struct nlmsghdr | Pad | Payload | Pad |
+ +-------------------+- - -+----------------------------+- - -+
+nlmsg_data(nlh) ---------------^ ^
+nlmsg_tail(nlh) --------------------------------------------------^
+--------
+
+The payload may consist of arbitary data but may have strict alignment
+and formatting rules depening on the actual netlink protocol.
+
+[[core_msg_attr]]
+.Message Attributes
+
+Most netlink protocols use netlink attributes. It not only makes the
+protocol self documenting but also gives flexibility in expanding the
+protocol at a later point. New attributes can be added at any time and
+older attributes can be obsoleted by newer ones without breaking
+binary compatibility of the protocol.
+
+[source,c]
+--------
+ <---------------------- payload ------------------------->
+ <----- hdrlen ----> <- nlmsg_attrlen(nlh, hdrlen) ->
+ +-------------------+- - -+----- ------------+- - -+--------------------------------+- - -+
+ | struct nlmsghdr | Pad | Protocol Header | Pad | Attributes | Pad |
+ +-------------------+- - -+-------------------+- - -+--------------------------------+- - -+
+nlmsg_attrdata(nlh, hdrlen) -----------------------------^
+--------
+
+The function nlmsg_attrdata() returns a pointer to the begin of the
+attributes section. The length of the attributes section is returned
+by the function nlmsg_attrlen().
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+struct nlattr *nlmsg_attrdata(const struct nlmsghdr *hdr, int hdrlen);
+int nlmsg_attrlen(const struct nlmsghdr *hdr, int hdrlen);
+--------
+
+See <<core_attr>> for more information on how to use netlink attributes.
+
+.Parsing a Message the Easy Way
+
+The function nlmsg_parse() validate a complete netlink message in one
+step. If +hdrlen > 0+ it will first call nlmsg_valid_hdr() to check
+if the protocol header fits into the message. If there is more payload
+to parse, it will assume it to be attributes and parse the payload
+accordingly. The function behaves exactly like nla_parse() when
+parsing attributes, see <<core_attr_parse_easy>>.
+
+[source,c]
+--------
+int nlmsg_parse(struct nlmsghdr *hdr, int hdrlen, struct nlattr **attrs,
+ int maxtype, struct nla_policy *policy);
+--------
+
+The function nlmsg_validate() is based on nla_validate() and behaves
+exactly the same as nlmsg_parse() except that it only validates and
+will not fill a array with pointers to each attribute.
+
+[source,c]
+--------
+int nlmsg_validate(struct nlmsghdr *hdr, int hdrlen, intmaxtype,
+ struct nla_policy *policy);
+--------
+
+See <<core_attr_parse_easy>> for an example and more information on
+attribute parsing.
+
+=== Construction of a Message
+
+See <<core_msg_format>> for information on the netlink message format
+and alignment requirements.
+
+Message construction is based on struct nl_msg which uses an internal
+buffer to store the actual netlink message. struct nl_msg +does not+
+point to the netlink message header. Use nlmsg_hdr() to retrieve a
+pointer to the netlink message header.
+
+At allocation time, a maximum message size is specified. It defaults
+to a page (PAGE_SIZE). The application constructing the message will
+reserve space out of this maximum message size repeatedly for each
+header or attribute added. This allows construction of messages across
+various layers of code where lower layers do not need to know about
+the space requirements of upper layers.
+
++Why is setting the maximum message size necessary?+ This
+question is often raised in combination with the proposed solution of
+reallocating the message payload buffer on the fly using realloc().
+While it is possible to reallocate the buffer during construction
+using nlmsg_expand() it will make all pointers into the message buffer
+become stale. This breaks usage of nlmsg_hdr(), nla_nest_start(), and
+nla_nest_end() and is therefore not acceptable as default behaviour.
+
+.Allocating struct nl_msg
+
+The first step in constructing a new netlink message it to allocate a
+`struct nl_msg` to hold the message header and payload. Several
+functions exist to simplify various tasks.
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+struct nl_msg *nlmsg_alloc(void);
+void nlmsg_free(struct nl_msg *msg);
+--------
+
+The function nlmsg_alloc() is the default message allocation function.
+It allocates a new message using the default maximum message size which
+equals to one page (PAGE_SIZE). The application can change the default
+size for messages by calling nlmsg_set_default_size():
+
+[source,c]
+--------
+void nlmsg_set_default_size(size_t);
+--------
+
+CAUTION: Calling nlmsg_set_default_size() does not change the maximum
+ message size of already allocated messages.
+
+[source,c]
+--------
+struct nl_msg *nlmsg_alloc_size(size_t max);
+--------
+
+Instead of changing the default message size, the function
+nlmsg_alloc_size() can be used to allocate a message with a individual
+maximum message size.
+
+
+If the netlink message header is already known at allocation time, the
+application may sue nlmsg_inherit(). It will allocate a message using
+the default maximum message size and copy the header into the message.
+Calling nlmsg_inherit with +set+ to NULL is equivalent to calling
+nlmsg_alloc().
+
+[source,c]
+--------
+struct nl_msg *nlmsg_inherit(struct nlmsghdr *hdr);
+--------
+
+Alternatively nlmsg_alloc_simple() takes a netlink message type and
+netlink message flags. It is equivalent to nlmsg_inherit() except that it
+takes the two common header fields as arguments instead of a complete
+header.
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+struct nl_msg *nlmsg_alloc_simple(int nlmsg_type, int flags);
+--------
+
+.Appending the netlink message header
+
+After allocating struct nl_msg, the netlink message header needs to be
+added unless one of the function nlmsg_alloc_simple() or nlmsg_inherit()
+have been used for allocation in which case this step will replace the
+netlink message header already in place.
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+struct nlmsghdr *nlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seqnr,
+ int nlmsg_type, int payload, int nlmsg_flags);
+--------
+
+The function nlmsg_put() will build a netlink message header out of
++nlmsg_type+, +nlmsg_flags+, +seqnr+, and +port+ and copy it into the
+netlink message. +seqnr+ can be set to +NL_AUTO_SEQ+ to indiciate
+that the next possible sequence number should be used automatically.
+To use this feature, the message must be sent using the function
+nl_send_auto(). Like +port+, the argument +seqnr+ can be set to
++NL_AUTO_PORT+ indicating that the local port assigned to the socket
+should be used as source port. This is generally a good idea unless
+you are replying to a request. See <<core_netlink_fundamentals>>
+for more information on how to fill the header.
+
+NOTE: The argument +payload+ can be used by the application to reserve
+ room for additional data after the header. A value of > 0 is
+ equivalent to calling +nlmsg_reserve(msg, payload, NLMSG_ALIGNTO)+.
+ See <<core_msg_reserve>> for more information on reserving room for
+ data.
+
+.Example
+[source,c]
+--------
+#include <netlink/msg.h>
+
+struct nlmsghdr *hdr;
+struct nl_msg *msg;
+struct myhdr {
+ uint32_t foo1, foo2;
+} hdr = { 10, 20 };
+
+/* Allocate a message with the default maximum message size */
+msg = nlmsg_alloc();
+
+/*
+ * Add header with message type MY_MSGTYPE, the flag NLM_F_CREATE,
+ * let library fill port and sequence number, and reserve room for
+ * struct myhdr
+ */
+hdr = nlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, MY_MSGTYPE, sizeof(hdr), NLM_F_CREATE);
+
+/* Copy own header into newly reserved payload section */
+memcpy(nlmsg_data(hdr), &hdr, sizeof(hdr));
+
+/*
+ * The message will now look like this:
+ * +-------------------+- - -+----------------+- - -+
+ * | struct nlmsghdr | Pad | struct myhdr | Pad |
+ * +-------------------+-----+----------------+- - -+
+ * nlh -^ / \
+ * +--------+---------+
+ * | foo1 | foo2 |
+ * +--------+---------+
+ */
+--------
+
+[[core_msg_reserve]]
+.Reserving room at the end of the message
+
+Most functions described later on will automatically take care of
+reserving room for the data that is added to the end of the netlink
+message. In some situations it may be requried for the application
+to reserve room directly though.
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+void *nlmsg_reserve(struct nl_msg *msg, size_t len, int pad);
+--------
+
+The function nlmsg_reserve() reserves +len+ bytes at the end of the
+netlink message and returns a pointer to the start of the reserved area.
+The +pad+ argument can be used to request +len+ to be aligned to any
+number of bytes prior to reservation.
+
+The following example requests to reserve a 17 bytes area at the end of
+message aligned to 4 bytes. Therefore a total of 20 bytes will be
+reserved.
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+void *buf = nlmsg_reserve(msg, 17, 4);
+--------
+
+NOTE: `nlmsg_reserve()` will *not* align the start of the buffer. Any
+ alignment requirements must be provided by the owner of the
+ previous message section.
+
+.Appending data at the end of the message
+
+The function `nlmsg_append()` appends `len` bytes at the end of the
+message, padding it if requested and necessary.
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+int nlmsg_append(struct nl_msg *msg, void *data, size_t len, int pad);
+--------
+
+It is equivalent to calling `nlmsg_reserve()` and `memcpy()`ing the
+data into the freshly reserved data section.
+
+NOTE: `nlmsg_append()` will *not* align the start of the data. Any
+ alignment requirements must be provided by the owner of the
+ previous message section.
+
+.Adding attribtues to a message
+
+Construction of attributes and addition of attribtues to the message is
+covereted in section <<core_attr>>.
+
+[[core_attr]]
+== Attributes
+
+Any form of payload should be encoded as netlink attributes whenever
+possible. Use of attributes allows to extend any netlink protocol in
+the future without breaking binary compatibility. F.e. Suppose your
+device may currently be using 32 bit counters for statistics but years
+later the device switches to maintaining 64 bit counters to account
+for faster network hardware. If your protocol is using attributes the
+move to 64 bit counters is trivial and only involves in sending an
+additional attribute containing the 64 bit variants while still
+providing the old legacy 32 bit counters. If your protocol is not using
+attributes you will not be able to switch data types without breaking
+all existing users of the protocol.
+
+The concept of nested attributes also allows for subsystems of your
+protocol to implement and maintain their own attribute schemas. Suppose
+a new generation of network device is introduced which requires a
+completely new set of configuration settings which was unthinkable when
+the netlink protocol was initially designed. Using attributes the new
+generation of devices may define a new attribute and fill it with its
+own new structure of attributes which extend or even obsolete the old
+attributes.
+
+Therefore, _always_ use attributes even if you are almost certain that
+the message format will never ever change in the future.
+
+[[core_attr_format]]
+=== Attribute Format
+
+Netlink attributes allow for any number of data chunks of arbitary
+length to be attached to a netlink message. See <<core_msg_attr>>
+for more information on where attributes are stored in the message.
+
+The format of the attributes data returned by nlmsg_attrdata() is as
+follows:
+
+[source,c]
+--------
+ <----------- nla_total_size(payload) ----------->
+ <---------- nla_size(payload) ----------->
+ +-----------------+- - -+- - - - - - - - - +- - -+-----------------+- - -
+ | struct nlattr | Pad | Payload | Pad | struct nlattr |
+ +-----------------+- - -+- - - - - - - - - +- - -+-----------------+- - -
+ <---- NLA_HDRLEN -----> <--- NLA_ALIGN(len) ---> <---- NLA_HDRLEN ---
+--------
+
+Every attribute must start at an offset which is a multiple of
++NLA_ALIGNTO+ (4 bytes). If you need to know whether an attribute needs
+to be padded at the end, the function nla_padlen() returns the number
+of padding bytes that will or need to be added.
+
+image:attribute_hdr.png["Netlink Attribute Header"]
+
+Every attribute is encoded with a type and length field, both 16 bits,
+stored in the attribute header (struct nlattr) preceding the attribute
+payload. The length of an attribute is used to calculate the offset to
+the next attribute.
+
+[[core_attr_parse]]
+=== Parsing Attributes
+
+[[core_attr_parse_split]]
+.Splitting an Attributes Stream into Attributes
+
+Although most applications will use one of the functions from the
+nlmsg_parse() family (See <<core_attr_parse_easy>>) an interface exists
+to split the attributes stream manually.
+
+As described in <<core_attr_format>> the attributes section contains a
+infinite sequence or stream of attributes. The pointer returned by
+nlmsg_attrdata() (See <<core_msg_attr>>) points to the first attribute
+header. Any subsequent attribute is accessed with the function nla_next()
+based on the previous header.
+
+[source,c]
+--------
+#include <netlink/attr.h>
+
+struct nlattr *nla_next(const struct nlattr *attr, int *remaining);
+--------
+
+The semantics are equivalent to nlmsg_next() and thus nla_next() will also
+subtract the size of the previous attribute from the remaining number of
+bytes in the attributes stream.
+
+Like messages, attributes do not contain an indicator whether another
+attribute follows or not. The only indication is the number of bytes left
+in the attribute stream. The function nla_ok() exists to determine whether
+another attribute fits into the remaining number of bytes or not.
+
+[source,c]
+--------
+#include <netlink/attr.h>
+
+int nla_ok(const struct nlattr *attr, int remaining);
+--------
+
+A typical use of nla_ok() and nla_next() looks like this:
+
+.nla_ok()/nla_next() usage
+[source,c]
+--------
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+struct nlattr *hdr = nlmsg_attrdata(msg, 0);
+int remaining = nlmsg_attrlen(msg, 0);
+
+while (nla_ok(hdr, remaining)) {
+ /* parse attribute here */
+ hdr = nla_next(hdr, &remaining);
+};
+--------
+
+NOTE: `nla_ok()` only returns true if the *complete* attributes
+ including the attribute payload fits into the remaining number
+ of bytes.
+
+.Accessing Attribute Header and Payload
+
+Once the individual attributes have been sorted out by either splitting
+the attributes stream or using another interface the attribute header
+and payload can be accessed.
+
+[source,c]
+--------
+ <- nla_len(hdr) ->
+ +-----------------+- - -+- - - - - - - - - +- - -+
+ | struct nlattr | Pad | Payload | Pad |
+ +-----------------+- - -+- - - - - - - - - +- - -+
+nla_data(hdr) ---------------^
+--------
+
+The functions nla_len() and nla_type() can be used to access the attribute
+header. nla_len() will return the length of the payload not including
+eventual padding bytes. nla_type returns the attribute type.
+
+[source,c]
+--------
+#include <netlink/attr.h>
+
+int nla_len(const struct nlattr *hdr);
+int nla_type(const struct nlattr *hdr);
+--------
+
+The function nla_data() will return a pointer to the attribute
+payload. Please note that due to +NLA_ALIGNTO+ being 4 bytes it may
+not be safe to cast and dereference the pointer for any datatype
+larger than 32 bit depending on the architecture the application is
+run on.
+
+[source,c]
+--------
+#include <netlink/attr.h>
+
+void *nla_data(const struct nlattr *hdr);
+--------
+
+[NOTE]
+Never rely on the size of a payload being what you expect it to be.
+_Always_ verify the payload size and make sure that it matches your
+expectations. See <<core_attr_validation>>
+
+[[core_attr_validation]]
+.Attribute Validation
+
+When receiving netlink attributes, the receiver has certain expections
+on how the attributes should look like. These expectations must be
+defined to make sure the sending side meets our expecations. For this
+purpose, a attribute validation interface exists which must be used
+prior to accessing any payload.
+
+All functions providing attribute validation functionality are based
+on struct nla_policy:
+
+[source,c]
+--------
+struct nla_policy {
+ uint16_t type;
+ uint16_t minlen;
+ uint16_t maxlen;
+};
+--------
+
+The +type+ member specifies the datatype of the attribute, e.g.
++NLA_U32+, +NLA_STRING+, +NLA_FLAG+. The default is +NLA_UNSPEC+. The
++minlen+ member defines the minmum payload length of an attribute to
+be considered a valid attribute. The value for +minlen+ is implicit
+for most basic datatypes such as integers or flags. The +maxlen+
+member can be used to define a maximum payload length for an
+attribute to still be considered valid.
+
+NOTE: Specyfing a maximum payload length is not recommended when
+ encoding structures in an attribute as it will prevent any
+ extension of the structure in the future. Something that is
+ frequently done in netlink protocols and does not break
+ backwards compatibility.
+
+One of the functions which use struct nla_policy is nla_validate().
+The function expects an array of struct nla_policy and will access the
+array using the attribute type as index. If an attribute type is out
+of bounds the attribute is assumed to be valid. This is intentional
+behaviour to allow older applications not yet aware of recently
+introduced attributes to continue functioning.
+
+[source,c]
+--------
+#include <netlink/attr.h>
+
+int nla_validate(struct nlattr *head, int len, int maxtype, struct nla_policy *policy);
+--------
+
+The function nla_validate() returns 0 if all attributes are valid,
+otherwise a validation failure specific error code is returned.
+
+Most applications will rarely use nla_validate() directly but use
+nla_parse() instead which takes care of validation in the same way but
+also parses the the attributes in the same step. See
+<<core_attr_parse_easy>> for an example and more information.
+
+The validation process in detail:
+
+. If attribute type is 0 or exceeds +maxtype+ attribute is
+ considered valid, 0 is returned.
+. If payload length is < +minlen+, +-NLE_ERANGE+ is returned.
+. If +maxlen+ is defined and payload exceeds it, +-NLE_ERANGE+
+ is returned.
+. Datatype specific requirements rules, see <<core_attr_types>>
+. If all is ok, 0 is returned.
+
+[[core_attr_parse_easy]]
+.Parsing Attributes the Easy Way
+
+Most applications will not want to deal with splitting attribute
+streams themselves as described in <<core_attr_parse_split>>
+A much easier method is to use nla_parse().
+
+[source,c]
+--------
+#include <netlink/attr.h>
+
+int nla_parse(struct nlattr **attrs, int maxtype, struct nlattr *head,
+ int len, struct nla_policy *policy);
+--------
+
+The function nla_parse() will iterate over a stream of attributes,
+validate each attribute as described in <<core_attr_validation>>
+If the validation of all attributes succeeds, a pointer to each attribute
+is stored in the +attrs+ array at `attrs[nla_type(attr)]`.
+
+As an alernative to nla_parse() the function nlmsg_parse() can be used
+to parse the message and its attributes in one step. See
+<<core_attr_parse_easy>> for information on how to use these functions.
+
+.Example:
+
+The following example demonstrates how to parse a netlink message sent
+over a netlink protocol which does not use protocol headers. The example
+does enforce a attribute policy however, the attribute MY_ATTR_FOO must
+be a 32 bit integer, and the attribute MY_ATTR_BAR must be a string with
+a maximum length of 16 characters.
+
+[source,c]
+---------
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+enum {
+ MY_ATTR_FOO = 1,
+ MY_ATTR_BAR,
+ __MY_ATTR_MAX,
+};
+
+#define MY_ATTR_MAX (__MY_ATTR_MAX - 1)
+
+static struct nla_policy my_policy[MY_ATTR_MAX+1] = {
+ [MY_ATTR_FOO] = { .type = NLA_U32 },
+ [MY_ATTR_BAR] = { .type = NLA_STRING,
+ .maxlen = 16 },
+};
+
+void parse_msg(struct nlmsghdr *nlh)
+{
+ struct nlattr *attrs[MY_ATTR_MAX+1];
+
+ if (nlmsg_parse(nlh, 0, attrs, MY_ATTR_MAX, my_policy) < 0)
+ /* error */
+
+ if (attrs[MY_ATTR_FOO]) {
+ /* MY_ATTR_FOO is present in message */
+ printf("value: %u\n", nla_get_u32(attrs[MY_ATTR_FOO]));
+ }
+}
+---------
+
+.Locating a Single Attribute
+
+An application only interested in a single attribute can use one of the
+functions nla_find() or nlmsg_find_attr(). These function will iterate
+over all attributes, search for a matching attribute and return a pointer
+to the corresponding attribute header.
+
+[source,c]
+--------
+#include <netlink/attr.h>
+
+struct nlattr *nla_find(struct nlattr *head, int len, int attrtype);
+--------
+
+[source,c]
+--------
+#include <netlink/msg.h>
+
+struct nlattr *nlmsg_find_attr(struct nlmsghdr *hdr, int hdrlen, int attrtype);
+--------
+
+NOTE: `nla_find()` and `nlmsg_find_attr()` will *not* search in nested
+ attributes recursively, see <<core_attr_nested>>.
+
+==== Iterating over a Stream of Attributes
+
+In some situations it does not make sense to assign a unique attribute
+type to each attribute in the attribute stream. For example a list may
+be transferd using a stream of attributes and even if the attribute type
+is incremented for each attribute it may not make sense to use the
+nlmsg_parse() or nla_parse() function to fill an array.
+
+Therefore methods exist to iterate over a stream of attributes:
+
+[source,c]
+--------
+#include <netlink/attr.h>
+
+nla_for_each_attr(attr, head, len, remaining)
+--------
+
+nla_for_each_attr() is a macro which can be used in front of a code
+block:
+
+[source,c]
+--------
+#include <netlink/attr.h>
+
+struct nalttr *nla;
+int rem;
+
+nla_for_each_attr(nla, attrstream, streamlen, rem) {
+ /* validate & parse attribute */
+}
+
+if (rem > 0)
+ /* unparsed attribute data */
+--------
+
+[[core_attr_constr]]
+=== Attribute Construction
+
+The interface to add attributes to a netlink message is based on the
+regular message construction interface. It assumes that the message
+header and an eventual protocol header has been added to the message
+already.
+
+[source,c]
+--------
+struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int len);
+--------
+
+The function nla_reserve() adds an attribute header at the end of the
+message and reserves room for +len+ bytes of payload. The function
+returns a pointer to the attribute payload section inside the message.
+Padding is added at the end of the attribute to ensure the next
+attribute is properly aligned.
+
+[source,c]
+--------
+int nla_put(struct nl_msg *msg, int attrtype, int attrlen, const void *data);
+--------
+
+The function nla_put() is base don nla_reserve() but takes an additional
+pointer +data+ pointing to a buffer containing the attribute payload.
+It will copy the buffer into the message automatically.
+
+.Example:
+
+[source,c]
+--------
+struct my_attr_struct {
+ uint32_t a;
+ uint32_t b;
+};
+
+int my_put(struct nl_msg *msg)
+{
+ struct my_attr_struct obj = {
+ .a = 10,
+ .b = 20,
+ };
+
+ return nla_put(msg, ATTR_MY_STRUCT, sizeof(obj), &obj);
+}
+--------
+
+See <<core_attr_types>> for datatype specific attribute construction
+functions.
+
+.Exception Based Attribute Construction
+
+Like in the kernel API an exception based construction interface is
+provided. The behaviour of the macros is identical to their regular
+function counterparts except that in case of an error, the target
+`nla_put_failure` is jumped.
+
+.Example:
+[source,c]
+--------
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+void construct_attrs(struct nl_msg *msg)
+{
+ NLA_PUT_STRING(msg, MY_ATTR_FOO1, "some text");
+ NLA_PUT_U32(msg, MY_ATTR_FOO1, 0x1010);
+ NLA_PUT_FLAG(msg, MY_ATTR_FOO3, 1);
+
+ return 0;
+
+nla_put_failure:
+ /* NLA_PUT* macros jump here in case of an error */
+ return -EMSGSIZE;
+}
+--------
+
+See <<core_attr_types>> for more information on the datatype specific
+exception based variants.
+
+[[core_attr_types]]
+=== Attribute Data Types
+
+A number of basic data types have been defined to simplify access and
+validation of attributes. The datatype is not encoded in the
+attribute, therefore bthe sender and receiver are required to use the
+same definition on what attribute is of what type.
+
+[options="header", cols="1m,5"]
+|================================================
+| Type | Description
+| NLA_UNSPEC | Unspecified attribute
+| NLA_U{8\|16\|32} | Integers
+| NLA_STRING | String
+| NLA_FLAG | Flag
+| NLA_NESTED | Nested attribute
+|================================================
+
+Besides simplified access to the payload of such datatypes, the major
+advantage is the automatic validation of each attribute based on a
+policy. The validation ensures safe access to the payload by checking
+for minimal payload size and can also be used to enforce maximum
+payload size for some datatypes.
+
+==== Integer Attributes
+
+The most frequently used datatypes are integers. Integers come in four
+different sizes:
+[horizontal]
+NLA_U8:: 8bit integer
+NLA_U16:: 16bit integer
+NLA_U32:: 32bit integer
+NLA_U64:: 64bit integer
+
+Note that due to the alignment requirements of attributes the integer
+attribtue +NLA_u8+ and +NLA_U16+ will not result in space savings in
+the netlink message. Their use is intended to limit the range of
+values.
+
+.Parsing Integer Attributes
+
+[source,c]
+--------
+#include <netlink/attr.h>
+
+uint8_t nla_get_u8(struct nlattr *hdr);
+uint16_t nla_get_u16(struct nlattr *hdr);
+uint32_t nla_get_u32(struct nlattr *hdr);
+uint64_t nla_get_u64(struct nlattr *hdr);
+--------
+
+Example:
+
+[source,c]
+--------
+if (attrs[MY_ATTR_FOO])
+ uint32_t val = nla_get_u32(attrs[MY_ATTR_FOO]);
+--------
+
+.Constructing Integer Attributes
+
+[source,c]
+--------
+#include <netlink/attr.h>
+
+int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value);
+int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value);
+int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value);
+int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value);
+--------
+
+Exception based:
+
+[source,c]
+--------
+NLA_PUT_U8(msg, attrtype, value)
+NLA_PUT_U16(msg, attrtype, value)
+NLA_PUT_U32(msg, attrtype, value)
+NLA_PUT_U64(msg, attrtype, value)
+--------
+
+.Validation
+
+Use +NLA_U8+, +NLA_U16+, +NLA_U32+, or +NLA_U64+ to define the type of
+integer when filling out a struct nla_policy array. It will
+automatically enforce the correct minimum payload length policy.
+
+Validation does not differ between signed and unsigned integers, only
+the size matters. If the appliaction wishes to enforce particular value
+ranges it must do so itself.
+
+[source,c]
+--------
+static struct nla_policy my_policy[ATTR_MAX+1] = {
+ [ATTR_FOO] = { .type = NLA_U32 },
+ [ATTR_BAR] = { .type = NLA_U8 },
+};
+--------
+
+The above is equivalent to:
+[source,c]
+--------
+static struct nla_policy my_policy[ATTR_MAX+1] = {
+ [ATTR_FOO] = { .minlen = sizeof(uint32_t) },
+ [ATTR_BAR] = { .minlen = sizeof(uint8_t) },
+};
+--------
+
+==== String Attributes
+
+The string datatype represents a NUL termianted character string of
+variable length. It is not intended for binary data streams.
+
+The payload of string attributes can be accessed with the function
+nla_get_string(). nla_strdup() calls strdup() on the payload and returns
+the newly allocated string.
+
+[source,c]
+--------
+#include <netlink/attr.h>
+
+char *nla_get_string(struct nlattr *hdr);
+char *nla_strdup(struct nlattr *hdr);
+--------
+
+String attributes are constructed with the function +nla_put_string()+
+respectively +NLA_PUT_STRING()+. The length of the payload will be
+strlen()+1, the trailing NUL byte is included.
+
+[source,c]
+--------
+int nla_put_string(struct nl_msg *msg, int attrtype, const char *data);
+
+NLA_PUT_STRING(msg, attrtype, data)
+--------
+
+For validation purposes the type +NLA_STRING+ can be used in
++struct nla_policy+ definitions. It implies a minimum payload length
+of 1 byte and checks for a trailing NUL byte. Optionally the +maxlen+
+member defines the maximum length of a character string (including the
+trailing NUL byte).
+
+[source,c]
+--------
+static struct nla_policy my_policy[] = {
+ [ATTR_FOO] = { .type = NLA_STRING,
+ .maxlen = IFNAMSIZ },
+};
+--------
+
+==== Flag Attributes
+
+The flag attribute represents a boolean datatype. The presence of the
+attribute implies a value of +true+, the absence of the attribute
+implies the value +false+. Therefore the payload length of flag
+attributes is always 0.
+
+[source,c]
+--------
+int nla_get_flag(struct nlattr *hdr);
+int nla_put_flag(struct nl_msg *msg, int attrtype);
+--------
+
+The type +NLA_FLAG+ is used for validation purposes. It implies a
++maxlen+ value of 0 and thus enforces a maximum payload length of 0.
+
+.Example:
+[source,c]
+--------
+/* nla_put_flag() appends a zero sized attribute to the message. */
+nla_put_flag(msg, ATTR_FLAG);
+
+/* There is no need for a receival function, the presence is the value. */
+if (attrs[ATTR_FLAG])
+ /* flag is present */
+--------
+
+[[core_attr_nested]]
+==== Nested Attributes
+
+As described in <<core_attr>>, attributes can be nested allowing for
+complex tree structures of attributes. It is commonly used to delegate
+the responsibility of a subsection of the message to a subsystem.
+Nested attributes are also commonly used for transmitting list of objects.
+
+When nesting attributes, the nested attributes are included as payload
+of a container attribute.
+
+NOTE: When validating the attributes using nlmsg_validate(),
+ nlmsg_parse(), nla_validate(), or nla_parse() only the
+ attributes on the first level are being validated. None of these
+ functions will validate attributes recursively. Therefore you
+ must explicitely call nla_validate() or use nla_parse_nested()
+ for each level of nested attributes.
+
+The type +NLA_NESTED+ should be used when defining nested attributes
+in a struct nla_policy definition. It will not enforce any minimum
+payload length unless +minlen+ is specified explicitely. This is
+because some netlink protocols implicitely allow empty container
+attributes.
+
+[source,c]
+--------
+static struct nla_policy my_policy[] = {
+ [ATTR_OPTS] = { .type = NLA_NESTED },
+};
+--------
+
+.Parsing of Nested Attributes
+
+The function nla_parse_nested() is used to parse nested attributes.
+Its behaviour is identical to nla_parse() except that it takes a
+struct nlattr as argument and will use the payload as stream of
+attributes.
+
+[source,c]
+--------
+if (attrs[ATTR_OPTS]) {
+ struct nlattr *nested[NESTED_MAX+1];
+ struct nla_policy nested_policy[] = {
+ [NESTED_FOO] = { .type = NLA_U32 },
+ };
+
+ if (nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_OPTS], nested_policy) < 0)
+ /* error */
+
+ if (nested[NESTED_FOO])
+ uint32_t val = nla_get_u32(nested[NESTED_FOO]);
+}
+--------
+
+.Construction of Nested Attributes
+
+Attributes are nested by surrounding them with calls to nla_nest_start()
+and nla_nest_end(). nla_nest_start() will add a attribute header to
+the message but no actual payload. All data added to the message from
+this point on will be part of the container attribute until nla_nest_end()
+is called which "closes" the attribute, correcting its payload length to
+include all data length.
+
+[source,c]
+--------
+int put_opts(struct nl_msg *msg)
+{
+ struct nlattr *opts;
+
+ if (!(opts = nla_nest_start(msg, ATTR_OPTS)))
+ goto nla_put_failure;
+
+ NLA_PUT_U32(msg, NESTED_FOO, 123);
+ NLA_PUT_STRING(msg, NESTED_BAR, "some text");
+
+ nla_nest_end(msg, opts);
+ return 0;
+
+nla_put_failure:
+ nla_nest_cancel(msg, opts);
+ return -EMSGSIZE;
+}
+--------
+
+==== Unspecified Attribute
+
+This is the default attribute type and used when none of the basic
+datatypes is suitable. It represents data of arbitary type and length.
+
+See <<core_addr_alloc, Address Allocation>> for a more information on
+a special interface allowing the allocation of abstract address object
+based on netlink attributes which carry some form of network address.
+
+See <<core_data_alloc, Abstract Data Allocation>> for more information
+on how to allocate abstract data objects based on netlink attributes.
+
+Use the function nla_get() and nla_put() to access the payload and
+construct attributes. See <<core_attr_constr, Attribute Construction>>
+for an example.
+
+=== Examples
+
+==== Constructing a Netlink Message with Attributes
+
+[source,c]
+--------
+struct nl_msg *build_msg(int ifindex, struct nl_addr *lladdr, int mtu)
+{
+ struct nl_msg *msg;
+ struct nlattr *info, *vlan;
+ struct ifinfomsg ifi = {
+ .ifi_family = AF_INET,
+ .ifi_index = ifindex,
+ };
+
+ /* Allocate a default sized netlink message */
+ if (!(msg = nlmsg_alloc_simple(RTM_SETLINK, 0)))
+ return NULL;
+
+ /* Append the protocol specific header (struct ifinfomsg)*/
+ if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
+ goto nla_put_failure
+
+ /* Append a 32 bit integer attribute to carry the MTU */
+ NLA_PUT_U32(msg, IFLA_MTU, mtu);
+
+ /* Append a unspecific attribute to carry the link layer address */
+ NLA_PUT_ADDR(msg, IFLA_ADDRESS, lladdr);
+
+ /* Append a container for nested attributes to carry link information */
+ if (!(info = nla_nest_start(msg, IFLA_LINKINFO)))
+ goto nla_put_failure;
+
+ /* Put a string attribute into the container */
+ NLA_PUT_STRING(msg, IFLA_INFO_KIND, "vlan");
+
+ /*
+ * Append another container inside the open container to carry
+ * vlan specific attributes
+ */
+ if (!(vlan = nla_nest_start(msg, IFLA_INFO_DATA)))
+ goto nla_put_failure;
+
+ /* add vlan specific info attributes here... */
+
+ /* Finish nesting the vlan attributes and close the second container. */
+ nla_nest_end(msg, vlan);
+
+ /* Finish nesting the link info attribute and close the first container. */
+ nla_nest_end(msg, info);
+
+ return msg;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return NULL;
+}
+------
+
+==== Parsing a Netlink Message with Attributes
+
+[source,c]
+--------
+int parse_message(struct nlmsghdr *hdr)
+{
+ /*
+ * The policy defines two attributes: a 32 bit integer and a container
+ * for nested attributes.
+ */
+ struct nla_policy attr_policy[] = {
+ [ATTR_FOO] = { .type = NLA_U32 },
+ [ATTR_BAR] = { .type = NLA_NESTED },
+ };
+ struct nlattr *attrs[ATTR_MAX+1];
+ int err;
+
+ /*
+ * The nlmsg_parse() function will make sure that the message contains
+ * enough payload to hold the header (struct my_hdr), validates any
+ * attributes attached to the messages and stores a pointer to each
+ * attribute in the attrs[] array accessable by attribute type.
+ */
+ if ((err = nlmsg_parse(hdr, sizeof(struct my_hdr), attrs, ATTR_MAX,
+ attr_policy)) < 0)
+ goto errout;
+
+ if (attrs[ATTR_FOO]) {
+ /*
+ * It is safe to directly access the attribute payload without
+ * any further checks since nlmsg_parse() enforced the policy.
+ */
+ uint32_t foo = nla_get_u32(attrs[ATTR_FOO]);
+ }
+
+ if (attrs[ATTR_BAR]) {
+ struct *nested[NESTED_MAX+1];
+
+ /*
+ * Attributes nested in a container can be parsed the same way
+ * as top level attributes.
+ */
+ err = nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_BAR],
+ nested_policy);
+ if (err < 0)
+ goto errout;
+
+ // Process nested attributes here.
+ }
+
+ err = 0;
+errout:
+ return err;
+}
+--------
+
+[[core_cb]]
+== Callback Configurations
+
+Callback hooks and overwriting capabilities are provided in various places
+inside library to control the behaviour of several functions. All the
+callback and overwrite functions are packed together in struct nl_cb which
+is attached to a netlink socket or passed on to functions directly.
+
+=== Callback Hooks
+
+Callback hooks are spread across the library to provide entry points for
+message processing and to take action upon certain events.
+
+Callback functions may return the following return codes:
+[options="header", cols="1m,4"]
+|========================================================================
+| Return Code | Description
+| NL_OK | Proceed.
+| NL_SKIP | Skip message currently being processed and continue
+ parsing the receive buffer.
+| NL_STOP | Stop parsing and discard all remaining data in the
+ receive buffer.
+|========================================================================
+
+.Default Callback Implementations
+
+The library provides three sets of default callback implementations:
+* +NL_CB_DEFAULT+ This is the default set. It implets the default behaviour.
+ See the table below for more information on the return codes of each
+ function.
+* +NL_CB_VERBOSE+ This set is based on the default set but will cause an
+ error message to be printed to stderr for error messages, invalid
+ messages, message overruns and unhandled valid messages. The
+ +arg+ pointer in nl_cb_set() and nl_cb_err() can be used to
+ provide a FILE * which overwrites stderr.
+* +NL_CB_DEBUG+ This set is intended for debugging purposes. It is
+ based on the verbose set but will decode and dump each message sent
+ or received to the console.
+
+.Message Processing Callbacks
+
+.nl_sendmsg() callback hooks:
+[cols="2m,4e,1m", options="header"]
+|============================================================================
+| Callback ID | Description | Default Return Value
+| NL_CB_MSG_OUT | Each message sent | NL_OK
+|============================================================================
+
+Any function called by NL_CB_MSG_OUT may return a negative error code to
+prevent the message from being sent and the error code being returned.
+
+nl_recvmsgs() callback hooks (ordered by priority):
+[cols="2m,4e,1m", options="header"]
+|============================================================================
+| Callback ID | Description | Default Return Value
+| NL_CB_MSG_IN | Each message received | NL_OK
+| NL_CB_SEQ_CHECK | May overwrite sequence check algo | NL_OK
+| NL_CB_INVALID | Invalid messages | NL_STOP
+| NL_CB_SEND_ACK | Messages with NLM_F_ACK flag set | NL_OK
+| NL_CB_FINISH | Messages of type NLMSG_DONE | NL_STOP
+| NL_CB_SKIPPED | Messages of type NLMSG_NOOP | NL_SKIP
+| NL_CB_OVERRUN | Messages of type NLMSG_OVERRUN | NL_STOP
+| NL_CB_ACK | ACK Messages | NL_STOP
+| NL_CB_VALID | Each valid message | NL_OK
+|============================================================================
+
+Any of these functions may return NL_OK, NL_SKIP, or NL_STOP.
+
+Message processing callback functions are set with nl_cb_set():
+[source,c]
+--------
+#include <netlink/handlers.h>
+
+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);
+
+typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg);
+--------
+
+.Callback for Error Messages
+
+A special function prototype is used for the error message callback hook:
+
+[source,c]
+--------
+#include <netlink/handlers.h>
+
+int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, nl_recvmsg_err_cb_t func, void *arg);
+
+typedef int(* nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg);
+--------
+
+.Example: Setting up a callback set
+[source,c]
+--------
+#include <netlink/handlers.h>
+
+/* 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);
+--------
+
+=== Overwriting of Internal Functions
+
+When the library needs to send or receive netlink messages in high level
+interfaces it does so by calling its own low level API. In the case the
+default characteristics are not sufficient for the application, it may
+overwrite several internal function calls with own implementations.
+
+.Overwriting recvmsgs()
+
+See <<core_recv, Receiving Netlink Messages>> for more information on
+how and when recvmsgs() is called internally.
+
+[source,c]
+--------
+#include <netlink/handlers.h>
+
+void nl_cb_overwrite_recvmsgs(struct nl_cb *cb,
+ int (*func)(struct nl_sock *sk, struct nl_cb *cb));
+--------
+
+The following criteras must be met if a recvmsgs() implementation is
+supposed to work with high level interfaces:
+
+- MUST respect the callback configuration +cb+, therefore:
+ - MUST call +NL_CB_VALID+ for all valid messages, passing on
+ - MUST call +NL_CB_ACK+ for all ACK messages
+ - MUST correctly handle multipart messages, calling NL_CB_VALID for
+ each message until a NLMSG_DONE message is received.
+- MUST report error code if a NLMSG_ERROR or NLMSG_OVERRUN mesasge is
+ received.
+
+.Overwriting nl_recv()
+
+Often it is sufficient to overwrite `nl_recv()` which is responsible
+from receiving the actual data from the socket instead of replacing
+the complete `recvmsgs()` logic.
+
+See <<core_recv_character, Receive Characteristics>> for more
+information on how and when `nl_recv()` is called internally.
+
+[source,c]
+--------
+#include <netlink/handlers.h>
+
+void nl_cb_overwrite_recv(struct nl_cb *cb,
+ int (*func)(struct nl_sock * sk,
+ struct sockaddr_nl *addr,
+ unsigned char **buf,
+ struct ucred **cred));
+--------
+
+The following criteras must be met for an own `nl_recv()`
+implementation:
+
+ - *MUST* return the number of bytes read or a negative error code if
+ an error occured. The function may also return 0 to indicate that
+ no data has been read.
+ - *MUST* set `*buf` to a buffer containing the data read. It must be
+ safe for the caller to access the number of bytes read returned as
+ return code.
+ - *MAY* fill out `*addr` with the netlink address of the peer the
+ data has been received from.
+ - *MAY* set `*cred` to a newly allocated struct ucred containg
+ credentials.
+
+.Overwriting nl_send()
+
+See <<core_send, Sending Netlink Messages>> for more information on
+how and when nl_send() is called internally.
+
+[source,c]
+--------
+#include <netlink/handlers.h>
+
+void nl_cb_overwrite_send(struct nl_cb *cb, int (*func)(struct nl_sock *sk,
+ struct nl_msg *msg));
+--------
+
+Own implementations must send the netlink message and return 0 on success
+or a negative error code.
+
+[[core_cache]]
+== Cache System
+
+=== Allocation of Caches
+
+Almost all subsystem provide a function to allocate a new cache
+of some form. The function usually looks like this:
+[source,c]
+--------
+struct nl_cache *<object name>_alloc_cache(struct nl_sock *sk);
+--------
+
+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.
+
+=== Cache Manager
+
+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.
+
+
+----
+ 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)--------------+
+ | +------------+|
+ | | +------------+| |
+ <-------|---| ... ||
+ | | +------------+| |
+ +-----------------+
+ | |
+----
+
+.Creating a new cache manager
+
+[source,c]
+----
+struct nl_cache_mngr *mngr;
+
+// Allocate a new cache manager for RTNETLINK and automatically
+// provide the caches added to the manager.
+err = nl_cache_mngr_alloc(NULL, NETLINK_ROUTE, NL_AUTO_PROVIDE, &mngr);
+----
+
+.Keep track of a cache
+
+[source,c]
+----
+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");
+-----
+
+.Make the manager receive updates
+
+[source,c]
+----
+// 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, ...);
+}
+----
+
+.Release cache manager
+
+[source,c]
+----
+nl_cache_mngr_free(mngr);
+----
+
+== Abstract Data Types
+
+A few high level abstract data types which are used by a majority netlink
+protocols are implemented in the core library. More may be added in the
+future if the need arises.
+
+=== Abstract Address
+
+Most netlink protocols deal with networking related topics and thus
+dealing with network addresses is a common task.
+
+Currently the following address families are supported:
+
+[options="compact"]
+ * `AF_INET`
+ * `AF_INET6`
+ * `AF_LLC`
+ * `AF_DECnet`
+ * `AF_UNSPEC`
+
+[[core_addr_alloc]]
+.Address Allocation
+
+The function nl_addr_alloc() allocates a new empty address. The
++maxsize+ argument defines the maximum length of an address in bytes.
+The size of an address is address family specific. If the address
+family and address data are known at allocation time the function
+nl_addr_build() can be used alternatively. You may also clone
+an address by calling nl_addr_clone()
+
+[source,c]
+--------
+#include <netlink/addr.h>
+
+struct nl_addr *nl_addr_alloc(size_t maxsize);
+struct nl_addr *nl_addr_clone(struct nl_addr *addr);
+struct nl_addr *nl_addr_build(int family, void *addr, size_t size);
+--------
+
+If the address is transported in a netlink attribute, the function
+nl_addr_alloc_attr() allocates a new address based on the payload
+of the attribute provided. The +family+ argument is used to specify
+the address family of the address, set to +AF_UNSPEC+ if unknown.
+
+[source,c]
+--------
+#include <netlink/addr.h>
+
+struct nl_addr *nl_addr_alloc_attr(struct nlattr *attr, int family);
+--------
+
+If the address is provided by a user, it is usually stored in a human
+readable format. The function nl_addr_parse() parses a character
+string representing an address and allocates a new address based on
+it.
+
+[source,c]
+--------
+#include <netlink/addr.h>
+
+int nl_addr_parse(const char *addr, int hint, struct nl_addr **result);
+--------
+
+If parsing succeeds the function returns 0 and the allocated address
+is stored in +*result+.
+
+NOTE: Make sure to return the reference to an address using
+ `nl_addr_put()` after usage to allow memory being freed.
+
+.Example: Transform character string to abstract address
+[source,c]
+-----
+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);
+-----
+
+.Address References
+
+Abstract addresses use reference counting to account for all users of
+a particular address. After the last user has returned the reference
+the address is freed.
+
+If you pass on a address object to another function and you are not
+sure how long it will be used, make sure to call nl_addr_get() to
+acquire an additional reference and have that function or code path
+call nl_addr_put() as soon as it has finished using the address.
+
+[source,c]
+--------
+#include <netlink/addr.h>
+
+struct nl_addr *nl_addr_get(struct nl_addr *addr);
+void nl_addr_put(struct nl_addr *addr);
+int nl_addr_shared(struct nl_addr *addr);
+--------
+
+You may call nl_addr_shared() at any time to check if you are the only
+user of an address.
+
+.Address Attributes
+
+The address is usually set at allocation time. If it was unknown at that
+time it can be specified later by calling nl_addr_set_family() and is
+accessed with the function nl_addr_get_family().
+
+[source,c]
+--------
+#include <netlink/addr.h>
+
+void nl_addr_set_family(struct nl_addr *addr, int family);
+int nl_addr_get_family(struct nl_addr *addr);
+--------
+
+The same is true for the actual address data. It is typically present
+at allocation time. For exceptions it can be specified later or
+overwritten with the function `nl_addr_set_binary_addr()`. Beware that
+the length of the address may not exceed `maxlen` specified at
+allocation time. The address data is returned by the function
+`nl_addr_get_binary_addr()` and its length by the function
+`nl_addr_get_len()`.
+
+[source,c]
+--------
+#include <netlink/addr.h>
+
+int nl_addr_set_binary_addr(struct nl_addr *addr, void *data, size_t size);
+void *nl_addr_get_binary_addr(struct nl_addr *addr);
+unsigned int nl_addr_get_len(struct nl_addr *addr);
+--------
+
+If you only want to check if the address data consists of all zeros
+the function `nl_addr_iszero()` is a shortcut to that.
+
+[source,c]
+--------
+#include <netlink/addr.h>
+
+int nl_addr_iszero(struct nl_addr *addr);
+--------
+
+==== Address Prefix Length
+
+Although this functionality is somewhat specific to routing it has
+been implemented here. Addresses can have a prefix length assigned
+which implies that only the first n bits are of importance. This is
+f.e. used to implement subnets.
+
+Use set functions `nl_addr_set_prefixlen()` and
+`nl_addr_get_prefixlen()` to work with prefix lengths.
+
+[source,c]
+--------
+#include <netlink/addr.h>
+
+void nl_addr_set_prefixlen(struct nl_addr *addr, int n);
+unsigned int nl_addr_get_prefixlen(struct nl_addr *addr);
+--------
+
+NOTE: The default prefix length is set to (address length * 8)
+
+.Address Helpers
+
+Several functions exist to help when dealing with addresses. The
+function `nl_addr_cmp()` compares two addresses and returns an integer
+less than, equal to or greater than zero without considering the
+prefix length at all. If you want to consider the prefix length, use
+the function `nl_addr_cmp_prefix()`.
+
+[source,c]
+--------
+#include <netlink/addr.h>
+
+int nl_addr_cmp(struct nl_addr *addr, struct nl_addr *addr);
+int nl_addr_cmp_prefix(struct nl_addr *addr, struct nl_addr *addr);
+--------
+
+If an abstract address needs to presented to the user it should be
+done in a human readable format which differs depending on the address
+family. The function `nl_addr2str()` takes care of this by calling the
+appropriate conversion functions internaly. It expects a `buf` of
+length `size` to write the character string into and returns a pointer
+to `buf` for easy `printf()` usage.
+
+[source,c]
+--------
+#include <netlink/addr.h>
+
+char *nl_addr2str(struct nl_addr *addr, char *buf, size_t size);
+--------
+
+If the address family is unknown, the address data will be printed in
+hexadecimal format `AA:BB:CC:DD:...`
+
+Often the only way to figure out the address family is by looking at
+the length of the address. The function `nl_addr_guess_family()` does
+just this and returns the address family guessed based on the address
+size.
+
+[source,c]
+--------
+#include <netlink/addr.h>
+
+int nl_addr_guess_family(struct nl_addr *addr);
+--------
+
+Before allocating an address you may want to check if the character
+string actually represents a valid address of the address family you
+are expecting. The function `nl_addr_valid()` can be used for that, it
+returns 1 if the supplised `addr` is a valid address in the context of
+`family`. See `inet_pton(3)`, `dnet_pton(3)` for more information on
+valid adddress formats.
+
+[source,c]
+--------
+#include <netlink/addr.h>
+
+int nl_addr_valid(char *addr, int family);
+--------
+
+=== Abstract Data
+
+The abstract data type is a trivial datatype with the primary purpose
+to simplify usage of netlink attributes of arbitary length.
+
+[[core_data_alloc]]
+.Allocation of a Data Object
+The function `nl_data_alloc()` alloctes a new abstract data object and
+fill it with the provided data. `nl_data_alloc_attr()` does the same
+but bases the data on the payload of a netlink attribute. New data
+objects can also be allocated by cloning existing ones by using
+`nl_data_clone()`.
+
+[source,c]
+--------
+struct nl_data *nl_data_alloc(void *buf, size_t size);
+struct nl_data *nl_data_alloc_attr(struct nlattr *attr);
+struct nl_data *nl_data_clone(struct nl_data *data);
+void nl_data_free(struct nl_data *data);
+--------
+
+.Access to Data
+
+The function `nl_data_get()` returns a pointer to the data, the size
+of data is returned by `nl_data_get_size()`.
+
+[source,c]
+--------
+void *nl_data_get(struct nl_data *data);
+size_t nl_data_get_size(struct nl_data *data);
+--------
+
+.Data Helpers
+
+The function nl_data_append() reallocates the internal data buffers
+and appends the specified `buf` to the existing data.
+
+[source,c]
+--------
+int nl_data_append(struct nl_data *data, void *buf, size_t size);
+--------
+
+CAUTION: Any call to `nl_data_append()` invalidates all pointers
+ returned by `nl_data_get()` of the same data object.
+
+[source,c]
+--------
+int nl_data_cmp(struct nl_data *data, struct nl_data *data);
+--------
diff --git a/doc/doxygen-link.py b/doc/doxygen-link.py
new file mode 100755
index 00000000..910b8f8d
--- /dev/null
+++ b/doc/doxygen-link.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+import fileinput
+import re
+import sys
+
+
+rc_script = re.compile(r'\s*(.*\S)?\s*')
+
+def parse_dict(filename):
+ links = {}
+ for line in open(filename, 'r'):
+ m = re.match('^([^=]+)=([^\n]+)$', line);
+ if not m:
+ continue
+ name = m.group(1)
+ value = m.group(2)
+
+ # strip leading and trailing whitespace
+ m = rc_script.match(name)
+ if m:
+ name = m.group(1)
+
+ # skip special names
+ if name == '':
+ continue
+ if name == '\\':
+ continue
+
+ links[name] = "<a href=\"" + value + "\" class=\"dg\">" + name + "</a>"
+ return links
+
+links = parse_dict(sys.argv[1])
+
+def translate(match):
+ return links[match.group(1)]
+
+# match for all names, with word boundaries \b
+rc = re.compile(r'\b(' + '|'.join(map(re.escape, sorted(links, reverse=True))) + r')\b')
+
+for line in open(sys.argv[2], 'r'):
+ print(rc.sub(translate, line), end='')
diff --git a/doc/gen-tags.sh b/doc/gen-tags.sh
new file mode 100755
index 00000000..862ec090
--- /dev/null
+++ b/doc/gen-tags.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+#
+# Based on a script found on the englinemtn-devel mailinglist
+# written by Carsten Haitzler <ras...@rasterman.com>
+#
+
+for f in api/group__*.html
+do
+ bf=$(basename $f)
+
+ grep -oE "href=\"$bf#[a-z0-9]+\">[^<]+</a>" $f |
+ sed 's/href="\([^"]*\)">\([^<]*\)<\/a>/\2=api\/\1/'
+done
diff --git a/doc/html/.gitignore b/doc/html/.gitignore
deleted file mode 100644
index 72e8ffc0..00000000
--- a/doc/html/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*
diff --git a/doc/images/.gitignore b/doc/images/.gitignore
new file mode 100644
index 00000000..efcc7e23
--- /dev/null
+++ b/doc/images/.gitignore
@@ -0,0 +1,3 @@
+core__*
+asciidoc__*.png
+*.odg
diff --git a/doc/images/addressing.png b/doc/images/addressing.png
new file mode 100644
index 00000000..9dcaaffd
--- /dev/null
+++ b/doc/images/addressing.png
Binary files differ
diff --git a/doc/images/attribute_hdr.png b/doc/images/attribute_hdr.png
new file mode 100644
index 00000000..0e6cfdad
--- /dev/null
+++ b/doc/images/attribute_hdr.png
Binary files differ
diff --git a/doc/images/classful_qdisc.png b/doc/images/classful_qdisc.png
new file mode 100644
index 00000000..7e77350f
--- /dev/null
+++ b/doc/images/classful_qdisc.png
Binary files differ
diff --git a/doc/images/classless_qdisc.png b/doc/images/classless_qdisc.png
new file mode 100644
index 00000000..bcf2c1ce
--- /dev/null
+++ b/doc/images/classless_qdisc.png
Binary files differ
diff --git a/doc/images/classless_qdisc_nbands.png b/doc/images/classless_qdisc_nbands.png
new file mode 100644
index 00000000..14cb0260
--- /dev/null
+++ b/doc/images/classless_qdisc_nbands.png
Binary files differ
diff --git a/doc/images/icons/README b/doc/images/icons/README
new file mode 100644
index 00000000..f12b2a73
--- /dev/null
+++ b/doc/images/icons/README
@@ -0,0 +1,5 @@
+Replaced the plain DocBook XSL admonition icons with Jimmac's DocBook
+icons (http://jimmac.musichall.cz/ikony.php3). I dropped transparency
+from the Jimmac icons to get round MS IE and FOP PNG incompatibilies.
+
+Stuart Rackham
diff --git a/doc/images/icons/callouts/1.png b/doc/images/icons/callouts/1.png
new file mode 100644
index 00000000..7d473430
--- /dev/null
+++ b/doc/images/icons/callouts/1.png
Binary files differ
diff --git a/doc/images/icons/callouts/10.png b/doc/images/icons/callouts/10.png
new file mode 100644
index 00000000..997bbc82
--- /dev/null
+++ b/doc/images/icons/callouts/10.png
Binary files differ
diff --git a/doc/images/icons/callouts/11.png b/doc/images/icons/callouts/11.png
new file mode 100644
index 00000000..ce47dac3
--- /dev/null
+++ b/doc/images/icons/callouts/11.png
Binary files differ
diff --git a/doc/images/icons/callouts/12.png b/doc/images/icons/callouts/12.png
new file mode 100644
index 00000000..31daf4e2
--- /dev/null
+++ b/doc/images/icons/callouts/12.png
Binary files differ
diff --git a/doc/images/icons/callouts/13.png b/doc/images/icons/callouts/13.png
new file mode 100644
index 00000000..14021a89
--- /dev/null
+++ b/doc/images/icons/callouts/13.png
Binary files differ
diff --git a/doc/images/icons/callouts/14.png b/doc/images/icons/callouts/14.png
new file mode 100644
index 00000000..64014b75
--- /dev/null
+++ b/doc/images/icons/callouts/14.png
Binary files differ
diff --git a/doc/images/icons/callouts/15.png b/doc/images/icons/callouts/15.png
new file mode 100644
index 00000000..0d65765f
--- /dev/null
+++ b/doc/images/icons/callouts/15.png
Binary files differ
diff --git a/doc/images/icons/callouts/2.png b/doc/images/icons/callouts/2.png
new file mode 100644
index 00000000..5d09341b
--- /dev/null
+++ b/doc/images/icons/callouts/2.png
Binary files differ
diff --git a/doc/images/icons/callouts/3.png b/doc/images/icons/callouts/3.png
new file mode 100644
index 00000000..ef7b7004
--- /dev/null
+++ b/doc/images/icons/callouts/3.png
Binary files differ
diff --git a/doc/images/icons/callouts/4.png b/doc/images/icons/callouts/4.png
new file mode 100644
index 00000000..adb8364e
--- /dev/null
+++ b/doc/images/icons/callouts/4.png
Binary files differ
diff --git a/doc/images/icons/callouts/5.png b/doc/images/icons/callouts/5.png
new file mode 100644
index 00000000..4d7eb460
--- /dev/null
+++ b/doc/images/icons/callouts/5.png
Binary files differ
diff --git a/doc/images/icons/callouts/6.png b/doc/images/icons/callouts/6.png
new file mode 100644
index 00000000..0ba694af
--- /dev/null
+++ b/doc/images/icons/callouts/6.png
Binary files differ
diff --git a/doc/images/icons/callouts/7.png b/doc/images/icons/callouts/7.png
new file mode 100644
index 00000000..472e96f8
--- /dev/null
+++ b/doc/images/icons/callouts/7.png
Binary files differ
diff --git a/doc/images/icons/callouts/8.png b/doc/images/icons/callouts/8.png
new file mode 100644
index 00000000..5e60973c
--- /dev/null
+++ b/doc/images/icons/callouts/8.png
Binary files differ
diff --git a/doc/images/icons/callouts/9.png b/doc/images/icons/callouts/9.png
new file mode 100644
index 00000000..a0676d26
--- /dev/null
+++ b/doc/images/icons/callouts/9.png
Binary files differ
diff --git a/doc/images/icons/caution.png b/doc/images/icons/caution.png
new file mode 100644
index 00000000..9a8c515a
--- /dev/null
+++ b/doc/images/icons/caution.png
Binary files differ
diff --git a/doc/images/icons/example.png b/doc/images/icons/example.png
new file mode 100644
index 00000000..1199e864
--- /dev/null
+++ b/doc/images/icons/example.png
Binary files differ
diff --git a/doc/images/icons/home.png b/doc/images/icons/home.png
new file mode 100644
index 00000000..37a5231b
--- /dev/null
+++ b/doc/images/icons/home.png
Binary files differ
diff --git a/doc/images/icons/important.png b/doc/images/icons/important.png
new file mode 100644
index 00000000..be685cc4
--- /dev/null
+++ b/doc/images/icons/important.png
Binary files differ
diff --git a/doc/images/icons/next.png b/doc/images/icons/next.png
new file mode 100644
index 00000000..64e126bd
--- /dev/null
+++ b/doc/images/icons/next.png
Binary files differ
diff --git a/doc/images/icons/note.png b/doc/images/icons/note.png
new file mode 100644
index 00000000..7c1f3e2f
--- /dev/null
+++ b/doc/images/icons/note.png
Binary files differ
diff --git a/doc/images/icons/prev.png b/doc/images/icons/prev.png
new file mode 100644
index 00000000..3e8f12fe
--- /dev/null
+++ b/doc/images/icons/prev.png
Binary files differ
diff --git a/doc/images/icons/tip.png b/doc/images/icons/tip.png
new file mode 100644
index 00000000..f087c73b
--- /dev/null
+++ b/doc/images/icons/tip.png
Binary files differ
diff --git a/doc/images/icons/up.png b/doc/images/icons/up.png
new file mode 100644
index 00000000..2db1ce62
--- /dev/null
+++ b/doc/images/icons/up.png
Binary files differ
diff --git a/doc/images/icons/warning.png b/doc/images/icons/warning.png
new file mode 100644
index 00000000..d41edb9a
--- /dev/null
+++ b/doc/images/icons/warning.png
Binary files differ
diff --git a/doc/images/ifinfomsg.png b/doc/images/ifinfomsg.png
new file mode 100644
index 00000000..fb94cf7b
--- /dev/null
+++ b/doc/images/ifinfomsg.png
Binary files differ
diff --git a/doc/images/library_overview.png b/doc/images/library_overview.png
new file mode 100644
index 00000000..dd9d5fe9
--- /dev/null
+++ b/doc/images/library_overview.png
Binary files differ
diff --git a/doc/images/nlmsgerr.png b/doc/images/nlmsgerr.png
new file mode 100644
index 00000000..58e53d55
--- /dev/null
+++ b/doc/images/nlmsgerr.png
Binary files differ
diff --git a/doc/images/nlmsghdr.png b/doc/images/nlmsghdr.png
new file mode 100644
index 00000000..dd39b9ca
--- /dev/null
+++ b/doc/images/nlmsghdr.png
Binary files differ
diff --git a/doc/images/qdisc_default.png b/doc/images/qdisc_default.png
new file mode 100644
index 00000000..a7ba1678
--- /dev/null
+++ b/doc/images/qdisc_default.png
Binary files differ
diff --git a/doc/images/qdisc_mq.png b/doc/images/qdisc_mq.png
new file mode 100644
index 00000000..c6318b28
--- /dev/null
+++ b/doc/images/qdisc_mq.png
Binary files differ
diff --git a/doc/images/tc_obj.png b/doc/images/tc_obj.png
new file mode 100644
index 00000000..bfc81452
--- /dev/null
+++ b/doc/images/tc_obj.png
Binary files differ
diff --git a/doc/images/tc_overview.png b/doc/images/tc_overview.png
new file mode 100644
index 00000000..ce23e67b
--- /dev/null
+++ b/doc/images/tc_overview.png
Binary files differ
diff --git a/doc/index.txt b/doc/index.txt
new file mode 100644
index 00000000..c207b448
--- /dev/null
+++ b/doc/index.txt
@@ -0,0 +1,22 @@
+Documentation Overview - libnl Suite
+====================================
+
+== Libraries
+
+image:library_overview.png["Library Hierarchy"]
+
+link:core.html[Netlink Library] (libnl)::
+Socket handling, sending and receiving, message construction and parsing, ...
+
+link:route.html[Routing Family Library] (libnl-route)::
+Adresses, links, neighbours, routing, traffic control, neighbour tables, ...
+
+Netfilter Library (libnl-nf)::
+Connection tracking, logging, queueing
+
+Generic Netlink Library (libnl-genl)::
+Controller API, family and command registration
+
+== Python Packages
+ - netlink.core
+ - netlink.route.link
diff --git a/doc/libnl.css b/doc/libnl.css
index 22c48430..85894507 100644
--- a/doc/libnl.css
+++ b/doc/libnl.css
@@ -1,473 +1,1156 @@
-BODY,H1,H2,H3,H4,H5,H6,P,CENTER,TD,TH,UL,DL,DIV {
- font-family: Geneva, Arial, Helvetica, sans-serif;
+/* The standard CSS for doxygen */
+
+body, table, div, p, dl {
+ font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
+ font-size: 13px;
+ line-height: 1.3;
}
-BODY,TD {
- font-size: 90%;
+
+/* @group Heading Levels */
+
+h1 {
+ font-size: 150%;
}
-H1 {
- text-align: center;
- font-size: 160%;
+
+.title {
+ font-size: 150%;
+ font-weight: bold;
+ margin: 10px 2px;
}
-H2 {
+
+h2 {
font-size: 120%;
}
-H3 {
+
+h3 {
font-size: 100%;
}
-CAPTION {
- font-weight: bold
+
+h1, h2, h3, h4, h5, h6 {
+ -webkit-transition: text-shadow 0.5s linear;
+ -moz-transition: text-shadow 0.5s linear;
+ -ms-transition: text-shadow 0.5s linear;
+ -o-transition: text-shadow 0.5s linear;
+ transition: text-shadow 0.5s linear;
+ margin-right: 15px;
}
-DIV.qindex {
- width: 100%;
- background-color: #e8eef2;
- border: 1px solid #84b0c7;
+
+h1.glow, h2.glow, h3.glow, h4.glow, h5.glow, h6.glow {
+ text-shadow: 0 0 15px cyan;
+}
+
+dt {
+ font-weight: bold;
+}
+
+div.multicol {
+ -moz-column-gap: 1em;
+ -webkit-column-gap: 1em;
+ -moz-column-count: 3;
+ -webkit-column-count: 3;
+}
+
+p.startli, p.startdd, p.starttd {
+ margin-top: 2px;
+}
+
+p.endli {
+ margin-bottom: 0px;
+}
+
+p.enddd {
+ margin-bottom: 4px;
+}
+
+p.endtd {
+ margin-bottom: 2px;
+}
+
+/* @end */
+
+caption {
+ font-weight: bold;
+}
+
+span.legend {
+ font-size: 70%;
+ text-align: center;
+}
+
+h3.version {
+ font-size: 90%;
+ text-align: center;
+}
+
+div.qindex, div.navtab{
+ background-color: #EBEFF6;
+ border: 1px solid #A3B4D7;
text-align: center;
- margin: 2px;
- padding: 2px;
- line-height: 140%;
}
-DIV.navpath {
+
+div.qindex, div.navpath {
width: 100%;
- background-color: #e8eef2;
- border: 1px solid #84b0c7;
- text-align: center;
- margin: 2px;
- padding: 2px;
line-height: 140%;
}
-DIV.navtab {
- background-color: #e8eef2;
- border: 1px solid #84b0c7;
- text-align: center;
- margin: 2px;
- margin-right: 15px;
- padding: 2px;
+
+div.navtab {
+ margin-right: 15px;
}
-TD.navtab {
- font-size: 70%;
+
+/* @group Link Styling */
+
+a {
+ color: #3D578C;
+ font-weight: normal;
+ text-decoration: none;
}
-A.qindex {
- text-decoration: none;
- font-weight: bold;
- color: #1A419D;
+
+.contents a:visited {
+ color: #4665A2;
}
-A.qindex:visited {
- text-decoration: none;
- font-weight: bold;
- color: #1A419D
+
+a:hover {
+ text-decoration: underline;
}
-A.qindex:hover {
- text-decoration: none;
- background-color: #ddddff;
+
+a.qindex {
+ font-weight: bold;
}
-A.qindexHL {
- text-decoration: none;
+
+a.qindexHL {
font-weight: bold;
- background-color: #6666cc;
+ background-color: #9CAFD4;
color: #ffffff;
- border: 1px double #9295C2;
+ border: 1px double #869DCA;
}
-A.qindexHL:hover {
- text-decoration: none;
- background-color: #6666cc;
- color: #ffffff;
+
+.contents a.qindexHL:visited {
+ color: #ffffff;
+}
+
+a.el {
+ font-weight: bold;
}
-A.qindexHL:visited {
- text-decoration: none;
- background-color: #6666cc;
- color: #ffffff
+
+a.elRef {
}
-A.el {
- text-decoration: none;
- font-weight: bold
+
+a.code, a.code:visited {
+ color: #4665A2;
}
-A.elRef {
- font-weight: bold
+
+a.codeRef, a.codeRef:visited {
+ color: #4665A2;
}
-A.code:link {
- text-decoration: none;
- font-weight: normal;
- color: #0000FF
+
+/* @end */
+
+dl.el {
+ margin-left: -1cm;
}
-A.code:visited {
- text-decoration: none;
- font-weight: normal;
- color: #0000FF
+
+pre.fragment {
+ border: 1px solid #C4CFE5;
+ background-color: #FBFCFD;
+ padding: 4px 6px;
+ margin: 4px 8px 4px 2px;
+ overflow: auto;
+ word-wrap: break-word;
+ font-size: 9pt;
+ line-height: 125%;
+ font-family: monospace, fixed;
+ font-size: 105%;
}
-A.codeRef:link {
- font-weight: normal;
- color: #0000FF
+
+div.fragment {
+ padding: 4px;
+ margin: 4px;
+ background-color: #FBFCFD;
+ border: 1px solid #C4CFE5;
}
-A.codeRef:visited {
- font-weight: normal;
- color: #0000FF
+
+div.line {
+ font-family: monospace, fixed;
+ font-size: 13px;
+ min-height: 13px;
+ line-height: 1.0;
+ text-wrap: unrestricted;
+ white-space: -moz-pre-wrap; /* Moz */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ white-space: pre-wrap; /* CSS3 */
+ word-wrap: break-word; /* IE 5.5+ */
+ text-indent: -53px;
+ padding-left: 53px;
+ padding-bottom: 0px;
+ margin: 0px;
+ -webkit-transition-property: background-color, box-shadow;
+ -webkit-transition-duration: 0.5s;
+ -moz-transition-property: background-color, box-shadow;
+ -moz-transition-duration: 0.5s;
+ -ms-transition-property: background-color, box-shadow;
+ -ms-transition-duration: 0.5s;
+ -o-transition-property: background-color, box-shadow;
+ -o-transition-duration: 0.5s;
+ transition-property: background-color, box-shadow;
+ transition-duration: 0.5s;
}
-A:hover {
- text-decoration: none;
- background-color: #f2f2ff
+
+div.line.glow {
+ background-color: cyan;
+ box-shadow: 0 0 10px cyan;
}
-DL.el {
- margin-left: -1cm
+
+
+span.lineno {
+ padding-right: 4px;
+ text-align: right;
+ border-right: 2px solid #0F0;
+ background-color: #E8E8E8;
+ white-space: pre;
}
-.fragment {
- font-family: monospace, fixed;
- font-size: 95%;
+span.lineno a {
+ background-color: #D8D8D8;
}
-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;
+
+span.lineno a:hover {
+ background-color: #C8C8C8;
}
-DIV.ah {
- background-color: black;
- font-weight: bold;
- color: #ffffff;
- margin-bottom: 3px;
- margin-top: 3px
+
+div.ah {
+ background-color: black;
+ font-weight: bold;
+ color: #ffffff;
+ margin-bottom: 3px;
+ margin-top: 3px;
+ padding: 0.2em;
+ border: solid thin #333;
+ border-radius: 0.5em;
+ -webkit-border-radius: .5em;
+ -moz-border-radius: .5em;
+ box-shadow: 2px 2px 3px #999;
+ -webkit-box-shadow: 2px 2px 3px #999;
+ -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000),color-stop(0.3, #444));
+ background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000);
}
-DIV.groupHeader {
- margin-left: 16px;
- margin-top: 12px;
- margin-bottom: 6px;
- font-weight: bold;
+div.groupHeader {
+ margin-left: 16px;
+ margin-top: 12px;
+ font-weight: bold;
}
-DIV.groupText {
- margin-left: 16px;
- font-style: italic;
- font-size: 90%
+
+div.groupText {
+ margin-left: 16px;
+ font-style: italic;
}
-BODY {
- background: white;
+
+body {
+ background-color: white;
color: black;
- margin-right: 20px;
- margin-left: 20px;
+ margin: 0;
}
-TD.indexkey {
- background-color: #e8eef2;
+
+div.contents {
+ margin-top: 10px;
+ margin-left: 12px;
+ margin-right: 8px;
+}
+
+td.indexkey {
+ background-color: #EBEFF6;
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: #e8eef2;
- 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 }
-SPAN.vhdldigit { color: #ff00ff }
-SPAN.vhdlchar { color: #000000 }
-SPAN.vhdlkeyword { color: #700070 }
-SPAN.vhdllogic { color: #ff0000 }
-
-.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;
+ border: 1px solid #C4CFE5;
+ margin: 2px 0px 2px 0;
+ padding: 2px 10px;
+ white-space: nowrap;
+ vertical-align: top;
}
-.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;
+
+td.indexvalue {
+ background-color: #EBEFF6;
+ border: 1px solid #C4CFE5;
+ padding: 2px 10px;
+ margin: 2px 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%;
+
+tr.memlist {
+ background-color: #EEF1F7;
}
-.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%;
+
+p.formulaDsp {
+ text-align: center;
}
-.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%;
+
+img.formulaDsp {
+
}
-.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%;
+
+img.formulaInl {
+ vertical-align: middle;
}
-.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%;
+
+div.center {
+ text-align: center;
+ margin-top: 0px;
+ margin-bottom: 0px;
+ padding: 0px;
+}
+
+div.center img {
+ border: 0px;
+}
+
+address.footer {
+ text-align: right;
+ padding-right: 12px;
+}
+
+img.footer {
+ border: 0px;
+ vertical-align: middle;
+}
+
+/* @group Code Colorization */
+
+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
+}
+
+span.vhdldigit {
+ color: #ff00ff
+}
+
+span.vhdlchar {
+ color: #000000
+}
+
+span.vhdlkeyword {
+ color: #700070
+}
+
+span.vhdllogic {
+ color: #ff0000
+}
+
+blockquote {
+ background-color: #F7F8FB;
+ border-left: 2px solid #9CAFD4;
+ margin: 0 24px 0 4px;
+ padding: 0 12px 0 16px;
}
-.search {
+
+/* @end */
+
+/*
+.search {
color: #003399;
font-weight: bold;
}
-FORM.search {
+
+form.search {
margin-bottom: 0px;
margin-top: 0px;
}
-INPUT.search {
+
+input.search {
font-size: 75%;
color: #000080;
font-weight: normal;
background-color: #e8eef2;
}
-TD.tiny {
+*/
+
+td.tiny {
font-size: 75%;
}
-a {
- color: #1A41A8;
-}
-a:visited {
- color: #2A3798;
-}
-.dirtab {
+
+.dirtab {
padding: 4px;
border-collapse: collapse;
- border: 1px solid #84b0c7;
+ border: 1px solid #A3B4D7;
}
-TH.dirtab {
- background: #e8eef2;
+
+th.dirtab {
+ background: #EBEFF6;
font-weight: bold;
}
-HR {
+
+hr {
+ height: 0px;
+ border: none;
+ border-top: 1px solid #4A6AAA;
+}
+
+hr.footer {
height: 1px;
+}
+
+/* @group Member Descriptions */
+
+table.memberdecls {
+ border-spacing: 0px;
+ padding: 0px;
+}
+
+.memberdecls td {
+ -webkit-transition-property: background-color, box-shadow;
+ -webkit-transition-duration: 0.5s;
+ -moz-transition-property: background-color, box-shadow;
+ -moz-transition-duration: 0.5s;
+ -ms-transition-property: background-color, box-shadow;
+ -ms-transition-duration: 0.5s;
+ -o-transition-property: background-color, box-shadow;
+ -o-transition-duration: 0.5s;
+ transition-property: background-color, box-shadow;
+ transition-duration: 0.5s;
+}
+
+.memberdecls td.glow {
+ background-color: cyan;
+ box-shadow: 0 0 15px cyan;
+}
+
+.mdescLeft, .mdescRight,
+.memItemLeft, .memItemRight,
+.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
+ background-color: #F9FAFC;
border: none;
- border-top: 1px solid black;
+ margin: 4px;
+ padding: 1px 0 0 8px;
+}
+
+.mdescLeft, .mdescRight {
+ padding: 0px 8px 4px 8px;
+ color: #555;
+}
+
+.memItemLeft, .memItemRight, .memTemplParams {
+ border-top: 1px solid #C4CFE5;
+}
+
+.memItemLeft, .memTemplItemLeft {
+ white-space: nowrap;
+}
+
+.memItemRight {
+ width: 100%;
}
-/* Style for detailed member documentation */
+.memTemplParams {
+ color: #4665A2;
+ white-space: nowrap;
+}
+
+/* @end */
+
+/* @group Member Details */
+
+/* Styles for detailed member documentation */
+
.memtemplate {
font-size: 80%;
- color: #606060;
+ color: #4665A2;
font-weight: normal;
- margin-left: 3px;
-}
-.memnav {
- background-color: #e8eef2;
- border: 1px solid #84b0c7;
+ margin-left: 9px;
+}
+
+.memnav {
+ background-color: #EBEFF6;
+ border: 1px solid #A3B4D7;
text-align: center;
margin: 2px;
margin-right: 15px;
padding: 2px;
}
+
+.mempage {
+ width: 100%;
+}
+
.memitem {
- padding: 4px;
- background-color: #eef3f5;
- border-width: 1px;
- border-style: solid;
- border-color: #dedeee;
- -moz-border-radius: 8px 8px 8px 8px;
+ padding: 0;
+ margin-bottom: 10px;
+ margin-right: 5px;
+ -webkit-transition: box-shadow 0.5s linear;
+ -moz-transition: box-shadow 0.5s linear;
+ -ms-transition: box-shadow 0.5s linear;
+ -o-transition: box-shadow 0.5s linear;
+ transition: box-shadow 0.5s linear;
+ display: table !important;
+ width: 100%;
+}
+
+.memitem.glow {
+ box-shadow: 0 0 15px cyan;
}
+
.memname {
- white-space: nowrap;
- font-weight: bold;
+ font-weight: bold;
+ margin-left: 6px;
}
-.memdoc{
- padding-left: 10px;
+
+.memname td {
+ vertical-align: bottom;
}
-.memproto {
- background-color: #d5e1e8;
- width: 100%;
- border-width: 1px;
- border-style: solid;
- border-color: #84b0c7;
- font-weight: bold;
- -moz-border-radius: 8px 8px 8px 8px;
+
+.memproto, dl.reflist dt {
+ border-top: 1px solid #A8B8D9;
+ border-left: 1px solid #A8B8D9;
+ border-right: 1px solid #A8B8D9;
+ padding: 6px 0px 6px 0px;
+ color: #253555;
+ font-weight: bold;
+ text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);
+ background-image:url('nav_f.png');
+ background-repeat:repeat-x;
+ background-color: #E2E8F2;
+ /* opera specific markup */
+ box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+ border-top-right-radius: 4px;
+ border-top-left-radius: 4px;
+ /* firefox specific markup */
+ -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+ -moz-border-radius-topright: 4px;
+ -moz-border-radius-topleft: 4px;
+ /* webkit specific markup */
+ -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+ -webkit-border-top-right-radius: 4px;
+ -webkit-border-top-left-radius: 4px;
+
}
+
+.memdoc, dl.reflist dd {
+ border-bottom: 1px solid #A8B8D9;
+ border-left: 1px solid #A8B8D9;
+ border-right: 1px solid #A8B8D9;
+ padding: 6px 10px 2px 10px;
+ background-color: #FBFCFD;
+ border-top-width: 0;
+ background-image:url('nav_g.png');
+ background-repeat:repeat-x;
+ background-color: #FFFFFF;
+ /* opera specific markup */
+ border-bottom-left-radius: 4px;
+ border-bottom-right-radius: 4px;
+ box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+ /* firefox specific markup */
+ -moz-border-radius-bottomleft: 4px;
+ -moz-border-radius-bottomright: 4px;
+ -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+ /* webkit specific markup */
+ -webkit-border-bottom-left-radius: 4px;
+ -webkit-border-bottom-right-radius: 4px;
+ -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+}
+
+dl.reflist dt {
+ padding: 5px;
+}
+
+dl.reflist dd {
+ margin: 0px 0px 10px 0px;
+ padding: 5px;
+}
+
.paramkey {
text-align: right;
}
+
.paramtype {
white-space: nowrap;
}
+
.paramname {
color: #602020;
- font-style: italic;
white-space: nowrap;
}
-/* End Styling for detailed member documentation */
+.paramname em {
+ font-style: normal;
+}
+.paramname code {
+ line-height: 14px;
+}
+
+.params, .retval, .exception, .tparams {
+ margin-left: 0px;
+ padding-left: 0px;
+}
-/* for the tree view */
-.ftvtree {
- font-family: sans-serif;
- margin:0.5em;
+.params .paramname, .retval .paramname {
+ font-weight: bold;
+ vertical-align: top;
+}
+
+.params .paramtype {
+ font-style: italic;
+ vertical-align: top;
+}
+
+.params .paramdir {
+ font-family: "courier new",courier,monospace;
+ vertical-align: top;
}
-/* these are for tree view when used as main index */
-.directory {
- font-size: 9pt;
- font-weight: bold;
+
+table.mlabels {
+ border-spacing: 0px;
}
-.directory h3 {
- margin: 0px;
- margin-top: 1em;
- font-size: 11pt;
+
+td.mlabels-left {
+ width: 100%;
+ padding: 0px;
}
-/* The following two styles can be used to replace the root node title */
-/* with an image of your choice. Simply uncomment the next two styles, */
-/* specify the name of your image and be sure to set 'height' to the */
-/* proper pixel height of your image. */
+td.mlabels-right {
+ vertical-align: bottom;
+ padding: 0px;
+ white-space: nowrap;
+}
-/* .directory h3.swap { */
-/* height: 61px; */
-/* background-repeat: no-repeat; */
-/* background-image: url("yourimage.gif"); */
-/* } */
-/* .directory h3.swap span { */
-/* display: none; */
-/* } */
+span.mlabels {
+ margin-left: 8px;
+}
-.directory > h3 {
- margin-top: 0;
+span.mlabel {
+ background-color: #728DC1;
+ border-top:1px solid #5373B4;
+ border-left:1px solid #5373B4;
+ border-right:1px solid #C4CFE5;
+ border-bottom:1px solid #C4CFE5;
+ text-shadow: none;
+ color: white;
+ margin-right: 4px;
+ padding: 2px 3px;
+ border-radius: 3px;
+ font-size: 7pt;
+ white-space: nowrap;
}
-.directory p {
- margin: 0px;
- white-space: nowrap;
+
+
+
+/* @end */
+
+/* these are for tree view when not used as main index */
+
+div.directory {
+ margin: 10px 0px;
+ border-top: 1px solid #A8B8D9;
+ border-bottom: 1px solid #A8B8D9;
+ width: 100%;
+}
+
+.directory table {
+ border-collapse:collapse;
}
-.directory div {
- display: none;
- margin: 0px;
+
+.directory td {
+ margin: 0px;
+ padding: 0px;
+ vertical-align: top;
}
-.directory img {
- vertical-align: -30%;
+
+.directory td.entry {
+ white-space: nowrap;
+ padding-right: 6px;
}
-/* these are for tree view when not used as main index */
-.directory-alt {
- font-size: 100%;
- font-weight: bold;
+
+.directory td.entry a {
+ outline:none;
+}
+
+.directory td.entry a img {
+ border: none;
+}
+
+.directory td.desc {
+ width: 100%;
+ padding-left: 6px;
+ padding-right: 6px;
+ border-left: 1px solid rgba(0,0,0,0.05);
+}
+
+.directory tr.even {
+ padding-left: 6px;
+ background-color: #F7F8FB;
+}
+
+.directory img {
+ vertical-align: -30%;
+}
+
+.directory .levels {
+ white-space: nowrap;
+ width: 100%;
+ text-align: right;
+ font-size: 9pt;
+}
+
+.directory .levels span {
+ cursor: pointer;
+ padding-left: 2px;
+ padding-right: 2px;
+ color: #3D578C;
+}
+
+div.dynheader {
+ margin-top: 8px;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+address {
+ font-style: normal;
+ color: #2A3D61;
+}
+
+table.doxtable {
+ border-collapse:collapse;
+ margin-top: 4px;
+ margin-bottom: 4px;
+}
+
+table.doxtable td, table.doxtable th {
+ border: 1px solid #2D4068;
+ padding: 3px 7px 2px;
+}
+
+table.doxtable th {
+ background-color: #374F7F;
+ color: #FFFFFF;
+ font-size: 110%;
+ padding-bottom: 4px;
+ padding-top: 5px;
+}
+
+table.fieldtable {
+ width: 100%;
+ margin-bottom: 10px;
+ border: 1px solid #A8B8D9;
+ border-spacing: 0px;
+ -moz-border-radius: 4px;
+ -webkit-border-radius: 4px;
+ border-radius: 4px;
+ -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
+ -webkit-box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15);
+ box-shadow: 2px 2px 2px rgba(0, 0, 0, 0.15);
+}
+
+.fieldtable td, .fieldtable th {
+ padding: 3px 7px 2px;
}
-.directory-alt h3 {
- margin: 0px;
- margin-top: 1em;
- font-size: 11pt;
+
+.fieldtable td.fieldtype, .fieldtable td.fieldname {
+ white-space: nowrap;
+ border-right: 1px solid #A8B8D9;
+ border-bottom: 1px solid #A8B8D9;
+ vertical-align: top;
+}
+
+.fieldtable td.fielddoc {
+ border-bottom: 1px solid #A8B8D9;
+ width: 100%;
+}
+
+.fieldtable tr:last-child td {
+ border-bottom: none;
+}
+
+.fieldtable th {
+ background-image:url('nav_f.png');
+ background-repeat:repeat-x;
+ background-color: #E2E8F2;
+ font-size: 90%;
+ color: #253555;
+ padding-bottom: 4px;
+ padding-top: 5px;
+ text-align:left;
+ -moz-border-radius-topleft: 4px;
+ -moz-border-radius-topright: 4px;
+ -webkit-border-top-left-radius: 4px;
+ -webkit-border-top-right-radius: 4px;
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ border-bottom: 1px solid #A8B8D9;
}
-.directory-alt > h3 {
- margin-top: 0;
+
+
+.tabsearch {
+ top: 0px;
+ left: 10px;
+ height: 36px;
+ background-image: url('tab_b.png');
+ z-index: 101;
+ overflow: hidden;
+ font-size: 13px;
}
-.directory-alt p {
- margin: 0px;
- white-space: nowrap;
+
+.navpath ul
+{
+ font-size: 11px;
+ background-image:url('tab_b.png');
+ background-repeat:repeat-x;
+ height:30px;
+ line-height:30px;
+ color:#8AA0CC;
+ border:solid 1px #C2CDE4;
+ overflow:hidden;
+ margin:0px;
+ padding:0px;
}
-.directory-alt div {
- display: none;
- margin: 0px;
+
+.navpath li
+{
+ list-style-type:none;
+ float:left;
+ padding-left:10px;
+ padding-right:15px;
+ background-image:url('bc_s.png');
+ background-repeat:no-repeat;
+ background-position:right;
+ color:#364D7C;
}
-.directory-alt img {
- vertical-align: -30%;
+
+.navpath li.navelem a
+{
+ height:32px;
+ display:block;
+ text-decoration: none;
+ outline: none;
+}
+
+.navpath li.navelem a:hover
+{
+ color:#6884BD;
+}
+
+.navpath li.footer
+{
+ list-style-type:none;
+ float:right;
+ padding-left:10px;
+ padding-right:15px;
+ background-image:none;
+ background-repeat:no-repeat;
+ background-position:right;
+ color:#364D7C;
+ font-size: 8pt;
+}
+
+
+div.summary
+{
+ float: right;
+ font-size: 8pt;
+ padding-right: 5px;
+ width: 50%;
+ text-align: right;
+}
+
+div.summary a
+{
+ white-space: nowrap;
+}
+
+div.ingroups
+{
+ font-size: 8pt;
+ width: 50%;
+ text-align: left;
+}
+
+div.ingroups a
+{
+ white-space: nowrap;
+}
+
+div.header
+{
+ background-image:url('nav_h.png');
+ background-repeat:repeat-x;
+ background-color: #F9FAFC;
+ margin: 0px;
+ border-bottom: 1px solid #C4CFE5;
+}
+
+div.headertitle
+{
+ padding: 5px 5px 5px 7px;
+}
+
+dl
+{
+ padding: 0 0 0 10px;
+}
+
+/* dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug */
+dl.section
+{
+ margin-left: 0px;
+ padding-left: 0px;
+}
+
+dl.note
+{
+ margin-left:-7px;
+ padding-left: 3px;
+ border-left:4px solid;
+ border-color: #D0C000;
+}
+
+dl.warning, dl.attention
+{
+ margin-left:-7px;
+ padding-left: 3px;
+ border-left:4px solid;
+ border-color: #FF0000;
+}
+
+dl.pre, dl.post, dl.invariant
+{
+ margin-left:-7px;
+ padding-left: 3px;
+ border-left:4px solid;
+ border-color: #00D000;
+}
+
+dl.deprecated
+{
+ margin-left:-7px;
+ padding-left: 3px;
+ border-left:4px solid;
+ border-color: #505050;
+}
+
+dl.todo
+{
+ margin-left:-7px;
+ padding-left: 3px;
+ border-left:4px solid;
+ border-color: #00C0E0;
+}
+
+dl.test
+{
+ margin-left:-7px;
+ padding-left: 3px;
+ border-left:4px solid;
+ border-color: #3030E0;
+}
+
+dl.bug
+{
+ margin-left:-7px;
+ padding-left: 3px;
+ border-left:4px solid;
+ border-color: #C08050;
+}
+
+dl.section dd {
+ margin-bottom: 6px;
+}
+
+
+#projectlogo
+{
+ text-align: center;
+ vertical-align: bottom;
+ border-collapse: separate;
+}
+
+#projectlogo img
+{
+ border: 0px none;
+}
+
+#projectname
+{
+ font: 300% Tahoma, Arial,sans-serif;
+ margin: 0px;
+ padding: 2px 0px;
+}
+
+#projectbrief
+{
+ font: 120% Tahoma, Arial,sans-serif;
+ margin: 0px;
+ padding: 0px;
+}
+
+#projectnumber
+{
+ font: 50% Tahoma, Arial,sans-serif;
+ margin: 0px;
+ padding: 0px;
+}
+
+#titlearea
+{
+ padding: 0px;
+ margin: 0px;
+ width: 100%;
+ border-bottom: 1px solid #5373B4;
+}
+
+.image
+{
+ text-align: center;
+}
+
+.dotgraph
+{
+ text-align: center;
+}
+
+.mscgraph
+{
+ text-align: center;
+}
+
+.caption
+{
+ font-weight: bold;
+}
+
+div.zoom
+{
+ border: 1px solid #90A5CE;
+}
+
+dl.citelist {
+ margin-bottom:50px;
+}
+
+dl.citelist dt {
+ color:#334975;
+ float:left;
+ font-weight:bold;
+ margin-right:10px;
+ padding:5px;
+}
+
+dl.citelist dd {
+ margin:2px 0;
+ padding:5px 0;
+}
+
+div.toc {
+ padding: 14px 25px;
+ background-color: #F4F6FA;
+ border: 1px solid #D8DFEE;
+ border-radius: 7px 7px 7px 7px;
+ float: right;
+ height: auto;
+ margin: 0 20px 10px 10px;
+ width: 200px;
+}
+
+div.toc li {
+ background: url("bdwn.png") no-repeat scroll 0 5px transparent;
+ font: 10px/1.2 Verdana,DejaVu Sans,Geneva,sans-serif;
+ margin-top: 5px;
+ padding-left: 10px;
+ padding-top: 2px;
+}
+
+div.toc h3 {
+ font: bold 12px/1.2 Arial,FreeSans,sans-serif;
+ color: #4665A2;
+ border-bottom: 0 none;
+ margin: 0;
+}
+
+div.toc ul {
+ list-style: none outside none;
+ border: medium none;
+ padding: 0px;
+}
+
+div.toc li.level1 {
+ margin-left: 0px;
+}
+
+div.toc li.level2 {
+ margin-left: 15px;
+}
+
+div.toc li.level3 {
+ margin-left: 30px;
+}
+
+div.toc li.level4 {
+ margin-left: 45px;
+}
+
+.inherit_header {
+ font-weight: bold;
+ color: gray;
+ cursor: pointer;
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+.inherit_header td {
+ padding: 6px 0px 2px 5px;
+}
+
+.inherit {
+ display: none;
+}
+
+tr.heading h2 {
+ margin-top: 12px;
+ margin-bottom: 4px;
+}
+
+@media print
+{
+ #top { display: none; }
+ #side-nav { display: none; }
+ #nav-path { display: none; }
+ body { overflow:visible; }
+ h1, h2, h3, h4, h5, h6 { page-break-after: avoid; }
+ .summary { display: none; }
+ .memitem { page-break-inside: avoid; }
+ #doc-content
+ {
+ margin-left:0 !important;
+ height:auto !important;
+ width:auto !important;
+ overflow:inherit;
+ display:inline;
+ }
}
diff --git a/doc/m4/ax_python.m4 b/doc/m4/ax_python.m4
new file mode 100644
index 00000000..f9a51359
--- /dev/null
+++ b/doc/m4/ax_python.m4
@@ -0,0 +1,97 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_python.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_PYTHON
+#
+# DESCRIPTION
+#
+# This macro does a complete Python development environment check.
+#
+# It recurses through several python versions (from 2.1 to 2.6 in this
+# version), looking for an executable. When it finds an executable, it
+# looks to find the header files and library.
+#
+# It sets PYTHON_BIN to the name of the python executable,
+# PYTHON_INCLUDE_DIR to the directory holding the header files, and
+# PYTHON_LIB to the name of the Python library.
+#
+# This macro calls AC_SUBST on PYTHON_BIN (via AC_CHECK_PROG),
+# PYTHON_INCLUDE_DIR and PYTHON_LIB.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Michael Tindal
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 9
+
+AC_DEFUN([AX_PYTHON],
+[AC_MSG_CHECKING(for python build information)
+AC_MSG_RESULT([])
+for python in python3.3 python3.2 python3.1 python3.0 python2.7 python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python; do
+AC_CHECK_PROGS(PYTHON_BIN, [$python])
+ax_python_bin=$PYTHON_BIN
+if test x$ax_python_bin != x; then
+ AC_CHECK_LIB($ax_python_bin, main, ax_python_lib=$ax_python_bin, ax_python_lib=no)
+ AC_CHECK_HEADER([$ax_python_bin/Python.h],
+ [[ax_python_header=`locate $ax_python_bin/Python.h | sed -e s,/Python.h,,`]],
+ ax_python_header=no)
+ if test x$ax_python_lib != xno; then
+ if test x$ax_python_header != xno; then
+ break;
+ fi
+ fi
+fi
+done
+if test x$ax_python_bin = x; then
+ ax_python_bin=no
+fi
+if test x$ax_python_header = x; then
+ ax_python_header=no
+fi
+if test x$ax_python_lib = x; then
+ ax_python_lib=no
+fi
+
+AC_MSG_RESULT([ results of the Python check:])
+AC_MSG_RESULT([ Binary: $ax_python_bin])
+AC_MSG_RESULT([ Library: $ax_python_lib])
+AC_MSG_RESULT([ Include Dir: $ax_python_header])
+
+if test x$ax_python_header != xno; then
+ PYTHON_INCLUDE_DIR=$ax_python_header
+ AC_SUBST(PYTHON_INCLUDE_DIR)
+fi
+if test x$ax_python_lib != xno; then
+ PYTHON_LIB=$ax_python_lib
+ AC_SUBST(PYTHON_LIB)
+fi
+])dnl
diff --git a/doc/resolve-asciidoc-refs.py b/doc/resolve-asciidoc-refs.py
new file mode 100755
index 00000000..54187473
--- /dev/null
+++ b/doc/resolve-asciidoc-refs.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+import fileinput
+import re
+import sys
+
+refs = {}
+complete_file = ""
+
+for line in open(sys.argv[1], 'r'):
+ complete_file += line
+
+for m in re.findall('\[\[(.+)\]\]\n=+ ([^\n]+)', complete_file):
+ ref, title = m
+ refs["<<" + ref + ">>"] = "<<" + ref + ", " + title + ">>"
+
+def translate(match):
+ try:
+ return refs[match.group(0)]
+ except KeyError:
+ return ""
+
+rc = re.compile('|'.join(map(re.escape, sorted(refs, reverse=True))))
+for line in open(sys.argv[1], 'r'):
+ print rc.sub(translate, line),
diff --git a/doc/route.txt b/doc/route.txt
new file mode 100644
index 00000000..d9f88e13
--- /dev/null
+++ b/doc/route.txt
@@ -0,0 +1,1889 @@
+////
+ vim.syntax: asciidoc
+
+ Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+////
+
+Routing Family Netlink Library (libnl-route)
+============================================
+Thomas Graf <tgraf@suug.ch>
+3.1, Aug 11 2011:
+
+== Introduction
+
+This library provides APIs to the kernel interfaces of the routing family.
+
+
+NOTE: Work in progress.
+
+== Addresses
+
+[[route_link]]
+== Links (Network Devices)
+
+The link configuration interface is part of the +NETLINK_ROUTE+ protocol
+family and implements the following netlink message types:
+
+- View and modify the configuration of physical and virtual network devices.
+- Create and delete virtual network devices (e.g. dummy devices, VLAN devices,
+ tun devices, bridging devices, ...)
+- View and modify per link network configuration settings (e.g.
+ +net.ipv6.conf.eth0.accept_ra+, +net.ipv4.conf.eth1.forwarding+, ...)
+
+.Naming Convention (network device, link, interface)
+
+In networking several terms are commonly used to refer to network devices.
+While they have distinct meanings they have been used interchangeably in
+the past. Within the Linux kernel, the term _network device_ or _netdev_ is
+commonly used In user space the term _network interface_ is very common.
+The routing netlink protocol uses the term _link_ and so does the _iproute2_
+utility and most routing daemons.
+
+=== Netlink Protocol
+
+This section describes the protocol semantics of the netlink based link
+configuration interface. The following messages are defined:
+
+[options="header", cols="1,2,2"]
+|==============================================================================
+| Message Type | User -> Kernel | Kernel -> User
+| +RTM_NEWLINK+ | Create or update virtual network device
+| Reply to +RTM_GETLINK+ request or notification of link added or updated
+| +RTM_DELLINK+ | Delete virtual network device
+| Notification of link deleted or disappeared
+| +RTM_GETLINK+ | Retrieve link configuration and statistics |
+| +RTM_SETLINK+ | Modify link configuration |
+|==============================================================================
+
+See link:core.html#core_msg_types[Netlink Library - Message Types] for more
+information on common semantics of these message types.
+
+==== Link Message Format
+
+All netlink link messages share a common header (+struct ifinfomsg+) which
+is appended after the netlink header (+struct nlmsghdr+).
+
+image:ifinfomsg.png["Link Message Header"]
+
+The meaning of each field may differ depending on the message type. A
++struct ifinfomsg+ is defined in +<linux/rtnetlink.h>+ to represent the
+header.
+
+Address Family (8bit)::
+The address family is usually set to +AF_UNSPEC+ but may be specified in
++RTM_GETLINK+ requests to limit the returned links to a specific address
+family.
+
+Link Layer Type (16bit)::
+Currently only used in kernel->user messages to report the link layer type
+of a link. The value corresponds to the +ARPHRD_*+ defines found in
++<linux/if_arp.h>+. Translation from/to strings can be done using the
+functions nl_llproto2str()/nl_str2llproto().
+
+Link Index (32bit)::
+Carries the interface index and is used to identify existing links.
+
+Flags (32bit)::
+In kernel->user messages the value of this field represents the current
+state of the link flags. In user->kernel messages this field is used to
+change flags or set the initial flag state of new links. Note that in order
+to change a flag, the flag must also be set in the _Flags Change Mask_ field.
+
+Flags Change Mask (32bit)::
+The primary use of this field is to specify a mask of flags that should be
+changed based on the value of the _Flags_ field. A special meaning is given
+to this field when present in link notifications, see TODO.
+
+Attributes (variable)::
+All link message types may carry netlink attributes. They are defined in the
+header file <linux/if_link.h> and share the prefix +IFLA_+.
+
+==== Link Message Types
+
+.RTM_GETLINK (user->kernel)
+
+Lookup link by 1. interface index or 2. link name (+IFLA_IFNAME+) and return
+a single +RTM_NEWLINK+ message containing the link configuration and statistics
+or a netlink error message if no such link was found.
+
+*Parameters:*
+
+* *Address family*
+** If the address family is set to +PF_BRIDGE+, only bridging devices will be
+ returned.
+** If the address family is set to +PF_INET6+, only ipv6 enabled devices will
+ be returned.
+
+*Flags:*
+
+* +NLM_F_DUMP+ If set, all links will be returned in form of a multipart
+ message.
+
+*Returns:*
+
+* +EINVAL+ if neither interface nor link name are set
+* +ENODEV+ if no link was found
+* +ENOBUFS+ if allocation failed
+
+.RTM_NEWLINK (user->kernel)
+
+Creates a new or updates an existing link. Only virtual links may be created
+but all links may be updated.
+
+*Flags:*
+
+- +NLM_F_CREATE+ Create link if it does not exist
+- +NLM_F_EXCL+ Return +EEXIST+ if link already exists
+
+*Returns:*
+
+- +EINVAL+ malformed message or invalid configuration parameters
+- +EAFNOSUPPORT+ if a address family specific configuration (+IFLA_AF_SPEC+)
+ is not supported.
+- +EOPNOTSUPP+ if the link does not support modification of parameters
+- +EEXIST+ if +NLM_F_EXCL+ was set and the link exists alraedy
+- +ENODEV+ if the link does not exist and +NLM_F_CREATE+ is not set
+
+.RTM_NEWLINK (kernel->user)
+
+This message type is used in reply to a +RTM_GETLINK+ request and carries
+the configuration and statistics of a link. If multiple links need to
+be sent, the messages will be sent in form of a multipart message.
+
+The message type is also used for notifications sent by the kernel to the
+multicast group +RTNLGRP_LINK+ to inform about various link events. It is
+therefore recommended to always use a separate link socket for link
+notifications in order to separate between the two message types.
+
+TODO: document how to detect different notifications
+
+.RTM_DELLINK (user->kernel)
+
+Lookup link by 1. interface index or 2. link name (+IFLA_IFNAME+) and delete
+the virtual link.
+
+*Returns:*
+
+* +EINVAL+ if neither interface nor link name are set
+* +ENODEV+ if no link was found
+* +ENOTSUPP+ if the operation is not supported (not a virtual link)
+
+.RTM_DELLINK (kernel->user)
+
+Notification sent by the kernel to the multicast group +RTNLGRP_LINK+ when
+
+a. a network device was unregistered (change == ~0)
+b. a bridging device was deleted (address family will be +PF_BRIDGE+)
+
+=== Get / List
+
+[[link_list]]
+==== Get list of links
+
+To retrieve the list of links in the kernel, allocate a new link cache
+using +rtnl_link_alloc_cache()+ to hold the links. It will automatically
+construct and send a +RTM_GETLINK+ message requesting a dump of all links
+from the kernel and feed the returned +RTM_NEWLINK+ to the internal link
+message parser which adds the returned links to the cache.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+int rtnl_link_alloc_cache(struct nl_sock *sk, int family, struct nl_cache **result)
+-----
+
+The cache will contain link objects (+struct rtnl_link+, see <<link_object>>)
+and can be accessed using the standard cache functions. By setting the
++family+ parameter to an address familly other than +AF_UNSPEC+, the resulting
+cache will only contain links supporting the specified address family.
+
+The following direct search functions are provided to search by interface
+index and by link name:
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+struct rtnl_link *rtnl_link_get(struct nl_cache *cache, int ifindex);
+struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache, const char *name);
+-----
+
+.Example: Link Cache
+
+[source,c]
+-----
+struct nl_cache *cache;
+struct rtnl_link *link;
+
+if (rtnl_link_alloc_cache(sock, AF_UNSPEC, &cache)) < 0)
+ /* error */
+
+if (!(link = rtnl_link_get_by_name(cache, "eth1")))
+ /* link does not exist */
+
+/* do something with link */
+
+rtnl_link_put(link);
+nl_cache_put(cache);
+-----
+
+[[link_direct_lookup]]
+==== Lookup Single Link (Direct Lookup)
+
+If only a single link is of interest, the link can be looked up directly
+without the use of a link cache using the function +rtnl_link_get_kernel()+.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+int rtnl_link_get_kernel(struct nl_sock *sk, int ifindex, const char *name, struct rtnl_link **result);
+-----
+
+It will construct and send a +RTM_GETLINK+ request using the parameters
+provided and wait for a +RTM_NEWLINK+ or netlink error message sent in
+return. If the link exists, the link is returned as link object
+(see <<link_object>>).
+
+.Example: Direct link lookup
+[source,c]
+-----
+struct rtnl_link *link;
+
+if (rtnl_link_get_kernel(sock, 0, "eth1", &link) < 0)
+ /* error */
+
+/* do something with link */
+
+rtnl_link_put(link);
+-----
+
+NOTE: While using this function can save a substantial amount of bandwidth
+ on the netlink socket, the result will not be cached, subsequent calls
+ to rtnl_link_get_kernel() will always trigger sending a +RTM_GETLINK+
+ request.
+
+[[link_translate_ifindex]]
+==== Translating interface index to link name
+
+Applications which require to translate interface index to a link name or
+vice verase may use the following functions to do so. Both functions require
+a filled link cache to work with.
+
+[source,c]
+-----
+char *rtnl_link_i2name (struct nl_cache *cache, int ifindex, char *dst, size_t len);
+int rtnl_link_name2i (struct nl_cache *cache, const char *name);
+-----
+
+=== Add / Modify
+
+Several types of virtual link can be added on the fly using the function
++rtnl_link_add()+.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+int rtnl_link_add(struct nl_sock *sk, struct rtnl_link *link, int flags);
+-----
+
+=== Delete
+
+The deletion of virtual links such as VLAN devices or dummy devices is done
+using the function +rtnl_link_delete()+. The link passed on to the function
+can be a link from a link cache or it can be construct with the minimal
+attributes needed to identify the link.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+int rtnl_link_delete(struct nl_sock *sk, const struct rtnl_link *link);
+-----
+
+The function will construct and send a +RTM_DELLINK+ request message and
+returns any errors returned by the kernel.
+
+.Example: Delete link by name
+[source,c]
+-----
+struct rtnl_link *link;
+
+if (!(link = rtnl_link_alloc()))
+ /* error */
+
+rtnl_link_set_name(link, "my_vlan");
+
+if (rtnl_link_delete(sock, link) < 0)
+ /* error */
+
+rtnl_link_put(link);
+-----
+
+[[link_object]]
+=== Link Object
+
+A link is represented by the structure +struct rtnl_link+. Instances may be
+created with the function +rtnl_link_alloc()+ or via a link cache (see
+<<link_list>>) and are freed again using the function +rtnl_link_put()+.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+struct rtnl_link *rtnl_link_alloc(void);
+void rtnl_link_put(struct rtnl_link *link);
+-----
+
+[[link_attr_name]]
+==== Name
+The name serves as unique, human readable description of the link. By
+default, links are named based on their type and then enumerated, e.g.
+eth0, eth1, ethn but they may be renamed at any time.
+
+Kernels >= 2.6.11 support identification by link name.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+void rtnl_link_set_name(struct rtnl_link *link, const char *name);
+char *rtnl_link_get_name(struct rtnl_link *link);
+-----
+
+*Accepted link name format:* +[^ /]*+ (maximum length: 15 characters)
+
+[[link_attr_ifindex]]
+==== Interface Index (Identifier)
+The interface index is an integer uniquely identifying a link. If present
+in any link message, it will be used to identify an existing link.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+void rtnl_link_set_ifindex(struct rtnl_link *link, int ifindex);
+int rtnl_link_get_ifindex(struct rtnl_link *link);
+-----
+
+[[link_attr_group]]
+==== Group
+Each link can be assigned a numeric group identifier to group a bunch of links
+together and apply a set of changes to a group instead of just a single link.
+
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+void rtnl_link_set_group(struct rtnl_link *link, uint32_t group);
+uint32_t rtnl_link_get_group(struct rtnl_link *link);
+-----
+
+[[link_attr_address]]
+==== Link Layer Address
+The link layer address (e.g. MAC address).
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+void rtnl_link_set_addr(struct rtnl_link *link, struct nl_addr *addr);
+struct nl_addr *rtnl_link_get_addr(struct rtnl_link *link);
+-----
+
+[[link_attr_broadcast]]
+==== Broadcast Address
+The link layer broadcast address
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+void rtnl_link_set_broadcast(struct rtnl_link *link, struct nl_addr *addr);
+struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *link);
+-----
+
+[[link_attr_mtu]]
+==== MTU (Maximum Transmission Unit)
+The maximum transmission unit specifies the maximum packet size a network
+device can transmit or receive. This value may be lower than the capability
+of the physical network device.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu);
+unsigned int rtnl_link_get_mtu(struct rtnl_link *link);
+-----
+
+[[link_attr_flags]]
+==== Flags
+The flags of a link enable or disable various link features or inform about
+the state of the link.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+void rtnl_link_set_flags(struct rtnl_link *link, unsigned int flags);
+void rtnl_link_unset_flags(struct rtnl_link *link, unsigned int flags);
+unsigned int rtnl_link_get_flags(struct rtnl_link *link);
+-----
+
+[options="compact"]
+[horizontal]
+IFF_UP:: Link is up (administratively)
+IFF_RUNNING:: Link is up and carrier is OK (RFC2863 OPER_UP)
+IFF_LOWER_UP:: Link layer is operational
+IFF_DORMANT:: Driver signals dormant
+IFF_BROADCAST:: Link supports broadcasting
+IFF_MULTICAST:: Link supports multicasting
+IFF_ALLMULTI:: Link supports multicast routing
+IFF_DEBUG:: Tell driver to do debugging (currently unused)
+IFF_LOOPBACK:: Link loopback network
+IFF_POINTOPOINT:: Point-to-point link
+IFF_NOARP:: ARP is not supported
+IFF_PROMISC:: Status of promiscious mode
+IFF_MASTER:: Master of a load balancer (bonding)
+IFF_SLAVE:: Slave to a master link
+IFF_PORTSEL:: Driver supports setting media type (only used by ARM ethernet)
+IFF_AUTOMEDIA:: Link selects port automatically (only used by ARM ethernet)
+IFF_ECHO:: Echo sent packets (testing feature, CAN only)
+IFF_DYNAMIC:: Unused (BSD compatibility)
+IFF_NOTRAILERS:: Unused (BSD compatibility)
+
+To translate a link flag to a link flag name or vice versa:
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+char *rtnl_link_flags2str(int flags, char *buf, size_t size);
+int rtnl_link_str2flags(const char *flag_name);
+-----
+
+[[link_attr_txqlen]]
+==== Transmission Queue Length
+
+The transmission queue holds packets before packets are delivered to
+the driver for transmission. It is usually specified in number of
+packets but the unit may be specific to the link type.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+void rtnl_link_set_txqlen(struct rtnl_link *link, unsigned int txqlen);
+unsigned int rtnl_link_get_txqlen(struct rtnl_link *link);
+-----
+
+[[link_attr_operstate]]
+==== Operational Status
+The operational status has been introduced to provide extended information
+on the link status. Traditionally the link state has been described using
+the link flags +IFF_UP, IFF_RUNNING, IFF_LOWER_UP+, and +IFF_DORMANT+ which
+was no longer sufficient for some link types.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+void rtnl_link_set_operstate(struct rtnl_link *link, uint8_t state);
+uint8_t rtnl_link_get_operstate(struct rtnl_link *link);
+-----
+
+[options="compact"]
+[horizontal]
+IF_OPER_UNKNOWN:: Unknown state
+IF_OPER_NOTPRESENT:: Link not present
+IF_OPER_DOWN:: Link down
+IF_OPER_LOWERLAYERDOWN:: L1 down
+IF_OPER_TESTING:: Testing
+IF_OPER_DORMANT:: Dormant
+IF_OPER_UP:: Link up
+
+Translation of operational status code to string and vice versa:
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+char *rtnl_link_operstate2str(uint8_t state, char *buf, size_t size);
+int rtnl_link_str2operstate(const char *name);
+-----
+
+[[link_attr_mode]]
+==== Mode
+Currently known link modes are:
+
+[options="compact"]
+[horizontal]
+IF_LINK_MODE_DEFAULT:: Default link mode
+IF_LINK_MODE_DORMANT:: Limit upward transition to dormant
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+void rtnl_link_set_linkmode(struct rtnl_link *link, uint8_t mode);
+uint8_t rtnl_link_get_linkmode(struct rtnl_link *link);
+-----
+
+Translation of link mode to string and vice versa:
+
+[source,c]
+-----
+char *rtnl_link_mode2str(uint8_t mode, char *buf, size_t len);
+uint8_t rtnl_link_str2mode(const char *name);
+-----
+
+[[link_attr_alias]]
+==== IfAlias
+Alternative name for the link, primarly used for SNMP IfAlias.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+const char *rtnl_link_get_ifalias(struct rtnl_link *link);
+void rtnl_link_set_ifalias(struct rtnl_link *link, const char *alias);
+-----
+
+*Length limit:* 256
+
+[[link_attr_arptype]]
+==== Hardware Type
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+#include <linux/if_arp.h>
+
+void rtnl_link_set_arptype(struct rtnl_link *link, unsigned int arptype);
+unsigned int rtnl_link_get_arptype(struct rtnl_link *link);
+----
+
+Translation of hardware type to character string and vice versa:
+
+[source,c]
+-----
+#include <netlink/utils.h>
+
+char *nl_llproto2str(int arptype, char *buf, size_t len);
+int nl_str2llproto(const char *name);
+-----
+
+[[link_attr_qdisc]]
+==== Qdisc
+The name of the queueing discipline used by the link is of informational
+nature only. It is a read-only attribute provided by the kernel and cannot
+be modified. The set function is provided solely for the purpose of creating
+link objects to be used for comparison.
+
+For more information on how to modify the qdisc of a link, see section
+<<route_tc>>.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+void rtnl_link_set_qdisc(struct rtnl_link *link, const char *name);
+char *rtnl_link_get_qdisc(struct rtnl_link *link);
+-----
+
+[[link_attr_promiscuity]]
+==== Promiscuity
+The number of subsystem currently depending on the link being promiscuous mode.
+A value of 0 indicates that the link is not in promiscuous mode. It is a
+read-only attribute provided by the kernel and cannot be modified. The set
+function is provided solely for the purpose of creating link objects to be
+used for comparison.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+void rtnl_link_set_promiscuity(struct rtnl_link *link, uint32_t count);
+uint32_t rtnl_link_get_promiscuity(struct rtnl_link *link);
+-----
+
+[[link_num_rxtx_queues]]
+==== RX/TX Queues
+The number of RX/TX queues the link provides. The attribute is writable but
+will only be considered when creating a new network device via netlink.
+
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+void rtnl_link_set_num_tx_queues(struct rtnl_link *link, uint32_t nqueues);
+uint32_t rtnl_link_get_num_tx_queues(struct rtnl_link *link);
+
+void rtnl_link_set_num_rx_queues(struct rtnl_link *link, uint32_t nqueues);
+uint32_t rtnl_link_get_num_rx_queues(struct rtnl_link *link);
+-----
+
+[[link_attr_weight]]
+==== Weight
+This attribute is unused and obsoleted in all recent kernels.
+
+
+[[link_modules]]
+=== Modules
+
+[[link_bonding]]
+==== Bonding
+
+.Example: Add bonding link
+[source,c]
+-----
+#include <netlink/route/link.h>
+
+struct rtnl_link *link;
+
+link = rtnl_link_bond_alloc();
+rtnl_link_set_name(link, "my_bond");
+
+/* requires admin privileges */
+if (rtnl_link_add(sk, link, NLM_F_CREATE) < 0)
+ /* error */
+
+rtnl_link_put(link);
+-----
+
+[[link_vlan]]
+==== VLAN
+
+[source,c]
+-----
+extern char * rtnl_link_vlan_flags2str(int, char *, size_t);
+extern int rtnl_link_vlan_str2flags(const char *);
+
+extern int rtnl_link_vlan_set_id(struct rtnl_link *, int);
+extern int rtnl_link_vlan_get_id(struct rtnl_link *);
+
+extern int rtnl_link_vlan_set_flags(struct rtnl_link *,
+ unsigned int);
+extern int rtnl_link_vlan_unset_flags(struct rtnl_link *,
+ unsigned int);
+extern unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *);
+
+extern int rtnl_link_vlan_set_ingress_map(struct rtnl_link *,
+ int, uint32_t);
+extern uint32_t * rtnl_link_vlan_get_ingress_map(struct rtnl_link *);
+
+extern int rtnl_link_vlan_set_egress_map(struct rtnl_link *,
+ uint32_t, int);
+extern struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *,
+ int *);
+-----
+
+.Example: Add a VLAN device
+[source,c]
+-----
+struct rtnl_link *link;
+int master_index;
+
+/* lookup interface index of eth0 */
+if (!(master_index = rtnl_link_name2i(link_cache, "eth0")))
+ /* error */
+
+/* allocate new link object of type vlan */
+link = rtnl_link_vlan_alloc();
+
+/* set eth0 to be our master device */
+rtnl_link_set_link(link, master_index);
+
+rtnl_link_vlan_set_id(link, 10);
+
+if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
+ /* error */
+
+rtnl_link_put(link);
+-----
+
+[[link_macvlan]]
+==== MACVLAN
+
+[source,c]
+-----
+extern struct rtnl_link *rtnl_link_macvlan_alloc(void);
+
+extern int rtnl_link_is_macvlan(struct rtnl_link *);
+
+extern char * rtnl_link_macvlan_mode2str(int, char *, size_t);
+extern int rtnl_link_macvlan_str2mode(const char *);
+
+extern char * rtnl_link_macvlan_flags2str(int, char *, size_t);
+extern int rtnl_link_macvlan_str2flags(const char *);
+
+extern int rtnl_link_macvlan_set_mode(struct rtnl_link *,
+ uint32_t);
+extern uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *);
+
+extern int rtnl_link_macvlan_set_flags(struct rtnl_link *,
+ uint16_t);
+extern int rtnl_link_macvlan_unset_flags(struct rtnl_link *,
+ uint16_t);
+extern uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *);
+-----
+
+.Example: Add a MACVLAN device
+[source,c]
+-----
+struct rtnl_link *link;
+int master_index;
+struct nl_addr* addr;
+
+/* lookup interface index of eth0 */
+if (!(master_index = rtnl_link_name2i(link_cache, "eth0")))
+ /* error */
+
+/* allocate new link object of type macvlan */
+link = rtnl_link_macvlan_alloc();
+
+/* set eth0 to be our master device */
+rtnl_link_set_link(link, master_index);
+
+/* set address of virtual interface */
+addr = nl_addr_build(AF_LLC, ether_aton("00:11:22:33:44:55"), ETH_ALEN);
+rtnl_link_set_addr(link, addr);
+nl_addr_put(addr);
+
+/* set mode of virtual interface */
+rtnl_link_macvlan_set_mode(link, rtnl_link_macvlan_str2mode("bridge"));
+
+if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
+ /* error */
+
+rtnl_link_put(link);
+-----
+
+[[link_vxlan]]
+==== VXLAN
+
+[source,c]
+-----
+extern struct rtnl_link *rtnl_link_vxlan_alloc(void);
+
+extern int rtnl_link_is_vxlan(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_id(struct rtnl_link *, uint32_t);
+extern int rtnl_link_vxlan_get_id(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_vxlan_set_group(struct rtnl_link *, struct nl_addr *);
+extern int rtnl_link_vxlan_get_group(struct rtnl_link *, struct nl_addr **);
+
+extern int rtnl_link_vxlan_set_link(struct rtnl_link *, uint32_t);
+extern int rtnl_link_vxlan_get_link(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_vxlan_set_local(struct rtnl_link *, struct nl_addr *);
+extern int rtnl_link_vxlan_get_local(struct rtnl_link *, struct nl_addr **);
+
+extern int rtnl_link_vxlan_set_ttl(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_ttl(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_tos(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_tos(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_learning(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_learning(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_learning(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_learning(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_ageing(struct rtnl_link *, uint32_t);
+extern int rtnl_link_vxlan_get_ageing(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_vxlan_set_limit(struct rtnl_link *, uint32_t);
+extern int rtnl_link_vxlan_get_limit(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_vxlan_set_port_range(struct rtnl_link *,
+ struct ifla_vxlan_port_range *);
+extern int rtnl_link_vxlan_get_port_range(struct rtnl_link *,
+ struct ifla_vxlan_port_range *);
+
+extern int rtnl_link_vxlan_set_proxy(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_proxy(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_proxy(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_proxy(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_rsc(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_rsc(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_rsc(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_rsc(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_l2miss(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_l2miss(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_l2miss(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_l2miss(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_l3miss(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_l3miss(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_l3miss(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_l3miss(struct rtnl_link *);
+-----
+
+.Example: Add a VXLAN device
+[source,c]
+-----
+struct rtnl_link *link;
+struct nl_addr* addr;
+
+/* allocate new link object of type vxlan */
+link = rtnl_link_vxlan_alloc();
+
+/* set interface name */
+rtnl_link_set_name(link, "vxlan128");
+
+/* set VXLAN network identifier */
+if ((err = rtnl_link_vxlan_set_id(link, 128)) < 0)
+ /* error */
+
+/* set multicast address to join */
+if ((err = nl_addr_parse("239.0.0.1", AF_INET, &addr)) < 0)
+ /* error */
+
+if ((err = rtnl_link_set_group(link, addr)) < 0)
+ /* error */
+
+nl_addr_put(addr);
+
+if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
+ /* error */
+
+rtnl_link_put(link);
+-----
+
+[[link_ipip]]
+==== IPIP
+
+[source,c]
+-----
+extern struct rtnl_link *rtnl_link_ipip_alloc(void);
+extern int rtnl_link_ipip_add(struct nl_sock *sk, const char *name);
+
+extern int rtnl_link_ipip_set_link(struct rtnl_link *link, uint32_t index);
+extern uint32_t rtnl_link_ipip_get_link(struct rtnl_link *link);
+
+extern int rtnl_link_ipip_set_local(struct rtnl_link *link, uint32_t addr);
+extern uint32_t rtnl_link_ipip_get_local(struct rtnl_link *link);
+
+extern int rtnl_link_ipip_set_remote(struct rtnl_link *link, uint32_t addr);
+extern uint32_t rtnl_link_ipip_get_remote(struct rtnl_link *link);
+
+extern int rtnl_link_ipip_set_ttl(struct rtnl_link *link, uint8_t ttl);
+extern uint8_t rtnl_link_ipip_get_ttl(struct rtnl_link *link);
+
+extern int rtnl_link_ipip_set_tos(struct rtnl_link *link, uint8_t tos);
+extern uint8_t rtnl_link_ipip_get_tos(struct rtnl_link *link);
+
+extern int rtnl_link_ipip_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc);
+extern uint8_t rtnl_link_ipip_get_pmtudisc(struct rtnl_link *link);
+
+-----
+
+.Example: Add a ipip tunnel device
+[source,c]
+-----
+struct rtnl_link *link
+struct in_addr addr
+
+/* allocate new link object of type vxlan */
+if(!(link = rtnl_link_ipip_alloc()))
+ /* error */
+
+/* set ipip tunnel name */
+if ((err = rtnl_link_set_name(link, "ipip-tun")) < 0)
+ /* error */
+
+/* set link index */
+if ((err = rtnl_link_ipip_set_link(link, if_index)) < 0)
+ /* error */
+
+/* set local address */
+inet_pton(AF_INET, "192.168.254.12", &addr.s_addr);
+if ((err = rtnl_link_ipip_set_local(link, addr.s_addr)) < 0)
+ /* error */
+
+/* set remote address */
+inet_pton(AF_INET, "192.168.254.13", &addr.s_addr
+if ((err = rtnl_link_ipip_set_remote(link, addr.s_addr)) < 0)
+ /* error */
+
+/* set tunnel ttl */
+if ((err = rtnl_link_ipip_set_ttl(link, 64)) < 0)
+ /* error */
+
+if((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
+ /* error */
+
+rtnl_link_put(link);
+-----
+
+[[link_ipgre]]
+==== IPGRE
+
+[source,c]
+-----
+extern struct rtnl_link *rtnl_link_ipgre_alloc(void);
+extern int rtnl_link_ipgre_add(struct nl_sock *sk, const char *name);
+
+extern int rtnl_link_ipgre_set_link(struct rtnl_link *link, uint32_t index);
+extern uint32_t rtnl_link_ipgre_get_link(struct rtnl_link *link);
+
+extern int rtnl_link_ipgre_set_iflags(struct rtnl_link *link, uint16_t iflags);
+extern uint16_t rtnl_link_get_iflags(struct rtnl_link *link);
+
+extern int rtnl_link_ipgre_set_oflags(struct rtnl_link *link, uint16_t oflags);
+extern uint16_t rtnl_link_get_oflags(struct rtnl_link *link);
+
+extern int rtnl_link_ipgre_set_ikey(struct rtnl_link *link, uint32_t ikey);
+extern uint32_t rtnl_link_get_ikey(struct rtnl_link *link);
+
+extern int rtnl_link_ipgre_set_okey(struct rtnl_link *link, uint32_t okey);
+extern uint32_t rtnl_link_get_okey(struct rtnl_link *link)
+
+extern int rtnl_link_ipgre_set_local(struct rtnl_link *link, uint32_t addr);
+extern uint32_t rtnl_link_ipgre_get_local(struct rtnl_link *link);
+
+extern int rtnl_link_ipgre_set_remote(struct rtnl_link *link, uint32_t addr);
+extern uint32_t rtnl_link_ipgre_get_remote(struct rtnl_link *link);
+
+extern int rtnl_link_ipgre_set_ttl(struct rtnl_link *link, uint8_t ttl);
+extern uint8_t rtnl_link_ipgre_get_ttl(struct rtnl_link *link);
+
+extern int rtnl_link_ipgre_set_tos(struct rtnl_link *link, uint8_t tos);
+extern uint8_t rtnl_link_ipgre_get_tos(struct rtnl_link *link);
+
+extern int rtnl_link_ipgre_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc);
+extern uint8_t rtnl_link_ipgre_get_pmtudisc(struct rtnl_link *link);
+
+-----
+
+.Example: Add a ipgre tunnel device
+[source,c]
+-----
+struct rtnl_link *link
+struct in_addr addr
+
+/* allocate new link object of type vxlan */
+if(!(link = rtnl_link_ipgre_alloc()))
+ /* error */
+
+/* set ipgre tunnel name */
+if ((err = rtnl_link_set_name(link, "ipgre-tun")) < 0)
+ /* error */
+
+/* set link index */
+if ((err = rtnl_link_ipgre_set_link(link, if_index)) < 0)
+ /* error */
+
+/* set local address */
+inet_pton(AF_INET, "192.168.254.12", &addr.s_addr);
+if ((err = rtnl_link_ipgre_set_local(link, addr.s_addr)) < 0)
+ /* error */
+
+/* set remote address */
+inet_pton(AF_INET, "192.168.254.13", &addr.s_addr
+if ((err = rtnl_link_ipgre_set_remote(link, addr.s_addr)) < 0)
+ /* error */
+
+/* set tunnel ttl */
+if ((err = rtnl_link_ipgre_set_ttl(link, 64)) < 0)
+ /* error */
+
+if((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
+ /* error */
+
+rtnl_link_put(link);
+-----
+
+[[link_sit]]
+==== SIT
+
+[source,c]
+-----
+extern struct rtnl_link *rtnl_link_sit_alloc(void);
+extern int rtnl_link_sit_add(struct nl_sock *sk, const char *name);
+
+extern int rtnl_link_sit_set_link(struct rtnl_link *link, uint32_t index);
+extern uint32_t rtnl_link_sit_get_link(struct rtnl_link *link);
+
+extern int rtnl_link_sit_set_iflags(struct rtnl_link *link, uint16_t iflags);
+extern uint16_t rtnl_link_get_iflags(struct rtnl_link *link);
+
+extern int rtnl_link_sit_set_oflags(struct rtnl_link *link, uint16_t oflags);
+extern uint16_t rtnl_link_get_oflags(struct rtnl_link *link);
+
+extern int rtnl_link_sit_set_ikey(struct rtnl_link *link, uint32_t ikey);
+extern uint32_t rtnl_link_get_ikey(struct rtnl_link *link);
+
+extern int rtnl_link_sit_set_okey(struct rtnl_link *link, uint32_t okey);
+extern uint32_t rtnl_link_get_okey(struct rtnl_link *link)
+
+extern int rtnl_link_sit_set_local(struct rtnl_link *link, uint32_t addr);
+extern uint32_t rtnl_link_sit_get_local(struct rtnl_link *link);
+
+extern int rtnl_link_sit_set_remote(struct rtnl_link *link, uint32_t addr);
+extern uint32_t rtnl_link_sit_get_remote(struct rtnl_link *link);
+
+extern int rtnl_link_sit_set_ttl(struct rtnl_link *link, uint8_t ttl);
+extern uint8_t rtnl_link_sit_get_ttl(struct rtnl_link *link);
+
+extern int rtnl_link_sit_set_tos(struct rtnl_link *link, uint8_t tos);
+extern uint8_t rtnl_link_sit_get_tos(struct rtnl_link *link);
+
+extern int rtnl_link_sit_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc);
+extern uint8_t rtnl_link_sit_get_pmtudisc(struct rtnl_link *link);
+
+-----
+
+.Example: Add a sit tunnel device
+[source,c]
+-----
+struct rtnl_link *link
+struct in_addr addr
+
+/* allocate new link object of type vxlan */
+if(!(link = rtnl_link_sit_alloc()))
+ /* error */
+
+/* set sit tunnel name */
+if ((err = rtnl_link_set_name(link, "sit-tun")) < 0)
+ /* error */
+
+/* set link index */
+if ((err = rtnl_link_sit_set_link(link, if_index)) < 0)
+ /* error */
+
+/* set local address */
+inet_pton(AF_INET, "192.168.254.12", &addr.s_addr);
+if ((err = rtnl_link_sit_set_local(link, addr.s_addr)) < 0)
+ /* error */
+
+/* set remote address */
+inet_pton(AF_INET, "192.168.254.13", &addr.s_addr
+if ((err = rtnl_link_sit_set_remote(link, addr.s_addr)) < 0)
+ /* error */
+
+/* set tunnel ttl */
+if ((err = rtnl_link_sit_set_ttl(link, 64)) < 0)
+ /* error */
+
+if((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
+ /* error */
+
+rtnl_link_put(link);
+-----
+
+
+[[link_ipvti]]
+==== IPVTI
+
+[source,c]
+-----
+extern struct rtnl_link *rtnl_link_ipvti_alloc(void);
+extern int rtnl_link_ipvti_add(struct nl_sock *sk, const char *name);
+
+extern int rtnl_link_ipvti_set_link(struct rtnl_link *link, uint32_t index);
+extern uint32_t rtnl_link_ipvti_get_link(struct rtnl_link *link);
+
+extern int rtnl_link_ipvti_set_ikey(struct rtnl_link *link, uint32_t ikey);
+extern uint32_t rtnl_link_get_ikey(struct rtnl_link *link);
+
+extern int rtnl_link_ipvti_set_okey(struct rtnl_link *link, uint32_t okey);
+extern uint32_t rtnl_link_get_okey(struct rtnl_link *link)
+
+extern int rtnl_link_ipvti_set_local(struct rtnl_link *link, uint32_t addr);
+extern uint32_t rtnl_link_ipvti_get_local(struct rtnl_link *link);
+
+extern int rtnl_link_ipvti_set_remote(struct rtnl_link *link, uint32_t addr);
+extern uint32_t rtnl_link_ipvti_get_remote(struct rtnl_link *link);
+
+-----
+
+.Example: Add a ipvti tunnel device
+[source,c]
+-----
+struct rtnl_link *link
+struct in_addr addr
+
+/* allocate new link object of type vxlan */
+if(!(link = rtnl_link_ipvti_alloc()))
+ /* error */
+
+/* set ipvti tunnel name */
+if ((err = rtnl_link_set_name(link, "ipvti-tun")) < 0)
+ /* error */
+
+/* set link index */
+if ((err = rtnl_link_ipvti_set_link(link, if_index)) < 0)
+ /* error */
+
+/* set local address */
+inet_pton(AF_INET, "192.168.254.12", &addr.s_addr);
+if ((err = rtnl_link_ipvti_set_local(link, addr.s_addr)) < 0)
+ /* error */
+
+/* set remote address */
+inet_pton(AF_INET, "192.168.254.13", &addr.s_addr
+if ((err = rtnl_link_ipvti_set_remote(link, addr.s_addr)) < 0)
+ /* error */
+
+if((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0)
+ /* error */
+
+rtnl_link_put(link);
+-----
+
+[[link_ip6tnl]]
+==== IP6TNL
+
+[source,c]
+-----
+extern struct rtnl_link *rtnl_link_ip6_tnl_alloc(void);
+extern int rtnl_link_ip6_tnl_add(struct nl_sock *sk, const char *name);
+
+extern int rtnl_link_ip6_tnl_set_link(struct rtnl_link *link, uint32_t index);
+extern uint32_t rtnl_link_ip6_tnl_get_link(struct rtnl_link *link);
+
+extern int rtnl_link_ip6_tnl_set_local(struct rtnl_link *link, struct in6_addr *);
+extern int rtnl_link_ip6_tnl_get_local(struct rtnl_link *link, struct in6_addr *);
+
+extern int rtnl_link_ip6_tnl_set_remote(struct rtnl_link *link, struct in6_addr *);
+extern int rtnl_link_ip6_tnl_get_remote(struct rtnl_link *link, struct in6_addr *);
+
+extern int rtnl_link_ip6_tnl_set_ttl(struct rtnl_link *link, uint8_t ttl);
+extern uint8_t rtnl_link_ip6_tnl_get_ttl(struct rtnl_link *link);
+
+extern int rtnl_link_ip6_tnl_set_tos(struct rtnl_link *link, uint8_t tos);
+extern uint8_t rtnl_link_ip6_tnl_get_tos(struct rtnl_link *link);
+
+extern int rtnl_link_ip6_tnl_set_encaplimit(struct rtnl_link *link, uint8_t encap_limit);
+extern uint8_t rtnl_link_ip6_tnl_get_encaplimit(struct rtnl_link *link);
+
+extern int rtnl_link_ip6_tnl_set_flags(struct rtnl_link *link, uint32_t flags);
+extern uint32_t rtnl_link_ip6_tnl_get_flags(struct rtnl_link *link);
+
+extern uint32_t rtnl_link_ip6_tnl_get_flowinfo(struct rtnl_link *link);
+extern int rtnl_link_ip6_tnl_set_flowinfo(struct rtnl_link *link, uint32_t flowinfo);
+
+extern int rtnl_link_ip6_tnl_set_proto(struct rtnl_link *link, uint8_t proto);
+extern uint8_t rtnl_link_ip6_tnl_get_proto(struct rtnl_link *link);
+
+-----
+
+.Example: Add a ip6tnl tunnel device
+[source,c]
+-----
+struct rtnl_link *link
+struct in6_addr addr
+
+link = rtnl_link_ip6_tnl_alloc();
+
+rtnl_link_set_name(link, "ip6tnl-tun");
+rtnl_link_ip6_tnl_set_link(link, if_index);
+
+inet_pton(AF_INET6, "2607:f0d0:1002:51::4", &addr);
+rtnl_link_ip6_tnl_set_local(link, &addr);
+
+inet_pton(AF_INET6, "2607:f0d0:1002:52::5", &addr);
+rtnl_link_ip6_tnl_set_remote(link, &addr);
+
+rtnl_link_add(sk, link, NLM_F_CREATE);
+rtnl_link_put(link);
+
+-----
+
+
+== Neighbouring
+
+== Routing
+
+[[route_tc]]
+== Traffic Control
+
+The traffic control architecture allows the queueing and
+prioritization of packets before they are enqueued to the network
+driver. To a limited degree it is also possible to take control of
+network traffic as it enters the network stack.
+
+The architecture consists of three different types of modules:
+
+- *Queueing disciplines (qdisc)* provide a mechanism to enqueue packets
+ in different forms. They may be used to implement fair queueing,
+ prioritization of differentiated services, enforce bandwidth
+ limitations, or even to simulate network behaviour such as packet
+ loss and packet delay. Qdiscs can be classful in which case they
+ allow traffic classes described in the next paragraph to be attached
+ to them.
+
+- *Traffic classes (class)* are supported by several qdiscs to build
+ a tree structure for different types of traffic. Each class may be
+ assigned its own set of attributes such as bandwidth limits or
+ queueing priorities. Some qdiscs even allow borrowing of bandwidth
+ between classes.
+
+- *Classifiers (cls)* are used to decide which qdisc/class the packet
+ should be enqueued to. Different types of classifiers exists,
+ ranging from classification based on protocol header values to
+ classification based on packet priority or firewall marks.
+ Additionally most classifiers support *extended matches (ematch)*
+ which allow extending classifiers by a set of matcher modules, and
+ *actions* which allow classifiers to take actions such as mangling,
+ mirroring, or even rerouting of packets.
+
+.Default Qdisc
+
+The default qdisc used on all network devices is `pfifo_fast`.
+Network devices which do not require a transmit queue such as the
+loopback device do not have a default qdisc attached. The `pfifo_fast`
+qdisc provides three bands to prioritize interactive traffic over bulk
+traffic. Classification is based on the packet priority (diffserv).
+
+image:qdisc_default.png["Default Qdisc"]
+
+.Multiqueue Default Qdisc
+
+If the network device provides multiple transmit queues the `mq`
+qdisc is used by default. It will automatically create a separate
+class for each transmit queue available and will also replace
+the single per device tx lock with a per queue lock.
+
+image:qdisc_mq.png["Multiqueue default Qdisc"]
+
+.Example of a customized classful qdisc setup
+
+The following figure illustrates a possible combination of different
+queueing and classification modules to implement quality of service
+needs.
+
+image:tc_overview.png["Classful Qdisc diagram"]
+
+=== Traffic Control Object
+
+Each type traffic control module (qdisc, class, classifier) is
+represented by its own structure. All of them are based on the traffic
+control object represented by `struct rtnl_tc` which itself is based
+on the generic object `struct nl_object` to make it cacheable. The
+traffic control object contains all attributes, implementation details
+and statistics that are shared by all of the traffic control object
+types.
+
+image:tc_obj.png["struct rtnl_tc hierarchy"]
+
+It is not possible to allocate a `struct rtnl_tc` object, instead the
+actual tc object types must be allocated directly using
+`rtnl_qdisc_alloc()`, `rtnl_class_alloc()`, `rtnl_cls_alloc()` and
+then casted to `struct rtnl_tc` using the `TC_CAST()` macro.
+
+.Usage Example: Allocation, Casting, Freeing
+[source,c]
+-----
+#include <netlink/route/tc.h>
+#include <netlink/route/qdisc.h>
+
+struct rtnl_qdisc *qdisc;
+
+/* Allocation of a qdisc object */
+qdisc = rtnl_qdisc_alloc();
+
+/* Cast the qdisc to a tc object using TC_CAST() to use rtnl_tc_ functions. */
+rtnl_tc_set_mpu(TC_CAST(qdisc), 64);
+
+/* Free the qdisc object */
+rtnl_qdisc_put(qdisc);
+-----
+
+[[tc_attr]]
+==== Attributes
+
+Handle::
+The handle uniquely identifies a tc object and is used to refer
+to other tc objects when constructing tc trees.
++
+[source,c]
+-----
+void rtnl_tc_set_handle(struct rtnl_tc *tc, uint32_t handle);
+uint32_t rtnl_tc_get_handle(struct rtnl_tc *tc);
+-----
+
+Interface Index::
+The interface index specifies the network device the traffic object
+is attached to. The function `rtnl_tc_set_link()` should be preferred
+when setting the interface index. It stores the reference to the link
+object in the tc object and allows retrieving the `mtu` and `linktype`
+automatically.
++
+[source,c]
+-----
+void rtnl_tc_set_ifindex(struct rtnl_tc *tc, int ifindex);
+void rtnl_tc_set_link(struct rtnl_tc *tc, struct rtnl_link *link);
+int rtnl_tc_get_ifindex(struct rtnl_tc *tc);
+-----
+
+Link Type::
+The link type specifies the kind of link that is used by the network
+device (e.g. ethernet, ATM, ...). It is derived automatically when
+the network device is specified with `rtnl_tc_set_link()`.
+The default fallback is `ARPHRD_ETHER` (ethernet).
++
+[source,c]
+-----
+void rtnl_tc_set_linktype(struct rtnl_tc *tc, uint32_t type);
+uint32_t rtnl_tc_get_linktype(struct rtnl_tc *tc);
+-----
+
+Kind::
+The kind character string specifies the type of qdisc, class,
+classifier. Setting the kind results in the module specific
+structure being allocated. Therefore it is imperative to call
+`rtnl_tc_set_kind()` before using any type specific API functions
+such as `rtnl_htb_set_rate()`.
++
+[source,c]
+-----
+int rtnl_tc_set_kind(struct rtnl_tc *tc, const char *kind);
+char *rtnl_tc_get_kind(struct rtnl_tc *tc);
+-----
+
+MPU::
+The Minimum Packet Unit specifies the minimum packet size which will
+be transmitted
+ever be seen by this traffic control object. This value is used for
+rate calculations. Not all object implementations will make use of
+this value. The default value is 0.
++
+[source,c]
+-----
+void rtnl_tc_set_mpu(struct rtnl_tc *tc, uint32_t mpu);
+uint32_t rtnl_tc_get_mpu(struct rtnl_tc *tc);
+-----
+
+MTU::
+The Maximum Transmission Unit specifies the maximum packet size which
+will be transmitted. The value is derived from the link specified
+with `rtnl_tc_set_link()` if not overwritten with `rtnl_tc_set_mtu()`.
+If no link and MTU is specified, the value defaults to 1500
+(ethernet).
++
+[source,c]
+-----
+void rtnl_tc_set_mtu(struct rtnl_tc *tc, uint32_t mtu);
+uint32_t rtnl_tc_get_mtu(struct rtnl_tc *tc);
+-----
+
+Overhead::
+The overhead specifies the additional overhead per packet caused by
+the network layer. This value can be used to correct packet size
+calculations if the packet size on the wire does not match the packet
+size seen by the kernel. The default value is 0.
++
+[source,c]
+-----
+void rtnl_tc_set_overhead(struct rtnl_tc *tc, uint32_t overhead);
+uint32_t rtnl_tc_get_overhead(struct rtnl_tc *tc);
+-----
+
+Parent::
+Specifies the parent traffic control object. The parent is identifier
+by its handle. Special values are:
+- `TC_H_ROOT`: attach tc object directly to network device (root
+ qdisc, root classifier)
+- `TC_H_INGRESS`: same as `TC_H_ROOT` but on the ingress side of the
+ network stack.
++
+[source,c]
+-----
+void rtnl_tc_set_parent(struct rtnl_tc *tc, uint32_t parent);
+uint32_t rtnl_tc_get_parent(struct rtnl_tc *tc);
+-----
+
+Statistics::
+Generic statistics, see <<tc_stats>> for additional information.
++
+[source,c]
+-----
+uint64_t rtnl_tc_get_stat(struct rtnl_tc *tc, enum rtnl_tc_stat id);
+-----
+
+[[tc_stats]]
+==== Accessing Statistics
+
+The traffic control object holds a set of generic statistics. Not all
+traffic control modules will make use of all of these statistics. Some
+modules may provide additional statistics via their own APIs.
+
+.Statistic identifiers `(enum rtnl_tc_stat)`
+[cols="m,,", options="header", frame="topbot"]
+|====================================================================
+| ID | Type | Description
+| RTNL_TC_PACKETS | Counter | Total # of packets transmitted
+| RTNL_TC_BYTES | Counter | Total # of bytes transmitted
+| RTNL_TC_RATE_BPS | Rate | Current bytes/s rate
+| RTNL_TC_RATE_PPS | Rate | Current packets/s rate
+| RTNL_TC_QLEN | Rate | Current length of the queue
+| RTNL_TC_BACKLOG | Rate | # of packets currently backloged
+| RTNL_TC_DROPS | Counter | # of packets dropped
+| RTNL_TC_REQUEUES | Counter | # of packets requeued
+| RTNL_TC_OVERLIMITS | Counter | # of packets that exceeded the limit
+|====================================================================
+
+NOTE: `RTNL_TC_RATE_BPS` and `RTNL_TC_RATE_PPS` only return meaningful
+ values if a rate estimator has been configured.
+
+.Usage Example: Retrieving tc statistics
+[source,c]
+-------
+#include <netlink/route/tc.h>
+
+uint64_t drops, qlen;
+
+drops = rtnl_tc_get_stat(TC_CAST(qdisc), RTNL_TC_DROPS);
+qlen = rtnl_tc_get_stat(TC_CAST(qdisc), RTNL_TC_QLEN);
+-------
+
+==== Rate Table Calculations
+
+[[tc_qdisc]]
+=== Queueing Discipline (qdisc)
+
+.Classless Qdisc
+
+The queueing discipline (qdisc) is used to implement fair queueing,
+priorization or rate control. It provides a _enqueue()_ and
+_dequeue()_ operation. Whenever a network packet leaves the networking
+stack over a network device, be it a physical or virtual device, it
+will be enqueued to a qdisc unless the device is queueless. The
+_enqueue()_ operation is followed by an immediate call to _dequeue()_
+for the same qdisc to eventually retrieve a packet which can be
+scheduled for transmission by the driver. Additionally, the networking
+stack runs a watchdog which polls the qdisc regularly to dequeue and
+send packets even if no new packets are being enqueued.
+
+This additional watchdog is required due to the fact that qdiscs may
+hold on to packets and not return any packets upon _dequeue()_ in
+order to enforce bandwidth restrictions.
+
+image:classless_qdisc_nbands.png[alt="Multiband Qdisc", float="right"]
+
+The figure illustrates a trivial example of a classless qdisc
+consisting of three bands (queues). Use of multiple bands is a common
+technique in qdiscs to implement fair queueing between flows or
+prioritize differentiated services.
+
+Classless qdiscs can be regarded as a blackbox, their inner workings
+can only be steered using the configuration parameters provided by the
+qdisc. There is no way of taking influence on the structure of its
+internal queues itself.
+
+.Classful Qdisc
+
+Classful qdiscs allow for the queueing structure and classification
+process to be created by the user.
+
+image:classful_qdisc.png["Classful Qdisc"]
+
+The figure above shows a classful qdisc with a classifier attached to
+it which will make the decision whether to enqueue a packet to traffic
+class +1:1+ or +1:2+. Unlike with classless qdiscs, classful qdiscs
+allow the classification process and the structure of the queues to be
+defined by the user. This allows for complex traffic class rules to
+be applied.
+
+.List of Qdisc Implementations
+[options="header", frame="topbot", cols="2,1^,8"]
+|======================================================================
+| Qdisc | Classful | Description
+| ATM | Yes | FIXME
+| Blackhole | No | This qdisc will drop all packets passed to it.
+| CBQ | Yes |
+The CBQ (Class Based Queueing) is a classful qdisc which allows
+creating traffic classes and enforce bandwidth limitations for each
+class.
+| DRR | Yes |
+The DRR (Deficit Round Robin) scheduler is a classful qdisc
+impelemting fair queueing. Each class is assigned a quantum specyfing
+the maximum number of bytes that can be served per round. Unused
+quantum at the end of the round is carried over to the next round.
+| DSMARK | Yes | FIXME
+| FIFO | No | FIXME
+| GRED | No | FIXME
+| HFSC | Yes | FIXME
+| HTB | Yes | FIXME
+| mq | Yes | FIXME
+| multiq | Yes | FIXME
+| netem | No | FIXME
+| Prio | Yes | FIXME
+| RED | Yes | FIXME
+| SFQ | Yes | FIXME
+| TBF | Yes | FIXME
+| teql | No | FIXME
+|======================================================================
+
+
+.QDisc API Overview
+[cols="a,a", options="header", frame="topbot"]
+|====================================================================
+| Attribute | C Interface
+|
+Allocation / Freeing::
+|
+[source,c]
+-----
+struct rtnl_qdisc *rtnl_qdisc_alloc(void);
+void rtnl_qdisc_put(struct rtnl_qdisc *qdisc);
+-----
+|
+Addition::
+|
+[source,c]
+-----
+int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
+ struct nl_msg **result);
+int rtnl_qdisc_add(struct nl_sock *sock, struct rtnl_qdisc *qdisc,
+ int flags);
+-----
+|
+Modification::
+|
+[source,c]
+-----
+int rtnl_qdisc_build_change_request(struct rtnl_qdisc *old,
+ struct rtnl_qdisc *new,
+ struct nl_msg **result);
+int rtnl_qdisc_change(struct nl_sock *sock, struct rtnl_qdisc *old,
+ struct rtnl_qdisc *new);
+-----
+|
+Deletion::
+|
+[source,c]
+-----
+int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
+ struct nl_msg **result);
+int rtnl_qdisc_delete(struct nl_sock *sock, struct rtnl_qdisc *qdisc);
+-----
+|
+Cache::
+|
+[source,c]
+-----
+int rtnl_qdisc_alloc_cache(struct nl_sock *sock,
+ struct nl_cache **cache);
+struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int, uint32_t);
+
+struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *, int, uint32_t);
+-----
+|====================================================================
+
+[[qdisc_get]]
+==== Retrieving Qdisc Configuration
+
+The function rtnl_qdisc_alloc_cache() is used to retrieve the current
+qdisc configuration in the kernel. It will construct a +RTM_GETQDISC+
+netlink message, requesting the complete list of qdiscs configured in
+the kernel.
+
+[source,c]
+-------
+#include <netlink/route/qdisc.h>
+
+struct nl_cache *all_qdiscs;
+
+if (rtnl_link_alloc_cache(sock, &all_qdiscs) < 0)
+ /* error while retrieving qdisc cfg */
+-------
+
+The cache can be accessed using the following functions:
+
+- Search qdisc with matching ifindex and handle:
++
+[source,c]
+--------
+struct rtnl_qdisc *rtnl_qdisc_get(struct nl_cache *cache, int ifindex, uint32_t handle);
+--------
+- Search qdisc with matching ifindex and parent:
++
+[source,c]
+--------
+struct rtnl_qdisc *rtnl_qdisc_get_by_parent(struct nl_cache *cache, int ifindex , uint32_t parent);
+--------
+- Or any of the generic cache functions (e.g. nl_cache_search(), nl_cache_dump(), etc.)
+
+.Example: Search and print qdisc
+[source,c]
+-------
+struct rtnl_qdisc *qdisc;
+int ifindex;
+
+ifindex = rtnl_link_get_ifindex(eth0_obj);
+
+/* search for qdisc on eth0 with handle 1:0 */
+if (!(qdisc = rtnl_qdisc_get(all_qdiscs, ifindex, TC_HANDLE(1, 0))))
+ /* no such qdisc found */
+
+nl_object_dump(OBJ_CAST(qdisc), NULL);
+
+rtnl_qdisc_put(qdisc);
+-------
+
+[[qdisc_add]]
+==== Adding a Qdisc
+
+In order to add a new qdisc to the kernel, a qdisc object needs to be
+allocated. It will hold all attributes of the new qdisc.
+
+[source,c]
+-----
+#include <netlink/route/qdisc.h>
+
+struct rtnl_qdisc *qdisc;
+
+if (!(qdisc = rtnl_qdisc_alloc()))
+ /* OOM error */
+-----
+
+The next step is to specify all generic qdisc attributes using the tc
+object interface described in the section <<tc_attr>>.
+
+The following attributes must be specified:
+- IfIndex
+- Parent
+- Kind
+
+[source,c]
+-----
+/* Attach qdisc to device eth0 */
+rtnl_tc_set_link(TC_CAST(qdisc), eth0_obj);
+
+/* Make this the root qdisc */
+rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT);
+
+/* Set qdisc identifier to 1:0, if left unspecified, a handle will be generated by the kernel. */
+rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(1, 0));
+
+/* Make this a HTB qdisc */
+rtnl_tc_set_kind(TC_CAST(qdisc), "htb");
+-----
+
+After specyfing the qdisc kind (rtnl_tc_set_kind()) the qdisc type
+specific interface can be used to set attributes which are specific
+to the respective qdisc implementations:
+
+[source,c]
+------
+/* HTB feature: Make unclassified packets go to traffic class 1:5 */
+rtnl_htb_set_defcls(qdisc, TC_HANDLE(1, 5));
+------
+
+Finally, the qdisc is ready to be added and can be passed on to the
+function rntl_qdisc_add() which takes care of constructing a netlink
+message requesting the addition of the new qdisc, sends the message to
+the kernel and waits for the response by the kernel. The function
+returns 0 if the qdisc has been added or updated successfully or a
+negative error code if an error occured.
+
+CAUTION: The kernel operation for updating and adding a qdisc is the
+ same. Therefore when calling rtnl_qdisc_add() any existing
+ qdisc with matching handle will be updated unless the flag
+ NLM_F_EXCL is specified.
+
+The following flags may be specified:
+[horizontal]
+NLM_F_CREATE:: Create qdisc if it does not exist, otherwise
+ -NLE_OBJ_NOTFOUND is returned.
+NLM_F_REPLACE:: If another qdisc is already attached to the same
+ parent and their handles mismatch, replace the qdisc
+ instead of returning -EEXIST.
+NLM_F_EXCL:: Return -NLE_EXISTS if a qdisc with matching handles
+ exists already.
+
+WARNING: The function rtnl_qdisc_add() requires administrator
+ privileges.
+
+[source,c]
+------
+/* Submit request to kernel and wait for response */
+err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE);
+
+/* Return the qdisc object to free memory resources */
+rtnl_qdisc_put(qdisc);
+
+if (err < 0) {
+ fprintf(stderr, "Unable to add qdisc: %s\n", nl_geterror(err));
+ return err;
+}
+------
+
+==== Deleting a qdisc
+
+[source,c]
+------
+#include <netlink/route/qdisc.h>
+
+struct rtnl_qdisc *qdisc;
+
+qdisc = rtnl_qdisc_alloc();
+
+rtnl_tc_set_link(TC_CAST(qdisc), eth0_obj);
+rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT);
+
+rtnl_qdisc_delete(sock, qdisc)
+
+rtnl_qdisc_put(qdisc);
+------
+
+WARNING: The function rtnl_qdisc_delete() requires administrator
+ privileges.
+
+
+[[qdisc_htb]]
+==== HTB - Hierarchical Token Bucket
+
+.HTB Qdisc Attributes
+
+Default Class::
+The default class is the fallback class to which all traffic which
+remained unclassified is directed to. If no default class or an
+invalid default class is specified, packets are transmitted directly
+to the next layer (direct transmissions).
++
+[source,c]
+-----
+uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *qdisc);
+int rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls);
+-----
+
+Rate to Quantum (r2q)::
+TODO
++
+[source,c]
+-----
+uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *qdisc);
+int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum);
+-----
+
+
+.HTB Class Attributes
+
+Priority::
++
+[source,c]
+-----
+uint32_t rtnl_htb_get_prio(struct rtnl_class *class);
+int rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio);
+-----
+
+Rate::
+The rate (bytes/s) specifies the maximum bandwidth an invidivual class
+can use without borrowing. The rate of a class should always be greater
+or erqual than the rate of its children.
++
+[source,c]
+-----
+uint32_t rtnl_htb_get_rate(struct rtnl_class *class);
+int rtnl_htb_set_rate(struct rtnl_class *class, uint32_t ceil);
+-----
+
+Ceil Rate::
+The ceil rate specifies the maximum bandwidth an invidivual class
+can use. This includes bandwidth that is being borrowed from other
+classes. Ceil defaults to the class rate implying that by default
+the class will not borrow. The ceil rate of a class should always
+be greater or erqual than the ceil rate of its children.
++
+[source,c]
+-----
+uint32_t rtnl_htb_get_ceil(struct rtnl_class *class);
+int rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil);
+-----
+
+Burst::
+TODO
++
+[source,c]
+-----
+uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *class);
+int rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t burst);
+-----
+
+Ceil Burst::
+TODO
++
+[source,c]
+-----
+uint32_t rtnl_htb_get_bbuffer(struct rtnl_class *class);
+int rtnl_htb_set_bbuffer(struct rtnl_class *class, uint32_t burst);
+-----
+
+Quantum::
+TODO
++
+[source,c]
+-----
+int rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum);
+-----
+
+extern int rtnl_htb_set_cbuffer(struct rtnl_class *, uint32_t);
+
+
+
+
+[[tc_class]]
+=== Class
+
+[options="header", cols="s,a,a,a,a"]
+|=======================================================================
+| | UNSPEC | TC_H_ROOT | 0:pY | pX:pY
+| UNSPEC 3+^|
+[horizontal]
+qdisc =:: root-qdisc
+class =:: root-qdisc:0
+|
+[horizontal]
+qdisc =:: pX:0
+class =:: pX:0
+| 0:hY 3+^|
+[horizontal]
+qdisc =:: root-qdisc
+class =:: root-qdisc:hY
+|
+[horizontal]
+qdisc =:: pX:0
+class =:: pX:hY
+| hX:hY 3+^|
+[horizontal]
+qdisc =:: hX:
+class =:: hX:hY
+|
+if pX != hX
+ return -EINVAL
+[horizontal]
+qdisc =:: hX:
+class =:: hX:hY
+|=======================================================================
+
+[[tc_cls]]
+=== Classifier (cls)
+
+TODO
+
+[[tc_classid_mngt]]
+=== ClassID Management
+
+TODO
+
+[[tc_pktloc]]
+=== Packet Location Aliasing (pktloc)
+
+TODO
+
+[[tc_api]]
+=== Traffic Control Module API
+
+TODO
diff --git a/doc/src/hidden.c b/doc/src/hidden.c
new file mode 100644
index 00000000..a63621d7
--- /dev/null
+++ b/doc/src/hidden.c
@@ -0,0 +1,42 @@
+/**
+ * \cond skip
+ * vim:syntax=doxygen
+ * \endcond
+
+\page auto_ack_warning Disabling Auto-ACK
+
+\attention Disabling Auto-ACK (nl_socket_disable_auto_ack()) will cause this
+ function to return immediately after sending the netlink message.
+ The function will not wait for an eventual error message. It is
+ the responsibility of the caller to handle any error messages or
+ ACKs returned.
+
+\page pointer_lifetime_warning Pointer Lifetime
+
+\attention The reference counter of the returned object is not incremented.
+ Therefore, the returned pointer is only valid during the lifetime
+ of the parent object. Increment the reference counter if the object
+ is supposed to stay around after the parent object was freed.
+
+\page private_struct Private Structure
+
+\note The definition of this structure is private to allow modification
+ without breaking API. Use the designated accessor functions to
+ access individual object attributes.
+
+\page read_only_attribute Read-Only Attribute
+
+\note The attribute this accessor is modifying is a read-only attribute
+ which can not be modified in the kernel. Any changes to the
+ attribute only have an effect on the local copy of the object. The
+ accessor function is provided solely for the purpose of creating
+ objects for comparison and filtering.
+
+\page low_level_api Low Level API
+
+\note This is a low level API function. A high level function implementing
+ the same functionality with a simplified usage pattern exists. This
+ function is available as an alternative if the default library
+ behaviour is not desirable.
+
+*/
diff --git a/doc/src/toc.c b/doc/src/toc.c
new file mode 100644
index 00000000..a91c2a71
--- /dev/null
+++ b/doc/src/toc.c
@@ -0,0 +1,54 @@
+/**
+ * \cond skip
+ * vim:syntax=doxygen
+ * \endcond
+
+\mainpage
+
+\section main_intro Introduction
+
+libnl is a set of libraries to deal with the netlink protocol and some
+of the high level protocols implemented on top of it. The goal is to
+provide APIs on different levels of abstraction. The core library libnl
+provides a fundamental set of functions to deal with sockets, construct
+messages, and send/receive those messages. Additional high level interfaces
+for several individual netlink protocols are provided in separate
+libraries (e.g. "nl-route", "nl-genl", ...).
+
+The library is designed to ensure that all components are optional, i.e.
+even though the core library provides a caching system which allows to
+easly manage objects of any kind, no application is required to use this
+caching system if it has no need for it.
+
+The library was developed and tested on 2.6.x and 3.x kernel releases. It
+may or may not work with older kernel series. Also, although all netlink
+protocols are required to maintain backwards compatibility, this has not
+always achieved and undesired side effects can occur if a recent libnl
+version is used with a considerably older kernel.
+
+\section main_toc Table of Contents
+
+\section main_trees GIT Trees
+
+\subsection tree_dev Development Tree
+
+@code
+git://git.infradead.org/users/tgr/libnl.git
+@endcode
+- Web: http://git.infradead.org/users/tgr/libnl.git
+
+\section main_website Website
+
+- http://www.infradead.org/~tgr/libnl/
+
+\section main_mailinglist Mailinglist
+
+Please post questions and patches to the libnl mailinglist:
+
+@code
+libnl@lists.infradead.org
+@endcode
+
+- Archives: http://canuck.infradead.org/pipermail/libnl/
+
+*/
diff --git a/doc/stylesheets/asciidoc-manpage.css b/doc/stylesheets/asciidoc-manpage.css
new file mode 100644
index 00000000..45eba236
--- /dev/null
+++ b/doc/stylesheets/asciidoc-manpage.css
@@ -0,0 +1,18 @@
+/* Overrides for manpage documents */
+h1 {
+ padding-top: 0.5em;
+ padding-bottom: 0.5em;
+ border-top: 2px solid silver;
+ border-bottom: 2px solid silver;
+}
+h2 {
+ border-style: none;
+}
+div.sectionbody {
+ margin-left: 3em;
+}
+
+@media print {
+ div#toc { display: none; }
+}
+
diff --git a/doc/stylesheets/asciidoc.css b/doc/stylesheets/asciidoc.css
new file mode 100644
index 00000000..2852168b
--- /dev/null
+++ b/doc/stylesheets/asciidoc.css
@@ -0,0 +1,526 @@
+/* Shared CSS for AsciiDoc xhtml11 and html5 backends */
+
+/* Default font. */
+body {
+ font-family: Georgia,serif;
+}
+
+/* Title font. */
+h1, h2, h3, h4, h5, h6,
+div.title, caption.title,
+thead, p.table.header,
+#toctitle,
+#author, #revnumber, #revdate, #revremark,
+#footer {
+ /* OLD: font-family: Arial,Helvetica,sans-serif; */
+ font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
+}
+
+body {
+ margin: 1em 5% 1em 5%;
+}
+
+a {
+ /* color: blue; */
+ color: #990000;
+ text-decoration: none;
+}
+a:visited {
+ /* color: fuchsia; */
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+em {
+ font-style: italic;
+ /* color: navy; */
+}
+
+strong {
+ font-weight: bold;
+ color: black;
+ /* color: #083194; */
+}
+
+h1, h2, h3, h4, h5, h6 {
+ /* color: #527bbd; */
+ color: #990000;
+ margin-top: 1.2em;
+ margin-bottom: 0.5em;
+ line-height: 1.3;
+}
+
+h1, h2, h3 {
+ border-bottom: 2px solid silver;
+}
+h2 {
+ padding-top: 0.5em;
+}
+h3 {
+ float: left;
+}
+h3 + * {
+ clear: left;
+}
+h5 {
+ font-size: 1.0em;
+}
+
+div.sectionbody {
+ margin-left: 0;
+}
+
+hr {
+ border: 1px solid silver;
+}
+
+p {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+ul, ol, li > p {
+ margin-top: 0;
+}
+ul > li { color: #aaa; }
+ul > li > * { color: black; }
+
+pre {
+ padding: 0;
+ margin: 0;
+}
+
+#author {
+ /* color: #527bbd; */
+ font-weight: bold;
+ font-size: 1.1em;
+}
+#email {
+}
+#revnumber, #revdate, #revremark {
+}
+
+#footer {
+ font-size: small;
+ border-top: 2px solid silver;
+ padding-top: 0.5em;
+ margin-top: 4.0em;
+}
+#footer-text {
+ float: left;
+ padding-bottom: 0.5em;
+}
+#footer-badges {
+ float: right;
+ padding-bottom: 0.5em;
+}
+
+#preamble {
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+}
+div.imageblock, div.exampleblock, div.verseblock,
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
+div.admonitionblock {
+ margin-top: 1.0em;
+ margin-bottom: 1.5em;
+}
+div.admonitionblock {
+ margin-top: 2.0em;
+ margin-bottom: 2.0em;
+ margin-right: 10%;
+ /* color: #606060; */
+}
+
+div.content { /* Block element content. */
+ padding: 0;
+}
+
+/* Block element titles. */
+div.title, caption.title {
+ /* OLD: color: #527bbd; */
+ color: #990000;
+ font-weight: bold;
+ text-align: left;
+ margin-top: 1.0em;
+ margin-bottom: 0.5em;
+}
+div.title + * {
+ margin-top: 0;
+}
+
+td div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content + div.title {
+ margin-top: 0.0em;
+}
+
+div.sidebarblock > div.content {
+ background: #ffffee;
+ border: 1px solid #dddddd;
+ border-left: 4px solid #f0f0f0;
+ padding: 0.5em;
+}
+
+div.listingblock > div.content {
+ border: 1px solid #dddddd;
+ /* border-left: 5px solid #f0f0f0; */
+ background: #f8f8f8;
+ padding: 0.5em;
+}
+
+div.quoteblock, div.verseblock {
+ padding-left: 1.0em;
+ margin-left: 1.0em;
+ margin-right: 10%;
+ border-left: 5px solid #f0f0f0;
+ color: #777777;
+}
+
+div.quoteblock > div.attribution {
+ padding-top: 0.5em;
+ text-align: right;
+}
+
+div.verseblock > pre.content {
+ font-family: inherit;
+ font-size: inherit;
+}
+div.verseblock > div.attribution {
+ padding-top: 0.75em;
+ text-align: left;
+}
+/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
+div.verseblock + div.attribution {
+ text-align: left;
+}
+
+div.admonitionblock .icon {
+ vertical-align: top;
+ font-size: 1.1em;
+ font-weight: bold;
+ text-decoration: underline;
+ /* OLD: color: #527bbd; */
+ color: #990000;
+ padding-right: 0.5em;
+}
+div.admonitionblock td.content {
+ padding-left: 0.5em;
+ border-left: 3px solid #dddddd;
+}
+
+div.exampleblock > div.content {
+ border-left: 3px solid #dddddd;
+ padding-left: 0.5em;
+}
+
+div.imageblock div.content { padding-left: 0; }
+span.image img { border-style: none; }
+a.image:visited { color: white; }
+
+dl {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+dt {
+ margin-top: 0.5em;
+ margin-bottom: 0;
+ font-style: normal;
+ color: navy;
+}
+dd > *:first-child {
+ margin-top: 0.1em;
+}
+
+ul, ol {
+ list-style-position: outside;
+}
+ol.arabic {
+ list-style-type: decimal;
+}
+ol.loweralpha {
+ list-style-type: lower-alpha;
+}
+ol.upperalpha {
+ list-style-type: upper-alpha;
+}
+ol.lowerroman {
+ list-style-type: lower-roman;
+}
+ol.upperroman {
+ list-style-type: upper-roman;
+}
+
+div.compact ul, div.compact ol,
+div.compact p, div.compact p,
+div.compact div, div.compact div {
+ margin-top: 0.1em;
+ margin-bottom: 0.1em;
+}
+
+tfoot {
+ font-weight: bold;
+}
+td > div.verse {
+ white-space: pre;
+}
+
+div.hdlist {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+div.hdlist tr {
+ padding-bottom: 15px;
+}
+dt.hdlist1.strong, td.hdlist1.strong {
+ font-weight: bold;
+}
+td.hdlist1 {
+ vertical-align: top;
+ font-style: normal;
+ padding-right: 0.8em;
+ /* color: navy; */
+ color: #990000;
+}
+td.hdlist2 {
+ vertical-align: top;
+}
+div.hdlist.compact tr {
+ margin: 0;
+ padding-bottom: 0;
+}
+
+.comment {
+ background: yellow;
+}
+
+.footnote, .footnoteref {
+ font-size: 0.8em;
+}
+
+span.footnote, span.footnoteref {
+ vertical-align: super;
+}
+
+#footnotes {
+ margin: 20px 0 20px 0;
+ padding: 7px 0 0 0;
+}
+
+#footnotes div.footnote {
+ margin: 0 0 5px 0;
+}
+
+#footnotes hr {
+ border: none;
+ border-top: 1px solid silver;
+ height: 1px;
+ text-align: left;
+ margin-left: 0;
+ width: 20%;
+ min-width: 100px;
+}
+
+div.colist td {
+ padding-right: 0.5em;
+ padding-bottom: 0.3em;
+ vertical-align: top;
+}
+div.colist td img {
+ margin-top: 0.3em;
+}
+
+@media print {
+ #footer-badges { display: none; }
+}
+
+#toc {
+ margin-bottom: 2.5em;
+}
+
+#toctitle {
+ /* color: #527bbd; */
+ color: #990000;
+ font-size: 1.1em;
+ font-weight: bold;
+ margin-top: 1.0em;
+ margin-bottom: 0.1em;
+}
+
+div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+div.toclevel2 {
+ margin-left: 2em;
+ font-size: 0.9em;
+}
+div.toclevel3 {
+ margin-left: 4em;
+ font-size: 0.9em;
+}
+div.toclevel4 {
+ margin-left: 6em;
+ font-size: 0.9em;
+}
+
+span.aqua { color: aqua; }
+span.black { color: black; }
+span.blue { color: blue; }
+span.fuchsia { color: fuchsia; }
+span.gray { color: gray; }
+span.green { color: green; }
+span.lime { color: lime; }
+span.maroon { color: maroon; }
+span.navy { color: navy; }
+span.olive { color: olive; }
+span.purple { color: purple; }
+span.red { color: red; }
+span.silver { color: silver; }
+span.teal { color: teal; }
+span.white { color: white; }
+span.yellow { color: yellow; }
+
+span.aqua-background { background: aqua; }
+span.black-background { background: black; }
+span.blue-background { background: blue; }
+span.fuchsia-background { background: fuchsia; }
+span.gray-background { background: gray; }
+span.green-background { background: green; }
+span.lime-background { background: lime; }
+span.maroon-background { background: maroon; }
+span.navy-background { background: navy; }
+span.olive-background { background: olive; }
+span.purple-background { background: purple; }
+span.red-background { background: red; }
+span.silver-background { background: silver; }
+span.teal-background { background: teal; }
+span.white-background { background: white; }
+span.yellow-background { background: yellow; }
+
+span.big { font-size: 2em; }
+span.small { font-size: 0.6em; }
+
+span.underline { text-decoration: underline; }
+span.overline { text-decoration: overline; }
+span.line-through { text-decoration: line-through; }
+
+
+/*
+ * xhtml11 specific
+ *
+ * */
+
+tt {
+ font-family: monospace;
+ font-size: inherit;
+ /* color: navy; */
+ color: black;
+}
+
+div.tableblock {
+ margin-top: 1.0em;
+ margin-bottom: 1.5em;
+}
+div.tableblock > table {
+ /* border: 3px solid #527bbd; */
+ border: 2px solid #990000;
+}
+thead, p.table.header {
+ font-weight: bold;
+ /* color: #527bbd; */
+ color: #990000;
+}
+p.table {
+ margin-top: 0;
+}
+/* Because the table frame attribute is overriden by CSS in most browsers. */
+div.tableblock > table[frame="void"] {
+ border-style: none;
+}
+div.tableblock > table[frame="hsides"] {
+ border-left-style: none;
+ border-right-style: none;
+}
+div.tableblock > table[frame="vsides"] {
+ border-top-style: none;
+ border-bottom-style: none;
+}
+
+
+/*
+ * html5 specific
+ *
+ * */
+
+.monospaced {
+ font-family: monospace;
+ font-size: inherit;
+ color: navy;
+}
+
+table.tableblock {
+ margin-top: 1.0em;
+ margin-bottom: 1.5em;
+}
+thead, p.tableblock.header {
+ font-weight: bold;
+ /* color: #527bbd; */
+ color: #990000;
+}
+p.tableblock {
+ margin-top: 0;
+}
+table.tableblock {
+ border-width: 3px;
+ border-spacing: 0px;
+ border-style: solid;
+ /* border-color: #527bbd; */
+ border-color: #990000;
+ border-collapse: collapse;
+}
+th.tableblock, td.tableblock {
+ border-width: 1px;
+ padding: 4px;
+ border-style: solid;
+ /* border-color: #527bbd; */
+ border-color: #990000;
+}
+
+table.tableblock.frame-topbot {
+ border-left-style: hidden;
+ border-right-style: hidden;
+}
+table.tableblock.frame-sides {
+ border-top-style: hidden;
+ border-bottom-style: hidden;
+}
+table.tableblock.frame-none {
+ border-style: hidden;
+}
+
+th.tableblock.halign-left, td.tableblock.halign-left {
+ text-align: left;
+}
+th.tableblock.halign-center, td.tableblock.halign-center {
+ text-align: center;
+}
+th.tableblock.halign-right, td.tableblock.halign-right {
+ text-align: right;
+}
+
+th.tableblock.valign-top, td.tableblock.valign-top {
+ vertical-align: top;
+}
+th.tableblock.valign-middle, td.tableblock.valign-middle {
+ vertical-align: middle;
+}
+th.tableblock.valign-bottom, td.tableblock.valign-bottom {
+ vertical-align: bottom;
+}
diff --git a/doc/stylesheets/docbook-xsl.css b/doc/stylesheets/docbook-xsl.css
new file mode 100644
index 00000000..6df2944f
--- /dev/null
+++ b/doc/stylesheets/docbook-xsl.css
@@ -0,0 +1,322 @@
+/*
+ CSS stylesheet for XHTML produced by DocBook XSL stylesheets.
+ Tested with XSL stylesheets 1.61.2, 1.67.2
+*/
+
+span.strong {
+ font-weight: bold;
+}
+
+body blockquote {
+ margin-top: .75em;
+ line-height: 1.5;
+ margin-bottom: .75em;
+}
+
+html body {
+ margin: 1em 5% 1em 5%;
+ line-height: 1.2;
+}
+
+body div {
+ margin: 0;
+}
+
+h1, h2, h3, h4, h5, h6
+{
+ color: #527bbd;
+ font-family: tahoma, verdana, sans-serif;
+}
+
+div.toc p:first-child,
+div.list-of-figures p:first-child,
+div.list-of-tables p:first-child,
+div.list-of-examples p:first-child,
+div.example p.title,
+div.sidebar p.title
+{
+ font-weight: bold;
+ color: #527bbd;
+ font-family: tahoma, verdana, sans-serif;
+ margin-bottom: 0.2em;
+}
+
+body h1 {
+ margin: .0em 0 0 -4%;
+ line-height: 1.3;
+ border-bottom: 2px solid silver;
+}
+
+body h2 {
+ margin: 0.5em 0 0 -4%;
+ line-height: 1.3;
+ border-bottom: 2px solid silver;
+}
+
+body h3 {
+ margin: .8em 0 0 -3%;
+ line-height: 1.3;
+}
+
+body h4 {
+ margin: .8em 0 0 -3%;
+ line-height: 1.3;
+}
+
+body h5 {
+ margin: .8em 0 0 -2%;
+ line-height: 1.3;
+}
+
+body h6 {
+ margin: .8em 0 0 -1%;
+ line-height: 1.3;
+}
+
+body hr {
+ border: none; /* Broken on IE6 */
+}
+div.footnotes hr {
+ border: 1px solid silver;
+}
+
+div.navheader th, div.navheader td, div.navfooter td {
+ font-family: sans-serif;
+ font-size: 0.9em;
+ font-weight: bold;
+ color: #527bbd;
+}
+div.navheader img, div.navfooter img {
+ border-style: none;
+}
+div.navheader a, div.navfooter a {
+ font-weight: normal;
+}
+div.navfooter hr {
+ border: 1px solid silver;
+}
+
+body td {
+ line-height: 1.2
+}
+
+body th {
+ line-height: 1.2;
+}
+
+ol {
+ line-height: 1.2;
+}
+
+ul, body dir, body menu {
+ line-height: 1.2;
+}
+
+html {
+ margin: 0;
+ padding: 0;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6 {
+ margin-left: 0
+}
+
+body pre {
+ margin: 0.5em 10% 0.5em 1em;
+ line-height: 1.0;
+ color: navy;
+}
+
+tt.literal, code.literal {
+ color: navy;
+}
+
+.programlisting, .screen {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ margin: 0.5em 10% 0.5em 0;
+ padding: 0.5em 1em;
+}
+
+div.sidebar {
+ background: #ffffee;
+ margin: 1.0em 10% 0.5em 0;
+ padding: 0.5em 1em;
+ border: 1px solid silver;
+}
+div.sidebar * { padding: 0; }
+div.sidebar div { margin: 0; }
+div.sidebar p.title {
+ margin-top: 0.5em;
+ margin-bottom: 0.2em;
+}
+
+div.bibliomixed {
+ margin: 0.5em 5% 0.5em 1em;
+}
+
+div.glossary dt {
+ font-weight: bold;
+}
+div.glossary dd p {
+ margin-top: 0.2em;
+}
+
+dl {
+ margin: .8em 0;
+ line-height: 1.2;
+}
+
+dt {
+ margin-top: 0.5em;
+}
+
+dt span.term {
+ font-style: normal;
+ color: navy;
+}
+
+div.variablelist dd p {
+ margin-top: 0;
+}
+
+div.itemizedlist li, div.orderedlist li {
+ margin-left: -0.8em;
+ margin-top: 0.5em;
+}
+
+ul, ol {
+ list-style-position: outside;
+}
+
+div.sidebar ul, div.sidebar ol {
+ margin-left: 2.8em;
+}
+
+div.itemizedlist p.title,
+div.orderedlist p.title,
+div.variablelist p.title
+{
+ margin-bottom: -0.8em;
+}
+
+div.revhistory table {
+ border-collapse: collapse;
+ border: none;
+}
+div.revhistory th {
+ border: none;
+ color: #527bbd;
+ font-family: tahoma, verdana, sans-serif;
+}
+div.revhistory td {
+ border: 1px solid silver;
+}
+
+/* Keep TOC and index lines close together. */
+div.toc dl, div.toc dt,
+div.list-of-figures dl, div.list-of-figures dt,
+div.list-of-tables dl, div.list-of-tables dt,
+div.indexdiv dl, div.indexdiv dt
+{
+ line-height: normal;
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+/*
+ Table styling does not work because of overriding attributes in
+ generated HTML.
+*/
+div.table table,
+div.informaltable table
+{
+ margin-left: 0;
+ margin-right: 5%;
+ margin-bottom: 0.8em;
+}
+div.informaltable table
+{
+ margin-top: 0.4em
+}
+div.table thead,
+div.table tfoot,
+div.table tbody,
+div.informaltable thead,
+div.informaltable tfoot,
+div.informaltable tbody
+{
+ /* No effect in IE6. */
+ border-top: 3px solid #527bbd;
+ border-bottom: 3px solid #527bbd;
+}
+div.table thead, div.table tfoot,
+div.informaltable thead, div.informaltable tfoot
+{
+ font-weight: bold;
+}
+
+div.mediaobject img {
+ margin-bottom: 0.8em;
+}
+div.figure p.title,
+div.table p.title
+{
+ margin-top: 1em;
+ margin-bottom: 0.4em;
+}
+
+div.calloutlist p
+{
+ margin-top: 0em;
+ margin-bottom: 0.4em;
+}
+
+a img {
+ border-style: none;
+}
+
+@media print {
+ div.navheader, div.navfooter { display: none; }
+}
+
+span.aqua { color: aqua; }
+span.black { color: black; }
+span.blue { color: blue; }
+span.fuchsia { color: fuchsia; }
+span.gray { color: gray; }
+span.green { color: green; }
+span.lime { color: lime; }
+span.maroon { color: maroon; }
+span.navy { color: navy; }
+span.olive { color: olive; }
+span.purple { color: purple; }
+span.red { color: red; }
+span.silver { color: silver; }
+span.teal { color: teal; }
+span.white { color: white; }
+span.yellow { color: yellow; }
+
+span.aqua-background { background: aqua; }
+span.black-background { background: black; }
+span.blue-background { background: blue; }
+span.fuchsia-background { background: fuchsia; }
+span.gray-background { background: gray; }
+span.green-background { background: green; }
+span.lime-background { background: lime; }
+span.maroon-background { background: maroon; }
+span.navy-background { background: navy; }
+span.olive-background { background: olive; }
+span.purple-background { background: purple; }
+span.red-background { background: red; }
+span.silver-background { background: silver; }
+span.teal-background { background: teal; }
+span.white-background { background: white; }
+span.yellow-background { background: yellow; }
+
+span.big { font-size: 2em; }
+span.small { font-size: 0.6em; }
+
+span.underline { text-decoration: underline; }
+span.overline { text-decoration: overline; }
+span.line-through { text-decoration: line-through; }
diff --git a/doc/stylesheets/flask-manpage.css b/doc/stylesheets/flask-manpage.css
new file mode 100644
index 00000000..75a2dda0
--- /dev/null
+++ b/doc/stylesheets/flask-manpage.css
@@ -0,0 +1 @@
+/* Empty placeholder file */
diff --git a/doc/stylesheets/flask.css b/doc/stylesheets/flask.css
new file mode 100644
index 00000000..8d33bc49
--- /dev/null
+++ b/doc/stylesheets/flask.css
@@ -0,0 +1,584 @@
+/*
+ * AsciiDoc 'flask' theme for xhtml11 and html5 backends. A shameless knock-off
+ * of the Flask website styling (http://flask.pocoo.org/docs/).
+ *
+ * The implementation is straight-forward, consisting of the asciidoc.css file
+ * followed by theme specific overrides.
+ *
+ * */
+
+
+/* Shared CSS for AsciiDoc xhtml11 and html5 backends */
+
+/* Default font. */
+body {
+ font-family: Georgia,serif;
+}
+
+/* Title font. */
+h1, h2, h3, h4, h5, h6,
+div.title, caption.title,
+thead, p.table.header,
+#toctitle,
+#author, #revnumber, #revdate, #revremark,
+#footer {
+ font-family: Arial,Helvetica,sans-serif;
+}
+
+body {
+ margin: 1em 5% 1em 5%;
+}
+
+a {
+ color: blue;
+ text-decoration: underline;
+}
+a:visited {
+ color: fuchsia;
+}
+
+em {
+ font-style: italic;
+ color: navy;
+}
+
+strong {
+ font-weight: bold;
+ color: #083194;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color: #527bbd;
+ margin-top: 1.2em;
+ margin-bottom: 0.5em;
+ line-height: 1.3;
+}
+
+h1, h2, h3 {
+ border-bottom: 2px solid silver;
+}
+h2 {
+ padding-top: 0.5em;
+}
+h3 {
+ float: left;
+}
+h3 + * {
+ clear: left;
+}
+h5 {
+ font-size: 1.0em;
+}
+
+div.sectionbody {
+ margin-left: 0;
+}
+
+hr {
+ border: 1px solid silver;
+}
+
+p {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+ul, ol, li > p {
+ margin-top: 0;
+}
+ul > li { color: #aaa; }
+ul > li > * { color: black; }
+
+pre {
+ padding: 0;
+ margin: 0;
+}
+
+#author {
+ color: #527bbd;
+ font-weight: bold;
+ font-size: 1.1em;
+}
+#email {
+}
+#revnumber, #revdate, #revremark {
+}
+
+#footer {
+ font-size: small;
+ border-top: 2px solid silver;
+ padding-top: 0.5em;
+ margin-top: 4.0em;
+}
+#footer-text {
+ float: left;
+ padding-bottom: 0.5em;
+}
+#footer-badges {
+ float: right;
+ padding-bottom: 0.5em;
+}
+
+#preamble {
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+}
+div.imageblock, div.exampleblock, div.verseblock,
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
+div.admonitionblock {
+ margin-top: 1.0em;
+ margin-bottom: 1.5em;
+}
+div.admonitionblock {
+ margin-top: 2.0em;
+ margin-bottom: 2.0em;
+ margin-right: 10%;
+ color: #606060;
+}
+
+div.content { /* Block element content. */
+ padding: 0;
+}
+
+/* Block element titles. */
+div.title, caption.title {
+ color: #527bbd;
+ font-weight: bold;
+ text-align: left;
+ margin-top: 1.0em;
+ margin-bottom: 0.5em;
+}
+div.title + * {
+ margin-top: 0;
+}
+
+td div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content + div.title {
+ margin-top: 0.0em;
+}
+
+div.sidebarblock > div.content {
+ background: #ffffee;
+ border: 1px solid #dddddd;
+ border-left: 4px solid #f0f0f0;
+ padding: 0.5em;
+}
+
+div.listingblock > div.content {
+ border: 1px solid #dddddd;
+ border-left: 5px solid #f0f0f0;
+ background: #f8f8f8;
+ padding: 0.5em;
+}
+
+div.quoteblock, div.verseblock {
+ padding-left: 1.0em;
+ margin-left: 1.0em;
+ margin-right: 10%;
+ border-left: 5px solid #f0f0f0;
+ color: #777777;
+}
+
+div.quoteblock > div.attribution {
+ padding-top: 0.5em;
+ text-align: right;
+}
+
+div.verseblock > pre.content {
+ font-family: inherit;
+ font-size: inherit;
+}
+div.verseblock > div.attribution {
+ padding-top: 0.75em;
+ text-align: left;
+}
+/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
+div.verseblock + div.attribution {
+ text-align: left;
+}
+
+div.admonitionblock .icon {
+ vertical-align: top;
+ font-size: 1.1em;
+ font-weight: bold;
+ text-decoration: underline;
+ color: #527bbd;
+ padding-right: 0.5em;
+}
+div.admonitionblock td.content {
+ padding-left: 0.5em;
+ border-left: 3px solid #dddddd;
+}
+
+div.exampleblock > div.content {
+ border-left: 3px solid #dddddd;
+ padding-left: 0.5em;
+}
+
+div.imageblock div.content { padding-left: 0; }
+span.image img { border-style: none; }
+a.image:visited { color: white; }
+
+dl {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+dt {
+ margin-top: 0.5em;
+ margin-bottom: 0;
+ font-style: normal;
+ color: navy;
+}
+dd > *:first-child {
+ margin-top: 0.1em;
+}
+
+ul, ol {
+ list-style-position: outside;
+}
+ol.arabic {
+ list-style-type: decimal;
+}
+ol.loweralpha {
+ list-style-type: lower-alpha;
+}
+ol.upperalpha {
+ list-style-type: upper-alpha;
+}
+ol.lowerroman {
+ list-style-type: lower-roman;
+}
+ol.upperroman {
+ list-style-type: upper-roman;
+}
+
+div.compact ul, div.compact ol,
+div.compact p, div.compact p,
+div.compact div, div.compact div {
+ margin-top: 0.1em;
+ margin-bottom: 0.1em;
+}
+
+tfoot {
+ font-weight: bold;
+}
+td > div.verse {
+ white-space: pre;
+}
+
+div.hdlist {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+div.hdlist tr {
+ padding-bottom: 15px;
+}
+dt.hdlist1.strong, td.hdlist1.strong {
+ font-weight: bold;
+}
+td.hdlist1 {
+ vertical-align: top;
+ font-style: normal;
+ padding-right: 0.8em;
+ color: navy;
+}
+td.hdlist2 {
+ vertical-align: top;
+}
+div.hdlist.compact tr {
+ margin: 0;
+ padding-bottom: 0;
+}
+
+.comment {
+ background: yellow;
+}
+
+.footnote, .footnoteref {
+ font-size: 0.8em;
+}
+
+span.footnote, span.footnoteref {
+ vertical-align: super;
+}
+
+#footnotes {
+ margin: 20px 0 20px 0;
+ padding: 7px 0 0 0;
+}
+
+#footnotes div.footnote {
+ margin: 0 0 5px 0;
+}
+
+#footnotes hr {
+ border: none;
+ border-top: 1px solid silver;
+ height: 1px;
+ text-align: left;
+ margin-left: 0;
+ width: 20%;
+ min-width: 100px;
+}
+
+div.colist td {
+ padding-right: 0.5em;
+ padding-bottom: 0.3em;
+ vertical-align: top;
+}
+div.colist td img {
+ margin-top: 0.3em;
+}
+
+@media print {
+ #footer-badges { display: none; }
+}
+
+#toc {
+ margin-bottom: 2.5em;
+}
+
+#toctitle {
+ color: #527bbd;
+ font-size: 1.1em;
+ font-weight: bold;
+ margin-top: 1.0em;
+ margin-bottom: 0.1em;
+}
+
+div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+div.toclevel2 {
+ margin-left: 2em;
+ font-size: 0.9em;
+}
+div.toclevel3 {
+ margin-left: 4em;
+ font-size: 0.9em;
+}
+div.toclevel4 {
+ margin-left: 6em;
+ font-size: 0.9em;
+}
+
+span.aqua { color: aqua; }
+span.black { color: black; }
+span.blue { color: blue; }
+span.fuchsia { color: fuchsia; }
+span.gray { color: gray; }
+span.green { color: green; }
+span.lime { color: lime; }
+span.maroon { color: maroon; }
+span.navy { color: navy; }
+span.olive { color: olive; }
+span.purple { color: purple; }
+span.red { color: red; }
+span.silver { color: silver; }
+span.teal { color: teal; }
+span.white { color: white; }
+span.yellow { color: yellow; }
+
+span.aqua-background { background: aqua; }
+span.black-background { background: black; }
+span.blue-background { background: blue; }
+span.fuchsia-background { background: fuchsia; }
+span.gray-background { background: gray; }
+span.green-background { background: green; }
+span.lime-background { background: lime; }
+span.maroon-background { background: maroon; }
+span.navy-background { background: navy; }
+span.olive-background { background: olive; }
+span.purple-background { background: purple; }
+span.red-background { background: red; }
+span.silver-background { background: silver; }
+span.teal-background { background: teal; }
+span.white-background { background: white; }
+span.yellow-background { background: yellow; }
+
+span.big { font-size: 2em; }
+span.small { font-size: 0.6em; }
+
+span.underline { text-decoration: underline; }
+span.overline { text-decoration: overline; }
+span.line-through { text-decoration: line-through; }
+
+
+/*
+ * xhtml11 specific
+ *
+ * */
+
+tt {
+ font-family: monospace;
+ font-size: inherit;
+ color: navy;
+}
+
+div.tableblock {
+ margin-top: 1.0em;
+ margin-bottom: 1.5em;
+}
+div.tableblock > table {
+ border: 3px solid #527bbd;
+}
+thead, p.table.header {
+ font-weight: bold;
+ color: #527bbd;
+}
+p.table {
+ margin-top: 0;
+}
+/* Because the table frame attribute is overriden by CSS in most browsers. */
+div.tableblock > table[frame="void"] {
+ border-style: none;
+}
+div.tableblock > table[frame="hsides"] {
+ border-left-style: none;
+ border-right-style: none;
+}
+div.tableblock > table[frame="vsides"] {
+ border-top-style: none;
+ border-bottom-style: none;
+}
+
+
+/*
+ * html5 specific
+ *
+ * */
+
+.monospaced {
+ font-family: monospace;
+ font-size: inherit;
+ color: navy;
+}
+
+table.tableblock {
+ margin-top: 1.0em;
+ margin-bottom: 1.5em;
+}
+thead, p.tableblock.header {
+ font-weight: bold;
+ color: #527bbd;
+}
+p.tableblock {
+ margin-top: 0;
+}
+table.tableblock {
+ border-width: 3px;
+ border-spacing: 0px;
+ border-style: solid;
+ border-color: #527bbd;
+ border-collapse: collapse;
+}
+th.tableblock, td.tableblock {
+ border-width: 1px;
+ padding: 4px;
+ border-style: solid;
+ border-color: #527bbd;
+}
+
+table.tableblock.frame-topbot {
+ border-left-style: hidden;
+ border-right-style: hidden;
+}
+table.tableblock.frame-sides {
+ border-top-style: hidden;
+ border-bottom-style: hidden;
+}
+table.tableblock.frame-none {
+ border-style: hidden;
+}
+
+th.tableblock.halign-left, td.tableblock.halign-left {
+ text-align: left;
+}
+th.tableblock.halign-center, td.tableblock.halign-center {
+ text-align: center;
+}
+th.tableblock.halign-right, td.tableblock.halign-right {
+ text-align: right;
+}
+
+th.tableblock.valign-top, td.tableblock.valign-top {
+ vertical-align: top;
+}
+th.tableblock.valign-middle, td.tableblock.valign-middle {
+ vertical-align: middle;
+}
+th.tableblock.valign-bottom, td.tableblock.valign-bottom {
+ vertical-align: bottom;
+}
+
+
+/*
+ * Theme specific overrides of the preceding (asciidoc.css) CSS.
+ *
+ */
+body {
+ font-family: Garamond, Georgia, serif;
+ font-size: 17px;
+ color: #3E4349;
+ line-height: 1.3em;
+}
+h1, h2, h3, h4, h5, h6,
+div.title, caption.title,
+thead, p.table.header,
+#toctitle,
+#author, #revnumber, #revdate, #revremark,
+#footer {
+ font-family: Garmond, Georgia, serif;
+ font-weight: normal;
+ border-bottom-width: 0;
+ color: #3E4349;
+}
+div.title, caption.title { color: #596673; font-weight: bold; }
+h1 { font-size: 240%; }
+h2 { font-size: 180%; }
+h3 { font-size: 150%; }
+h4 { font-size: 130%; }
+h5 { font-size: 100%; }
+h6 { font-size: 100%; }
+#header h1 { margin-top: 0; }
+#toc {
+ color: #444444;
+ line-height: 1.5;
+ padding-top: 1.5em;
+}
+#toctitle {
+ font-size: 20px;
+}
+#toc a {
+ border-bottom: 1px dotted #999999;
+ color: #444444 !important;
+ text-decoration: none !important;
+}
+#toc a:hover {
+ border-bottom: 1px solid #6D4100;
+ color: #6D4100 !important;
+ text-decoration: none !important;
+}
+div.toclevel1 { margin-top: 0.2em; font-size: 16px; }
+div.toclevel2 { margin-top: 0.15em; font-size: 14px; }
+em, dt, td.hdlist1 { color: black; }
+strong { color: #3E4349; }
+a { color: #004B6B; text-decoration: none; border-bottom: 1px dotted #004B6B; }
+a:visited { color: #615FA0; border-bottom: 1px dotted #615FA0; }
+a:hover { color: #6D4100; border-bottom: 1px solid #6D4100; }
+div.tableblock > table, table.tableblock { border: 3px solid #E8E8E8; }
+th.tableblock, td.tableblock { border: 1px solid #E8E8E8; }
+ul > li > * { color: #3E4349; }
+pre, tt, .monospaced { font-family: Consolas,Menlo,'Deja Vu Sans Mono','Bitstream Vera Sans Mono',monospace; }
+tt, .monospaced { font-size: 0.9em; color: black;
+}
+div.exampleblock > div.content, div.sidebarblock > div.content, div.listingblock > div.content { border-width: 0 0 0 3px; border-color: #E8E8E8; }
+div.verseblock { border-left-width: 0; margin-left: 3em; }
+div.quoteblock { border-left-width: 3px; margin-left: 0; margin-right: 0;}
+div.admonitionblock td.content { border-left: 3px solid #E8E8E8; }
diff --git a/doc/stylesheets/pygments.css b/doc/stylesheets/pygments.css
new file mode 100644
index 00000000..9ca3659c
--- /dev/null
+++ b/doc/stylesheets/pygments.css
@@ -0,0 +1,66 @@
+/*
+ pygmentize filter
+*/
+.highlight .hll { background-color: #ffffcc }
+.highlight { background: #f4f4f4; }
+.highlight .c { color: #008800; font-style: italic } /* Comment */
+.highlight .err { border: 1px solid #FF0000 } /* Error */
+.highlight .k { color: #AA22FF; font-weight: bold } /* Keyword */
+.highlight .o { color: #666666 } /* Operator */
+.highlight .cm { color: #008800; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #008800 } /* Comment.Preproc */
+.highlight .c1 { color: #008800; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #008800; font-weight: bold } /* Comment.Special */
+.highlight .gd { color: #A00000 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #FF0000 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #808080 } /* Generic.Output */
+.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #0040D0 } /* Generic.Traceback */
+.highlight .kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */
+.highlight .kn { color: #AA22FF; font-weight: bold } /* Keyword.Namespace */
+.highlight .kp { color: #AA22FF } /* Keyword.Pseudo */
+.highlight .kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #00BB00; font-weight: bold } /* Keyword.Type */
+.highlight .m { color: #666666 } /* Literal.Number */
+.highlight .s { color: #BB4444 } /* Literal.String */
+.highlight .na { color: #BB4444 } /* Name.Attribute */
+.highlight .nb { color: #AA22FF } /* Name.Builtin */
+.highlight .nc { color: #0000FF } /* Name.Class */
+.highlight .no { color: #880000 } /* Name.Constant */
+.highlight .nd { color: #AA22FF } /* Name.Decorator */
+.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
+.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #00A000 } /* Name.Function */
+.highlight .nl { color: #A0A000 } /* Name.Label */
+.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #B8860B } /* Name.Variable */
+.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mf { color: #666666 } /* Literal.Number.Float */
+.highlight .mh { color: #666666 } /* Literal.Number.Hex */
+.highlight .mi { color: #666666 } /* Literal.Number.Integer */
+.highlight .mo { color: #666666 } /* Literal.Number.Oct */
+.highlight .sb { color: #BB4444 } /* Literal.String.Backtick */
+.highlight .sc { color: #BB4444 } /* Literal.String.Char */
+.highlight .sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */
+.highlight .s2 { color: #BB4444 } /* Literal.String.Double */
+.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+.highlight .sh { color: #BB4444 } /* Literal.String.Heredoc */
+.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+.highlight .sx { color: #008000 } /* Literal.String.Other */
+.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
+.highlight .s1 { color: #BB4444 } /* Literal.String.Single */
+.highlight .ss { color: #B8860B } /* Literal.String.Symbol */
+.highlight .bp { color: #AA22FF } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #B8860B } /* Name.Variable.Class */
+.highlight .vg { color: #B8860B } /* Name.Variable.Global */
+.highlight .vi { color: #B8860B } /* Name.Variable.Instance */
+.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
+
diff --git a/doc/stylesheets/slidy.css b/doc/stylesheets/slidy.css
new file mode 100644
index 00000000..bbb790e7
--- /dev/null
+++ b/doc/stylesheets/slidy.css
@@ -0,0 +1,445 @@
+/* slidy.css
+
+ Copyright (c) 2005-2010 W3C (MIT, ERCIM, Keio), All Rights Reserved.
+ W3C liability, trademark, document use and software licensing
+ rules apply, see:
+
+ http://www.w3.org/Consortium/Legal/copyright-documents
+ http://www.w3.org/Consortium/Legal/copyright-software
+*/
+
+/*
+ SJR: 2010-09-29: Modified for AsciiDoc slidy backend.
+ Mostly just commented out stuff that is handled by AsciiDoc's CSS files.
+*/
+
+body
+{
+ margin: 0 0 0 0;
+ padding: 0 0 0 0;
+ width: 100%;
+ height: 100%;
+ color: black;
+ background-color: white;
+/*
+ font-family: "Gill Sans MT", "Gill Sans", GillSans, sans-serif;
+*/
+ font-size: 14pt;
+}
+
+div.toolbar {
+ position: fixed; z-index: 200;
+ top: auto; bottom: 0; left: 0; right: 0;
+ height: 1.2em; text-align: right;
+ padding-left: 1em;
+ padding-right: 1em;
+ font-size: 60%;
+ color: red;
+ background-color: rgb(240,240,240);
+ border-top: solid 1px rgb(180,180,180);
+}
+
+div.toolbar span.copyright {
+ color: black;
+ margin-left: 0.5em;
+}
+
+div.initial_prompt {
+ position: absolute;
+ z-index: 1000;
+ bottom: 1.2em;
+ width: 90%;
+ background-color: rgb(200,200,200);
+ opacity: 0.35;
+ background-color: rgb(200,200,200, 0.35);
+ cursor: pointer;
+}
+
+div.initial_prompt p.help {
+ text-align: center;
+}
+
+div.initial_prompt p.close {
+ text-align: right;
+ font-style: italic;
+}
+
+div.slidy_toc {
+ position: absolute;
+ z-index: 300;
+ width: 60%;
+ max-width: 30em;
+ height: 30em;
+ overflow: auto;
+ top: auto;
+ right: auto;
+ left: 4em;
+ bottom: 4em;
+ padding: 1em;
+ background: rgb(240,240,240);
+ border-style: solid;
+ border-width: 2px;
+ font-size: 60%;
+}
+
+div.slidy_toc .toc_heading {
+ text-align: center;
+ width: 100%;
+ margin: 0;
+ margin-bottom: 1em;
+ border-bottom-style: solid;
+ border-bottom-color: rgb(180,180,180);
+ border-bottom-width: 1px;
+}
+
+div.slide {
+ z-index: 20;
+ margin: 0 0 0 0;
+ padding-top: 0;
+ padding-bottom: 0;
+ padding-left: 20px;
+ padding-right: 20px;
+ border-width: 0;
+ clear: both;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ line-height: 120%;
+ background-color: transparent;
+}
+
+div.background {
+ display: none;
+}
+
+div.handout {
+ margin-left: 20px;
+ margin-right: 20px;
+}
+
+div.slide.titlepage {
+ text-align: center;
+}
+
+div.slide.titlepage.h1 {
+ padding-top: 10%;
+}
+
+div.slide h1 {
+ padding-left: 0;
+ padding-right: 20pt;
+ padding-top: 4pt;
+ padding-bottom: 4pt;
+ margin-top: 0;
+ margin-left: 0;
+ margin-right: 60pt;
+ margin-bottom: 0.5em;
+ display: block;
+ font-size: 160%;
+ line-height: 1.2em;
+ background: transparent;
+}
+
+div.toc {
+ position: absolute;
+ top: auto;
+ bottom: 4em;
+ left: 4em;
+ right: auto;
+ width: 60%;
+ max-width: 30em;
+ height: 30em;
+ border: solid thin black;
+ padding: 1em;
+ background: rgb(240,240,240);
+ color: black;
+ z-index: 300;
+ overflow: auto;
+ display: block;
+ visibility: visible;
+}
+
+div.toc-heading {
+ width: 100%;
+ border-bottom: solid 1px rgb(180,180,180);
+ margin-bottom: 1em;
+ text-align: center;
+}
+
+/*
+pre {
+ font-size: 80%;
+ font-weight: bold;
+ line-height: 120%;
+ padding-top: 0.2em;
+ padding-bottom: 0.2em;
+ padding-left: 1em;
+ padding-right: 1em;
+ border-style: solid;
+ border-left-width: 1em;
+ border-top-width: thin;
+ border-right-width: thin;
+ border-bottom-width: thin;
+ border-color: #95ABD0;
+ color: #00428C;
+ background-color: #E4E5E7;
+}
+*/
+
+/*
+li pre { margin-left: 0; }
+
+blockquote { font-style: italic }
+
+img { background-color: transparent }
+
+p.copyright { font-size: smaller }
+*/
+
+.center { text-align: center }
+.footnote { font-size: smaller; margin-left: 2em; }
+
+/*
+a img { border-width: 0; border-style: none }
+*/
+
+a:visited { color: navy }
+a:link { color: navy }
+a:hover { color: red; text-decoration: underline }
+a:active { color: red; text-decoration: underline }
+
+a {text-decoration: none}
+.navbar a:link {color: white}
+.navbar a:visited {color: yellow}
+.navbar a:active {color: red}
+.navbar a:hover {color: red}
+
+/*
+ul { list-style-type: square; }
+ul ul { list-style-type: disc; }
+ul ul ul { list-style-type: circle; }
+ul ul ul ul { list-style-type: disc; }
+li { margin-left: 0.5em; margin-top: 0.5em; }
+li li { font-size: 85%; font-style: italic }
+li li li { font-size: 85%; font-style: normal }
+*/
+
+div dt
+{
+ margin-left: 0;
+ margin-top: 1em;
+ margin-bottom: 0.5em;
+ font-weight: bold;
+}
+div dd
+{
+ margin-left: 2em;
+ margin-bottom: 0.5em;
+}
+
+
+/*
+p,pre,ul,ol,blockquote,h2,h3,h4,h5,h6,dl,table {
+ margin-left: 1em;
+ margin-right: 1em;
+}
+*/
+
+p.subhead { font-weight: bold; margin-top: 2em; }
+
+.smaller { font-size: smaller }
+.bigger { font-size: 130% }
+
+/*
+td,th { padding: 0.2em }
+*/
+
+ul {
+ margin: 0.5em 1.5em 0.5em 1.5em;
+ padding: 0;
+}
+
+ol {
+ margin: 0.5em 1.5em 0.5em 1.5em;
+ padding: 0;
+}
+
+ul { list-style-type: square; }
+ul ul { list-style-type: disc; }
+ul ul ul { list-style-type: circle; }
+ul ul ul ul { list-style-type: disc; }
+
+/*
+ul li {
+ list-style: square;
+ margin: 0.1em 0em 0.6em 0;
+ padding: 0 0 0 0;
+ line-height: 140%;
+}
+
+ol li {
+ margin: 0.1em 0em 0.6em 1.5em;
+ padding: 0 0 0 0px;
+ line-height: 140%;
+ list-style-type: decimal;
+}
+
+li ul li {
+ font-size: 85%;
+ font-style: italic;
+ list-style-type: disc;
+ background: transparent;
+ padding: 0 0 0 0;
+}
+li li ul li {
+ font-size: 85%;
+ font-style: normal;
+ list-style-type: circle;
+ background: transparent;
+ padding: 0 0 0 0;
+}
+li li li ul li {
+ list-style-type: disc;
+ background: transparent;
+ padding: 0 0 0 0;
+}
+
+li ol li {
+ list-style-type: decimal;
+}
+
+
+li li ol li {
+ list-style-type: decimal;
+}
+*/
+
+/*
+ setting class="outline" on ol or ul makes it behave as an
+ ouline list where blocklevel content in li elements is
+ hidden by default and can be expanded or collapsed with
+ mouse click. Set class="expand" on li to override default
+*/
+
+ol.outline li:hover { cursor: pointer }
+ol.outline li.nofold:hover { cursor: default }
+
+ul.outline li:hover { cursor: pointer }
+ul.outline li.nofold:hover { cursor: default }
+
+ol.outline { list-style:decimal; }
+ol.outline ol { list-style-type:lower-alpha }
+
+ol.outline li.nofold {
+ padding: 0 0 0 20px;
+ background: transparent url(../graphics/nofold-dim.gif) no-repeat 0px 0.5em;
+}
+ol.outline li.unfolded {
+ padding: 0 0 0 20px;
+ background: transparent url(../graphics/fold-dim.gif) no-repeat 0px 0.5em;
+}
+ol.outline li.folded {
+ padding: 0 0 0 20px;
+ background: transparent url(../graphics/unfold-dim.gif) no-repeat 0px 0.5em;
+}
+ol.outline li.unfolded:hover {
+ padding: 0 0 0 20px;
+ background: transparent url(../graphics/fold.gif) no-repeat 0px 0.5em;
+}
+ol.outline li.folded:hover {
+ padding: 0 0 0 20px;
+ background: transparent url(../graphics/unfold.gif) no-repeat 0px 0.5em;
+}
+
+ul.outline li.nofold {
+ padding: 0 0 0 20px;
+ background: transparent url(../graphics/nofold-dim.gif) no-repeat 0px 0.5em;
+}
+ul.outline li.unfolded {
+ padding: 0 0 0 20px;
+ background: transparent url(../graphics/fold-dim.gif) no-repeat 0px 0.5em;
+}
+ul.outline li.folded {
+ padding: 0 0 0 20px;
+ background: transparent url(../graphics/unfold-dim.gif) no-repeat 0px 0.5em;
+}
+ul.outline li.unfolded:hover {
+ padding: 0 0 0 20px;
+ background: transparent url(../graphics/fold.gif) no-repeat 0px 0.5em;
+}
+ul.outline li.folded:hover {
+ padding: 0 0 0 20px;
+ background: transparent url(../graphics/unfold.gif) no-repeat 0px 0.5em;
+}
+
+/* for slides with class "title" in table of contents */
+a.titleslide { font-weight: bold; font-style: italic }
+
+/*
+ hide images for work around for save as bug
+ where browsers fail to save images used by CSS
+*/
+img.hidden { display: none; visibility: hidden }
+div.initial_prompt { display: none; visibility: hidden }
+
+ div.slide {
+ visibility: visible;
+ position: inherit;
+ }
+ div.handout {
+ border-top-style: solid;
+ border-top-width: thin;
+ border-top-color: black;
+ }
+
+@media screen {
+ .hidden { display: none; visibility: visible }
+
+ div.slide.hidden { display: block; visibility: visible }
+ div.handout.hidden { display: block; visibility: visible }
+ div.background { display: none; visibility: hidden }
+ body.single_slide div.initial_prompt { display: block; visibility: visible }
+ body.single_slide div.background { display: block; visibility: visible }
+ body.single_slide div.background.hidden { display: none; visibility: hidden }
+ body.single_slide .invisible { visibility: hidden }
+ body.single_slide .hidden { display: none; visibility: hidden }
+ body.single_slide div.slide { position: absolute }
+ body.single_slide div.handout { display: none; visibility: hidden }
+}
+
+@media print {
+ .hidden { display: block; visibility: visible }
+
+/*
+ div.slide pre { font-size: 60%; padding-left: 0.5em; }
+*/
+ div.toolbar { display: none; visibility: hidden; }
+ div.slidy_toc { display: none; visibility: hidden; }
+ div.background { display: none; visibility: hidden; }
+ div.slide { page-break-before: always }
+ /* :first-child isn't reliable for print media */
+ div.slide.first-slide { page-break-before: avoid }
+}
+
+
+/* SJR: AsciiDoc slidy backend tweaks */
+
+ol, ul {
+ margin: 0.8em 1.5em 0.8em 1.8em;
+}
+li > ul, li > ol {
+ margin-top: 0.5em;
+}
+
+.outline > li.folded,
+.outline > li.unfolded {
+ color: #527bbd;
+}
+ul > li{ color: #aaa; }
+ul > li > *, ol > li > * { color: black; }
+
+li {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
diff --git a/doc/stylesheets/toc2.css b/doc/stylesheets/toc2.css
new file mode 100644
index 00000000..a1e368bc
--- /dev/null
+++ b/doc/stylesheets/toc2.css
@@ -0,0 +1,34 @@
+@media screen {
+ body {
+ max-width: 50em; /* approximately 80 characters wide */
+ margin-left: 16em;
+ }
+
+ #toc {
+ position: fixed;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 13em;
+ padding: 0.5em;
+ padding-bottom: 1.5em;
+ margin: 0;
+ overflow: auto;
+ border-right: 3px solid #f8f8f8;
+ }
+
+ #toc .toclevel1 {
+ margin-top: 0.5em;
+ }
+
+ #toc .toclevel2 {
+ margin-top: 0.25em;
+ display: list-item;
+ /* OLD color: #aaaaaa; */
+ color: #990000;
+ }
+
+ #toctitle {
+ margin-top: 0.5em;
+ }
+}
diff --git a/doc/stylesheets/volnitsky-manpage.css b/doc/stylesheets/volnitsky-manpage.css
new file mode 100644
index 00000000..75a2dda0
--- /dev/null
+++ b/doc/stylesheets/volnitsky-manpage.css
@@ -0,0 +1 @@
+/* Empty placeholder file */
diff --git a/doc/stylesheets/volnitsky.css b/doc/stylesheets/volnitsky.css
new file mode 100644
index 00000000..b6c4a156
--- /dev/null
+++ b/doc/stylesheets/volnitsky.css
@@ -0,0 +1,435 @@
+/*
+ * AsciiDoc 'volnitsky' theme for xhtml11 and html5 backends.
+ * Based on css from http://volnitsky.com, which was in turn based on default
+ * theme from AsciiDoc
+ *
+ * FIXME: The stlying is still a bit rough in places.
+ *
+ */
+
+/* Default font. */
+body {
+ font-family: Georgia,"Times New Roman",Times,serif;
+}
+
+/* Title font. */
+h1, h2, h3, h4, h5, h6,
+div.title, caption.title,
+thead, p.table.header,
+#toctitle,
+#author, #revnumber, #revdate, #revremark,
+#footer {
+ font-family: Candara,Arial,sans-serif;
+}
+
+
+#toc a {
+ border-bottom: 1px dotted #999999;
+ color: #3A3A4D !important;
+ text-decoration: none !important;
+}
+#toc a:hover {
+ border-bottom: 1px solid #6D4100;
+ color: #6D4100 !important;
+ text-decoration: none !important;
+}
+a { color: #666688; text-decoration: none; border-bottom: 1px dotted #666688; }
+a:visited { color: #615FA0; border-bottom: 1px dotted #615FA0; }
+a:hover { color: #6D4100; border-bottom: 1px solid #6D4100; }
+
+em {
+ font-style: italic;
+ color: #444466;
+}
+
+strong {
+ font-weight: bold;
+ color: #444466;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color: #666688;
+ margin-bottom: 0.5em;
+ line-height: 1.3;
+ letter-spacing:+0.15em;
+}
+
+h1, h2, h3 { border-bottom: 2px solid #ccd; }
+h2 { padding-top: 0.5em; }
+h3 { float: left; }
+h3 + * { clear: left; }
+
+div.sectionbody {
+ margin-left: 0;
+}
+
+hr {
+ border: 1px solid #444466;
+}
+
+p {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+ul, ol, li > p {
+ margin-top: 0;
+}
+
+pre {
+ padding: 0;
+ margin: 0;
+}
+
+#author {
+ color: #444466;
+ font-weight: bold;
+ font-size: 1.1em;
+}
+
+#footer {
+ font-size: small;
+ border-top: 2px solid silver;
+ padding-top: 0.5em;
+ margin-top: 4.0em;
+}
+
+#footer-text {
+ float: left;
+ padding-bottom: 0.5em;
+}
+
+#footer-badges {
+ float: right;
+ padding-bottom: 0.5em;
+}
+
+#preamble {
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+}
+
+div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
+div.admonitionblock {
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+}
+
+div.admonitionblock {
+ margin-top: 2.5em;
+ margin-bottom: 2.5em;
+}
+
+div.content { /* Block element content. */
+ padding: 0;
+}
+
+/* Block element titles. */
+div.title, caption.title {
+ color: #444466;
+ font-weight: bold;
+ text-align: left;
+ margin-top: 1.0em;
+ margin-bottom: 0.5em;
+}
+div.title + * {
+ margin-top: 0;
+}
+
+td div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content + div.title {
+ margin-top: 0.0em;
+}
+
+div.sidebarblock > div.content {
+ background: #ffffee;
+ border: 1px solid silver;
+ padding: 0.5em;
+}
+
+div.listingblock > div.content {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ padding: 0.5em;
+}
+
+div.quoteblock {
+ padding-left: 2.0em;
+ margin-right: 10%;
+}
+div.quoteblock > div.attribution {
+ padding-top: 0.5em;
+ text-align: right;
+}
+
+div.verseblock {
+ padding-left: 2.0em;
+ margin-right: 10%;
+}
+div.verseblock > pre.content {
+ font-family: inherit;
+}
+div.verseblock > div.attribution {
+ padding-top: 0.75em;
+ text-align: left;
+}
+/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
+div.verseblock + div.attribution {
+ text-align: left;
+}
+
+div.admonitionblock .icon {
+ vertical-align: top;
+ font-size: 1.1em;
+ font-weight: bold;
+ text-decoration: underline;
+ color: #444466;
+ padding-right: 0.5em;
+}
+div.admonitionblock td.content {
+ padding-left: 0.5em;
+ border-left: 2px solid silver;
+}
+
+div.exampleblock > div.content {
+ border-left: 2px solid silver;
+ padding: 0.5em;
+}
+
+div.imageblock div.content { padding-left: 0; }
+span.image img { border-style: none; }
+a.image:visited { color: white; }
+
+dl {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+dt {
+ margin-top: 0.5em;
+ margin-bottom: 0;
+ font-style: normal;
+ color: #444466;
+}
+dd > *:first-child {
+ margin-top: 0.1em;
+}
+
+ul, ol {
+ list-style-position: outside;
+}
+ol.arabic {
+ list-style-type: decimal;
+}
+ol.loweralpha {
+ list-style-type: lower-alpha;
+}
+ol.upperalpha {
+ list-style-type: upper-alpha;
+}
+ol.lowerroman {
+ list-style-type: lower-roman;
+}
+ol.upperroman {
+ list-style-type: upper-roman;
+}
+
+div.compact ul, div.compact ol,
+div.compact p, div.compact p,
+div.compact div, div.compact div {
+ margin-top: 0.1em;
+ margin-bottom: 0.1em;
+}
+
+div.tableblock > table {
+ border: 3px solid #444466;
+}
+thead {
+ font-weight: bold;
+ color: #444466;
+}
+tfoot {
+ font-weight: bold;
+}
+td > div.verse {
+ white-space: pre;
+}
+p.table {
+ margin-top: 0;
+}
+/* Because the table frame attribute is overriden by CSS in most browsers. */
+div.tableblock > table[frame="void"] {
+ border-style: none;
+}
+div.tableblock > table[frame="hsides"] {
+ border-left-style: none;
+ border-right-style: none;
+}
+div.tableblock > table[frame="vsides"] {
+ border-top-style: none;
+ border-bottom-style: none;
+}
+
+
+div.hdlist {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+div.hdlist tr {
+ padding-bottom: 15px;
+}
+dt.hdlist1.strong, td.hdlist1.strong {
+ font-weight: bold;
+}
+td.hdlist1 {
+ vertical-align: top;
+ font-style: normal;
+ padding-right: 0.8em;
+ color: #444466;
+}
+td.hdlist2 {
+ vertical-align: top;
+}
+div.hdlist.compact tr {
+ margin: 0;
+ padding-bottom: 0;
+}
+
+.comment {
+ background: yellow;
+}
+
+@media print {
+ #footer-badges { display: none; }
+}
+
+#toctitle {
+ color: #666688;
+ font-size: 1.2em;
+ font-weight: bold;
+ margin-top: 1.0em;
+ margin-bottom: 0.1em;
+}
+
+div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { margin-top: 0; margin-bottom: 0; }
+div.toclevel1 { margin-top: 0.3em; margin-left: 0; font-size: 1.0em; }
+div.toclevel2 { margin-top: 0.25em; margin-left: 2em; font-size: 0.9em; }
+div.toclevel3 { margin-left: 4em; font-size: 0.8em; }
+div.toclevel4 { margin-left: 6em; font-size: 0.8em; }
+
+body {
+ margin: 1em 5%;
+ max-width: 55em;
+ padding-left: 0;
+
+}
+
+.monospaced, tt, div.listingblock > div.content {
+ font-family: Consolas, "Andale Mono", "Courier New", monospace;
+ color: #004400;
+ background: #f4f4f4;
+ max-width: 80em;
+ line-height: 1.2em;
+}
+
+.paragraph p {
+ line-height: 1.5em;
+ margin-top: 1em;
+}
+
+.paragraph p, li, dd, .content { max-width: 45em; }
+.admonitionblock { max-width: 35em; }
+
+div.sectionbody div.ulist > ul > li {
+ list-style-type: square;
+ color: #aaa;
+}
+ div.sectionbody div.ulist > ul > li > * {
+ color: black;
+ /*font-size: 50%;*/
+ }
+
+
+div.sectionbody div.ulist > ul > li div.ulist > ul > li {
+ color: #ccd ;
+}
+ div.sectionbody div.ulist > ul > li div.ulist > ul > li > * {
+ color: black ;
+ }
+
+em {
+ font-style: normal ! important;
+ font-weight: bold ! important;
+ color: #662222 ! important;
+ letter-spacing:+0.08em ! important;
+}
+
+
+/*
+ * html5 specific
+ *
+ * */
+
+table.tableblock {
+ margin-top: 1.0em;
+ margin-bottom: 1.5em;
+}
+thead, p.tableblock.header {
+ font-weight: bold;
+ color: #666688;
+}
+p.tableblock {
+ margin-top: 0;
+}
+table.tableblock {
+ border-width: 3px;
+ border-spacing: 0px;
+ border-style: solid;
+ border-color: #444466;
+ border-collapse: collapse;
+}
+th.tableblock, td.tableblock {
+ border-width: 1px;
+ padding: 4px;
+ border-style: solid;
+ border-color: #444466;
+}
+
+table.tableblock.frame-topbot {
+ border-left-style: hidden;
+ border-right-style: hidden;
+}
+table.tableblock.frame-sides {
+ border-top-style: hidden;
+ border-bottom-style: hidden;
+}
+table.tableblock.frame-none {
+ border-style: hidden;
+}
+
+th.tableblock.halign-left, td.tableblock.halign-left {
+ text-align: left;
+}
+th.tableblock.halign-center, td.tableblock.halign-center {
+ text-align: center;
+}
+th.tableblock.halign-right, td.tableblock.halign-right {
+ text-align: right;
+}
+
+th.tableblock.valign-top, td.tableblock.valign-top {
+ vertical-align: top;
+}
+th.tableblock.valign-middle, td.tableblock.valign-middle {
+ vertical-align: middle;
+}
+th.tableblock.valign-bottom, td.tableblock.valign-bottom {
+ vertical-align: bottom;
+}
+
+
diff --git a/doc/stylesheets/xhtml11-quirks.css b/doc/stylesheets/xhtml11-quirks.css
new file mode 100644
index 00000000..b3b46d2a
--- /dev/null
+++ b/doc/stylesheets/xhtml11-quirks.css
@@ -0,0 +1,43 @@
+/* Workarounds for IE6's broken and incomplete CSS2. */
+
+div.sidebar-content {
+ background: #ffffee;
+ border: 1px solid silver;
+ padding: 0.5em;
+}
+div.sidebar-title, div.image-title {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-weight: bold;
+ margin-top: 0.0em;
+ margin-bottom: 0.5em;
+}
+
+div.listingblock div.content {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ padding: 0.5em;
+}
+
+div.quoteblock-attribution {
+ padding-top: 0.5em;
+ text-align: right;
+}
+
+pre.verseblock-content {
+ font-family: inherit;
+}
+div.verseblock-attribution {
+ padding-top: 0.75em;
+ text-align: left;
+}
+
+div.exampleblock-content {
+ border-left: 3px solid #dddddd;
+ padding-left: 0.5em;
+}
+
+div.imageblock.latex div.image-title { margin-top: 0.5em; }
+
+/* IE6 sets dynamically generated links as visited. */
+div#toc a:visited { color: blue; }
diff --git a/doc/stylesheets/xhtml11.css b/doc/stylesheets/xhtml11.css
new file mode 100644
index 00000000..1e6bf5a4
--- /dev/null
+++ b/doc/stylesheets/xhtml11.css
@@ -0,0 +1,333 @@
+/* Debug borders */
+p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
+/*
+ border: 1px solid red;
+*/
+}
+
+body {
+ margin: 1em 5% 1em 5%;
+}
+
+a {
+ color: blue;
+ text-decoration: underline;
+}
+a:visited {
+ color: fuchsia;
+}
+
+em {
+ font-style: italic;
+ color: navy;
+}
+
+strong {
+ font-weight: bold;
+ color: #083194;
+}
+
+tt {
+ color: navy;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color: #527bbd;
+ font-family: sans-serif;
+ margin-top: 1.2em;
+ margin-bottom: 0.5em;
+ line-height: 1.3;
+}
+
+h1, h2, h3 {
+ border-bottom: 2px solid silver;
+}
+h2 {
+ padding-top: 0.5em;
+}
+h3 {
+ float: left;
+}
+h3 + * {
+ clear: left;
+}
+
+div.sectionbody {
+ font-family: serif;
+ margin-left: 0;
+}
+
+hr {
+ border: 1px solid silver;
+}
+
+p {
+ margin-top: 0.5em;
+ margin-bottom: 0.5em;
+}
+
+ul, ol, li > p {
+ margin-top: 0;
+}
+
+pre {
+ padding: 0;
+ margin: 0;
+}
+
+span#author {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-weight: bold;
+ font-size: 1.1em;
+}
+span#email {
+}
+span#revnumber, span#revdate, span#revremark {
+ font-family: sans-serif;
+}
+
+div#footer {
+ font-family: sans-serif;
+ font-size: small;
+ border-top: 2px solid silver;
+ padding-top: 0.5em;
+ margin-top: 4.0em;
+}
+div#footer-text {
+ float: left;
+ padding-bottom: 0.5em;
+}
+div#footer-badges {
+ float: right;
+ padding-bottom: 0.5em;
+}
+
+div#preamble {
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+}
+div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
+div.admonitionblock {
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+}
+div.admonitionblock {
+ margin-top: 2.5em;
+ margin-bottom: 2.5em;
+}
+
+div.content { /* Block element content. */
+ padding: 0;
+}
+
+/* Block element titles. */
+div.title, caption.title {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-weight: bold;
+ text-align: left;
+ margin-top: 1.0em;
+ margin-bottom: 0.5em;
+}
+div.title + * {
+ margin-top: 0;
+}
+
+td div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content + div.title {
+ margin-top: 0.0em;
+}
+
+div.sidebarblock > div.content {
+ background: #ffffee;
+ border: 1px solid silver;
+ padding: 0.5em;
+}
+
+div.listingblock > div.content {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ padding: 0.5em;
+}
+
+div.quoteblock {
+ padding-left: 2.0em;
+ margin-right: 10%;
+}
+div.quoteblock > div.attribution {
+ padding-top: 0.5em;
+ text-align: right;
+}
+
+div.verseblock {
+ padding-left: 2.0em;
+ margin-right: 10%;
+}
+div.verseblock > div.content {
+ white-space: pre;
+}
+div.verseblock > div.attribution {
+ padding-top: 0.75em;
+ text-align: left;
+}
+/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
+div.verseblock + div.attribution {
+ text-align: left;
+}
+
+div.admonitionblock .icon {
+ vertical-align: top;
+ font-size: 1.1em;
+ font-weight: bold;
+ text-decoration: underline;
+ color: #527bbd;
+ padding-right: 0.5em;
+}
+div.admonitionblock td.content {
+ padding-left: 0.5em;
+ border-left: 2px solid silver;
+}
+
+div.exampleblock > div.content {
+ border-left: 2px solid silver;
+ padding: 0.5em;
+}
+
+div.imageblock div.content { padding-left: 0; }
+span.image img { border-style: none; }
+a.image:visited { color: white; }
+
+dl {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+dt {
+ margin-top: 0.5em;
+ margin-bottom: 0;
+ font-style: normal;
+ color: navy;
+}
+dd > *:first-child {
+ margin-top: 0.1em;
+}
+
+ul, ol {
+ list-style-position: outside;
+}
+ol.arabic {
+ list-style-type: decimal;
+}
+ol.loweralpha {
+ list-style-type: lower-alpha;
+}
+ol.upperalpha {
+ list-style-type: upper-alpha;
+}
+ol.lowerroman {
+ list-style-type: lower-roman;
+}
+ol.upperroman {
+ list-style-type: upper-roman;
+}
+
+div.compact ul, div.compact ol,
+div.compact p, div.compact p,
+div.compact div, div.compact div {
+ margin-top: 0.1em;
+ margin-bottom: 0.1em;
+}
+
+div.tableblock > table {
+ border: 3px solid #527bbd;
+}
+thead {
+ font-family: sans-serif;
+ font-weight: bold;
+}
+tfoot {
+ font-weight: bold;
+}
+td > div.verse {
+ white-space: pre;
+}
+p.table {
+ margin-top: 0;
+}
+/* Because the table frame attribute is overriden by CSS in most browsers. */
+div.tableblock > table[frame="void"] {
+ border-style: none;
+}
+div.tableblock > table[frame="hsides"] {
+ border-left-style: none;
+ border-right-style: none;
+}
+div.tableblock > table[frame="vsides"] {
+ border-top-style: none;
+ border-bottom-style: none;
+}
+
+
+div.hdlist {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+div.hdlist tr {
+ padding-bottom: 15px;
+}
+dt.hdlist1.strong, td.hdlist1.strong {
+ font-weight: bold;
+}
+td.hdlist1 {
+ vertical-align: top;
+ font-style: normal;
+ padding-right: 0.8em;
+ color: navy;
+}
+td.hdlist2 {
+ vertical-align: top;
+}
+div.hdlist.compact tr {
+ margin: 0;
+ padding-bottom: 0;
+}
+
+.comment {
+ background: yellow;
+}
+
+@media print {
+ div#footer-badges { display: none; }
+}
+
+div#toctitle {
+ color: #527bbd;
+ font-family: sans-serif;
+ font-size: 1.1em;
+ font-weight: bold;
+ margin-top: 1.0em;
+ margin-bottom: 0.1em;
+}
+
+div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+div.toclevel2 {
+ margin-left: 2em;
+ font-size: 0.9em;
+}
+div.toclevel3 {
+ margin-left: 4em;
+ font-size: 0.9em;
+}
+div.toclevel4 {
+ margin-left: 6em;
+ font-size: 0.9em;
+}
diff --git a/etc/classid b/etc/classid
new file mode 100644
index 00000000..22032431
--- /dev/null
+++ b/etc/classid
@@ -0,0 +1,45 @@
+###############################################################################
+#
+# ClassID <-> Name Translation Table
+#
+# This file can be used to assign names to classids for easier reference
+# in all libnl tools.
+#
+# Format:
+# <MAJ:> <NAME> # qdisc definition
+# <MAJ:MIN> <NAME> # class deifnition
+# <NAME:MIN> <NAME> # class definition referencing an
+# existing qdisc definition.
+#
+# Example:
+# 1: top # top -> 1:0
+# top:1 interactive # interactive -> 1:1
+# top:2 www # www -> 1:2
+# top:3 bulk # bulk -> 1:3
+# 2:1 test_class # test_class -> 2:1
+#
+# Illegal Example:
+# 30:1 classD
+# classD:2 invalidClass # classD refers to a class, not a qdisc
+#
+###############################################################################
+
+# <CLASSID> <NAME>
+
+# Reserved default classids
+0:0 none
+ffff:ffff root
+ffff:fff1 ingress
+
+#
+# List your classid definitions here:
+#
+
+
+
+###############################################################################
+# List of auto-generated classids
+#
+# DO NOT ADD CLASSID DEFINITIONS BELOW THIS LINE
+#
+# <CLASSID> <NAME>
diff --git a/etc/pktloc b/etc/pktloc
index db36d401..505c44e6 100644
--- a/etc/pktloc
+++ b/etc/pktloc
@@ -2,14 +2,15 @@
# Location definitions for packet matching
#
-# name alignment offset mask
-ip.version u8 net+0 0xF0
+# name alignment offset mask shift
+ip.version u8 net+0 0xF0 4
ip.hdrlen u8 net+0 0x0F
ip.diffserv u8 net+1
ip.length u16 net+2
ip.id u16 net+4
-ip.df u8 net+6 0x40
-ip.mf u8 net+6 0x20
+ip.flag.res u8 net+6 0xff 7
+ip.df u8 net+6 0x40 6
+ip.mf u8 net+6 0x20 5
ip.offset u16 net+6 0x1FFF
ip.ttl u8 net+8
ip.proto u8 net+9
@@ -17,18 +18,49 @@ ip.chksum u16 net+10
ip.src u32 net+12
ip.dst u32 net+16
+# if ip.ihl > 5
+ip.opts u32 net+20
+
+
+#
+# IP version 6
+#
+# name alignment offset mask shift
+ip6.version u8 net+0 0xF0 4
+ip6.tc u16 net+0 0xFF0 4
+ip6.flowlabel u32 net+0 0xFFFFF
+ip6.length u16 net+4
+ip6.nexthdr u8 net+6
+ip6.hoplimit u8 net+7
+ip6.src 16 net+8
+ip6.dst 16 net+24
#
# Transmission Control Protocol (TCP)
#
-# name alignment offset mask
+# name alignment offset mask shift
tcp.sport u16 tcp+0
tcp.dport u16 tcp+2
tcp.seq u32 tcp+4
tcp.ack u32 tcp+8
-tcp.off u8 tcp+12 0xF0
-tcp.reserved u8 tcp+12 0x0F
-# FLAGS
+
+# Data offset (4 bits)
+tcp.off u8 tcp+12 0xF0 4
+
+# Reserved [0 0 0] (3 bits)
+tcp.reserved u8 tcp+12 0x04 1
+
+# ECN [N C E] (3 bits)
+tcp.ecn u16 tcp+12 0x01C00 6
+
+# Individual TCP flags (0|1) (6 bits in total)
+tcp.flag.urg u8 tcp+13 0x20 5
+tcp.flag.ack u8 tcp+13 0x10 4
+tcp.flag.psh u8 tcp+13 0x08 3
+tcp.flag.rst u8 tcp+13 0x04 2
+tpc.flag.syn u8 tcp+13 0x02 1
+tcp.flag.fin u8 tcp+13 0x01
+
tcp.win u16 tcp+14
tcp.csum u16 tcp+16
tcp.urg u16 tcp+18
@@ -37,7 +69,7 @@ tcp.opts u32 tcp+20
#
# User Datagram Protocol (UDP)
#
-# name alignment offset mask
+# name alignment offset mask shift
udp.sport u16 tcp+0
udp.dport u16 tcp+2
udp.length u16 tcp+4
diff --git a/include/Makefile.am b/include/Makefile.am
index 8815a6e1..765cf5a9 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -1,6 +1,8 @@
# -*- Makefile -*-
-nobase_include_HEADERS = \
+libnlincludedir = $(includedir)/libnl@MAJ_VERSION@
+
+nobase_libnlinclude_HEADERS = \
netlink/fib_lookup/lookup.h \
netlink/fib_lookup/request.h \
netlink/genl/ctrl.h \
@@ -8,6 +10,7 @@ nobase_include_HEADERS = \
netlink/genl/genl.h \
netlink/genl/mngt.h \
netlink/netfilter/ct.h \
+ netlink/netfilter/exp.h \
netlink/netfilter/log.h \
netlink/netfilter/log_msg.h \
netlink/netfilter/netfilter.h \
@@ -16,42 +19,64 @@ nobase_include_HEADERS = \
netlink/netfilter/queue_msg.h \
netlink/addr.h \
netlink/attr.h \
- netlink/cache-api.h \
netlink/cache.h \
netlink/data.h \
netlink/errno.h \
netlink/handlers.h \
+ netlink/hash.h \
+ netlink/hashtable.h \
netlink/list.h \
netlink/msg.h \
netlink/netlink-compat.h \
netlink/netlink-kernel.h \
netlink/netlink.h \
- netlink/object-api.h \
netlink/object.h \
+ netlink/route/action.h \
+ netlink/route/act/mirred.h \
+ netlink/route/cls/ematch/cmp.h \
+ netlink/route/cls/ematch/meta.h \
+ netlink/route/cls/ematch/nbyte.h \
+ netlink/route/cls/ematch/text.h \
+ netlink/route/cls/basic.h \
+ netlink/route/cls/cgroup.h \
+ netlink/route/cls/ematch.h \
netlink/route/cls/fw.h \
netlink/route/cls/police.h \
netlink/route/cls/u32.h \
+ netlink/route/link/api.h \
+ netlink/route/link/bonding.h \
+ netlink/route/link/bridge.h \
+ netlink/route/link/can.h \
+ netlink/route/link/inet.h \
netlink/route/link/info-api.h \
+ netlink/route/link/macvlan.h \
netlink/route/link/vlan.h \
- netlink/route/sch/cbq.h \
- netlink/route/sch/dsmark.h \
- netlink/route/sch/fifo.h \
- netlink/route/sch/htb.h \
- netlink/route/sch/netem.h \
- netlink/route/sch/prio.h \
- netlink/route/sch/red.h \
- netlink/route/sch/sfq.h \
- netlink/route/sch/tbf.h \
+ netlink/route/link/vxlan.h \
+ netlink/route/link/veth.h \
+ netlink/route/link/ip6tnl.h \
+ netlink/route/link/ipgre.h \
+ netlink/route/link/ipip.h \
+ netlink/route/link/ipvti.h \
+ netlink/route/link/sit.h \
+ netlink/route/qdisc/cbq.h \
+ netlink/route/qdisc/dsmark.h \
+ netlink/route/qdisc/fifo.h \
+ netlink/route/qdisc/htb.h \
+ netlink/route/qdisc/netem.h \
+ netlink/route/qdisc/prio.h \
+ netlink/route/qdisc/red.h \
+ netlink/route/qdisc/sfq.h \
+ netlink/route/qdisc/tbf.h \
+ netlink/route/qdisc/plug.h \
+ netlink/route/qdisc/fq_codel.h \
netlink/route/addr.h \
- netlink/route/class-modules.h \
netlink/route/class.h \
- netlink/route/classifier-modules.h \
netlink/route/classifier.h \
netlink/route/link.h \
netlink/route/neighbour.h \
netlink/route/neightbl.h \
netlink/route/nexthop.h \
- netlink/route/qdisc-modules.h \
+ netlink/route/pktloc.h \
netlink/route/qdisc.h \
netlink/route/route.h \
netlink/route/rtnl.h \
@@ -60,4 +85,69 @@ nobase_include_HEADERS = \
netlink/socket.h \
netlink/types.h \
netlink/utils.h \
- netlink/version.h
+ netlink/version.h \
+ netlink/cache-api.h \
+ netlink/object-api.h \
+ netlink/route/tc-api.h \
+ netlink/idiag/idiagnl.h \
+ netlink/idiag/meminfo.h \
+ netlink/idiag/msg.h \
+ netlink/idiag/req.h \
+ netlink/idiag/vegasinfo.h
+
+if ENABLE_CLI
+nobase_libnlinclude_HEADERS += \
+ netlink/cli/addr.h \
+ netlink/cli/class.h \
+ netlink/cli/cls.h \
+ netlink/cli/ct.h \
+ netlink/cli/exp.h \
+ netlink/cli/link.h \
+ netlink/cli/neigh.h \
+ netlink/cli/qdisc.h \
+ netlink/cli/route.h \
+ netlink/cli/rule.h \
+ netlink/cli/tc.h \
+ netlink/cli/utils.h
+endif
+
+noinst_HEADERS = \
+ linux/fib_rules.h \
+ linux/genetlink.h \
+ linux/gen_stats.h \
+ linux/if_addr.h \
+ linux/if_arp.h \
+ linux/if_ether.h \
+ linux/if.h \
+ linux/if_bridge.h \
+ linux/if_link.h \
+ linux/if_tunnel.h \
+ linux/if_vlan.h \
+ linux/ip.h \
+ linux/ip_mp_alg.h \
+ linux/ipv6.h \
+ linux/can/netlink.h \
+ linux/neighbour.h \
+ linux/netfilter.h \
+ linux/netfilter/nf_conntrack_common.h \
+ linux/netfilter/nfnetlink_compat.h \
+ linux/netfilter/nfnetlink_conntrack.h \
+ linux/netfilter/nfnetlink.h \
+ linux/netfilter/nfnetlink_log.h \
+ linux/netfilter/nfnetlink_queue.h \
+ linux/netlink.h \
+ linux/pkt_cls.h \
+ linux/tc_act/tc_mirred.h \
+ linux/pkt_sched.h \
+ linux/rtnetlink.h \
+ linux/snmp.h \
+ linux/tc_ematch/tc_em_meta.h \
+ netlink-private/genl.h \
+ netlink-private/netlink.h \
+ netlink-private/socket.h \
+ netlink-private/tc.h \
+ netlink-private/types.h \
+ netlink-private/cache-api.h \
+ netlink-private/object-api.h \
+ netlink-private/route/link/api.h \
+ netlink-private/route/tc-api.h
diff --git a/include/defs.h b/include/defs.h
new file mode 100644
index 00000000..62785cb3
--- /dev/null
+++ b/include/defs.h
@@ -0,0 +1,86 @@
+/* lib/defs.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to 1 to disable pthreads */
+#undef DISABLE_PTHREADS
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#undef HAVE_DLFCN_H
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `m' library (-lm). */
+#undef HAVE_LIBM
+
+/* Define to 1 if you have the `pthread' library (-lpthread). */
+#undef HAVE_LIBPTHREAD
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+ */
+#undef LT_OBJDIR
+
+/* Define to 1 to enable debugging */
+#undef NL_DEBUG
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+#undef NO_MINUS_C_MINUS_O
+
+/* Name of package */
+#undef PACKAGE
+
+/* 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 home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Version number of package */
+#undef VERSION
+
+/* 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/include/linux/can/netlink.h b/include/linux/can/netlink.h
new file mode 100644
index 00000000..14966ddb
--- /dev/null
+++ b/include/linux/can/netlink.h
@@ -0,0 +1,122 @@
+/*
+ * linux/can/netlink.h
+ *
+ * Definitions for the CAN netlink interface
+ *
+ * Copyright (c) 2009 Wolfgang Grandegger <wg@grandegger.com>
+ *
+ */
+
+#ifndef CAN_NETLINK_H
+#define CAN_NETLINK_H
+
+#include <linux/types.h>
+
+/*
+ * CAN bit-timing parameters
+ *
+ * For further information, please read chapter "8 BIT TIMING
+ * REQUIREMENTS" of the "Bosch CAN Specification version 2.0"
+ * at http://www.semiconductors.bosch.de/pdf/can2spec.pdf.
+ */
+struct can_bittiming {
+ __u32 bitrate; /* Bit-rate in bits/second */
+ __u32 sample_point; /* Sample point in one-tenth of a percent */
+ __u32 tq; /* Time quanta (TQ) in nanoseconds */
+ __u32 prop_seg; /* Propagation segment in TQs */
+ __u32 phase_seg1; /* Phase buffer segment 1 in TQs */
+ __u32 phase_seg2; /* Phase buffer segment 2 in TQs */
+ __u32 sjw; /* Synchronisation jump width in TQs */
+ __u32 brp; /* Bit-rate prescaler */
+};
+
+/*
+ * CAN harware-dependent bit-timing constant
+ *
+ * Used for calculating and checking bit-timing parameters
+ */
+struct can_bittiming_const {
+ char name[16]; /* Name of the CAN controller hardware */
+ __u32 tseg1_min; /* Time segement 1 = prop_seg + phase_seg1 */
+ __u32 tseg1_max;
+ __u32 tseg2_min; /* Time segement 2 = phase_seg2 */
+ __u32 tseg2_max;
+ __u32 sjw_max; /* Synchronisation jump width */
+ __u32 brp_min; /* Bit-rate prescaler */
+ __u32 brp_max;
+ __u32 brp_inc;
+};
+
+/*
+ * CAN clock parameters
+ */
+struct can_clock {
+ __u32 freq; /* CAN system clock frequency in Hz */
+};
+
+/*
+ * CAN operational and error states
+ */
+enum can_state {
+ CAN_STATE_ERROR_ACTIVE = 0, /* RX/TX error count < 96 */
+ CAN_STATE_ERROR_WARNING, /* RX/TX error count < 128 */
+ CAN_STATE_ERROR_PASSIVE, /* RX/TX error count < 256 */
+ CAN_STATE_BUS_OFF, /* RX/TX error count >= 256 */
+ CAN_STATE_STOPPED, /* Device is stopped */
+ CAN_STATE_SLEEPING, /* Device is sleeping */
+ CAN_STATE_MAX
+};
+
+/*
+ * CAN bus error counters
+ */
+struct can_berr_counter {
+ __u16 txerr;
+ __u16 rxerr;
+};
+
+/*
+ * CAN controller mode
+ */
+struct can_ctrlmode {
+ __u32 mask;
+ __u32 flags;
+};
+
+#define CAN_CTRLMODE_LOOPBACK 0x01 /* Loopback mode */
+#define CAN_CTRLMODE_LISTENONLY 0x02 /* Listen-only mode */
+#define CAN_CTRLMODE_3_SAMPLES 0x04 /* Triple sampling mode */
+#define CAN_CTRLMODE_ONE_SHOT 0x08 /* One-Shot mode */
+#define CAN_CTRLMODE_BERR_REPORTING 0x10 /* Bus-error reporting */
+
+/*
+ * CAN device statistics
+ */
+struct can_device_stats {
+ __u32 bus_error; /* Bus errors */
+ __u32 error_warning; /* Changes to error warning state */
+ __u32 error_passive; /* Changes to error passive state */
+ __u32 bus_off; /* Changes to bus off state */
+ __u32 arbitration_lost; /* Arbitration lost errors */
+ __u32 restarts; /* CAN controller re-starts */
+};
+
+/*
+ * CAN netlink interface
+ */
+enum {
+ IFLA_CAN_UNSPEC,
+ IFLA_CAN_BITTIMING,
+ IFLA_CAN_BITTIMING_CONST,
+ IFLA_CAN_CLOCK,
+ IFLA_CAN_STATE,
+ IFLA_CAN_CTRLMODE,
+ IFLA_CAN_RESTART_MS,
+ IFLA_CAN_RESTART,
+ IFLA_CAN_BERR_COUNTER,
+ __IFLA_CAN_MAX
+};
+
+#define IFLA_CAN_MAX (__IFLA_CAN_MAX - 1)
+
+#endif /* CAN_NETLINK_H */
diff --git a/include/linux/fib_rules.h b/include/linux/fib_rules.h
new file mode 100644
index 00000000..ed4504a8
--- /dev/null
+++ b/include/linux/fib_rules.h
@@ -0,0 +1,69 @@
+#ifndef __LINUX_FIB_RULES_H
+#define __LINUX_FIB_RULES_H
+
+/* rule is permanent, and cannot be deleted */
+#define FIB_RULE_PERMANENT 0x00000001
+#define FIB_RULE_INVERT 0x00000002
+#define FIB_RULE_UNRESOLVED 0x00000004
+#define FIB_RULE_IIF_DETACHED 0x00000008
+#define FIB_RULE_DEV_DETACHED FIB_RULE_IIF_DETACHED
+#define FIB_RULE_OIF_DETACHED 0x00000010
+
+/* try to find source address in routing lookups */
+#define FIB_RULE_FIND_SADDR 0x00010000
+
+struct fib_rule_hdr {
+ __u8 family;
+ __u8 dst_len;
+ __u8 src_len;
+ __u8 tos;
+
+ __u8 table;
+ __u8 res1; /* reserved */
+ __u8 res2; /* reserved */
+ __u8 action;
+
+ __u32 flags;
+};
+
+enum {
+ FRA_UNSPEC,
+ FRA_DST, /* destination address */
+ FRA_SRC, /* source address */
+ FRA_IIFNAME, /* interface name */
+#define FRA_IFNAME FRA_IIFNAME
+ FRA_GOTO, /* target to jump to (FR_ACT_GOTO) */
+ FRA_UNUSED2,
+ FRA_PRIORITY, /* priority/preference */
+ FRA_UNUSED3,
+ FRA_UNUSED4,
+ FRA_UNUSED5,
+ FRA_FWMARK, /* mark */
+ FRA_FLOW, /* flow/class id */
+ FRA_UNUSED6,
+ FRA_UNUSED7,
+ FRA_UNUSED8,
+ FRA_TABLE, /* Extended table id */
+ FRA_FWMASK, /* mask for netfilter mark */
+ FRA_OIFNAME,
+ __FRA_MAX
+};
+
+#define FRA_MAX (__FRA_MAX - 1)
+
+enum {
+ FR_ACT_UNSPEC,
+ FR_ACT_TO_TBL, /* Pass to fixed table */
+ FR_ACT_GOTO, /* Jump to another rule */
+ FR_ACT_NOP, /* No operation */
+ FR_ACT_RES3,
+ FR_ACT_RES4,
+ FR_ACT_BLACKHOLE, /* Drop without notification */
+ FR_ACT_UNREACHABLE, /* Drop with ENETUNREACH */
+ FR_ACT_PROHIBIT, /* Drop with EACCES */
+ __FR_ACT_MAX,
+};
+
+#define FR_ACT_MAX (__FR_ACT_MAX - 1)
+
+#endif
diff --git a/include/linux/gen_stats.h b/include/linux/gen_stats.h
index ca60fc17..552c8a0a 100644
--- a/include/linux/gen_stats.h
+++ b/include/linux/gen_stats.h
@@ -1,6 +1,8 @@
#ifndef __LINUX_GEN_STATS_H
#define __LINUX_GEN_STATS_H
+#include <linux/types.h>
+
enum {
TCA_STATS_UNSPEC,
TCA_STATS_BASIC,
@@ -12,33 +14,38 @@ enum {
#define TCA_STATS_MAX (__TCA_STATS_MAX - 1)
/**
+ * struct gnet_stats_basic - byte/packet throughput statistics
* @bytes: number of seen bytes
* @packets: number of seen packets
*/
-struct gnet_stats_basic
-{
+struct gnet_stats_basic {
__u64 bytes;
__u32 packets;
};
+struct gnet_stats_basic_packed {
+ __u64 bytes;
+ __u32 packets;
+} __attribute__ ((packed));
/**
+ * struct gnet_stats_rate_est - rate estimator
* @bps: current byte rate
* @pps: current packet rate
*/
-struct gnet_stats_rate_est
-{
+struct gnet_stats_rate_est {
__u32 bps;
__u32 pps;
};
/**
+ * struct gnet_stats_queue - queuing statistics
* @qlen: queue length
* @backlog: backlog size of queue
* @drops: number of dropped packets
* @requeues: number of requeues
+ * @overlimits: number of enqueues over the limit
*/
-struct gnet_stats_queue
-{
+struct gnet_stats_queue {
__u32 qlen;
__u32 backlog;
__u32 drops;
@@ -47,11 +54,11 @@ struct gnet_stats_queue
};
/**
+ * struct gnet_estimator - rate estimator configuration
* @interval: sampling period
* @ewma_log: the log of measurement window weight
*/
-struct gnet_estimator
-{
+struct gnet_estimator {
signed char interval;
unsigned char ewma_log;
};
diff --git a/include/linux/genetlink.h b/include/linux/genetlink.h
index 7da02c93..b834ef6d 100644
--- a/include/linux/genetlink.h
+++ b/include/linux/genetlink.h
@@ -1,6 +1,7 @@
#ifndef __LINUX_GENERIC_NETLINK_H
#define __LINUX_GENERIC_NETLINK_H
+#include <linux/types.h>
#include <linux/netlink.h>
#define GENL_NAMSIZ 16 /* length of family name */
diff --git a/include/linux/if_addr.h b/include/linux/if_addr.h
index 43f3beda..7d4de855 100644
--- a/include/linux/if_addr.h
+++ b/include/linux/if_addr.h
@@ -1,10 +1,10 @@
#ifndef __LINUX_IF_ADDR_H
#define __LINUX_IF_ADDR_H
+#include <linux/types.h>
#include <linux/netlink.h>
-struct ifaddrmsg
-{
+struct ifaddrmsg {
__u8 ifa_family;
__u8 ifa_prefixlen; /* The prefix length */
__u8 ifa_flags; /* Flags */
@@ -18,9 +18,11 @@ struct ifaddrmsg
* 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.
+ *
+ * IFA_FLAGS is a u32 attribute that extends the u8 field ifa_flags.
+ * If present, the value from struct ifaddrmsg will be ignored.
*/
-enum
-{
+enum {
IFA_UNSPEC,
IFA_ADDRESS,
IFA_LOCAL,
@@ -29,6 +31,7 @@ enum
IFA_ANYCAST,
IFA_CACHEINFO,
IFA_MULTICAST,
+ IFA_FLAGS,
__IFA_MAX,
};
@@ -40,23 +43,19 @@ enum
#define IFA_F_NODAD 0x02
#define IFA_F_OPTIMISTIC 0x04
+#define IFA_F_DADFAILED 0x08
#define IFA_F_HOMEADDRESS 0x10
#define IFA_F_DEPRECATED 0x20
#define IFA_F_TENTATIVE 0x40
#define IFA_F_PERMANENT 0x80
+#define IFA_F_MANAGETEMPADDR 0x100
+#define IFA_F_NOPREFIXROUTE 0x200
-struct ifa_cacheinfo
-{
+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
index 1c7417b4..e04cd2cd 100644
--- a/include/linux/if_arp.h
+++ b/include/linux/if_arp.h
@@ -23,6 +23,8 @@
#ifndef _LINUX_IF_ARP_H
#define _LINUX_IF_ARP_H
+#include <linux/netdevice.h>
+
/* ARP protocol HARDWARE identifiers. */
#define ARPHRD_NETROM 0 /* from KA9Q: NET/ROM pseudo */
#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */
@@ -50,6 +52,7 @@
#define ARPHRD_ROSE 270
#define ARPHRD_X25 271 /* CCITT X.25 */
#define ARPHRD_HWX25 272 /* Boards with X.25 in firmware */
+#define ARPHRD_CAN 280 /* Controller Area Network */
#define ARPHRD_PPP 512
#define ARPHRD_CISCO 513 /* Cisco HDLC */
#define ARPHRD_HDLC ARPHRD_CISCO
@@ -83,6 +86,11 @@
#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_IEEE802154 804
+
+#define ARPHRD_PHONET 820 /* PhoNet media type */
+#define ARPHRD_PHONET_PIPE 821 /* PhoNet pipe header */
+#define ARPHRD_CAIF 822 /* CAIF media type */
#define ARPHRD_VOID 0xFFFF /* Void type, nothing is known */
#define ARPHRD_NONE 0xFFFE /* zero header length */
@@ -126,13 +134,12 @@ struct arpreq_old {
* 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 */
+struct arphdr {
+ __be16 ar_hrd; /* format of hardware address */
+ __be16 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) */
+ __be16 ar_op; /* ARP opcode (command) */
#if 0
/*
diff --git a/include/linux/if_bad.h b/include/linux/if_bad.h
index 857ce1e5..bbb3ea37 100644
--- a/include/linux/if_bad.h
+++ b/include/linux/if_bad.h
@@ -19,9 +19,8 @@
#ifndef _LINUX_IF_H
#define _LINUX_IF_H
-#include <linux/types.h> /* for "__kernel_caddr_t" et al */
-
#define IFNAMSIZ 16
+#define IFALIASZ 256
/* Standard interface flags (netdevice->flags). */
#define IFF_UP 0x1 /* interface is up */
@@ -61,6 +60,18 @@
#define IFF_BONDING 0x20 /* bonding master or slave */
#define IFF_SLAVE_NEEDARP 0x40 /* need ARPs for validation */
#define IFF_ISATAP 0x80 /* ISATAP interface (RFC4214) */
+#define IFF_MASTER_ARPMON 0x100 /* bonding master, ARP mon in use */
+#define IFF_WAN_HDLC 0x200 /* WAN HDLC device */
+#define IFF_XMIT_DST_RELEASE 0x400 /* dev_hard_start_xmit() is allowed to
+ * release skb->dst
+ */
+#define IFF_DONT_BRIDGE 0x800 /* disallow bridging this ether dev */
+#define IFF_IN_NETPOLL 0x1000 /* whether we are processing netpoll */
+#define IFF_DISABLE_NETPOLL 0x2000 /* disable netpoll at run-time */
+#define IFF_MACVLAN_PORT 0x4000 /* device used as macvlan port */
+#define IFF_BRIDGE_PORT 0x8000 /* device used as bridge port */
+#define IFF_OVS_DATAPATH 0x10000 /* device used as Open vSwitch
+ * datapath port */
#define IF_GET_IFACE 0x0001 /* for querying only */
#define IF_GET_PROTO 0x0002
@@ -106,6 +117,12 @@ enum {
IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */
};
+/* carrier state */
+enum {
+ IF_CARRIER_DOWN,
+ IF_CARRIER_UP
+};
+
/*
* Device mapping structure. I'd just gone off and designed a
* beautiful scheme using only loadable modules with arguments
@@ -116,8 +133,7 @@ enum {
* being very small might be worth keeping for clean configuration.
*/
-struct ifmap
-{
+struct ifmap {
unsigned long mem_start;
unsigned long mem_end;
unsigned short base_addr;
@@ -127,5 +143,4 @@ struct ifmap
/* 3 bytes spare */
};
-
#endif /* _LINUX_IF_H */
diff --git a/include/linux/if_bridge.h b/include/linux/if_bridge.h
new file mode 100644
index 00000000..5db29751
--- /dev/null
+++ b/include/linux/if_bridge.h
@@ -0,0 +1,185 @@
+/*
+ * Linux ethernet bridge
+ *
+ * Authors:
+ * Lennert Buytenhek <buytenh@gnu.org>
+ *
+ * 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 _UAPI_LINUX_IF_BRIDGE_H
+#define _UAPI_LINUX_IF_BRIDGE_H
+
+#include <linux/types.h>
+
+#define SYSFS_BRIDGE_ATTR "bridge"
+#define SYSFS_BRIDGE_FDB "brforward"
+#define SYSFS_BRIDGE_PORT_SUBDIR "brif"
+#define SYSFS_BRIDGE_PORT_ATTR "brport"
+#define SYSFS_BRIDGE_PORT_LINK "bridge"
+
+#define BRCTL_VERSION 1
+
+#define BRCTL_GET_VERSION 0
+#define BRCTL_GET_BRIDGES 1
+#define BRCTL_ADD_BRIDGE 2
+#define BRCTL_DEL_BRIDGE 3
+#define BRCTL_ADD_IF 4
+#define BRCTL_DEL_IF 5
+#define BRCTL_GET_BRIDGE_INFO 6
+#define BRCTL_GET_PORT_LIST 7
+#define BRCTL_SET_BRIDGE_FORWARD_DELAY 8
+#define BRCTL_SET_BRIDGE_HELLO_TIME 9
+#define BRCTL_SET_BRIDGE_MAX_AGE 10
+#define BRCTL_SET_AGEING_TIME 11
+#define BRCTL_SET_GC_INTERVAL 12
+#define BRCTL_GET_PORT_INFO 13
+#define BRCTL_SET_BRIDGE_STP_STATE 14
+#define BRCTL_SET_BRIDGE_PRIORITY 15
+#define BRCTL_SET_PORT_PRIORITY 16
+#define BRCTL_SET_PATH_COST 17
+#define BRCTL_GET_FDB_ENTRIES 18
+
+#define BR_STATE_DISABLED 0
+#define BR_STATE_LISTENING 1
+#define BR_STATE_LEARNING 2
+#define BR_STATE_FORWARDING 3
+#define BR_STATE_BLOCKING 4
+
+struct __bridge_info {
+ __u64 designated_root;
+ __u64 bridge_id;
+ __u32 root_path_cost;
+ __u32 max_age;
+ __u32 hello_time;
+ __u32 forward_delay;
+ __u32 bridge_max_age;
+ __u32 bridge_hello_time;
+ __u32 bridge_forward_delay;
+ __u8 topology_change;
+ __u8 topology_change_detected;
+ __u8 root_port;
+ __u8 stp_enabled;
+ __u32 ageing_time;
+ __u32 gc_interval;
+ __u32 hello_timer_value;
+ __u32 tcn_timer_value;
+ __u32 topology_change_timer_value;
+ __u32 gc_timer_value;
+};
+
+struct __port_info {
+ __u64 designated_root;
+ __u64 designated_bridge;
+ __u16 port_id;
+ __u16 designated_port;
+ __u32 path_cost;
+ __u32 designated_cost;
+ __u8 state;
+ __u8 top_change_ack;
+ __u8 config_pending;
+ __u8 unused0;
+ __u32 message_age_timer_value;
+ __u32 forward_delay_timer_value;
+ __u32 hold_timer_value;
+};
+
+struct __fdb_entry {
+ __u8 mac_addr[6];
+ __u8 port_no;
+ __u8 is_local;
+ __u32 ageing_timer_value;
+ __u8 port_hi;
+ __u8 pad0;
+ __u16 unused;
+};
+
+/* Bridge Flags */
+#define BRIDGE_FLAGS_MASTER 1 /* Bridge command to/from master */
+#define BRIDGE_FLAGS_SELF 2 /* Bridge command to/from lowerdev */
+
+#define BRIDGE_MODE_VEB 0 /* Default loopback mode */
+#define BRIDGE_MODE_VEPA 1 /* 802.1Qbg defined VEPA mode */
+
+/* Bridge management nested attributes
+ * [IFLA_AF_SPEC] = {
+ * [IFLA_BRIDGE_FLAGS]
+ * [IFLA_BRIDGE_MODE]
+ * }
+ */
+enum {
+ IFLA_BRIDGE_FLAGS,
+ IFLA_BRIDGE_MODE,
+ __IFLA_BRIDGE_MAX,
+};
+#define IFLA_BRIDGE_MAX (__IFLA_BRIDGE_MAX - 1)
+
+/* Bridge multicast database attributes
+ * [MDBA_MDB] = {
+ * [MDBA_MDB_ENTRY] = {
+ * [MDBA_MDB_ENTRY_INFO]
+ * }
+ * }
+ * [MDBA_ROUTER] = {
+ * [MDBA_ROUTER_PORT]
+ * }
+ */
+enum {
+ MDBA_UNSPEC,
+ MDBA_MDB,
+ MDBA_ROUTER,
+ __MDBA_MAX,
+};
+#define MDBA_MAX (__MDBA_MAX - 1)
+
+enum {
+ MDBA_MDB_UNSPEC,
+ MDBA_MDB_ENTRY,
+ __MDBA_MDB_MAX,
+};
+#define MDBA_MDB_MAX (__MDBA_MDB_MAX - 1)
+
+enum {
+ MDBA_MDB_ENTRY_UNSPEC,
+ MDBA_MDB_ENTRY_INFO,
+ __MDBA_MDB_ENTRY_MAX,
+};
+#define MDBA_MDB_ENTRY_MAX (__MDBA_MDB_ENTRY_MAX - 1)
+
+enum {
+ MDBA_ROUTER_UNSPEC,
+ MDBA_ROUTER_PORT,
+ __MDBA_ROUTER_MAX,
+};
+#define MDBA_ROUTER_MAX (__MDBA_ROUTER_MAX - 1)
+
+struct br_port_msg {
+ __u8 family;
+ __u32 ifindex;
+};
+
+struct br_mdb_entry {
+ __u32 ifindex;
+#define MDB_TEMPORARY 0
+#define MDB_PERMANENT 1
+ __u8 state;
+ struct {
+ union {
+ __be32 ip4;
+ struct in6_addr ip6;
+ } u;
+ __be16 proto;
+ } addr;
+};
+
+enum {
+ MDBA_SET_ENTRY_UNSPEC,
+ MDBA_SET_ENTRY,
+ __MDBA_SET_ENTRY_MAX,
+};
+#define MDBA_SET_ENTRY_MAX (__MDBA_SET_ENTRY_MAX - 1)
+
+#endif /* _UAPI_LINUX_IF_BRIDGE_H */
diff --git a/include/linux/if_ether.h b/include/linux/if_ether.h
index ab7df166..a6af32d7 100644
--- a/include/linux/if_ether.h
+++ b/include/linux/if_ether.h
@@ -9,7 +9,7 @@
*
* Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
* Donald Becker, <becker@super.org>
- * Alan Cox, <alan@redhat.com>
+ * Alan Cox, <alan@lxorguk.ukuu.org.uk>
* Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
*
* This program is free software; you can redistribute it and/or
@@ -17,13 +17,15 @@
* 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
+#include <linux/types.h>
+
/*
* IEEE 802.3 Ethernet magic constants. The frame sizes omit the preamble
- * and FCS/CRC (frame check sequence).
+ * and FCS/CRC (frame check sequence).
*/
#define ETH_ALEN 6 /* Octets in one ethernet addr */
@@ -31,6 +33,7 @@
#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 */
+#define ETH_FCS_LEN 4 /* Octets in the FCS */
/*
* These are the defined Ethernet Protocol ID's.
@@ -53,12 +56,15 @@
#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_TEB 0x6558 /* Trans Ether Bridging */
#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_PAUSE 0x8808 /* IEEE Pause frames. See 802.3 31B */
+#define ETH_P_SLOW 0x8809 /* Slow Protocol. See 802.3ad 43B */
#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 */
@@ -66,15 +72,22 @@
#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_LINK_CTL 0x886c /* HPNA, wlan link local tunnel */
#define ETH_P_ATMFATE 0x8884 /* Frame-based ATM Transport
* over Ethernet
*/
+#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */
#define ETH_P_AOE 0x88A2 /* ATA over Ethernet */
+#define ETH_P_TIPC 0x88CA /* TIPC */
+#define ETH_P_1588 0x88F7 /* IEEE 1588 Timesync */
+#define ETH_P_FCOE 0x8906 /* Fibre Channel over Ethernet */
+#define ETH_P_FIP 0x8914 /* FCoE Initialization Protocol */
+#define ETH_P_EDSA 0xDADA /* Ethertype DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
/*
* 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!!!) */
@@ -84,6 +97,7 @@
#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_CAN 0x000C /* Controller Area Network */
#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) */
@@ -92,15 +106,20 @@
#define ETH_P_ECONET 0x0018 /* Acorn Econet */
#define ETH_P_HDLC 0x0019 /* HDLC frames */
#define ETH_P_ARCNET 0x001A /* 1A for ArcNet :-) */
+#define ETH_P_DSA 0x001B /* Distributed Switch Arch. */
+#define ETH_P_TRAILER 0x001C /* Trailer switch tagging */
+#define ETH_P_PHONET 0x00F5 /* Nokia Phonet frames */
+#define ETH_P_IEEE802154 0x00F6 /* IEEE802.15.4 frame */
+#define ETH_P_CAIF 0x00F7 /* ST-Ericsson CAIF protocol */
/*
* 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 */
+ __be16 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
index 84c3492a..8b849394 100644
--- a/include/linux/if_link.h
+++ b/include/linux/if_link.h
@@ -1,11 +1,11 @@
-#ifndef _LINUX_IF_LINK_H
-#define _LINUX_IF_LINK_H
+#ifndef _UAPI_LINUX_IF_LINK_H
+#define _UAPI_LINUX_IF_LINK_H
+#include <linux/types.h>
#include <linux/netlink.h>
-/* The struct should be in sync with struct net_device_stats */
-struct rtnl_link_stats
-{
+/* This struct should be in sync with struct rtnl_link_stats64 */
+struct rtnl_link_stats {
__u32 rx_packets; /* total packets received */
__u32 tx_packets; /* total packets transmitted */
__u32 rx_bytes; /* total bytes received */
@@ -37,9 +37,41 @@ struct rtnl_link_stats
__u32 tx_compressed;
};
+/* The main device statistics structure */
+struct rtnl_link_stats64 {
+ __u64 rx_packets; /* total packets received */
+ __u64 tx_packets; /* total packets transmitted */
+ __u64 rx_bytes; /* total bytes received */
+ __u64 tx_bytes; /* total bytes transmitted */
+ __u64 rx_errors; /* bad packets received */
+ __u64 tx_errors; /* packet transmit problems */
+ __u64 rx_dropped; /* no space in linux buffers */
+ __u64 tx_dropped; /* no space available in linux */
+ __u64 multicast; /* multicast packets received */
+ __u64 collisions;
+
+ /* detailed rx_errors: */
+ __u64 rx_length_errors;
+ __u64 rx_over_errors; /* receiver ring buff overflow */
+ __u64 rx_crc_errors; /* recved pkt with crc error */
+ __u64 rx_frame_errors; /* recv'd frame alignment error */
+ __u64 rx_fifo_errors; /* recv'r fifo overrun */
+ __u64 rx_missed_errors; /* receiver missed packet */
+
+ /* detailed tx_errors */
+ __u64 tx_aborted_errors;
+ __u64 tx_carrier_errors;
+ __u64 tx_fifo_errors;
+ __u64 tx_heartbeat_errors;
+ __u64 tx_window_errors;
+
+ /* for cslip etc */
+ __u64 rx_compressed;
+ __u64 tx_compressed;
+};
+
/* The struct should be in sync with struct ifmap */
-struct rtnl_link_ifmap
-{
+struct rtnl_link_ifmap {
__u64 mem_start;
__u64 mem_end;
__u64 base_addr;
@@ -48,8 +80,25 @@ struct rtnl_link_ifmap
__u8 port;
};
-enum
-{
+/*
+ * IFLA_AF_SPEC
+ * Contains nested attributes for address family specific attributes.
+ * Each address family may create a attribute with the address family
+ * number as type and create its own attribute structure in it.
+ *
+ * Example:
+ * [IFLA_AF_SPEC] = {
+ * [AF_INET] = {
+ * [IFLA_INET_CONF] = ...,
+ * },
+ * [AF_INET6] = {
+ * [IFLA_INET6_FLAGS] = ...,
+ * [IFLA_INET6_CONF] = ...,
+ * }
+ * }
+ */
+
+enum {
IFLA_UNSPEC,
IFLA_ADDRESS,
IFLA_BROADCAST,
@@ -79,17 +128,35 @@ enum
IFLA_LINKINFO,
#define IFLA_LINKINFO IFLA_LINKINFO
IFLA_NET_NS_PID,
+ IFLA_IFALIAS,
+ IFLA_NUM_VF, /* Number of VFs if device is SR-IOV PF */
+ IFLA_VFINFO_LIST,
+ IFLA_STATS64,
+ IFLA_VF_PORTS,
+ IFLA_PORT_SELF,
+ IFLA_AF_SPEC,
+ IFLA_GROUP, /* Group the device belongs to */
+ IFLA_NET_NS_FD,
+ IFLA_EXT_MASK, /* Extended info mask, VFs, etc */
+ IFLA_PROMISCUITY, /* Promiscuity count: > 0 means acts PROMISC */
+#define IFLA_PROMISCUITY IFLA_PROMISCUITY
+ IFLA_NUM_TX_QUEUES,
+ IFLA_NUM_RX_QUEUES,
+ IFLA_CARRIER,
+ IFLA_PHYS_PORT_ID,
__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
+enum {
+ IFLA_INET_UNSPEC,
+ IFLA_INET_CONF,
+ __IFLA_INET_MAX,
+};
+
+#define IFLA_INET_MAX (__IFLA_INET_MAX - 1)
/* ifi_flags.
@@ -121,8 +188,7 @@ enum
*/
/* Subtype attributes for IFLA_PROTINFO */
-enum
-{
+enum {
IFLA_INET6_UNSPEC,
IFLA_INET6_FLAGS, /* link flags */
IFLA_INET6_CONF, /* sysctl parameters */
@@ -135,16 +201,32 @@ enum
#define IFLA_INET6_MAX (__IFLA_INET6_MAX - 1)
-struct ifla_cacheinfo
-{
+enum {
+ BRIDGE_MODE_UNSPEC,
+ BRIDGE_MODE_HAIRPIN,
+};
+
+enum {
+ IFLA_BRPORT_UNSPEC,
+ IFLA_BRPORT_STATE, /* Spanning tree state */
+ IFLA_BRPORT_PRIORITY, /* " priority */
+ IFLA_BRPORT_COST, /* " cost */
+ IFLA_BRPORT_MODE, /* mode (hairpin) */
+ IFLA_BRPORT_GUARD, /* bpdu guard */
+ IFLA_BRPORT_PROTECT, /* root port protection */
+ IFLA_BRPORT_FAST_LEAVE, /* multicast fast leave */
+ __IFLA_BRPORT_MAX
+};
+#define IFLA_BRPORT_MAX (__IFLA_BRPORT_MAX - 1)
+
+struct ifla_cacheinfo {
__u32 max_reasm_len;
__u32 tstamp; /* ipv6InterfaceTable updated timestamp */
__u32 reachable_time;
__u32 retrans_time;
};
-enum
-{
+enum {
IFLA_INFO_UNSPEC,
IFLA_INFO_KIND,
IFLA_INFO_DATA,
@@ -156,13 +238,13 @@ enum
/* VLAN section */
-enum
-{
+enum {
IFLA_VLAN_UNSPEC,
IFLA_VLAN_ID,
IFLA_VLAN_FLAGS,
IFLA_VLAN_EGRESS_QOS,
IFLA_VLAN_INGRESS_QOS,
+ IFLA_VLAN_PROTOCOL,
__IFLA_VLAN_MAX,
};
@@ -173,8 +255,7 @@ struct ifla_vlan_flags {
__u32 mask;
};
-enum
-{
+enum {
IFLA_VLAN_QOS_UNSPEC,
IFLA_VLAN_QOS_MAPPING,
__IFLA_VLAN_QOS_MAX
@@ -182,10 +263,194 @@ enum
#define IFLA_VLAN_QOS_MAX (__IFLA_VLAN_QOS_MAX - 1)
-struct ifla_vlan_qos_mapping
-{
+struct ifla_vlan_qos_mapping {
__u32 from;
__u32 to;
};
-#endif /* _LINUX_IF_LINK_H */
+/* MACVLAN section */
+enum {
+ IFLA_MACVLAN_UNSPEC,
+ IFLA_MACVLAN_MODE,
+ IFLA_MACVLAN_FLAGS,
+ __IFLA_MACVLAN_MAX,
+};
+
+#define IFLA_MACVLAN_MAX (__IFLA_MACVLAN_MAX - 1)
+
+enum macvlan_mode {
+ MACVLAN_MODE_PRIVATE = 1, /* don't talk to other macvlans */
+ MACVLAN_MODE_VEPA = 2, /* talk to other ports through ext bridge */
+ MACVLAN_MODE_BRIDGE = 4, /* talk to bridge ports directly */
+ MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */
+};
+
+#define MACVLAN_FLAG_NOPROMISC 1
+
+/* VXLAN section */
+enum {
+ IFLA_VXLAN_UNSPEC,
+ IFLA_VXLAN_ID,
+ IFLA_VXLAN_GROUP,
+ IFLA_VXLAN_LINK,
+ IFLA_VXLAN_LOCAL,
+ IFLA_VXLAN_TTL,
+ IFLA_VXLAN_TOS,
+ IFLA_VXLAN_LEARNING,
+ IFLA_VXLAN_AGEING,
+ IFLA_VXLAN_LIMIT,
+ IFLA_VXLAN_PORT_RANGE,
+ IFLA_VXLAN_PROXY,
+ IFLA_VXLAN_RSC,
+ IFLA_VXLAN_L2MISS,
+ IFLA_VXLAN_L3MISS,
+ __IFLA_VXLAN_MAX
+};
+#define IFLA_VXLAN_MAX (__IFLA_VXLAN_MAX - 1)
+
+struct ifla_vxlan_port_range {
+ __be16 low;
+ __be16 high;
+};
+
+enum {
+ VETH_INFO_UNSPEC,
+ VETH_INFO_PEER,
+
+ __VETH_INFO_MAX
+#define VETH_INFO_MAX (__VETH_INFO_MAX - 1)
+};
+
+/* SR-IOV virtual function management section */
+
+enum {
+ IFLA_VF_INFO_UNSPEC,
+ IFLA_VF_INFO,
+ __IFLA_VF_INFO_MAX,
+};
+
+#define IFLA_VF_INFO_MAX (__IFLA_VF_INFO_MAX - 1)
+
+enum {
+ IFLA_VF_UNSPEC,
+ IFLA_VF_MAC, /* Hardware queue specific attributes */
+ IFLA_VF_VLAN,
+ IFLA_VF_TX_RATE, /* TX Bandwidth Allocation */
+ IFLA_VF_SPOOFCHK, /* Spoof Checking on/off switch */
+ __IFLA_VF_MAX,
+};
+
+#define IFLA_VF_MAX (__IFLA_VF_MAX - 1)
+
+struct ifla_vf_mac {
+ __u32 vf;
+ __u8 mac[32]; /* MAX_ADDR_LEN */
+};
+
+struct ifla_vf_vlan {
+ __u32 vf;
+ __u32 vlan; /* 0 - 4095, 0 disables VLAN filter */
+ __u32 qos;
+};
+
+struct ifla_vf_tx_rate {
+ __u32 vf;
+ __u32 rate; /* Max TX bandwidth in Mbps, 0 disables throttling */
+};
+
+struct ifla_vf_spoofchk {
+ __u32 vf;
+ __u32 setting;
+};
+
+/* VF ports management section
+ *
+ * Nested layout of set/get msg is:
+ *
+ * [IFLA_NUM_VF]
+ * [IFLA_VF_PORTS]
+ * [IFLA_VF_PORT]
+ * [IFLA_PORT_*], ...
+ * [IFLA_VF_PORT]
+ * [IFLA_PORT_*], ...
+ * ...
+ * [IFLA_PORT_SELF]
+ * [IFLA_PORT_*], ...
+ */
+
+enum {
+ IFLA_VF_PORT_UNSPEC,
+ IFLA_VF_PORT, /* nest */
+ __IFLA_VF_PORT_MAX,
+};
+
+#define IFLA_VF_PORT_MAX (__IFLA_VF_PORT_MAX - 1)
+
+enum {
+ IFLA_PORT_UNSPEC,
+ IFLA_PORT_VF, /* __u32 */
+ IFLA_PORT_PROFILE, /* string */
+ IFLA_PORT_VSI_TYPE, /* 802.1Qbg (pre-)standard VDP */
+ IFLA_PORT_INSTANCE_UUID, /* binary UUID */
+ IFLA_PORT_HOST_UUID, /* binary UUID */
+ IFLA_PORT_REQUEST, /* __u8 */
+ IFLA_PORT_RESPONSE, /* __u16, output only */
+ __IFLA_PORT_MAX,
+};
+
+#define IFLA_PORT_MAX (__IFLA_PORT_MAX - 1)
+
+#define PORT_PROFILE_MAX 40
+#define PORT_UUID_MAX 16
+#define PORT_SELF_VF -1
+
+enum {
+ PORT_REQUEST_PREASSOCIATE = 0,
+ PORT_REQUEST_PREASSOCIATE_RR,
+ PORT_REQUEST_ASSOCIATE,
+ PORT_REQUEST_DISASSOCIATE,
+};
+
+enum {
+ PORT_VDP_RESPONSE_SUCCESS = 0,
+ PORT_VDP_RESPONSE_INVALID_FORMAT,
+ PORT_VDP_RESPONSE_INSUFFICIENT_RESOURCES,
+ PORT_VDP_RESPONSE_UNUSED_VTID,
+ PORT_VDP_RESPONSE_VTID_VIOLATION,
+ PORT_VDP_RESPONSE_VTID_VERSION_VIOALTION,
+ PORT_VDP_RESPONSE_OUT_OF_SYNC,
+ /* 0x08-0xFF reserved for future VDP use */
+ PORT_PROFILE_RESPONSE_SUCCESS = 0x100,
+ PORT_PROFILE_RESPONSE_INPROGRESS,
+ PORT_PROFILE_RESPONSE_INVALID,
+ PORT_PROFILE_RESPONSE_BADSTATE,
+ PORT_PROFILE_RESPONSE_INSUFFICIENT_RESOURCES,
+ PORT_PROFILE_RESPONSE_ERROR,
+};
+
+struct ifla_port_vsi {
+ __u8 vsi_mgr_id;
+ __u8 vsi_type_id[3];
+ __u8 vsi_type_version;
+ __u8 pad[3];
+};
+
+
+/* IPoIB section */
+
+enum {
+ IFLA_IPOIB_UNSPEC,
+ IFLA_IPOIB_PKEY,
+ IFLA_IPOIB_MODE,
+ IFLA_IPOIB_UMCAST,
+ __IFLA_IPOIB_MAX
+};
+
+enum {
+ IPOIB_MODE_DATAGRAM = 0, /* using unreliable datagram QPs */
+ IPOIB_MODE_CONNECTED = 1, /* using connected QPs */
+};
+
+#define IFLA_IPOIB_MAX (__IFLA_IPOIB_MAX - 1)
+
+#endif /* _UAPI_LINUX_IF_LINK_H */
diff --git a/include/linux/if_tunnel.h b/include/linux/if_tunnel.h
new file mode 100644
index 00000000..aee73d06
--- /dev/null
+++ b/include/linux/if_tunnel.h
@@ -0,0 +1,116 @@
+#ifndef _UAPI_IF_TUNNEL_H_
+#define _UAPI_IF_TUNNEL_H_
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+
+#define SIOCGETTUNNEL (SIOCDEVPRIVATE + 0)
+#define SIOCADDTUNNEL (SIOCDEVPRIVATE + 1)
+#define SIOCDELTUNNEL (SIOCDEVPRIVATE + 2)
+#define SIOCCHGTUNNEL (SIOCDEVPRIVATE + 3)
+#define SIOCGETPRL (SIOCDEVPRIVATE + 4)
+#define SIOCADDPRL (SIOCDEVPRIVATE + 5)
+#define SIOCDELPRL (SIOCDEVPRIVATE + 6)
+#define SIOCCHGPRL (SIOCDEVPRIVATE + 7)
+#define SIOCGET6RD (SIOCDEVPRIVATE + 8)
+#define SIOCADD6RD (SIOCDEVPRIVATE + 9)
+#define SIOCDEL6RD (SIOCDEVPRIVATE + 10)
+#define SIOCCHG6RD (SIOCDEVPRIVATE + 11)
+
+#define GRE_CSUM __cpu_to_be16(0x8000)
+#define GRE_ROUTING __cpu_to_be16(0x4000)
+#define GRE_KEY __cpu_to_be16(0x2000)
+#define GRE_SEQ __cpu_to_be16(0x1000)
+#define GRE_STRICT __cpu_to_be16(0x0800)
+#define GRE_REC __cpu_to_be16(0x0700)
+#define GRE_FLAGS __cpu_to_be16(0x00F8)
+#define GRE_VERSION __cpu_to_be16(0x0007)
+
+struct ip_tunnel_parm {
+ char name[IFNAMSIZ];
+ int link;
+ __be16 i_flags;
+ __be16 o_flags;
+ __be32 i_key;
+ __be32 o_key;
+ struct iphdr iph;
+};
+
+enum {
+ IFLA_IPTUN_UNSPEC,
+ IFLA_IPTUN_LINK,
+ IFLA_IPTUN_LOCAL,
+ IFLA_IPTUN_REMOTE,
+ IFLA_IPTUN_TTL,
+ IFLA_IPTUN_TOS,
+ IFLA_IPTUN_ENCAP_LIMIT,
+ IFLA_IPTUN_FLOWINFO,
+ IFLA_IPTUN_FLAGS,
+ IFLA_IPTUN_PROTO,
+ IFLA_IPTUN_PMTUDISC,
+ IFLA_IPTUN_6RD_PREFIX,
+ IFLA_IPTUN_6RD_RELAY_PREFIX,
+ IFLA_IPTUN_6RD_PREFIXLEN,
+ IFLA_IPTUN_6RD_RELAY_PREFIXLEN,
+ __IFLA_IPTUN_MAX,
+};
+#define IFLA_IPTUN_MAX (__IFLA_IPTUN_MAX - 1)
+
+/* SIT-mode i_flags */
+#define SIT_ISATAP 0x0001
+
+struct ip_tunnel_prl {
+ __be32 addr;
+ __u16 flags;
+ __u16 __reserved;
+ __u32 datalen;
+ __u32 __reserved2;
+ /* data follows */
+};
+
+/* PRL flags */
+#define PRL_DEFAULT 0x0001
+
+struct ip_tunnel_6rd {
+ struct in6_addr prefix;
+ __be32 relay_prefix;
+ __u16 prefixlen;
+ __u16 relay_prefixlen;
+};
+
+enum {
+ IFLA_GRE_UNSPEC,
+ IFLA_GRE_LINK,
+ IFLA_GRE_IFLAGS,
+ IFLA_GRE_OFLAGS,
+ IFLA_GRE_IKEY,
+ IFLA_GRE_OKEY,
+ IFLA_GRE_LOCAL,
+ IFLA_GRE_REMOTE,
+ IFLA_GRE_TTL,
+ IFLA_GRE_TOS,
+ IFLA_GRE_PMTUDISC,
+ IFLA_GRE_ENCAP_LIMIT,
+ IFLA_GRE_FLOWINFO,
+ IFLA_GRE_FLAGS,
+ __IFLA_GRE_MAX,
+};
+
+#define IFLA_GRE_MAX (__IFLA_GRE_MAX - 1)
+
+/* VTI-mode i_flags */
+#define VTI_ISVTI 0x0001
+
+enum {
+ IFLA_VTI_UNSPEC,
+ IFLA_VTI_LINK,
+ IFLA_VTI_IKEY,
+ IFLA_VTI_OKEY,
+ IFLA_VTI_LOCAL,
+ IFLA_VTI_REMOTE,
+ __IFLA_VTI_MAX,
+};
+
+#define IFLA_VTI_MAX (__IFLA_VTI_MAX - 1)
+#endif /* _UAPI_IF_TUNNEL_H_ */
diff --git a/include/linux/if_vlan.h b/include/linux/if_vlan.h
index 068cd7bb..67affd16 100644
--- a/include/linux/if_vlan.h
+++ b/include/linux/if_vlan.h
@@ -13,7 +13,6 @@
#ifndef _LINUX_IF_VLAN_H_
#define _LINUX_IF_VLAN_H_
-
/* VLAN IOCTLs are found in sockios.h */
/* Passed in vlan_ioctl_args structure to determine behaviour. */
@@ -32,6 +31,8 @@ enum vlan_ioctl_cmds {
enum vlan_flags {
VLAN_FLAG_REORDER_HDR = 0x1,
+ VLAN_FLAG_GVRP = 0x2,
+ VLAN_FLAG_LOOSE_BINDING = 0x4,
};
enum vlan_name_types {
@@ -55,7 +56,7 @@ struct vlan_ioctl_args {
unsigned int flag; /* Matches vlan_dev_info flags */
} u;
- short vlan_qos;
+ short vlan_qos;
};
#endif /* !(_LINUX_IF_VLAN_H_) */
diff --git a/include/linux/ip.h b/include/linux/ip.h
new file mode 100644
index 00000000..41195940
--- /dev/null
+++ b/include/linux/ip.h
@@ -0,0 +1,172 @@
+/*
+ * 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.
+ *
+ * Definitions for the IP protocol.
+ *
+ * Version: @(#)ip.h 1.0.2 04/28/93
+ *
+ * Authors: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * 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 _UAPI_LINUX_IP_H
+#define _UAPI_LINUX_IP_H
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+#define IPTOS_TOS_MASK 0x1E
+#define IPTOS_TOS(tos) ((tos)&IPTOS_TOS_MASK)
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+#define IPTOS_MINCOST 0x02
+
+#define IPTOS_PREC_MASK 0xE0
+#define IPTOS_PREC(tos) ((tos)&IPTOS_PREC_MASK)
+#define IPTOS_PREC_NETCONTROL 0xe0
+#define IPTOS_PREC_INTERNETCONTROL 0xc0
+#define IPTOS_PREC_CRITIC_ECP 0xa0
+#define IPTOS_PREC_FLASHOVERRIDE 0x80
+#define IPTOS_PREC_FLASH 0x60
+#define IPTOS_PREC_IMMEDIATE 0x40
+#define IPTOS_PREC_PRIORITY 0x20
+#define IPTOS_PREC_ROUTINE 0x00
+
+
+/* IP options */
+#define IPOPT_COPY 0x80
+#define IPOPT_CLASS_MASK 0x60
+#define IPOPT_NUMBER_MASK 0x1f
+
+#define IPOPT_COPIED(o) ((o)&IPOPT_COPY)
+#define IPOPT_CLASS(o) ((o)&IPOPT_CLASS_MASK)
+#define IPOPT_NUMBER(o) ((o)&IPOPT_NUMBER_MASK)
+
+#define IPOPT_CONTROL 0x00
+#define IPOPT_RESERVED1 0x20
+#define IPOPT_MEASUREMENT 0x40
+#define IPOPT_RESERVED2 0x60
+
+#define IPOPT_END (0 |IPOPT_CONTROL)
+#define IPOPT_NOOP (1 |IPOPT_CONTROL)
+#define IPOPT_SEC (2 |IPOPT_CONTROL|IPOPT_COPY)
+#define IPOPT_LSRR (3 |IPOPT_CONTROL|IPOPT_COPY)
+#define IPOPT_TIMESTAMP (4 |IPOPT_MEASUREMENT)
+#define IPOPT_CIPSO (6 |IPOPT_CONTROL|IPOPT_COPY)
+#define IPOPT_RR (7 |IPOPT_CONTROL)
+#define IPOPT_SID (8 |IPOPT_CONTROL|IPOPT_COPY)
+#define IPOPT_SSRR (9 |IPOPT_CONTROL|IPOPT_COPY)
+#define IPOPT_RA (20|IPOPT_CONTROL|IPOPT_COPY)
+
+#define IPVERSION 4
+#define MAXTTL 255
+#define IPDEFTTL 64
+
+#define IPOPT_OPTVAL 0
+#define IPOPT_OLEN 1
+#define IPOPT_OFFSET 2
+#define IPOPT_MINOFF 4
+#define MAX_IPOPTLEN 40
+#define IPOPT_NOP IPOPT_NOOP
+#define IPOPT_EOL IPOPT_END
+#define IPOPT_TS IPOPT_TIMESTAMP
+
+#define IPOPT_TS_TSONLY 0 /* timestamps only */
+#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
+#define IPOPT_TS_PRESPEC 3 /* specified modules only */
+
+#define IPV4_BEET_PHMAXLEN 8
+
+struct iphdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 ihl:4,
+ version:4;
+#elif defined (__BIG_ENDIAN_BITFIELD)
+ __u8 version:4,
+ ihl:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ __u8 tos;
+ __be16 tot_len;
+ __be16 id;
+ __be16 frag_off;
+ __u8 ttl;
+ __u8 protocol;
+ __sum16 check;
+ __be32 saddr;
+ __be32 daddr;
+ /*The options start here. */
+};
+
+
+struct ip_auth_hdr {
+ __u8 nexthdr;
+ __u8 hdrlen; /* This one is measured in 32 bit units! */
+ __be16 reserved;
+ __be32 spi;
+ __be32 seq_no; /* Sequence number */
+ __u8 auth_data[0]; /* Variable len but >=4. Mind the 64 bit alignment! */
+};
+
+struct ip_esp_hdr {
+ __be32 spi;
+ __be32 seq_no; /* Sequence number */
+ __u8 enc_data[0]; /* Variable len but >=8. Mind the 64 bit alignment! */
+};
+
+struct ip_comp_hdr {
+ __u8 nexthdr;
+ __u8 flags;
+ __be16 cpi;
+};
+
+struct ip_beet_phdr {
+ __u8 nexthdr;
+ __u8 hdrlen;
+ __u8 padlen;
+ __u8 reserved;
+};
+
+/* index values for the variables in ipv4_devconf */
+enum
+{
+ IPV4_DEVCONF_FORWARDING=1,
+ IPV4_DEVCONF_MC_FORWARDING,
+ IPV4_DEVCONF_PROXY_ARP,
+ IPV4_DEVCONF_ACCEPT_REDIRECTS,
+ IPV4_DEVCONF_SECURE_REDIRECTS,
+ IPV4_DEVCONF_SEND_REDIRECTS,
+ IPV4_DEVCONF_SHARED_MEDIA,
+ IPV4_DEVCONF_RP_FILTER,
+ IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE,
+ IPV4_DEVCONF_BOOTP_RELAY,
+ IPV4_DEVCONF_LOG_MARTIANS,
+ IPV4_DEVCONF_TAG,
+ IPV4_DEVCONF_ARPFILTER,
+ IPV4_DEVCONF_MEDIUM_ID,
+ IPV4_DEVCONF_NOXFRM,
+ IPV4_DEVCONF_NOPOLICY,
+ IPV4_DEVCONF_FORCE_IGMP_VERSION,
+ IPV4_DEVCONF_ARP_ANNOUNCE,
+ IPV4_DEVCONF_ARP_IGNORE,
+ IPV4_DEVCONF_PROMOTE_SECONDARIES,
+ IPV4_DEVCONF_ARP_ACCEPT,
+ IPV4_DEVCONF_ARP_NOTIFY,
+ IPV4_DEVCONF_ACCEPT_LOCAL,
+ IPV4_DEVCONF_SRC_VMARK,
+ IPV4_DEVCONF_PROXY_ARP_PVLAN,
+ IPV4_DEVCONF_ROUTE_LOCALNET,
+ IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL,
+ IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL,
+ __IPV4_DEVCONF_MAX
+};
+
+#define IPV4_DEVCONF_MAX (__IPV4_DEVCONF_MAX - 1)
+
+#endif /* _UAPI_LINUX_IP_H */
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
new file mode 100644
index 00000000..f16349df
--- /dev/null
+++ b/include/linux/ipv6.h
@@ -0,0 +1,146 @@
+#ifndef _IPV6_H
+#define _IPV6_H
+
+#include <asm/byteorder.h>
+
+/* The latest drafts declared increase in minimal mtu up to 1280. */
+
+#define IPV6_MIN_MTU 1280
+
+/*
+ * Advanced API
+ * source interface/address selection, source routing, etc...
+ * *under construction*
+ */
+
+
+#define IPV6_SRCRT_STRICT 0x01 /* Deprecated; will be removed */
+#define IPV6_SRCRT_TYPE_0 0 /* Deprecated; will be removed */
+#define IPV6_SRCRT_TYPE_2 2 /* IPv6 type 2 Routing Header */
+
+/*
+ * routing header
+ */
+struct ipv6_rt_hdr {
+ __u8 nexthdr;
+ __u8 hdrlen;
+ __u8 type;
+ __u8 segments_left;
+
+ /*
+ * type specific data
+ * variable length field
+ */
+};
+
+
+struct ipv6_opt_hdr {
+ __u8 nexthdr;
+ __u8 hdrlen;
+ /*
+ * TLV encoded option data follows.
+ */
+} __attribute__((packed)); /* required for some archs */
+
+#define ipv6_destopt_hdr ipv6_opt_hdr
+#define ipv6_hopopt_hdr ipv6_opt_hdr
+
+
+/*
+ * routing header type 0 (used in cmsghdr struct)
+ */
+
+struct rt0_hdr {
+ struct ipv6_rt_hdr rt_hdr;
+ __u32 reserved;
+ struct in6_addr addr[0];
+
+#define rt0_type rt_hdr.type
+};
+
+/*
+ * routing header type 2
+ */
+
+struct rt2_hdr {
+ struct ipv6_rt_hdr rt_hdr;
+ __u32 reserved;
+ struct in6_addr addr;
+
+#define rt2_type rt_hdr.type
+};
+
+/*
+ * home address option in destination options header
+ */
+
+struct ipv6_destopt_hao {
+ __u8 type;
+ __u8 length;
+ struct in6_addr addr;
+} __attribute__((packed));
+
+/*
+ * IPv6 fixed header
+ *
+ * BEWARE, it is incorrect. The first 4 bits of flow_lbl
+ * are glued to priority now, forming "class".
+ */
+
+struct ipv6hdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 priority:4,
+ version:4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u8 version:4,
+ priority:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ __u8 flow_lbl[3];
+
+ __be16 payload_len;
+ __u8 nexthdr;
+ __u8 hop_limit;
+
+ struct in6_addr saddr;
+ struct in6_addr daddr;
+};
+
+
+/* index values for the variables in ipv6_devconf */
+enum {
+ DEVCONF_FORWARDING = 0,
+ DEVCONF_HOPLIMIT,
+ DEVCONF_MTU6,
+ DEVCONF_ACCEPT_RA,
+ DEVCONF_ACCEPT_REDIRECTS,
+ DEVCONF_AUTOCONF,
+ DEVCONF_DAD_TRANSMITS,
+ DEVCONF_RTR_SOLICITS,
+ DEVCONF_RTR_SOLICIT_INTERVAL,
+ DEVCONF_RTR_SOLICIT_DELAY,
+ DEVCONF_USE_TEMPADDR,
+ DEVCONF_TEMP_VALID_LFT,
+ DEVCONF_TEMP_PREFERED_LFT,
+ DEVCONF_REGEN_MAX_RETRY,
+ DEVCONF_MAX_DESYNC_FACTOR,
+ DEVCONF_MAX_ADDRESSES,
+ DEVCONF_FORCE_MLD_VERSION,
+ DEVCONF_ACCEPT_RA_DEFRTR,
+ DEVCONF_ACCEPT_RA_PINFO,
+ DEVCONF_ACCEPT_RA_RTR_PREF,
+ DEVCONF_RTR_PROBE_INTERVAL,
+ DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN,
+ DEVCONF_PROXY_NDP,
+ DEVCONF_OPTIMISTIC_DAD,
+ DEVCONF_ACCEPT_SOURCE_ROUTE,
+ DEVCONF_MC_FORWARDING,
+ DEVCONF_DISABLE_IPV6,
+ DEVCONF_ACCEPT_DAD,
+ DEVCONF_FORCE_TLLAO,
+ DEVCONF_MAX
+};
+
+
+#endif /* _IPV6_H */
diff --git a/include/linux/neighbour.h b/include/linux/neighbour.h
index bd3bbf66..a7003b7a 100644
--- a/include/linux/neighbour.h
+++ b/include/linux/neighbour.h
@@ -1,10 +1,10 @@
#ifndef __LINUX_NEIGHBOUR_H
#define __LINUX_NEIGHBOUR_H
+#include <linux/types.h>
#include <linux/netlink.h>
-struct ndmsg
-{
+struct ndmsg {
__u8 ndm_family;
__u8 ndm_pad1;
__u16 ndm_pad2;
@@ -14,8 +14,7 @@ struct ndmsg
__u8 ndm_type;
};
-enum
-{
+enum {
NDA_UNSPEC,
NDA_DST,
NDA_LLADDR,
@@ -30,6 +29,7 @@ enum
* Neighbor Cache Entry Flags
*/
+#define NTF_USE 0x01
#define NTF_PROXY 0x08 /* == ATF_PUBL */
#define NTF_ROUTER 0x80
@@ -54,8 +54,7 @@ enum
NUD_PERMANENT is also cannot be deleted by garbage collectors.
*/
-struct nda_cacheinfo
-{
+struct nda_cacheinfo {
__u32 ndm_confirmed;
__u32 ndm_used;
__u32 ndm_updated;
@@ -87,8 +86,7 @@ struct nda_cacheinfo
* device.
****/
-struct ndt_stats
-{
+struct ndt_stats {
__u64 ndts_allocs;
__u64 ndts_destroys;
__u64 ndts_hash_grows;
@@ -122,15 +120,13 @@ enum {
};
#define NDTPA_MAX (__NDTPA_MAX - 1)
-struct ndtmsg
-{
+struct ndtmsg {
__u8 ndtm_family;
__u8 ndtm_pad1;
__u16 ndtm_pad2;
};
-struct ndt_config
-{
+struct ndt_config {
__u16 ndtc_key_len;
__u16 ndtc_entry_size;
__u32 ndtc_entries;
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index 0750ca62..79998855 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -1,6 +1,7 @@
#ifndef __LINUX_NETFILTER_H
#define __LINUX_NETFILTER_H
+#include <linux/types.h>
/* Responses from hook functions. */
#define NF_DROP 0
@@ -19,9 +20,8 @@
#define NF_VERDICT_QMASK 0xffff0000
#define NF_VERDICT_QBITS 16
-#define NF_QUEUE_NR(x) (((x << NF_VERDICT_QBITS) & NF_VERDICT_QMASK) | NF_QUEUE)
+#define NF_QUEUE_NR(x) ((((x) << NF_VERDICT_BITS) & NF_VERDICT_QMASK) | NF_QUEUE)
-/* only for userspace compatibility */
/* Generic cache responses from hook functions.
<= 0x2000 is used for protocol-flags. */
#define NFC_UNKNOWN 0x4000
@@ -33,7 +33,25 @@ enum nf_inet_hooks {
NF_INET_FORWARD,
NF_INET_LOCAL_OUT,
NF_INET_POST_ROUTING,
- NF_INET_NUMHOOKS,
+ NF_INET_NUMHOOKS
+};
+
+enum {
+ NFPROTO_UNSPEC = 0,
+ NFPROTO_IPV4 = 2,
+ NFPROTO_ARP = 3,
+ NFPROTO_BRIDGE = 7,
+ NFPROTO_IPV6 = 10,
+ NFPROTO_DECNET = 12,
+ NFPROTO_NUMPROTO,
+};
+
+union nf_inet_addr {
+ __u32 all[4];
+ __be32 ip;
+ __be32 ip6[4];
+ struct in_addr in;
+ struct in6_addr in6;
};
#endif /*__LINUX_NETFILTER_H*/
diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h
new file mode 100644
index 00000000..1644cdd8
--- /dev/null
+++ b/include/linux/netfilter/nf_conntrack_common.h
@@ -0,0 +1,117 @@
+#ifndef _UAPI_NF_CONNTRACK_COMMON_H
+#define _UAPI_NF_CONNTRACK_COMMON_H
+/* Connection state tracking for netfilter. This is separated from,
+ but required by, the NAT layer; it can also be used by an iptables
+ extension. */
+enum ip_conntrack_info {
+ /* Part of an established connection (either direction). */
+ IP_CT_ESTABLISHED,
+
+ /* Like NEW, but related to an existing connection, or ICMP error
+ (in either direction). */
+ IP_CT_RELATED,
+
+ /* Started a new connection to track (only
+ IP_CT_DIR_ORIGINAL); may be a retransmission. */
+ IP_CT_NEW,
+
+ /* >= this indicates reply direction */
+ IP_CT_IS_REPLY,
+
+ IP_CT_ESTABLISHED_REPLY = IP_CT_ESTABLISHED + IP_CT_IS_REPLY,
+ IP_CT_RELATED_REPLY = IP_CT_RELATED + IP_CT_IS_REPLY,
+ IP_CT_NEW_REPLY = IP_CT_NEW + IP_CT_IS_REPLY,
+ /* Number of distinct IP_CT types (no NEW in reply dirn). */
+ IP_CT_NUMBER = IP_CT_IS_REPLY * 2 - 1
+};
+
+/* Bitset representing status of connection. */
+enum ip_conntrack_status {
+ /* It's an expected connection: bit 0 set. This bit never changed */
+ IPS_EXPECTED_BIT = 0,
+ IPS_EXPECTED = (1 << IPS_EXPECTED_BIT),
+
+ /* We've seen packets both ways: bit 1 set. Can be set, not unset. */
+ IPS_SEEN_REPLY_BIT = 1,
+ IPS_SEEN_REPLY = (1 << IPS_SEEN_REPLY_BIT),
+
+ /* Conntrack should never be early-expired. */
+ IPS_ASSURED_BIT = 2,
+ IPS_ASSURED = (1 << IPS_ASSURED_BIT),
+
+ /* Connection is confirmed: originating packet has left box */
+ IPS_CONFIRMED_BIT = 3,
+ IPS_CONFIRMED = (1 << IPS_CONFIRMED_BIT),
+
+ /* Connection needs src nat in orig dir. This bit never changed. */
+ IPS_SRC_NAT_BIT = 4,
+ IPS_SRC_NAT = (1 << IPS_SRC_NAT_BIT),
+
+ /* Connection needs dst nat in orig dir. This bit never changed. */
+ IPS_DST_NAT_BIT = 5,
+ IPS_DST_NAT = (1 << IPS_DST_NAT_BIT),
+
+ /* Both together. */
+ IPS_NAT_MASK = (IPS_DST_NAT | IPS_SRC_NAT),
+
+ /* Connection needs TCP sequence adjusted. */
+ IPS_SEQ_ADJUST_BIT = 6,
+ IPS_SEQ_ADJUST = (1 << IPS_SEQ_ADJUST_BIT),
+
+ /* NAT initialization bits. */
+ IPS_SRC_NAT_DONE_BIT = 7,
+ IPS_SRC_NAT_DONE = (1 << IPS_SRC_NAT_DONE_BIT),
+
+ IPS_DST_NAT_DONE_BIT = 8,
+ IPS_DST_NAT_DONE = (1 << IPS_DST_NAT_DONE_BIT),
+
+ /* Both together */
+ IPS_NAT_DONE_MASK = (IPS_DST_NAT_DONE | IPS_SRC_NAT_DONE),
+
+ /* Connection is dying (removed from lists), can not be unset. */
+ IPS_DYING_BIT = 9,
+ IPS_DYING = (1 << IPS_DYING_BIT),
+
+ /* Connection has fixed timeout. */
+ IPS_FIXED_TIMEOUT_BIT = 10,
+ IPS_FIXED_TIMEOUT = (1 << IPS_FIXED_TIMEOUT_BIT),
+
+ /* Conntrack is a template */
+ IPS_TEMPLATE_BIT = 11,
+ IPS_TEMPLATE = (1 << IPS_TEMPLATE_BIT),
+
+ /* Conntrack is a fake untracked entry */
+ IPS_UNTRACKED_BIT = 12,
+ IPS_UNTRACKED = (1 << IPS_UNTRACKED_BIT),
+
+ /* Conntrack got a helper explicitly attached via CT target. */
+ IPS_HELPER_BIT = 13,
+ IPS_HELPER = (1 << IPS_HELPER_BIT),
+};
+
+/* Connection tracking event types */
+enum ip_conntrack_events {
+ IPCT_NEW, /* new conntrack */
+ IPCT_RELATED, /* related conntrack */
+ IPCT_DESTROY, /* destroyed conntrack */
+ IPCT_REPLY, /* connection has seen two-way traffic */
+ IPCT_ASSURED, /* connection status has changed to assured */
+ IPCT_PROTOINFO, /* protocol information has changed */
+ IPCT_HELPER, /* new helper has been set */
+ IPCT_MARK, /* new mark has been set */
+ IPCT_NATSEQADJ, /* NAT is doing sequence adjustment */
+ IPCT_SECMARK, /* new security mark has been set */
+};
+
+enum ip_conntrack_expect_events {
+ IPEXP_NEW, /* new expectation */
+ IPEXP_DESTROY, /* destroyed expectation */
+};
+
+/* expectation flags */
+#define NF_CT_EXPECT_PERMANENT 0x1
+#define NF_CT_EXPECT_INACTIVE 0x2
+#define NF_CT_EXPECT_USERSPACE 0x4
+
+
+#endif /* _UAPI_NF_CONNTRACK_COMMON_H */
diff --git a/include/linux/netfilter/nfnetlink.h b/include/linux/netfilter/nfnetlink.h
index 4bb4c444..1fe29727 100644
--- a/include/linux/netfilter/nfnetlink.h
+++ b/include/linux/netfilter/nfnetlink.h
@@ -1,5 +1,5 @@
-#ifndef _NFNETLINK_H
-#define _NFNETLINK_H
+#ifndef _UAPI_NFNETLINK_H
+#define _UAPI_NFNETLINK_H
#include <linux/types.h>
#ifndef __KERNEL__
@@ -39,8 +39,8 @@ enum nfnetlink_groups {
/* General form of address family dependent message.
*/
struct nfgenmsg {
- u_int8_t nfgen_family; /* AF_xxx */
- u_int8_t version; /* nfnetlink version */
+ __u8 nfgen_family; /* AF_xxx */
+ __u8 version; /* nfnetlink version */
__be16 res_id; /* resource id */
};
@@ -60,7 +60,11 @@ struct nfgenmsg {
#define NFNL_SUBSYS_CTNETLINK_EXP 2
#define NFNL_SUBSYS_QUEUE 3
#define NFNL_SUBSYS_ULOG 4
-#define NFNL_SUBSYS_COUNT 5
+#define NFNL_SUBSYS_OSF 5
+#define NFNL_SUBSYS_IPSET 6
#define NFNL_SUBSYS_ACCT 7
+#define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8
+#define NFNL_SUBSYS_CTHELPER 9
+#define NFNL_SUBSYS_COUNT 10
-#endif /* _NFNETLINK_H */
+#endif /* _UAPI_NFNETLINK_H */
diff --git a/include/linux/netfilter/nfnetlink_compat.h b/include/linux/netfilter/nfnetlink_compat.h
new file mode 100644
index 00000000..ffb95036
--- /dev/null
+++ b/include/linux/netfilter/nfnetlink_compat.h
@@ -0,0 +1,63 @@
+#ifndef _NFNETLINK_COMPAT_H
+#define _NFNETLINK_COMPAT_H
+
+#include <linux/types.h>
+
+#ifndef __KERNEL__
+/* Old nfnetlink macros for userspace */
+
+/* nfnetlink groups: Up to 32 maximum */
+#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
+
+/* Generic structure for encapsulation optional netfilter information.
+ * It is reminiscent of sockaddr, but with sa_family replaced
+ * with attribute type.
+ * ! This should someday be put somewhere generic as now rtnetlink and
+ * ! nfnetlink use the same attributes methods. - J. Schulist.
+ */
+
+struct nfattr {
+ __u16 nfa_len;
+ __u16 nfa_type; /* we use 15 bits for the type, and the highest
+ * bit to indicate whether the payload is nested */
+};
+
+/* FIXME: Apart from NFNL_NFA_NESTED shamelessly copy and pasted from
+ * rtnetlink.h, it's time to put this in a generic file */
+
+#define NFNL_NFA_NEST 0x8000
+#define NFA_TYPE(attr) ((attr)->nfa_type & 0x7fff)
+
+#define NFA_ALIGNTO 4
+#define NFA_ALIGN(len) (((len) + NFA_ALIGNTO - 1) & ~(NFA_ALIGNTO - 1))
+#define NFA_OK(nfa,len) ((len) > 0 && (nfa)->nfa_len >= sizeof(struct nfattr) \
+ && (nfa)->nfa_len <= (len))
+#define NFA_NEXT(nfa,attrlen) ((attrlen) -= NFA_ALIGN((nfa)->nfa_len), \
+ (struct nfattr *)(((char *)(nfa)) + NFA_ALIGN((nfa)->nfa_len)))
+#define NFA_LENGTH(len) (NFA_ALIGN(sizeof(struct nfattr)) + (len))
+#define NFA_SPACE(len) NFA_ALIGN(NFA_LENGTH(len))
+#define NFA_DATA(nfa) ((void *)(((char *)(nfa)) + NFA_LENGTH(0)))
+#define NFA_PAYLOAD(nfa) ((int)((nfa)->nfa_len) - NFA_LENGTH(0))
+#define NFA_NEST(skb, type) \
+({ struct nfattr *__start = (struct nfattr *)skb_tail_pointer(skb); \
+ NFA_PUT(skb, (NFNL_NFA_NEST | type), 0, NULL); \
+ __start; })
+#define NFA_NEST_END(skb, start) \
+({ (start)->nfa_len = skb_tail_pointer(skb) - (unsigned char *)(start); \
+ (skb)->len; })
+#define NFA_NEST_CANCEL(skb, start) \
+({ if (start) \
+ skb_trim(skb, (unsigned char *) (start) - (skb)->data); \
+ -1; })
+
+#define NFM_NFA(n) ((struct nfattr *)(((char *)(n)) \
+ + NLMSG_ALIGN(sizeof(struct nfgenmsg))))
+#define NFM_PAYLOAD(n) NLMSG_PAYLOAD(n, sizeof(struct nfgenmsg))
+
+#endif /* ! __KERNEL__ */
+#endif /* _NFNETLINK_COMPAT_H */
diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h
index d7c35039..43bfe3e1 100644
--- a/include/linux/netfilter/nfnetlink_conntrack.h
+++ b/include/linux/netfilter/nfnetlink_conntrack.h
@@ -7,6 +7,8 @@ enum cntl_msg_types {
IPCTNL_MSG_CT_GET,
IPCTNL_MSG_CT_DELETE,
IPCTNL_MSG_CT_GET_CTRZERO,
+ IPCTNL_MSG_CT_GET_STATS_CPU,
+ IPCTNL_MSG_CT_GET_STATS,
IPCTNL_MSG_MAX
};
@@ -15,6 +17,7 @@ enum ctnl_exp_msg_types {
IPCTNL_MSG_EXP_NEW,
IPCTNL_MSG_EXP_GET,
IPCTNL_MSG_EXP_DELETE,
+ IPCTNL_MSG_EXP_GET_STATS_CPU,
IPCTNL_MSG_EXP_MAX
};
@@ -36,6 +39,14 @@ enum ctattr_type {
CTA_USE,
CTA_ID,
CTA_NAT_DST,
+ CTA_TUPLE_MASTER,
+ CTA_NAT_SEQ_ADJ_ORIG,
+ CTA_NAT_SEQ_ADJ_REPLY,
+ CTA_SECMARK, /* obsolete */
+ CTA_ZONE,
+ CTA_SECCTX,
+ CTA_TIMESTAMP,
+ CTA_MARK_MASK,
__CTA_MAX
};
#define CTA_MAX (__CTA_MAX - 1)
@@ -76,6 +87,8 @@ enum ctattr_l4proto {
enum ctattr_protoinfo {
CTA_PROTOINFO_UNSPEC,
CTA_PROTOINFO_TCP,
+ CTA_PROTOINFO_DCCP,
+ CTA_PROTOINFO_SCTP,
__CTA_PROTOINFO_MAX
};
#define CTA_PROTOINFO_MAX (__CTA_PROTOINFO_MAX - 1)
@@ -91,21 +104,51 @@ enum ctattr_protoinfo_tcp {
};
#define CTA_PROTOINFO_TCP_MAX (__CTA_PROTOINFO_TCP_MAX - 1)
+enum ctattr_protoinfo_dccp {
+ CTA_PROTOINFO_DCCP_UNSPEC,
+ CTA_PROTOINFO_DCCP_STATE,
+ CTA_PROTOINFO_DCCP_ROLE,
+ CTA_PROTOINFO_DCCP_HANDSHAKE_SEQ,
+ __CTA_PROTOINFO_DCCP_MAX,
+};
+#define CTA_PROTOINFO_DCCP_MAX (__CTA_PROTOINFO_DCCP_MAX - 1)
+
+enum ctattr_protoinfo_sctp {
+ CTA_PROTOINFO_SCTP_UNSPEC,
+ CTA_PROTOINFO_SCTP_STATE,
+ CTA_PROTOINFO_SCTP_VTAG_ORIGINAL,
+ CTA_PROTOINFO_SCTP_VTAG_REPLY,
+ __CTA_PROTOINFO_SCTP_MAX
+};
+#define CTA_PROTOINFO_SCTP_MAX (__CTA_PROTOINFO_SCTP_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_PACKETS, /* 64bit counters */
+ CTA_COUNTERS_BYTES, /* 64bit counters */
+ CTA_COUNTERS32_PACKETS, /* old 32bit counters, unused */
+ CTA_COUNTERS32_BYTES, /* old 32bit counters, unused */
__CTA_COUNTERS_MAX
};
#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1)
+enum ctattr_tstamp {
+ CTA_TIMESTAMP_UNSPEC,
+ CTA_TIMESTAMP_START,
+ CTA_TIMESTAMP_STOP,
+ __CTA_TIMESTAMP_MAX
+};
+#define CTA_TIMESTAMP_MAX (__CTA_TIMESTAMP_MAX - 1)
+
enum ctattr_nat {
CTA_NAT_UNSPEC,
- CTA_NAT_MINIP,
- CTA_NAT_MAXIP,
+ CTA_NAT_V4_MINIP,
+#define CTA_NAT_MINIP CTA_NAT_V4_MINIP
+ CTA_NAT_V4_MAXIP,
+#define CTA_NAT_MAXIP CTA_NAT_V4_MAXIP
CTA_NAT_PROTO,
+ CTA_NAT_V6_MINIP,
+ CTA_NAT_V6_MAXIP,
__CTA_NAT_MAX
};
#define CTA_NAT_MAX (__CTA_NAT_MAX - 1)
@@ -118,6 +161,15 @@ enum ctattr_protonat {
};
#define CTA_PROTONAT_MAX (__CTA_PROTONAT_MAX - 1)
+enum ctattr_natseq {
+ CTA_NAT_SEQ_UNSPEC,
+ CTA_NAT_SEQ_CORRECTION_POS,
+ CTA_NAT_SEQ_OFFSET_BEFORE,
+ CTA_NAT_SEQ_OFFSET_AFTER,
+ __CTA_NAT_SEQ_MAX
+};
+#define CTA_NAT_SEQ_MAX (__CTA_NAT_SEQ_MAX - 1)
+
enum ctattr_expect {
CTA_EXPECT_UNSPEC,
CTA_EXPECT_MASTER,
@@ -126,15 +178,71 @@ enum ctattr_expect {
CTA_EXPECT_TIMEOUT,
CTA_EXPECT_ID,
CTA_EXPECT_HELP_NAME,
+ CTA_EXPECT_ZONE,
+ CTA_EXPECT_FLAGS,
+ CTA_EXPECT_CLASS,
+ CTA_EXPECT_NAT,
+ CTA_EXPECT_FN,
__CTA_EXPECT_MAX
};
#define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1)
+enum ctattr_expect_nat {
+ CTA_EXPECT_NAT_UNSPEC,
+ CTA_EXPECT_NAT_DIR,
+ CTA_EXPECT_NAT_TUPLE,
+ __CTA_EXPECT_NAT_MAX
+};
+#define CTA_EXPECT_NAT_MAX (__CTA_EXPECT_NAT_MAX - 1)
+
enum ctattr_help {
CTA_HELP_UNSPEC,
CTA_HELP_NAME,
+ CTA_HELP_INFO,
__CTA_HELP_MAX
};
#define CTA_HELP_MAX (__CTA_HELP_MAX - 1)
+enum ctattr_secctx {
+ CTA_SECCTX_UNSPEC,
+ CTA_SECCTX_NAME,
+ __CTA_SECCTX_MAX
+};
+#define CTA_SECCTX_MAX (__CTA_SECCTX_MAX - 1)
+
+enum ctattr_stats_cpu {
+ CTA_STATS_UNSPEC,
+ CTA_STATS_SEARCHED,
+ CTA_STATS_FOUND,
+ CTA_STATS_NEW,
+ CTA_STATS_INVALID,
+ CTA_STATS_IGNORE,
+ CTA_STATS_DELETE,
+ CTA_STATS_DELETE_LIST,
+ CTA_STATS_INSERT,
+ CTA_STATS_INSERT_FAILED,
+ CTA_STATS_DROP,
+ CTA_STATS_EARLY_DROP,
+ CTA_STATS_ERROR,
+ CTA_STATS_SEARCH_RESTART,
+ __CTA_STATS_MAX,
+};
+#define CTA_STATS_MAX (__CTA_STATS_MAX - 1)
+
+enum ctattr_stats_global {
+ CTA_STATS_GLOBAL_UNSPEC,
+ CTA_STATS_GLOBAL_ENTRIES,
+ __CTA_STATS_GLOBAL_MAX,
+};
+#define CTA_STATS_GLOBAL_MAX (__CTA_STATS_GLOBAL_MAX - 1)
+
+enum ctattr_expect_stats {
+ CTA_STATS_EXP_UNSPEC,
+ CTA_STATS_EXP_NEW,
+ CTA_STATS_EXP_CREATE,
+ CTA_STATS_EXP_DELETE,
+ __CTA_STATS_EXP_MAX,
+};
+#define CTA_STATS_EXP_MAX (__CTA_STATS_EXP_MAX - 1)
+
#endif /* _IPCONNTRACK_NETLINK_H */
diff --git a/include/linux/netfilter/nfnetlink_log.h b/include/linux/netfilter/nfnetlink_log.h
index 38fafc16..2cfbf139 100644
--- a/include/linux/netfilter/nfnetlink_log.h
+++ b/include/linux/netfilter/nfnetlink_log.h
@@ -5,8 +5,8 @@
* 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)))
+#ifndef __aligned_be64
+#define __aligned_be64 u_int64_t __attribute__((aligned(8)))
#endif
#include <linux/types.h>
@@ -21,30 +21,30 @@ enum nfulnl_msg_types {
struct nfulnl_msg_packet_hdr {
__be16 hw_protocol; /* hw protocol (network order) */
- u_int8_t hook; /* netfilter hook */
- u_int8_t _pad;
+ __u8 hook; /* netfilter hook */
+ __u8 _pad;
};
struct nfulnl_msg_packet_hw {
__be16 hw_addrlen;
- u_int16_t _pad;
- u_int8_t hw_addr[8];
+ __u16 _pad;
+ __u8 hw_addr[8];
};
struct nfulnl_msg_packet_timestamp {
- aligned_be64 sec;
- aligned_be64 usec;
+ __aligned_be64 sec;
+ __aligned_be64 usec;
};
enum nfulnl_attr_type {
NFULA_UNSPEC,
NFULA_PACKET_HDR,
- NFULA_MARK, /* u_int32_t nfmark */
+ NFULA_MARK, /* __u32 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_IFINDEX_INDEV, /* __u32 ifindex */
+ NFULA_IFINDEX_OUTDEV, /* __u32 ifindex */
+ NFULA_IFINDEX_PHYSINDEV, /* __u32 ifindex */
+ NFULA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */
NFULA_HWADDR, /* nfulnl_msg_packet_hw */
NFULA_PAYLOAD, /* opaque data payload */
NFULA_PREFIX, /* string prefix */
@@ -52,6 +52,9 @@ enum nfulnl_attr_type {
NFULA_SEQ, /* instance-local sequence number */
NFULA_SEQ_GLOBAL, /* global sequence number */
NFULA_GID, /* group id of socket */
+ NFULA_HWTYPE, /* hardware type */
+ NFULA_HWHEADER, /* hardware header */
+ NFULA_HWLEN, /* hardware header length */
__NFULA_MAX
};
@@ -66,23 +69,23 @@ enum nfulnl_msg_config_cmds {
};
struct nfulnl_msg_config_cmd {
- u_int8_t command; /* nfulnl_msg_config_cmds */
+ __u8 command; /* nfulnl_msg_config_cmds */
} __attribute__ ((packed));
struct nfulnl_msg_config_mode {
__be32 copy_range;
- u_int8_t copy_mode;
- u_int8_t _pad;
+ __u8 copy_mode;
+ __u8 _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_NLBUFSIZ, /* __u32 buffer size */
+ NFULA_CFG_TIMEOUT, /* __u32 in 1/100 s */
+ NFULA_CFG_QTHRESH, /* __u32 */
+ NFULA_CFG_FLAGS, /* __u16 */
__NFULA_CFG_MAX
};
#define NFULA_CFG_MAX (__NFULA_CFG_MAX -1)
@@ -90,6 +93,7 @@ enum nfulnl_attr_config {
#define NFULNL_COPY_NONE 0x00
#define NFULNL_COPY_META 0x01
#define NFULNL_COPY_PACKET 0x02
+/* 0xff is reserved, don't use it for new copy modes. */
#define NFULNL_CFG_F_SEQ 0x0001
#define NFULNL_CFG_F_SEQ_GLOBAL 0x0002
diff --git a/include/linux/netfilter/nfnetlink_queue.h b/include/linux/netfilter/nfnetlink_queue.h
index bf7cfb67..95af967d 100644
--- a/include/linux/netfilter/nfnetlink_queue.h
+++ b/include/linux/netfilter/nfnetlink_queue.h
@@ -4,14 +4,15 @@
#include <linux/types.h>
#include <linux/netfilter/nfnetlink.h>
-#ifndef aligned_be64
-#define aligned_be64 u_int64_t __attribute__((aligned(8)))
+#ifndef __aligned_be64
+#define __aligned_be64 u_int64_t __attribute__((aligned(8)))
#endif
enum nfqnl_msg_types {
NFQNL_MSG_PACKET, /* packet from kernel to userspace */
NFQNL_MSG_VERDICT, /* verdict from userspace to kernel */
NFQNL_MSG_CONFIG, /* connect to a particular queue */
+ NFQNL_MSG_VERDICT_BATCH, /* batchv from userspace to kernel */
NFQNL_MSG_MAX
};
@@ -19,32 +20,35 @@ enum nfqnl_msg_types {
struct nfqnl_msg_packet_hdr {
__be32 packet_id; /* unique ID of packet in queue */
__be16 hw_protocol; /* hw protocol (network order) */
- u_int8_t hook; /* netfilter hook */
+ __u8 hook; /* netfilter hook */
} __attribute__ ((packed));
struct nfqnl_msg_packet_hw {
__be16 hw_addrlen;
- u_int16_t _pad;
- u_int8_t hw_addr[8];
+ __u16 _pad;
+ __u8 hw_addr[8];
};
struct nfqnl_msg_packet_timestamp {
- aligned_be64 sec;
- aligned_be64 usec;
+ __aligned_be64 sec;
+ __aligned_be64 usec;
};
enum nfqnl_attr_type {
NFQA_UNSPEC,
NFQA_PACKET_HDR,
NFQA_VERDICT_HDR, /* nfqnl_msg_verdict_hrd */
- NFQA_MARK, /* u_int32_t nfmark */
+ NFQA_MARK, /* __u32 nfmark */
NFQA_TIMESTAMP, /* nfqnl_msg_packet_timestamp */
- NFQA_IFINDEX_INDEV, /* u_int32_t ifindex */
- NFQA_IFINDEX_OUTDEV, /* u_int32_t ifindex */
- NFQA_IFINDEX_PHYSINDEV, /* u_int32_t ifindex */
- NFQA_IFINDEX_PHYSOUTDEV, /* u_int32_t ifindex */
+ NFQA_IFINDEX_INDEV, /* __u32 ifindex */
+ NFQA_IFINDEX_OUTDEV, /* __u32 ifindex */
+ NFQA_IFINDEX_PHYSINDEV, /* __u32 ifindex */
+ NFQA_IFINDEX_PHYSOUTDEV, /* __u32 ifindex */
NFQA_HWADDR, /* nfqnl_msg_packet_hw */
NFQA_PAYLOAD, /* opaque data payload */
+ NFQA_CT, /* nf_conntrack_netlink.h */
+ NFQA_CT_INFO, /* enum ip_conntrack_info */
+ NFQA_CAP_LEN, /* __u32 length of captured packet */
__NFQA_MAX
};
@@ -65,8 +69,8 @@ enum nfqnl_msg_config_cmds {
};
struct nfqnl_msg_config_cmd {
- u_int8_t command; /* nfqnl_msg_config_cmds */
- u_int8_t _pad;
+ __u8 command; /* nfqnl_msg_config_cmds */
+ __u8 _pad;
__be16 pf; /* AF_xxx for PF_[UN]BIND */
};
@@ -78,7 +82,7 @@ enum nfqnl_config_mode {
struct nfqnl_msg_config_params {
__be32 copy_range;
- u_int8_t copy_mode; /* enum nfqnl_config_mode */
+ __u8 copy_mode; /* enum nfqnl_config_mode */
} __attribute__ ((packed));
@@ -86,9 +90,16 @@ enum nfqnl_attr_config {
NFQA_CFG_UNSPEC,
NFQA_CFG_CMD, /* nfqnl_msg_config_cmd */
NFQA_CFG_PARAMS, /* nfqnl_msg_config_params */
- NFQA_CFG_QUEUE_MAXLEN, /* u_int32_t */
+ NFQA_CFG_QUEUE_MAXLEN, /* __u32 */
+ NFQA_CFG_MASK, /* identify which flags to change */
+ NFQA_CFG_FLAGS, /* value of these flags (__u32) */
__NFQA_CFG_MAX
};
#define NFQA_CFG_MAX (__NFQA_CFG_MAX-1)
+/* Flags for NFQA_CFG_FLAGS */
+#define NFQA_CFG_F_FAIL_OPEN (1 << 0)
+#define NFQA_CFG_F_CONNTRACK (1 << 1)
+#define NFQA_CFG_F_MAX (1 << 2)
+
#endif /* _NFNETLINK_QUEUE_H */
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index d252103d..39252548 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -24,19 +24,18 @@
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
#define NETLINK_ECRYPTFS 19
+#define NETLINK_RDMA 20
#define MAX_LINKS 32
-struct sockaddr_nl
-{
+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
-{
+struct nlmsghdr {
__u32 nlmsg_len; /* Length of message including header */
__u16 nlmsg_type; /* Message content */
__u16 nlmsg_flags; /* Additional flags */
@@ -50,6 +49,7 @@ struct nlmsghdr
#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 */
+#define NLM_F_DUMP_INTR 16 /* Dump was inconsistent due to sequence change */
/* Modifiers to GET request */
#define NLM_F_ROOT 0x100 /* specify tree root */
@@ -72,7 +72,7 @@ struct nlmsghdr
Check NLM_F_EXCL
*/
-#define NLMSG_ALIGNTO 4
+#define NLMSG_ALIGNTO 4U
#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))
@@ -92,8 +92,7 @@ struct nlmsghdr
#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */
-struct nlmsgerr
-{
+struct nlmsgerr {
int error;
struct nlmsghdr msg;
};
@@ -101,9 +100,10 @@ struct nlmsgerr
#define NETLINK_ADD_MEMBERSHIP 1
#define NETLINK_DROP_MEMBERSHIP 2
#define NETLINK_PKTINFO 3
+#define NETLINK_BROADCAST_ERROR 4
+#define NETLINK_NO_ENOBUFS 5
-struct nl_pktinfo
-{
+struct nl_pktinfo {
__u32 group;
};
@@ -123,8 +123,7 @@ enum {
* <-------------- nlattr->nla_len -------------->
*/
-struct nlattr
-{
+struct nlattr {
__u16 nla_len;
__u16 nla_type;
};
diff --git a/include/linux/pkt_cls.h b/include/linux/pkt_cls.h
index 3c842edf..defbde20 100644
--- a/include/linux/pkt_cls.h
+++ b/include/linux/pkt_cls.h
@@ -75,8 +75,7 @@ bits 9,10,11: redirect counter - redirect TTL. Loop avoidance
#define SET_TC_AT(v,n) ((V_TC_AT(n)) | (v & ~M_TC_AT))
/* Action attributes */
-enum
-{
+enum {
TCA_ACT_UNSPEC,
TCA_ACT_KIND,
TCA_ACT_OPTIONS,
@@ -108,8 +107,7 @@ enum
#define TC_ACT_JUMP 0x10000000
/* Action type identifiers*/
-enum
-{
+enum {
TCA_ID_UNSPEC=0,
TCA_ID_POLICE=1,
/* other actions go here */
@@ -118,8 +116,7 @@ enum
#define TCA_ID_MAX __TCA_ID_MAX
-struct tc_police
-{
+struct tc_police {
__u32 index;
int action;
#define TC_POLICE_UNSPEC TC_ACT_UNSPEC
@@ -138,15 +135,13 @@ struct tc_police
__u32 capab;
};
-struct tcf_t
-{
+struct tcf_t {
__u64 install;
__u64 lastuse;
__u64 expires;
};
-struct tc_cnt
-{
+struct tc_cnt {
int refcnt;
int bindcnt;
};
@@ -158,8 +153,7 @@ struct tc_cnt
int refcnt; \
int bindcnt
-enum
-{
+enum {
TCA_POLICE_UNSPEC,
TCA_POLICE_TBF,
TCA_POLICE_RATE,
@@ -182,8 +176,7 @@ enum
#define TC_U32_UNSPEC 0
#define TC_U32_ROOT (0xFFF00000)
-enum
-{
+enum {
TCA_U32_UNSPEC,
TCA_U32_CLASSID,
TCA_U32_HASH,
@@ -200,16 +193,14 @@ enum
#define TCA_U32_MAX (__TCA_U32_MAX - 1)
-struct tc_u32_key
-{
+struct tc_u32_key {
__be32 mask;
__be32 val;
int off;
int offmask;
};
-struct tc_u32_sel
-{
+struct tc_u32_sel {
unsigned char flags;
unsigned char offshift;
unsigned char nkeys;
@@ -223,15 +214,13 @@ struct tc_u32_sel
struct tc_u32_key keys[0];
};
-struct tc_u32_mark
-{
+struct tc_u32_mark {
__u32 val;
__u32 mask;
__u32 success;
};
-struct tc_u32_pcnt
-{
+struct tc_u32_pcnt {
__u64 rcnt;
__u64 rhit;
__u64 kcnts[0];
@@ -249,8 +238,7 @@ struct tc_u32_pcnt
/* RSVP filter */
-enum
-{
+enum {
TCA_RSVP_UNSPEC,
TCA_RSVP_CLASSID,
TCA_RSVP_DST,
@@ -263,15 +251,13 @@ enum
#define TCA_RSVP_MAX (__TCA_RSVP_MAX - 1 )
-struct tc_rsvp_gpi
-{
+struct tc_rsvp_gpi {
__u32 key;
__u32 mask;
int offset;
};
-struct tc_rsvp_pinfo
-{
+struct tc_rsvp_pinfo {
struct tc_rsvp_gpi dpi;
struct tc_rsvp_gpi spi;
__u8 protocol;
@@ -282,8 +268,7 @@ struct tc_rsvp_pinfo
/* ROUTE filter */
-enum
-{
+enum {
TCA_ROUTE4_UNSPEC,
TCA_ROUTE4_CLASSID,
TCA_ROUTE4_TO,
@@ -299,8 +284,7 @@ enum
/* FW filter */
-enum
-{
+enum {
TCA_FW_UNSPEC,
TCA_FW_CLASSID,
TCA_FW_POLICE,
@@ -314,8 +298,7 @@ enum
/* TC index filter */
-enum
-{
+enum {
TCA_TCINDEX_UNSPEC,
TCA_TCINDEX_HASH,
TCA_TCINDEX_MASK,
@@ -331,8 +314,7 @@ enum
/* Flow filter */
-enum
-{
+enum {
FLOW_KEY_SRC,
FLOW_KEY_DST,
FLOW_KEY_PROTO,
@@ -350,19 +332,18 @@ enum
FLOW_KEY_SKUID,
FLOW_KEY_SKGID,
FLOW_KEY_VLAN_TAG,
+ FLOW_KEY_RXHASH,
__FLOW_KEY_MAX,
};
#define FLOW_KEY_MAX (__FLOW_KEY_MAX - 1)
-enum
-{
+enum {
FLOW_MODE_MAP,
FLOW_MODE_HASH,
};
-enum
-{
+enum {
TCA_FLOW_UNSPEC,
TCA_FLOW_KEYS,
TCA_FLOW_MODE,
@@ -383,8 +364,7 @@ enum
/* Basic filter */
-enum
-{
+enum {
TCA_BASIC_UNSPEC,
TCA_BASIC_CLASSID,
TCA_BASIC_EMATCHES,
@@ -398,8 +378,7 @@ enum
/* Cgroup classifier */
-enum
-{
+enum {
TCA_CGROUP_UNSPEC,
TCA_CGROUP_ACT,
TCA_CGROUP_POLICE,
@@ -411,14 +390,12 @@ enum
/* Extended Matches */
-struct tcf_ematch_tree_hdr
-{
+struct tcf_ematch_tree_hdr {
__u16 nmatches;
__u16 progid;
};
-enum
-{
+enum {
TCA_EMATCH_TREE_UNSPEC,
TCA_EMATCH_TREE_HDR,
TCA_EMATCH_TREE_LIST,
@@ -426,8 +403,7 @@ enum
};
#define TCA_EMATCH_TREE_MAX (__TCA_EMATCH_TREE_MAX - 1)
-struct tcf_ematch_hdr
-{
+struct tcf_ematch_hdr {
__u16 matchid;
__u16 kind;
__u16 flags;
@@ -457,8 +433,7 @@ struct tcf_ematch_hdr
#define TCF_EM_REL_MASK 3
#define TCF_EM_REL_VALID(v) (((v) & TCF_EM_REL_MASK) != TCF_EM_REL_MASK)
-enum
-{
+enum {
TCF_LAYER_LINK,
TCF_LAYER_NETWORK,
TCF_LAYER_TRANSPORT,
@@ -479,13 +454,11 @@ enum
#define TCF_EM_VLAN 6
#define TCF_EM_MAX 6
-enum
-{
+enum {
TCF_EM_PROG_TC
};
-enum
-{
+enum {
TCF_EM_OPND_EQ,
TCF_EM_OPND_GT,
TCF_EM_OPND_LT
diff --git a/include/linux/pkt_sched.h b/include/linux/pkt_sched.h
index 268c5159..a0837a0b 100644
--- a/include/linux/pkt_sched.h
+++ b/include/linux/pkt_sched.h
@@ -1,6 +1,8 @@
#ifndef __LINUX_PKT_SCHED_H
#define __LINUX_PKT_SCHED_H
+#include <linux/types.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.
@@ -27,8 +29,7 @@
Particular schedulers may have also their private records.
*/
-struct tc_stats
-{
+struct tc_stats {
__u64 bytes; /* NUmber of enqueues bytes */
__u32 packets; /* Number of enqueued packets */
__u32 drops; /* Packets dropped because of lack of resources */
@@ -40,8 +41,7 @@ struct tc_stats
__u32 backlog;
};
-struct tc_estimator
-{
+struct tc_estimator {
signed char interval;
unsigned char ewma_log;
};
@@ -73,20 +73,40 @@ struct tc_estimator
#define TC_H_ROOT (0xFFFFFFFFU)
#define TC_H_INGRESS (0xFFFFFFF1U)
-struct tc_ratespec
-{
+struct tc_ratespec {
unsigned char cell_log;
unsigned char __reserved;
- unsigned short feature;
- short addend;
+ unsigned short overhead;
+ short cell_align;
unsigned short mpu;
__u32 rate;
};
+#define TC_RTAB_SIZE 1024
+
+struct tc_sizespec {
+ unsigned char cell_log;
+ unsigned char size_log;
+ short cell_align;
+ int overhead;
+ unsigned int linklayer;
+ unsigned int mpu;
+ unsigned int mtu;
+ unsigned int tsize;
+};
+
+enum {
+ TCA_STAB_UNSPEC,
+ TCA_STAB_BASE,
+ TCA_STAB_DATA,
+ __TCA_STAB_MAX
+};
+
+#define TCA_STAB_MAX (__TCA_STAB_MAX - 1)
+
/* FIFO section */
-struct tc_fifo_qopt
-{
+struct tc_fifo_qopt {
__u32 limit; /* Queue length: bytes for bfifo, packets for pfifo */
};
@@ -95,25 +115,42 @@ struct tc_fifo_qopt
#define TCQ_PRIO_BANDS 16
#define TCQ_MIN_PRIO_BANDS 2
-struct tc_prio_qopt
-{
+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
+/* MULTIQ section */
+
+struct tc_multiq_qopt {
+ __u16 bands; /* Number of bands */
+ __u16 max_bands; /* Maximum number of queues */
};
-#define TCA_PRIO_MAX (__TCA_PRIO_MAX - 1)
+/* PLUG section */
+
+#define TCQ_PLUG_BUFFER 0
+#define TCQ_PLUG_RELEASE_ONE 1
+#define TCQ_PLUG_RELEASE_INDEFINITE 2
+#define TCQ_PLUG_LIMIT 3
+
+struct tc_plug_qopt {
+ /* TCQ_PLUG_BUFFER: Inset a plug into the queue and
+ * buffer any incoming packets
+ * TCQ_PLUG_RELEASE_ONE: Dequeue packets from queue head
+ * to beginning of the next plug.
+ * TCQ_PLUG_RELEASE_INDEFINITE: Dequeue all packets from queue.
+ * Stop buffering packets until the next TCQ_PLUG_BUFFER
+ * command is received (just act as a pass-thru queue).
+ * TCQ_PLUG_LIMIT: Increase/decrease queue size
+ */
+ int action;
+ __u32 limit;
+};
/* TBF section */
-struct tc_tbf_qopt
-{
+struct tc_tbf_qopt {
struct tc_ratespec rate;
struct tc_ratespec peakrate;
__u32 limit;
@@ -121,8 +158,7 @@ struct tc_tbf_qopt
__u32 mtu;
};
-enum
-{
+enum {
TCA_TBF_UNSPEC,
TCA_TBF_PARMS,
TCA_TBF_RTAB,
@@ -139,8 +175,7 @@ enum
/* SFQ section */
-struct tc_sfq_qopt
-{
+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 */
@@ -148,6 +183,10 @@ struct tc_sfq_qopt
unsigned flows; /* Maximal number of flows */
};
+struct tc_sfq_xstats {
+ __s32 allot;
+};
+
/*
* NOTE: limit, divisor and flows are hardwired to code at the moment.
*
@@ -159,8 +198,7 @@ struct tc_sfq_qopt
/* RED section */
-enum
-{
+enum {
TCA_RED_UNSPEC,
TCA_RED_PARMS,
TCA_RED_STAB,
@@ -169,8 +207,7 @@ enum
#define TCA_RED_MAX (__TCA_RED_MAX - 1)
-struct tc_red_qopt
-{
+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) */
@@ -182,8 +219,7 @@ struct tc_red_qopt
#define TC_RED_HARDDROP 2
};
-struct tc_red_xstats
-{
+struct tc_red_xstats {
__u32 early; /* Early drops */
__u32 pdrop; /* Drops due to queue limits */
__u32 other; /* Drops due to drop() calls */
@@ -194,8 +230,7 @@ struct tc_red_xstats
#define MAX_DPs 16
-enum
-{
+enum {
TCA_GRED_UNSPEC,
TCA_GRED_PARMS,
TCA_GRED_STAB,
@@ -205,12 +240,11 @@ enum
#define TCA_GRED_MAX (__TCA_GRED_MAX - 1)
-struct tc_gred_qopt
-{
+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 DP; /* up to 2^32 DPs */
__u32 backlog;
__u32 qave;
__u32 forced;
@@ -226,8 +260,7 @@ struct tc_gred_qopt
};
/* gred setup */
-struct tc_gred_sopt
-{
+struct tc_gred_sopt {
__u32 DPs;
__u32 def_DP;
__u8 grio;
@@ -235,13 +268,41 @@ struct tc_gred_sopt
__u16 pad1;
};
+/* CHOKe section */
+
+enum {
+ TCA_CHOKE_UNSPEC,
+ TCA_CHOKE_PARMS,
+ TCA_CHOKE_STAB,
+ __TCA_CHOKE_MAX,
+};
+
+#define TCA_CHOKE_MAX (__TCA_CHOKE_MAX - 1)
+
+struct tc_choke_qopt {
+ __u32 limit; /* Hard queue length (packets) */
+ __u32 qth_min; /* Min average threshold (packets) */
+ __u32 qth_max; /* Max average threshold (packets) */
+ 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; /* see RED flags */
+};
+
+struct tc_choke_xstats {
+ __u32 early; /* Early drops */
+ __u32 pdrop; /* Drops due to queue limits */
+ __u32 other; /* Drops due to drop() calls */
+ __u32 marked; /* Marked packets */
+ __u32 matched; /* Drops due to flow match */
+};
+
/* 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_htb_opt {
struct tc_ratespec rate;
struct tc_ratespec ceil;
__u32 buffer;
@@ -250,8 +311,7 @@ struct tc_htb_opt
__u32 level; /* out only */
__u32 prio;
};
-struct tc_htb_glob
-{
+struct tc_htb_glob {
__u32 version; /* to match HTB/TC */
__u32 rate2quantum; /* bps->quantum divisor */
__u32 defcls; /* default class number */
@@ -260,8 +320,7 @@ struct tc_htb_glob
/* stats */
__u32 direct_pkts; /* count of non shapped packets */
};
-enum
-{
+enum {
TCA_HTB_UNSPEC,
TCA_HTB_PARMS,
TCA_HTB_INIT,
@@ -272,8 +331,7 @@ enum
#define TCA_HTB_MAX (__TCA_HTB_MAX - 1)
-struct tc_htb_xstats
-{
+struct tc_htb_xstats {
__u32 lends;
__u32 borrows;
__u32 giants; /* too big packets (rate will not be accurate) */
@@ -283,28 +341,24 @@ struct tc_htb_xstats
/* HFSC section */
-struct tc_hfsc_qopt
-{
+struct tc_hfsc_qopt {
__u16 defcls; /* default class */
};
-struct tc_service_curve
-{
+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
-{
+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
-{
+enum {
TCA_HFSC_UNSPEC,
TCA_HFSC_RSC,
TCA_HFSC_FSC,
@@ -321,8 +375,7 @@ enum
#define TC_CBQ_MAXLEVEL 8
#define TC_CBQ_DEF_EWMA 5
-struct tc_cbq_lssopt
-{
+struct tc_cbq_lssopt {
unsigned char change;
unsigned char flags;
#define TCF_CBQ_LSS_BOUNDED 1
@@ -341,8 +394,7 @@ struct tc_cbq_lssopt
__u32 avpkt;
};
-struct tc_cbq_wrropt
-{
+struct tc_cbq_wrropt {
unsigned char flags;
unsigned char priority;
unsigned char cpriority;
@@ -351,8 +403,7 @@ struct tc_cbq_wrropt
__u32 weight;
};
-struct tc_cbq_ovl
-{
+struct tc_cbq_ovl {
unsigned char strategy;
#define TC_CBQ_OVL_CLASSIC 0
#define TC_CBQ_OVL_DELAY 1
@@ -364,30 +415,26 @@ struct tc_cbq_ovl
__u32 penalty;
};
-struct tc_cbq_police
-{
+struct tc_cbq_police {
unsigned char police;
unsigned char __res1;
unsigned short __res2;
};
-struct tc_cbq_fopt
-{
+struct tc_cbq_fopt {
__u32 split;
__u32 defmap;
__u32 defchange;
};
-struct tc_cbq_xstats
-{
+struct tc_cbq_xstats {
__u32 borrows;
__u32 overactions;
__s32 avgidle;
__s32 undertime;
};
-enum
-{
+enum {
TCA_CBQ_UNSPEC,
TCA_CBQ_LSSOPT,
TCA_CBQ_WRROPT,
@@ -415,6 +462,21 @@ enum {
#define TCA_DSMARK_MAX (__TCA_DSMARK_MAX - 1)
+/* fq_codel section */
+
+enum {
+ TCA_FQ_CODEL_UNSPEC,
+ TCA_FQ_CODEL_TARGET,
+ TCA_FQ_CODEL_LIMIT,
+ TCA_FQ_CODEL_INTERVAL,
+ TCA_FQ_CODEL_ECN,
+ TCA_FQ_CODEL_FLOWS,
+ TCA_FQ_CODEL_QUANTUM,
+ __TCA_FQ_CODEL_MAX
+};
+
+#define TCA_FQ_CODEL_MAX (__TCA_FQ_CODEL_MAX - 1)
+
/* ATM section */
enum {
@@ -432,20 +494,19 @@ enum {
/* Network emulator */
-enum
-{
+enum {
TCA_NETEM_UNSPEC,
TCA_NETEM_CORR,
TCA_NETEM_DELAY_DIST,
TCA_NETEM_REORDER,
TCA_NETEM_CORRUPT,
+ TCA_NETEM_LOSS,
__TCA_NETEM_MAX,
};
#define TCA_NETEM_MAX (__TCA_NETEM_MAX - 1)
-struct tc_netem_qopt
-{
+struct tc_netem_qopt {
__u32 latency; /* added delay (us) */
__u32 limit; /* fifo limit (packets) */
__u32 loss; /* random packet loss (0=none ~0=100%) */
@@ -454,25 +515,128 @@ struct tc_netem_qopt
__u32 jitter; /* random jitter in latency (us) */
};
-struct tc_netem_corr
-{
+struct tc_netem_corr {
__u32 delay_corr; /* delay correlation */
__u32 loss_corr; /* packet loss correlation */
__u32 dup_corr; /* duplicate correlation */
};
-struct tc_netem_reorder
-{
+struct tc_netem_reorder {
__u32 probability;
__u32 correlation;
};
-struct tc_netem_corrupt
-{
+struct tc_netem_corrupt {
__u32 probability;
__u32 correlation;
};
+enum {
+ NETEM_LOSS_UNSPEC,
+ NETEM_LOSS_GI, /* General Intuitive - 4 state model */
+ NETEM_LOSS_GE, /* Gilbert Elliot models */
+ __NETEM_LOSS_MAX
+};
+#define NETEM_LOSS_MAX (__NETEM_LOSS_MAX - 1)
+
+/* State transition probablities for 4 state model */
+struct tc_netem_gimodel {
+ __u32 p13;
+ __u32 p31;
+ __u32 p32;
+ __u32 p14;
+ __u32 p23;
+};
+
+/* Gilbert-Elliot models */
+struct tc_netem_gemodel {
+ __u32 p;
+ __u32 r;
+ __u32 h;
+ __u32 k1;
+};
+
#define NETEM_DIST_SCALE 8192
+#define NETEM_DIST_MAX 16384
+
+/* DRR */
+
+enum {
+ TCA_DRR_UNSPEC,
+ TCA_DRR_QUANTUM,
+ __TCA_DRR_MAX
+};
+
+#define TCA_DRR_MAX (__TCA_DRR_MAX - 1)
+
+struct tc_drr_stats {
+ __u32 deficit;
+};
+
+/* MQPRIO */
+#define TC_QOPT_BITMASK 15
+#define TC_QOPT_MAX_QUEUE 16
+
+struct tc_mqprio_qopt {
+ __u8 num_tc;
+ __u8 prio_tc_map[TC_QOPT_BITMASK + 1];
+ __u8 hw;
+ __u16 count[TC_QOPT_MAX_QUEUE];
+ __u16 offset[TC_QOPT_MAX_QUEUE];
+};
+
+/* SFB */
+
+enum {
+ TCA_SFB_UNSPEC,
+ TCA_SFB_PARMS,
+ __TCA_SFB_MAX,
+};
+
+#define TCA_SFB_MAX (__TCA_SFB_MAX - 1)
+
+/*
+ * Note: increment, decrement are Q0.16 fixed-point values.
+ */
+struct tc_sfb_qopt {
+ __u32 rehash_interval; /* delay between hash move, in ms */
+ __u32 warmup_time; /* double buffering warmup time in ms (warmup_time < rehash_interval) */
+ __u32 max; /* max len of qlen_min */
+ __u32 bin_size; /* maximum queue length per bin */
+ __u32 increment; /* probability increment, (d1 in Blue) */
+ __u32 decrement; /* probability decrement, (d2 in Blue) */
+ __u32 limit; /* max SFB queue length */
+ __u32 penalty_rate; /* inelastic flows are rate limited to 'rate' pps */
+ __u32 penalty_burst;
+};
+
+struct tc_sfb_xstats {
+ __u32 earlydrop;
+ __u32 penaltydrop;
+ __u32 bucketdrop;
+ __u32 queuedrop;
+ __u32 childdrop; /* drops in child qdisc */
+ __u32 marked;
+ __u32 maxqlen;
+ __u32 maxprob;
+ __u32 avgprob;
+};
+
+#define SFB_MAX_PROB 0xFFFF
+
+/* QFQ */
+enum {
+ TCA_QFQ_UNSPEC,
+ TCA_QFQ_WEIGHT,
+ TCA_QFQ_LMAX,
+ __TCA_QFQ_MAX
+};
+
+#define TCA_QFQ_MAX (__TCA_QFQ_MAX - 1)
+
+struct tc_qfq_stats {
+ __u32 weight;
+ __u32 lmax;
+};
#endif
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index 2473dbe2..2363c18e 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -1,11 +1,19 @@
#ifndef __LINUX_RTNETLINK_H
#define __LINUX_RTNETLINK_H
+#include <linux/types.h>
#include <linux/netlink.h>
#include <linux/if_link.h>
#include <linux/if_addr.h>
#include <linux/neighbour.h>
+/* rtnetlink families. Values up to 127 are reserved for real address
+ * families, values above 128 may be used arbitrarily.
+ */
+#define RTNL_FAMILY_IPMR 128
+#define RTNL_FAMILY_IP6MR 129
+#define RTNL_FAMILY_MAX 129
+
/****
* Routing/neighbour discovery messages.
****/
@@ -103,10 +111,15 @@ enum {
RTM_NEWADDRLABEL = 72,
#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL
RTM_DELADDRLABEL,
-#define RTM_NEWADDRLABEL RTM_NEWADDRLABEL
+#define RTM_DELADDRLABEL RTM_DELADDRLABEL
RTM_GETADDRLABEL,
#define RTM_GETADDRLABEL RTM_GETADDRLABEL
+ RTM_GETDCB = 78,
+#define RTM_GETDCB RTM_GETDCB
+ RTM_SETDCB,
+#define RTM_SETDCB RTM_SETDCB
+
__RTM_MAX,
#define RTM_MAX (((__RTM_MAX + 3) & ~3) - 1)
};
@@ -121,8 +134,7 @@ enum {
with attribute type.
*/
-struct rtattr
-{
+struct rtattr {
unsigned short rta_len;
unsigned short rta_type;
};
@@ -148,8 +160,7 @@ struct rtattr
* Definitions used in routing table administration.
****/
-struct rtmsg
-{
+struct rtmsg {
unsigned char rtm_family;
unsigned char rtm_dst_len;
unsigned char rtm_src_len;
@@ -165,8 +176,7 @@ struct rtmsg
/* rtm_type */
-enum
-{
+enum {
RTN_UNSPEC,
RTN_UNICAST, /* Gateway or direct route */
RTN_LOCAL, /* Accept locally */
@@ -211,6 +221,7 @@ enum
#define RTPROT_DNROUTED 13 /* DECnet routing daemon */
#define RTPROT_XORP 14 /* XORP */
#define RTPROT_NTK 15 /* Netsukuku */
+#define RTPROT_DHCP 16 /* DHCP client */
/* rtm_scope
@@ -223,8 +234,7 @@ enum
could be assigned a value between UNIVERSE and LINK.
*/
-enum rt_scope_t
-{
+enum rt_scope_t {
RT_SCOPE_UNIVERSE=0,
/* User defined values */
RT_SCOPE_SITE=200,
@@ -242,10 +252,10 @@ enum rt_scope_t
/* Reserved table identifiers */
-enum rt_class_t
-{
+enum rt_class_t {
RT_TABLE_UNSPEC=0,
/* User defined values */
+ RT_TABLE_COMPAT=252,
RT_TABLE_DEFAULT=253,
RT_TABLE_MAIN=254,
RT_TABLE_LOCAL=255,
@@ -255,8 +265,7 @@ enum rt_class_t
/* Routing message attributes */
-enum rtattr_type_t
-{
+enum rtattr_type_t {
RTA_UNSPEC,
RTA_DST,
RTA_SRC,
@@ -273,7 +282,7 @@ enum rtattr_type_t
RTA_SESSION, /* no longer used */
RTA_MP_ALGO, /* no longer used */
RTA_TABLE,
- RTA_GENERATION,
+ RTA_MARK,
__RTA_MAX
};
@@ -291,8 +300,7 @@ enum rtattr_type_t
* and rtt for different paths from multipath.
*/
-struct rtnexthop
-{
+struct rtnexthop {
unsigned short rtnh_len;
unsigned char rtnh_flags;
unsigned char rtnh_hops;
@@ -318,8 +326,7 @@ struct rtnexthop
/* RTM_CACHEINFO */
-struct rta_cacheinfo
-{
+struct rta_cacheinfo {
__u32 rta_clntref;
__u32 rta_lastuse;
__s32 rta_expires;
@@ -334,8 +341,7 @@ struct rta_cacheinfo
/* RTM_METRICS --- array of struct rtattr with types of RTAX_* */
-enum
-{
+enum {
RTAX_UNSPEC,
#define RTAX_UNSPEC RTAX_UNSPEC
RTAX_LOCK,
@@ -364,6 +370,8 @@ enum
#define RTAX_FEATURES RTAX_FEATURES
RTAX_RTO_MIN,
#define RTAX_RTO_MIN RTAX_RTO_MIN
+ RTAX_INITRWND,
+#define RTAX_INITRWND RTAX_INITRWND
__RTAX_MAX
};
@@ -374,8 +382,7 @@ enum
#define RTAX_FEATURE_TIMESTAMP 0x00000004
#define RTAX_FEATURE_ALLFRAG 0x00000008
-struct rta_session
-{
+struct rta_session {
__u8 proto;
__u8 pad1;
__u16 pad2;
@@ -400,8 +407,7 @@ struct rta_session
* General form of address family dependent message.
****/
-struct rtgenmsg
-{
+struct rtgenmsg {
unsigned char rtgen_family;
};
@@ -414,8 +420,7 @@ struct rtgenmsg
* on network protocol.
*/
-struct ifinfomsg
-{
+struct ifinfomsg {
unsigned char ifi_family;
unsigned char __ifi_pad;
unsigned short ifi_type; /* ARPHRD_* */
@@ -428,8 +433,7 @@ struct ifinfomsg
* prefix information
****/
-struct prefixmsg
-{
+struct prefixmsg {
unsigned char prefix_family;
unsigned char prefix_pad1;
unsigned short prefix_pad2;
@@ -450,8 +454,7 @@ enum
#define PREFIX_MAX (__PREFIX_MAX - 1)
-struct prefix_cacheinfo
-{
+struct prefix_cacheinfo {
__u32 preferred_time;
__u32 valid_time;
};
@@ -461,8 +464,7 @@ struct prefix_cacheinfo
* Traffic control messages.
****/
-struct tcmsg
-{
+struct tcmsg {
unsigned char tcm_family;
unsigned char tcm__pad1;
unsigned short tcm__pad2;
@@ -472,8 +474,7 @@ struct tcmsg
__u32 tcm_info;
};
-enum
-{
+enum {
TCA_UNSPEC,
TCA_KIND,
TCA_OPTIONS,
@@ -482,6 +483,7 @@ enum
TCA_RATE,
TCA_FCNT,
TCA_STATS2,
+ TCA_STAB,
__TCA_MAX
};
@@ -494,8 +496,7 @@ enum
* Neighbor Discovery userland options
****/
-struct nduseroptmsg
-{
+struct nduseroptmsg {
unsigned char nduseropt_family;
unsigned char nduseropt_pad1;
unsigned short nduseropt_opts_len; /* Total length of options */
@@ -507,8 +508,7 @@ struct nduseroptmsg
/* Followed by one or more ND options */
};
-enum
-{
+enum {
NDUSEROPT_UNSPEC,
NDUSEROPT_SRCADDR,
__NDUSEROPT_MAX
@@ -581,13 +581,16 @@ enum rtnetlink_groups {
#define RTNLGRP_IPV6_RULE RTNLGRP_IPV6_RULE
RTNLGRP_ND_USEROPT,
#define RTNLGRP_ND_USEROPT RTNLGRP_ND_USEROPT
+ RTNLGRP_PHONET_IFADDR,
+#define RTNLGRP_PHONET_IFADDR RTNLGRP_PHONET_IFADDR
+ RTNLGRP_PHONET_ROUTE,
+#define RTNLGRP_PHONET_ROUTE RTNLGRP_PHONET_ROUTE
__RTNLGRP_MAX
};
#define RTNLGRP_MAX (__RTNLGRP_MAX - 1)
/* TC action piece */
-struct tcamsg
-{
+struct tcamsg {
unsigned char tca_family;
unsigned char tca__pad1;
unsigned short tca__pad2;
diff --git a/include/linux/snmp.h b/include/linux/snmp.h
new file mode 100644
index 00000000..1bdb4a39
--- /dev/null
+++ b/include/linux/snmp.h
@@ -0,0 +1,299 @@
+/*
+ * Definitions for MIBs
+ *
+ * Author: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
+ */
+
+#ifndef _LINUX_SNMP_H
+#define _LINUX_SNMP_H
+
+/* ipstats mib definitions */
+/*
+ * RFC 1213: MIB-II
+ * RFC 2011 (updates 1213): SNMPv2-MIB-IP
+ * RFC 2863: Interfaces Group MIB
+ * RFC 2465: IPv6 MIB: General Group
+ * draft-ietf-ipv6-rfc2011-update-10.txt: MIB for IP: IP Statistics Tables
+ */
+enum
+{
+ IPSTATS_MIB_NUM = 0,
+/* frequently written fields in fast path, kept in same cache line */
+ IPSTATS_MIB_INPKTS, /* InReceives */
+ IPSTATS_MIB_INOCTETS, /* InOctets */
+ IPSTATS_MIB_INDELIVERS, /* InDelivers */
+ IPSTATS_MIB_OUTFORWDATAGRAMS, /* OutForwDatagrams */
+ IPSTATS_MIB_OUTPKTS, /* OutRequests */
+ IPSTATS_MIB_OUTOCTETS, /* OutOctets */
+/* other fields */
+ IPSTATS_MIB_INHDRERRORS, /* InHdrErrors */
+ IPSTATS_MIB_INTOOBIGERRORS, /* InTooBigErrors */
+ IPSTATS_MIB_INNOROUTES, /* InNoRoutes */
+ IPSTATS_MIB_INADDRERRORS, /* InAddrErrors */
+ IPSTATS_MIB_INUNKNOWNPROTOS, /* InUnknownProtos */
+ IPSTATS_MIB_INTRUNCATEDPKTS, /* InTruncatedPkts */
+ IPSTATS_MIB_INDISCARDS, /* InDiscards */
+ IPSTATS_MIB_OUTDISCARDS, /* OutDiscards */
+ IPSTATS_MIB_OUTNOROUTES, /* OutNoRoutes */
+ IPSTATS_MIB_REASMTIMEOUT, /* ReasmTimeout */
+ IPSTATS_MIB_REASMREQDS, /* ReasmReqds */
+ IPSTATS_MIB_REASMOKS, /* ReasmOKs */
+ IPSTATS_MIB_REASMFAILS, /* ReasmFails */
+ IPSTATS_MIB_FRAGOKS, /* FragOKs */
+ IPSTATS_MIB_FRAGFAILS, /* FragFails */
+ IPSTATS_MIB_FRAGCREATES, /* FragCreates */
+ IPSTATS_MIB_INMCASTPKTS, /* InMcastPkts */
+ IPSTATS_MIB_OUTMCASTPKTS, /* OutMcastPkts */
+ IPSTATS_MIB_INBCASTPKTS, /* InBcastPkts */
+ IPSTATS_MIB_OUTBCASTPKTS, /* OutBcastPkts */
+ IPSTATS_MIB_INMCASTOCTETS, /* InMcastOctets */
+ IPSTATS_MIB_OUTMCASTOCTETS, /* OutMcastOctets */
+ IPSTATS_MIB_INBCASTOCTETS, /* InBcastOctets */
+ IPSTATS_MIB_OUTBCASTOCTETS, /* OutBcastOctets */
+ IPSTATS_MIB_CSUMERRORS, /* InCsumErrors */
+ IPSTATS_MIB_NOECTPKTS, /* InNoECTPkts */
+ IPSTATS_MIB_ECT1PKTS, /* InECT1Pkts */
+ IPSTATS_MIB_ECT0PKTS, /* InECT0Pkts */
+ IPSTATS_MIB_CEPKTS, /* InCEPkts */
+ __IPSTATS_MIB_MAX
+};
+
+/* icmp mib definitions */
+/*
+ * RFC 1213: MIB-II ICMP Group
+ * RFC 2011 (updates 1213): SNMPv2 MIB for IP: ICMP group
+ */
+enum
+{
+ ICMP_MIB_NUM = 0,
+ ICMP_MIB_INMSGS, /* InMsgs */
+ ICMP_MIB_INERRORS, /* InErrors */
+ ICMP_MIB_INDESTUNREACHS, /* InDestUnreachs */
+ ICMP_MIB_INTIMEEXCDS, /* InTimeExcds */
+ ICMP_MIB_INPARMPROBS, /* InParmProbs */
+ ICMP_MIB_INSRCQUENCHS, /* InSrcQuenchs */
+ ICMP_MIB_INREDIRECTS, /* InRedirects */
+ ICMP_MIB_INECHOS, /* InEchos */
+ ICMP_MIB_INECHOREPS, /* InEchoReps */
+ ICMP_MIB_INTIMESTAMPS, /* InTimestamps */
+ ICMP_MIB_INTIMESTAMPREPS, /* InTimestampReps */
+ ICMP_MIB_INADDRMASKS, /* InAddrMasks */
+ ICMP_MIB_INADDRMASKREPS, /* InAddrMaskReps */
+ ICMP_MIB_OUTMSGS, /* OutMsgs */
+ ICMP_MIB_OUTERRORS, /* OutErrors */
+ ICMP_MIB_OUTDESTUNREACHS, /* OutDestUnreachs */
+ ICMP_MIB_OUTTIMEEXCDS, /* OutTimeExcds */
+ ICMP_MIB_OUTPARMPROBS, /* OutParmProbs */
+ ICMP_MIB_OUTSRCQUENCHS, /* OutSrcQuenchs */
+ ICMP_MIB_OUTREDIRECTS, /* OutRedirects */
+ ICMP_MIB_OUTECHOS, /* OutEchos */
+ ICMP_MIB_OUTECHOREPS, /* OutEchoReps */
+ ICMP_MIB_OUTTIMESTAMPS, /* OutTimestamps */
+ ICMP_MIB_OUTTIMESTAMPREPS, /* OutTimestampReps */
+ ICMP_MIB_OUTADDRMASKS, /* OutAddrMasks */
+ ICMP_MIB_OUTADDRMASKREPS, /* OutAddrMaskReps */
+ ICMP_MIB_CSUMERRORS, /* InCsumErrors */
+ __ICMP_MIB_MAX
+};
+
+#define __ICMPMSG_MIB_MAX 512 /* Out+In for all 8-bit ICMP types */
+
+/* icmp6 mib definitions */
+/*
+ * RFC 2466: ICMPv6-MIB
+ */
+enum
+{
+ ICMP6_MIB_NUM = 0,
+ ICMP6_MIB_INMSGS, /* InMsgs */
+ ICMP6_MIB_INERRORS, /* InErrors */
+ ICMP6_MIB_OUTMSGS, /* OutMsgs */
+ ICMP6_MIB_OUTERRORS, /* OutErrors */
+ ICMP6_MIB_CSUMERRORS, /* InCsumErrors */
+ __ICMP6_MIB_MAX
+};
+
+#define __ICMP6MSG_MIB_MAX 512 /* Out+In for all 8-bit ICMPv6 types */
+
+/* tcp mib definitions */
+/*
+ * RFC 1213: MIB-II TCP group
+ * RFC 2012 (updates 1213): SNMPv2-MIB-TCP
+ */
+enum
+{
+ TCP_MIB_NUM = 0,
+ TCP_MIB_RTOALGORITHM, /* RtoAlgorithm */
+ TCP_MIB_RTOMIN, /* RtoMin */
+ TCP_MIB_RTOMAX, /* RtoMax */
+ TCP_MIB_MAXCONN, /* MaxConn */
+ TCP_MIB_ACTIVEOPENS, /* ActiveOpens */
+ TCP_MIB_PASSIVEOPENS, /* PassiveOpens */
+ TCP_MIB_ATTEMPTFAILS, /* AttemptFails */
+ TCP_MIB_ESTABRESETS, /* EstabResets */
+ TCP_MIB_CURRESTAB, /* CurrEstab */
+ TCP_MIB_INSEGS, /* InSegs */
+ TCP_MIB_OUTSEGS, /* OutSegs */
+ TCP_MIB_RETRANSSEGS, /* RetransSegs */
+ TCP_MIB_INERRS, /* InErrs */
+ TCP_MIB_OUTRSTS, /* OutRsts */
+ TCP_MIB_CSUMERRORS, /* InCsumErrors */
+ __TCP_MIB_MAX
+};
+
+/* udp mib definitions */
+/*
+ * RFC 1213: MIB-II UDP group
+ * RFC 2013 (updates 1213): SNMPv2-MIB-UDP
+ */
+enum
+{
+ UDP_MIB_NUM = 0,
+ UDP_MIB_INDATAGRAMS, /* InDatagrams */
+ UDP_MIB_NOPORTS, /* NoPorts */
+ UDP_MIB_INERRORS, /* InErrors */
+ UDP_MIB_OUTDATAGRAMS, /* OutDatagrams */
+ UDP_MIB_RCVBUFERRORS, /* RcvbufErrors */
+ UDP_MIB_SNDBUFERRORS, /* SndbufErrors */
+ UDP_MIB_CSUMERRORS, /* InCsumErrors */
+ __UDP_MIB_MAX
+};
+
+/* linux mib definitions */
+enum
+{
+ LINUX_MIB_NUM = 0,
+ LINUX_MIB_SYNCOOKIESSENT, /* SyncookiesSent */
+ LINUX_MIB_SYNCOOKIESRECV, /* SyncookiesRecv */
+ LINUX_MIB_SYNCOOKIESFAILED, /* SyncookiesFailed */
+ LINUX_MIB_EMBRYONICRSTS, /* EmbryonicRsts */
+ LINUX_MIB_PRUNECALLED, /* PruneCalled */
+ LINUX_MIB_RCVPRUNED, /* RcvPruned */
+ LINUX_MIB_OFOPRUNED, /* OfoPruned */
+ LINUX_MIB_OUTOFWINDOWICMPS, /* OutOfWindowIcmps */
+ LINUX_MIB_LOCKDROPPEDICMPS, /* LockDroppedIcmps */
+ LINUX_MIB_ARPFILTER, /* ArpFilter */
+ LINUX_MIB_TIMEWAITED, /* TimeWaited */
+ LINUX_MIB_TIMEWAITRECYCLED, /* TimeWaitRecycled */
+ LINUX_MIB_TIMEWAITKILLED, /* TimeWaitKilled */
+ LINUX_MIB_PAWSPASSIVEREJECTED, /* PAWSPassiveRejected */
+ LINUX_MIB_PAWSACTIVEREJECTED, /* PAWSActiveRejected */
+ LINUX_MIB_PAWSESTABREJECTED, /* PAWSEstabRejected */
+ LINUX_MIB_DELAYEDACKS, /* DelayedACKs */
+ LINUX_MIB_DELAYEDACKLOCKED, /* DelayedACKLocked */
+ LINUX_MIB_DELAYEDACKLOST, /* DelayedACKLost */
+ LINUX_MIB_LISTENOVERFLOWS, /* ListenOverflows */
+ LINUX_MIB_LISTENDROPS, /* ListenDrops */
+ LINUX_MIB_TCPPREQUEUED, /* TCPPrequeued */
+ LINUX_MIB_TCPDIRECTCOPYFROMBACKLOG, /* TCPDirectCopyFromBacklog */
+ LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE, /* TCPDirectCopyFromPrequeue */
+ LINUX_MIB_TCPPREQUEUEDROPPED, /* TCPPrequeueDropped */
+ LINUX_MIB_TCPHPHITS, /* TCPHPHits */
+ LINUX_MIB_TCPHPHITSTOUSER, /* TCPHPHitsToUser */
+ LINUX_MIB_TCPPUREACKS, /* TCPPureAcks */
+ LINUX_MIB_TCPHPACKS, /* TCPHPAcks */
+ LINUX_MIB_TCPRENORECOVERY, /* TCPRenoRecovery */
+ LINUX_MIB_TCPSACKRECOVERY, /* TCPSackRecovery */
+ LINUX_MIB_TCPSACKRENEGING, /* TCPSACKReneging */
+ LINUX_MIB_TCPFACKREORDER, /* TCPFACKReorder */
+ LINUX_MIB_TCPSACKREORDER, /* TCPSACKReorder */
+ LINUX_MIB_TCPRENOREORDER, /* TCPRenoReorder */
+ LINUX_MIB_TCPTSREORDER, /* TCPTSReorder */
+ LINUX_MIB_TCPFULLUNDO, /* TCPFullUndo */
+ LINUX_MIB_TCPPARTIALUNDO, /* TCPPartialUndo */
+ LINUX_MIB_TCPDSACKUNDO, /* TCPDSACKUndo */
+ LINUX_MIB_TCPLOSSUNDO, /* TCPLossUndo */
+ LINUX_MIB_TCPLOSTRETRANSMIT, /* TCPLostRetransmit */
+ LINUX_MIB_TCPRENOFAILURES, /* TCPRenoFailures */
+ LINUX_MIB_TCPSACKFAILURES, /* TCPSackFailures */
+ LINUX_MIB_TCPLOSSFAILURES, /* TCPLossFailures */
+ LINUX_MIB_TCPFASTRETRANS, /* TCPFastRetrans */
+ LINUX_MIB_TCPFORWARDRETRANS, /* TCPForwardRetrans */
+ LINUX_MIB_TCPSLOWSTARTRETRANS, /* TCPSlowStartRetrans */
+ LINUX_MIB_TCPTIMEOUTS, /* TCPTimeouts */
+ LINUX_MIB_TCPLOSSPROBES, /* TCPLossProbes */
+ LINUX_MIB_TCPLOSSPROBERECOVERY, /* TCPLossProbeRecovery */
+ LINUX_MIB_TCPRENORECOVERYFAIL, /* TCPRenoRecoveryFail */
+ LINUX_MIB_TCPSACKRECOVERYFAIL, /* TCPSackRecoveryFail */
+ LINUX_MIB_TCPSCHEDULERFAILED, /* TCPSchedulerFailed */
+ LINUX_MIB_TCPRCVCOLLAPSED, /* TCPRcvCollapsed */
+ LINUX_MIB_TCPDSACKOLDSENT, /* TCPDSACKOldSent */
+ LINUX_MIB_TCPDSACKOFOSENT, /* TCPDSACKOfoSent */
+ LINUX_MIB_TCPDSACKRECV, /* TCPDSACKRecv */
+ LINUX_MIB_TCPDSACKOFORECV, /* TCPDSACKOfoRecv */
+ LINUX_MIB_TCPABORTONDATA, /* TCPAbortOnData */
+ LINUX_MIB_TCPABORTONCLOSE, /* TCPAbortOnClose */
+ LINUX_MIB_TCPABORTONMEMORY, /* TCPAbortOnMemory */
+ LINUX_MIB_TCPABORTONTIMEOUT, /* TCPAbortOnTimeout */
+ LINUX_MIB_TCPABORTONLINGER, /* TCPAbortOnLinger */
+ LINUX_MIB_TCPABORTFAILED, /* TCPAbortFailed */
+ LINUX_MIB_TCPMEMORYPRESSURES, /* TCPMemoryPressures */
+ LINUX_MIB_TCPSACKDISCARD, /* TCPSACKDiscard */
+ LINUX_MIB_TCPDSACKIGNOREDOLD, /* TCPSACKIgnoredOld */
+ LINUX_MIB_TCPDSACKIGNOREDNOUNDO, /* TCPSACKIgnoredNoUndo */
+ LINUX_MIB_TCPSPURIOUSRTOS, /* TCPSpuriousRTOs */
+ LINUX_MIB_TCPMD5NOTFOUND, /* TCPMD5NotFound */
+ LINUX_MIB_TCPMD5UNEXPECTED, /* TCPMD5Unexpected */
+ LINUX_MIB_SACKSHIFTED,
+ LINUX_MIB_SACKMERGED,
+ LINUX_MIB_SACKSHIFTFALLBACK,
+ LINUX_MIB_TCPBACKLOGDROP,
+ LINUX_MIB_TCPMINTTLDROP, /* RFC 5082 */
+ LINUX_MIB_TCPDEFERACCEPTDROP,
+ LINUX_MIB_IPRPFILTER, /* IP Reverse Path Filter (rp_filter) */
+ LINUX_MIB_TCPTIMEWAITOVERFLOW, /* TCPTimeWaitOverflow */
+ LINUX_MIB_TCPREQQFULLDOCOOKIES, /* TCPReqQFullDoCookies */
+ LINUX_MIB_TCPREQQFULLDROP, /* TCPReqQFullDrop */
+ LINUX_MIB_TCPRETRANSFAIL, /* TCPRetransFail */
+ LINUX_MIB_TCPRCVCOALESCE, /* TCPRcvCoalesce */
+ LINUX_MIB_TCPOFOQUEUE, /* TCPOFOQueue */
+ LINUX_MIB_TCPOFODROP, /* TCPOFODrop */
+ LINUX_MIB_TCPOFOMERGE, /* TCPOFOMerge */
+ LINUX_MIB_TCPCHALLENGEACK, /* TCPChallengeACK */
+ LINUX_MIB_TCPSYNCHALLENGE, /* TCPSYNChallenge */
+ LINUX_MIB_TCPFASTOPENACTIVE, /* TCPFastOpenActive */
+ LINUX_MIB_TCPFASTOPENPASSIVE, /* TCPFastOpenPassive*/
+ LINUX_MIB_TCPFASTOPENPASSIVEFAIL, /* TCPFastOpenPassiveFail */
+ LINUX_MIB_TCPFASTOPENLISTENOVERFLOW, /* TCPFastOpenListenOverflow */
+ LINUX_MIB_TCPFASTOPENCOOKIEREQD, /* TCPFastOpenCookieReqd */
+ LINUX_MIB_TCPSPURIOUS_RTX_HOSTQUEUES, /* TCPSpuriousRtxHostQueues */
+ LINUX_MIB_BUSYPOLLRXPACKETS, /* BusyPollRxPackets */
+ __LINUX_MIB_MAX
+};
+
+/* linux Xfrm mib definitions */
+enum
+{
+ LINUX_MIB_XFRMNUM = 0,
+ LINUX_MIB_XFRMINERROR, /* XfrmInError */
+ LINUX_MIB_XFRMINBUFFERERROR, /* XfrmInBufferError */
+ LINUX_MIB_XFRMINHDRERROR, /* XfrmInHdrError */
+ LINUX_MIB_XFRMINNOSTATES, /* XfrmInNoStates */
+ LINUX_MIB_XFRMINSTATEPROTOERROR, /* XfrmInStateProtoError */
+ LINUX_MIB_XFRMINSTATEMODEERROR, /* XfrmInStateModeError */
+ LINUX_MIB_XFRMINSTATESEQERROR, /* XfrmInStateSeqError */
+ LINUX_MIB_XFRMINSTATEEXPIRED, /* XfrmInStateExpired */
+ LINUX_MIB_XFRMINSTATEMISMATCH, /* XfrmInStateMismatch */
+ LINUX_MIB_XFRMINSTATEINVALID, /* XfrmInStateInvalid */
+ LINUX_MIB_XFRMINTMPLMISMATCH, /* XfrmInTmplMismatch */
+ LINUX_MIB_XFRMINNOPOLS, /* XfrmInNoPols */
+ LINUX_MIB_XFRMINPOLBLOCK, /* XfrmInPolBlock */
+ LINUX_MIB_XFRMINPOLERROR, /* XfrmInPolError */
+ LINUX_MIB_XFRMOUTERROR, /* XfrmOutError */
+ LINUX_MIB_XFRMOUTBUNDLEGENERROR, /* XfrmOutBundleGenError */
+ LINUX_MIB_XFRMOUTBUNDLECHECKERROR, /* XfrmOutBundleCheckError */
+ LINUX_MIB_XFRMOUTNOSTATES, /* XfrmOutNoStates */
+ LINUX_MIB_XFRMOUTSTATEPROTOERROR, /* XfrmOutStateProtoError */
+ LINUX_MIB_XFRMOUTSTATEMODEERROR, /* XfrmOutStateModeError */
+ LINUX_MIB_XFRMOUTSTATESEQERROR, /* XfrmOutStateSeqError */
+ LINUX_MIB_XFRMOUTSTATEEXPIRED, /* XfrmOutStateExpired */
+ LINUX_MIB_XFRMOUTPOLBLOCK, /* XfrmOutPolBlock */
+ LINUX_MIB_XFRMOUTPOLDEAD, /* XfrmOutPolDead */
+ LINUX_MIB_XFRMOUTPOLERROR, /* XfrmOutPolError */
+ LINUX_MIB_XFRMFWDHDRERROR, /* XfrmFwdHdrError*/
+ LINUX_MIB_XFRMOUTSTATEINVALID, /* XfrmOutStateInvalid */
+ LINUX_MIB_XFRMACQUIREERROR, /* XfrmAcquireError */
+ __LINUX_MIB_XFRMMAX
+};
+
+#endif /* _LINUX_SNMP_H */
diff --git a/include/linux/tc_act/tc_mirred.h b/include/linux/tc_act/tc_mirred.h
new file mode 100644
index 00000000..7561750e
--- /dev/null
+++ b/include/linux/tc_act/tc_mirred.h
@@ -0,0 +1,27 @@
+#ifndef __LINUX_TC_MIR_H
+#define __LINUX_TC_MIR_H
+
+#include <linux/types.h>
+#include <linux/pkt_cls.h>
+
+#define TCA_ACT_MIRRED 8
+#define TCA_EGRESS_REDIR 1 /* packet redirect to EGRESS*/
+#define TCA_EGRESS_MIRROR 2 /* mirror packet to EGRESS */
+#define TCA_INGRESS_REDIR 3 /* packet redirect to INGRESS*/
+#define TCA_INGRESS_MIRROR 4 /* mirror packet to INGRESS */
+
+struct tc_mirred {
+ tc_gen;
+ int eaction; /* one of IN/EGRESS_MIRROR/REDIR */
+ __u32 ifindex; /* ifindex of egress port */
+};
+
+enum {
+ TCA_MIRRED_UNSPEC,
+ TCA_MIRRED_TM,
+ TCA_MIRRED_PARMS,
+ __TCA_MIRRED_MAX
+};
+#define TCA_MIRRED_MAX (__TCA_MIRRED_MAX - 1)
+
+#endif
diff --git a/include/linux/tc_ematch/tc_em_meta.h b/include/linux/tc_ematch/tc_em_meta.h
new file mode 100644
index 00000000..fe815e26
--- /dev/null
+++ b/include/linux/tc_ematch/tc_em_meta.h
@@ -0,0 +1,89 @@
+#ifndef __LINUX_TC_EM_META_H
+#define __LINUX_TC_EM_META_H
+
+enum {
+ TCA_EM_META_UNSPEC,
+ TCA_EM_META_HDR,
+ TCA_EM_META_LVALUE,
+ TCA_EM_META_RVALUE,
+ __TCA_EM_META_MAX
+};
+#define TCA_EM_META_MAX (__TCA_EM_META_MAX - 1)
+
+struct tcf_meta_val {
+ __u16 kind;
+ __u8 shift;
+ __u8 op;
+};
+
+#define TCF_META_TYPE_MASK (0xf << 12)
+#define TCF_META_TYPE(kind) (((kind) & TCF_META_TYPE_MASK) >> 12)
+#define TCF_META_ID_MASK 0x7ff
+#define TCF_META_ID(kind) ((kind) & TCF_META_ID_MASK)
+
+enum {
+ TCF_META_TYPE_VAR,
+ TCF_META_TYPE_INT,
+ __TCF_META_TYPE_MAX
+};
+#define TCF_META_TYPE_MAX (__TCF_META_TYPE_MAX - 1)
+
+enum {
+ TCF_META_ID_VALUE,
+ TCF_META_ID_RANDOM,
+ TCF_META_ID_LOADAVG_0,
+ TCF_META_ID_LOADAVG_1,
+ TCF_META_ID_LOADAVG_2,
+ TCF_META_ID_DEV,
+ TCF_META_ID_PRIORITY,
+ TCF_META_ID_PROTOCOL,
+ TCF_META_ID_PKTTYPE,
+ TCF_META_ID_PKTLEN,
+ TCF_META_ID_DATALEN,
+ TCF_META_ID_MACLEN,
+ TCF_META_ID_NFMARK,
+ TCF_META_ID_TCINDEX,
+ TCF_META_ID_RTCLASSID,
+ TCF_META_ID_RTIIF,
+ TCF_META_ID_SK_FAMILY,
+ TCF_META_ID_SK_STATE,
+ TCF_META_ID_SK_REUSE,
+ TCF_META_ID_SK_BOUND_IF,
+ TCF_META_ID_SK_REFCNT,
+ TCF_META_ID_SK_SHUTDOWN,
+ TCF_META_ID_SK_PROTO,
+ TCF_META_ID_SK_TYPE,
+ TCF_META_ID_SK_RCVBUF,
+ TCF_META_ID_SK_RMEM_ALLOC,
+ TCF_META_ID_SK_WMEM_ALLOC,
+ TCF_META_ID_SK_OMEM_ALLOC,
+ TCF_META_ID_SK_WMEM_QUEUED,
+ TCF_META_ID_SK_RCV_QLEN,
+ TCF_META_ID_SK_SND_QLEN,
+ TCF_META_ID_SK_ERR_QLEN,
+ TCF_META_ID_SK_FORWARD_ALLOCS,
+ TCF_META_ID_SK_SNDBUF,
+ TCF_META_ID_SK_ALLOCS,
+ TCF_META_ID_SK_ROUTE_CAPS,
+ TCF_META_ID_SK_HASH,
+ TCF_META_ID_SK_LINGERTIME,
+ TCF_META_ID_SK_ACK_BACKLOG,
+ TCF_META_ID_SK_MAX_ACK_BACKLOG,
+ TCF_META_ID_SK_PRIO,
+ TCF_META_ID_SK_RCVLOWAT,
+ TCF_META_ID_SK_RCVTIMEO,
+ TCF_META_ID_SK_SNDTIMEO,
+ TCF_META_ID_SK_SENDMSG_OFF,
+ TCF_META_ID_SK_WRITE_PENDING,
+ TCF_META_ID_VLAN_TAG,
+ TCF_META_ID_RXHASH,
+ __TCF_META_ID_MAX
+};
+#define TCF_META_ID_MAX (__TCF_META_ID_MAX - 1)
+
+struct tcf_meta_hdr {
+ struct tcf_meta_val left;
+ struct tcf_meta_val right;
+};
+
+#endif
diff --git a/include/netlink-local.h b/include/netlink-local.h
deleted file mode 100644
index 7e331190..00000000
--- a/include/netlink-local.h
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * 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-2008 Thomas Graf <tgraf@suug.ch>
- */
-
-#ifndef NETLINK_LOCAL_H_
-#define NETLINK_LOCAL_H_
-
-#include <stdio.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <math.h>
-#include <time.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-#include <inttypes.h>
-#include <assert.h>
-#include <limits.h>
-
-#include <arpa/inet.h>
-#include <netdb.h>
-
-#ifndef SOL_NETLINK
-#define SOL_NETLINK 270
-#endif
-
-#include <linux/types.h>
-
-/* local header copies */
-#include <linux/if.h>
-#include <linux/if_arp.h>
-#include <linux/if_ether.h>
-#include <linux/pkt_sched.h>
-#include <linux/pkt_cls.h>
-#include <linux/gen_stats.h>
-#include <linux/ip_mp_alg.h>
-
-#include <netlink/netlink.h>
-#include <netlink/handlers.h>
-#include <netlink/cache.h>
-#include <netlink/route/tc.h>
-#include <netlink/object-api.h>
-#include <netlink/cache-api.h>
-#include <netlink-types.h>
-
-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)
-
-extern int __nl_read_num_str_file(const char *path,
- int (*cb)(long, const char *));
-
-extern int __trans_list_add(int, const char *, struct nl_list_head *);
-extern void __trans_list_clear(struct nl_list_head *);
-
-extern char *__type2str(int, char *, size_t, struct trans_tbl *, size_t);
-extern int __str2type(const char *, struct trans_tbl *, size_t);
-
-extern char *__list_type2str(int, char *, size_t, struct nl_list_head *);
-extern int __list_str2type(const char *, struct nl_list_head *);
-
-extern char *__flags2str(int, char *, size_t, struct trans_tbl *, size_t);
-extern int __str2flags(const char *, struct trans_tbl *, size_t);
-
-extern void dump_from_ops(struct nl_object *, struct nl_dump_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 __init __attribute__ ((constructor))
-#define __exit __attribute__ ((destructor))
-#undef __deprecated
-#define __deprecated __attribute__ ((deprecated))
-
-#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, \
- }
-
-static inline int wait_for_ack(struct nl_sock *sk)
-{
- if (sk->s_flags & NL_NO_AUTO_ACK)
- return 0;
- else
- return nl_wait_for_ack(sk);
-}
-
-#endif
diff --git a/include/netlink-private/cache-api.h b/include/netlink-private/cache-api.h
new file mode 100644
index 00000000..f3d9f01a
--- /dev/null
+++ b/include/netlink-private/cache-api.h
@@ -0,0 +1,270 @@
+/*
+ * netlink-private/cache-api.h Caching 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-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_CACHE_API_H_
+#define NETLINK_CACHE_API_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @ingroup cache
+ * @defgroup cache_api Cache Implementation
+ * @brief
+ *
+ * @par 1) Cache Definition
+ * @code
+ * struct nl_cache_ops my_cache_ops = {
+ * .co_name = "route/link",
+ * .co_protocol = NETLINK_ROUTE,
+ * .co_hdrsize = sizeof(struct ifinfomsg),
+ * .co_obj_ops = &my_obj_ops,
+ * };
+ * @endcode
+ *
+ * @par 2)
+ * @code
+ * // The simplest way to fill a cache is by providing a request-update
+ * // function which must trigger a complete dump on the kernel-side of
+ * // whatever the cache covers.
+ * static int my_request_update(struct nl_cache *cache,
+ * struct nl_sock *socket)
+ * {
+ * // In this example, we request a full dump of the interface table
+ * return nl_rtgen_request(socket, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP);
+ * }
+ *
+ * // The resulting netlink messages sent back will be fed into a message
+ * // parser one at a time. The message parser has to extract all relevant
+ * // information from the message and create an object reflecting the
+ * // contents of the message and pass it on to the parser callback function
+ * // provide which will add the object to the cache.
+ * static int my_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+ * struct nlmsghdr *nlh, struct nl_parser_param *pp)
+ * {
+ * struct my_obj *obj;
+ *
+ * obj = my_obj_alloc();
+ * obj->ce_msgtype = nlh->nlmsg_type;
+ *
+ * // Parse the netlink message and continue creating the object.
+ *
+ * err = pp->pp_cb((struct nl_object *) obj, pp);
+ * if (err < 0)
+ * goto errout;
+ * }
+ *
+ * struct nl_cache_ops my_cache_ops = {
+ * ...
+ * .co_request_update = my_request_update,
+ * .co_msg_parser = my_msg_parser,
+ * };
+ * @endcode
+ *
+ * @par 3) Notification based Updates
+ * @code
+ * // Caches can be kept up-to-date based on notifications if the kernel
+ * // sends out notifications whenever an object is added/removed/changed.
+ * //
+ * // It is trivial to support this, first a list of groups needs to be
+ * // defined which are required to join in order to receive all necessary
+ * // notifications. The groups are separated by address family to support
+ * // the common situation where a separate group is used for each address
+ * // family. If there is only one group, simply specify AF_UNSPEC.
+ * static struct nl_af_group addr_groups[] = {
+ * { AF_INET, RTNLGRP_IPV4_IFADDR },
+ * { AF_INET6, RTNLGRP_IPV6_IFADDR },
+ * { END_OF_GROUP_LIST },
+ * };
+ *
+ * // In order for the caching system to know the meaning of each message
+ * // type it requires a table which maps each supported message type to
+ * // a cache action, e.g. RTM_NEWADDR means address has been added or
+ * // updated, RTM_DELADDR means address has been removed.
+ * static struct nl_cache_ops rtnl_addr_ops = {
+ * ...
+ * .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_groups = addr_groups,
+ * };
+ *
+ * // It is now possible to keep the cache up-to-date using the cache manager.
+ * @endcode
+ * @{
+ */
+
+#define END_OF_MSGTYPES_LIST { -1, -1, NULL }
+
+/**
+ * Message type to cache action association
+ */
+struct nl_msgtype
+{
+ /** Netlink message type */
+ int mt_id;
+
+ /** Cache action to take */
+ int mt_act;
+
+ /** Name of operation for human-readable printing */
+ char * mt_name;
+};
+
+/**
+ * Address family to netlink group association
+ */
+struct nl_af_group
+{
+ /** Address family */
+ int ag_family;
+
+ /** Netlink group identifier */
+ int ag_group;
+};
+
+#define END_OF_GROUP_LIST AF_UNSPEC, 0
+
+/**
+ * Parser parameters
+ *
+ * This structure is used to configure what kind of parser to use
+ * when parsing netlink messages to create objects.
+ */
+struct nl_parser_param
+{
+ /** Function to parse netlink messages into objects */
+ int (*pp_cb)(struct nl_object *, struct nl_parser_param *);
+
+ /** Arbitary argument to be passed to the parser */
+ void * pp_arg;
+};
+
+/**
+ * Cache Operations
+ *
+ * This structure defines the characterstics of a cache type. It contains
+ * pointers to functions which implement the specifics of the object type
+ * the cache can hold.
+ */
+struct nl_cache_ops
+{
+ /** Name of cache type (must be unique) */
+ char * co_name;
+
+ /** Size of family specific netlink header */
+ int co_hdrsize;
+
+ /** Netlink protocol */
+ int co_protocol;
+
+ /** cache object hash size **/
+ int co_hash_size;
+
+ /** cache flags */
+ unsigned int co_flags;
+
+ /** Reference counter */
+ unsigned int co_refcnt;
+
+ /** Group definition */
+ 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_sock *);
+
+ /**
+ * 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 *, struct nl_parser_param *);
+
+ /**
+ * The function registered under this callback is called after a
+ * netlink notification associated with this cache type has been
+ * parsed into an object and is being considered for inclusio into
+ * the specified cache.
+ *
+ * The purpose of this function is to filter out notifications
+ * which should be ignored when updating caches.
+ *
+ * The function must return NL_SKIP to prevent the object from
+ * being included, or NL_OK to include it.
+ *
+ * @code
+ * int my_filter(struct nl_cache *cache, struct nl_object *obj)
+ * {
+ * if (reason_to_not_include_obj(obj))
+ * return NL_SKIP;
+ *
+ * return NL_OK;
+ * }
+ * @endcode
+ */
+ int (*co_event_filter)(struct nl_cache *, struct nl_object *obj);
+
+ /**
+ * The function registered under this callback is called when an
+ * object formed from a notification event needs to be included in
+ * a cache.
+ *
+ * For each modified object, the change callback \c change_cb must
+ * be called with the \c data argument provided.
+ *
+ * If no function is registered, the function nl_cache_include()
+ * will be used for this purpose.
+ *
+ * @see nl_cache_include()
+ */
+ int (*co_include_event)(struct nl_cache *cache, struct nl_object *obj,
+ change_func_t change_cb, void *data);
+
+ void (*reserved_1)(void);
+ void (*reserved_2)(void);
+ void (*reserved_3)(void);
+ void (*reserved_4)(void);
+ void (*reserved_5)(void);
+ void (*reserved_6)(void);
+ void (*reserved_7)(void);
+ void (*reserved_8)(void);
+
+ /** Object operations */
+ struct nl_object_ops * co_obj_ops;
+
+ /** Internal, do not touch! */
+ struct nl_cache_ops *co_next;
+
+ struct nl_cache *co_major_cache;
+ struct genl_ops * co_genl;
+
+ /* Message type definition */
+ struct nl_msgtype co_msgtypes[];
+};
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink-generic.h b/include/netlink-private/genl.h
index 10aa2f01..5b93db3c 100644
--- a/include/netlink-generic.h
+++ b/include/netlink-private/genl.h
@@ -1,20 +1,22 @@
/*
- * netlink-generic.h Local Generic Netlink Interface
+ * netlink-private/genl.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 <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_GENL_PRIV_H_
#define NETLINK_GENL_PRIV_H_
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#define GENL_HDRSIZE(hdrlen) (GENL_HDRLEN + (hdrlen))
+extern int genl_resolve_id(struct genl_ops *ops);
+
#endif
diff --git a/include/netlink-private/netlink.h b/include/netlink-private/netlink.h
new file mode 100644
index 00000000..e366d1e9
--- /dev/null
+++ b/include/netlink-private/netlink.h
@@ -0,0 +1,276 @@
+/*
+ * netlink-private/netlink.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-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_LOCAL_H_
+#define NETLINK_LOCAL_H_
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <math.h>
+#include <time.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <limits.h>
+#include <search.h>
+
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <defs.h>
+
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+#include <linux/types.h>
+
+/* local header copies */
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/ethtool.h>
+#include <linux/pkt_sched.h>
+#include <linux/pkt_cls.h>
+#include <linux/gen_stats.h>
+#include <linux/ip_mp_alg.h>
+#include <linux/atm.h>
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/snmp.h>
+
+#ifndef DISABLE_PTHREADS
+#include <pthread.h>
+#endif
+
+#include <netlink/netlink.h>
+#include <netlink/handlers.h>
+#include <netlink/cache.h>
+#include <netlink/route/tc.h>
+#include <netlink-private/object-api.h>
+#include <netlink-private/cache-api.h>
+#include <netlink-private/types.h>
+
+#define NSEC_PER_SEC 1000000000L
+
+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;
+};
+
+#ifdef NL_DEBUG
+#define NL_DBG(LVL,FMT,ARG...) \
+ do { \
+ if (LVL <= nl_debug) \
+ fprintf(stderr, \
+ "DBG<" #LVL ">%20s:%-4u %s: " FMT, \
+ __FILE__, __LINE__, \
+ __PRETTY_FUNCTION__, ##ARG); \
+ } while (0)
+#else /* NL_DEBUG */
+#define NL_DBG(LVL,FMT,ARG...) do { } while(0)
+#endif /* NL_DEBUG */
+
+#define BUG() \
+ do { \
+ fprintf(stderr, "BUG at file position %s:%d:%s\n", \
+ __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+ assert(0); \
+ } while (0)
+
+#define BUG_ON(condition) \
+ do { \
+ if (condition) \
+ BUG(); \
+ } while (0)
+
+
+#define APPBUG(msg) \
+ do { \
+ fprintf(stderr, "APPLICATION BUG: %s:%d:%s: %s\n", \
+ __FILE__, __LINE__, __PRETTY_FUNCTION__, msg); \
+ assert(0); \
+ } while(0)
+
+extern int __nl_read_num_str_file(const char *path,
+ int (*cb)(long, const char *));
+
+extern int __trans_list_add(int, const char *, struct nl_list_head *);
+extern void __trans_list_clear(struct nl_list_head *);
+
+extern char *__type2str(int, char *, size_t, const struct trans_tbl *, size_t);
+extern int __str2type(const char *, const struct trans_tbl *, size_t);
+
+extern char *__list_type2str(int, char *, size_t, struct nl_list_head *);
+extern int __list_str2type(const char *, struct nl_list_head *);
+
+extern char *__flags2str(int, char *, size_t, const struct trans_tbl *, size_t);
+extern int __str2flags(const char *, const struct trans_tbl *, size_t);
+
+extern void dump_from_ops(struct nl_object *, struct nl_dump_params *);
+
+static inline int nl_cb_call(struct nl_cb *cb, int type, struct nl_msg *msg)
+{
+ int ret;
+
+ cb->cb_active = type;
+ ret = cb->cb_set[type](msg, cb->cb_args[type]);
+ cb->cb_active = __NL_CB_TYPE_MAX;
+ return ret;
+}
+
+#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
+
+/* This is also defined in stddef.h */
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#define __init __attribute__ ((constructor))
+#define __exit __attribute__ ((destructor))
+#undef __deprecated
+#define __deprecated __attribute__ ((deprecated))
+
+#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_overhead = src->overhead;
+ dst->rs_cell_align = src->cell_align;
+ 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->overhead = src->rs_overhead;
+ dst->cell_align = src->rs_cell_align;
+ 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, \
+ }
+
+static inline int wait_for_ack(struct nl_sock *sk)
+{
+ if (sk->s_flags & NL_NO_AUTO_ACK)
+ return 0;
+ else
+ return nl_wait_for_ack(sk);
+}
+
+static inline int build_sysconf_path(char **strp, const char *filename)
+{
+ char *sysconfdir;
+
+ sysconfdir = getenv("NLSYSCONFDIR");
+
+ if (!sysconfdir)
+ sysconfdir = SYSCONFDIR;
+
+ return asprintf(strp, "%s/%s", sysconfdir, filename);
+}
+
+#ifndef DISABLE_PTHREADS
+#define NL_LOCK(NAME) pthread_mutex_t (NAME) = PTHREAD_MUTEX_INITIALIZER
+#define NL_RW_LOCK(NAME) pthread_rwlock_t (NAME) = PTHREAD_RWLOCK_INITIALIZER
+
+static inline void nl_lock(pthread_mutex_t *lock)
+{
+ pthread_mutex_lock(lock);
+}
+
+static inline void nl_unlock(pthread_mutex_t *lock)
+{
+ pthread_mutex_unlock(lock);
+}
+
+static inline void nl_read_lock(pthread_rwlock_t *lock)
+{
+ pthread_rwlock_rdlock(lock);
+}
+
+static inline void nl_read_unlock(pthread_rwlock_t *lock)
+{
+ pthread_rwlock_unlock(lock);
+}
+
+static inline void nl_write_lock(pthread_rwlock_t *lock)
+{
+ pthread_rwlock_wrlock(lock);
+}
+
+static inline void nl_write_unlock(pthread_rwlock_t *lock)
+{
+ pthread_rwlock_unlock(lock);
+}
+
+#else
+#define NL_LOCK(NAME) int __unused_lock_ ##NAME __attribute__((unused))
+#define NL_RW_LOCK(NAME) int __unused_lock_ ##NAME __attribute__((unused))
+
+#define nl_lock(LOCK) do { } while(0)
+#define nl_unlock(LOCK) do { } while(0)
+#define nl_read_lock(LOCK) do { } while(0)
+#define nl_read_unlock(LOCK) do { } while(0)
+#define nl_write_lock(LOCK) do { } while(0)
+#define nl_write_unlock(LOCK) do { } while(0)
+#endif
+
+#endif
diff --git a/include/netlink-private/object-api.h b/include/netlink-private/object-api.h
new file mode 100644
index 00000000..f4fd71e5
--- /dev/null
+++ b/include/netlink-private/object-api.h
@@ -0,0 +1,376 @@
+/*
+ * netlink-private/object-api.c Object 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-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_OBJECT_API_H_
+#define NETLINK_OBJECT_API_H_
+
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @ingroup object
+ * @defgroup object_api Object API
+ * @brief
+ *
+ * @par 1) Object Definition
+ * @code
+ * // Define your object starting with the common object header
+ * struct my_obj {
+ * NLHDR_COMMON
+ * int my_data;
+ * };
+ *
+ * // Fill out the object operations structure
+ * struct nl_object_ops my_ops = {
+ * .oo_name = "my_obj",
+ * .oo_size = sizeof(struct my_obj),
+ * };
+ *
+ * // At this point the object can be allocated, you may want to provide a
+ * // separate _alloc() function to ease allocting objects of this kind.
+ * struct nl_object *obj = nl_object_alloc(&my_ops);
+ *
+ * // And release it again...
+ * nl_object_put(obj);
+ * @endcode
+ *
+ * @par 2) Allocating additional data
+ * @code
+ * // You may require to allocate additional data and store it inside
+ * // object, f.e. assuming there is a field `ptr'.
+ * struct my_obj {
+ * NLHDR_COMMON
+ * void * ptr;
+ * };
+ *
+ * // And at some point you may assign allocated data to this field:
+ * my_obj->ptr = calloc(1, ...);
+ *
+ * // In order to not introduce any memory leaks you have to release
+ * // this data again when the last reference is given back.
+ * static void my_obj_free_data(struct nl_object *obj)
+ * {
+ * struct my_obj *my_obj = nl_object_priv(obj);
+ *
+ * free(my_obj->ptr);
+ * }
+ *
+ * // Also when the object is cloned, you must ensure for your pointer
+ * // stay valid even if one of the clones is freed by either making
+ * // a clone as well or increase the reference count.
+ * static int my_obj_clone(struct nl_object *src, struct nl_object *dst)
+ * {
+ * struct my_obj *my_src = nl_object_priv(src);
+ * struct my_obj *my_dst = nl_object_priv(dst);
+ *
+ * if (src->ptr) {
+ * dst->ptr = calloc(1, ...);
+ * memcpy(dst->ptr, src->ptr, ...);
+ * }
+ * }
+ *
+ * struct nl_object_ops my_ops = {
+ * ...
+ * .oo_free_data = my_obj_free_data,
+ * .oo_clone = my_obj_clone,
+ * };
+ * @endcode
+ *
+ * @par 3) Object Dumping
+ * @code
+ * static int my_obj_dump_detailed(struct nl_object *obj,
+ * struct nl_dump_params *params)
+ * {
+ * struct my_obj *my_obj = nl_object_priv(obj);
+ *
+ * // It is absolutely essential to use nl_dump() when printing
+ * // any text to make sure the dumping parameters are respected.
+ * nl_dump(params, "Obj Integer: %d\n", my_obj->my_int);
+ *
+ * // Before we can dump the next line, make sure to prefix
+ * // this line correctly.
+ * nl_new_line(params);
+ *
+ * // You may also split a line into multiple nl_dump() calls.
+ * nl_dump(params, "String: %s ", my_obj->my_string);
+ * nl_dump(params, "String-2: %s\n", my_obj->another_string);
+ * }
+ *
+ * struct nl_object_ops my_ops = {
+ * ...
+ * .oo_dump[NL_DUMP_FULL] = my_obj_dump_detailed,
+ * };
+ * @endcode
+ *
+ * @par 4) Object Attributes
+ * @code
+ * // The concept of object attributes is optional but can ease the typical
+ * // case of objects that have optional attributes, e.g. a route may have a
+ * // nexthop assigned but it is not required to.
+ *
+ * // The first step to define your object specific bitmask listing all
+ * // attributes
+ * #define MY_ATTR_FOO (1<<0)
+ * #define MY_ATTR_BAR (1<<1)
+ *
+ * // When assigning an optional attribute to the object, make sure
+ * // to mark its availability.
+ * my_obj->foo = 123123;
+ * my_obj->ce_mask |= MY_ATTR_FOO;
+ *
+ * // At any time you may use this mask to check for the availability
+ * // of the attribute, e.g. while dumping
+ * if (my_obj->ce_mask & MY_ATTR_FOO)
+ * nl_dump(params, "foo %d ", my_obj->foo);
+ *
+ * // One of the big advantages of this concept is that it allows for
+ * // standardized comparisons which make it trivial for caches to
+ * // identify unique objects by use of unified comparison functions.
+ * // In order for it to work, your object implementation must provide
+ * // a comparison function and define a list of attributes which
+ * // combined together make an object unique.
+ *
+ * static int my_obj_compare(struct nl_object *_a, struct nl_object *_b,
+ * uint32_t attrs, int flags)
+ * {
+ * struct my_obj *a = nl_object_priv(_a):
+ * struct my_obj *b = nl_object_priv(_b):
+ * int diff = 0;
+ *
+ * // We help ourselves in defining our own DIFF macro which will
+ * // call ATTR_DIFF() on both objects which will make sure to only
+ * // compare the attributes if required.
+ * #define MY_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, MY_ATTR_##ATTR, a, b, EXPR)
+ *
+ * // Call our own diff macro for each attribute to build a bitmask
+ * // representing the attributes which mismatch.
+ * diff |= MY_DIFF(FOO, a->foo != b->foo)
+ * diff |= MY_DIFF(BAR, strcmp(a->bar, b->bar))
+ *
+ * return diff;
+ * }
+ *
+ * // In order to identify identical objects with differing attributes
+ * // you must specify the attributes required to uniquely identify
+ * // your object. Make sure to not include too many attributes, this
+ * // list is used when caches look for an old version of an object.
+ * struct nl_object_ops my_ops = {
+ * ...
+ * .oo_id_attrs = MY_ATTR_FOO,
+ * .oo_compare = my_obj_compare,
+ * };
+ * @endcode
+ * @{
+ */
+
+/**
+ * Common Object Header
+ *
+ * This macro must be included as first member in every object
+ * definition to allow objects to be cached.
+ */
+#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
+};
+
+
+/**
+ * Return true if attribute is available in both objects
+ * @arg A an object
+ * @arg B another object
+ * @arg ATTR attribute bit
+ *
+ * @return True if the attribute is available, otherwise false is returned.
+ */
+#define AVAILABLE(A, B, ATTR) (((A)->ce_mask & (B)->ce_mask) & (ATTR))
+
+/**
+ * Return true if attribute is available in only one of both objects
+ * @arg A an object
+ * @arg B another object
+ * @arg ATTR attribute bit
+ *
+ * @return True if the attribute is available in only one of both objects,
+ * otherwise false is returned.
+ */
+#define AVAILABLE_MISMATCH(A, B, ATTR) (((A)->ce_mask ^ (B)->ce_mask) & (ATTR))
+
+/**
+ * Return true if attributes mismatch
+ * @arg A an object
+ * @arg B another object
+ * @arg ATTR attribute bit
+ * @arg EXPR Comparison expression
+ *
+ * This function will check if the attribute in question is available
+ * in both objects, if not this will count as a mismatch.
+ *
+ * If available the function will execute the expression which must
+ * return true if the attributes mismatch.
+ *
+ * @return True if the attribute mismatch, or false if they match.
+ */
+#define ATTR_MISMATCH(A, B, ATTR, EXPR) (AVAILABLE_MISMATCH(A, B, ATTR) || \
+ (AVAILABLE(A, B, ATTR) && (EXPR)))
+
+/**
+ * Return attribute bit if attribute does not match
+ * @arg LIST list of attributes to be compared
+ * @arg ATTR attribute bit
+ * @arg A an object
+ * @arg B another object
+ * @arg EXPR Comparison expression
+ *
+ * This function will check if the attribute in question is available
+ * in both objects, if not this will count as a mismatch.
+ *
+ * If available the function will execute the expression which must
+ * return true if the attributes mismatch.
+ *
+ * In case the attributes mismatch, the attribute is returned, otherwise
+ * 0 is returned.
+ *
+ * @code
+ * diff |= ATTR_DIFF(attrs, MY_ATTR_FOO, a, b, a->foo != b->foo);
+ * @endcode
+ */
+#define ATTR_DIFF(LIST, ATTR, A, B, EXPR) \
+({ int diff = 0; \
+ if (((LIST) & (ATTR)) && ATTR_MISMATCH(A, B, ATTR, EXPR)) \
+ diff = ATTR; \
+ diff; })
+
+/**
+ * Object Operations
+ */
+struct nl_object_ops
+{
+ /**
+ * Unique name of object type
+ *
+ * Must be in the form family/name, e.g. "route/addr"
+ */
+ char * oo_name;
+
+ /** Size of object including its header */
+ size_t oo_size;
+
+ /* List of attributes needed to uniquely identify the object */
+ uint32_t oo_id_attrs;
+
+ /**
+ * Constructor function
+ *
+ * Will be called when a new object of this type is allocated.
+ * Can be used to initialize members such as lists etc.
+ */
+ void (*oo_constructor)(struct nl_object *);
+
+ /**
+ * Destructor function
+ *
+ * Will be called when an object is freed. Must free all
+ * resources which may have been allocated as part of this
+ * object.
+ */
+ void (*oo_free_data)(struct nl_object *);
+
+ /**
+ * Cloning function
+ *
+ * Will be called when an object needs to be cloned. Please
+ * note that the generic object code will make an exact
+ * copy of the object first, therefore you only need to take
+ * care of members which require reference counting etc.
+ *
+ * May return a negative error code to abort cloning.
+ */
+ int (*oo_clone)(struct nl_object *, struct nl_object *);
+
+ /**
+ * Dumping functions
+ *
+ * Will be called when an object is dumped. The implementations
+ * have to use nl_dump(), nl_dump_line(), and nl_new_line() to
+ * dump objects.
+ *
+ * The functions must return the number of lines printed.
+ */
+ void (*oo_dump[NL_DUMP_MAX+1])(struct nl_object *,
+ struct nl_dump_params *);
+
+ /**
+ * Comparison function
+ *
+ * Will be called when two objects of the same type are
+ * compared. It takes the two objects in question, an object
+ * specific bitmask defining which attributes should be
+ * compared and flags to control the behaviour.
+ *
+ * The function must return a bitmask with the relevant bit
+ * set for each attribute that mismatches.
+ */
+ int (*oo_compare)(struct nl_object *, struct nl_object *,
+ uint32_t, int);
+
+
+ /**
+ * update function
+ *
+ * Will be called when the object given by first argument
+ * needs to be updated with the contents of the second object
+ *
+ * The function must return 0 for success and error for failure
+ * to update. In case of failure its assumed that the original
+ * object is not touched
+ */
+ int (*oo_update)(struct nl_object *, struct nl_object *);
+
+ /**
+ * Hash Key generator function
+ *
+ * When called returns a hash key for the object being
+ * referenced. This key will be used by higher level hash functions
+ * to build association lists. Each object type gets to specify
+ * it's own key formulation
+ */
+ void (*oo_keygen)(struct nl_object *, uint32_t *, uint32_t);
+
+ char *(*oo_attrs2str)(int, char *, size_t);
+
+ /**
+ * Get key attributes by family function
+ */
+ uint32_t (*oo_id_attrs_get)(struct nl_object *);
+};
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink-private/route/link/api.h b/include/netlink-private/route/link/api.h
new file mode 100644
index 00000000..bb98cccb
--- /dev/null
+++ b/include/netlink-private/route/link/api.h
@@ -0,0 +1,153 @@
+/*
+ * netlink-private/route/link/api.h Link Modules 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-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_LINK_API_H_
+#define NETLINK_LINK_API_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @ingroup link_api
+ *
+ * Available operations to modules implementing a link info type.
+ */
+struct rtnl_link_info_ops
+{
+ /** Name of link info type, must match name on kernel side */
+ char * io_name;
+
+ /** Reference count, DO NOT MODIFY */
+ int io_refcnt;
+
+ /** Called to assign an info type to a link.
+ * Has to allocate enough resources to hold attributes. Can
+ * use link->l_info to store a pointer. */
+ int (*io_alloc)(struct rtnl_link *);
+
+ /** Called to parse the link info attribute.
+ * Must parse the attribute and assign all values to the link.
+ */
+ int (*io_parse)(struct rtnl_link *,
+ struct nlattr *,
+ struct nlattr *);
+
+ /** Called when the link object is dumped.
+ * Must dump the info type specific attributes. */
+ void (*io_dump[NL_DUMP_MAX+1])(struct rtnl_link *,
+ struct nl_dump_params *);
+
+ /** Called when a link object is cloned.
+ * Must clone all info type specific attributes. */
+ int (*io_clone)(struct rtnl_link *, struct rtnl_link *);
+
+ /** Called when construction a link netlink message.
+ * Must append all info type specific attributes to the message. */
+ int (*io_put_attrs)(struct nl_msg *, struct rtnl_link *);
+
+ /** Called to release all resources previously allocated
+ * in either io_alloc() or io_parse(). */
+ void (*io_free)(struct rtnl_link *);
+
+ struct nl_list_head io_list;
+};
+
+extern struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *);
+extern void rtnl_link_info_ops_put(struct rtnl_link_info_ops *);
+extern int rtnl_link_register_info(struct rtnl_link_info_ops *);
+extern int rtnl_link_unregister_info(struct rtnl_link_info_ops *);
+
+
+/**
+ * @ingroup link_api
+ *
+ * Available operations to modules implementing a link address family.
+ */
+struct rtnl_link_af_ops
+{
+ /** The address family this operations set implements */
+ const unsigned int ao_family;
+
+ /** Number of users of this operations, DO NOT MODIFY. */
+ int ao_refcnt;
+
+ /** Validation policy for IFLA_PROTINFO attribute. This pointer
+ * can be set to a nla_policy structure describing the minimal
+ * requirements the attribute must meet. Failure of meeting these
+ * requirements will result in a parsing error. */
+ const struct nla_policy *ao_protinfo_policy;
+
+ /** Called after address family has been assigned to link. Must
+ * allocate data buffer to hold address family specific data and
+ * store it in link->l_af_data. */
+ void * (*ao_alloc)(struct rtnl_link *);
+
+ /** Called when the link is cloned, must allocate a clone of the
+ * address family specific buffer and return it. */
+ void * (*ao_clone)(struct rtnl_link *, void *);
+
+ /** Called when the link gets freed. Must free all allocated data */
+ void (*ao_free)(struct rtnl_link *, void *);
+
+ /** Called if a IFLA_PROTINFO attribute needs to be parsed. Typically
+ * stores the parsed data in the address family specific buffer. */
+ int (*ao_parse_protinfo)(struct rtnl_link *,
+ struct nlattr *, void *);
+
+ /** Called if a IFLA_AF_SPEC attribute needs to be parsed. Typically
+ * stores the parsed data in the address family specific buffer. */
+ int (*ao_parse_af)(struct rtnl_link *,
+ struct nlattr *, void *);
+
+ /** Called if a link message is sent to the kernel. Must append the
+ * link address family specific attributes to the message. */
+ int (*ao_fill_af)(struct rtnl_link *,
+ struct nl_msg *msg, void *);
+
+ /** Dump address family specific link attributes */
+ void (*ao_dump[NL_DUMP_MAX+1])(struct rtnl_link *,
+ struct nl_dump_params *,
+ void *);
+
+ /** Comparison function
+ *
+ * Will be called when two links are compared for their af data. It
+ * takes two link objects in question, an object specific bitmask
+ * defining which attributes should be compared and flags to control
+ * the behaviour
+ *
+ * The function must return a bitmask with the relevant bit set for
+ * each attribute that mismatches
+ */
+ int (*ao_compare)(struct rtnl_link *,
+ struct rtnl_link *, int, uint32_t, int);
+};
+
+extern struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(unsigned int);
+extern void rtnl_link_af_ops_put(struct rtnl_link_af_ops *);
+extern void * rtnl_link_af_alloc(struct rtnl_link *,
+ const struct rtnl_link_af_ops *);
+extern void * rtnl_link_af_data(const struct rtnl_link *,
+ const struct rtnl_link_af_ops *);
+extern int rtnl_link_af_register(struct rtnl_link_af_ops *);
+extern int rtnl_link_af_unregister(struct rtnl_link_af_ops *);
+extern int rtnl_link_af_data_compare(struct rtnl_link *a,
+ struct rtnl_link *b,
+ int family);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink-private/route/tc-api.h b/include/netlink-private/route/tc-api.h
new file mode 100644
index 00000000..bf0c8a3b
--- /dev/null
+++ b/include/netlink-private/route/tc-api.h
@@ -0,0 +1,134 @@
+/*
+ * netlink-private/route/tc-api.h Traffic Control 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) 2011-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_TC_API_H_
+#define NETLINK_TC_API_H_
+
+#include <netlink/netlink.h>
+#include <netlink/msg.h>
+#include <netlink/route/tc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Traffic control object operations
+ * @ingroup tc
+ *
+ * This structure holds function pointers and settings implementing
+ * the features of each traffic control object implementation.
+ */
+struct rtnl_tc_ops
+{
+ /**
+ * Name of traffic control module
+ */
+ char *to_kind;
+
+ /**
+ * Type of traffic control object
+ */
+ enum rtnl_tc_type to_type;
+
+
+ /**
+ * Size of private data
+ */
+ size_t to_size;
+
+ /**
+ * Dump callbacks
+ */
+ void (*to_dump[NL_DUMP_MAX+1])(struct rtnl_tc *, void *,
+ struct nl_dump_params *);
+ /**
+ * Used to fill the contents of TCA_OPTIONS
+ */
+ int (*to_msg_fill)(struct rtnl_tc *, void *, struct nl_msg *);
+
+ /**
+ * Uesd to to fill tc related messages, unlike with to_msg_fill,
+ * the contents is not encapsulated with a TCA_OPTIONS nested
+ * attribute.
+ */
+ int (*to_msg_fill_raw)(struct rtnl_tc *, void *, struct nl_msg *);
+
+ /**
+ * TCA_OPTIONS message parser
+ */
+ int (*to_msg_parser)(struct rtnl_tc *, void *);
+
+ /**
+ * Called before a tc object is destroyed
+ */
+ void (*to_free_data)(struct rtnl_tc *, void *);
+
+ /**
+ * Called whenever a classifier object needs to be cloned
+ */
+ int (*to_clone)(void *, void *);
+
+ /**
+ * Internal, don't touch
+ */
+ struct nl_list_head to_list;
+};
+
+struct rtnl_tc_type_ops
+{
+ enum rtnl_tc_type tt_type;
+
+ char *tt_dump_prefix;
+
+ /**
+ * Dump callbacks
+ */
+ void (*tt_dump[NL_DUMP_MAX+1])(struct rtnl_tc *,
+ struct nl_dump_params *);
+};
+
+extern int rtnl_tc_msg_parse(struct nlmsghdr *,
+ struct rtnl_tc *);
+extern int rtnl_tc_msg_build(struct rtnl_tc *, int,
+ int, struct nl_msg **);
+
+extern void rtnl_tc_free_data(struct nl_object *);
+extern int rtnl_tc_clone(struct nl_object *,
+ struct nl_object *);
+extern void rtnl_tc_dump_line(struct nl_object *,
+ struct nl_dump_params *);
+extern void rtnl_tc_dump_details(struct nl_object *,
+ struct nl_dump_params *);
+extern void rtnl_tc_dump_stats(struct nl_object *,
+ struct nl_dump_params *);
+extern int rtnl_tc_compare(struct nl_object *,
+ struct nl_object *,
+ uint32_t, int);
+
+extern void * rtnl_tc_data(struct rtnl_tc *);
+extern void * rtnl_tc_data_check(struct rtnl_tc *,
+ struct rtnl_tc_ops *);
+
+extern struct rtnl_tc_ops * rtnl_tc_lookup_ops(enum rtnl_tc_type,
+ const char *);
+extern struct rtnl_tc_ops * rtnl_tc_get_ops(struct rtnl_tc *);
+extern int rtnl_tc_register(struct rtnl_tc_ops *);
+extern void rtnl_tc_unregister(struct rtnl_tc_ops *);
+
+extern void rtnl_tc_type_register(struct rtnl_tc_type_ops *);
+extern void rtnl_tc_type_unregister(struct rtnl_tc_type_ops *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink-private/socket.h b/include/netlink-private/socket.h
new file mode 100644
index 00000000..86a440c5
--- /dev/null
+++ b/include/netlink-private/socket.h
@@ -0,0 +1,31 @@
+/*
+ * netlink-private/socket.h Private declarations for 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) 2014 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_SOCKET_PRIV_H_
+#define NETLINK_SOCKET_PRIV_H_
+
+#include <netlink-private/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int _nl_socket_is_local_port_unspecified (struct nl_sock *sk);
+uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk);
+
+void _nl_socket_used_ports_release_all(const uint32_t *used_ports);
+void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink-private/tc.h b/include/netlink-private/tc.h
new file mode 100644
index 00000000..d0cb283c
--- /dev/null
+++ b/include/netlink-private/tc.h
@@ -0,0 +1,57 @@
+/*
+ * netlink-private/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-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_TC_PRIV_H_
+#define NETLINK_TC_PRIV_H_
+
+#include <netlink-private/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define TCA_ATTR_HANDLE 0x0001
+#define TCA_ATTR_PARENT 0x0002
+#define TCA_ATTR_IFINDEX 0x0004
+#define TCA_ATTR_KIND 0x0008
+#define TCA_ATTR_FAMILY 0x0010
+#define TCA_ATTR_INFO 0x0020
+#define TCA_ATTR_OPTS 0x0040
+#define TCA_ATTR_STATS 0x0080
+#define TCA_ATTR_XSTATS 0x0100
+#define TCA_ATTR_LINK 0x0200
+#define TCA_ATTR_MTU 0x0400
+#define TCA_ATTR_MPU 0x0800
+#define TCA_ATTR_OVERHEAD 0x1000
+#define TCA_ATTR_LINKTYPE 0x2000
+#define TCA_ATTR_MAX TCA_ATTR_LINKTYPE
+
+extern int tca_parse(struct nlattr **, int, struct rtnl_tc *,
+ struct nla_policy *);
+
+#define RTNL_TC_RTABLE_SIZE 256
+
+extern int rtnl_tc_build_rate_table(struct rtnl_tc *tc, struct rtnl_ratespec *,
+ uint32_t *);
+
+
+static inline void *tca_xstats(struct rtnl_tc *tca)
+{
+ return tca->tc_xstats->d_data;
+}
+
+extern struct nl_af_group tc_groups[];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink-types.h b/include/netlink-private/types.h
index ff699bbe..3ff4fe1a 100644
--- a/include/netlink-types.h
+++ b/include/netlink-private/types.h
@@ -1,12 +1,13 @@
/*
- * netlink-types.h Netlink Types (Private)
+ * netlink-private/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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2013 Sassano Systems LLC <joe@sassanosystems.com>
*/
#ifndef NETLINK_LOCAL_TYPES_H_
@@ -17,6 +18,10 @@
#include <netlink/route/qdisc.h>
#include <netlink/route/rtnl.h>
#include <netlink/route/route.h>
+#include <netlink/idiag/idiagnl.h>
+#include <netlink/netfilter/ct.h>
+#include <netlink-private/route/tc-api.h>
+#include <linux/tc_act/tc_mirred.h>
#define NL_SOCK_BUFSIZE_SET (1<<0)
#define NL_SOCK_PASSCRED (1<<1)
@@ -29,12 +34,13 @@
struct nl_cache_ops;
struct nl_sock;
struct nl_object;
+struct nl_hash_table;
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;
@@ -56,6 +62,8 @@ struct nl_cb
struct nl_msg *);
int cb_refcnt;
+ /** indicates the callback that is currently active */
+ enum nl_cb_type cb_active;
};
struct nl_sock
@@ -68,6 +76,7 @@ struct nl_sock
unsigned int s_seq_expect;
int s_flags;
struct nl_cb * s_cb;
+ size_t s_bufsize;
};
struct nl_cache
@@ -76,6 +85,9 @@ struct nl_cache
int c_nitems;
int c_iarg1;
int c_iarg2;
+ int c_refcnt;
+ unsigned int c_flags;
+ struct nl_hash_table * hashtable;
struct nl_cache_ops * c_ops;
};
@@ -91,7 +103,8 @@ struct nl_cache_mngr
int cm_protocol;
int cm_flags;
int cm_nassocs;
- struct nl_sock * cm_handle;
+ struct nl_sock * cm_sock;
+ struct nl_sock * cm_sync_sock;
struct nl_cache_assoc * cm_assocs;
};
@@ -101,11 +114,6 @@ struct nl_parser_param;
#define NL_OBJ_MARK 1
-struct nl_object
-{
- NLHDR_COMMON
-};
-
struct nl_data
{
size_t d_size;
@@ -150,29 +158,42 @@ 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;
- uint8_t l_operstate;
- uint8_t l_linkmode;
+ 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;
+ uint32_t l_num_vf;
+ uint8_t l_operstate;
+ uint8_t l_linkmode;
/* 2 byte hole */
- struct rtnl_link_info_ops *l_info_ops;
- void * l_info;
+ char * l_info_kind;
+ struct rtnl_link_info_ops * l_info_ops;
+ void * l_af_data[AF_MAX];
+ void * l_info;
+ char * l_ifalias;
+ uint32_t l_promiscuity;
+ uint32_t l_num_tx_queues;
+ uint32_t l_num_rx_queues;
+ uint32_t l_group;
+ uint8_t l_carrier;
+ /* 3 byte hole */
+ struct rtnl_link_af_ops * l_af_ops;
+ struct nl_data * l_phys_port_id;
+ int l_ns_fd;
+ pid_t l_ns_pid;
};
struct rtnl_ncacheinfo
@@ -191,28 +212,29 @@ struct rtnl_neigh
uint32_t n_ifindex;
uint16_t n_state;
uint8_t n_flags;
- uint8_t n_type;
+ uint8_t n_type;
struct nl_addr *n_lladdr;
- struct nl_addr *n_dst;
+ struct nl_addr *n_dst;
uint32_t n_probes;
struct rtnl_ncacheinfo n_cacheinfo;
uint32_t n_state_mask;
uint32_t n_flag_mask;
+ uint32_t n_master;
};
struct rtnl_addr_cacheinfo
{
- /* Preferred lifetime in seconds */
+ /* Preferred lifetime in seconds, ticking from when the message gets constructed */
uint32_t aci_prefered;
- /* Valid lifetime in seconds */
+ /* Valid lifetime in seconds, ticking from when the message gets constructed */
uint32_t aci_valid;
- /* Timestamp of creation in 1/100s seince boottime */
+ /* Timestamp of creation in 1/100s since boottime, clock_gettime(CLOCK_MONOTONIC) */
uint32_t aci_cstamp;
- /* Timestamp of last update in 1/100s since boottime */
+ /* Timestamp of last update in 1/100s since boottime, clock_gettime(CLOCK_MONOTONIC) */
uint32_t aci_tstamp;
};
@@ -222,20 +244,21 @@ struct rtnl_addr
uint8_t a_family;
uint8_t a_prefixlen;
- uint8_t a_flags;
uint8_t a_scope;
+ uint32_t a_flags;
uint32_t a_ifindex;
- struct nl_addr *a_peer;
+ 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;
+ struct rtnl_link *a_link;
};
struct rtnl_nexthop
@@ -281,20 +304,21 @@ struct rtnl_route
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];
+ uint8_t r_action;
+ uint8_t r_dsfield; /* ipv4 only */
+ uint8_t r_unused;
+ uint32_t r_table;
+ uint32_t r_flags;
+ uint32_t r_prio;
+ uint32_t r_mark;
+ uint32_t r_mask;
+ uint32_t r_goto;
+ uint32_t r_flow; /* ipv4 only */
struct nl_addr *r_src;
struct nl_addr *r_dst;
- struct nl_addr *r_srcmap;
+ char r_iifname[IFNAMSIZ];
+ char r_oifname[IFNAMSIZ];
};
struct rtnl_neightbl_parms
@@ -383,7 +407,7 @@ struct rtnl_neightbl_parms
* Queue length for the delayed proxy arp requests.
*/
uint32_t ntp_proxy_qlen;
-
+
/**
* Mask of available parameter attributes
*/
@@ -414,8 +438,8 @@ struct rtnl_neightbl
struct rtnl_ratespec
{
uint8_t rs_cell_log;
- uint16_t rs_feature;
- uint16_t rs_addend;
+ uint16_t rs_overhead;
+ int16_t rs_cell_align;
uint16_t rs_mpu;
uint32_t rs_rate;
};
@@ -443,43 +467,57 @@ struct rtnl_tstats
#define TCKINDSIZ 32
-#define NL_TCA_GENERIC(pre) \
+#define NL_TC_GENERIC(pre) \
NLHDR_COMMON \
uint32_t pre ##_family; \
uint32_t pre ##_ifindex; \
uint32_t pre ##_handle; \
uint32_t pre ##_parent; \
uint32_t pre ##_info; \
+ uint32_t pre ##_mtu; \
+ uint32_t pre ##_mpu; \
+ uint32_t pre ##_overhead; \
+ uint32_t pre ##_linktype; \
char pre ##_kind[TCKINDSIZ]; \
struct nl_data * pre ##_opts; \
uint64_t pre ##_stats[RTNL_TC_STATS_MAX+1]; \
struct nl_data * pre ##_xstats; \
struct nl_data * pre ##_subdata; \
+ struct rtnl_link * pre ##_link; \
+ struct rtnl_tc_ops * pre ##_ops; \
+ enum rtnl_tc_type pre ##_type
-
-struct rtnl_tca
+struct rtnl_tc
{
- NL_TCA_GENERIC(tc);
+ NL_TC_GENERIC(tc);
};
struct rtnl_qdisc
{
- NL_TCA_GENERIC(q);
- struct rtnl_qdisc_ops *q_ops;
+ NL_TC_GENERIC(q);
};
struct rtnl_class
{
- NL_TCA_GENERIC(c);
- struct rtnl_class_ops *c_ops;
+ NL_TC_GENERIC(c);
};
struct rtnl_cls
{
- NL_TCA_GENERIC(c);
+ NL_TC_GENERIC(c);
uint16_t c_prio;
uint16_t c_protocol;
- struct rtnl_cls_ops *c_ops;
+};
+
+struct rtnl_act
+{
+ NL_TC_GENERIC(c);
+ struct rtnl_act * a_next;
+};
+
+struct rtnl_mirred
+{
+ struct tc_mirred m_parm;
};
struct rtnl_u32
@@ -490,7 +528,7 @@ struct rtnl_u32
uint32_t cu_link;
struct nl_data * cu_pcnt;
struct nl_data * cu_selector;
- struct nl_data * cu_act;
+ struct rtnl_act* cu_act;
struct nl_data * cu_police;
char cu_indev[IFNAMSIZ];
int cu_mask;
@@ -508,6 +546,7 @@ struct rtnl_fw
struct nl_data * cf_act;
struct nl_data * cf_police;
char cf_indev[IFNAMSIZ];
+ uint32_t cf_fwmask;
int cf_mask;
};
@@ -516,12 +555,14 @@ struct rtnl_ematch
uint16_t e_id;
uint16_t e_kind;
uint16_t e_flags;
+ uint16_t e_index;
+ size_t e_datalen;
struct nl_list_head e_childs;
struct nl_list_head e_list;
struct rtnl_ematch_ops *e_ops;
- char e_data[0];
+ void * e_data;
};
struct rtnl_ematch_tree
@@ -562,7 +603,6 @@ struct rtnl_prio
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;
@@ -627,20 +667,19 @@ struct rtnl_htb_qdisc
uint32_t qh_rate2quantum;
uint32_t qh_defcls;
uint32_t qh_mask;
+ uint32_t qh_direct_pkts;
};
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;
+ uint32_t ch_level;
};
struct rtnl_cbq
@@ -665,6 +704,23 @@ struct rtnl_red
uint32_t qr_mask;
};
+struct rtnl_plug
+{
+ int action;
+ uint32_t limit;
+};
+
+struct rtnl_fq_codel
+{
+ int fq_limit;
+ uint32_t fq_target;
+ uint32_t fq_interval;
+ int fq_flows;
+ uint32_t fq_quantum;
+ int fq_ecn;
+ uint32_t fq_mask;
+};
+
struct flnl_request
{
NLHDR_COMMON
@@ -702,6 +758,13 @@ struct genl_family_op
struct nl_list_head o_list;
};
+struct genl_family_grp {
+ struct genl_family *family; /* private */
+ struct nl_list_head list; /* private */
+ char name[GENL_NAMSIZ];
+ u_int32_t id;
+};
+
struct genl_family
{
NLHDR_COMMON
@@ -713,6 +776,7 @@ struct genl_family
uint32_t gf_maxattr;
struct nl_list_head gf_ops;
+ struct nl_list_head gf_mc_grps;
};
union nfnl_ct_proto
@@ -755,9 +819,56 @@ struct nfnl_ct {
uint32_t ct_mark;
uint32_t ct_use;
uint32_t ct_id;
+ uint16_t ct_zone;
struct nfnl_ct_dir ct_orig;
struct nfnl_ct_dir ct_repl;
+
+ struct nfnl_ct_timestamp ct_tstamp;
+};
+
+union nfnl_exp_protodata {
+ struct {
+ uint16_t src;
+ uint16_t dst;
+ } port;
+ struct {
+ uint16_t id;
+ uint8_t type;
+ uint8_t code;
+ } icmp;
+};
+
+// Allow for different master/expect l4 protocols
+struct nfnl_exp_proto
+{
+ uint8_t l4protonum;
+ union nfnl_exp_protodata l4protodata;
+};
+
+struct nfnl_exp_dir {
+ struct nl_addr * src;
+ struct nl_addr * dst;
+ struct nfnl_exp_proto proto;
+};
+
+struct nfnl_exp {
+ NLHDR_COMMON
+
+ uint8_t exp_family;
+ uint32_t exp_timeout;
+ uint32_t exp_id;
+ uint16_t exp_zone;
+ uint32_t exp_class;
+ uint32_t exp_flags;
+ char * exp_helper_name;
+ char * exp_fn;
+ uint8_t exp_nat_dir;
+
+ struct nfnl_exp_dir exp_expect;
+ struct nfnl_exp_dir exp_master;
+ struct nfnl_exp_dir exp_mask;
+ struct nfnl_exp_dir exp_nat;
};
struct nfnl_log {
@@ -826,4 +937,67 @@ struct nfnl_queue_msg {
uint32_t queue_msg_verdict;
};
+struct ematch_quoted {
+ char * data;
+ size_t len;
+ int index;
+};
+
+struct idiagnl_meminfo {
+ NLHDR_COMMON
+
+ uint32_t idiag_rmem;
+ uint32_t idiag_wmem;
+ uint32_t idiag_fmem;
+ uint32_t idiag_tmem;
+};
+
+struct idiagnl_vegasinfo {
+ NLHDR_COMMON
+
+ uint32_t tcpv_enabled;
+ uint32_t tcpv_rttcnt;
+ uint32_t tcpv_rtt;
+ uint32_t tcpv_minrtt;
+};
+
+struct idiagnl_msg {
+ NLHDR_COMMON
+
+ uint8_t idiag_family;
+ uint8_t idiag_state;
+ uint8_t idiag_timer;
+ uint8_t idiag_retrans;
+ uint16_t idiag_sport;
+ uint16_t idiag_dport;
+ struct nl_addr * idiag_src;
+ struct nl_addr * idiag_dst;
+ uint32_t idiag_ifindex;
+ uint32_t idiag_expires;
+ uint32_t idiag_rqueue;
+ uint32_t idiag_wqueue;
+ uint32_t idiag_uid;
+ uint32_t idiag_inode;
+
+ uint8_t idiag_tos;
+ uint8_t idiag_tclass;
+ uint8_t idiag_shutdown;
+ char * idiag_cong;
+ struct idiagnl_meminfo * idiag_meminfo;
+ struct idiagnl_vegasinfo * idiag_vegasinfo;
+ struct tcp_info idiag_tcpinfo;
+ uint32_t idiag_skmeminfo[IDIAG_SK_MEMINFO_VARS];
+};
+
+struct idiagnl_req {
+ NLHDR_COMMON
+
+ uint8_t idiag_family;
+ uint8_t idiag_ext;
+ struct nl_addr * idiag_src;
+ struct nl_addr * idiag_dst;
+ uint32_t idiag_ifindex;
+ uint32_t idiag_states;
+ uint32_t idiag_dbs;
+};
#endif
diff --git a/include/netlink-tc.h b/include/netlink-tc.h
deleted file mode 100644
index 71a20ffb..00000000
--- a/include/netlink-tc.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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-2008 Thomas Graf <tgraf@suug.ch>
- */
-
-#ifndef NETLINK_TC_PRIV_H_
-#define NETLINK_TC_PRIV_H_
-
-#include <netlink-local.h>
-
-#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 void tca_dump_line(struct rtnl_tca *, const char *,
- struct nl_dump_params *);
-extern void tca_dump_details(struct rtnl_tca *, struct nl_dump_params *);
-extern void tca_dump_stats(struct rtnl_tca *, struct nl_dump_params *);
-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 int tca_build_msg(struct rtnl_tca *, int, int, struct nl_msg **);
-
-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/addr.h b/include/netlink/addr.h
index cc3d201f..db3e4c20 100644
--- a/include/netlink/addr.h
+++ b/include/netlink/addr.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_ADDR_H_
@@ -27,9 +27,6 @@ extern struct nl_addr * nl_addr_build(int, void *, size_t);
extern int nl_addr_parse(const char *, int, struct nl_addr **);
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 *);
@@ -43,7 +40,7 @@ extern int nl_addr_guess_family(struct nl_addr *);
extern int nl_addr_fill_sockaddr(struct nl_addr *,
struct sockaddr *, socklen_t *);
extern int nl_addr_info(struct nl_addr *, struct addrinfo **);
-extern int nl_addr_resolve(struct nl_addr *addr, char *host, size_t hostlen);
+extern int nl_addr_resolve(struct nl_addr *, char *, size_t);
/* Access Functions */
extern void nl_addr_set_family(struct nl_addr *, int);
diff --git a/include/netlink/attr.h b/include/netlink/attr.h
index 8479c233..82e4c383 100644
--- a/include/netlink/attr.h
+++ b/include/netlink/attr.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_ATTR_H_
@@ -28,12 +28,12 @@ struct nl_msg;
* @{
*/
- /**
- * @ingroup attr
- * Basic attribute data types
- *
- * See \ref attr_datatypes for more details.
- */
+/**
+ * @ingroup attr
+ * Basic attribute data types
+ *
+ * See section @core_doc{core_attr_parse,Attribute Parsing} for more details.
+ */
enum {
NLA_UNSPEC, /**< Unspecified type, binary data chunk */
NLA_U8, /**< 8 bit integer */
@@ -55,7 +55,7 @@ enum {
* @ingroup attr
* Attribute validation policy.
*
- * See \ref attr_datatypes for more details.
+ * See section @core_doc{core_attr_parse,Attribute Parsing} for more details.
*/
struct nla_policy {
/** Type of attribute or NLA_UNSPEC */
@@ -124,8 +124,10 @@ extern int nla_put_msecs(struct nl_msg *, int, unsigned long);
extern int nla_put_nested(struct nl_msg *, int, struct nl_msg *);
extern struct nlattr * nla_nest_start(struct nl_msg *, int);
extern int nla_nest_end(struct nl_msg *, struct nlattr *);
+extern void nla_nest_cancel(struct nl_msg *, struct nlattr *);
extern int nla_parse_nested(struct nlattr **, int, struct nlattr *,
struct nla_policy *);
+extern int nla_is_nested(struct nlattr *);
/**
* @name Attribute Construction (Exception Based)
@@ -203,7 +205,7 @@ extern int nla_parse_nested(struct nlattr **, int, struct nlattr *,
* @arg value NUL terminated character string.
*/
#define NLA_PUT_STRING(msg, attrtype, value) \
- NLA_PUT(msg, attrtype, strlen(value) + 1, value)
+ NLA_PUT(msg, attrtype, (int) strlen(value) + 1, value)
/**
* Add flag attribute to netlink message.
diff --git a/include/netlink/cache-api.h b/include/netlink/cache-api.h
index 22fc449d..e43c7ca0 100644
--- a/include/netlink/cache-api.h
+++ b/include/netlink/cache-api.h
@@ -6,194 +6,15 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2013 Thomas Graf <tgraf@suug.ch>
*/
-#ifndef NETLINK_CACHE_API_H_
-#define NETLINK_CACHE_API_H_
+#ifndef NETLINK_DUMMY_CACHE_API_H_
+#define NETLINK_DUMMY_CACHE_API_H_
#include <netlink/netlink.h>
+#include <netlink/cache.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @ingroup cache
- * @defgroup cache_api Cache Implementation
- * @brief
- *
- * @par 1) Cache Definition
- * @code
- * struct nl_cache_ops my_cache_ops = {
- * .co_name = "route/link",
- * .co_protocol = NETLINK_ROUTE,
- * .co_hdrsize = sizeof(struct ifinfomsg),
- * .co_obj_ops = &my_obj_ops,
- * };
- * @endcode
- *
- * @par 2)
- * @code
- * // The simplest way to fill a cache is by providing a request-update
- * // function which must trigger a complete dump on the kernel-side of
- * // whatever the cache covers.
- * static int my_request_update(struct nl_cache *cache,
- * struct nl_sock *socket)
- * {
- * // In this example, we request a full dump of the interface table
- * return nl_rtgen_request(socket, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP);
- * }
- *
- * // The resulting netlink messages sent back will be fed into a message
- * // parser one at a time. The message parser has to extract all relevant
- * // information from the message and create an object reflecting the
- * // contents of the message and pass it on to the parser callback function
- * // provide which will add the object to the cache.
- * static int my_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
- * struct nlmsghdr *nlh, struct nl_parser_param *pp)
- * {
- * struct my_obj *obj;
- *
- * obj = my_obj_alloc();
- * obj->ce_msgtype = nlh->nlmsg_type;
- *
- * // Parse the netlink message and continue creating the object.
- *
- * err = pp->pp_cb((struct nl_object *) obj, pp);
- * if (err < 0)
- * goto errout;
- * }
- *
- * struct nl_cache_ops my_cache_ops = {
- * ...
- * .co_request_update = my_request_update,
- * .co_msg_parser = my_msg_parser,
- * };
- * @endcode
- *
- * @par 3) Notification based Updates
- * @code
- * // Caches can be kept up-to-date based on notifications if the kernel
- * // sends out notifications whenever an object is added/removed/changed.
- * //
- * // It is trivial to support this, first a list of groups needs to be
- * // defined which are required to join in order to receive all necessary
- * // notifications. The groups are separated by address family to support
- * // the common situation where a separate group is used for each address
- * // family. If there is only one group, simply specify AF_UNSPEC.
- * static struct nl_af_group addr_groups[] = {
- * { AF_INET, RTNLGRP_IPV4_IFADDR },
- * { AF_INET6, RTNLGRP_IPV6_IFADDR },
- * { END_OF_GROUP_LIST },
- * };
- *
- * // In order for the caching system to know the meaning of each message
- * // type it requires a table which maps each supported message type to
- * // a cache action, e.g. RTM_NEWADDR means address has been added or
- * // updated, RTM_DELADDR means address has been removed.
- * static struct nl_cache_ops rtnl_addr_ops = {
- * ...
- * .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_groups = addr_groups,
- * };
- *
- * // It is now possible to keep the cache up-to-date using the cache manager.
- * @endcode
- * @{
- */
-
-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 }
-
-/**
- * Message type to cache action association
- */
-struct nl_msgtype
-{
- /** Netlink message type */
- int mt_id;
-
- /** Cache action to take */
- int mt_act;
-
- /** Name of operation for human-readable printing */
- char * mt_name;
-};
-
-/**
- * Address family to netlink group association
- */
-struct nl_af_group
-{
- /** Address family */
- int ag_family;
-
- /** Netlink group identifier */
- int ag_group;
-};
-
-#define END_OF_GROUP_LIST AF_UNSPEC, 0
-
-struct nl_parser_param
-{
- int (*pp_cb)(struct nl_object *, struct nl_parser_param *);
- void * pp_arg;
-};
-
-/**
- * Cache Operations
- */
-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_sock *);
-
- /**
- * 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 *, struct nl_parser_param *);
-
- 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[];
-};
-
-/** @} */
-
-#ifdef __cplusplus
-}
-#endif
+#warning "You are including a deprecated header file, include <netlink/cache.h>."
#endif
diff --git a/include/netlink/cache.h b/include/netlink/cache.h
index c7529208..e21aa1c6 100644
--- a/include/netlink/cache.h
+++ b/include/netlink/cache.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_CACHE_H_
@@ -16,16 +16,32 @@
#include <netlink/msg.h>
#include <netlink/utils.h>
#include <netlink/object.h>
-#include <netlink/cache-api.h>
#ifdef __cplusplus
extern "C" {
#endif
-struct nl_cache;
+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)
+struct nl_cache;
typedef void (*change_func_t)(struct nl_cache *, struct nl_object *, int, void *);
+/**
+ * @ingroup cache
+ * Explicitely iterate over all address families when updating the cache
+ */
+#define NL_CACHE_AF_ITER 0x0001
+
/* Access Functions */
extern int nl_cache_nitems(struct nl_cache *);
extern int nl_cache_nitems_filter(struct nl_cache *,
@@ -44,14 +60,19 @@ extern int nl_cache_alloc_name(const char *,
struct nl_cache **);
extern struct nl_cache * nl_cache_subset(struct nl_cache *,
struct nl_object *);
+extern struct nl_cache * nl_cache_clone(struct nl_cache *);
extern void nl_cache_clear(struct nl_cache *);
+extern void nl_cache_get(struct nl_cache *);
extern void nl_cache_free(struct nl_cache *);
+extern void nl_cache_put(struct nl_cache *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 *);
+extern int nl_cache_move(struct nl_cache *,
+ struct nl_object *);
extern void nl_cache_remove(struct nl_object *);
extern int nl_cache_refill(struct nl_sock *,
struct nl_cache *);
@@ -65,9 +86,16 @@ extern int nl_cache_include(struct nl_cache *,
struct nl_object *,
change_func_t,
void *);
+extern void nl_cache_set_arg1(struct nl_cache *, int);
+extern void nl_cache_set_arg2(struct nl_cache *, int);
+extern void nl_cache_set_flags(struct nl_cache *, unsigned int);
/* General */
extern int nl_cache_is_empty(struct nl_cache *);
+extern struct nl_object * nl_cache_search(struct nl_cache *,
+ struct nl_object *);
+extern struct nl_object *nl_cache_find(struct nl_cache *,
+ struct nl_object *);
extern void nl_cache_mark_all(struct nl_cache *);
/* Dumping */
@@ -93,7 +121,9 @@ extern void nl_cache_foreach_filter(struct nl_cache *,
/* Cache type management */
extern struct nl_cache_ops * nl_cache_ops_lookup(const char *);
+extern struct nl_cache_ops * nl_cache_ops_lookup_safe(const char *);
extern struct nl_cache_ops * nl_cache_ops_associate(int, int);
+extern struct nl_cache_ops * nl_cache_ops_associate_safe(int, int);
extern struct nl_msgtype * nl_msgtype_lookup(struct nl_cache_ops *, int);
extern void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *);
extern int nl_cache_mngt_register(struct nl_cache_ops *);
@@ -103,10 +133,13 @@ extern int nl_cache_mngt_unregister(struct nl_cache_ops *);
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 *);
+extern struct nl_cache * nl_cache_mngt_require_safe(const char *);
+extern struct nl_cache * __nl_cache_mngt_require(const char *);
struct nl_cache_mngr;
#define NL_AUTO_PROVIDE 1
+#define NL_ALLOCATED_SOCK 2 /* For internal use only, do not use */
extern int nl_cache_mngr_alloc(struct nl_sock *,
int, int,
@@ -116,12 +149,20 @@ extern int nl_cache_mngr_add(struct nl_cache_mngr *,
change_func_t,
void *,
struct nl_cache **);
+extern int nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr,
+ struct nl_cache *cache,
+ change_func_t cb, void *data);
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_info(struct nl_cache_mngr *,
+ struct nl_dump_params *);
extern void nl_cache_mngr_free(struct nl_cache_mngr *);
+extern void nl_cache_ops_get(struct nl_cache_ops *);
+extern void nl_cache_ops_put(struct nl_cache_ops *);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/netlink/cli/class.h b/include/netlink/cli/class.h
new file mode 100644
index 00000000..5001e428
--- /dev/null
+++ b/include/netlink/cli/class.h
@@ -0,0 +1,21 @@
+/*
+ * netlink/cli/class.h CLI Class Helpers
+ *
+ * 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __NETLINK_CLI_CLASS_H_
+#define __NETLINK_CLI_CLASS_H_
+
+#include <netlink/route/class.h>
+#include <netlink/cli/tc.h>
+
+extern struct rtnl_class *nl_cli_class_alloc(void);
+extern struct nl_cache *nl_cli_class_alloc_cache(struct nl_sock *, int);
+
+#endif
diff --git a/include/netlink/cli/cls.h b/include/netlink/cli/cls.h
new file mode 100644
index 00000000..a2707b8e
--- /dev/null
+++ b/include/netlink/cli/cls.h
@@ -0,0 +1,24 @@
+/*
+ * netlink/cli/cls.h CLI Classifier Helpers
+ *
+ * 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __NETLINK_CLI_CLS_H_
+#define __NETLINK_CLI_CLS_H_
+
+#include <netlink/route/classifier.h>
+#include <netlink/cli/tc.h>
+
+extern struct rtnl_cls * nl_cli_cls_alloc(void);
+extern struct nl_cache * nl_cli_cls_alloc_cache(struct nl_sock *,
+ int, uint32_t);
+extern void nl_cli_cls_parse_proto(struct rtnl_cls *, char *);
+extern struct rtnl_ematch_tree *nl_cli_cls_parse_ematch(struct rtnl_cls *, char *);
+
+#endif
diff --git a/include/netlink/cli/ct.h b/include/netlink/cli/ct.h
index bed776b1..ebe7c9dc 100644
--- a/include/netlink/cli/ct.h
+++ b/include/netlink/cli/ct.h
@@ -30,5 +30,6 @@ extern void nl_cli_ct_parse_src_port(struct nfnl_ct *, int, char *);
extern void nl_cli_ct_parse_dst_port(struct nfnl_ct *, int, char *);
extern void nl_cli_ct_parse_tcp_state(struct nfnl_ct *, char *);
extern void nl_cli_ct_parse_status(struct nfnl_ct *, char *);
+extern void nl_cli_ct_parse_zone(struct nfnl_ct *, char *);
#endif
diff --git a/include/netlink/cli/exp.h b/include/netlink/cli/exp.h
new file mode 100644
index 00000000..b2418f8d
--- /dev/null
+++ b/include/netlink/cli/exp.h
@@ -0,0 +1,42 @@
+/*
+ * netlink/cli/exp.h CLI Expectation 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) 2012 Rich Fought <Rich.Fought@watchguard.com>
+ * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __NETLINK_CLI_EXP_H_
+#define __NETLINK_CLI_EXP_H_
+
+#include <netlink/netfilter/exp.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+
+extern struct nfnl_exp *nl_cli_exp_alloc(void);
+extern struct nl_cache *nl_cli_exp_alloc_cache(struct nl_sock *);
+
+extern void nl_cli_exp_parse_family(struct nfnl_exp *, char *);
+extern void nl_cli_exp_parse_timeout(struct nfnl_exp *, char *);
+extern void nl_cli_exp_parse_id(struct nfnl_exp *, char *);
+extern void nl_cli_exp_parse_helper_name(struct nfnl_exp *, char *);
+extern void nl_cli_exp_parse_zone(struct nfnl_exp *, char *);
+extern void nl_cli_exp_parse_flags(struct nfnl_exp *, char *);
+extern void nl_cli_exp_parse_class(struct nfnl_exp *, char *);
+extern void nl_cli_exp_parse_nat_dir(struct nfnl_exp *, char *);
+extern void nl_cli_exp_parse_fn(struct nfnl_exp *, char *);
+
+extern void nl_cli_exp_parse_src(struct nfnl_exp *, int, char *);
+extern void nl_cli_exp_parse_dst(struct nfnl_exp *, int, char *);
+extern void nl_cli_exp_parse_l4protonum(struct nfnl_exp *, int, char *);
+extern void nl_cli_exp_parse_src_port(struct nfnl_exp *, int, char *);
+extern void nl_cli_exp_parse_dst_port(struct nfnl_exp *, int, char *);
+extern void nl_cli_exp_parse_icmp_id(struct nfnl_exp *, int, char *);
+extern void nl_cli_exp_parse_icmp_type(struct nfnl_exp *, int, char *);
+extern void nl_cli_exp_parse_icmp_code(struct nfnl_exp *, int, char *);
+
+
+#endif
diff --git a/include/netlink/cli/link.h b/include/netlink/cli/link.h
index c4040199..3f37948d 100644
--- a/include/netlink/cli/link.h
+++ b/include/netlink/cli/link.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
*/
#ifndef __NETLINK_CLI_LINK_H_
@@ -15,10 +15,9 @@
#include <netlink/route/link.h>
#include <netlink/cli/utils.h>
-#define nl_cli_link_alloc_cache(sk) \
- nl_cli_alloc_cache((sk), "link", rtnl_link_alloc_cache)
-
extern struct rtnl_link *nl_cli_link_alloc(void);
+extern struct nl_cache *nl_cli_link_alloc_cache_family(struct nl_sock *, int);
+extern struct nl_cache *nl_cli_link_alloc_cache(struct nl_sock *);
extern void nl_cli_link_parse_family(struct rtnl_link *, char *);
extern void nl_cli_link_parse_name(struct rtnl_link *, char *);
@@ -26,5 +25,6 @@ extern void nl_cli_link_parse_mtu(struct rtnl_link *, char *);
extern void nl_cli_link_parse_ifindex(struct rtnl_link *, char *);
extern void nl_cli_link_parse_txqlen(struct rtnl_link *, char *);
extern void nl_cli_link_parse_weight(struct rtnl_link *, char *);
+extern void nl_cli_link_parse_ifalias(struct rtnl_link *, char *);
#endif
diff --git a/include/netlink/cli/qdisc.h b/include/netlink/cli/qdisc.h
index 9fc45063..b102da4f 100644
--- a/include/netlink/cli/qdisc.h
+++ b/include/netlink/cli/qdisc.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2011 Thomas Graf <tgraf@suug.ch>
*/
#ifndef __NETLINK_CLI_QDISC_H_
@@ -20,9 +20,4 @@
extern struct rtnl_qdisc *nl_cli_qdisc_alloc(void);
-extern void nl_cli_qdisc_parse_dev(struct rtnl_qdisc *, struct nl_cache *, char *);
-extern void nl_cli_qdisc_parse_parent(struct rtnl_qdisc *, char *);
-extern void nl_cli_qdisc_parse_handle(struct rtnl_qdisc *, char *);
-extern void nl_cli_qdisc_parse_kind(struct rtnl_qdisc *, char *);
-
#endif
diff --git a/include/netlink/cli/tc.h b/include/netlink/cli/tc.h
new file mode 100644
index 00000000..77042c71
--- /dev/null
+++ b/include/netlink/cli/tc.h
@@ -0,0 +1,41 @@
+/*
+ * netlink/cli/tc.h CLI Traffic Control Helpers
+ *
+ * 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) 2010-2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __NETLINK_CLI_TC_H_
+#define __NETLINK_CLI_TC_H_
+
+#include <netlink/route/tc.h>
+
+struct rtnl_tc_ops;
+
+extern void nl_cli_tc_parse_dev(struct rtnl_tc *, struct nl_cache *, char *);
+extern void nl_cli_tc_parse_parent(struct rtnl_tc *, char *);
+extern void nl_cli_tc_parse_handle(struct rtnl_tc *, char *, int);
+extern void nl_cli_tc_parse_mtu(struct rtnl_tc *, char *);
+extern void nl_cli_tc_parse_mpu(struct rtnl_tc *, char *);
+extern void nl_cli_tc_parse_overhead(struct rtnl_tc *, char *);
+extern void nl_cli_tc_parse_linktype(struct rtnl_tc *, char *);
+extern void nl_cli_tc_parse_kind(struct rtnl_tc *, char *);
+
+struct nl_cli_tc_module
+{
+ const char * tm_name;
+ enum rtnl_tc_type tm_type;
+ struct rtnl_tc_ops * tm_ops;
+ void (*tm_parse_argv)(struct rtnl_tc *, int, char **);
+ struct nl_list_head tm_list;
+};
+
+extern struct nl_cli_tc_module *nl_cli_tc_lookup(struct rtnl_tc_ops *);
+extern void nl_cli_tc_register(struct nl_cli_tc_module *);
+extern void nl_cli_tc_unregister(struct nl_cli_tc_module *);
+
+#endif
diff --git a/include/netlink/cli/utils.h b/include/netlink/cli/utils.h
index 2a232082..da41c10e 100644
--- a/include/netlink/cli/utils.h
+++ b/include/netlink/cli/utils.h
@@ -73,6 +73,8 @@ extern int nl_cli_confirm(struct nl_object *,
extern struct nl_cache *nl_cli_alloc_cache(struct nl_sock *, const char *,
int (*ac)(struct nl_sock *, struct nl_cache **));
+extern void nl_cli_load_module(const char *, const char *);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/netlink/errno.h b/include/netlink/errno.h
index c8a376e6..f8b5130c 100644
--- a/include/netlink/errno.h
+++ b/include/netlink/errno.h
@@ -46,8 +46,12 @@ extern "C" {
#define NLE_NOACCESS 27
#define NLE_PERM 28
#define NLE_PKTLOC_FILE 29
+#define NLE_PARSE_ERR 30
+#define NLE_NODEV 31
+#define NLE_IMMUTABLE 32
+#define NLE_DUMP_INTR 33
-#define NLE_MAX NLE_PKTLOC_FILE
+#define NLE_MAX NLE_DUMP_INTR
extern const char * nl_geterror(int);
extern void nl_perror(int, const char *);
diff --git a/include/netlink/genl/ctrl.h b/include/netlink/genl/ctrl.h
index 1ae62f44..017b8fdf 100644
--- a/include/netlink/genl/ctrl.h
+++ b/include/netlink/genl/ctrl.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_GENL_CTRL_H_
@@ -29,6 +29,9 @@ extern struct genl_family * genl_ctrl_search_by_name(struct nl_cache *,
const char *);
extern int genl_ctrl_resolve(struct nl_sock *,
const char *);
+extern int genl_ctrl_resolve_grp(struct nl_sock *sk,
+ const char *family,
+ const char *grp);
#ifdef __cplusplus
}
diff --git a/include/netlink/genl/family.h b/include/netlink/genl/family.h
index 74319e59..5432b594 100644
--- a/include/netlink/genl/family.h
+++ b/include/netlink/genl/family.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_GENL_FAMILY_H_
@@ -25,23 +25,20 @@ 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 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 void genl_family_set_name(struct genl_family *, const char *);
extern uint8_t genl_family_get_version(struct genl_family *);
-extern void genl_family_set_version(struct genl_family *,
- uint8_t);
+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 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 void genl_family_set_maxattr(struct genl_family *, uint32_t);
+
+extern int genl_family_add_op(struct genl_family *, int, int);
+extern int genl_family_add_grp(struct genl_family *, uint32_t ,
+ const char *);
-extern int genl_family_add_op(struct genl_family *,
- int, int);
#ifdef __cplusplus
}
diff --git a/include/netlink/genl/genl.h b/include/netlink/genl/genl.h
index 3f3340cf..e455581b 100644
--- a/include/netlink/genl/genl.h
+++ b/include/netlink/genl/genl.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_GENL_H_
@@ -21,7 +21,6 @@ extern "C" {
#endif
extern int genl_connect(struct nl_sock *);
-
extern int genl_send_simple(struct nl_sock *, int, int,
int, int);
@@ -33,7 +32,13 @@ extern int genlmsg_validate(struct nlmsghdr *, int, int,
struct nla_policy *);
extern int genlmsg_parse(struct nlmsghdr *, int, struct nlattr **,
int, struct nla_policy *);
+extern struct genlmsghdr *
+ genlmsg_hdr(struct nlmsghdr *);
extern void * genlmsg_data(const struct genlmsghdr *);
+extern void * genlmsg_user_hdr(const struct genlmsghdr *);
+extern void * genlmsg_user_data(const struct genlmsghdr *, const int);
+extern int genlmsg_user_datalen(const struct genlmsghdr *,
+ const int);
extern int genlmsg_len(const struct genlmsghdr *);
extern struct nlattr * genlmsg_attrdata(const struct genlmsghdr *, int);
extern int genlmsg_attrlen(const struct genlmsghdr *, int);
diff --git a/include/netlink/genl/mngt.h b/include/netlink/genl/mngt.h
index 8b0244f2..8a51ccdd 100644
--- a/include/netlink/genl/mngt.h
+++ b/include/netlink/genl/mngt.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_GENL_MNGT_H_
@@ -22,64 +22,153 @@ extern "C" {
struct nl_cache_ops;
+/**
+ * @ingroup genl_mngt
+ * @struct genl_info netlink/genl/mngt.h
+ *
+ * Informative structure passed on to message parser callbacks
+ *
+ * This structure is passed on to all message parser callbacks and contains
+ * information about the sender of the message as well as pointers to all
+ * relevant sections of the parsed message.
+ *
+ * @see genl_cmd::c_msg_parser
+ */
struct genl_info
{
+ /** Socket address of sender */
struct sockaddr_nl * who;
+
+ /** Pointer to Netlink message header */
struct nlmsghdr * nlh;
+
+ /** Pointer to Generic Netlink message header */
struct genlmsghdr * genlhdr;
+
+ /** Pointer to user header */
void * userhdr;
+
+ /** Pointer to array of parsed attributes */
struct nlattr ** attrs;
};
/**
* @ingroup genl_mngt
- * Generic Netlink Command
+ * @struct genl_cmd netlink/genl/mngt.h
+ *
+ * Definition of a Generic Netlink command.
+ *
+ * This structure is used to define the list of available commands on the
+ * receiving side.
+ *
+ * @par Example:
+ * @code
+ * 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" ,
+ * },
+ * };
+ *
+ * static struct genl_ops my_genl_ops = {
+ * [...]
+ * .o_cmds = foo_cmds,
+ * .o_ncmds = ARRAY_SIZE(foo_cmds),
+ * };
+ * @endcode
*/
struct genl_cmd
{
- /** Unique command identifier */
+ /** Numeric command identifier (required) */
int c_id;
- /** Name/description of command */
+ /** Human readable name (required) */
char * c_name;
- /**
- * Maximum attribute identifier, must be provided if
- * a message parser is available.
- */
+ /** Maximum attribute identifier that the command is prepared to handle. */
int c_maxattr;
+ /** Called whenever a message for this command is received */
int (*c_msg_parser)(struct nl_cache_ops *,
struct genl_cmd *,
struct genl_info *, void *);
- /**
- * Attribute validation policy (optional)
- */
+ /** Attribute validation policy, enforced before the callback is called */
struct nla_policy * c_attr_policy;
};
/**
* @ingroup genl_mngt
- * Generic Netlink Operations
+ * @struct genl_ops netlink/genl/mngt.h
+ *
+ * Definition of a Generic Netlink family
+ *
+ * @par Example:
+ * @code
+ * static struct genl_cmd foo_cmds[] = {
+ * [...]
+ * };
+ *
+ * static struct genl_ops my_genl_ops = {
+ * .o_name = "foo",
+ * .o_hdrsize = sizeof(struct my_hdr),
+ * .o_cmds = foo_cmds,
+ * .o_ncmds = ARRAY_SIZE(foo_cmds),
+ * };
+ *
+ * if ((err = genl_register_family(&my_genl_ops)) < 0)
+ * // ERROR
+ * @endcode
+ *
+ * @see genl_cmd
*/
struct genl_ops
{
- int o_family;
+ /** Length of user header */
+ unsigned int o_hdrsize;
+
+ /** Numeric identifier, automatically filled in by genl_ops_resolve() */
int o_id;
+
+ /** Human readable name, used by genl_ops_resolve() to resolve numeric id */
char * o_name;
+
+ /**
+ * If registered via genl_register(), will point to the related
+ * cache operations.
+ */
struct nl_cache_ops * o_cache_ops;
+
+ /** Optional array defining the available Generic Netlink commands */
struct genl_cmd * o_cmds;
+
+ /** Number of elements in \c o_cmds array */
int o_ncmds;
- /* linked list of all genl cache operations */
+ /**
+ * @private
+ * Used internally to link together all registered operations.
+ */
struct nl_list_head o_list;
};
+extern int genl_register_family(struct genl_ops *);
+extern int genl_unregister_family(struct genl_ops *);
+extern int genl_handle_msg(struct nl_msg *, void *);
extern int genl_register(struct nl_cache_ops *);
extern void genl_unregister(struct nl_cache_ops *);
+extern int genl_ops_resolve(struct nl_sock *, struct genl_ops *);
+extern int genl_mngt_resolve(struct nl_sock *);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/netlink/handlers.h b/include/netlink/handlers.h
index f373f58e..e94cd348 100644
--- a/include/netlink/handlers.h
+++ b/include/netlink/handlers.h
@@ -108,6 +108,8 @@ enum nl_cb_type {
NL_CB_SEQ_CHECK,
/** Sending of an acknowledge message has been requested */
NL_CB_SEND_ACK,
+ /** Flag NLM_F_DUMP_INTR is set in message */
+ NL_CB_DUMP_INTR,
__NL_CB_TYPE_MAX,
};
@@ -137,6 +139,8 @@ extern void nl_cb_overwrite_send(struct nl_cb *,
int (*func)(struct nl_sock *,
struct nl_msg *));
+extern enum nl_cb_type nl_cb_active_type(struct nl_cb *cb);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/netlink/hash.h b/include/netlink/hash.h
new file mode 100644
index 00000000..0bda74ea
--- /dev/null
+++ b/include/netlink/hash.h
@@ -0,0 +1,69 @@
+/*
+ * This file was taken from http://ccodearchive.net/info/hash.html
+ * Changes to the original file include cleanups and removal of unwanted code
+ * and also code that depended on build_asert
+ */
+#ifndef CCAN_HASH_H
+#define CCAN_HASH_H
+#include <stdint.h>
+#include <stdlib.h>
+#include <endian.h>
+
+/* Stolen mostly from: lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+ *
+ * http://burtleburtle.net/bob/c/lookup3.c
+ */
+
+#ifdef __LITTLE_ENDIAN
+# define HAVE_LITTLE_ENDIAN 1
+#elif __BIG_ENDIAN
+# define HAVE_BIG_ENDIAN 1
+#else
+#error Unknown endianness. Failure in endian.h
+#endif
+
+/**
+ * hash - fast hash of an array for internal use
+ * @p: the array or pointer to first element
+ * @num: the number of elements to hash
+ * @base: the base number to roll into the hash (usually 0)
+ *
+ * The memory region pointed to by p is combined with the base to form
+ * a 32-bit hash.
+ *
+ * This hash will have different results on different machines, so is
+ * only useful for internal hashes (ie. not hashes sent across the
+ * network or saved to disk).
+ *
+ * It may also change with future versions: it could even detect at runtime
+ * what the fastest hash to use is.
+ *
+ * See also: hash64, hash_stable.
+ *
+ * Example:
+ * #include <ccan/hash/hash.h>
+ * #include <err.h>
+ * #include <stdio.h>
+ * #include <string.h>
+ *
+ * // Simple demonstration: idential strings will have the same hash, but
+ * // two different strings will probably not.
+ * int main(int argc, char *argv[])
+ * {
+ * uint32_t hash1, hash2;
+ *
+ * if (argc != 3)
+ * err(1, "Usage: %s <string1> <string2>", argv[0]);
+ *
+ * hash1 = __nl_hash(argv[1], strlen(argv[1]), 0);
+ * hash2 = __nl_hash(argv[2], strlen(argv[2]), 0);
+ * printf("Hash is %s\n", hash1 == hash2 ? "same" : "different");
+ * return 0;
+ * }
+ */
+#define __nl_hash(p, num, base) nl_hash_any((p), (num)*sizeof(*(p)), (base))
+
+/* Our underlying operations. */
+uint32_t nl_hash_any(const void *key, size_t length, uint32_t base);
+
+#endif /* HASH_H */
diff --git a/include/netlink/hashtable.h b/include/netlink/hashtable.h
new file mode 100644
index 00000000..d9e6ee45
--- /dev/null
+++ b/include/netlink/hashtable.h
@@ -0,0 +1,52 @@
+/*
+ * netlink/hashtable.h Netlink hashtable 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) 2012 Cumulus Networks, Inc
+ */
+
+#ifndef NETLINK_HASHTABLE_H_
+#define NETLINK_HASHTABLE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct nl_hash_node {
+ uint32_t key;
+ uint32_t key_size;
+ struct nl_object * obj;
+ struct nl_hash_node * next;
+} nl_hash_node_t;
+
+typedef struct nl_hash_table {
+ int size;
+ nl_hash_node_t ** nodes;
+} nl_hash_table_t;
+
+/* Default hash table size */
+#define NL_MAX_HASH_ENTRIES 1024
+
+/* Access Functions */
+extern nl_hash_table_t * nl_hash_table_alloc(int size);
+extern void nl_hash_table_free(nl_hash_table_t *ht);
+
+extern int nl_hash_table_add(nl_hash_table_t *ht,
+ struct nl_object *obj);
+extern int nl_hash_table_del(nl_hash_table_t *ht,
+ struct nl_object *obj);
+
+extern struct nl_object * nl_hash_table_lookup(nl_hash_table_t *ht,
+ struct nl_object *obj);
+extern uint32_t nl_hash(void *k, size_t length,
+ uint32_t initval);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* NETLINK_HASHTABLE_H_ */
diff --git a/include/netlink/idiag/idiagnl.h b/include/netlink/idiag/idiagnl.h
new file mode 100644
index 00000000..d7434cd7
--- /dev/null
+++ b/include/netlink/idiag/idiagnl.h
@@ -0,0 +1,126 @@
+/*
+ * netlink/idiag/idiagnl.h Inetdiag 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) 2013 Sassano Systems LLC <joe@sassanosystems.com>
+ */
+
+#ifndef NETLINK_IDIAGNL_H_
+#define NETLINK_IDIAGNL_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Inet Diag message types
+ */
+#define IDIAG_TCPDIAG_GETSOCK 18
+#define IDIAG_DCCPDIAG_GETSOCK 19
+#define IDIAG_GETSOCK_MAX 24
+
+/**
+ * Socket state identifiers
+ * @ingroup idiag
+ */
+enum {
+ IDIAG_SS_UNKNOWN,
+ IDIAG_SS_ESTABLISHED,
+ IDIAG_SS_SYN_SENT,
+ IDIAG_SS_SYN_RECV,
+ IDIAG_SS_FIN_WAIT1,
+ IDIAG_SS_FIN_WAIT2,
+ IDIAG_SS_TIME_WAIT,
+ IDIAG_SS_CLOSE,
+ IDIAG_SS_CLOSE_WAIT,
+ IDIAG_SS_LAST_ACK,
+ IDIAG_SS_LISTEN,
+ IDIAG_SS_CLOSING,
+ IDIAG_SS_MAX
+};
+
+/**
+ * Macro to represent all socket states.
+ * @ingroup idiag
+ */
+#define IDIAG_SS_ALL ((1<<IDIAG_SS_MAX)-1)
+
+/**
+ * Inet Diag extended attributes
+ * @ingroup idiag
+ */
+enum {
+ IDIAG_ATTR_NONE,
+ IDIAG_ATTR_MEMINFO,
+ IDIAG_ATTR_INFO,
+ IDIAG_ATTR_VEGASINFO,
+ IDIAG_ATTR_CONG,
+ IDIAG_ATTR_TOS,
+ IDIAG_ATTR_TCLASS,
+ IDIAG_ATTR_SKMEMINFO,
+ IDIAG_ATTR_SHUTDOWN,
+ IDIAG_ATTR_MAX,
+};
+
+/**
+ * Macro to represent all socket attributes.
+ * @ingroup idiag
+ */
+#define IDIAG_ATTR_ALL ((1<<IDIAG_ATTR_MAX)-1)
+
+/**
+ * Socket memory info identifiers
+ * @ingroup idiag
+ */
+enum {
+ IDIAG_SK_MEMINFO_RMEM_ALLOC,
+ IDIAG_SK_MEMINFO_RCVBUF,
+ IDIAG_SK_MEMINFO_WMEM_ALLOC,
+ IDIAG_SK_MEMINFO_SNDBUF,
+ IDIAG_SK_MEMINFO_FWD_ALLOC,
+ IDIAG_SK_MEMINFO_WMEM_QUEUED,
+ IDIAG_SK_MEMINFO_OPTMEM,
+ IDIAG_SK_MEMINFO_BACKLOG,
+
+ IDIAG_SK_MEMINFO_VARS,
+};
+
+/**
+ * Socket timer indentifiers
+ * @ingroupd idiag
+ */
+enum {
+ IDIAG_TIMER_OFF,
+ IDIAG_TIMER_ON,
+ IDIAG_TIMER_KEEPALIVE,
+ IDIAG_TIMER_TIMEWAIT,
+ IDIAG_TIMER_PERSIST,
+ IDIAG_TIMER_UNKNOWN,
+};
+
+extern char * idiagnl_state2str(int, char *, size_t);
+extern int idiagnl_str2state(const char *);
+
+extern int idiagnl_connect(struct nl_sock *);
+extern int idiagnl_send_simple(struct nl_sock *, int, uint8_t, uint16_t,
+ uint16_t);
+
+extern char * idiagnl_timer2str(int, char *, size_t);
+extern int idiagnl_str2timer(const char *);
+extern char * idiagnl_attrs2str(int, char *, size_t);
+extern char * idiagnl_tcpstate2str(uint8_t, char *, size_t);
+extern char * idiagnl_tcpopts2str(uint8_t, char *, size_t);
+extern char * idiagnl_shutdown2str(uint8_t, char *, size_t);
+extern char * idiagnl_exts2str(uint8_t, char *, size_t);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* NETLINK_IDIAGNL_H_ */
diff --git a/include/netlink/idiag/meminfo.h b/include/netlink/idiag/meminfo.h
new file mode 100644
index 00000000..19223953
--- /dev/null
+++ b/include/netlink/idiag/meminfo.h
@@ -0,0 +1,41 @@
+/*
+ * netlink/idiag/meminfo.h Inetdiag Netlink Memory Info
+ *
+ * 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) 2013 Sassano Systems LLC <joe@sassanosystems.com>
+ */
+
+#ifndef NETLINK_IDIAGNL_MEMINFO_H_
+#define NETLINK_IDIAGNL_MEMINFO_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+extern struct nl_object_ops idiagnl_meminfo_obj_ops;
+
+extern struct idiagnl_meminfo *idiagnl_meminfo_alloc(void);
+extern void idiagnl_meminfo_get(struct idiagnl_meminfo *);
+extern void idiagnl_meminfo_put(struct idiagnl_meminfo *);
+
+extern uint32_t idiagnl_meminfo_get_rmem(const struct idiagnl_meminfo *);
+extern uint32_t idiagnl_meminfo_get_wmem(const struct idiagnl_meminfo *);
+extern uint32_t idiagnl_meminfo_get_fmem(const struct idiagnl_meminfo *);
+extern uint32_t idiagnl_meminfo_get_tmem(const struct idiagnl_meminfo *);
+
+extern void idiagnl_meminfo_set_rmem(struct idiagnl_meminfo *, uint32_t);
+extern void idiagnl_meminfo_set_wmem(struct idiagnl_meminfo *, uint32_t);
+extern void idiagnl_meminfo_set_fmem(struct idiagnl_meminfo *, uint32_t);
+extern void idiagnl_meminfo_set_tmem(struct idiagnl_meminfo *, uint32_t);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* NETLINK_IDIAGNL_MEMINFO_H_ */
diff --git a/include/netlink/idiag/msg.h b/include/netlink/idiag/msg.h
new file mode 100644
index 00000000..4aae606f
--- /dev/null
+++ b/include/netlink/idiag/msg.h
@@ -0,0 +1,83 @@
+/*
+ * netlink/idiag/msg.h Inetdiag Netlink Message
+ *
+ * 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) 2013 Sassano Systems LLC <joe@sassanosystems.com>
+ */
+
+#ifndef NETLINK_IDIAGNL_MSG_H_
+#define NETLINK_IDIAGNL_MSG_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct idiagnl_msg;
+extern struct nl_object_ops idiagnl_msg_obj_ops;
+
+extern struct idiagnl_msg * idiagnl_msg_alloc(void);
+extern int idiagnl_msg_alloc_cache(struct nl_sock *, int, int,
+ struct nl_cache**);
+extern void idiagnl_msg_get(struct idiagnl_msg *);
+extern void idiagnl_msg_put(struct idiagnl_msg *);
+extern uint8_t idiagnl_msg_get_family(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_family(struct idiagnl_msg *, uint8_t);
+extern uint8_t idiagnl_msg_get_state(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_state(struct idiagnl_msg *, uint8_t);
+extern uint8_t idiagnl_msg_get_timer(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_timer(struct idiagnl_msg *, uint8_t);
+extern uint8_t idiagnl_msg_get_retrans(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_retrans(struct idiagnl_msg *, uint8_t);
+extern uint16_t idiagnl_msg_get_sport(struct idiagnl_msg *);
+extern void idiagnl_msg_set_sport(struct idiagnl_msg *, uint16_t);
+extern uint16_t idiagnl_msg_get_dport(struct idiagnl_msg *);
+extern void idiagnl_msg_set_dport(struct idiagnl_msg *, uint16_t);
+extern struct nl_addr * idiagnl_msg_get_src(const struct idiagnl_msg *);
+extern int idiagnl_msg_set_src(struct idiagnl_msg *,
+ struct nl_addr *);
+extern struct nl_addr * idiagnl_msg_get_dst(const struct idiagnl_msg *);
+extern int idiagnl_msg_set_dst(struct idiagnl_msg *,
+ struct nl_addr *);
+extern uint32_t idiagnl_msg_get_ifindex(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_ifindex(struct idiagnl_msg *, uint32_t);
+extern uint32_t idiagnl_msg_get_expires(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_expires(struct idiagnl_msg *, uint32_t);
+extern uint32_t idiagnl_msg_get_rqueue(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_rqueue(struct idiagnl_msg *, uint32_t);
+extern uint32_t idiagnl_msg_get_wqueue(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_wqueue(struct idiagnl_msg *, uint32_t);
+extern uint32_t idiagnl_msg_get_uid(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_uid(struct idiagnl_msg *, uint32_t);
+extern uint32_t idiagnl_msg_get_inode(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_inode(struct idiagnl_msg *, uint32_t);
+extern uint8_t idiagnl_msg_get_tos(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_tos(struct idiagnl_msg *, uint8_t);
+extern uint8_t idiagnl_msg_get_tclass(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_tclass(struct idiagnl_msg *, uint8_t);
+extern uint8_t idiagnl_msg_get_shutdown(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_shutdown(struct idiagnl_msg *, uint8_t);
+extern char * idiagnl_msg_get_cong(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_cong(struct idiagnl_msg *, char *);
+extern struct idiagnl_meminfo *idiagnl_msg_get_meminfo(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_meminfo(struct idiagnl_msg *,
+ struct idiagnl_meminfo *);
+extern struct idiagnl_vegasinfo *idiagnl_msg_get_vegasinfo(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_vegasinfo(struct idiagnl_msg *,
+ struct idiagnl_vegasinfo *);
+extern struct tcp_info idiagnl_msg_get_tcpinfo(const struct idiagnl_msg *);
+extern void idiagnl_msg_set_tcpinfo(struct idiagnl_msg *,
+ struct tcp_info *);
+
+extern int idiagnl_msg_parse(struct nlmsghdr *,
+ struct idiagnl_msg **);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* NETLINK_IDIAGNL_MSG_H_ */
diff --git a/include/netlink/idiag/req.h b/include/netlink/idiag/req.h
new file mode 100644
index 00000000..3c9f8ace
--- /dev/null
+++ b/include/netlink/idiag/req.h
@@ -0,0 +1,50 @@
+/*
+ * netlink/idiag/req.h Inetdiag Netlink 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) 2013 Sassano Systems LLC <joe@sassanosystems.com>
+ */
+
+#ifndef NETLINK_IDIAGNL_REQ_H_
+#define NETLINK_IDIAGNL_REQ_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+struct idiagnl_req;
+extern struct nl_object_ops idiagnl_req_obj_ops;
+
+extern struct idiagnl_req * idiagnl_req_alloc(void);
+extern void idiagnl_req_get(struct idiagnl_req *);
+extern void idiagnl_req_put(struct idiagnl_req *);
+extern uint8_t idiagnl_req_get_family(const struct idiagnl_req *);
+extern void idiagnl_req_set_family(struct idiagnl_req *,
+ uint8_t);
+extern uint8_t idiagnl_req_get_ext(const struct idiagnl_req *);
+extern void idiagnl_req_set_ext(struct idiagnl_req *, uint8_t);
+extern uint32_t idiagnl_req_get_ifindex(const struct idiagnl_req *);
+extern void idiagnl_req_set_ifindex(struct idiagnl_req *,
+ uint32_t);
+extern uint32_t idiagnl_req_get_states(const struct idiagnl_req *);
+extern void idiagnl_req_set_states(struct idiagnl_req *,
+ uint32_t);
+extern uint32_t idiagnl_req_get_dbs(const struct idiagnl_req *);
+extern void idiagnl_req_set_dbs(struct idiagnl_req *, uint32_t);
+extern struct nl_addr * idiagnl_req_get_src(const struct idiagnl_req *);
+extern int idiagnl_req_set_src(struct idiagnl_req *,
+ struct nl_addr *);
+extern struct nl_addr * idiagnl_req_get_dst(const struct idiagnl_req *);
+extern int idiagnl_req_set_dst(struct idiagnl_req *,
+ struct nl_addr *);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* NETLINK_IDIAGNL_REQ_H_ */
diff --git a/include/netlink/idiag/vegasinfo.h b/include/netlink/idiag/vegasinfo.h
new file mode 100644
index 00000000..792b5c16
--- /dev/null
+++ b/include/netlink/idiag/vegasinfo.h
@@ -0,0 +1,43 @@
+/*
+ * netlink/idiag/vegasinfo.h Inetdiag Netlink TCP Vegas Info
+ *
+ * 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) 2013 Sassano Systems LLC <joe@sassanosystems.com>
+ */
+
+#ifndef NETLINK_IDIAGNL_VEGASINFO_H_
+#define NETLINK_IDIAGNL_VEGASINFO_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+extern struct nl_object_ops idiagnl_vegasinfo_obj_ops;
+extern struct idiagnl_vegasinfo * idiagnl_vegasinfo_alloc(void);
+extern void idiagnl_vegasinfo_get(struct idiagnl_vegasinfo *);
+extern void idiagnl_vegasinfo_put(struct idiagnl_vegasinfo *);
+
+extern uint32_t idiagnl_vegasinfo_get_enabled(const struct idiagnl_vegasinfo *);
+extern uint32_t idiagnl_vegasinfo_get_rttcnt(const struct idiagnl_vegasinfo *);
+extern uint32_t idiagnl_vegasinfo_get_rtt(const struct idiagnl_vegasinfo *);
+extern uint32_t idiagnl_vegasinfo_get_minrtt(const struct idiagnl_vegasinfo *);
+
+extern void idiagnl_vegasinfo_set_enabled(struct idiagnl_vegasinfo *,
+ uint32_t);
+extern void idiagnl_vegasinfo_set_rttcnt(struct idiagnl_vegasinfo *,
+ uint32_t);
+extern void idiagnl_vegasinfo_set_rtt(struct idiagnl_vegasinfo *, uint32_t);
+extern void idiagnl_vegasinfo_set_minrtt(struct idiagnl_vegasinfo *,
+ uint32_t);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* NETLINK_IDIAGNL_VEGASINFO_H_ */
diff --git a/include/netlink/msg.h b/include/netlink/msg.h
index e331f421..f3d50ae2 100644
--- a/include/netlink/msg.h
+++ b/include/netlink/msg.h
@@ -25,18 +25,21 @@ extern "C" {
/**
* @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()!
+ * Will cause the netlink port to be set to the port assigned to
+ * the netlink icoket ust before sending the message off.
+ *
+ * @note Requires the use of nl_send_auto()!
*/
-#define NL_AUTO_PID 0
+#define NL_AUTO_PORT 0
+#define NL_AUTO_PID NL_AUTO_PORT
/**
* @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()!
+ *
+ * @note Requires the use of nl_send_auto()!
*/
#define NL_AUTO_SEQ 0
@@ -44,15 +47,13 @@ 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);
+extern int nlmsg_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 *);
+extern void * nlmsg_data(const struct nlmsghdr *);
+extern int nlmsg_datalen(const struct nlmsghdr *);
+extern void * nlmsg_tail(const struct nlmsghdr *);
/* attribute access */
extern struct nlattr * nlmsg_attrdata(const struct nlmsghdr *, int);
@@ -128,12 +129,14 @@ extern void nl_msg_dump(struct nl_msg *, FILE *);
* @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(pos, head, len) \
+ for (int rem = len, pos = head; \
+ nlmsg_ok(pos, rem); \
+ pos = nlmsg_next(pos, &rem))
+
#define nlmsg_for_each_msg(pos, head, len, rem) \
- for (pos = head, rem = len; \
- nlmsg_ok(pos, rem); \
- pos = nlmsg_next(pos, &(rem)))
+ nlmsg_for_each(pos, head, len)
/** @} */
diff --git a/include/netlink/netfilter/ct.h b/include/netlink/netfilter/ct.h
index c4402b3b..ef5d0355 100644
--- a/include/netlink/netfilter/ct.h
+++ b/include/netlink/netfilter/ct.h
@@ -25,6 +25,11 @@ extern "C" {
struct nfnl_ct;
+struct nfnl_ct_timestamp {
+ uint64_t start;
+ uint64_t stop;
+};
+
extern struct nl_object_ops ct_obj_ops;
extern struct nfnl_ct * nfnl_ct_alloc(void);
@@ -44,7 +49,7 @@ extern int nfnl_ct_add(struct nl_sock *, const struct nfnl_ct *, int);
extern int nfnl_ct_build_delete_request(const struct nfnl_ct *, int,
struct nl_msg **);
-extern int nfnl_ct_delete(struct nl_sock *, const struct nfnl_ct *, int);
+extern int nfnl_ct_del(struct nl_sock *, const struct nfnl_ct *, int);
extern int nfnl_ct_build_query_request(const struct nfnl_ct *, int,
struct nl_msg **);
@@ -65,6 +70,7 @@ extern int nfnl_ct_str2tcp_state(const char *name);
extern void nfnl_ct_set_status(struct nfnl_ct *, uint32_t);
extern void nfnl_ct_unset_status(struct nfnl_ct *, uint32_t);
+extern int nfnl_ct_test_status(const struct nfnl_ct *ct);
extern uint32_t nfnl_ct_get_status(const struct nfnl_ct *);
extern char * nfnl_ct_status2str(int, char *, size_t);
extern int nfnl_ct_str2status(const char *);
@@ -85,6 +91,10 @@ 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 void nfnl_ct_set_zone(struct nfnl_ct *, uint16_t);
+extern int nfnl_ct_test_zone(const struct nfnl_ct *);
+extern uint16_t nfnl_ct_get_zone(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);
@@ -119,6 +129,10 @@ 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);
+extern void nfnl_ct_set_timestamp(struct nfnl_ct *, uint64_t, uint64_t);
+extern int nfnl_ct_test_timestamp(const struct nfnl_ct *);
+extern const struct nfnl_ct_timestamp *nfnl_ct_get_timestamp(const struct nfnl_ct *);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/netlink/netfilter/exp.h b/include/netlink/netfilter/exp.h
new file mode 100644
index 00000000..4e950147
--- /dev/null
+++ b/include/netlink/netfilter/exp.h
@@ -0,0 +1,129 @@
+/*
+ * netlink/netfilter/exp.h Conntrack Expectation
+ *
+ * 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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ * Copyright (c) 2012 Rich Fought <rich.fought@watchguard.com>
+ */
+
+#ifndef NETLINK_EXP_H_
+#define NETLINK_EXP_H_
+
+#include <netlink/netlink.h>
+#include <netlink/addr.h>
+#include <netlink/cache.h>
+#include <netlink/msg.h>
+
+#include <linux/version.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nfnl_exp;
+
+enum nfnl_exp_tuples {
+ NFNL_EXP_TUPLE_EXPECT,
+ NFNL_EXP_TUPLE_MASTER,
+ NFNL_EXP_TUPLE_MASK,
+ NFNL_EXP_TUPLE_NAT,
+ NFNL_EXP_TUPLE_MAX
+};
+
+extern struct nl_object_ops exp_obj_ops;
+
+extern struct nfnl_exp * nfnl_exp_alloc(void);
+extern int nfnl_exp_alloc_cache(struct nl_sock *, struct nl_cache **);
+
+extern int nfnlmsg_exp_group(struct nlmsghdr *);
+extern int nfnlmsg_exp_parse(struct nlmsghdr *, struct nfnl_exp **);
+
+extern void nfnl_exp_get(struct nfnl_exp *);
+extern void nfnl_exp_put(struct nfnl_exp *);
+
+extern int nfnl_exp_dump_request(struct nl_sock *);
+
+extern int nfnl_exp_build_add_request(const struct nfnl_exp *, int,
+ struct nl_msg **);
+extern int nfnl_exp_add(struct nl_sock *, const struct nfnl_exp *, int);
+
+extern int nfnl_exp_build_delete_request(const struct nfnl_exp *, int,
+ struct nl_msg **);
+extern int nfnl_exp_del(struct nl_sock *, const struct nfnl_exp *, int);
+
+extern int nfnl_exp_build_query_request(const struct nfnl_exp *, int,
+ struct nl_msg **);
+extern int nfnl_exp_query(struct nl_sock *, const struct nfnl_exp *, int);
+
+extern void nfnl_exp_set_family(struct nfnl_exp *, uint8_t);
+extern uint8_t nfnl_exp_get_family(const struct nfnl_exp *);
+
+extern void nfnl_exp_set_timeout(struct nfnl_exp *, uint32_t);
+extern int nfnl_exp_test_timeout(const struct nfnl_exp *);
+extern uint32_t nfnl_exp_get_timeout(const struct nfnl_exp *);
+
+extern void nfnl_exp_set_id(struct nfnl_exp *, uint32_t);
+extern int nfnl_exp_test_id(const struct nfnl_exp *);
+extern uint32_t nfnl_exp_get_id(const struct nfnl_exp *);
+
+extern int nfnl_exp_set_helper_name(struct nfnl_exp *, void *);
+extern int nfnl_exp_test_helper_name(const struct nfnl_exp *);
+extern const char * nfnl_exp_get_helper_name(const struct nfnl_exp *);
+
+extern void nfnl_exp_set_zone(struct nfnl_exp *, uint16_t);
+extern int nfnl_exp_test_zone(const struct nfnl_exp *);
+extern uint16_t nfnl_exp_get_zone(const struct nfnl_exp *);
+
+extern void nfnl_exp_set_flags(struct nfnl_exp *, uint32_t);
+extern int nfnl_exp_test_flags(const struct nfnl_exp *);
+extern uint32_t nfnl_exp_get_flags(const struct nfnl_exp *);
+
+extern void nfnl_exp_set_class(struct nfnl_exp *, uint32_t);
+extern int nfnl_exp_test_class(const struct nfnl_exp *);
+extern uint32_t nfnl_exp_get_class(const struct nfnl_exp *);
+
+extern int nfnl_exp_set_fn(struct nfnl_exp *, void *);
+extern int nfnl_exp_test_fn(const struct nfnl_exp *);
+extern const char * nfnl_exp_get_fn(const struct nfnl_exp *);
+
+extern void nfnl_exp_set_nat_dir(struct nfnl_exp *, uint8_t);
+extern int nfnl_exp_test_nat_dir(const struct nfnl_exp *);
+extern uint8_t nfnl_exp_get_nat_dir(const struct nfnl_exp *);
+
+// The int argument specifies which nfnl_exp_dir (expect, master, mask or nat)
+// Expectation objects only use orig, not reply
+
+extern int nfnl_exp_set_src(struct nfnl_exp *, int, struct nl_addr *);
+extern int nfnl_exp_test_src(const struct nfnl_exp *, int);
+extern struct nl_addr * nfnl_exp_get_src(const struct nfnl_exp *, int);
+
+extern int nfnl_exp_set_dst(struct nfnl_exp *, int, struct nl_addr *);
+extern int nfnl_exp_test_dst(const struct nfnl_exp *, int);
+extern struct nl_addr * nfnl_exp_get_dst(const struct nfnl_exp *, int);
+
+extern void nfnl_exp_set_l4protonum(struct nfnl_exp *, int, uint8_t);
+extern int nfnl_exp_test_l4protonum(const struct nfnl_exp *, int);
+extern uint8_t nfnl_exp_get_l4protonum(const struct nfnl_exp *, int);
+
+extern void nfnl_exp_set_ports(struct nfnl_exp *, int, uint16_t, uint16_t);
+extern int nfnl_exp_test_ports(const struct nfnl_exp *, int);
+extern uint16_t nfnl_exp_get_src_port(const struct nfnl_exp *, int);
+extern uint16_t nfnl_exp_get_dst_port(const struct nfnl_exp *, int);
+
+extern void nfnl_exp_set_icmp(struct nfnl_exp *, int, uint16_t, uint8_t, uint8_t);
+extern int nfnl_exp_test_icmp(const struct nfnl_exp *, int);
+extern uint16_t nfnl_exp_get_icmp_id(const struct nfnl_exp *, int);
+extern uint8_t nfnl_exp_get_icmp_type(const struct nfnl_exp *, int);
+extern uint8_t nfnl_exp_get_icmp_code(const struct nfnl_exp *, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/netfilter/queue_msg.h b/include/netlink/netfilter/queue_msg.h
index 24ed0818..9befee7c 100644
--- a/include/netlink/netfilter/queue_msg.h
+++ b/include/netlink/netfilter/queue_msg.h
@@ -93,6 +93,8 @@ extern unsigned int nfnl_queue_msg_get_verdict(const struct nfnl_queue_msg *);
extern struct nl_msg * nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *);
extern int nfnl_queue_msg_send_verdict(struct nl_sock *,
const struct nfnl_queue_msg *);
+extern int nfnl_queue_msg_send_verdict_batch(struct nl_sock *,
+ const struct nfnl_queue_msg *);
extern int nfnl_queue_msg_send_verdict_payload(struct nl_sock *,
const struct nfnl_queue_msg *,
const void *, unsigned );
diff --git a/include/netlink/netlink-kernel.h b/include/netlink/netlink-kernel.h
index a0f55356..f09051da 100644
--- a/include/netlink/netlink-kernel.h
+++ b/include/netlink/netlink-kernel.h
@@ -1,5 +1,13 @@
-#ifndef __LINUX_NETLINK_H
-#define __LINUX_NETLINK_H
+#ifndef __NETLINK_KERNEL_H_
+#define __NETLINK_KERNEL_H_
+
+#if 0
+
+/*
+ * FIXME: Goal is to preseve the documentation but make it simple
+ * to keep linux/netlink.h in sync. Maybe use named documentation
+ * sections.
+ */
/**
* Netlink socket address
@@ -21,34 +29,29 @@ struct sockaddr_nl
};
/**
+ * @addtogroup msg
+ * @{
+ */
+
+
+/**
* Netlink message header
- * @ingroup msg
*/
struct nlmsghdr
{
- /**
- * Length of message including header.
- */
+ /** Length of message including header and padding. */
uint32_t nlmsg_len;
- /**
- * Message type (content type)
- */
+ /** Message type (content type) */
uint16_t nlmsg_type;
- /**
- * Message flags
- */
+ /** Message flags */
uint16_t nlmsg_flags;
- /**
- * Sequence number
- */
+ /** Sequence number of message \see core_sk_seq_num. */
uint32_t nlmsg_seq;
- /**
- * Netlink PID of the proccess sending the message.
- */
+ /** Netlink port */
uint32_t nlmsg_pid;
};
@@ -60,7 +63,6 @@ struct nlmsghdr
/**
* Must be set on all request messages (typically from user space to
* kernel space).
- * @ingroup msg
*/
#define NLM_F_REQUEST 1
@@ -89,7 +91,6 @@ struct nlmsghdr
/**
* Return the complete table instead of a single entry.
- * @ingroup msg
*/
#define NLM_F_ROOT 0x100
@@ -119,7 +120,6 @@ struct nlmsghdr
/**
* Replace existing matching config object with this request.
- * @ingroup msg
*/
#define NLM_F_REPLACE 0x100
@@ -147,7 +147,6 @@ struct nlmsghdr
/**
* No operation, message must be ignored
- * @ingroup msg
*/
#define NLMSG_NOOP 0x1
@@ -176,8 +175,7 @@ struct nlmsghdr
/** @} */
/**
- * Netlink error message
- * @ingroup msg
+ * Netlink error message header
*/
struct nlmsgerr
{
@@ -193,4 +191,103 @@ struct nl_pktinfo
__u32 group;
};
+/**
+ * Netlink alignment constant, all boundries within messages must be align to this.
+ *
+ * See \ref core_msg_fmt_align for more information on message alignment.
+ */
+#define NLMSG_ALIGNTO 4
+
+/**
+ * Returns \p len properly aligned to NLMSG_ALIGNTO.
+ *
+ * See \ref core_msg_fmt_align for more information on message alignment.
+ */
+#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
+
+/**
+ * Length of a netlink message header including padding.
+ *
+ * See \ref core_msg_fmt_align for more information on message alignment.
+ */
+#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+
+/** @} */
+
+/**
+ * @addtogroup attr
+ * @{
+ */
+
+/*
+ */
+
+/**
+ * Netlink attribute structure
+ *
+ * @code
+ * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ * | Header | Pad | Payload | Pad |
+ * | (struct nlattr) | ing | | ing |
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ * <-------------- nlattr->nla_len -------------->
+ * @endcode
+ */
+struct nlattr {
+ /**
+ * Attribute length in bytes including header
+ */
+ __u16 nla_len;
+
+ /**
+ * Netlink attribute type
+ */
+ __u16 nla_type;
+};
+
+/**
+ * @name Attribute Type Flags
+ *
+ * @code
+ * nla_type (16 bits)
+ * +---+---+-------------------------------+
+ * | N | O | Attribute Type |
+ * +---+---+-------------------------------+
+ * N := Carries nested attributes
+ * O := Payload stored in network byte order
+ * @endcode
+ *
+ * @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
+
+/**
+ * Returns \p len properly aligned to NLA_ALIGNTO.
+ *
+ * See \ref core_msg_fmt_align for more information on message alignment.
+ */
+#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
+
+/**
+ * Length of a netlink attribute header including padding.
+ *
+ * See \ref core_msg_fmt_align for more information on message alignment.
+ */
+#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr)))
+
+/** @} */
+
+#endif
#endif /* __LINUX_NETLINK_H */
diff --git a/include/netlink/netlink.h b/include/netlink/netlink.h
index 1cfe220c..28dba06e 100644
--- a/include/netlink/netlink.h
+++ b/include/netlink/netlink.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_NETLINK_H_
@@ -26,17 +26,23 @@
#include <linux/rtnetlink.h>
#include <linux/genetlink.h>
#include <linux/netfilter/nfnetlink.h>
+#include <netinet/tcp.h>
#include <netlink/version.h>
#include <netlink/errno.h>
#include <netlink/types.h>
#include <netlink/handlers.h>
#include <netlink/socket.h>
+#include <netlink/object.h>
#ifdef __cplusplus
extern "C" {
#endif
struct ucred;
+struct nl_cache_ops;
+struct nl_parser_param;
+struct nl_object;
+struct nl_sock;
extern int nl_debug;
extern struct nl_dump_params nl_debug_dp;
@@ -52,10 +58,14 @@ extern int nl_sendmsg(struct nl_sock *, struct nl_msg *,
extern int nl_send(struct nl_sock *, struct nl_msg *);
extern int nl_send_iovec(struct nl_sock *, struct nl_msg *,
struct iovec *, unsigned);
+extern void nl_complete_msg(struct nl_sock *,
+ struct nl_msg *);
extern void nl_auto_complete(struct nl_sock *,
- struct nl_msg *);
+ struct nl_msg *);
+extern int nl_send_auto(struct nl_sock *, struct nl_msg *);
extern int nl_send_auto_complete(struct nl_sock *,
struct nl_msg *);
+extern int nl_send_sync(struct nl_sock *, struct nl_msg *);
extern int nl_send_simple(struct nl_sock *, int, int,
void *, size_t);
@@ -65,11 +75,18 @@ extern int nl_recv(struct nl_sock *,
struct ucred **);
extern int nl_recvmsgs(struct nl_sock *, struct nl_cb *);
+extern int nl_recvmsgs_report(struct nl_sock *, struct nl_cb *);
extern int nl_recvmsgs_default(struct nl_sock *);
extern int nl_wait_for_ack(struct nl_sock *);
+extern int nl_pickup(struct nl_sock *,
+ int (*parser)(struct nl_cache_ops *,
+ struct sockaddr_nl *,
+ struct nlmsghdr *,
+ struct nl_parser_param *),
+ struct nl_object **);
/* Netlink Family Translations */
extern char * nl_nlfamily2str(int, char *, size_t);
extern int nl_str2nlfamily(const char *);
diff --git a/include/netlink/object-api.h b/include/netlink/object-api.h
index b3337f00..75f29cb5 100644
--- a/include/netlink/object-api.h
+++ b/include/netlink/object-api.h
@@ -1,342 +1,19 @@
/*
- * netlink/object-api.c Object API
+ * netlink/object-api.h Object 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-2007 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2013 Thomas Graf <tgraf@suug.ch>
*/
-#ifndef NETLINK_OBJECT_API_H_
-#define NETLINK_OBJECT_API_H_
+#ifndef NETLINK_DUMMY_OBJECT_API_H_
+#define NETLINK_DUMMY_OBJECT_API_H_
#include <netlink/netlink.h>
#include <netlink/utils.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @ingroup object
- * @defgroup object_api Object API
- * @brief
- *
- * @par 1) Object Definition
- * @code
- * // Define your object starting with the common object header
- * struct my_obj {
- * NLHDR_COMMON
- * int my_data;
- * };
- *
- * // Fill out the object operations structure
- * struct nl_object_ops my_ops = {
- * .oo_name = "my_obj",
- * .oo_size = sizeof(struct my_obj),
- * };
- *
- * // At this point the object can be allocated, you may want to provide a
- * // separate _alloc() function to ease allocting objects of this kind.
- * struct nl_object *obj = nl_object_alloc(&my_ops);
- *
- * // And release it again...
- * nl_object_put(obj);
- * @endcode
- *
- * @par 2) Allocating additional data
- * @code
- * // You may require to allocate additional data and store it inside
- * // object, f.e. assuming there is a field `ptr'.
- * struct my_obj {
- * NLHDR_COMMON
- * void * ptr;
- * };
- *
- * // And at some point you may assign allocated data to this field:
- * my_obj->ptr = calloc(1, ...);
- *
- * // In order to not introduce any memory leaks you have to release
- * // this data again when the last reference is given back.
- * static void my_obj_free_data(struct nl_object *obj)
- * {
- * struct my_obj *my_obj = nl_object_priv(obj);
- *
- * free(my_obj->ptr);
- * }
- *
- * // Also when the object is cloned, you must ensure for your pointer
- * // stay valid even if one of the clones is freed by either making
- * // a clone as well or increase the reference count.
- * static int my_obj_clone(struct nl_object *src, struct nl_object *dst)
- * {
- * struct my_obj *my_src = nl_object_priv(src);
- * struct my_obj *my_dst = nl_object_priv(dst);
- *
- * if (src->ptr) {
- * dst->ptr = calloc(1, ...);
- * memcpy(dst->ptr, src->ptr, ...);
- * }
- * }
- *
- * struct nl_object_ops my_ops = {
- * ...
- * .oo_free_data = my_obj_free_data,
- * .oo_clone = my_obj_clone,
- * };
- * @endcode
- *
- * @par 3) Object Dumping
- * @code
- * static int my_obj_dump_detailed(struct nl_object *obj,
- * struct nl_dump_params *params)
- * {
- * struct my_obj *my_obj = nl_object_priv(obj);
- *
- * // It is absolutely essential to use nl_dump() when printing
- * // any text to make sure the dumping parameters are respected.
- * nl_dump(params, "Obj Integer: %d\n", my_obj->my_int);
- *
- * // Before we can dump the next line, make sure to prefix
- * // this line correctly.
- * nl_new_line(params);
- *
- * // You may also split a line into multiple nl_dump() calls.
- * nl_dump(params, "String: %s ", my_obj->my_string);
- * nl_dump(params, "String-2: %s\n", my_obj->another_string);
- * }
- *
- * struct nl_object_ops my_ops = {
- * ...
- * .oo_dump[NL_DUMP_FULL] = my_obj_dump_detailed,
- * };
- * @endcode
- *
- * @par 4) Object Attributes
- * @code
- * // The concept of object attributes is optional but can ease the typical
- * // case of objects that have optional attributes, e.g. a route may have a
- * // nexthop assigned but it is not required to.
- *
- * // The first step to define your object specific bitmask listing all
- * // attributes
- * #define MY_ATTR_FOO (1<<0)
- * #define MY_ATTR_BAR (1<<1)
- *
- * // When assigning an optional attribute to the object, make sure
- * // to mark its availability.
- * my_obj->foo = 123123;
- * my_obj->ce_mask |= MY_ATTR_FOO;
- *
- * // At any time you may use this mask to check for the availability
- * // of the attribute, e.g. while dumping
- * if (my_obj->ce_mask & MY_ATTR_FOO)
- * nl_dump(params, "foo %d ", my_obj->foo);
- *
- * // One of the big advantages of this concept is that it allows for
- * // standardized comparisons which make it trivial for caches to
- * // identify unique objects by use of unified comparison functions.
- * // In order for it to work, your object implementation must provide
- * // a comparison function and define a list of attributes which
- * // combined together make an object unique.
- *
- * static int my_obj_compare(struct nl_object *_a, struct nl_object *_b,
- * uint32_t attrs, int flags)
- * {
- * struct my_obj *a = nl_object_priv(_a):
- * struct my_obj *b = nl_object_priv(_b):
- * int diff = 0;
- *
- * // We help ourselves in defining our own DIFF macro which will
- * // call ATTR_DIFF() on both objects which will make sure to only
- * // compare the attributes if required.
- * #define MY_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, MY_ATTR_##ATTR, a, b, EXPR)
- *
- * // Call our own diff macro for each attribute to build a bitmask
- * // representing the attributes which mismatch.
- * diff |= MY_DIFF(FOO, a->foo != b->foo)
- * diff |= MY_DIFF(BAR, strcmp(a->bar, b->bar))
- *
- * return diff;
- * }
- *
- * // In order to identify identical objects with differing attributes
- * // you must specify the attributes required to uniquely identify
- * // your object. Make sure to not include too many attributes, this
- * // list is used when caches look for an old version of an object.
- * struct nl_object_ops my_ops = {
- * ...
- * .oo_id_attrs = MY_ATTR_FOO,
- * .oo_compare = my_obj_compare,
- * };
- * @endcode
- * @{
- */
-
-/**
- * Common Object Header
- *
- * This macro must be included as first member in every object
- * definition to allow objects to be cached.
- */
-#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;
-
-/**
- * Return true if attribute is available in both objects
- * @arg A an object
- * @arg B another object
- * @arg ATTR attribute bit
- *
- * @return True if the attribute is available, otherwise false is returned.
- */
-#define AVAILABLE(A, B, ATTR) (((A)->ce_mask & (B)->ce_mask) & (ATTR))
-
-/**
- * Return true if attribute is available in only one of both objects
- * @arg A an object
- * @arg B another object
- * @arg ATTR attribute bit
- *
- * @return True if the attribute is available in only one of both objects,
- * otherwise false is returned.
- */
-#define AVAILABLE_MISMATCH(A, B, ATTR) (((A)->ce_mask ^ (B)->ce_mask) & (ATTR))
-
-/**
- * Return true if attributes mismatch
- * @arg A an object
- * @arg B another object
- * @arg ATTR attribute bit
- * @arg EXPR Comparison expression
- *
- * This function will check if the attribute in question is available
- * in both objects, if not this will count as a mismatch.
- *
- * If available the function will execute the expression which must
- * return true if the attributes mismatch.
- *
- * @return True if the attribute mismatch, or false if they match.
- */
-#define ATTR_MISMATCH(A, B, ATTR, EXPR) (AVAILABLE_MISMATCH(A, B, ATTR) || \
- (AVAILABLE(A, B, ATTR) && (EXPR)))
-
-/**
- * Return attribute bit if attribute does not match
- * @arg LIST list of attributes to be compared
- * @arg ATTR attribute bit
- * @arg A an object
- * @arg B another object
- * @arg EXPR Comparison expression
- *
- * This function will check if the attribute in question is available
- * in both objects, if not this will count as a mismatch.
- *
- * If available the function will execute the expression which must
- * return true if the attributes mismatch.
- *
- * In case the attributes mismatch, the attribute is returned, otherwise
- * 0 is returned.
- *
- * @code
- * diff |= ATTR_DIFF(attrs, MY_ATTR_FOO, a, b, a->foo != b->foo);
- * @endcode
- */
-#define ATTR_DIFF(LIST, ATTR, A, B, EXPR) \
-({ int diff = 0; \
- if (((LIST) & (ATTR)) && ATTR_MISMATCH(A, B, ATTR, EXPR)) \
- diff = ATTR; \
- diff; })
-
-/**
- * Object Operations
- */
-struct nl_object_ops
-{
- /**
- * Unique name of object type
- *
- * Must be in the form family/name, e.g. "route/addr"
- */
- char * oo_name;
-
- /** Size of object including its header */
- size_t oo_size;
-
- /* List of attributes needed to uniquely identify the object */
- uint32_t oo_id_attrs;
-
- /**
- * Constructor function
- *
- * Will be called when a new object of this type is allocated.
- * Can be used to initialize members such as lists etc.
- */
- void (*oo_constructor)(struct nl_object *);
-
- /**
- * Destructor function
- *
- * Will be called when an object is freed. Must free all
- * resources which may have been allocated as part of this
- * object.
- */
- void (*oo_free_data)(struct nl_object *);
-
- /**
- * Cloning function
- *
- * Will be called when an object needs to be cloned. Please
- * note that the generic object code will make an exact
- * copy of the object first, therefore you only need to take
- * care of members which require reference counting etc.
- *
- * May return a negative error code to abort cloning.
- */
- int (*oo_clone)(struct nl_object *, struct nl_object *);
-
- /**
- * Dumping functions
- *
- * Will be called when an object is dumped. The implementations
- * have to use nl_dump(), nl_dump_line(), and nl_new_line() to
- * dump objects.
- *
- * The functions must return the number of lines printed.
- */
- void (*oo_dump[NL_DUMP_MAX+1])(struct nl_object *,
- struct nl_dump_params *);
-
- /**
- * Comparison function
- *
- * Will be called when two objects of the same type are
- * compared. It takes the two objects in question, an object
- * specific bitmask defining which attributes should be
- * compared and flags to control the behaviour.
- *
- * The function must return a bitmask with the relevant bit
- * set for each attribute that mismatches.
- */
- int (*oo_compare)(struct nl_object *, struct nl_object *,
- uint32_t, int);
-
-
- char *(*oo_attrs2str)(int, char *, size_t);
-};
-
-/** @} */
-
-#ifdef __cplusplus
-}
-#endif
+#include <netlink/object.h>
#endif
diff --git a/include/netlink/object.h b/include/netlink/object.h
index ef1ed9ff..a95feda7 100644
--- a/include/netlink/object.h
+++ b/include/netlink/object.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_OBJECT_H_
@@ -31,11 +31,14 @@ extern int nl_object_alloc_name(const char *,
struct nl_object **);
extern void nl_object_free(struct nl_object *);
extern struct nl_object * nl_object_clone(struct nl_object *obj);
+extern int nl_object_update(struct nl_object *dst,
+ struct nl_object *src);
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 void nl_object_dump_buf(struct nl_object *, char *, size_t);
extern int nl_object_identical(struct nl_object *,
struct nl_object *);
extern uint32_t nl_object_diff(struct nl_object *,
@@ -47,6 +50,8 @@ extern char * nl_object_attrs2str(struct nl_object *,
size_t);
extern char * nl_object_attr_list(struct nl_object *,
char *, size_t);
+extern void nl_object_keygen(struct nl_object *,
+ uint32_t *, uint32_t);
/* Marks */
extern void nl_object_mark(struct nl_object *);
@@ -56,6 +61,12 @@ 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 const char * nl_object_get_type(const struct nl_object *);
+extern int nl_object_get_msgtype(const struct nl_object *);
+struct nl_object_ops * nl_object_get_ops(const struct nl_object *);
+uint32_t nl_object_get_id_attrs(struct nl_object *obj);
+
+
static inline void * nl_object_priv(struct nl_object *obj)
{
return obj;
diff --git a/include/netlink/route/act/mirred.h b/include/netlink/route/act/mirred.h
new file mode 100644
index 00000000..d65ed378
--- /dev/null
+++ b/include/netlink/route/act/mirred.h
@@ -0,0 +1,35 @@
+/*
+ * netlink/route/cls/mirred.h mirred action
+ *
+ * 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) 2013 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+#ifndef NETLINK_MIRRED_H_
+#define NETLINK_MIRRED_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/route/action.h>
+#include <linux/tc_act/tc_mirred.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int rtnl_mirred_set_action(struct rtnl_act *, int);
+extern int rtnl_mirred_get_action(struct rtnl_act *);
+extern int rtnl_mirred_set_ifindex(struct rtnl_act *, uint32_t);
+extern uint32_t rtnl_mirred_get_ifindex(struct rtnl_act *);
+extern int rtnl_mirred_set_policy(struct rtnl_act *, int);
+extern int rtnl_mirred_get_policy(struct rtnl_act *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/action.h b/include/netlink/route/action.h
new file mode 100644
index 00000000..054bdd87
--- /dev/null
+++ b/include/netlink/route/action.h
@@ -0,0 +1,46 @@
+/*
+ * netlink/route/action.h Actions
+ *
+ * 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) 2013 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+#ifndef NETLINK_ACTION_H_
+#define NETLINK_ACTION_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/route/tc.h>
+#include <netlink/utils.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct rtnl_act *rtnl_act_alloc(void);
+extern void rtnl_act_get(struct rtnl_act *);
+extern void rtnl_act_put(struct rtnl_act *);
+extern int rtnl_act_build_add_request(struct rtnl_act *, int,
+ struct nl_msg **);
+extern int rtnl_act_add(struct nl_sock *, struct rtnl_act *, int);
+
+extern int rtnl_act_build_change_request(struct rtnl_act *, int,
+ struct nl_msg **);
+extern int rtnl_act_build_delete_request(struct rtnl_act *, int,
+ struct nl_msg **);
+extern int rtnl_act_delete(struct nl_sock *, struct rtnl_act *,
+ int);
+extern int rtnl_act_append(struct rtnl_act **, struct rtnl_act *);
+extern int rtnl_act_remove(struct rtnl_act **, struct rtnl_act *);
+extern int rtnl_act_fill(struct nl_msg *, int, struct rtnl_act *);
+extern void rtnl_act_put_all(struct rtnl_act **);
+extern int rtnl_act_parse(struct rtnl_act **, struct nlattr *);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/addr.h b/include/netlink/route/addr.h
index 1381486c..56c12e78 100644
--- a/include/netlink/route/addr.h
+++ b/include/netlink/route/addr.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2003-2006 Baruch Even <baruch@ev-en.org>,
* Mediatrix Telecom, inc. <ericb@mediatrix.com>
*/
@@ -17,6 +17,7 @@
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/addr.h>
+#include <netlink/route/link.h>
#ifdef __cplusplus
extern "C" {
@@ -29,6 +30,8 @@ extern struct rtnl_addr *rtnl_addr_alloc(void);
extern void rtnl_addr_put(struct rtnl_addr *);
extern int rtnl_addr_alloc_cache(struct nl_sock *, struct nl_cache **);
+extern struct rtnl_addr *
+ rtnl_addr_get(struct nl_cache *, int, struct nl_addr *);
extern int rtnl_addr_build_add_request(struct rtnl_addr *, int,
struct nl_msg **);
@@ -48,6 +51,10 @@ 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_link(struct rtnl_addr *, struct rtnl_link *);
+extern struct rtnl_link *
+ rtnl_addr_get_link(struct rtnl_addr *);
+
extern void rtnl_addr_set_family(struct rtnl_addr *, int);
extern int rtnl_addr_get_family(struct rtnl_addr *);
diff --git a/include/netlink/route/class-modules.h b/include/netlink/route/class-modules.h
deleted file mode 100644
index 74a25c9d..00000000
--- a/include/netlink/route/class-modules.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * 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 <tgraf@suug.ch>
- */
-
-#ifndef NETLINK_CLASS_MODULES_H_
-#define NETLINK_CLASS_MODULES_H_
-
-#include <netlink/netlink.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Class operations
- * @ingroup class_api
- */
-struct rtnl_class_ops
-{
- /**
- * Kind/Name of class
- */
- char co_kind[32];
-
- /**
- * Dump callbacks
- */
- void (*co_dump[NL_DUMP_MAX+1])(struct rtnl_class *,
- struct nl_dump_params *);
-
- /**
- * 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
index 480095e1..e73b60a7 100644
--- a/include/netlink/route/class.h
+++ b/include/netlink/route/class.h
@@ -1,12 +1,12 @@
/*
- * netlink/route/class.h Classes
+ * netlink/route/class.h Traffic 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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_CLASS_H_
@@ -22,16 +22,17 @@ extern "C" {
struct rtnl_class;
-extern struct nl_object_ops class_obj_ops;
-
-extern struct rtnl_class * rtnl_class_alloc(void);
+extern struct rtnl_class *
+ rtnl_class_alloc(void);
extern void rtnl_class_put(struct rtnl_class *);
+
extern int rtnl_class_alloc_cache(struct nl_sock *, int,
struct nl_cache **);
-extern struct rtnl_class *rtnl_class_get(struct nl_cache *, int, uint32_t);
+extern struct rtnl_class *
+ rtnl_class_get(struct nl_cache *, int, uint32_t);
-/* leaf qdisc access */
-extern struct rtnl_qdisc * rtnl_class_leaf_qdisc(struct rtnl_class *,
+extern struct rtnl_qdisc *
+ rtnl_class_leaf_qdisc(struct rtnl_class *,
struct nl_cache *);
extern int rtnl_class_build_add_request(struct rtnl_class *, int,
@@ -39,32 +40,24 @@ extern int rtnl_class_build_add_request(struct rtnl_class *, int,
extern int rtnl_class_add(struct nl_sock *, struct rtnl_class *,
int);
-extern int rtnl_class_build_delete_request(struct rtnl_class *,
- struct nl_msg **);
-extern int rtnl_class_delete(struct nl_sock *, struct rtnl_class *);
-
-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);
+extern int rtnl_class_build_delete_request(struct rtnl_class *,
+ struct nl_msg **);
+extern int rtnl_class_delete(struct nl_sock *,
+ struct rtnl_class *);
-/* iterators */
+/* deprecated functions */
extern void rtnl_class_foreach_child(struct rtnl_class *,
struct nl_cache *,
void (*cb)(struct nl_object *,
void *),
- void *);
+ void *)
+ __attribute__((deprecated));
extern void rtnl_class_foreach_cls(struct rtnl_class *,
struct nl_cache *,
void (*cb)(struct nl_object *,
void *),
- void *);
+ void *)
+ __attribute__((deprecated));
#ifdef __cplusplus
}
diff --git a/include/netlink/route/classifier-modules.h b/include/netlink/route/classifier-modules.h
deleted file mode 100644
index 35cb06e4..00000000
--- a/include/netlink/route/classifier-modules.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * 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-2009 Thomas Graf <tgraf@suug.ch>
- */
-
-#ifndef NETLINK_CLASS_MODULES_H_
-#define NETLINK_CLASS_MODULES_H_
-
-#include <netlink/netlink.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Classifier operations
- * @ingroup cls_api
- */
-struct rtnl_cls_ops
-{
- /**
- * Name of classifier module
- */
- char co_kind[32];
-
-
- /**
- * Size of private classifier data
- */
- size_t co_size;
-
- /**
- * Dump callbacks
- */
- void (*co_dump[NL_DUMP_MAX+1])(struct rtnl_cls *,
- struct nl_dump_params *);
- /**
- * Must return the contents supposed to be in TCA_OPTIONS
- */
- int (*co_get_opts)(struct rtnl_cls *, struct nl_msg *);
-
- /**
- * 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
index d9c3d211..a8c11798 100644
--- a/include/netlink/route/classifier.h
+++ b/include/netlink/route/classifier.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_CLASSIFIER_H_
@@ -21,39 +21,29 @@
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 int rtnl_cls_alloc_cache(struct nl_sock *, int, uint32_t,
- struct nl_cache **);
-
-extern int rtnl_cls_build_add_request(struct rtnl_cls *, int,
- struct nl_msg **);
-extern int rtnl_cls_add(struct nl_sock *, struct rtnl_cls *, int);
+extern void rtnl_cls_put(struct rtnl_cls *);
-extern int rtnl_cls_build_change_request(struct rtnl_cls *, int,
- struct nl_msg **);
-extern int rtnl_cls_build_delete_request(struct rtnl_cls *, int,
- struct nl_msg **);
-extern int rtnl_cls_delete(struct nl_sock *, struct rtnl_cls *, int);
+extern int rtnl_cls_alloc_cache(struct nl_sock *, int, uint32_t,
+ struct nl_cache **);
-extern void rtnl_cls_set_ifindex(struct rtnl_cls *, int);
-extern int rtnl_cls_get_ifindex(struct rtnl_cls *);
-extern void rtnl_cls_set_handle(struct rtnl_cls *, uint32_t);
-extern void rtnl_cls_set_parent(struct rtnl_cls *, uint32_t);
-extern uint32_t rtnl_cls_get_parent(struct rtnl_cls *);
-extern int rtnl_cls_set_kind(struct rtnl_cls *, const char *);
-extern struct rtnl_cls_ops *rtnl_cls_get_ops(struct rtnl_cls *);
+extern int rtnl_cls_build_add_request(struct rtnl_cls *, int,
+ struct nl_msg **);
+extern int rtnl_cls_add(struct nl_sock *, struct rtnl_cls *, int);
+extern int rtnl_cls_change(struct nl_sock *, struct rtnl_cls *, int);
-extern void rtnl_cls_set_prio(struct rtnl_cls *, uint16_t);
-extern uint16_t rtnl_cls_get_prio(struct rtnl_cls *);
+extern int rtnl_cls_build_change_request(struct rtnl_cls *, int,
+ struct nl_msg **);
+extern int rtnl_cls_build_delete_request(struct rtnl_cls *, int,
+ struct nl_msg **);
+extern int rtnl_cls_delete(struct nl_sock *, struct rtnl_cls *,
+ int);
-extern void rtnl_cls_set_protocol(struct rtnl_cls *, uint16_t);
-extern uint16_t rtnl_cls_get_protocol(struct rtnl_cls *);
+extern void rtnl_cls_set_prio(struct rtnl_cls *, uint16_t);
+extern uint16_t rtnl_cls_get_prio(struct rtnl_cls *);
-extern void *rtnl_cls_data(struct rtnl_cls *);
+extern void rtnl_cls_set_protocol(struct rtnl_cls *, uint16_t);
+extern uint16_t rtnl_cls_get_protocol(struct rtnl_cls *);
#ifdef __cplusplus
}
diff --git a/include/netlink/route/cls/basic.h b/include/netlink/route/cls/basic.h
index 7003124e..f00793ca 100644
--- a/include/netlink/route/cls/basic.h
+++ b/include/netlink/route/cls/basic.h
@@ -6,25 +6,28 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_BASIC_H_
#define NETLINK_BASIC_H_
#include <netlink/netlink.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/cls/ematch.h>
+#include <netlink/route/action.h>
#ifdef __cplusplus
extern "C" {
#endif
-extern struct rtnl_cls_ops *rtnl_basic_get_ops(void);
-extern int rtnl_basic_set_classid(struct rtnl_cls *, uint32_t);
-extern uint32_t rtnl_basic_get_classid(struct rtnl_cls *);
-extern int rtnl_basic_set_ematch(struct rtnl_cls *,
- struct rtnl_ematch_tree *);
-extern struct rtnl_ematch_tree *
- rtnl_basic_get_ematch(struct rtnl_cls *);
+extern void rtnl_basic_set_target(struct rtnl_cls *, uint32_t);
+extern uint32_t rtnl_basic_get_target(struct rtnl_cls *);
+extern void rtnl_basic_set_ematch(struct rtnl_cls *,
+ struct rtnl_ematch_tree *);
+extern struct rtnl_ematch_tree *rtnl_basic_get_ematch(struct rtnl_cls *);
+extern int rtnl_basic_add_action(struct rtnl_cls *, struct rtnl_act *);
+extern int rtnl_basic_del_action(struct rtnl_cls *, struct rtnl_act *);
#ifdef __cplusplus
}
diff --git a/include/netlink/route/cls/cgroup.h b/include/netlink/route/cls/cgroup.h
index 7b0e3d3a..9cd48454 100644
--- a/include/netlink/route/cls/cgroup.h
+++ b/include/netlink/route/cls/cgroup.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2009-2010 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_CLS_CGROUP_H_
@@ -14,15 +14,16 @@
#include <netlink/netlink.h>
#include <netlink/cache.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/cls/ematch.h>
#ifdef __cplusplus
extern "C" {
#endif
-extern int rtnl_cgroup_set_ematch(struct rtnl_cls *,
- struct rtnl_ematch_tree *);
-extern struct rtnl_ematch_tree *
- rtnl_cgroup_get_ematch(struct rtnl_cls *);
+extern void rtnl_cgroup_set_ematch(struct rtnl_cls *,
+ struct rtnl_ematch_tree *);
+struct rtnl_ematch_tree * rtnl_cgroup_get_ematch(struct rtnl_cls *);
#ifdef __cplusplus
}
diff --git a/include/netlink/route/cls/ematch.h b/include/netlink/route/cls/ematch.h
index c4292bfa..13f9c323 100644
--- a/include/netlink/route/cls/ematch.h
+++ b/include/netlink/route/cls/ematch.h
@@ -6,13 +6,14 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_CLS_EMATCH_H_
#define NETLINK_CLS_EMATCH_H_
#include <netlink/netlink.h>
+#include <netlink/msg.h>
#include <netlink/route/classifier.h>
#include <linux/pkt_cls.h>
@@ -20,51 +21,72 @@
extern "C" {
#endif
+/* FIXME: Should be moved to the kernel header at some point */
+#define RTNL_EMATCH_PROGID 2
+
struct rtnl_ematch;
struct rtnl_ematch_tree;
+/**
+ * Extended Match Operations
+ */
struct rtnl_ematch_ops
{
- int eo_kind;
- const char * eo_name;
- size_t eo_datalen;
-
- int (*eo_parse)(struct rtnl_ematch *,
- void *, size_t);
- void (*eo_dump)(struct rtnl_ematch *,
- struct nl_dump_params *);
- struct nl_list_head eo_list;
+ int eo_kind;
+ const char * eo_name;
+ size_t eo_minlen;
+ size_t eo_datalen;
+
+ int (*eo_parse)(struct rtnl_ematch *, void *, size_t);
+ void (*eo_dump)(struct rtnl_ematch *,
+ struct nl_dump_params *);
+ int (*eo_fill)(struct rtnl_ematch *, struct nl_msg *);
+ void (*eo_free)(struct rtnl_ematch *);
+ struct nl_list_head eo_list;
};
-extern int rtnl_ematch_register(struct rtnl_ematch_ops *);
-extern int rtnl_ematch_unregister(struct rtnl_ematch_ops *);
-
-extern struct rtnl_ematch_ops *
- rtnl_ematch_lookup_ops(int);
-extern struct rtnl_ematch_ops *
- rtnl_ematch_lookup_ops_name(const char *);
-
-extern struct rtnl_ematch *
- rtnl_ematch_alloc(struct rtnl_ematch_ops *);
-extern void rtnl_ematch_add_child(struct rtnl_ematch *,
- struct rtnl_ematch *);
-extern void rtnl_ematch_unlink(struct rtnl_ematch *);
-extern void rtnl_ematch_free(struct rtnl_ematch *);
-
-extern void * rtnl_ematch_data(struct rtnl_ematch *);
-extern void rtnl_ematch_set_flags(struct rtnl_ematch *, uint16_t);
-extern void rtnl_ematch_unset_flags(struct rtnl_ematch *, uint16_t);
-extern uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *);
-
-extern struct rtnl_ematch_tree *
- rtnl_ematch_tree_alloc(uint16_t);
-extern void rtnl_ematch_tree_free(struct rtnl_ematch_tree *);
-
-extern int rtnl_ematch_parse(struct nlattr *, struct rtnl_ematch_tree **);
-extern void rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree *,
- struct rtnl_ematch *);
-extern void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *,
- struct nl_dump_params *);
+extern int rtnl_ematch_register(struct rtnl_ematch_ops *);
+extern struct rtnl_ematch_ops * rtnl_ematch_lookup_ops(int);
+extern struct rtnl_ematch_ops * rtnl_ematch_lookup_ops_by_name(const char *);
+
+extern struct rtnl_ematch * rtnl_ematch_alloc(void);
+extern int rtnl_ematch_add_child(struct rtnl_ematch *,
+ struct rtnl_ematch *);
+extern void rtnl_ematch_unlink(struct rtnl_ematch *);
+extern void rtnl_ematch_free(struct rtnl_ematch *);
+
+extern void * rtnl_ematch_data(struct rtnl_ematch *);
+extern void rtnl_ematch_set_flags(struct rtnl_ematch *,
+ uint16_t);
+extern void rtnl_ematch_unset_flags(struct rtnl_ematch *,
+ uint16_t);
+extern uint16_t rtnl_ematch_get_flags(struct rtnl_ematch *);
+extern int rtnl_ematch_set_ops(struct rtnl_ematch *,
+ struct rtnl_ematch_ops *);
+extern int rtnl_ematch_set_kind(struct rtnl_ematch *,
+ uint16_t);
+extern int rtnl_ematch_set_name(struct rtnl_ematch *,
+ const char *);
+
+extern struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t);
+extern void rtnl_ematch_tree_free(struct rtnl_ematch_tree *);
+extern void rtnl_ematch_tree_add(struct rtnl_ematch_tree *,
+ struct rtnl_ematch *);
+
+extern int rtnl_ematch_parse_attr(struct nlattr *,
+ struct rtnl_ematch_tree **);
+extern int rtnl_ematch_fill_attr(struct nl_msg *, int,
+ struct rtnl_ematch_tree *);
+extern void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *,
+ struct nl_dump_params *);
+
+
+extern int rtnl_ematch_parse_expr(const char *, char **,
+ struct rtnl_ematch_tree **);
+
+extern char * rtnl_ematch_offset2txt(uint8_t, uint16_t,
+ char *, size_t);
+extern char * rtnl_ematch_opnd2txt(uint8_t, char *, size_t);
#ifdef __cplusplus
}
diff --git a/include/netlink/route/cls/ematch/cmp.h b/include/netlink/route/cls/ematch/cmp.h
index b4ad03a5..308113e0 100644
--- a/include/netlink/route/cls/ematch/cmp.h
+++ b/include/netlink/route/cls/ematch/cmp.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_CLS_EMATCH_CMP_H_
@@ -14,6 +14,7 @@
#include <netlink/netlink.h>
#include <netlink/route/cls/ematch.h>
+#include <linux/tc_ematch/tc_em_cmp.h>
#ifdef __cplusplus
extern "C" {
diff --git a/include/netlink/route/cls/ematch/meta.h b/include/netlink/route/cls/ematch/meta.h
new file mode 100644
index 00000000..2fe58990
--- /dev/null
+++ b/include/netlink/route/cls/ematch/meta.h
@@ -0,0 +1,41 @@
+/*
+ * netlink/route/cls/ematch/meta.h Metadata Match
+ *
+ * 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_CLS_EMATCH_META_H_
+#define NETLINK_CLS_EMATCH_META_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/cls/ematch.h>
+#include <linux/tc_ematch/tc_em_meta.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct rtnl_meta_value;
+
+extern struct rtnl_meta_value * rtnl_meta_value_alloc_int(uint64_t);
+extern struct rtnl_meta_value * rtnl_meta_value_alloc_var(void *, size_t);
+extern struct rtnl_meta_value * rtnl_meta_value_alloc_id(uint8_t, uint16_t,
+ uint8_t, uint64_t);
+extern void rtnl_meta_value_put(struct rtnl_meta_value *);
+
+extern void rtnl_ematch_meta_set_lvalue(struct rtnl_ematch *,
+ struct rtnl_meta_value *);
+void rtnl_ematch_meta_set_rvalue(struct rtnl_ematch *,
+ struct rtnl_meta_value *);
+extern void rtnl_ematch_meta_set_operand(struct rtnl_ematch *, uint8_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/cls/ematch/nbyte.h b/include/netlink/route/cls/ematch/nbyte.h
new file mode 100644
index 00000000..014c719b
--- /dev/null
+++ b/include/netlink/route/cls/ematch/nbyte.h
@@ -0,0 +1,36 @@
+/*
+ * netlink/route/cls/ematch/nbyte.h N-Byte Comparison
+ *
+ * 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_CLS_EMATCH_NBYTE_H_
+#define NETLINK_CLS_EMATCH_NBYTE_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/cls/ematch.h>
+#include <linux/tc_ematch/tc_em_nbyte.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void rtnl_ematch_nbyte_set_offset(struct rtnl_ematch *,
+ uint8_t, uint16_t);
+extern uint16_t rtnl_ematch_nbyte_get_offset(struct rtnl_ematch *);
+extern uint8_t rtnl_ematch_nbyte_get_layer(struct rtnl_ematch *);
+extern void rtnl_ematch_nbyte_set_pattern(struct rtnl_ematch *,
+ uint8_t *, size_t);
+extern uint8_t * rtnl_ematch_nbyte_get_pattern(struct rtnl_ematch *);
+extern size_t rtnl_ematch_nbyte_get_len(struct rtnl_ematch *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/cls/ematch/text.h b/include/netlink/route/cls/ematch/text.h
new file mode 100644
index 00000000..e599abf0
--- /dev/null
+++ b/include/netlink/route/cls/ematch/text.h
@@ -0,0 +1,42 @@
+/*
+ * netlink/route/cls/ematch/text.h Text Search
+ *
+ * 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_CLS_EMATCH_TEXT_H_
+#define NETLINK_CLS_EMATCH_TEXT_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/cls/ematch.h>
+#include <linux/tc_ematch/tc_em_text.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void rtnl_ematch_text_set_from(struct rtnl_ematch *,
+ uint8_t, uint16_t);
+extern uint16_t rtnl_ematch_text_get_from_offset(struct rtnl_ematch *);
+extern uint8_t rtnl_ematch_text_get_from_layer(struct rtnl_ematch *);
+extern void rtnl_ematch_text_set_to(struct rtnl_ematch *,
+ uint8_t, uint16_t);
+extern uint16_t rtnl_ematch_text_get_to_offset(struct rtnl_ematch *);
+extern uint8_t rtnl_ematch_text_get_to_layer(struct rtnl_ematch *);
+extern void rtnl_ematch_text_set_pattern(struct rtnl_ematch *,
+ char *, size_t);
+extern char * rtnl_ematch_text_get_pattern(struct rtnl_ematch *);
+extern size_t rtnl_ematch_text_get_len(struct rtnl_ematch *);
+extern void rtnl_ematch_text_set_algo(struct rtnl_ematch *, const char *);
+extern char * rtnl_ematch_text_get_algo(struct rtnl_ematch *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/cls/fw.h b/include/netlink/route/cls/fw.h
index 39878dec..2e1bade0 100644
--- a/include/netlink/route/cls/fw.h
+++ b/include/netlink/route/cls/fw.h
@@ -15,12 +15,14 @@
#define NETLINK_FW_H_
#include <netlink/netlink.h>
+#include <netlink/route/classifier.h>
#ifdef __cplusplus
extern "C" {
#endif
extern int rtnl_fw_set_classid(struct rtnl_cls *, uint32_t);
+extern int rtnl_fw_set_mask(struct rtnl_cls *, uint32_t);
#ifdef __cplusplus
}
diff --git a/include/netlink/route/cls/u32.h b/include/netlink/route/cls/u32.h
index cf35e266..f35d37a4 100644
--- a/include/netlink/route/cls/u32.h
+++ b/include/netlink/route/cls/u32.h
@@ -14,6 +14,8 @@
#include <netlink/netlink.h>
#include <netlink/cache.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/action.h>
#ifdef __cplusplus
extern "C" {
@@ -21,20 +23,29 @@ extern "C" {
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_divisor(struct rtnl_cls *, uint32_t);
+extern int rtnl_u32_set_link(struct rtnl_cls *, uint32_t);
+extern int rtnl_u32_set_hashtable(struct rtnl_cls *, uint32_t);
+extern int rtnl_u32_set_hashmask(struct rtnl_cls *, uint32_t, uint32_t);
+extern int rtnl_u32_set_cls_terminal(struct rtnl_cls *);
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_get_key(struct rtnl_cls *, uint8_t, 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 *,
+extern int rtnl_u32_add_key_in_addr(struct rtnl_cls *, const struct in_addr *,
uint8_t, int, int);
-extern int rtnl_u32_add_key_in6_addr(struct rtnl_cls *, struct in6_addr *,
+extern int rtnl_u32_add_key_in6_addr(struct rtnl_cls *, const struct in6_addr *,
uint8_t, int, int);
+extern int rtnl_u32_add_action(struct rtnl_cls *, struct rtnl_act *);
+extern int rtnl_u32_del_action(struct rtnl_cls *, struct rtnl_act *);
#ifdef __cplusplus
}
diff --git a/include/netlink/route/link.h b/include/netlink/route/link.h
index 4b630f74..a7aa88b7 100644
--- a/include/netlink/route/link.h
+++ b/include/netlink/route/link.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_LINK_H_
@@ -15,59 +15,122 @@
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/addr.h>
+#include <linux/if.h>
+#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
+/**
+ * @struct rtnl_link link.h "netlink/route/link.h"
+ * @brief Link object
+ * @implements nl_object
+ * @ingroup link
+ *
+ * @copydoc private_struct
+ */
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,
+/**
+ * @ingroup link
+ */
+typedef enum {
+ RTNL_LINK_RX_PACKETS, /*!< Packets received */
+ RTNL_LINK_TX_PACKETS, /*!< Packets sent */
+ RTNL_LINK_RX_BYTES, /*!< Bytes received */
+ RTNL_LINK_TX_BYTES, /*!< Bytes sent */
+ RTNL_LINK_RX_ERRORS, /*!< Receive errors */
+ RTNL_LINK_TX_ERRORS, /*!< Send errors */
+ RTNL_LINK_RX_DROPPED, /*!< Received packets dropped */
+ RTNL_LINK_TX_DROPPED, /*!< Packets dropped during transmit */
+ RTNL_LINK_RX_COMPRESSED, /*!< Compressed packets received */
+ RTNL_LINK_TX_COMPRESSED, /*!< Compressed packets sent */
+ RTNL_LINK_RX_FIFO_ERR, /*!< Receive FIFO errors */
+ RTNL_LINK_TX_FIFO_ERR, /*!< Send FIFO errors */
+ RTNL_LINK_RX_LEN_ERR, /*!< Length errors */
+ RTNL_LINK_RX_OVER_ERR, /*!< Over errors */
+ RTNL_LINK_RX_CRC_ERR, /*!< CRC errors */
+ RTNL_LINK_RX_FRAME_ERR, /*!< Frame errors */
+ RTNL_LINK_RX_MISSED_ERR, /*!< Missed errors */
+ RTNL_LINK_TX_ABORT_ERR, /*!< Aborted errors */
+ RTNL_LINK_TX_CARRIER_ERR, /*!< Carrier errors */
+ RTNL_LINK_TX_HBEAT_ERR, /*!< Heartbeat errors */
+ RTNL_LINK_TX_WIN_ERR, /*!< Window errors */
+ RTNL_LINK_COLLISIONS, /*!< Send collisions */
+ RTNL_LINK_MULTICAST, /*!< Multicast */
+ RTNL_LINK_IP6_INPKTS, /*!< IPv6 SNMP InReceives */
+ RTNL_LINK_IP6_INHDRERRORS, /*!< IPv6 SNMP InHdrErrors */
+ RTNL_LINK_IP6_INTOOBIGERRORS, /*!< IPv6 SNMP InTooBigErrors */
+ RTNL_LINK_IP6_INNOROUTES, /*!< IPv6 SNMP InNoRoutes */
+ RTNL_LINK_IP6_INADDRERRORS, /*!< IPv6 SNMP InAddrErrors */
+ RTNL_LINK_IP6_INUNKNOWNPROTOS, /*!< IPv6 SNMP InUnknownProtos */
+ RTNL_LINK_IP6_INTRUNCATEDPKTS, /*!< IPv6 SNMP InTruncatedPkts */
+ RTNL_LINK_IP6_INDISCARDS, /*!< IPv6 SNMP InDiscards */
+ RTNL_LINK_IP6_INDELIVERS, /*!< IPv6 SNMP InDelivers */
+ RTNL_LINK_IP6_OUTFORWDATAGRAMS, /*!< IPv6 SNMP OutForwDatagrams */
+ RTNL_LINK_IP6_OUTPKTS, /*!< IPv6 SNMP OutRequests */
+ RTNL_LINK_IP6_OUTDISCARDS, /*!< IPv6 SNMP OutDiscards */
+ RTNL_LINK_IP6_OUTNOROUTES, /*!< IPv6 SNMP OutNoRoutes */
+ RTNL_LINK_IP6_REASMTIMEOUT, /*!< IPv6 SNMP ReasmTimeout */
+ RTNL_LINK_IP6_REASMREQDS, /*!< IPv6 SNMP ReasmReqds */
+ RTNL_LINK_IP6_REASMOKS, /*!< IPv6 SNMP ReasmOKs */
+ RTNL_LINK_IP6_REASMFAILS, /*!< IPv6 SNMP ReasmFails */
+ RTNL_LINK_IP6_FRAGOKS, /*!< IPv6 SNMP FragOKs */
+ RTNL_LINK_IP6_FRAGFAILS, /*!< IPv6 SNMP FragFails */
+ RTNL_LINK_IP6_FRAGCREATES, /*!< IPv6 SNMP FragCreates */
+ RTNL_LINK_IP6_INMCASTPKTS, /*!< IPv6 SNMP InMcastPkts */
+ RTNL_LINK_IP6_OUTMCASTPKTS, /*!< IPv6 SNMP OutMcastPkts */
+ RTNL_LINK_IP6_INBCASTPKTS, /*!< IPv6 SNMP InBcastPkts */
+ RTNL_LINK_IP6_OUTBCASTPKTS, /*!< IPv6 SNMP OutBcastPkts */
+ RTNL_LINK_IP6_INOCTETS, /*!< IPv6 SNMP InOctets */
+ RTNL_LINK_IP6_OUTOCTETS, /*!< IPv6 SNMP OutOctets */
+ RTNL_LINK_IP6_INMCASTOCTETS, /*!< IPv6 SNMP InMcastOctets */
+ RTNL_LINK_IP6_OUTMCASTOCTETS, /*!< IPv6 SNMP OutMcastOctets */
+ RTNL_LINK_IP6_INBCASTOCTETS, /*!< IPv6 SNMP InBcastOctets */
+ RTNL_LINK_IP6_OUTBCASTOCTETS, /*!< IPv6 SNMP OutBcastOctets */
+ RTNL_LINK_ICMP6_INMSGS, /*!< ICMPv6 SNMP InMsgs */
+ RTNL_LINK_ICMP6_INERRORS, /*!< ICMPv6 SNMP InErrors */
+ RTNL_LINK_ICMP6_OUTMSGS, /*!< ICMPv6 SNMP OutMsgs */
+ RTNL_LINK_ICMP6_OUTERRORS, /*!< ICMPv6 SNMP OutErrors */
+ RTNL_LINK_ICMP6_CSUMERRORS, /*!< ICMPv6 SNMP InCsumErrors */
+ RTNL_LINK_IP6_CSUMERRORS, /*!< IPv6 SNMP InCsumErrors */
+ RTNL_LINK_IP6_NOECTPKTS, /*!< IPv6 SNMP InNoECTPkts */
+ RTNL_LINK_IP6_ECT1PKTS, /*!< IPv6 SNMP InECT1Pkts */
+ RTNL_LINK_IP6_ECT0PKTS, /*!< IPv6 SNMP InECT0Pkts */
+ RTNL_LINK_IP6_CEPKTS, /*!< IPv6 SNMP InCEPkts */
__RTNL_LINK_STATS_MAX,
-};
+} rtnl_link_stat_id_t;
#define RTNL_LINK_STATS_MAX (__RTNL_LINK_STATS_MAX - 1)
-/* link object allocation/freeage */
+extern struct nla_policy rtln_link_policy[];
+
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 int rtnl_link_alloc_cache(struct nl_sock *, struct nl_cache **);
+extern int rtnl_link_alloc_cache(struct nl_sock *, int, struct nl_cache **);
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 *);
+extern int rtnl_link_build_add_request(struct rtnl_link *, int,
+ struct nl_msg **);
+extern int rtnl_link_add(struct nl_sock *, struct rtnl_link *, int);
extern int rtnl_link_build_change_request(struct rtnl_link *,
struct rtnl_link *, int,
struct nl_msg **);
extern int rtnl_link_change(struct nl_sock *, struct rtnl_link *,
struct rtnl_link *, int);
+extern int rtnl_link_build_delete_request(const struct rtnl_link *,
+ struct nl_msg **);
+extern int rtnl_link_delete(struct nl_sock *, const struct rtnl_link *);
+extern int rtnl_link_build_get_request(int, const char *,
+ struct nl_msg **);
+extern int rtnl_link_get_kernel(struct nl_sock *, int, const char *,
+ struct rtnl_link **);
+
/* 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 *);
@@ -80,12 +143,16 @@ extern int rtnl_link_str2stat(const char *);
extern char * rtnl_link_flags2str(int, char *, size_t);
extern int rtnl_link_str2flags(const char *);
-extern char * rtnl_link_operstate2str(int, char *, size_t);
+extern char * rtnl_link_operstate2str(uint8_t, char *, size_t);
extern int rtnl_link_str2operstate(const char *);
-extern char * rtnl_link_mode2str(int, char *, size_t);
+extern char * rtnl_link_mode2str(uint8_t, char *, size_t);
extern int rtnl_link_str2mode(const char *);
+/* Carrier State Translations */
+extern char * rtnl_link_carrier2str(uint8_t, char *, size_t);
+extern int rtnl_link_str2carrier(const char *);
+
/* Access Functions */
extern void rtnl_link_set_qdisc(struct rtnl_link *, const char *);
extern char * rtnl_link_get_qdisc(struct rtnl_link *);
@@ -93,6 +160,9 @@ 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_group(struct rtnl_link *, uint32_t);
+extern uint32_t rtnl_link_get_group(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 *);
@@ -103,9 +173,6 @@ 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 *);
@@ -127,16 +194,58 @@ 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 void rtnl_link_set_carrier(struct rtnl_link *, uint8_t);
+extern uint8_t rtnl_link_get_carrier(struct rtnl_link *);
+
extern void rtnl_link_set_operstate(struct rtnl_link *, uint8_t);
extern uint8_t rtnl_link_get_operstate(struct rtnl_link *);
extern void rtnl_link_set_linkmode(struct rtnl_link *, uint8_t);
extern uint8_t rtnl_link_get_linkmode(struct rtnl_link *);
-extern uint64_t rtnl_link_get_stat(struct rtnl_link *, int);
+extern const char * rtnl_link_get_ifalias(struct rtnl_link *);
+extern void rtnl_link_set_ifalias(struct rtnl_link *, const char *);
+
+extern int rtnl_link_get_num_vf(struct rtnl_link *, uint32_t *);
+
+extern uint64_t rtnl_link_get_stat(struct rtnl_link *, rtnl_link_stat_id_t);
+extern int rtnl_link_set_stat(struct rtnl_link *, rtnl_link_stat_id_t,
+ const uint64_t);
+
+extern int rtnl_link_set_type(struct rtnl_link *, const char *);
+extern char * rtnl_link_get_type(struct rtnl_link *);
+
+extern void rtnl_link_set_promiscuity(struct rtnl_link *, uint32_t);
+extern uint32_t rtnl_link_get_promiscuity(struct rtnl_link *);
+
+extern void rtnl_link_set_num_tx_queues(struct rtnl_link *, uint32_t);
+extern uint32_t rtnl_link_get_num_tx_queues(struct rtnl_link *);
+
+extern void rtnl_link_set_num_rx_queues(struct rtnl_link *, uint32_t);
+extern uint32_t rtnl_link_get_num_rx_queues(struct rtnl_link *);
+
+extern struct nl_data * rtnl_link_get_phys_port_id(struct rtnl_link *);
+
+extern void rtnl_link_set_ns_fd(struct rtnl_link *, int);
+extern int rtnl_link_get_ns_fd(struct rtnl_link *);
+extern void rtnl_link_set_ns_pid(struct rtnl_link *, pid_t);
+extern pid_t rtnl_link_get_ns_pid(struct rtnl_link *);
+
+extern int rtnl_link_enslave_ifindex(struct nl_sock *, int, int);
+extern int rtnl_link_enslave(struct nl_sock *, struct rtnl_link *,
+ struct rtnl_link *);
+extern int rtnl_link_release_ifindex(struct nl_sock *, int);
+extern int rtnl_link_release(struct nl_sock *, struct rtnl_link *);
+extern int rtnl_link_fill_info(struct nl_msg *, struct rtnl_link *);
+extern int rtnl_link_info_parse(struct rtnl_link *, struct nlattr **);
+
+
+/* deprecated */
+extern int rtnl_link_set_info_type(struct rtnl_link *, const char *) __attribute__((deprecated));
+extern char * rtnl_link_get_info_type(struct rtnl_link *) __attribute__((deprecated));
+extern void rtnl_link_set_weight(struct rtnl_link *, unsigned int) __attribute__((deprecated));
+extern unsigned int rtnl_link_get_weight(struct rtnl_link *) __attribute__((deprecated));
-extern int rtnl_link_set_info_type(struct rtnl_link *, const char *);
-extern char * rtnl_link_get_info_type(struct rtnl_link *);
#ifdef __cplusplus
}
diff --git a/include/netlink/route/link/api.h b/include/netlink/route/link/api.h
new file mode 100644
index 00000000..03b1e5e5
--- /dev/null
+++ b/include/netlink/route/link/api.h
@@ -0,0 +1,20 @@
+/*
+ * netlink/route/link/api.h Link Modules 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) 2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_DUMMY_LINK_API_H_
+#define NETLINK_DUMMY_LINK_API_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#warning "You are including a deprecated header file, include <netlink/route/link.h>."
+
+#endif
diff --git a/include/netlink/route/link/bonding.h b/include/netlink/route/link/bonding.h
new file mode 100644
index 00000000..5c34662d
--- /dev/null
+++ b/include/netlink/route/link/bonding.h
@@ -0,0 +1,39 @@
+/*
+ * netlink/route/link/bonding.h Bonding 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) 2011-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_LINK_BONDING_H_
+#define NETLINK_LINK_BONDING_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct rtnl_link *rtnl_link_bond_alloc(void);
+
+extern int rtnl_link_bond_add(struct nl_sock *, const char *,
+ struct rtnl_link *);
+
+extern int rtnl_link_bond_enslave_ifindex(struct nl_sock *, int, int);
+extern int rtnl_link_bond_enslave(struct nl_sock *, struct rtnl_link *,
+ struct rtnl_link *);
+
+extern int rtnl_link_bond_release_ifindex(struct nl_sock *, int);
+extern int rtnl_link_bond_release(struct nl_sock *, struct rtnl_link *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/include/netlink/route/link/bridge.h b/include/netlink/route/link/bridge.h
new file mode 100644
index 00000000..16a4505f
--- /dev/null
+++ b/include/netlink/route/link/bridge.h
@@ -0,0 +1,60 @@
+/*
+ * netlink/route/link/bridge.h Bridge
+ *
+ * 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) 2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_LINK_BRIDGE_H_
+#define NETLINK_LINK_BRIDGE_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Bridge flags
+ * @ingroup bridge
+ */
+enum rtnl_link_bridge_flags {
+ RTNL_BRIDGE_HAIRPIN_MODE = 0x0001,
+ RTNL_BRIDGE_BPDU_GUARD = 0x0002,
+ RTNL_BRIDGE_ROOT_BLOCK = 0x0004,
+ RTNL_BRIDGE_FAST_LEAVE = 0x0008,
+};
+
+extern struct rtnl_link *rtnl_link_bridge_alloc(void);
+
+extern int rtnl_link_is_bridge(struct rtnl_link *);
+extern int rtnl_link_bridge_has_ext_info(struct rtnl_link *);
+
+extern int rtnl_link_bridge_set_port_state(struct rtnl_link *, uint8_t );
+extern int rtnl_link_bridge_get_port_state(struct rtnl_link *);
+
+extern int rtnl_link_bridge_set_priority(struct rtnl_link *, uint16_t);
+extern int rtnl_link_bridge_get_priority(struct rtnl_link *);
+
+extern int rtnl_link_bridge_set_cost(struct rtnl_link *, uint32_t);
+extern int rtnl_link_bridge_get_cost(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_bridge_unset_flags(struct rtnl_link *, unsigned int);
+extern int rtnl_link_bridge_set_flags(struct rtnl_link *, unsigned int);
+extern int rtnl_link_bridge_get_flags(struct rtnl_link *);
+
+extern char * rtnl_link_bridge_flags2str(int, char *, size_t);
+extern int rtnl_link_bridge_str2flags(const char *);
+
+extern int rtnl_link_bridge_add(struct nl_sock *sk, const char *name);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/include/netlink/route/link/can.h b/include/netlink/route/link/can.h
new file mode 100644
index 00000000..61c9f47e
--- /dev/null
+++ b/include/netlink/route/link/can.h
@@ -0,0 +1,60 @@
+/*
+ * netlink/route/link/can.h CAN 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) 2012 Benedikt Spranger <b.spranger@linutronix.de>
+ */
+
+#ifndef NETLINK_LINK_CAN_H_
+#define NETLINK_LINK_CAN_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <linux/can/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int rtnl_link_is_can(struct rtnl_link *link);
+
+extern char *rtnl_link_can_ctrlmode2str(int, char *, size_t);
+extern int rtnl_link_can_str2ctrlmode(const char *);
+
+extern int rtnl_link_can_restart(struct rtnl_link *);
+extern int rtnl_link_can_freq(struct rtnl_link *, uint32_t *);
+extern int rtnl_link_can_state(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_can_berr_rx(struct rtnl_link *);
+extern int rtnl_link_can_berr_tx(struct rtnl_link *);
+extern int rtnl_link_can_berr(struct rtnl_link *, struct can_berr_counter *);
+
+extern int rtnl_link_can_get_bt_const(struct rtnl_link *,
+ struct can_bittiming_const *);
+extern int rtnl_link_can_get_bittiming(struct rtnl_link *,
+ struct can_bittiming *);
+extern int rtnl_link_can_set_bittiming(struct rtnl_link *,
+ struct can_bittiming *);
+
+extern int rtnl_link_can_get_bitrate(struct rtnl_link *, uint32_t *);
+extern int rtnl_link_can_set_bitrate(struct rtnl_link *, uint32_t);
+
+extern int rtnl_link_can_get_sample_point(struct rtnl_link *, uint32_t *);
+extern int rtnl_link_can_set_sample_point(struct rtnl_link *, uint32_t);
+
+extern int rtnl_link_can_get_restart_ms(struct rtnl_link *, uint32_t *);
+extern int rtnl_link_can_set_restart_ms(struct rtnl_link *, uint32_t);
+
+extern int rtnl_link_can_get_ctrlmode(struct rtnl_link *, uint32_t *);
+extern int rtnl_link_can_set_ctrlmode(struct rtnl_link *, uint32_t);
+extern int rtnl_link_can_unset_ctrlmode(struct rtnl_link *, uint32_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/inet.h b/include/netlink/route/link/inet.h
new file mode 100644
index 00000000..506542f6
--- /dev/null
+++ b/include/netlink/route/link/inet.h
@@ -0,0 +1,33 @@
+/*
+ * netlink/route/link/inet.h INET Link 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_LINK_INET_H_
+#define NETLINK_LINK_INET_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const char * rtnl_link_inet_devconf2str(int, char *, size_t);
+extern int rtnl_link_inet_str2devconf(const char *);
+
+extern int rtnl_link_inet_get_conf(struct rtnl_link *,
+ const unsigned int, uint32_t *);
+extern int rtnl_link_inet_set_conf(struct rtnl_link *,
+ const unsigned int, uint32_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/info-api.h b/include/netlink/route/link/info-api.h
index 7e18e31e..1087ad48 100644
--- a/include/netlink/route/link/info-api.h
+++ b/include/netlink/route/link/info-api.h
@@ -1,71 +1,20 @@
/*
- * netlink/route/link/info-api.h Link Info API
+ * netlink/route/link/info-api.h Link Modules 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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2013 Thomas Graf <tgraf@suug.ch>
*/
-#ifndef NETLINK_LINK_INFO_API_H_
-#define NETLINK_LINK_INFO_API_H_
+#ifndef NETLINK_DUMMY_LINK_INFO_API_H_
+#define NETLINK_DUMMY_LINK_INFO_API_H_
#include <netlink/netlink.h>
+#include <netlink/route/link.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * @ingroup link_info
- *
- * Link info operations
- */
-struct rtnl_link_info_ops
-{
- /** Name of operations, must match name on kernel side */
- char * io_name;
-
- /** Reference count (internal, do not use) */
- int io_refcnt;
-
- /** Called to assign an info type to a link.
- * Has to allocate enough resources to hold attributes. Can
- * use link->l_info to store a pointer. */
- int (*io_alloc)(struct rtnl_link *);
-
- /** Called to parse the link info attribute.
- * Must parse the attribute and assign all values to the link.
- */
- int (*io_parse)(struct rtnl_link *,
- struct nlattr *,
- struct nlattr *);
-
- /** Called when the link object is dumped.
- * Must dump the info type specific attributes. */
- void (*io_dump[NL_DUMP_MAX+1])(struct rtnl_link *,
- struct nl_dump_params *);
-
- /** Called when a link object is cloned.
- * Must clone all info type specific attributes. */
- int (*io_clone)(struct rtnl_link *, struct rtnl_link *);
-
- /** Called when construction a link netlink message.
- * Must append all info type specific attributes to the message. */
- int (*io_put_attrs)(struct nl_msg *, struct rtnl_link *);
-
- /** Called to release all resources previously allocated
- * in either io_alloc() or io_parse(). */
- void (*io_free)(struct rtnl_link *);
-
- struct rtnl_link_info_ops * io_next;
-};
-
-extern struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *);
-
-extern int rtnl_link_register_info(struct rtnl_link_info_ops *);
-extern int rtnl_link_unregister_info(struct rtnl_link_info_ops *);
+#warning "You are including a deprecated header file, include <netlink/route/link.h>."
#endif
diff --git a/include/netlink/route/link/ip6tnl.h b/include/netlink/route/link/ip6tnl.h
new file mode 100644
index 00000000..7e0c2958
--- /dev/null
+++ b/include/netlink/route/link/ip6tnl.h
@@ -0,0 +1,56 @@
+/*
+ * netlink/route/link/ip6tnl.h IP6TNL 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) 2014 Susant Sahani <susant@redhat.com>
+ */
+
+#ifndef NETLINK_LINK_IP6TNL_H_
+#define NETLINK_LINK_IP6TNL_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ extern struct rtnl_link *rtnl_link_ip6_tnl_alloc(void);
+ extern int rtnl_link_ip6_tnl_add(struct nl_sock *sk, const char *name);
+
+ extern int rtnl_link_ip6_tnl_set_link(struct rtnl_link *link, uint32_t index);
+ extern uint32_t rtnl_link_ip6_tnl_get_link(struct rtnl_link *link);
+
+ extern int rtnl_link_ip6_tnl_set_local(struct rtnl_link *link, struct in6_addr *);
+ extern int rtnl_link_ip6_tnl_get_local(struct rtnl_link *link, struct in6_addr *addr);
+
+ extern int rtnl_link_ip6_tnl_set_remote(struct rtnl_link *link, struct in6_addr *);
+ extern int rtnl_link_ip6_tnl_get_remote(struct rtnl_link *link, struct in6_addr *);
+
+ extern int rtnl_link_ip6_tnl_set_ttl(struct rtnl_link *link, uint8_t ttl);
+ extern uint8_t rtnl_link_ip6_tnl_get_ttl(struct rtnl_link *link);
+
+ extern int rtnl_link_ip6_tnl_set_tos(struct rtnl_link *link, uint8_t tos);
+ extern uint8_t rtnl_link_ip6_tnl_get_tos(struct rtnl_link *link);
+
+ extern int rtnl_link_ip6_tnl_set_encaplimit(struct rtnl_link *link, uint8_t encap_limit);
+ extern uint8_t rtnl_link_ip6_tnl_get_encaplimit(struct rtnl_link *link);
+
+ extern int rtnl_link_ip6_tnl_set_flags(struct rtnl_link *link, uint32_t flags);
+ extern uint32_t rtnl_link_ip6_tnl_get_flags(struct rtnl_link *link);
+
+ extern uint32_t rtnl_link_ip6_tnl_get_flowinfo(struct rtnl_link *link);
+ extern int rtnl_link_ip6_tnl_set_flowinfo(struct rtnl_link *link, uint32_t flowinfo);
+
+ extern int rtnl_link_ip6_tnl_set_proto(struct rtnl_link *link, uint8_t proto);
+ extern uint8_t rtnl_link_ip6_tnl_get_proto(struct rtnl_link *link);
+
+#ifdef _cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/ipgre.h b/include/netlink/route/link/ipgre.h
new file mode 100644
index 00000000..5a0a295a
--- /dev/null
+++ b/include/netlink/route/link/ipgre.h
@@ -0,0 +1,59 @@
+/*
+ * netlink/route/link/ip_gre.h IPGRE 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) 2014 Susant Sahani <susant@redhat.com>
+ */
+
+#ifndef NETLINK_LINK_IPGRE_H_
+#define NETLINK_LINK_IPGRE_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ extern struct rtnl_link *rtnl_link_ipgre_alloc(void);
+ extern int rtnl_link_ipgre_add(struct nl_sock *sk, const char *name);
+
+ extern int rtnl_link_ipgre_set_link(struct rtnl_link *link, uint32_t index);
+ extern uint32_t rtnl_link_ipgre_get_link(struct rtnl_link *link);
+
+ extern int rtnl_link_ipgre_set_iflags(struct rtnl_link *link, uint16_t iflags);
+ extern uint16_t rtnl_link_ipgre_get_iflags(struct rtnl_link *link);
+
+ extern int rtnl_link_ipgre_set_oflags(struct rtnl_link *link, uint16_t oflags);
+ extern uint16_t rtnl_link_ipgre_get_oflags(struct rtnl_link *link);
+
+ extern int rtnl_link_ipgre_set_ikey(struct rtnl_link *link, uint32_t ikey);
+ extern uint32_t rtnl_link_ipgre_get_ikey(struct rtnl_link *link);
+
+ extern int rtnl_link_ipgre_set_okey(struct rtnl_link *link, uint32_t okey);
+ extern uint32_t rtnl_link_ipgre_get_okey(struct rtnl_link *link);
+
+ extern int rtnl_link_ipgre_set_local(struct rtnl_link *link, uint32_t addr);
+ extern uint32_t rtnl_link_ipgre_get_local(struct rtnl_link *link);
+
+ extern int rtnl_link_ipgre_set_remote(struct rtnl_link *link, uint32_t addr);
+ extern uint32_t rtnl_link_ipgre_get_remote(struct rtnl_link *link);
+
+ extern int rtnl_link_ipgre_set_ttl(struct rtnl_link *link, uint8_t ttl);
+ extern uint8_t rtnl_link_ipgre_get_ttl(struct rtnl_link *link);
+
+ extern int rtnl_link_ipgre_set_tos(struct rtnl_link *link, uint8_t tos);
+ extern uint8_t rtnl_link_ipgre_get_tos(struct rtnl_link *link);
+
+ extern int rtnl_link_ipgre_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc);
+ extern uint8_t rtnl_link_ipgre_get_pmtudisc(struct rtnl_link *link);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/ipip.h b/include/netlink/route/link/ipip.h
new file mode 100644
index 00000000..ccadb874
--- /dev/null
+++ b/include/netlink/route/link/ipip.h
@@ -0,0 +1,47 @@
+/*
+ * netlink/route/link/ipip.h IPIP 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) 2014 Susant Sahani <susant@redhat.com>
+ */
+
+#ifndef NETLINK_LINK_IPIP_H_
+#define NETLINK_LINK_IPIP_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ extern struct rtnl_link *rtnl_link_ipip_alloc(void);
+ extern int rtnl_link_ipip_add(struct nl_sock *sk, const char *name);
+
+ extern uint32_t rtnl_link_ipip_get_link(struct rtnl_link *link);
+ extern int rtnl_link_ipip_set_link(struct rtnl_link *link, uint32_t index);
+
+ extern int rtnl_link_ipip_set_local(struct rtnl_link *link, uint32_t addr);
+ extern uint32_t rtnl_link_ipip_get_local(struct rtnl_link *link);
+
+ extern int rtnl_link_ipip_set_remote(struct rtnl_link *link, uint32_t addr);
+ extern uint32_t rtnl_link_ipip_get_remote(struct rtnl_link *link);
+
+ extern int rtnl_link_ipip_set_ttl(struct rtnl_link *link, uint8_t ttl);
+ extern uint8_t rtnl_link_ipip_get_ttl(struct rtnl_link *link);
+
+ extern int rtnl_link_ipip_set_tos(struct rtnl_link *link, uint8_t tos);
+ extern uint8_t rtnl_link_ipip_get_tos(struct rtnl_link *link);
+
+ extern int rtnl_link_ipip_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc);
+ extern uint8_t rtnl_link_ipip_get_pmtudisc(struct rtnl_link *link);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/ipvti.h b/include/netlink/route/link/ipvti.h
new file mode 100644
index 00000000..a3e7bbad
--- /dev/null
+++ b/include/netlink/route/link/ipvti.h
@@ -0,0 +1,43 @@
+/*
+ * netlink/route/link/ipvti.h IPVTI 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) 2014 Susant Sahani <susant@redhat.com>
+ */
+
+#ifndef NETLINK_LINK_IPVTI_H_
+#define NETLINK_LINK_IPVTI_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ extern struct rtnl_link *rtnl_link_ipvti_alloc(void);
+ extern int rtnl_link_ipvti_add(struct nl_sock *sk, const char *name);
+
+ extern int rtnl_link_ipvti_set_link(struct rtnl_link *link, uint32_t index);
+ extern uint32_t rtnl_link_ipvti_get_link(struct rtnl_link *link);
+
+ extern int rtnl_link_ipvti_set_ikey(struct rtnl_link *link, uint32_t ikey);
+ extern uint32_t rtnl_link_get_ikey(struct rtnl_link *link);
+
+ extern int rtnl_link_ipvti_set_okey(struct rtnl_link *link, uint32_t okey);
+ extern uint32_t rtnl_link_get_okey(struct rtnl_link *link);
+
+ extern int rtnl_link_ipvti_set_local(struct rtnl_link *link, uint32_t addr);
+ extern uint32_t rtnl_link_get_local(struct rtnl_link *link);
+
+ extern int rtnl_link_ipvti_set_remote(struct rtnl_link *link, uint32_t addr);
+ extern uint32_t rtnl_link_get_remote(struct rtnl_link *link);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/macvlan.h b/include/netlink/route/link/macvlan.h
new file mode 100644
index 00000000..2207c534
--- /dev/null
+++ b/include/netlink/route/link/macvlan.h
@@ -0,0 +1,46 @@
+/*
+ * netlink/route/link/macvlan.h MACVLAN 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) 2013 Michael Braun <michael-dev@fami-braun.de>
+ */
+
+#ifndef NETLINK_LINK_MACVLAN_H_
+#define NETLINK_LINK_MACVLAN_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct rtnl_link *rtnl_link_macvlan_alloc(void);
+
+extern int rtnl_link_is_macvlan(struct rtnl_link *);
+
+extern char * rtnl_link_macvlan_mode2str(int, char *, size_t);
+extern int rtnl_link_macvlan_str2mode(const char *);
+
+extern char * rtnl_link_macvlan_flags2str(int, char *, size_t);
+extern int rtnl_link_macvlan_str2flags(const char *);
+
+extern int rtnl_link_macvlan_set_mode(struct rtnl_link *,
+ uint32_t);
+extern uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *);
+
+extern int rtnl_link_macvlan_set_flags(struct rtnl_link *,
+ uint16_t);
+extern int rtnl_link_macvlan_unset_flags(struct rtnl_link *,
+ uint16_t);
+extern uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/sit.h b/include/netlink/route/link/sit.h
new file mode 100644
index 00000000..84dc44aa
--- /dev/null
+++ b/include/netlink/route/link/sit.h
@@ -0,0 +1,53 @@
+/*
+ * netlink/route/link/sit.h SIT 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) 2014 Susant Sahani <susant@redhat.com>
+ */
+
+#ifndef NETLINK_LINK_SIT_H_
+#define NETLINK_LINK_SIT_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ extern struct rtnl_link *rtnl_link_sit_alloc(void);
+ extern int rtnl_link_sit_add(struct nl_sock *sk, const char *name);
+
+ extern int rtnl_link_sit_set_link(struct rtnl_link *link, uint32_t index);
+ extern uint32_t rtnl_link_sit_get_link(struct rtnl_link *link);
+
+ extern int rtnl_link_sit_set_local(struct rtnl_link *link, uint32_t addr);
+ extern uint32_t rtnl_link_get_sit_local(struct rtnl_link *link);
+
+ extern int rtnl_link_sit_set_remote(struct rtnl_link *link, uint32_t addr);
+ extern uint32_t rtnl_link_sit_get_remote(struct rtnl_link *link);
+
+ extern int rtnl_link_sit_set_ttl(struct rtnl_link *link, uint8_t ttl);
+ extern uint8_t rtnl_link_sit_get_ttl(struct rtnl_link *link);
+
+ extern int rtnl_link_sit_set_tos(struct rtnl_link *link, uint8_t tos);
+ extern uint8_t rtnl_link_sit_get_tos(struct rtnl_link *link);
+
+ extern int rtnl_link_sit_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc);
+ extern uint8_t rtnl_link_sit_get_pmtudisc(struct rtnl_link *link);
+
+ extern int rtnl_link_sit_set_flags(struct rtnl_link *link, uint16_t flags);
+ extern uint16_t rtnl_link_sit_get_flags(struct rtnl_link *link);
+
+ int rtnl_link_sit_set_proto(struct rtnl_link *link, uint8_t proto);
+ uint8_t rtnl_link_get_proto(struct rtnl_link *link);
+
+#ifdef _cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/veth.h b/include/netlink/route/link/veth.h
new file mode 100644
index 00000000..35c2345c
--- /dev/null
+++ b/include/netlink/route/link/veth.h
@@ -0,0 +1,36 @@
+/*
+ * netlink/route/link/veth.h VETH 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) 2013 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+#ifndef NETLINK_LINK_VETH_H_
+#define NETLINK_LINK_VETH_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern struct rtnl_link *rtnl_link_veth_alloc(void);
+extern void rtnl_link_veth_release(struct rtnl_link *);
+
+extern int rtnl_link_is_veth(struct rtnl_link *);
+
+extern struct rtnl_link *rtnl_link_veth_get_peer(struct rtnl_link *);
+extern int rtnl_link_veth_add(struct nl_sock *sock, const char *name,
+ const char *peer, pid_t pid);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/link/vlan.h b/include/netlink/route/link/vlan.h
index a3ad76db..4ec751e5 100644
--- a/include/netlink/route/link/vlan.h
+++ b/include/netlink/route/link/vlan.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_LINK_VLAN_H_
@@ -27,17 +27,24 @@ struct vlan_map
#define VLAN_PRIO_MAX 7
+extern struct rtnl_link *rtnl_link_vlan_alloc(void);
+
+extern int rtnl_link_is_vlan(struct rtnl_link *);
+
extern char * rtnl_link_vlan_flags2str(int, char *, size_t);
extern int rtnl_link_vlan_str2flags(const char *);
-extern int rtnl_link_vlan_set_id(struct rtnl_link *, int);
+extern int rtnl_link_vlan_set_protocol(struct rtnl_link *link, uint16_t);
+extern int rtnl_link_vlan_get_protocol(struct rtnl_link *link);
+
+extern int rtnl_link_vlan_set_id(struct rtnl_link *, uint16_t);
extern int rtnl_link_vlan_get_id(struct rtnl_link *);
extern int rtnl_link_vlan_set_flags(struct rtnl_link *,
unsigned int);
extern int rtnl_link_vlan_unset_flags(struct rtnl_link *,
unsigned int);
-extern unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *);
+extern int rtnl_link_vlan_get_flags(struct rtnl_link *);
extern int rtnl_link_vlan_set_ingress_map(struct rtnl_link *,
int, uint32_t);
diff --git a/include/netlink/route/link/vxlan.h b/include/netlink/route/link/vxlan.h
new file mode 100644
index 00000000..f7f7b609
--- /dev/null
+++ b/include/netlink/route/link/vxlan.h
@@ -0,0 +1,86 @@
+/*
+ * netlink/route/link/vxlan.h VXLAN 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) 2013 Yasunobu Chiba <yasu@dsl.gr.jp>
+ */
+
+#ifndef NETLINK_LINK_VXLAN_H_
+#define NETLINK_LINK_VXLAN_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define VXLAN_ID_MAX 16777215
+
+extern struct rtnl_link *rtnl_link_vxlan_alloc(void);
+
+extern int rtnl_link_is_vxlan(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_id(struct rtnl_link *, uint32_t);
+extern int rtnl_link_vxlan_get_id(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_vxlan_set_group(struct rtnl_link *, struct nl_addr *);
+extern int rtnl_link_vxlan_get_group(struct rtnl_link *, struct nl_addr **);
+
+extern int rtnl_link_vxlan_set_link(struct rtnl_link *, uint32_t);
+extern int rtnl_link_vxlan_get_link(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_vxlan_set_local(struct rtnl_link *, struct nl_addr *);
+extern int rtnl_link_vxlan_get_local(struct rtnl_link *, struct nl_addr **);
+
+extern int rtnl_link_vxlan_set_ttl(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_ttl(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_tos(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_tos(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_learning(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_learning(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_learning(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_learning(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_ageing(struct rtnl_link *, uint32_t);
+extern int rtnl_link_vxlan_get_ageing(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_vxlan_set_limit(struct rtnl_link *, uint32_t);
+extern int rtnl_link_vxlan_get_limit(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_vxlan_set_port_range(struct rtnl_link *,
+ struct ifla_vxlan_port_range *);
+extern int rtnl_link_vxlan_get_port_range(struct rtnl_link *,
+ struct ifla_vxlan_port_range *);
+
+extern int rtnl_link_vxlan_set_proxy(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_proxy(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_proxy(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_proxy(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_rsc(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_rsc(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_rsc(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_rsc(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_l2miss(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_l2miss(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_l2miss(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_l2miss(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_l3miss(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_l3miss(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_l3miss(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_l3miss(struct rtnl_link *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/neighbour.h b/include/netlink/route/neighbour.h
index 698539a0..1d1179b3 100644
--- a/include/netlink/route/neighbour.h
+++ b/include/netlink/route/neighbour.h
@@ -29,6 +29,8 @@ extern int rtnl_neigh_alloc_cache(struct nl_sock *, struct nl_cache **);
extern struct rtnl_neigh *rtnl_neigh_get(struct nl_cache *, int,
struct nl_addr *);
+extern int rtnl_neigh_parse(struct nlmsghdr *, struct rtnl_neigh **);
+
extern char * rtnl_neigh_state2str(int, char *, size_t);
extern int rtnl_neigh_str2state(const char *);
diff --git a/include/netlink/route/pktloc.h b/include/netlink/route/pktloc.h
index 28e1dc21..c3768ce9 100644
--- a/include/netlink/route/pktloc.h
+++ b/include/netlink/route/pktloc.h
@@ -25,17 +25,22 @@ extern "C" {
struct rtnl_pktloc
{
char * name;
- uint8_t align:4;
- uint8_t layer:4;
- uint8_t flags;
+ uint8_t layer;
+ uint8_t shift;
uint16_t offset;
+ uint16_t align;
uint32_t mask;
+ uint32_t refcnt;
struct nl_list_head list;
};
-extern int rtnl_pktloc_lookup(const char *, struct rtnl_pktloc **);
-
+extern int rtnl_pktloc_lookup(const char *, struct rtnl_pktloc **);
+extern struct rtnl_pktloc *rtnl_pktloc_alloc(void);
+extern void rtnl_pktloc_put(struct rtnl_pktloc *);
+extern int rtnl_pktloc_add(struct rtnl_pktloc *);
+extern void rtnl_pktloc_foreach(void (*cb)(struct rtnl_pktloc *, void *),
+ void *);
#ifdef __cplusplus
}
diff --git a/include/netlink/route/qdisc-modules.h b/include/netlink/route/qdisc-modules.h
deleted file mode 100644
index 769625eb..00000000
--- a/include/netlink/route/qdisc-modules.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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 <tgraf@suug.ch>
- */
-
-#ifndef NETLINK_QDISC_MODULES_H_
-#define NETLINK_QDISC_MODULES_H_
-
-#include <netlink/netlink.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- * Qdisc Operations
- * @ingroup qdisc
- */
-struct rtnl_qdisc_ops
-{
- /**
- * Kind/Name of Qdisc
- */
- char qo_kind[32];
-
- /**
- * Dump callbacks
- */
- void (*qo_dump[NL_DUMP_MAX+1])(struct rtnl_qdisc *,
- struct nl_dump_params *);
-
- /**
- * Must return the contents supposed to be in TCA_OPTIONS
- */
- struct nl_msg *(*qo_get_opts)(struct rtnl_qdisc *);
-
- int (*qo_build_msg)(struct rtnl_qdisc *, struct nl_msg *);
-
- /**
- * 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
index 5acd6e1c..10b85c5d 100644
--- a/include/netlink/route/qdisc.h
+++ b/include/netlink/route/qdisc.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_QDISC_H_
@@ -22,49 +22,49 @@ extern "C" {
struct rtnl_qdisc;
-extern struct nl_object_ops qdisc_obj_ops;
-
-extern struct rtnl_qdisc *rtnl_qdisc_alloc(void);
+extern struct rtnl_qdisc *
+ rtnl_qdisc_alloc(void);
extern void rtnl_qdisc_put(struct rtnl_qdisc *);
extern int rtnl_qdisc_alloc_cache(struct nl_sock *, struct nl_cache **);
-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);
+
+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);
extern int rtnl_qdisc_build_add_request(struct rtnl_qdisc *, int,
struct nl_msg **);
extern int rtnl_qdisc_add(struct nl_sock *, struct rtnl_qdisc *, int);
-extern int rtnl_qdisc_build_change_request(struct rtnl_qdisc *,
+extern int rtnl_qdisc_build_update_request(struct rtnl_qdisc *,
struct rtnl_qdisc *,
- struct nl_msg **);
-extern int rtnl_qdisc_change(struct nl_sock *, struct rtnl_qdisc *,
- struct rtnl_qdisc *);
+ int, struct nl_msg **);
+
+extern int rtnl_qdisc_update(struct nl_sock *, struct rtnl_qdisc *,
+ struct rtnl_qdisc *, int);
extern int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *,
struct nl_msg **);
extern int rtnl_qdisc_delete(struct nl_sock *, struct rtnl_qdisc *);
-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);
-
-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 *);
-
-extern struct nl_msg * rtnl_qdisc_get_opts(struct rtnl_qdisc *);
+/* Deprecated functions */
+extern void rtnl_qdisc_foreach_child(struct rtnl_qdisc *, struct nl_cache *,
+ void (*cb)(struct nl_object *, void *),
+ void *) __attribute__ ((deprecated));
+
+extern void rtnl_qdisc_foreach_cls(struct rtnl_qdisc *, struct nl_cache *,
+ void (*cb)(struct nl_object *, void *),
+ void *) __attribute__ ((deprecated));
+
+extern int rtnl_qdisc_build_change_request(struct rtnl_qdisc *,
+ struct rtnl_qdisc *,
+ struct nl_msg **)
+ __attribute__ ((deprecated));
+
+extern int rtnl_qdisc_change(struct nl_sock *, struct rtnl_qdisc *,
+ struct rtnl_qdisc *) __attribute__ ((deprecated));
#ifdef __cplusplus
}
diff --git a/include/netlink/route/sch/cbq.h b/include/netlink/route/qdisc/cbq.h
index 3dbdd2dc..3dbdd2dc 100644
--- a/include/netlink/route/sch/cbq.h
+++ b/include/netlink/route/qdisc/cbq.h
diff --git a/include/netlink/route/sch/dsmark.h b/include/netlink/route/qdisc/dsmark.h
index de65496e..06bd9d38 100644
--- a/include/netlink/route/sch/dsmark.h
+++ b/include/netlink/route/qdisc/dsmark.h
@@ -6,20 +6,22 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_DSMARK_H_
#define NETLINK_DSMARK_H_
#include <netlink/netlink.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/class.h>
#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_bitmask(struct rtnl_class *, uint8_t);
+extern int rtnl_class_dsmark_get_bitmask(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 *);
diff --git a/include/netlink/route/sch/fifo.h b/include/netlink/route/qdisc/fifo.h
index c18dd796..c0334273 100644
--- a/include/netlink/route/sch/fifo.h
+++ b/include/netlink/route/qdisc/fifo.h
@@ -13,6 +13,7 @@
#define NETLINK_FIFO_H_
#include <netlink/netlink.h>
+#include <netlink/route/qdisc.h>
#ifdef __cplusplus
extern "C" {
diff --git a/include/netlink/route/qdisc/fq_codel.h b/include/netlink/route/qdisc/fq_codel.h
new file mode 100644
index 00000000..d2c3d25e
--- /dev/null
+++ b/include/netlink/route/qdisc/fq_codel.h
@@ -0,0 +1,44 @@
+/*
+ * netlink/route/sch/fq_codel.h fq_codel
+ *
+ * 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) 2013 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+#ifndef NETLINK_FQ_CODEL_H_
+#define NETLINK_FQ_CODEL_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/qdisc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int rtnl_qdisc_fq_codel_set_limit(struct rtnl_qdisc *, int);
+extern int rtnl_qdisc_fq_codel_get_limit(struct rtnl_qdisc *);
+
+extern int rtnl_qdisc_fq_codel_set_target(struct rtnl_qdisc *, uint32_t);
+extern uint32_t rtnl_qdisc_fq_codel_get_target(struct rtnl_qdisc *);
+
+extern int rtnl_qdisc_fq_codel_set_interval(struct rtnl_qdisc *, uint32_t);
+extern uint32_t rtnl_qdisc_fq_codel_get_interval(struct rtnl_qdisc *);
+
+extern int rtnl_qdisc_fq_codel_set_quantum(struct rtnl_qdisc *, uint32_t);
+extern uint32_t rtnl_qdisc_fq_codel_get_quantum(struct rtnl_qdisc *);
+
+extern int rtnl_qdisc_fq_codel_set_flows(struct rtnl_qdisc *, int);
+extern int rtnl_qdisc_fq_codel_get_flows(struct rtnl_qdisc *);
+
+extern int rtnl_qdisc_fq_codel_set_ecn(struct rtnl_qdisc *, int);
+extern int rtnl_qdisc_fq_codel_get_ecn(struct rtnl_qdisc *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/qdisc/htb.h b/include/netlink/route/qdisc/htb.h
new file mode 100644
index 00000000..c909f843
--- /dev/null
+++ b/include/netlink/route/qdisc/htb.h
@@ -0,0 +1,49 @@
+/*
+ * 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-2011 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2005 Petr Gotthard <petr.gotthard@siemens.com>
+ * Copyright (c) 2005 Siemens AG Oesterreich
+ */
+
+#ifndef NETLINK_HTB_H_
+#define NETLINK_HTB_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/tc.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/class.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *);
+extern int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *, uint32_t);
+extern uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *);
+extern int rtnl_htb_set_defcls(struct rtnl_qdisc *, uint32_t);
+
+extern uint32_t rtnl_htb_get_prio(struct rtnl_class *);
+extern int rtnl_htb_set_prio(struct rtnl_class *, uint32_t);
+extern uint32_t rtnl_htb_get_rate(struct rtnl_class *);
+extern int rtnl_htb_set_rate(struct rtnl_class *, uint32_t);
+extern uint32_t rtnl_htb_get_ceil(struct rtnl_class *);
+extern int rtnl_htb_set_ceil(struct rtnl_class *, uint32_t);
+extern uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *);
+extern int rtnl_htb_set_rbuffer(struct rtnl_class *, uint32_t);
+extern uint32_t rtnl_htb_get_cbuffer(struct rtnl_class *);
+extern int rtnl_htb_set_cbuffer(struct rtnl_class *, uint32_t);
+extern uint32_t rtnl_htb_get_quantum(struct rtnl_class *);
+extern int rtnl_htb_set_quantum(struct rtnl_class *, uint32_t);
+extern int rtnl_htb_get_level(struct rtnl_class *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/sch/netem.h b/include/netlink/route/qdisc/netem.h
index c293777e..4b071bf1 100644
--- a/include/netlink/route/sch/netem.h
+++ b/include/netlink/route/qdisc/netem.h
@@ -13,53 +13,54 @@
#define NETLINK_NETEM_H_
#include <netlink/netlink.h>
+#include <netlink/route/qdisc.h>
#ifdef __cplusplus
extern "C" {
#endif
-extern int rtnl_netem_set_limit(struct rtnl_qdisc *, int);
+extern void 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 void 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 void 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 void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *, int);
extern int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *);
/* Corruption */
-extern int rtnl_netem_set_corruption_probability(struct rtnl_qdisc *, int);
+extern void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *, int);
extern int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *);
-extern int rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *, int);
+extern void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *, int);
extern int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *);
/* Packet Loss */
-extern int rtnl_netem_set_loss(struct rtnl_qdisc *, int);
+extern void 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 void 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 void 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 void 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 void 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 void 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 void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *, int);
extern int rtnl_netem_get_delay_correlation(struct rtnl_qdisc *);
/* Delay Distribution */
diff --git a/include/netlink/route/qdisc/plug.h b/include/netlink/route/qdisc/plug.h
new file mode 100644
index 00000000..40f7e53b
--- /dev/null
+++ b/include/netlink/route/qdisc/plug.h
@@ -0,0 +1,31 @@
+/*
+ * netlink/route/qdisc/plug.c PLUG 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) 2012 Shriram Rajagopalan <rshriram@cs.ubc.ca>
+ */
+
+#ifndef NETLINK_PLUG_H_
+#define NETLINK_PLUG_H_
+
+#include <netlink/netlink.h>
+#include <netlink/route/qdisc.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int rtnl_qdisc_plug_set_limit(struct rtnl_qdisc *, int);
+extern int rtnl_qdisc_plug_buffer(struct rtnl_qdisc *);
+extern int rtnl_qdisc_plug_release_one(struct rtnl_qdisc *);
+extern int rtnl_qdisc_plug_release_indefinite(struct rtnl_qdisc *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/include/netlink/route/sch/prio.h b/include/netlink/route/qdisc/prio.h
index ff35b079..636a8f92 100644
--- a/include/netlink/route/sch/prio.h
+++ b/include/netlink/route/qdisc/prio.h
@@ -6,13 +6,14 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_PRIO_H_
#define NETLINK_PRIO_H_
#include <netlink/netlink.h>
+#include <netlink/route/qdisc.h>
#ifdef __cplusplus
extern "C" {
@@ -38,7 +39,7 @@ extern "C" {
/** @} */
-extern int rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *, int);
+extern void 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 *);
diff --git a/include/netlink/route/sch/red.h b/include/netlink/route/qdisc/red.h
index a4e8642f..a4e8642f 100644
--- a/include/netlink/route/sch/red.h
+++ b/include/netlink/route/qdisc/red.h
diff --git a/include/netlink/route/sch/sfq.h b/include/netlink/route/qdisc/sfq.h
index 19b38173..77d2e293 100644
--- a/include/netlink/route/sch/sfq.h
+++ b/include/netlink/route/qdisc/sfq.h
@@ -6,25 +6,26 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_SFQ_H_
#define NETLINK_SFQ_H_
#include <netlink/netlink.h>
+#include <netlink/route/qdisc.h>
#ifdef __cplusplus
extern "C" {
#endif
-extern int rtnl_sfq_set_quantum(struct rtnl_qdisc *, int);
+extern void 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 void 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 void 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 *);
diff --git a/include/netlink/route/sch/tbf.h b/include/netlink/route/qdisc/tbf.h
index 8e0ea1ed..ce31c548 100644
--- a/include/netlink/route/sch/tbf.h
+++ b/include/netlink/route/qdisc/tbf.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_TBF_H_
@@ -14,19 +14,17 @@
#include <netlink/netlink.h>
#include <netlink/route/tc.h>
+#include <netlink/route/qdisc.h>
#ifdef __cplusplus
extern "C" {
#endif
-extern int rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *, int);
+extern void 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 void 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 *);
diff --git a/include/netlink/route/route.h b/include/netlink/route/route.h
index 5729cd74..477250dd 100644
--- a/include/netlink/route/route.h
+++ b/include/netlink/route/route.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_ROUTE_H_
@@ -24,7 +24,12 @@
extern "C" {
#endif
-/* flags */
+/**
+ * @ingroup route
+ * When passed to rtnl_route_alloc_cache() the cache will
+ * correspond to the contents of the routing cache instead
+ * of the actual routes.
+ */
#define ROUTE_CACHE_CONTENT 1
struct rtnl_route;
@@ -49,7 +54,6 @@ extern int rtnl_route_alloc_cache(struct nl_sock *, int, int,
struct nl_cache **);
extern void rtnl_route_get(struct rtnl_route *);
-extern void rtnl_route_put(struct rtnl_route *);
extern int rtnl_route_parse(struct nlmsghdr *, struct rtnl_route **);
extern int rtnl_route_build_msg(struct nl_msg *, struct rtnl_route *);
diff --git a/include/netlink/route/rule.h b/include/netlink/route/rule.h
index 928dc0f1..760b782a 100644
--- a/include/netlink/route/rule.h
+++ b/include/netlink/route/rule.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2010 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_RULE_H_
@@ -16,6 +16,7 @@
#include <netlink/cache.h>
#include <netlink/addr.h>
#include <netlink/route/route.h>
+#include <linux/fib_rules.h>
#ifdef __cplusplus
extern "C" {
@@ -42,34 +43,30 @@ extern int rtnl_rule_delete(struct nl_sock *, 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 *);
-extern void rtnl_rule_set_mark(struct rtnl_rule *, uint64_t);
-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 void rtnl_rule_set_prio(struct rtnl_rule *, uint32_t);
+extern uint32_t rtnl_rule_get_prio(struct rtnl_rule *);
+extern void rtnl_rule_set_mark(struct rtnl_rule *, uint32_t);
+extern uint32_t rtnl_rule_get_mark(struct rtnl_rule *);
+extern void rtnl_rule_set_mask(struct rtnl_rule *, uint32_t);
+extern uint32_t rtnl_rule_get_mask(struct rtnl_rule *);
+extern void rtnl_rule_set_table(struct rtnl_rule *, uint32_t);
+extern uint32_t rtnl_rule_get_table(struct rtnl_rule *);
+extern void rtnl_rule_set_dsfield(struct rtnl_rule *, uint8_t);
+extern uint8_t 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 void rtnl_rule_set_action(struct rtnl_rule *, uint8_t);
+extern uint8_t 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 int rtnl_rule_set_oif(struct rtnl_rule *, const char *);
+extern char * rtnl_rule_get_oif(struct rtnl_rule *);
extern void rtnl_rule_set_realms(struct rtnl_rule *, uint32_t);
extern uint32_t rtnl_rule_get_realms(struct rtnl_rule *);
+extern void rtnl_rule_set_goto(struct rtnl_rule *, uint32_t);
+extern uint32_t rtnl_rule_get_goto(struct rtnl_rule *);
#ifdef __cplusplus
}
diff --git a/include/netlink/route/sch/htb.h b/include/netlink/route/sch/htb.h
deleted file mode 100644
index d44f039a..00000000
--- a/include/netlink/route/sch/htb.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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 <tgraf@suug.ch>
- * Copyright (c) 2005 Petr Gotthard <petr.gotthard@siemens.com>
- * Copyright (c) 2005 Siemens AG Oesterreich
- */
-
-#ifndef NETLINK_HTB_H_
-#define NETLINK_HTB_H_
-
-#include <netlink/netlink.h>
-#include <netlink/route/tc.h>
-
-#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 *, uint32_t quantum);
-extern void rtnl_htb_set_overhead(struct rtnl_class *, uint8_t overhead);
-extern void rtnl_htb_set_mpu(struct rtnl_class *, uint8_t mpu);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/include/netlink/route/tc-api.h b/include/netlink/route/tc-api.h
new file mode 100644
index 00000000..b7771b50
--- /dev/null
+++ b/include/netlink/route/tc-api.h
@@ -0,0 +1,21 @@
+/*
+ * netlink/route/tc-api.h Traffic Control 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) 2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_DUMMY_TC_API_H_
+#define NETLINK_DUMMY_TC_API_H_
+
+#include <netlink/netlink.h>
+#include <netlink/msg.h>
+#include <netlink/route/tc.h>
+
+#warning "You are including a deprecated header file, include <netlink/route/tc.h>."
+
+#endif
diff --git a/include/netlink/route/tc.h b/include/netlink/route/tc.h
index 3cb876f5..870c1f26 100644
--- a/include/netlink/route/tc.h
+++ b/include/netlink/route/tc.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_TC_H_
@@ -15,46 +15,98 @@
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/data.h>
+#include <netlink/route/link.h>
+#include <linux/pkt_sched.h>
+#include <linux/pkt_cls.h>
#ifdef __cplusplus
extern "C" {
#endif
+enum rtnl_tc_type {
+ RTNL_TC_TYPE_QDISC,
+ RTNL_TC_TYPE_CLASS,
+ RTNL_TC_TYPE_CLS,
+ RTNL_TC_TYPE_ACT,
+ __RTNL_TC_TYPE_MAX,
+};
+
+#define RTNL_TC_TYPE_MAX (__RTNL_TC_TYPE_MAX - 1)
+
/**
- * TC statistics identifiers
+ * Compute tc handle based on major and minor parts
* @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 TC_HANDLE(maj, min) (TC_H_MAJ((maj) << 16) | TC_H_MIN(min))
-#define RTNL_TC_STATS_MAX (__RTNL_TC_STATS_MAX - 1)
+/**
+ * Traffic control object
+ * @ingroup tc
+ */
+struct rtnl_tc;
-extern int rtnl_tc_calc_txtime(int, int);
-extern int rtnl_tc_calc_bufsize(int, int);
-extern int rtnl_tc_calc_cell_log(int);
+/**
+ * Macro to cast qdisc/class/classifier to tc object
+ * @ingroup tc
+ *
+ * @code
+ * rtnl_tc_set_mpu(TC_CAST(qdisc), 40);
+ * @endcode
+ */
+#define TC_CAST(ptr) ((struct rtnl_tc *) (ptr))
/**
- * Number of entries in a transmission time lookup table
+ * Traffic control statistical identifier
* @ingroup tc
+ *
+ * @code
+ * uint64_t n = rtnl_tc_get_stat(TC_CAST(class), RTNL_TC_PACKETS);
+ * @endcode
*/
-#define RTNL_TC_RTABLE_SIZE 256
+enum rtnl_tc_stat {
+ RTNL_TC_PACKETS, /**< Number of packets seen */
+ RTNL_TC_BYTES, /**< Total bytes seen */
+ RTNL_TC_RATE_BPS, /**< Current bits/s (rate estimator) */
+ RTNL_TC_RATE_PPS, /**< Current packet/s (rate estimator) */
+ RTNL_TC_QLEN, /**< Current queue length */
+ RTNL_TC_BACKLOG, /**< Current backlog length */
+ RTNL_TC_DROPS, /**< Total number of packets dropped */
+ RTNL_TC_REQUEUES, /**< Total number of requeues */
+ RTNL_TC_OVERLIMITS, /**< Total number of overlimits */
+ __RTNL_TC_STATS_MAX,
+};
+
+#define RTNL_TC_STATS_MAX (__RTNL_TC_STATS_MAX - 1)
-extern int rtnl_tc_build_rate_table(uint32_t *, uint8_t, uint8_t, int, int);
+extern void rtnl_tc_set_ifindex(struct rtnl_tc *, int);
+extern int rtnl_tc_get_ifindex(struct rtnl_tc *);
+extern void rtnl_tc_set_link(struct rtnl_tc *, struct rtnl_link *);
+extern struct rtnl_link *rtnl_tc_get_link(struct rtnl_tc *);
+extern void rtnl_tc_set_mtu(struct rtnl_tc *, uint32_t);
+extern uint32_t rtnl_tc_get_mtu(struct rtnl_tc *);
+extern void rtnl_tc_set_mpu(struct rtnl_tc *, uint32_t);
+extern uint32_t rtnl_tc_get_mpu(struct rtnl_tc *);
+extern void rtnl_tc_set_overhead(struct rtnl_tc *, uint32_t);
+extern uint32_t rtnl_tc_get_overhead(struct rtnl_tc *);
+extern void rtnl_tc_set_linktype(struct rtnl_tc *, uint32_t);
+extern uint32_t rtnl_tc_get_linktype(struct rtnl_tc *);
+extern void rtnl_tc_set_handle(struct rtnl_tc *, uint32_t);
+extern uint32_t rtnl_tc_get_handle(struct rtnl_tc *);
+extern void rtnl_tc_set_parent(struct rtnl_tc *, uint32_t);
+extern uint32_t rtnl_tc_get_parent(struct rtnl_tc *);
+extern int rtnl_tc_set_kind(struct rtnl_tc *, const char *);
+extern char * rtnl_tc_get_kind(struct rtnl_tc *);
+extern uint64_t rtnl_tc_get_stat(struct rtnl_tc *, enum rtnl_tc_stat);
+extern int rtnl_tc_calc_txtime(int, int);
+extern int rtnl_tc_calc_bufsize(int, int);
+extern int rtnl_tc_calc_cell_log(int);
-/* TC Handle Translations */
+extern int rtnl_tc_read_classid_file(void);
extern char * rtnl_tc_handle2str(uint32_t, char *, size_t);
extern int rtnl_tc_str2handle(const char *, uint32_t *);
+extern int rtnl_classid_generate(const char *, uint32_t *,
+ uint32_t);
#ifdef __cplusplus
}
diff --git a/include/netlink/socket.h b/include/netlink/socket.h
index 7e71aed7..1007eba0 100644
--- a/include/netlink/socket.h
+++ b/include/netlink/socket.h
@@ -23,7 +23,7 @@ extern struct nl_sock * nl_socket_alloc(void);
extern struct nl_sock * nl_socket_alloc_cb(struct nl_cb *);
extern void nl_socket_free(struct nl_sock *);
-extern uint32_t nl_socket_get_local_port(struct nl_sock *);
+extern uint32_t nl_socket_get_local_port(const struct nl_sock *);
extern void nl_socket_set_local_port(struct nl_sock *, uint32_t);
extern int nl_socket_add_memberships(struct nl_sock *, int, ...);
@@ -34,18 +34,23 @@ extern int nl_socket_drop_membership(struct nl_sock *,
extern void nl_join_groups(struct nl_sock *, int);
-extern uint32_t nl_socket_get_peer_port(struct nl_sock *);
+extern uint32_t nl_socket_get_peer_port(const struct nl_sock *);
extern void nl_socket_set_peer_port(struct nl_sock *,
uint32_t);
-
-extern struct nl_cb * nl_socket_get_cb(struct nl_sock *);
+extern uint32_t nl_socket_get_peer_groups(const struct nl_sock *sk);
+extern void nl_socket_set_peer_groups(struct nl_sock *sk, uint32_t groups);
+extern struct nl_cb * nl_socket_get_cb(const struct nl_sock *);
extern void nl_socket_set_cb(struct nl_sock *,
struct nl_cb *);
extern int nl_socket_modify_cb(struct nl_sock *, enum nl_cb_type,
enum nl_cb_kind,
nl_recvmsg_msg_cb_t, void *);
+extern int nl_socket_modify_err_cb(struct nl_sock *, enum nl_cb_kind,
+ nl_recvmsg_err_cb_t, void *);
extern int nl_socket_set_buffer_size(struct nl_sock *, int, int);
+extern int nl_socket_set_msg_buf_size(struct nl_sock *, size_t);
+extern size_t nl_socket_get_msg_buf_size(struct nl_sock *);
extern int nl_socket_set_passcred(struct nl_sock *, int);
extern int nl_socket_recv_pktinfo(struct nl_sock *, int);
@@ -54,8 +59,8 @@ extern unsigned int nl_socket_use_seq(struct nl_sock *);
extern void nl_socket_disable_auto_ack(struct nl_sock *);
extern void nl_socket_enable_auto_ack(struct nl_sock *);
-extern int nl_socket_get_fd(struct nl_sock *);
-extern int nl_socket_set_nonblocking(struct nl_sock *);
+extern int nl_socket_get_fd(const struct nl_sock *);
+extern int nl_socket_set_nonblocking(const struct nl_sock *);
extern void nl_socket_enable_msg_peek(struct nl_sock *);
extern void nl_socket_disable_msg_peek(struct nl_sock *);
diff --git a/include/netlink/types.h b/include/netlink/types.h
index 2e0b9c36..09cc5bd9 100644
--- a/include/netlink/types.h
+++ b/include/netlink/types.h
@@ -1,12 +1,12 @@
/*
- * netlink/netlink-types.h Netlink Types
+ * netlink/types.h Definition of public 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 <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
#ifndef __NETLINK_TYPES_H_
@@ -15,21 +15,20 @@
#include <stdio.h>
/**
- * Dumping types (dp_type)
* @ingroup utils
+ * Enumeration of dumping variations (dp_type)
*/
enum nl_dump_type {
NL_DUMP_LINE, /**< Dump object briefly on one line */
NL_DUMP_DETAILS, /**< Dump all attributes but no statistics */
NL_DUMP_STATS, /**< Dump all attributes including statistics */
- NL_DUMP_ENV, /**< Dump all attribtues as env variables */
__NL_DUMP_MAX,
};
#define NL_DUMP_MAX (__NL_DUMP_MAX - 1)
/**
- * Dumping parameters
* @ingroup utils
+ * Dumping parameters
*/
struct nl_dump_params
{
diff --git a/include/netlink/utils.h b/include/netlink/utils.h
index 480bab61..6b4b7874 100644
--- a/include/netlink/utils.h
+++ b/include/netlink/utils.h
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_UTILS_H_
@@ -31,24 +31,32 @@ extern "C" {
#define NL_PROB_MIN 0x0
/**
- * Upper probability limit
+ * Upper probability limit nl_dump_type
* @ingroup utils
*/
#define NL_PROB_MAX 0xffffffff
/** @} */
+enum {
+ NL_BYTE_RATE,
+ NL_BIT_RATE,
+};
+
/* unit pretty-printing */
extern double nl_cancel_down_bytes(unsigned long long, char **);
extern double nl_cancel_down_bits(unsigned long long, char **);
+extern int nl_rate2str(unsigned long long, int, char *, size_t);
extern double nl_cancel_down_us(uint32_t, char **);
/* generic unit translations */
extern long nl_size2int(const char *);
+extern char * nl_size2str(const size_t, char *, const size_t);
extern long nl_prob2int(const char *);
/* time translations */
-extern int nl_get_hz(void);
+extern int nl_get_user_hz(void);
+extern int nl_get_psched_hz(void);
extern uint32_t nl_us2ticks(uint32_t);
extern uint32_t nl_ticks2us(uint32_t);
extern int nl_str2msec(const char *, uint64_t *);
@@ -71,6 +79,45 @@ extern void nl_new_line(struct nl_dump_params *);
extern void nl_dump(struct nl_dump_params *, const char *, ...);
extern void nl_dump_line(struct nl_dump_params *, const char *, ...);
+enum {
+ NL_CAPABILITY_NONE,
+
+ /**
+ * rtnl_route_build_msg() no longer guesses the route scope
+ * if explicitly set to RT_SCOPE_NOWHERE.
+ * @ingroup utils
+ */
+ NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE = 1,
+#define NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE
+
+ /**
+ * rtnl_link_veth_get_peer() now returns a reference that is owned by the
+ * caller and must be released by the caller with rtnl_link_put().
+ */
+ NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE = 2,
+#define NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE
+
+ /**
+ * rtnl_u32_add_action() and rtnl_basic_add_action() now grab a reference to act
+ * caller are free to release its own
+ */
+ NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE = 3,
+#define NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE
+
+ /**
+ * Indicate that the local port is unspecified until the user accesses
+ * it (via nl_socket_get_local_port()) or until nl_connect(). More importantly,
+ * if the port is left unspecified, nl_connect() will retry generating another
+ * port when bind() fails with ADDRINUSE.
+ */
+ NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE = 4,
+#define NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE
+
+ __NL_CAPABILITY_MAX
+#define NL_CAPABILITY_MAX (__NL_CAPABILITY_MAX - 1)
+};
+int nl_has_capability (int capability);
+
#ifdef __cplusplus
}
#endif
diff --git a/include/netlink/version.h b/include/netlink/version.h
index 84af8f31..a8094420 100644
--- a/include/netlink/version.h
+++ b/include/netlink/version.h
@@ -1,18 +1,37 @@
/*
- * netlink/version.h Compile Time Versioning Information
+ * netlink/version.h Versioning Information
*
* 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) 2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2011 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_VERSION_H_
#define NETLINK_VERSION_H_
-#define LIBNL_STRING "libnl 2.0"
-#define LIBNL_VERSION "2.0"
+/* Compile Time Versioning Information */
+
+#define LIBNL_STRING "libnl 3.2.25"
+#define LIBNL_VERSION "3.2.25"
+
+#define LIBNL_VER_MAJ 3
+#define LIBNL_VER_MIN 2
+#define LIBNL_VER_MIC 25
+#define LIBNL_VER(maj,min) ((maj) << 8 | (min))
+#define LIBNL_VER_NUM LIBNL_VER(LIBNL_VER_MAJ, LIBNL_VER_MIN)
+
+#define LIBNL_CURRENT 220
+#define LIBNL_REVISION 0
+#define LIBNL_AGE 20
+
+/* Run-time version information */
+
+extern const int nl_ver_num;
+extern const int nl_ver_maj;
+extern const int nl_ver_min;
+extern const int nl_ver_mic;
#endif
diff --git a/include/netlink/version.h.in b/include/netlink/version.h.in
index 7bd38ccf..35bf2aa8 100644
--- a/include/netlink/version.h.in
+++ b/include/netlink/version.h.in
@@ -1,18 +1,37 @@
/*
- * netlink/version.h Compile Time Versioning Information
+ * netlink/version.h Versioning Information
*
* 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) 2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2011 Thomas Graf <tgraf@suug.ch>
*/
#ifndef NETLINK_VERSION_H_
#define NETLINK_VERSION_H_
+/* Compile Time Versioning Information */
+
#define LIBNL_STRING "@PACKAGE_STRING@"
#define LIBNL_VERSION "@PACKAGE_VERSION@"
+#define LIBNL_VER_MAJ @MAJ_VERSION@
+#define LIBNL_VER_MIN @MIN_VERSION@
+#define LIBNL_VER_MIC @MIC_VERSION@
+#define LIBNL_VER(maj,min) ((maj) << 8 | (min))
+#define LIBNL_VER_NUM LIBNL_VER(LIBNL_VER_MAJ, LIBNL_VER_MIN)
+
+#define LIBNL_CURRENT @LT_CURRENT@
+#define LIBNL_REVISION @LT_REVISION@
+#define LIBNL_AGE @LT_AGE@
+
+/* Run-time version information */
+
+extern const int nl_ver_num;
+extern const int nl_ver_maj;
+extern const int nl_ver_min;
+extern const int nl_ver_mic;
+
#endif
diff --git a/lib/.gitignore b/lib/.gitignore
index 2a450e8d..e1abf186 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -1,2 +1,3 @@
libnl.so*
libnl-*.so*
+lex.yy.c
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 92a916e3..1ae37a93 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -1,57 +1,134 @@
# -*- Makefile -*-
-AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\"
+AM_CPPFLAGS = \
+ -Wall \
+ -I${top_srcdir}/include \
+ -I${top_builddir}/include \
+ -I${builddir}/route \
+ -I${builddir}/route/cls \
+ -D_GNU_SOURCE \
+ -DSYSCONFDIR=\"$(sysconfdir)/libnl\"
+
+AM_LDFLAGS = \
+ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
+ -Wl,--version-script=$(top_builddir)/libnl.sym
lib_LTLIBRARIES = \
- libnl.la libnl-genl.la libnl-route.la libnl-nf.la
+ libnl-3.la libnl-genl-3.la libnl-route-3.la libnl-nf-3.la libnl-idiag-3.la
+
+libnl_3_la_SOURCES = \
+ addr.c attr.c cache.c cache_mngr.c cache_mngt.c data.c \
+ error.c handlers.c msg.c nl.c object.c socket.c utils.c \
+ version.c hash.c hashtable.c
-libnl_la_LDFLAGS = -version-info 2:0:0
-libnl_la_SOURCES = \
- addr.c attr.c cache.c cache_mngr.c cache_mngt.c data.c doc.c \
- error.c handlers.c msg.c nl.c object.c socket.c utils.c
+libnl_idiag_3_la_LIBADD = libnl-3.la
+libnl_idiag_3_la_SOURCES = \
+ idiag/idiag_meminfo_obj.c idiag/idiag_vegasinfo_obj.c \
+ idiag/idiag_msg_obj.c idiag/idiag_req_obj.c idiag/idiag.c
-libnl_genl_la_LDFLAGS = -version-info 2:0:0
-libnl_genl_la_LIBADD = libnl.la
-libnl_genl_la_SOURCES = \
+libnl_genl_3_la_LIBADD = libnl-3.la
+libnl_genl_3_la_SOURCES = \
genl/ctrl.c genl/family.c genl/genl.c genl/mngt.c
-libnl_nf_la_LDFLAGS = -version-info 2:0:0
-libnl_nf_la_LIBADD = libnl-route.la
-libnl_nf_la_SOURCES = \
+libnl_nf_3_la_LIBADD = libnl-route-3.la
+libnl_nf_3_la_SOURCES = \
netfilter/ct.c netfilter/ct_obj.c netfilter/log.c \
netfilter/log_msg.c netfilter/log_msg_obj.c netfilter/log_obj.c \
netfilter/netfilter.c netfilter/nfnl.c netfilter/queue.c \
- netfilter/queue_msg.c netfilter/queue_msg_obj.c netfilter/queue_obj.c
+ netfilter/queue_msg.c netfilter/queue_msg_obj.c netfilter/queue_obj.c \
+ netfilter/exp.c netfilter/exp_obj.c
CLEANFILES = \
route/pktloc_grammar.c route/pktloc_grammar.h \
- route/pktloc_syntax.c route/pktloc_syntax.h
+ route/pktloc_syntax.c route/pktloc_syntax.h \
+ route/cls/ematch_grammar.c route/cls/ematch_grammar.h \
+ route/cls/ematch_syntax.c route/cls/ematch_syntax.h
# Hack to avoid using ylwrap. It does not function correctly in combination
# with --header-file=
route/pktloc_grammar.c: route/pktloc_grammar.l
- $(LEX) --header-file=route/pktloc_grammar.h $(LFLAGS) -o $@ $^
+ $(AM_V_GEN) $(MKDIR_P) route; $(FLEX) --header-file=route/pktloc_grammar.h $(LFLAGS) -o $@ $^
route/pktloc_syntax.c: route/pktloc_syntax.y
- $(YACC) -d $(YFLAGS) -o $@ $^
+ $(AM_V_GEN) $(MKDIR_P) route; $(YACC) -d $(YFLAGS) -o $@ $^
+
+route/cls/ematch_grammar.c: route/cls/ematch_grammar.l
+ $(AM_V_GEN) $(MKDIR_P) route/cls; $(FLEX) --header-file=route/cls/ematch_grammar.h $(LFLAGS) -o $@ $^
-libnl_route_la_LDFLAGS = -version-info 2:0:0
-libnl_route_la_LIBADD = libnl.la
-libnl_route_la_SOURCES = \
- route/addr.c route/class.c route/class_api.c route/class_obj.c \
- route/cls.c route/cls_api.c route/cls_obj.c route/link.c \
+route/cls/ematch_syntax.c: route/cls/ematch_syntax.y
+ $(AM_V_GEN) $(MKDIR_P) route/cls; $(YACC) -d $(YFLAGS) -o $@ $^
+
+libnl_route_3_la_LIBADD = libnl-3.la
+libnl_route_3_la_SOURCES = \
+ route/addr.c route/class.c route/cls.c route/act.c route/link.c \
route/neigh.c route/neightbl.c route/nexthop.c route/qdisc.c \
- route/qdisc_api.c route/qdisc_obj.c route/route.c route/route_obj.c \
- route/route_utils.c route/rtnl.c route/rule.c route/tc.c \
+ route/route.c route/route_obj.c route/route_utils.c route/rtnl.c \
+ route/rule.c route/tc.c route/classid.c \
+ \
+ route/cls/fw.c route/cls/police.c route/cls/u32.c route/cls/basic.c \
+ route/cls/cgroup.c \
+ \
+ route/act/mirred.c \
\
- route/cls/fw.c route/cls/police.c route/cls/u32.c \
+ route/cls/ematch.c \
+ route/cls/ematch/container.c route/cls/ematch/cmp.c \
+ route/cls/ematch/nbyte.c route/cls/ematch/text.c \
+ route/cls/ematch/meta.c \
\
- route/link/api.c route/link/vlan.c \
+ route/link/api.c route/link/vlan.c route/link/dummy.c \
+ route/link/bridge.c route/link/inet6.c route/link/inet.c \
+ route/link/bonding.c route/link/can.c route/link/macvlan.c \
+ route/link/vxlan.c route/link/veth.c route/link/ipip.c \
+ route/link/ipgre.c route/link/sit.c route/link/ipvti.c \
+ route/link/ip6tnl.c \
\
- route/sch/blackhole.c route/sch/cbq.c route/sch/dsmark.c \
- route/sch/fifo.c route/sch/htb.c route/sch/netem.c route/sch/prio.c \
- route/sch/red.c route/sch/sfq.c route/sch/tbf.c \
+ route/qdisc/blackhole.c route/qdisc/cbq.c route/qdisc/dsmark.c \
+ route/qdisc/fifo.c route/qdisc/htb.c route/qdisc/netem.c \
+ route/qdisc/prio.c route/qdisc/red.c route/qdisc/sfq.c \
+ route/qdisc/tbf.c route/qdisc/plug.c route/qdisc/ingress.c \
+ route/qdisc/fq_codel.c \
\
fib_lookup/lookup.c fib_lookup/request.c \
\
- route/pktloc_syntax.c route/pktloc_grammar.c route/pktloc.c
+ route/pktloc.c
+
+nodist_libnl_route_3_la_SOURCES = \
+ route/pktloc_syntax.c route/pktloc_syntax.h \
+ route/pktloc_grammar.c route/pktloc_grammar.h \
+ route/cls/ematch_syntax.c route/cls/ematch_syntax.h \
+ route/cls/ematch_grammar.c route/cls/ematch_grammar.h
+
+BUILT_SOURCES = \
+ route/cls/ematch_grammar.c \
+ route/cls/ematch_syntax.c \
+ route/pktloc_grammar.c \
+ route/pktloc_syntax.c
+
+EXTRA_DIST = \
+ route/pktloc_grammar.l \
+ route/pktloc_syntax.y \
+ route/cls/ematch_grammar.l \
+ route/cls/ematch_syntax.y
+
+if ENABLE_CLI
+nobase_pkglib_LTLIBRARIES = \
+ cli/qdisc/htb.la \
+ cli/qdisc/blackhole.la \
+ cli/qdisc/pfifo.la \
+ cli/qdisc/plug.la \
+ cli/qdisc/bfifo.la \
+ cli/qdisc/ingress.la \
+ cli/qdisc/fq_codel.la \
+ cli/cls/basic.la \
+ cli/cls/cgroup.la
+
+cli_qdisc_htb_la_LDFLAGS = -module -avoid-version
+cli_qdisc_blackhole_la_LDFLAGS = -module -avoid-version
+cli_qdisc_pfifo_la_LDFLAGS = -module -avoid-version
+cli_qdisc_plug_la_LDFLAGS = -module -avoid-version
+cli_qdisc_bfifo_la_LDFLAGS = -module -avoid-version
+cli_qdisc_ingress_la_LDFLAGS = -module -avoid-version
+cli_qdisc_fq_codel_la_LDFLAGS = -module -avoid-version
+cli_cls_basic_la_LDFLAGS = -module -avoid-version
+cli_cls_cgroup_la_LDFLAGS = -module -avoid-version
+endif
diff --git a/lib/addr.c b/lib/addr.c
index 1f000e75..54d2b1db 100644
--- a/lib/addr.c
+++ b/lib/addr.c
@@ -1,31 +1,33 @@
/*
- * lib/addr.c Abstract Address
+ * lib/addr.c Network 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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup core
- * @defgroup addr Abstract Address
+ * @ingroup core_types
+ * @defgroup addr Network Address
+ *
+ * Abstract data type representing any kind of network address
+ *
+ * Related sections in the development guide:
+ * - @core_doc{_abstract_address, Network Addresses}
*
- * @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
* @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/addr.h>
+ * ~~~~
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/addr.h>
@@ -151,15 +153,34 @@ static inline int dnet_pton(const char *src, char *addrbuf)
return 1;
}
+static void addr_destroy(struct nl_addr *addr)
+{
+ if (!addr)
+ return;
+
+ if (addr->a_refcnt != 1)
+ BUG();
+
+ free(addr);
+}
+
/**
- * @name Creating Abstract Addresses
+ * @name Creating Abstract Network Addresses
* @{
*/
/**
- * Allocate new abstract address object.
- * @arg maxsize Maximum size of the binary address.
- * @return Newly allocated address object or NULL
+ * Allocate empty abstract address
+ * @arg maxsize Upper limit of the binary address to be stored
+ *
+ * The new address object will be empty with a prefix length of 0 and will
+ * be capable of holding binary addresses up to the specified limit.
+ *
+ * @see nl_addr_build()
+ * @see nl_addr_parse()
+ * @see nl_addr_put()
+ *
+ * @return Allocated address object or NULL upon failure.
*/
struct nl_addr *nl_addr_alloc(size_t maxsize)
{
@@ -176,11 +197,21 @@ struct nl_addr *nl_addr_alloc(size_t maxsize)
}
/**
- * 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
+ * Allocate abstract address based on a binary address.
+ * @arg family Address family
+ * @arg buf Binary address
+ * @arg size Length of binary address
+ *
+ * This function will allocate an abstract address capable of holding the
+ * binary address specified. The prefix length will be set to the full
+ * length of the binary address provided.
+ *
+ * @see nl_addr_alloc()
+ * @see nl_addr_alloc_attr()
+ * @see nl_addr_parse()
+ * @see nl_addr_put()
+ *
+ * @return Allocated address object or NULL upon failure.
*/
struct nl_addr *nl_addr_build(int family, void *buf, size_t size)
{
@@ -201,14 +232,25 @@ struct nl_addr *nl_addr_build(int family, void *buf, size_t size)
}
/**
- * Allocate abstract address based on netlink attribute.
- * @arg nla Netlink attribute of unspecific type.
+ * Allocate abstract address based on Netlink attribute.
+ * @arg nla Netlink attribute
* @arg family Address family.
*
- * Considers the netlink attribute payload a address of the specified
- * family and allocates a new abstract address based on it.
+ * Allocates an abstract address based on the specified Netlink attribute
+ * by interpreting the payload of the Netlink attribute as the binary
+ * address.
+ *
+ * This function is identical to:
+ * @code
+ * nl_addr_build(family, nla_data(nla), nla_len(nla));
+ * @endcode
+ *
+ * @see nl_addr_alloc()
+ * @see nl_addr_build()
+ * @see nl_addr_parse()
+ * @see nl_addr_put()
*
- * @return Newly allocated address handle or NULL.
+ * @return Allocated address object or NULL upon failure.
*/
struct nl_addr *nl_addr_alloc_attr(struct nlattr *nla, int family)
{
@@ -216,13 +258,13 @@ struct nl_addr *nl_addr_alloc_attr(struct nlattr *nla, int family)
}
/**
- * Allocate abstract address object based on a character string
+ * Allocate abstract address based on character string
* @arg addrstr Address represented as character string.
* @arg hint Address family hint or AF_UNSPEC.
* @arg result Pointer to store resulting address.
*
* Regognizes the following address formats:
- *@code
+ * @code
* Format Len Family
* ----------------------------------------------------------------
* IPv6 address format 16 AF_INET6
@@ -240,6 +282,10 @@ struct nl_addr *nl_addr_alloc_attr(struct nlattr *nla, int family)
* The prefix length may be appened at the end prefixed with a
* slash, e.g. 10.0.0.0/8.
*
+ * @see nl_addr_alloc()
+ * @see nl_addr_build()
+ * @see nl_addr_put()
+ *
* @return 0 on success or a negative error code.
*/
int nl_addr_parse(const char *addrstr, int hint, struct nl_addr **result)
@@ -266,7 +312,9 @@ int nl_addr_parse(const char *addrstr, int hint, struct nl_addr **result)
if (!strcasecmp(str, "default") ||
!strcasecmp(str, "all") ||
!strcasecmp(str, "any")) {
-
+
+ len = 0;
+
switch (hint) {
case AF_INET:
case AF_UNSPEC:
@@ -274,17 +322,14 @@ int nl_addr_parse(const char *addrstr, int hint, struct nl_addr **result)
* 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:
@@ -355,7 +400,7 @@ int nl_addr_parse(const char *addrstr, int hint, struct nl_addr **result)
}
if (hint == AF_UNSPEC && strchr(str, ':')) {
- int i = 0;
+ size_t i = 0;
char *s = str, *p;
for (;;) {
long l = strtol(s, &p, 16);
@@ -395,7 +440,7 @@ prefix:
char *p;
long pl = strtol(++prefix, &p, 0);
if (p == prefix) {
- nl_addr_destroy(addr);
+ addr_destroy(addr);
err = -NLE_INVAL;
goto errout;
}
@@ -412,10 +457,16 @@ errout:
}
/**
- * 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.
+ * Clone existing abstract address object
+ * @arg addr Abstract address object
+ *
+ * Allocates new abstract address representing an identical clone of an
+ * existing address.
+ *
+ * @see nl_addr_alloc()
+ * @see nl_addr_put()
+ *
+ * @return Allocated abstract address or NULL upon failure.
*/
struct nl_addr *nl_addr_clone(struct nl_addr *addr)
{
@@ -431,32 +482,22 @@ struct nl_addr *nl_addr_clone(struct nl_addr *addr)
/** @} */
/**
- * @name Destroying Abstract Addresses
+ * @name Managing Usage References
* @{
*/
/**
- * 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
- * @{
+ * Increase the reference counter of an abstract address
+ * @arg addr Abstract address
+ *
+ * Increases the reference counter of the address and thus prevents the
+ * release of the memory resources until the reference is given back
+ * using the function nl_addr_put().
+ *
+ * @see nl_addr_put()
+ *
+ * @return Pointer to the existing abstract address
*/
-
struct nl_addr *nl_addr_get(struct nl_addr *addr)
{
addr->a_refcnt++;
@@ -464,21 +505,31 @@ struct nl_addr *nl_addr_get(struct nl_addr *addr)
return addr;
}
+/**
+ * Decrease the reference counter of an abstract address
+ * @arg addr Abstract addr
+ *
+ * @note The resources of the abstract address will be freed after the
+ * last reference to the address has been returned.
+ *
+ * @see nl_addr_get()
+ */
void nl_addr_put(struct nl_addr *addr)
{
if (!addr)
return;
if (addr->a_refcnt == 1)
- nl_addr_destroy(addr);
+ addr_destroy(addr);
else
addr->a_refcnt--;
}
/**
- * Check whether an abstract address object is shared.
+ * Check whether an abstract address is shared.
* @arg addr Abstract address object.
- * @return Non-zero if the abstract address object is shared, otherwise 0.
+ *
+ * @return Non-zero if the abstract address is shared, otherwise 0.
*/
int nl_addr_shared(struct nl_addr *addr)
{
@@ -493,12 +544,21 @@ int nl_addr_shared(struct nl_addr *addr)
*/
/**
- * Compares two abstract address objects.
- * @arg a A abstract address object.
- * @arg b Another abstract address object.
+ * Compare abstract addresses
+ * @arg a An abstract address
+ * @arg b Another abstract address
+ *
+ * Verifies whether the address family, address length, prefix length, and
+ * binary addresses of two abstract addresses matches.
+ *
+ * @note This function will *not* respect the prefix length in the sense
+ * that only the actual prefix will be compared. Please refer to the
+ * nl_addr_cmp_prefix() function if you require this functionality.
+ *
+ * @see nl_addr_cmp_prefix()
*
- * @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.
+ * @return Integer less than, equal to or greather than zero if the two
+ * addresses match.
*/
int nl_addr_cmp(struct nl_addr *a, struct nl_addr *b)
{
@@ -507,20 +567,29 @@ int nl_addr_cmp(struct nl_addr *a, struct nl_addr *b)
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);
+ if (a->a_len && d == 0) {
+ d = memcmp(a->a_addr, b->a_addr, a->a_len);
+
+ if (d == 0)
+ return (a->a_prefixlen - b->a_prefixlen);
+ }
}
return d;
}
/**
- * Compares the prefix of two abstract address objects.
- * @arg a A abstract address object.
- * @arg b Another abstract address object.
+ * Compare the prefix of two abstract addresses
+ * @arg a An abstract address
+ * @arg b Another abstract address
+ *
+ * Verifies whether the address family and the binary address covered by
+ * the smaller prefix length of the two abstract addresses matches.
*
- * @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.
+ * @see nl_addr_cmp()
+ *
+ * @return Integer less than, equal to or greather than zero if the two
+ * addresses match.
*/
int nl_addr_cmp_prefix(struct nl_addr *a, struct nl_addr *b)
{
@@ -531,8 +600,8 @@ int nl_addr_cmp_prefix(struct nl_addr *a, struct nl_addr *b)
int bytes = len / 8;
d = memcmp(a->a_addr, b->a_addr, bytes);
- if (d == 0) {
- int mask = (1UL << (len % 8)) - 1UL;
+ if (d == 0 && (len % 8) != 0) {
+ int mask = (0xFF00 >> (len % 8)) & 0xFF;
d = (a->a_addr[bytes] & mask) -
(b->a_addr[bytes] & mask);
@@ -544,11 +613,13 @@ int nl_addr_cmp_prefix(struct nl_addr *a, struct nl_addr *b)
/**
* Returns true if the address consists of all zeros
- * @arg addr Address to look at.
+ * @arg addr Abstract address
+ *
+ * @return 1 if the binary address consists of all zeros, 0 otherwise.
*/
int nl_addr_iszero(struct nl_addr *addr)
{
- int i;
+ unsigned int i;
for (i = 0; i < addr->a_len; i++)
if (addr->a_addr[i])
@@ -558,11 +629,11 @@ int nl_addr_iszero(struct nl_addr *addr)
}
/**
- * Check if an address matches a certain family.
+ * Check if address string is parseable for a specific address family
* @arg addr Address represented as character string.
* @arg family Desired address family.
*
- * @return 1 if the address is of the desired address family,
+ * @return 1 if the address is parseable assuming the specified address family,
* otherwise 0 is returned.
*/
int nl_addr_valid(char *addr, int family)
@@ -594,9 +665,10 @@ int nl_addr_valid(char *addr, int family)
}
/**
- * Guess address family of an abstract address object based on address size.
+ * Guess address family of abstract address based on address size
* @arg addr Abstract address object.
- * @return Address family or AF_UNSPEC if guessing wasn't successful.
+ *
+ * @return Numeric address family or AF_UNSPEC
*/
int nl_addr_guess_family(struct nl_addr *addr)
{
@@ -672,7 +744,7 @@ int nl_addr_fill_sockaddr(struct nl_addr *addr, struct sockaddr *sa,
* Call getaddrinfo() for an abstract address object.
* @arg addr Abstract address object.
* @arg result Pointer to store resulting address list.
- *
+ *
* Calls getaddrinfo() for the specified abstract address in AI_NUMERICHOST
* mode.
*
@@ -750,11 +822,26 @@ int nl_addr_resolve(struct nl_addr *addr, char *host, size_t hostlen)
* @{
*/
+/**
+ * Set address family
+ * @arg addr Abstract address object
+ * @arg family Address family
+ *
+ * @see nl_addr_get_family()
+ */
void nl_addr_set_family(struct nl_addr *addr, int family)
{
addr->a_family = family;
}
+/**
+ * Return address family
+ * @arg addr Abstract address object
+ *
+ * @see nl_addr_set_family()
+ *
+ * @return The numeric address family or `AF_UNSPEC`
+ */
int nl_addr_get_family(struct nl_addr *addr)
{
return addr->a_family;
@@ -765,6 +852,20 @@ int nl_addr_get_family(struct nl_addr *addr)
* @arg addr Abstract address object.
* @arg buf Buffer containing binary address.
* @arg len Length of buffer containing binary address.
+ *
+ * Modifies the binary address portion of the abstract address. The
+ * abstract address must be capable of holding the required amount
+ * or this function will fail.
+ *
+ * @note This function will *not* modify the prefix length. It is within
+ * the responsibility of the caller to set the prefix length to the
+ * desirable length.
+ *
+ * @see nl_addr_alloc()
+ * @see nl_addr_get_binary_addr()
+ * @see nl_addr_get_len()
+ *
+ * @return 0 on success or a negative error code.
*/
int nl_addr_set_binary_addr(struct nl_addr *addr, void *buf, size_t len)
{
@@ -772,7 +873,10 @@ int nl_addr_set_binary_addr(struct nl_addr *addr, void *buf, size_t len)
return -NLE_RANGE;
addr->a_len = len;
- memcpy(addr->a_addr, buf, len);
+ memset(addr->a_addr, 0, addr->a_maxsize);
+
+ if (len)
+ memcpy(addr->a_addr, buf, len);
return 0;
}
@@ -780,6 +884,11 @@ int nl_addr_set_binary_addr(struct nl_addr *addr, void *buf, size_t len)
/**
* Get binary address of abstract address object.
* @arg addr Abstract address object.
+ *
+ * @see nl_addr_set_binary_addr()
+ * @see nl_addr_get_len()
+ *
+ * @return Pointer to binary address of length nl_addr_get_len()
*/
void *nl_addr_get_binary_addr(struct nl_addr *addr)
{
@@ -789,20 +898,32 @@ void *nl_addr_get_binary_addr(struct nl_addr *addr)
/**
* Get length of binary address of abstract address object.
* @arg addr Abstract address object.
+ *
+ * @see nl_addr_get_binary_addr()
+ * @see nl_addr_set_binary_addr()
*/
unsigned int nl_addr_get_len(struct nl_addr *addr)
{
return addr->a_len;
}
+/**
+ * Set the prefix length of an abstract address
+ * @arg addr Abstract address object
+ * @arg prefixlen New prefix length
+ *
+ * @see nl_addr_get_prefixlen()
+ */
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.
+ * Return prefix length of abstract address object.
+ * @arg addr Abstract address object
+ *
+ * @see nl_addr_set_prefixlen()
*/
unsigned int nl_addr_get_prefixlen(struct nl_addr *addr)
{
@@ -829,7 +950,7 @@ unsigned int nl_addr_get_prefixlen(struct nl_addr *addr)
*/
char *nl_addr2str(struct nl_addr *addr, char *buf, size_t size)
{
- int i;
+ unsigned int i;
char tmp[16];
if (!addr || !addr->a_len) {
@@ -881,10 +1002,9 @@ prefix:
* @{
*/
-static struct trans_tbl afs[] = {
+static const 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)
@@ -900,17 +1020,49 @@ static struct trans_tbl afs[] = {
__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)
+#ifdef AF_RDS
+ __ADD(AF_RDS,rds)
+#endif
__ADD(AF_SNA,sna)
__ADD(AF_IRDA,irda)
__ADD(AF_PPPOX,pppox)
__ADD(AF_WANPIPE,wanpipe)
__ADD(AF_LLC,llc)
+#ifdef AF_CAN
+ __ADD(AF_CAN,can)
+#endif
+#ifdef AF_TIPC
+ __ADD(AF_TIPC,tipc)
+#endif
__ADD(AF_BLUETOOTH,bluetooth)
+#ifdef AF_IUCV
+ __ADD(AF_IUCV,iucv)
+#endif
+#ifdef AF_RXRPC
+ __ADD(AF_RXRPC,rxrpc)
+#endif
+#ifdef AF_ISDN
+ __ADD(AF_ISDN,isdn)
+#endif
+#ifdef AF_PHONET
+ __ADD(AF_PHONET,phonet)
+#endif
+#ifdef AF_IEEE802154
+ __ADD(AF_IEEE802154,ieee802154)
+#endif
+#ifdef AF_CAIF
+ __ADD(AF_CAIF,caif)
+#endif
+#ifdef AF_ALG
+ __ADD(AF_ALG,alg)
+#endif
+#ifdef AF_NFC
+ __ADD(AF_NFC,nfc)
+#endif
};
char *nl_af2str(int family, char *buf, size_t size)
@@ -921,7 +1073,7 @@ char *nl_af2str(int family, char *buf, size_t size)
int nl_str2af(const char *name)
{
int fam = __str2type(name, afs, ARRAY_SIZE(afs));
- return fam >= 0 ? fam : AF_UNSPEC;
+ return fam >= 0 ? fam : -EINVAL;
}
/** @} */
diff --git a/lib/attr.c b/lib/attr.c
index 298fbb14..d1f0268e 100644
--- a/lib/attr.c
+++ b/lib/attr.c
@@ -6,10 +6,10 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/addr.h>
@@ -22,356 +22,16 @@
* @defgroup attr Attributes
* Netlink Attributes Construction/Parsing Interface
*
- * \section attr_sec Netlink Attributes
- * Netlink attributes allow for data chunks of arbitary length to be
- * attached to a netlink message. Each attribute is encoded with a
- * type and length field, both 16 bits, stored in the attribute header
- * preceding the attribute data. The main advantage of using attributes
- * over packing everything into the family header is that the interface
- * stays extendable as new attributes can supersede old attributes while
- * remaining backwards compatible. Also attributes can be defined optional
- * thus avoiding the transmission of unnecessary empty data blocks.
- * Special nested attributes allow for more complex data structures to
- * be transmitted, e.g. trees, lists, etc.
- *
- * While not required, netlink attributes typically follow the family
- * header of a netlink message and must be properly aligned to NLA_ALIGNTO:
- * @code
- * +----------------+- - -+---------------+- - -+------------+- - -+
- * | Netlink Header | Pad | Family Header | Pad | Attributes | Pad |
- * +----------------+- - -+---------------+- - -+------------+- - -+
- * @endcode
- *
- * The actual attributes are chained together each separately aligned to
- * NLA_ALIGNTO. The position of an attribute is defined based on the
- * length field of the preceding attributes:
- * @code
- * +-------------+- - -+-------------+- - -+------
- * | Attribute 1 | Pad | Attribute 2 | Pad | ...
- * +-------------+- - -+-------------+- - -+------
- * nla_next(attr1)------^
- * @endcode
- *
- * The attribute itself consists of the attribute header followed by
- * the actual payload also aligned to NLA_ALIGNTO. The function nla_data()
- * returns a pointer to the start of the payload while nla_len() returns
- * the length of the payload in bytes.
- *
- * \b Note: Be aware, NLA_ALIGNTO equals to 4 bytes, therefore it is not
- * safe to dereference any 64 bit data types directly.
- *
- * @code
- * <----------- nla_total_size(payload) ----------->
- * <-------- nla_attr_size(payload) --------->
- * +------------------+- - -+- - - - - - - - - +- - -+
- * | Attribute Header | Pad | Payload | Pad |
- * +------------------+- - -+- - - - - - - - - +- - -+
- * nla_data(nla)-------------^
- * <- nla_len(nla) ->
- * @endcode
- *
- * @subsection attr_datatypes Attribute Data Types
- * A number of basic data types are supported to simplify access and
- * validation of netlink attributes. This data type information is
- * not encoded in the attribute, both the kernel and userspace part
- * are required to share this information on their own.
- *
- * One of the major advantages of these basic types is the automatic
- * validation of each attribute based on an attribute policy. The
- * validation covers most of the checks required to safely use
- * attributes and thus keeps the individual sanity check to a minimum.
- *
- * Never access attribute payload without ensuring basic validation
- * first, attributes may:
- * - not be present even though required
- * - contain less actual payload than expected
- * - fake a attribute length which exceeds the end of the message
- * - contain unterminated character strings
- *
- * Policies are defined as array of the struct nla_policy. The array is
- * indexed with the attribute type, therefore the array must be sized
- * accordingly.
- * @code
- * static struct nla_policy my_policy[ATTR_MAX+1] = {
- * [ATTR_FOO] = { .type = ..., .minlen = ..., .maxlen = ... },
- * };
- *
- * err = nla_validate(attrs, attrlen, ATTR_MAX, &my_policy);
- * @endcode
- *
- * Some basic validations are performed on every attribute, regardless of type.
- * - If the attribute type exceeds the maximum attribute type specified or
- * the attribute type is lesser-or-equal than zero, the attribute will
- * be silently ignored.
- * - If the payload length falls below the \a minlen value the attribute
- * will be rejected.
- * - If \a maxlen is non-zero and the payload length exceeds the \a maxlen
- * value the attribute will be rejected.
- *
- *
- * @par Unspecific Attribute (NLA_UNSPEC)
- * This is the standard type if no type is specified. It is used for
- * binary data of arbitary length. Typically this attribute carries
- * a binary structure or a stream of bytes.
- * @par
- * @code
- * // In this example, we will assume a binary structure requires to
- * // be transmitted. The definition of the structure will typically
- * // go into a header file available to both the kernel and userspace
- * // side.
- * //
- * // Note: Be careful when putting 64 bit data types into a structure.
- * // The attribute payload is only aligned to 4 bytes, dereferencing
- * // the member may fail.
- * struct my_struct {
- * int a;
- * int b;
- * };
- *
- * // The validation function will not enforce an exact length match to
- * // allow structures to grow as required. Note: While it is allowed
- * // to add members to the end of the structure, changing the order or
- * // inserting members in the middle of the structure will break your
- * // binary interface.
- * static struct nla_policy my_policy[ATTR_MAX+1] = {
- * [ATTR_MY_STRICT] = { .type = NLA_UNSPEC,
- * .minlen = sizeof(struct my_struct) },
- *
- * // The binary structure is appened to the message using nla_put()
- * struct my_struct foo = { .a = 1, .b = 2 };
- * nla_put(msg, ATTR_MY_STRUCT, sizeof(foo), &foo);
- *
- * // On the receiving side, a pointer to the structure pointing inside
- * // the message payload is returned by nla_get().
- * if (attrs[ATTR_MY_STRUCT])
- * struct my_struct *foo = nla_get(attrs[ATTR_MY_STRUCT]);
- * @endcode
- *
- * @par Integers (NLA_U8, NLA_U16, NLA_U32, NLA_U64)
- * Integers come in different sizes from 8 bit to 64 bit. However, since the
- * payload length is aligned to 4 bytes, integers smaller than 32 bit are
- * only useful to enforce the maximum range of values.
- * @par
- * \b Note: There is no difference made between signed and unsigned integers.
- * The validation only enforces the minimal payload length required to store
- * an integer of specified type.
- * @par
- * @code
- * // Even though possible, it does not make sense to specify .minlen or
- * // .maxlen for integer types. The data types implies the corresponding
- * // minimal payload length.
- * static struct nla_policy my_policy[ATTR_MAX+1] = {
- * [ATTR_FOO] = { .type = NLA_U32 },
- *
- * // Numeric values can be appended directly using the respective
- * // nla_put_uxxx() function
- * nla_put_u32(msg, ATTR_FOO, 123);
- *
- * // Same for the receiving side.
- * if (attrs[ATTR_FOO])
- * uint32_t foo = nla_get_u32(attrs[ATTR_FOO]);
- * @endcode
- *
- * @par Character string (NLA_STRING)
- * This data type represents a NUL terminated character string of variable
- * length. For binary data streams the type NLA_UNSPEC is recommended.
- * @par
- * @code
- * // Enforce a NUL terminated character string of at most 4 characters
- * // including the NUL termination.
- * static struct nla_policy my_policy[ATTR_MAX+1] = {
- * [ATTR_BAR] = { .type = NLA_STRING, maxlen = 4 },
- *
- * // nla_put_string() creates a string attribute of the necessary length
- * // and appends it to the message including the NUL termination.
- * nla_put_string(msg, ATTR_BAR, "some text");
- *
- * // It is safe to use the returned character string directly if the
- * // attribute has been validated as the validation enforces the proper
- * // termination of the string.
- * if (attrs[ATTR_BAR])
- * char *text = nla_get_string(attrs[ATTR_BAR]);
- * @endcode
- *
- * @par Flag (NLA_FLAG)
- * This attribute type may be used to indicate the presence of a flag. The
- * attribute is only valid if the payload length is zero. The presence of
- * the attribute header indicates the presence of the flag.
- * @par
- * @code
- * // This attribute type is special as .minlen and .maxlen have no effect.
- * static struct nla_policy my_policy[ATTR_MAX+1] = {
- * [ATTR_FLAG] = { .type = NLA_FLAG },
- *
- * // nla_put_flag() appends a zero sized attribute to the message.
- * nla_put_flag(msg, ATTR_FLAG);
- *
- * // There is no need for a receival function, the presence is the value.
- * if (attrs[ATTR_FLAG])
- * // flag is present
- * @endcode
- *
- * @par Micro Seconds (NLA_MSECS)
- *
- * @par Nested Attribute (NLA_NESTED)
- * Attributes can be nested and put into a container to create groups, lists
- * or to construct trees of attributes. Nested attributes are often used to
- * pass attributes to a subsystem where the top layer has no knowledge of the
- * configuration possibilities of each subsystem.
- * @par
- * \b Note: When validating the attributes using nlmsg_validate() or
- * nlmsg_parse() it will only affect the top level attributes. Each
- * level of nested attributes must be validated seperately using
- * nla_parse_nested() or nla_validate().
- * @par
- * @code
- * // The minimal length policy may be used to enforce the presence of at
- * // least one attribute.
- * static struct nla_policy my_policy[ATTR_MAX+1] = {
- * [ATTR_OPTS] = { .type = NLA_NESTED, minlen = NLA_HDRLEN },
- *
- * // Nested attributes are constructed by enclosing the attributes
- * // to be nested with calls to nla_nest_start() respetively nla_nest_end().
- * struct nlattr *opts = nla_nest_start(msg, ATTR_OPTS);
- * nla_put_u32(msg, ATTR_FOO, 123);
- * nla_put_string(msg, ATTR_BAR, "some text");
- * nla_nest_end(msg, opts);
- *
- * // Various methods exist to parse nested attributes, the easiest being
- * // nla_parse_nested() which also allows validation in the same step.
- * if (attrs[ATTR_OPTS]) {
- * struct nlattr *nested[ATTR_MAX+1];
- *
- * nla_parse_nested(nested, ATTR_MAX, attrs[ATTR_OPTS], &policy);
- *
- * if (nested[ATTR_FOO])
- * uint32_t foo = nla_get_u32(nested[ATTR_FOO]);
- * }
- * @endcode
- *
- * @subsection attr_exceptions Exception Based Attribute Construction
- * Often a large number of attributes are added to a message in a single
- * function. In order to simplify error handling, a second set of
- * construction functions exist which jump to a error label when they
- * fail instead of returning an error code. This second set consists
- * of macros which are named after their error code based counterpart
- * except that the name is written all uppercase.
- *
- * All of the macros jump to the target \c nla_put_failure if they fail.
- * @code
- * void my_func(struct nl_msg *msg)
- * {
- * NLA_PUT_U32(msg, ATTR_FOO, 10);
- * NLA_PUT_STRING(msg, ATTR_BAR, "bar");
- *
- * return 0;
- *
- * nla_put_failure:
- * return -NLE_NOMEM;
- * }
- * @endcode
- *
- * @subsection attr_examples Examples
- * @par Example 1.1 Constructing a netlink message with attributes.
- * @code
- * struct nl_msg *build_msg(int ifindex, struct nl_addr *lladdr, int mtu)
- * {
- * struct nl_msg *msg;
- * struct nlattr *info, *vlan;
- * struct ifinfomsg ifi = {
- * .ifi_family = AF_INET,
- * .ifi_index = ifindex,
- * };
- *
- * // Allocate a new netlink message, type=RTM_SETLINK, flags=NLM_F_ECHO
- * if (!(msg = nlmsg_alloc_simple(RTM_SETLINK, NLM_F_ECHO)))
- * return NULL;
- *
- * // Append the family specific header (struct ifinfomsg)
- * if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
- * goto nla_put_failure
- *
- * // Append a 32 bit integer attribute to carry the MTU
- * NLA_PUT_U32(msg, IFLA_MTU, mtu);
- *
- * // Append a unspecific attribute to carry the link layer address
- * NLA_PUT_ADDR(msg, IFLA_ADDRESS, lladdr);
- *
- * // Append a container for nested attributes to carry link information
- * if (!(info = nla_nest_start(msg, IFLA_LINKINFO)))
- * goto nla_put_failure;
- *
- * // Put a string attribute into the container
- * NLA_PUT_STRING(msg, IFLA_INFO_KIND, "vlan");
- *
- * // Append another container inside the open container to carry
- * // vlan specific attributes
- * if (!(vlan = nla_nest_start(msg, IFLA_INFO_DATA)))
- * goto nla_put_failure;
- *
- * // add vlan specific info attributes here...
- *
- * // Finish nesting the vlan attributes and close the second container.
- * nla_nest_end(msg, vlan);
- *
- * // Finish nesting the link info attribute and close the first container.
- * nla_nest_end(msg, info);
- *
- * return msg;
- *
- * // If any of the construction macros fails, we end up here.
- * nla_put_failure:
- * nlmsg_free(msg);
- * return NULL;
- * }
- * @endcode
- *
- * @par Example 2.1 Parsing a netlink message with attributes.
- * @code
- * int parse_message(struct nl_msg *msg)
- * {
- * // The policy defines two attributes: a 32 bit integer and a container
- * // for nested attributes.
- * struct nla_policy attr_policy[ATTR_MAX+1] = {
- * [ATTR_FOO] = { .type = NLA_U32 },
- * [ATTR_BAR] = { .type = NLA_NESTED },
- * };
- * struct nlattr *attrs[ATTR_MAX+1];
- * int err;
- *
- * // The nlmsg_parse() function will make sure that the message contains
- * // enough payload to hold the header (struct my_hdr), validates any
- * // attributes attached to the messages and stores a pointer to each
- * // attribute in the attrs[] array accessable by attribute type.
- * if ((err = nlmsg_parse(nlmsg_hdr(msg), sizeof(struct my_hdr), attrs,
- * ATTR_MAX, attr_policy)) < 0)
- * goto errout;
- *
- * if (attrs[ATTR_FOO]) {
- * // It is safe to directly access the attribute payload without
- * // any further checks since nlmsg_parse() enforced the policy.
- * uint32_t foo = nla_get_u32(attrs[ATTR_FOO]);
- * }
- *
- * if (attrs[ATTR_BAR]) {
- * struct nlattr *nested[NESTED_MAX+1];
- *
- * // Attributes nested in a container can be parsed the same way
- * // as top level attributes.
- * if ((err = nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_BAR],
- * nested_policy)) < 0)
- * goto errout;
- *
- * // Process nested attributes here.
- * }
- *
- * err = 0;
- * errout:
- * return err;
- * }
- * @endcode
+ * Related sections in the development guide:
+ * - @core_doc{core_attr,Netlink Attributes}
*
* @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/attr.h>
+ * ~~~~
*/
/**
@@ -522,15 +182,17 @@ static uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = {
[NLA_U32] = sizeof(uint32_t),
[NLA_U64] = sizeof(uint64_t),
[NLA_STRING] = 1,
+ [NLA_FLAG] = 0,
};
static int validate_nla(struct nlattr *nla, int maxtype,
struct nla_policy *policy)
{
struct nla_policy *pt;
- int minlen = 0, type = nla_type(nla);
+ unsigned int minlen = 0;
+ int type = nla_type(nla);
- if (type <= 0 || type > maxtype)
+ if (type < 0 || type > maxtype)
return 0;
pt = &policy[type];
@@ -543,9 +205,6 @@ static int validate_nla(struct nlattr *nla, int maxtype,
else if (pt->type != NLA_UNSPEC)
minlen = nla_attr_minlen[pt->type];
- if (pt->type == NLA_FLAG && nla_len(nla) > 0)
- return -NLE_RANGE;
-
if (nla_len(nla) < minlen)
return -NLE_RANGE;
@@ -591,24 +250,24 @@ int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
nla_for_each_attr(nla, head, len, rem) {
int type = nla_type(nla);
- if (type == 0) {
- fprintf(stderr, "Illegal nla->nla_type == 0\n");
+ if (type > maxtype)
continue;
+
+ if (policy) {
+ err = validate_nla(nla, maxtype, policy);
+ if (err < 0)
+ goto errout;
}
- if (type <= maxtype) {
- if (policy) {
- err = validate_nla(nla, maxtype, policy);
- if (err < 0)
- goto errout;
- }
+ if (tb[type])
+ NL_DBG(1, "Attribute of type %#x found multiple times in message, "
+ "previous attribute is being ignored.\n", type);
- tb[type] = nla;
- }
+ tb[type] = nla;
}
if (rem > 0)
- fprintf(stderr, "netlink: %d bytes leftover after parsing "
+ NL_DBG(1, "netlink: %d bytes leftover after parsing "
"attributes.\n", rem);
err = 0;
@@ -628,8 +287,7 @@ errout:
* than the maximum type specified will be silently ignored in order to
* maintain backwards compatibility.
*
- * See \ref attr_datatypes for more details on what kind of validation
- * checks are performed on each attribute data type.
+ * See section @core_doc{core_attr_parse,Attribute Parsing} for more details.
*
* @return 0 on success or a negative error code.
*/
@@ -802,20 +460,22 @@ struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int attrlen)
tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen);
- if ((tlen + msg->nm_nlh->nlmsg_len) > msg->nm_size)
+ if (tlen > msg->nm_size)
return NULL;
nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh);
nla->nla_type = attrtype;
nla->nla_len = nla_attr_size(attrlen);
- memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen));
+ if (attrlen)
+ memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen));
msg->nm_nlh->nlmsg_len = tlen;
- NL_DBG(2, "msg %p: Reserved %d bytes at offset +%td for attr %d "
- "nlmsg_len=%d\n", msg, attrlen,
+ NL_DBG(2, "msg %p: attr <%p> %d: Reserved %d (%d) bytes at offset +%td "
+ "nlmsg_len=%d\n", msg, nla, nla->nla_type,
+ nla_total_size(attrlen), attrlen,
(void *) nla - nlmsg_data(msg->nm_nlh),
- attrtype, msg->nm_nlh->nlmsg_len);
+ msg->nm_nlh->nlmsg_len);
return nla;
}
@@ -842,9 +502,12 @@ int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data)
if (!nla)
return -NLE_NOMEM;
- memcpy(nla_data(nla), data, datalen);
- NL_DBG(2, "msg %p: Wrote %d bytes at offset +%td for attr %d\n",
- msg, datalen, (void *) nla - nlmsg_data(msg->nm_nlh), attrtype);
+ if (datalen > 0) {
+ memcpy(nla_data(nla), data, datalen);
+ NL_DBG(2, "msg %p: attr <%p> %d: Wrote %d bytes at offset +%td\n",
+ msg, nla, nla->nla_type, datalen,
+ (void *) nla - nlmsg_data(msg->nm_nlh));
+ }
return 0;
}
@@ -985,9 +648,10 @@ int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value)
*/
uint64_t nla_get_u64(struct nlattr *nla)
{
- uint64_t tmp;
+ uint64_t tmp = 0;
- nla_memcpy(&tmp, nla, sizeof(tmp));
+ if (nla && nla_len(nla) >= sizeof(tmp))
+ memcpy(&tmp, nla_data(nla), sizeof(tmp));
return tmp;
}
@@ -1107,7 +771,10 @@ unsigned long nla_get_msecs(struct nlattr *nla)
*/
int nla_put_nested(struct nl_msg *msg, int attrtype, struct nl_msg *nested)
{
- return nla_put(msg, attrtype, nlmsg_len(nested->nm_nlh),
+ NL_DBG(2, "msg %p: attr <> %d: adding msg %p as nested attribute\n",
+ msg, attrtype, nested);
+
+ return nla_put(msg, attrtype, nlmsg_datalen(nested->nm_nlh),
nlmsg_data(nested->nm_nlh));
}
@@ -1126,6 +793,9 @@ struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype)
if (nla_put(msg, attrtype, 0, NULL) < 0)
return NULL;
+ NL_DBG(2, "msg %p: attr <%p> %d: starting nesting\n",
+ msg, start, start->nla_type);
+
return start;
}
@@ -1140,12 +810,66 @@ struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype)
*/
int nla_nest_end(struct nl_msg *msg, struct nlattr *start)
{
- start->nla_len = (unsigned char *) nlmsg_tail(msg->nm_nlh) -
- (unsigned char *) start;
+ size_t pad, len;
+
+ len = (void *) nlmsg_tail(msg->nm_nlh) - (void *) start;
+
+ if (len == NLA_HDRLEN) {
+ /*
+ * Kernel can't handle empty nested attributes, trim the
+ * attribute header again
+ */
+ nla_nest_cancel(msg, start);
+
+ return 0;
+ }
+
+ start->nla_len = len;
+
+ pad = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) - msg->nm_nlh->nlmsg_len;
+ if (pad > 0) {
+ /*
+ * Data inside attribute does not end at a alignment boundry.
+ * Pad accordingly and accoun for the additional space in
+ * the message. nlmsg_reserve() may never fail in this situation,
+ * the allocate message buffer must be a multiple of NLMSG_ALIGNTO.
+ */
+ if (!nlmsg_reserve(msg, pad, 0))
+ BUG();
+
+ NL_DBG(2, "msg %p: attr <%p> %d: added %zu bytes of padding\n",
+ msg, start, start->nla_type, pad);
+ }
+
+ NL_DBG(2, "msg %p: attr <%p> %d: closing nesting, len=%u\n",
+ msg, start, start->nla_type, start->nla_len);
+
return 0;
}
/**
+ * Cancel the addition of a nested attribute
+ * @arg msg Netlink message
+ * @arg attr Nested netlink attribute
+ *
+ * Removes any partially added nested Netlink attribute from the message
+ * by resetting the message to the size before the call to nla_nest_start()
+ * and by overwriting any potentially touched message segments with 0.
+ */
+void nla_nest_cancel(struct nl_msg *msg, struct nlattr *attr)
+{
+ ssize_t len;
+
+ len = (void *) nlmsg_tail(msg->nm_nlh) - (void *) attr;
+ if (len < 0)
+ BUG();
+ else if (len > 0) {
+ msg->nm_nlh->nlmsg_len -= len;
+ memset(nlmsg_tail(msg->nm_nlh), 0, len);
+ }
+}
+
+/**
* Create attribute index based on nested attribute
* @arg tb Index array to be filled (maxtype+1 elements).
* @arg maxtype Maximum attribute type expected and accepted.
@@ -1164,6 +888,17 @@ int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy);
}
+/**
+ * Return true if attribute has NLA_F_NESTED flag set
+ * @arg attr Netlink attribute
+ *
+ * @return True if attribute has NLA_F_NESTED flag set, oterhwise False.
+ */
+int nla_is_nested(struct nlattr *attr)
+{
+ return !!(attr->nla_type & NLA_F_NESTED);
+}
+
/** @} */
/** @} */
diff --git a/lib/cache.c b/lib/cache.c
index 2b249469..b4f9649c 100644
--- a/lib/cache.c
+++ b/lib/cache.c
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
@@ -37,13 +37,23 @@
* | | Core Netlink
* @endcode
*
+ * Related sections in the development guide:
+ * - @core_doc{core_cache, Caching System}
+ *
* @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/cache.h>
+ * ~~~~
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/object.h>
+#include <netlink/hashtable.h>
#include <netlink/utils.h>
/**
@@ -67,15 +77,12 @@ int nl_cache_nitems(struct nl_cache *cache)
*/
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;
@@ -160,15 +167,18 @@ struct nl_object *nl_cache_get_prev(struct nl_object *obj)
/** @} */
/**
- * @name Cache Creation/Deletion
+ * @name Cache Allocation/Deletion
* @{
*/
/**
- * Allocate an empty cache
- * @arg ops cache operations to base the cache on
- *
- * @return A newly allocated and initialized cache.
+ * Allocate new cache
+ * @arg ops Cache operations
+ *
+ * Allocate and initialize a new cache based on the cache operations
+ * provided.
+ *
+ * @return Allocated cache or NULL if allocation failed.
*/
struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops)
{
@@ -180,12 +190,46 @@ struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops)
nl_init_list_head(&cache->c_items);
cache->c_ops = ops;
+ cache->c_flags |= ops->co_flags;
+ cache->c_refcnt = 1;
+
+ /*
+ * If object type provides a hash keygen
+ * functions, allocate a hash table for the
+ * cache objects for faster lookups
+ */
+ if (ops->co_obj_ops->oo_keygen) {
+ int hashtable_size;
+
+ if (ops->co_hash_size)
+ hashtable_size = ops->co_hash_size;
+ else
+ hashtable_size = NL_MAX_HASH_ENTRIES;
+
+ cache->hashtable = nl_hash_table_alloc(hashtable_size);
+ }
NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache));
return cache;
}
+/**
+ * Allocate new cache and fill it
+ * @arg ops Cache operations
+ * @arg sock Netlink socket
+ * @arg result Result pointer
+ *
+ * Allocate new cache and fill it. Equivalent to calling:
+ * @code
+ * cache = nl_cache_alloc(ops);
+ * nl_cache_refill(sock, cache);
+ * @endcode
+ *
+ * @see nl_cache_alloc
+ *
+ * @return 0 on success or a negative error code.
+ */
int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock,
struct nl_cache **result)
{
@@ -205,20 +249,30 @@ int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock,
}
/**
- * Allocate an empty cache based on type name
+ * Allocate new cache based on type name
* @arg kind Name of cache type
- * @return A newly allocated and initialized cache.
+ * @arg result Result pointer
+ *
+ * Lookup cache ops via nl_cache_ops_lookup() and allocate the cache
+ * by calling nl_cache_alloc(). Stores the allocated cache in the
+ * result pointer provided.
+ *
+ * @see nl_cache_alloc
+ *
+ * @return 0 on success or a negative error code.
*/
int nl_cache_alloc_name(const char *kind, struct nl_cache **result)
{
struct nl_cache_ops *ops;
struct nl_cache *cache;
- ops = nl_cache_ops_lookup(kind);
+ ops = nl_cache_ops_lookup_safe(kind);
if (!ops)
return -NLE_NOCACHE;
- if (!(cache = nl_cache_alloc(ops)))
+ cache = nl_cache_alloc(ops);
+ nl_cache_ops_put(ops);
+ if (!cache)
return -NLE_NOMEM;
*result = cache;
@@ -226,16 +280,24 @@ int nl_cache_alloc_name(const char *kind, struct nl_cache **result)
}
/**
- * 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
+ * Allocate new cache containing a subset of an existing cache
+ * @arg orig Original cache to base new cache on
+ * @arg filter Filter defining the subset to be filled into the new cache
+ *
+ * Allocates a new cache matching the type of the cache specified by
+ * \p orig. Iterates over the \p orig cache applying the specified
+ * \p filter and copies all objects that match to the new cache.
+ *
+ * The copied objects are clones but do not contain a reference to each
+ * other. Later modifications to objects in the original cache will
+ * not affect objects in the 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)
@@ -245,7 +307,8 @@ struct nl_cache *nl_cache_subset(struct nl_cache *orig,
if (!cache)
return NULL;
- ops = orig->c_ops->co_obj_ops;
+ NL_DBG(2, "Filling subset of cache %p <%s> with filter %p into %p\n",
+ orig, nl_cache_name(orig), filter, cache);
nl_list_for_each_entry(obj, &orig->c_items, ce_list) {
if (!nl_object_match_filter(obj, filter))
@@ -258,37 +321,107 @@ struct nl_cache *nl_cache_subset(struct nl_cache *orig,
}
/**
- * Clear a cache.
- * @arg cache cache to clear
+ * Allocate new cache and copy the contents of an existing cache
+ * @arg cache Original cache to base new cache on
*
- * Removes all elements of a cache.
+ * Allocates a new cache matching the type of the cache specified by
+ * \p cache. Iterates over the \p cache cache and copies all objects
+ * to the new cache.
+ *
+ * The copied objects are clones but do not contain a reference to each
+ * other. Later modifications to objects in the original cache will
+ * not affect objects in the new cache.
+ *
+ * @return A newly allocated cache or NULL.
+ */
+struct nl_cache *nl_cache_clone(struct nl_cache *cache)
+{
+ struct nl_cache_ops *ops = nl_cache_get_ops(cache);
+ struct nl_cache *clone;
+ struct nl_object *obj;
+
+ clone = nl_cache_alloc(ops);
+ if (!clone)
+ return NULL;
+
+ NL_DBG(2, "Cloning %p into %p\n", cache, clone);
+
+ nl_list_for_each_entry(obj, &cache->c_items, ce_list)
+ nl_cache_add(clone, obj);
+
+ return clone;
+}
+
+/**
+ * Remove all objects of a cache.
+ * @arg cache Cache to clear
+ *
+ * The objects are unliked/removed from the cache by calling
+ * nl_cache_remove() on each object in the cache. If any of the objects
+ * to not contain any further references to them, those objects will
+ * be freed.
+ *
+ * Unlike with nl_cache_free(), the cache is not freed just emptied.
*/
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_DBG(2, "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);
}
+static void __nl_cache_free(struct nl_cache *cache)
+{
+ nl_cache_clear(cache);
+
+ if (cache->hashtable)
+ nl_hash_table_free(cache->hashtable);
+
+ NL_DBG(2, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache));
+ free(cache);
+}
+
+/**
+ * Increase reference counter of cache
+ * @arg cache Cache
+ */
+void nl_cache_get(struct nl_cache *cache)
+{
+ cache->c_refcnt++;
+
+ NL_DBG(3, "Incremented cache %p <%s> reference count to %d\n",
+ cache, nl_cache_name(cache), cache->c_refcnt);
+}
+
/**
* Free a cache.
* @arg cache Cache to free.
*
- * Removes all elements of a cache and frees all memory.
+ * Calls nl_cache_clear() to remove all objects associated with the
+ * cache and frees the cache afterwards.
*
- * @note Use this function if you are working with allocated caches.
+ * @see nl_cache_clear()
*/
void nl_cache_free(struct nl_cache *cache)
{
if (!cache)
return;
- nl_cache_clear(cache);
- NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache));
- free(cache);
+ cache->c_refcnt--;
+
+ NL_DBG(3, "Decremented cache %p <%s> reference count, %d remaining\n",
+ cache, nl_cache_name(cache), cache->c_refcnt);
+
+ if (cache->c_refcnt <= 0)
+ __nl_cache_free(cache);
+}
+
+void nl_cache_put(struct nl_cache *cache)
+{
+ return nl_cache_free(cache);
}
/** @} */
@@ -300,35 +433,60 @@ void nl_cache_free(struct nl_cache *cache)
static int __cache_add(struct nl_cache *cache, struct nl_object *obj)
{
+ int ret;
+
obj->ce_cache = cache;
+ if (cache->hashtable) {
+ ret = nl_hash_table_add(cache->hashtable, obj);
+ if (ret < 0) {
+ obj->ce_cache = NULL;
+ return ret;
+ }
+ }
+
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));
+ NL_DBG(3, "Added object %p to cache %p <%s>, nitems %d\n",
+ obj, cache, nl_cache_name(cache), cache->c_nitems);
return 0;
}
/**
- * Add object to a cache.
- * @arg cache Cache to add object to
+ * Add object to cache.
+ * @arg cache Cache
* @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.
+ * Adds the object \p obj to the specified \p cache. In case the object
+ * is already associated with another cache, the object is cloned before
+ * adding it to the cache. In this case, the sole reference to the object
+ * will be the one of the cache. Therefore clearing/freeing the cache
+ * will result in the object being freed again.
+ *
+ * If the object has not been associated with a cache yet, the reference
+ * counter of the object is incremented to account for the additional
+ * reference.
+ *
+ * The type of the object and cache must match, otherwise an error is
+ * returned (-NLE_OBJ_MISMATCH).
+ *
+ * @see nl_cache_move()
*
* @return 0 or a negative error code.
*/
int nl_cache_add(struct nl_cache *cache, struct nl_object *obj)
{
struct nl_object *new;
+ int ret = 0;
if (cache->c_ops->co_obj_ops != obj->ce_ops)
return -NLE_OBJ_MISMATCH;
if (!nl_list_empty(&obj->ce_list)) {
+ NL_DBG(3, "Object %p already in cache, cloning new object\n", obj);
+
new = nl_object_clone(obj);
if (!new)
return -NLE_NOMEM;
@@ -337,7 +495,11 @@ int nl_cache_add(struct nl_cache *cache, struct nl_object *obj)
new = obj;
}
- return __cache_add(cache, new);
+ ret = __cache_add(cache, new);
+ if (ret < 0)
+ nl_object_put(new);
+
+ return ret;
}
/**
@@ -345,8 +507,16 @@ int nl_cache_add(struct nl_cache *cache, struct nl_object *obj)
* @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.
+ * Removes the the specified object \p obj from its associated cache
+ * and moves it to another cache.
+ *
+ * If the object is not associated with a cache, the function behaves
+ * just like nl_cache_add().
+ *
+ * The type of the object and cache must match, otherwise an error is
+ * returned (-NLE_OBJ_MISMATCH).
+ *
+ * @see nl_cache_add()
*
* @return 0 on success or a negative error code.
*/
@@ -355,7 +525,8 @@ int nl_cache_move(struct nl_cache *cache, struct nl_object *obj)
if (cache->c_ops->co_obj_ops != obj->ce_ops)
return -NLE_OBJ_MISMATCH;
- NL_DBG(3, "Moving object %p to cache %p\n", obj, cache);
+ NL_DBG(3, "Moving object %p from cache %p to cache %p\n",
+ obj, obj->ce_cache, cache);
/* Acquire reference, if already in a cache this will be
* reverted during removal */
@@ -368,82 +539,118 @@ int nl_cache_move(struct nl_cache *cache, struct nl_object *obj)
}
/**
- * Removes an object from a cache.
- * @arg obj Object to remove from its cache
+ * Remove object from cache.
+ * @arg obj Object to remove from cache
+ *
+ * Removes the object \c obj from the cache it is associated with. The
+ * reference counter of the object will be decremented. If the reference
+ * to the object was the only one remaining, the object will be freed.
*
- * 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.
+ * If no cache is associated with the object, this function is a NOP.
*/
void nl_cache_remove(struct nl_object *obj)
{
+ int ret;
struct nl_cache *cache = obj->ce_cache;
if (cache == NULL)
return;
+ if (cache->hashtable) {
+ ret = nl_hash_table_del(cache->hashtable, obj);
+ if (ret < 0)
+ NL_DBG(2, "Failed to delete %p from cache %p <%s>.\n",
+ obj, cache, nl_cache_name(cache));
+ }
+
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",
+ NL_DBG(2, "Deleted object %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.
+ * @name Synchronization
+ * @{
+ */
+
+/**
+ * Set synchronization arg1 of cache
+ * @arg cache Cache
+ * @arg arg argument
*
- * @return Reference to object or NULL if not found.
- * @note The returned object must be returned via nl_object_put().
+ * Synchronization arguments are used to specify filters when
+ * requesting dumps from the kernel.
*/
-struct nl_object *nl_cache_search(struct nl_cache *cache,
- struct nl_object *needle)
+void nl_cache_set_arg1(struct nl_cache *cache, int arg)
{
- 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;
+ cache->c_iarg1 = arg;
}
-
-/** @} */
+/**
+ * Set synchronization arg2 of cache
+ * @arg cache Cache
+ * @arg arg argument
+ *
+ * Synchronization arguments are used to specify filters when
+ * requesting dumps from the kernel.
+ */
+void nl_cache_set_arg2(struct nl_cache *cache, int arg)
+{
+ cache->c_iarg2 = arg;
+}
/**
- * @name Synchronization
- * @{
+ * Set cache flags
+ * @arg cache Cache
+ * @arg flags Flags
*/
+void nl_cache_set_flags(struct nl_cache *cache, unsigned int flags)
+{
+ cache->c_flags |= flags;
+}
/**
- * Request a full dump from the kernel to fill a cache
+ * Invoke the request-update operation
* @arg sk Netlink socket.
- * @arg cache Cache subjected to be filled.
+ * @arg cache Cache
+ *
+ * This function causes the \e request-update function of the cache
+ * operations to be invoked. This usually causes a dump request to
+ * be sent over the netlink socket which triggers the kernel to dump
+ * all objects of a specific type to be dumped onto the netlink
+ * socket for pickup.
+ *
+ * The behaviour of this function depends on the implemenation of
+ * the \e request_update function of each individual type of cache.
*
- * Send a dumping request to the kernel causing it to dump all objects
- * related to the specified cache to the netlink socket.
+ * This function will not have any effects on the cache (unless the
+ * request_update implementation of the cache operations does so).
*
- * Use nl_cache_pickup() to read the objects from the socket and fill them
- * into a cache.
+ * Use nl_cache_pickup() to pick-up (read) the objects from the socket
+ * and fill them into the cache.
+ *
+ * @see nl_cache_pickup(), nl_cache_resync()
+ *
+ * @return 0 on success or a negative error code.
*/
-int nl_cache_request_full_dump(struct nl_sock *sk, struct nl_cache *cache)
+static int nl_cache_request_full_dump(struct nl_sock *sk,
+ struct nl_cache *cache)
{
- NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n",
- cache, nl_cache_name(cache));
+ if (sk->s_proto != cache->c_ops->co_protocol)
+ return -NLE_PROTO_MISMATCH;
if (cache->c_ops->co_request_update == NULL)
return -NLE_OPNOTSUPP;
+ NL_DBG(2, "Requesting update from kernel for cache %p <%s>\n",
+ cache, nl_cache_name(cache));
+
return cache->c_ops->co_request_update(cache, sk);
}
@@ -456,13 +663,24 @@ struct update_xdata {
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);
+ int ret = 0;
+
+ ret = nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params);
+ if (ret == -NLE_EXIST)
+ return NL_SKIP;
+ else
+ return ret;
}
/** @endcond */
-int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
- struct nl_parser_param *param)
+/**
+ * Pick-up a netlink request-update with your own parser
+ * @arg sk Netlink socket
+ * @arg cache Cache
+ * @arg param Parser parameters
+ */
+static int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
+ struct nl_parser_param *param)
{
int err;
struct nl_cb *cb;
@@ -471,8 +689,8 @@ int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
.params = param,
};
- NL_DBG(1, "Picking up answer for cache %p <%s>...\n",
- cache, nl_cache_name(cache));
+ NL_DBG(2, "Picking up answer for cache %p <%s>\n",
+ cache, nl_cache_name(cache));
cb = nl_cb_clone(sk->s_cb);
if (cb == NULL)
@@ -482,9 +700,8 @@ int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
err = nl_recvmsgs(sk, 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(err));
+ NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned %d: %s\n",
+ cache, nl_cache_name(cache), err, nl_geterror(err));
nl_cb_put(cb);
@@ -493,7 +710,21 @@ int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
static int pickup_cb(struct nl_object *c, struct nl_parser_param *p)
{
- return nl_cache_add((struct nl_cache *) p->pp_arg, c);
+ struct nl_cache *cache = (struct nl_cache *)p->pp_arg;
+ struct nl_object *old;
+
+ old = nl_cache_search(cache, c);
+ if (old) {
+ if (nl_object_update(old, c) == 0) {
+ nl_object_put(old);
+ return 0;
+ }
+
+ nl_cache_remove(old);
+ nl_object_put(old);
+ }
+
+ return nl_cache_add(cache, c);
}
/**
@@ -502,7 +733,10 @@ static int pickup_cb(struct nl_object *c, struct nl_parser_param *p)
* @arg cache Cache to put items into.
*
* Waits for netlink messages to arrive, parses them and puts them into
- * the specified cache.
+ * the specified cache. If an old object with same key attributes is
+ * present in the cache, it is replaced with the new object.
+ * If the old object type supports an update operation, an update is
+ * attempted before a replace.
*
* @return 0 on success or a negative error code.
*/
@@ -513,6 +747,9 @@ int nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache)
.pp_arg = cache,
};
+ if (sk->s_proto != cache->c_ops->co_protocol)
+ return -NLE_PROTO_MISMATCH;
+
return __cache_pickup(sk, cache, &p);
}
@@ -526,6 +763,18 @@ static int cache_include(struct nl_cache *cache, struct nl_object *obj,
case NL_ACT_DEL:
old = nl_cache_search(cache, obj);
if (old) {
+ /*
+ * Some objects types might support merging the new
+ * object with the old existing cache object.
+ * Handle them first.
+ */
+ if (nl_object_update(old, obj) == 0) {
+ if (cb)
+ cb(cache, old, NL_ACT_CHANGE, data);
+ nl_object_put(old);
+ return 0;
+ }
+
nl_cache_remove(old);
if (type->mt_act == NL_ACT_DEL) {
if (cb)
@@ -568,6 +817,9 @@ int nl_cache_include(struct nl_cache *cache, struct nl_object *obj,
return cache_include(cache, obj, &ops->co_msgtypes[i],
change_cb, data);
+ NL_DBG(3, "Object %p does not seem to belong to cache %p <%s>\n",
+ obj, cache, nl_cache_name(cache));
+
return -NLE_MSGTYPE_NOSUPPORT;
}
@@ -582,6 +834,7 @@ int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache,
change_func_t change_cb, void *data)
{
struct nl_object *obj, *next;
+ struct nl_af_group *grp;
struct nl_cache_assoc ca = {
.ca_cache = cache,
.ca_change = change_cb,
@@ -593,18 +846,35 @@ int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache,
};
int err;
+ if (sk->s_proto != cache->c_ops->co_protocol)
+ return -NLE_PROTO_MISMATCH;
+
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(sk, cache);
- if (err < 0)
- goto errout;
+ grp = cache->c_ops->co_groups;
+ do {
+ if (grp && grp->ag_group &&
+ (cache->c_flags & NL_CACHE_AF_ITER))
+ nl_cache_set_arg1(cache, grp->ag_family);
- err = __cache_pickup(sk, cache, &p);
- if (err < 0)
- goto errout;
+restart:
+ err = nl_cache_request_full_dump(sk, cache);
+ if (err < 0)
+ goto errout;
+
+ err = __cache_pickup(sk, cache, &p);
+ if (err == -NLE_DUMP_INTR)
+ goto restart;
+ else if (err < 0)
+ goto errout;
+
+ if (grp)
+ grp++;
+ } while (grp && grp->ag_group &&
+ (cache->c_flags & NL_CACHE_AF_ITER));
nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list) {
if (nl_object_is_marked(obj)) {
@@ -660,7 +930,10 @@ errout:
* @arg msg netlink message
*
* Parses a netlink message by calling the cache specific message parser
- * and adds the new element to the cache.
+ * and adds the new element to the cache. If an old object with same key
+ * attributes is present in the cache, it is replaced with the new object.
+ * If the old object type supports an update operation, an update is
+ * attempted before a replace.
*
* @return 0 or a negative error code.
*/
@@ -686,17 +959,40 @@ int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg)
*/
int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache)
{
+ struct nl_af_group *grp;
int err;
- err = nl_cache_request_full_dump(sk, cache);
- if (err < 0)
- return err;
+ if (sk->s_proto != cache->c_ops->co_protocol)
+ return -NLE_PROTO_MISMATCH;
- NL_DBG(2, "Upading cache %p <%s>, request sent, waiting for dump...\n",
- cache, nl_cache_name(cache));
nl_cache_clear(cache);
+ grp = cache->c_ops->co_groups;
+ do {
+ if (grp && grp->ag_group &&
+ (cache->c_flags & NL_CACHE_AF_ITER))
+ nl_cache_set_arg1(cache, grp->ag_family);
+
+restart:
+ err = nl_cache_request_full_dump(sk, cache);
+ if (err < 0)
+ return err;
+
+ NL_DBG(2, "Updating cache %p <%s> for family %u, request sent, waiting for reply\n",
+ cache, nl_cache_name(cache), grp ? grp->ag_family : AF_UNSPEC);
+
+ err = nl_cache_pickup(sk, cache);
+ if (err == -NLE_DUMP_INTR) {
+ NL_DBG(2, "Dump interrupted, restarting!\n");
+ goto restart;
+ } else if (err < 0)
+ break;
+
+ if (grp)
+ grp++;
+ } while (grp && grp->ag_group &&
+ (cache->c_flags & NL_CACHE_AF_ITER));
- return nl_cache_pickup(sk, cache);
+ return err;
}
/** @} */
@@ -705,17 +1001,105 @@ int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache)
* @name Utillities
* @{
*/
+static struct nl_object *__cache_fast_lookup(struct nl_cache *cache,
+ struct nl_object *needle)
+{
+ struct nl_object *obj;
+
+ obj = nl_hash_table_lookup(cache->hashtable, needle);
+ if (obj) {
+ nl_object_get(obj);
+ return obj;
+ }
+
+ return NULL;
+}
+
+/**
+ * Search object in cache
+ * @arg cache Cache
+ * @arg needle Object to look for.
+ *
+ * Searches the cache for an object which matches the object \p needle.
+ * The function nl_object_identical() is used to determine if the
+ * objects match. If a matching object is found, the reference counter
+ * is incremented and the object is returned.
+ *
+ * Therefore, if an object is returned, the reference to the object
+ * must be returned by calling nl_object_put() after usage.
+ *
+ * @return Reference to object or NULL if not found.
+ */
+struct nl_object *nl_cache_search(struct nl_cache *cache,
+ struct nl_object *needle)
+{
+ struct nl_object *obj;
+
+ if (cache->hashtable)
+ return __cache_fast_lookup(cache, needle);
+
+ 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;
+}
+
+/**
+ * Find object in cache
+ * @arg cache Cache
+ * @arg filter object acting as a filter
+ *
+ * Searches the cache for an object which matches the object filter.
+ * If the filter attributes matches the object type id attributes,
+ * and the cache supports hash lookups, a faster hashtable lookup
+ * is used to return the object. Else, function nl_object_match_filter() is
+ * used to determine if the objects match. If a matching object is
+ * found, the reference counter is incremented and the object is returned.
+ *
+ * Therefore, if an object is returned, the reference to the object
+ * must be returned by calling nl_object_put() after usage.
+ *
+ * @return Reference to object or NULL if not found.
+ */
+struct nl_object *nl_cache_find(struct nl_cache *cache,
+ struct nl_object *filter)
+{
+ struct nl_object *obj;
+
+ if (cache->c_ops == NULL)
+ BUG();
+
+ if ((nl_object_get_id_attrs(filter) == filter->ce_mask)
+ && cache->hashtable)
+ return __cache_fast_lookup(cache, filter);
+
+ nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
+ if (nl_object_match_filter(obj, filter)) {
+ nl_object_get(obj);
+ return obj;
+ }
+ }
+
+ return NULL;
+}
/**
- * Mark all objects in a cache
- * @arg cache Cache to mark all objects in
+ * Mark all objects of a cache
+ * @arg cache Cache
+ *
+ * Marks all objects of a cache by calling nl_object_mark() on each
+ * object associated with the cache.
*/
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_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);
@@ -757,7 +1141,7 @@ void nl_cache_dump_filter(struct nl_cache *cache,
struct nl_object_ops *ops;
struct nl_object *obj;
- NL_DBG(2, "Dumping cache %p <%s> filter %p\n",
+ NL_DBG(2, "Dumping cache %p <%s> with filter %p\n",
cache, nl_cache_name(cache), filter);
if (type > NL_DUMP_MAX || type < 0)
@@ -770,6 +1154,9 @@ void nl_cache_dump_filter(struct nl_cache *cache,
if (!ops->oo_dump[type])
return;
+ if (params && params->dp_buf)
+ memset(params->dp_buf, 0, params->dp_buflen);
+
nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
if (filter && !nl_object_match_filter(obj, filter))
continue;
@@ -816,18 +1203,27 @@ 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;
+ if (filter) {
+ int diff = nl_object_match_filter(obj, filter);
+
+ NL_DBG(3, "%p<->%p object difference: %x\n",
+ obj, filter, diff);
+
+ if (!diff)
+ continue;
+ }
+
+ /* Caller may hold obj for a long time */
+ nl_object_get(obj);
cb(obj, arg);
+
+ nl_object_put(obj);
}
}
diff --git a/lib/cache_mngr.c b/lib/cache_mngr.c
index 81052aa7..9b25e9b8 100644
--- a/lib/cache_mngr.c
+++ b/lib/cache_mngr.c
@@ -6,96 +6,62 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
* @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
+ * @brief Manager keeping caches up to date automatically.
+ *
+ * The cache manager keeps caches up to date automatically by listening to
+ * netlink notifications and integrating the received information into the
+ * existing cache.
+ *
+ * @note This functionality is still considered experimental.
+ *
+ * Related sections in the development guide:
+ * - @core_doc{_cache_manager,Cache Manager}
+ *
* @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/cache.h>
+ * ~~~~
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/utils.h>
+/** @cond SKIP */
+#define NASSOC_INIT 16
+#define NASSOC_EXPAND 8
+/** @endcond */
+
static int include_cb(struct nl_object *obj, struct nl_parser_param *p)
{
struct nl_cache_assoc *ca = p->pp_arg;
+ struct nl_cache_ops *ops = ca->ca_cache->c_ops;
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
- return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data);
+
+ if (ops->co_event_filter)
+ if (ops->co_event_filter(ca->ca_cache, obj) != NL_OK)
+ return 0;
+
+ if (ops->co_include_event)
+ return ops->co_include_event(ca->ca_cache, obj, ca->ca_change,
+ ca->ca_change_data);
+ else
+ return nl_cache_include(ca->ca_cache, obj, ca->ca_change, ca->ca_change_data);
}
static int event_input(struct nl_msg *msg, void *arg)
@@ -140,11 +106,31 @@ found:
/**
* Allocate new cache manager
- * @arg sk Netlink socket.
- * @arg protocol Netlink Protocol this manager is used for
- * @arg flags Flags
+ * @arg sk Netlink socket or NULL to auto allocate
+ * @arg protocol Netlink protocol this manager is used for
+ * @arg flags Flags (\c NL_AUTO_PROVIDE)
+ * @arg result Result pointer
*
- * @return Newly allocated cache manager or NULL on failure.
+ * Allocates a new cache manager for the specified netlink protocol.
+ *
+ * 1. If sk is not specified (\c NULL) a netlink socket matching the
+ * specified protocol will be automatically allocated.
+ *
+ * 2. The socket will be put in non-blocking mode and sequence checking
+ * will be disabled regardless of whether the socket was provided by
+ * the caller or automatically allocated.
+ *
+ * 3. The socket will be connected.
+ *
+ * If the flag \c NL_AUTO_PROVIDE is specified, any cache added to the
+ * manager will automatically be made available to other users using
+ * nl_cache_mngt_provide().
+ *
+ * @note If the socket is provided by the caller, it is NOT recommended
+ * to use the socket for anything else besides receiving netlink
+ * notifications.
+ *
+ * @return 0 on success or a negative error code.
*/
int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags,
struct nl_cache_mngr **result)
@@ -152,15 +138,23 @@ int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags,
struct nl_cache_mngr *mngr;
int err = -NLE_NOMEM;
- if (sk == NULL)
+ /* Catch abuse of flags */
+ if (flags & NL_ALLOCATED_SOCK)
BUG();
mngr = calloc(1, sizeof(*mngr));
if (!mngr)
- goto errout;
+ return -NLE_NOMEM;
+
+ if (!sk) {
+ if (!(sk = nl_socket_alloc()))
+ goto errout;
+
+ flags |= NL_ALLOCATED_SOCK;
+ }
- mngr->cm_handle = sk;
- mngr->cm_nassocs = 32;
+ mngr->cm_sock = sk;
+ mngr->cm_nassocs = NASSOC_INIT;
mngr->cm_protocol = protocol;
mngr->cm_flags = flags;
mngr->cm_assocs = calloc(mngr->cm_nassocs,
@@ -168,55 +162,72 @@ int nl_cache_mngr_alloc(struct nl_sock *sk, int protocol, int flags,
if (!mngr->cm_assocs)
goto errout;
- nl_socket_modify_cb(mngr->cm_handle, NL_CB_VALID, NL_CB_CUSTOM,
- event_input, mngr);
-
/* Required to receive async event notifications */
- nl_socket_disable_seq_check(mngr->cm_handle);
+ nl_socket_disable_seq_check(mngr->cm_sock);
- if ((err = nl_connect(mngr->cm_handle, protocol) < 0))
+ if ((err = nl_connect(mngr->cm_sock, protocol)) < 0)
goto errout;
- if ((err = nl_socket_set_nonblocking(mngr->cm_handle) < 0))
+ if ((err = nl_socket_set_nonblocking(mngr->cm_sock)) < 0)
goto errout;
+ /* Create and allocate socket for sync cache fills */
+ mngr->cm_sync_sock = nl_socket_alloc();
+ if (!mngr->cm_sync_sock) {
+ err = -NLE_NOMEM;
+ goto errout;
+ }
+ if ((err = nl_connect(mngr->cm_sync_sock, protocol)) < 0)
+ goto errout_free_sync_sock;
+
NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n",
mngr, protocol, mngr->cm_nassocs);
*result = mngr;
return 0;
+errout_free_sync_sock:
+ nl_socket_free(mngr->cm_sync_sock);
errout:
nl_cache_mngr_free(mngr);
return err;
}
/**
- * Add cache responsibility to cache manager
+ * Add cache to cache manager
* @arg mngr Cache manager.
- * @arg name Name of cache to keep track of
+ * @arg cache Cache to be added to cache manager
* @arg cb Function to be called upon changes.
- * @arg result Pointer to store added cache.
+ * @arg data Argument passed on to change callback
*
- * 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.
+ * Adds cache 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 and keep track of any further changes.
+ *
+ * The user is responsible for calling nl_cache_mngr_poll() or monitor
+ * the socket and call nl_cache_mngr_data_ready() to allow the library
+ * to process netlink notification events.
+ *
+ * @see nl_cache_mngr_poll()
+ * @see nl_cache_mngr_data_ready()
*
* @return 0 on success or a negative error code.
+ * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and
+ * cache type
+ * @return -NLE_OPNOTSUPP Cache type does not support updates
+ * @return -NLE_EXIST Cache of this type already being managed
*/
-int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
- change_func_t cb, void *data, struct nl_cache **result)
+int nl_cache_mngr_add_cache(struct nl_cache_mngr *mngr, struct nl_cache *cache,
+ change_func_t cb, void *data)
{
struct nl_cache_ops *ops;
- struct nl_cache *cache;
struct nl_af_group *grp;
int err, i;
- ops = nl_cache_ops_lookup(name);
+ ops = cache->c_ops;
if (!ops)
- return -NLE_NOCACHE;
+ return -NLE_INVAL;
if (ops->co_protocol != mngr->cm_protocol)
return -NLE_PROTO_MISMATCH;
@@ -235,30 +246,28 @@ retry:
break;
if (i >= mngr->cm_nassocs) {
- mngr->cm_nassocs += 16;
+ mngr->cm_nassocs += NASSOC_EXPAND;
mngr->cm_assocs = realloc(mngr->cm_assocs,
mngr->cm_nassocs *
sizeof(struct nl_cache_assoc));
if (mngr->cm_assocs == NULL)
return -NLE_NOMEM;
- 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)
- return -NLE_NOMEM;
+ memset(mngr->cm_assocs + (mngr->cm_nassocs - NASSOC_EXPAND), 0,
+ NASSOC_EXPAND * sizeof(struct nl_cache_assoc));
+
+ NL_DBG(1, "Increased capacity of cache manager %p " \
+ "to %d\n", mngr, mngr->cm_nassocs);
+ goto retry;
+ }
for (grp = ops->co_groups; grp->ag_group; grp++) {
- err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group);
+ err = nl_socket_add_membership(mngr->cm_sock, grp->ag_group);
if (err < 0)
- goto errout_free_cache;
+ return err;
}
- err = nl_cache_refill(mngr->cm_handle, cache);
+ err = nl_cache_refill(mngr->cm_sync_sock, cache);
if (err < 0)
goto errout_drop_membership;
@@ -272,12 +281,66 @@ retry:
NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
cache, nl_cache_name(cache), mngr);
- *result = cache;
return 0;
errout_drop_membership:
for (grp = ops->co_groups; grp->ag_group; grp++)
- nl_socket_drop_membership(mngr->cm_handle, grp->ag_group);
+ nl_socket_drop_membership(mngr->cm_sock, grp->ag_group);
+
+ return err;
+}
+
+/**
+ * Add cache to cache manager
+ * @arg mngr Cache manager.
+ * @arg name Name of cache to keep track of
+ * @arg cb Function to be called upon changes.
+ * @arg data Argument passed on to change callback
+ * @arg result Pointer to store added cache (optional)
+ *
+ * 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 and keep track of any further
+ * changes.
+ *
+ * The user is responsible for calling nl_cache_mngr_poll() or monitor
+ * the socket and call nl_cache_mngr_data_ready() to allow the library
+ * to process netlink notification events.
+ *
+ * @see nl_cache_mngr_poll()
+ * @see nl_cache_mngr_data_ready()
+ *
+ * @return 0 on success or a negative error code.
+ * @return -NLE_NOCACHE Unknown cache type
+ * @return -NLE_PROTO_MISMATCH Protocol mismatch between cache manager and
+ * cache type
+ * @return -NLE_OPNOTSUPP Cache type does not support updates
+ * @return -NLE_EXIST Cache of this type already being managed
+ */
+int nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
+ change_func_t cb, void *data, struct nl_cache **result)
+{
+ struct nl_cache_ops *ops;
+ struct nl_cache *cache;
+ int err;
+
+ ops = nl_cache_ops_lookup_safe(name);
+ if (!ops)
+ return -NLE_NOCACHE;
+
+ cache = nl_cache_alloc(ops);
+ nl_cache_ops_put(ops);
+ if (!cache)
+ return -NLE_NOMEM;
+
+ err = nl_cache_mngr_add_cache(mngr, cache, cb, data);
+ if (err < 0)
+ goto errout_free_cache;
+
+ *result = cache;
+ return 0;
+
errout_free_cache:
nl_cache_free(cache);
@@ -285,16 +348,17 @@ errout_free_cache:
}
/**
- * Get file descriptor
+ * Get socket 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().
+ * Get the file descriptor of the socket associated with the manager.
+ *
+ * @note Do not use the socket for anything besides receiving
+ * notifications.
*/
int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
{
- return nl_socket_get_fd(mngr->cm_handle);
+ return nl_socket_get_fd(mngr->cm_sock);
}
/**
@@ -303,20 +367,24 @@ int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
* @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.
+ * being available. Calls nl_cache_mngr_data_ready() to process
+ * available data.
*
* 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.
+ * A timeout can be specified in milliseconds to limit the time the
+ * function will wait for updates.
+ *
+ * @see nl_cache_mngr_data_ready()
+ *
+ * @return The number of messages processed 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),
+ .fd = nl_socket_get_fd(mngr->cm_sock),
.events = POLLIN,
};
@@ -326,6 +394,7 @@ int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
if (ret < 0)
return -nl_syserr2nlerr(errno);
+ /* No events, return */
if (ret == 0)
return 0;
@@ -337,28 +406,90 @@ int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
* @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.
+ * contains updates to be received. This function should only be used
+ * if nl_cache_mngr_poll() is not used.
+ *
+ * The function will process messages until there is no more data to
+ * be read from the socket.
*
- * @return A positive value if at least one update was handled, 0
- * for none, or a negative error code.
+ * @see nl_cache_mngr_poll()
+ *
+ * @return The number of messages processed or a negative error code.
*/
int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
{
- int err;
+ int err, nread = 0;
+ struct nl_cb *cb;
- err = nl_recvmsgs_default(mngr->cm_handle);
- if (err < 0)
+ NL_DBG(2, "Cache manager %p, reading new data from fd %d\n",
+ mngr, nl_socket_get_fd(mngr->cm_sock));
+
+ cb = nl_cb_clone(mngr->cm_sock->s_cb);
+ if (cb == NULL)
+ return -NLE_NOMEM;
+
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, event_input, mngr);
+
+ while ((err = nl_recvmsgs_report(mngr->cm_sock, cb)) > 0) {
+ NL_DBG(2, "Cache manager %p, recvmsgs read %d messages\n",
+ mngr, err);
+ nread += err;
+ }
+
+ nl_cb_put(cb);
+ if (err < 0 && err != -NLE_AGAIN)
return err;
- return 1;
+ return nread;
+}
+
+/**
+ * Print information about cache manager
+ * @arg mngr Cache manager
+ * @arg p Dumping parameters
+ *
+ * Prints information about the cache manager including all managed caches.
+ *
+ * @note This is a debugging function.
+ */
+void nl_cache_mngr_info(struct nl_cache_mngr *mngr, struct nl_dump_params *p)
+{
+ char buf[128];
+ int i;
+
+ nl_dump_line(p, "cache-manager <%p>\n", mngr);
+ nl_dump_line(p, " .protocol = %s\n",
+ nl_nlfamily2str(mngr->cm_protocol, buf, sizeof(buf)));
+ nl_dump_line(p, " .flags = %#x\n", mngr->cm_flags);
+ nl_dump_line(p, " .nassocs = %u\n", mngr->cm_nassocs);
+ nl_dump_line(p, " .sock = <%p>\n", mngr->cm_sock);
+
+ for (i = 0; i < mngr->cm_nassocs; i++) {
+ struct nl_cache_assoc *assoc = &mngr->cm_assocs[i];
+
+ if (assoc->ca_cache) {
+ nl_dump_line(p, " .cache[%d] = <%p> {\n", i, assoc->ca_cache);
+ nl_dump_line(p, " .name = %s\n", assoc->ca_cache->c_ops->co_name);
+ nl_dump_line(p, " .change_func = <%p>\n", assoc->ca_change);
+ nl_dump_line(p, " .change_data = <%p>\n", assoc->ca_change_data);
+ nl_dump_line(p, " .nitems = %u\n", nl_cache_nitems(assoc->ca_cache));
+ nl_dump_line(p, " .objects = {\n");
+
+ p->dp_prefix += 6;
+ nl_cache_dump(assoc->ca_cache, p);
+ p->dp_prefix -= 6;
+
+ nl_dump_line(p, " }\n");
+ nl_dump_line(p, " }\n");
+ }
+ }
}
/**
* Free cache manager and all caches.
* @arg mngr Cache manager.
*
- * Release all resources after usage of a cache manager.
+ * Release all resources held by a cache manager.
*/
void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
{
@@ -367,17 +498,29 @@ void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
if (!mngr)
return;
- if (mngr->cm_handle)
- nl_close(mngr->cm_handle);
+ if (mngr->cm_sock)
+ nl_close(mngr->cm_sock);
- for (i = 0; i < mngr->cm_nassocs; i++)
- if (mngr->cm_assocs[i].ca_cache)
+ if (mngr->cm_sync_sock) {
+ nl_close(mngr->cm_sync_sock);
+ nl_socket_free(mngr->cm_sync_sock);
+ }
+
+ if (mngr->cm_flags & NL_ALLOCATED_SOCK)
+ nl_socket_free(mngr->cm_sock);
+
+ for (i = 0; i < mngr->cm_nassocs; i++) {
+ if (mngr->cm_assocs[i].ca_cache) {
+ nl_cache_mngt_unprovide(mngr->cm_assocs[i].ca_cache);
nl_cache_free(mngr->cm_assocs[i].ca_cache);
+ }
+ }
free(mngr->cm_assocs);
- free(mngr);
NL_DBG(1, "Cache manager %p freed\n", mngr);
+
+ free(mngr);
}
/** @} */
diff --git a/lib/cache_mngt.c b/lib/cache_mngt.c
index d57d8364..4d3d6ff6 100644
--- a/lib/cache_mngt.c
+++ b/lib/cache_mngt.c
@@ -6,57 +6,109 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
* @ingroup core
- * @defgroup cache_mngt Caching
+ * @defgroup cache_mngt Caching System
+ *
+ * Related sections in the development guide:
+ * - @core_doc{core_cache, Caching System}
+ *
* @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/cache.h>
+ * ~~~~
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/utils.h>
static struct nl_cache_ops *cache_ops;
+static NL_RW_LOCK(cache_ops_lock);
/**
* @name Cache Operations Sets
* @{
*/
+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;
+}
+
/**
- * Lookup the set cache operations of a certain cache type
+ * Increment reference counter
+ * @arg ops Cache operations
+ */
+void nl_cache_ops_get(struct nl_cache_ops *ops)
+{
+ ops->co_refcnt++;
+}
+
+/**
+ * Decrement reference counter
+ * @arg ops Cache operations
+ */
+void nl_cache_ops_put(struct nl_cache_ops *ops)
+{
+ ops->co_refcnt--;
+}
+
+/**
+ * Lookup cache operations by name
* @arg name name of the cache type
*
- * @return The cache operations or NULL if no operations
- * have been registered under the specified name.
+ * @attention This function is not safe, it does not increment the reference
+ * counter. Please use nl_cache_ops_lookup_safe().
+ *
+ * @return The cache operations or NULL if not found.
*/
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;
+ nl_read_lock(&cache_ops_lock);
+ ops = __nl_cache_ops_lookup(name);
+ nl_read_unlock(&cache_ops_lock);
- return NULL;
+ return ops;
}
/**
- * Associate a message type to a set of cache operations
- * @arg protocol netlink protocol
- * @arg msgtype netlink message type
+ * Lookup cache operations by name
+ * @arg name name of the cache type
*
- * Associates the specified netlink message type with
- * a registered set of cache operations.
+ * @note The reference counter of the returned cache operation is incremented
+ * and must be decremented after use with nl_cache_ops_put().
*
- * @return The cache operations or NULL if no association
- * could be made.
+ * @return The cache operations or NULL if not found.
*/
-struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype)
+struct nl_cache_ops *nl_cache_ops_lookup_safe(const char *name)
+{
+ struct nl_cache_ops *ops;
+
+ nl_write_lock(&cache_ops_lock);
+ if ((ops = __nl_cache_ops_lookup(name)))
+ nl_cache_ops_get(ops);
+ nl_write_unlock(&cache_ops_lock);
+
+ return ops;
+}
+
+static struct nl_cache_ops *__cache_ops_associate(int protocol, int msgtype)
{
int i;
struct nl_cache_ops *ops;
@@ -74,6 +126,54 @@ struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype)
}
/**
+ * Associate protocol and message type to cache operations
+ * @arg protocol netlink protocol
+ * @arg msgtype netlink message type
+ *
+ * @attention This function is not safe, it does not increment the reference
+ * counter. Please use nl_cache_ops_associate_safe().
+ *
+ * @see nl_cache_ops_associate_safe()
+ *
+ * @return The cache operations or NULL if no match found.
+ */
+struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype)
+{
+ struct nl_cache_ops *ops;
+
+ nl_read_lock(&cache_ops_lock);
+ ops = __cache_ops_associate(protocol, msgtype);
+ nl_read_unlock(&cache_ops_lock);
+
+ return ops;
+}
+
+/**
+ * Associate protocol and message type to cache operations
+ * @arg protocol netlink protocol
+ * @arg msgtype netlink message type
+ *
+ * Searches the registered cache operations for a matching protocol
+ * and message type.
+ *
+ * @note The reference counter of the returned cache operation is incremented
+ * and must be decremented after use with nl_cache_ops_put().
+ *
+ * @return The cache operations or NULL if no no match was found.
+ */
+struct nl_cache_ops *nl_cache_ops_associate_safe(int protocol, int msgtype)
+{
+ struct nl_cache_ops *ops;
+
+ nl_write_lock(&cache_ops_lock);
+ if ((ops = __cache_ops_associate(protocol, msgtype)))
+ nl_cache_ops_get(ops);
+ nl_write_unlock(&cache_ops_lock);
+
+ return ops;
+}
+
+/**
* Lookup message type cache association
* @arg ops cache operations
* @arg msgtype netlink message type
@@ -81,6 +181,9 @@ struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype)
* Searches for a matching message type association ing the specified
* cache operations.
*
+ * @attention The guranteed lifetime of the returned message type is bound
+ * to the lifetime of the underlying cache operations.
+ *
* @return A message type association or NULL.
*/
struct nl_msgtype *nl_msgtype_lookup(struct nl_cache_ops *ops, int msgtype)
@@ -94,6 +197,7 @@ struct nl_msgtype *nl_msgtype_lookup(struct nl_cache_ops *ops, int msgtype)
return NULL;
}
+/* Must hold cache_ops_lock */
static struct nl_cache_ops *cache_ops_lookup_for_obj(struct nl_object_ops *obj_ops)
{
struct nl_cache_ops *ops;
@@ -115,8 +219,25 @@ void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *arg)
{
struct nl_cache_ops *ops;
+ nl_read_lock(&cache_ops_lock);
for (ops = cache_ops; ops; ops = ops->co_next)
cb(ops, arg);
+ nl_read_unlock(&cache_ops_lock);
+}
+
+/**
+ * Set default flags for caches of this type
+ * @arg ops Cache ops
+ * @arg flags Flags to set
+ *
+ * The cache operation flags will be derived to all caches allocates
+ * based on this set of cache operations.
+ */
+void nl_cache_ops_set_flags(struct nl_cache_ops *ops, unsigned int flags)
+{
+ nl_write_lock(&cache_ops_lock);
+ ops->co_flags |= flags;
+ nl_write_unlock(&cache_ops_lock);
}
/**
@@ -133,11 +254,16 @@ int nl_cache_mngt_register(struct nl_cache_ops *ops)
if (!ops->co_name || !ops->co_obj_ops)
return -NLE_INVAL;
- if (nl_cache_ops_lookup(ops->co_name))
+ nl_write_lock(&cache_ops_lock);
+ if (__nl_cache_ops_lookup(ops->co_name)) {
+ nl_write_unlock(&cache_ops_lock);
return -NLE_EXIST;
+ }
+ ops->co_refcnt = 0;
ops->co_next = cache_ops;
cache_ops = ops;
+ nl_write_unlock(&cache_ops_lock);
NL_DBG(1, "Registered cache operations %s\n", ops->co_name);
@@ -158,18 +284,31 @@ int nl_cache_mngt_register(struct nl_cache_ops *ops)
int nl_cache_mngt_unregister(struct nl_cache_ops *ops)
{
struct nl_cache_ops *t, **tp;
+ int err = 0;
+
+ nl_write_lock(&cache_ops_lock);
+
+ if (ops->co_refcnt > 0) {
+ err = -NLE_BUSY;
+ goto errout;
+ }
for (tp = &cache_ops; (t=*tp) != NULL; tp = &t->co_next)
if (t == ops)
break;
- if (!t)
- return -NLE_NOCACHE;
+ if (!t) {
+ err = -NLE_NOCACHE;
+ goto errout;
+ }
NL_DBG(1, "Unregistered cache operations %s\n", ops->co_name);
*tp = t->co_next;
- return 0;
+errout:
+ nl_write_unlock(&cache_ops_lock);
+
+ return err;
}
/** @} */
@@ -191,11 +330,25 @@ void nl_cache_mngt_provide(struct nl_cache *cache)
{
struct nl_cache_ops *ops;
+ nl_write_lock(&cache_ops_lock);
+
ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops);
if (!ops)
BUG();
- else
+ else {
+ nl_cache_get(cache);
+
+ /*
+ * Hold a reference to the cache operations to ensure the
+ * ops don't go away while we use it to store the cache pointer.
+ */
+ if (!ops->co_major_cache)
+ nl_cache_ops_get(ops);
+
ops->co_major_cache = cache;
+ }
+
+ nl_write_unlock(&cache_ops_lock);
}
/**
@@ -210,38 +363,75 @@ void nl_cache_mngt_unprovide(struct nl_cache *cache)
{
struct nl_cache_ops *ops;
+ nl_write_lock(&cache_ops_lock);
+
ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops);
if (!ops)
BUG();
- else if (ops->co_major_cache == cache)
+ else if (ops->co_major_cache == cache) {
+ nl_cache_free(ops->co_major_cache);
+ nl_cache_ops_put(ops);
ops->co_major_cache = NULL;
+ }
+
+ nl_write_unlock(&cache_ops_lock);
+}
+
+struct nl_cache *__nl_cache_mngt_require(const char *name)
+{
+ struct nl_cache_ops *ops;
+ struct nl_cache *cache = NULL;
+
+ ops = nl_cache_ops_lookup_safe(name);
+ if (ops) {
+ cache = ops->co_major_cache;
+ nl_cache_ops_put(ops);
+ }
+
+ return cache;
}
/**
- * Demand the use of a global cache
- * @arg name name of the required object type
+ * Return cache previously provided via nl_cache_mngt_provide()
+ * @arg name Name of cache to lookup
*
- * Trys to find a cache of the specified type for global
- * use.
+ * @attention This function is not safe, it does not increment the reference
+ * counter. Please use nl_cache_mngt_require_safe().
*
- * @return A cache provided by another subsystem of the
- * specified type marked to be available.
+ * @see nl_cache_mngt_require_safe()
+ *
+ * @return Pointer to cache or NULL if none registered
*/
struct nl_cache *nl_cache_mngt_require(const char *name)
{
- struct nl_cache_ops *ops;
+ struct nl_cache *cache;
- 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);
+ if (!(cache = __nl_cache_mngt_require(name)))
+ NL_DBG(1, "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 cache;
+}
- return NULL;
- }
+/**
+ * Return cache previously provided via nl_cache_mngt_provide()
+ * @arg name Name of cache to lookup
+ *
+ * @note The reference counter of the returned cache is incremented
+ * and must be decremented after use with nl_cache_put().
+ *
+ * @return Pointer to cache or NULL if none registered
+ */
+struct nl_cache *nl_cache_mngt_require_safe(const char *name)
+{
+ struct nl_cache *cache;
+
+ if ((cache = nl_cache_mngt_require(name)))
+ nl_cache_get(cache);
- return ops->co_major_cache;
+ return cache;
}
/** @} */
diff --git a/lib/cli/cls/basic.c b/lib/cli/cls/basic.c
new file mode 100644
index 00000000..1939988a
--- /dev/null
+++ b/lib/cli/cls/basic.c
@@ -0,0 +1,93 @@
+/*
+ * lib/cli/cls/basic.c basic classifier module for CLI lib
+ *
+ * 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) 2010-2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/cli/cls.h>
+#include <netlink/route/cls/basic.h>
+
+static void print_usage(void)
+{
+ printf(
+"Usage: nl-cls-add [...] basic [OPTIONS]...\n"
+"\n"
+"OPTIONS\n"
+" -h, --help Show this help text.\n"
+" -t, --target=ID Target class to send matching packets to\n"
+" -e, --ematch=EXPR Ematch expression\n"
+"\n"
+"EXAMPLE"
+" # Create a \"catch-all\" classifier, attached to \"q_root\", classyfing\n"
+" # all not yet classified packets to class \"c_default\"\n"
+" nl-cls-add --dev=eth0 --parent=q_root basic --target=c_default\n");
+}
+
+static void parse_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+ struct rtnl_cls *cls = (struct rtnl_cls *) tc;
+ struct rtnl_ematch_tree *tree;
+ uint32_t target;
+ int err;
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_TARGET = 257,
+ ARG_DEFAULT = 258,
+ };
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { "target", 1, 0, 't' },
+ { "ematch", 1, 0, 'e' },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "ht:e:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_usage();
+ exit(0);
+
+ case 't':
+ if ((err = rtnl_tc_str2handle(optarg, &target)) < 0)
+ nl_cli_fatal(err, "Unable to parse target \"%s\":",
+ optarg, nl_geterror(err));
+
+ rtnl_basic_set_target(cls, target);
+ break;
+
+ case 'e':
+ tree = nl_cli_cls_parse_ematch(cls, optarg);
+ rtnl_basic_set_ematch(cls, tree);
+ break;
+ }
+ }
+}
+
+static struct nl_cli_tc_module basic_module =
+{
+ .tm_name = "basic",
+ .tm_type = RTNL_TC_TYPE_CLS,
+ .tm_parse_argv = parse_argv,
+};
+
+static void __init basic_init(void)
+{
+ nl_cli_tc_register(&basic_module);
+}
+
+static void __exit basic_exit(void)
+{
+ nl_cli_tc_unregister(&basic_module);
+}
diff --git a/lib/cli/cls/cgroup.c b/lib/cli/cls/cgroup.c
new file mode 100644
index 00000000..fae62085
--- /dev/null
+++ b/lib/cli/cls/cgroup.c
@@ -0,0 +1,75 @@
+/*
+ * lib/cli/cls/cgroup.c cgroup classifier module for CLI lib
+ *
+ * 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) 2010-2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/cli/cls.h>
+#include <netlink/route/cls/cgroup.h>
+
+static void print_usage(void)
+{
+ printf(
+"Usage: nl-cls-add [...] cgroup [OPTIONS]...\n"
+"\n"
+"OPTIONS\n"
+" -h, --help Show this help text.\n"
+" -e, --ematch=EXPR Ematch expression\n"
+"\n"
+"EXAMPLE"
+" nl-cls-add --dev=eth0 --parent=q_root cgroup\n");
+}
+
+static void parse_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+ struct rtnl_cls *cls = (struct rtnl_cls *) tc;
+ struct rtnl_ematch_tree *tree;
+
+ for (;;) {
+ int c, optidx = 0;
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { "ematch", 1, 0, 'e' },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "he:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_usage();
+ exit(0);
+
+ case 'e':
+ tree = nl_cli_cls_parse_ematch(cls, optarg);
+ rtnl_cgroup_set_ematch(cls, tree);
+ break;
+ }
+ }
+}
+
+static struct nl_cli_tc_module cgroup_module =
+{
+ .tm_name = "cgroup",
+ .tm_type = RTNL_TC_TYPE_CLS,
+ .tm_parse_argv = parse_argv,
+};
+
+static void __init cgroup_init(void)
+{
+ nl_cli_tc_register(&cgroup_module);
+}
+
+static void __exit cgroup_exit(void)
+{
+ nl_cli_tc_unregister(&cgroup_module);
+}
diff --git a/lib/cli/qdisc/bfifo.c b/lib/cli/qdisc/bfifo.c
new file mode 100644
index 00000000..1ee47773
--- /dev/null
+++ b/lib/cli/qdisc/bfifo.c
@@ -0,0 +1,83 @@
+/*
+ * src/lib/bfifo.c bfifo module for CLI lib
+ *
+ * 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) 2010-2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/route/qdisc/fifo.h>
+
+static void print_usage(void)
+{
+ printf(
+"Usage: nl-qdisc-add [...] bfifo [OPTIONS]...\n"
+"\n"
+"OPTIONS\n"
+" --help Show this help text.\n"
+" --limit=LIMIT Maximum queue length in number of bytes.\n"
+"\n"
+"EXAMPLE"
+" # Attach bfifo with a 4KB bytes limit to eth1\n"
+" nl-qdisc-add --dev=eth1 --parent=root bfifo --limit=4096\n");
+}
+
+static void bfifo_parse_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+ struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
+ int limit;
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_LIMIT = 257,
+ };
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { "limit", 1, 0, ARG_LIMIT },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "h", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_usage();
+ return;
+
+ case ARG_LIMIT:
+ limit = nl_size2int(optarg);
+ if (limit < 0) {
+ nl_cli_fatal(limit, "Unable to parse bfifo limit "
+ "\"%s\": Invalid format.", optarg);
+ }
+
+ rtnl_qdisc_fifo_set_limit(qdisc, limit);
+ break;
+ }
+ }
+}
+
+static struct nl_cli_tc_module bfifo_module =
+{
+ .tm_name = "bfifo",
+ .tm_type = RTNL_TC_TYPE_QDISC,
+ .tm_parse_argv = bfifo_parse_argv,
+};
+
+static void __init bfifo_init(void)
+{
+ nl_cli_tc_register(&bfifo_module);
+}
+
+static void __exit bfifo_exit(void)
+{
+ nl_cli_tc_unregister(&bfifo_module);
+}
diff --git a/lib/cli/qdisc/blackhole.c b/lib/cli/qdisc/blackhole.c
new file mode 100644
index 00000000..af9dc6d8
--- /dev/null
+++ b/lib/cli/qdisc/blackhole.c
@@ -0,0 +1,64 @@
+/*
+ * src/lib/blackhole.c Blackhole module for CLI lib
+ *
+ * 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) 2010-2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+
+static void print_usage(void)
+{
+ printf(
+"Usage: nl-qdisc-add [...] blackhole [OPTIONS]...\n"
+"\n"
+"OPTIONS\n"
+" --help Show this help text.\n"
+"\n"
+"EXAMPLE"
+" # Drop all outgoing packets on eth1\n"
+" nl-qdisc-add --dev=eth1 --parent=root blackhole\n");
+}
+
+static void blackhole_parse_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+ for (;;) {
+ int c, optidx = 0;
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "h", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_usage();
+ return;
+ }
+ }
+}
+
+static struct nl_cli_tc_module blackhole_module =
+{
+ .tm_name = "blackhole",
+ .tm_type = RTNL_TC_TYPE_QDISC,
+ .tm_parse_argv = blackhole_parse_argv,
+};
+
+static void __init blackhole_init(void)
+{
+ nl_cli_tc_register(&blackhole_module);
+}
+
+static void __exit blackhole_exit(void)
+{
+ nl_cli_tc_unregister(&blackhole_module);
+}
diff --git a/lib/cli/qdisc/fq_codel.c b/lib/cli/qdisc/fq_codel.c
new file mode 100644
index 00000000..1602bcba
--- /dev/null
+++ b/lib/cli/qdisc/fq_codel.c
@@ -0,0 +1,112 @@
+/*
+ * lib/cli/qdisc/fq_codel.c fq_codel module for CLI lib
+ *
+ * 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) 2013 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/route/qdisc/fq_codel.h>
+
+static void print_usage(void)
+{
+ printf(
+"Usage: nl-qdisc-add [...] fq_codel [OPTIONS]...\n"
+"\n"
+"OPTIONS\n"
+" --help Show this help text.\n"
+" --limit=LIMIT Maximum queue length in number of bytes.\n"
+" --quantum=SIZE Amount of bytes to serve at once.\n"
+" --flows=N Number of flows.\n"
+" --interval=N The interval in usec.\n"
+" --target=N The minimum delay in usec.\n"
+"\n"
+"EXAMPLE"
+" # Attach fq_codel with a 4096 packets limit to eth1\n"
+" nl-qdisc-add --dev=eth1 --parent=root fq_codel --limit=4096\n");
+}
+
+static void fq_codel_parse_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+ struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
+ int limit, flows;
+ uint32_t quantum, target, interval;
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_LIMIT = 257,
+ ARG_QUANTUM = 258,
+ ARG_FLOWS,
+ ARG_INTERVAL,
+ ARG_TARGET,
+ };
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { "limit", 1, 0, ARG_LIMIT },
+ { "quantum", 1, 0, ARG_QUANTUM },
+ { "flows", 1, 0, ARG_FLOWS},
+ { "interval", 1, 0, ARG_INTERVAL},
+ { "target", 1, 0, ARG_TARGET},
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "h", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_usage();
+ return;
+
+ case ARG_LIMIT:
+ limit = nl_cli_parse_u32(optarg);
+ rtnl_qdisc_fq_codel_set_limit(qdisc, limit);
+ break;
+
+ case ARG_QUANTUM:
+ quantum = nl_cli_parse_u32(optarg);
+ rtnl_qdisc_fq_codel_set_quantum(qdisc, quantum);
+ break;
+
+ case ARG_FLOWS:
+ flows = nl_cli_parse_u32(optarg);
+ rtnl_qdisc_fq_codel_set_flows(qdisc, flows);
+ break;
+
+ case ARG_INTERVAL:
+ interval = nl_cli_parse_u32(optarg);
+ rtnl_qdisc_fq_codel_set_interval(qdisc, interval);
+ break;
+
+ case ARG_TARGET:
+ target = nl_cli_parse_u32(optarg);
+ rtnl_qdisc_fq_codel_set_target(qdisc, target);
+ break;
+
+ }
+ }
+}
+
+static struct nl_cli_tc_module fq_codel_module =
+{
+ .tm_name = "fq_codel",
+ .tm_type = RTNL_TC_TYPE_QDISC,
+ .tm_parse_argv = fq_codel_parse_argv,
+};
+
+static void __init fq_codel_init(void)
+{
+ nl_cli_tc_register(&fq_codel_module);
+}
+
+static void __exit fq_codel_exit(void)
+{
+ nl_cli_tc_unregister(&fq_codel_module);
+}
diff --git a/lib/cli/qdisc/htb.c b/lib/cli/qdisc/htb.c
new file mode 100644
index 00000000..1751595e
--- /dev/null
+++ b/lib/cli/qdisc/htb.c
@@ -0,0 +1,203 @@
+/*
+ * src/lib/htb.c HTB module for CLI lib
+ *
+ * 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) 2010-2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/route/qdisc/htb.h>
+
+static void print_qdisc_usage(void)
+{
+ printf(
+"Usage: nl-qdisc-add [...] htb [OPTIONS]...\n"
+"\n"
+"OPTIONS\n"
+" --help Show this help text.\n"
+" --r2q=DIV Rate to quantum divisor (default: 10)\n"
+" --default=ID Default class for unclassified traffic.\n"
+"\n"
+"EXAMPLE"
+" # Create htb root qdisc 1: and direct unclassified traffic to class 1:10\n"
+" nl-qdisc-add --dev=eth1 --parent=root --handle=1: htb --default=10\n");
+}
+
+static void htb_parse_qdisc_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+ struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_R2Q = 257,
+ ARG_DEFAULT = 258,
+ };
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { "r2q", 1, 0, ARG_R2Q },
+ { "default", 1, 0, ARG_DEFAULT },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "hv", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_qdisc_usage();
+ return;
+
+ case ARG_R2Q:
+ rtnl_htb_set_rate2quantum(qdisc, nl_cli_parse_u32(optarg));
+ break;
+
+ case ARG_DEFAULT:
+ rtnl_htb_set_defcls(qdisc, nl_cli_parse_u32(optarg));
+ break;
+ }
+ }
+}
+
+static void print_class_usage(void)
+{
+ printf(
+"Usage: nl-class-add [...] htb [OPTIONS]...\n"
+"\n"
+"OPTIONS\n"
+" --help Show this help text.\n"
+" --rate=RATE Rate limit.\n"
+" --ceil=RATE Rate limit while borrowing (default: equal to --rate).\n"
+" --prio=PRIO Priority, lower is served first (default: 0).\n"
+" --quantum=SIZE Amount of bytes to serve at once (default: rate/r2q).\n"
+" --burst=SIZE Max charge size of rate burst buffer (default: auto).\n"
+" --cburst=SIZE Max charge size of ceil rate burst buffer (default: auto)\n"
+"\n"
+"EXAMPLE"
+" # Attach class 1:1 to htb qdisc 1: and rate limit it to 20mbit\n"
+" nl-class-add --dev=eth1 --parent=1: --classid=1:1 htb --rate=20mbit\n");
+}
+
+static void htb_parse_class_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+ struct rtnl_class *class = (struct rtnl_class *) tc;
+ long rate;
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_RATE = 257,
+ ARG_QUANTUM = 258,
+ ARG_CEIL,
+ ARG_PRIO,
+ ARG_BURST,
+ ARG_CBURST,
+ };
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { "rate", 1, 0, ARG_RATE },
+ { "quantum", 1, 0, ARG_QUANTUM },
+ { "ceil", 1, 0, ARG_CEIL },
+ { "prio", 1, 0, ARG_PRIO },
+ { "burst", 1, 0, ARG_BURST },
+ { "cburst", 1, 0, ARG_CBURST },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "h", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_class_usage();
+ return;
+
+ case ARG_RATE:
+ rate = nl_size2int(optarg);
+ if (rate < 0) {
+ nl_cli_fatal(rate, "Unable to parse htb rate "
+ "\"%s\": Invalid format.", optarg);
+ }
+
+ rtnl_htb_set_rate(class, rate);
+ break;
+
+ case ARG_CEIL:
+ rate = nl_size2int(optarg);
+ if (rate < 0) {
+ nl_cli_fatal(rate, "Unable to parse htb ceil rate "
+ "\"%s\": Invalid format.", optarg);
+ }
+
+ rtnl_htb_set_ceil(class, rate);
+ break;
+
+ case ARG_PRIO:
+ rtnl_htb_set_prio(class, nl_cli_parse_u32(optarg));
+ break;
+
+ case ARG_QUANTUM:
+ rate = nl_size2int(optarg);
+ if (rate < 0) {
+ nl_cli_fatal(rate, "Unable to parse quantum "
+ "\"%s\": Invalid format.", optarg);
+ }
+
+ rtnl_htb_set_quantum(class, rate);
+ break;
+
+ case ARG_BURST:
+ rate = nl_size2int(optarg);
+ if (rate < 0) {
+ nl_cli_fatal(rate, "Unable to parse burst "
+ "\"%s\": Invalid format.", optarg);
+ }
+
+ rtnl_htb_set_rbuffer(class, rate);
+ break;
+
+ case ARG_CBURST:
+ rate = nl_size2int(optarg);
+ if (rate < 0) {
+ nl_cli_fatal(rate, "Unable to parse cburst "
+ "\"%s\": Invalid format.", optarg);
+ }
+
+ rtnl_htb_set_cbuffer(class, rate);
+ break;
+ }
+ }
+}
+
+static struct nl_cli_tc_module htb_qdisc_module =
+{
+ .tm_name = "htb",
+ .tm_type = RTNL_TC_TYPE_QDISC,
+ .tm_parse_argv = htb_parse_qdisc_argv,
+};
+
+static struct nl_cli_tc_module htb_class_module =
+{
+ .tm_name = "htb",
+ .tm_type = RTNL_TC_TYPE_CLASS,
+ .tm_parse_argv = htb_parse_class_argv,
+};
+
+static void __init htb_init(void)
+{
+ nl_cli_tc_register(&htb_qdisc_module);
+ nl_cli_tc_register(&htb_class_module);
+}
+
+static void __exit htb_exit(void)
+{
+ nl_cli_tc_unregister(&htb_class_module);
+ nl_cli_tc_unregister(&htb_qdisc_module);
+}
diff --git a/lib/cli/qdisc/ingress.c b/lib/cli/qdisc/ingress.c
new file mode 100644
index 00000000..8892a64c
--- /dev/null
+++ b/lib/cli/qdisc/ingress.c
@@ -0,0 +1,65 @@
+
+/*
+ * src/lib/ingress.c ingress module for CLI lib
+ *
+ * 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) 2013 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+
+static void print_usage(void)
+{
+ printf(
+"Usage: nl-qdisc-add [...] ingress\n"
+"\n"
+"OPTIONS\n"
+" --help Show this help text.\n"
+"\n"
+"EXAMPLE"
+" # Attach ingress to eth1\n"
+" nl-qdisc-add --dev=eth1 --parent=root ingress\n");
+}
+
+static void ingress_parse_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+ for (;;) {
+ int c, optidx = 0;
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "h", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_usage();
+ return;
+ }
+ }
+}
+
+static struct nl_cli_tc_module ingress_module =
+{
+ .tm_name = "ingress",
+ .tm_type = RTNL_TC_TYPE_QDISC,
+ .tm_parse_argv = ingress_parse_argv,
+};
+
+static void __init ingress_init(void)
+{
+ nl_cli_tc_register(&ingress_module);
+}
+
+static void __exit ingress_exit(void)
+{
+ nl_cli_tc_unregister(&ingress_module);
+}
diff --git a/lib/cli/qdisc/pfifo.c b/lib/cli/qdisc/pfifo.c
new file mode 100644
index 00000000..02c4d229
--- /dev/null
+++ b/lib/cli/qdisc/pfifo.c
@@ -0,0 +1,77 @@
+
+/*
+ * src/lib/pfifo.c pfifo module for CLI lib
+ *
+ * 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) 2010-2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/route/qdisc/fifo.h>
+
+static void print_usage(void)
+{
+ printf(
+"Usage: nl-qdisc-add [...] pfifo [OPTIONS]...\n"
+"\n"
+"OPTIONS\n"
+" --help Show this help text.\n"
+" --limit=LIMIT Maximum queue length in number of packets.\n"
+"\n"
+"EXAMPLE"
+" # Attach pfifo with a 32 packet limit to eth1\n"
+" nl-qdisc-add --dev=eth1 --parent=root pfifo --limit=32\n");
+}
+
+static void pfifo_parse_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+ struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_LIMIT = 257,
+ };
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { "limit", 1, 0, ARG_LIMIT },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "h", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_usage();
+ return;
+
+ case ARG_LIMIT:
+ rtnl_qdisc_fifo_set_limit(qdisc, nl_cli_parse_u32(optarg));
+ break;
+ }
+ }
+}
+
+static struct nl_cli_tc_module pfifo_module =
+{
+ .tm_name = "pfifo",
+ .tm_type = RTNL_TC_TYPE_QDISC,
+ .tm_parse_argv = pfifo_parse_argv,
+};
+
+static void __init pfifo_init(void)
+{
+ nl_cli_tc_register(&pfifo_module);
+}
+
+static void __exit pfifo_exit(void)
+{
+ nl_cli_tc_unregister(&pfifo_module);
+}
diff --git a/lib/cli/qdisc/plug.c b/lib/cli/qdisc/plug.c
new file mode 100644
index 00000000..2b8d5d65
--- /dev/null
+++ b/lib/cli/qdisc/plug.c
@@ -0,0 +1,113 @@
+
+/*
+ * src/lib/cli/qdisc/plug.c plug module for CLI lib
+ *
+ * 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) 2012 Shriram Rajagopalan <rshriram@cs.ubc.ca>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/route/qdisc/plug.h>
+
+static void print_usage(void)
+{
+ printf(
+"Usage: nl-qdisc-add [...] plug [OPTIONS]...\n"
+"\n"
+"OPTIONS\n"
+" --help Show this help text.\n"
+" --limit Maximum queue length in bytes.\n"
+" --buffer create a new buffer(plug) and queue incoming traffic into it.\n"
+" --release-one release traffic from previous buffer.\n"
+" --release-indefinite stop buffering and release all (buffered and new) packets.\n"
+"\n"
+"EXAMPLE"
+" # Attach plug qdisc with 32KB queue size to ifb0\n"
+" nl-qdisc-add --dev=ifb0 --parent=root plug --limit=32768\n"
+" # Plug network traffic arriving at ifb0\n"
+" nl-qdisc-add --dev=ifb0 --parent=root --update plug --buffer\n"
+" # Unplug traffic arriving at ifb0 indefinitely\n"
+" nl-qdisc-add --dev=ifb0 --parent=root --update plug --release-indefinite\n\n"
+" # If operating in output buffering mode:\n"
+" # at time t=t0, create a new output buffer b0 to hold network output\n"
+" nl-qdisc-add --dev=ifb0 --parent=root --update plug --buffer\n\n"
+" # at time t=t1, take a checkpoint c0, create a new output buffer b1\n"
+" nl-qdisc-add --dev=ifb0 --parent=root --update plug --buffer\n"
+" # at time t=t1+r, after c0 is committed, release b0\n"
+" nl-qdisc-add --dev=ifb0 --parent=root --update plug --release-one\n\n"
+" # at time t=t2, take a checkpoint c1, create a new output buffer b2\n"
+" nl-qdisc-add --dev=ifb0 --parent=root --update plug --buffer\n"
+" # at time t=t2+r, after c1 is committed, release b1\n"
+" nl-qdisc-add --dev=ifb0 --parent=root --update plug --release-one\n");
+}
+
+static void plug_parse_argv(struct rtnl_tc *tc, int argc, char **argv)
+{
+ struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_LIMIT = 257,
+ ARG_BUFFER = 258,
+ ARG_RELEASE_ONE = 259,
+ ARG_RELEASE_INDEFINITE = 260,
+ };
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { "limit", 1, 0, ARG_LIMIT },
+ { "buffer", 0, 0, ARG_BUFFER },
+ { "release-one", 0, 0, ARG_RELEASE_ONE },
+ { "release-indefinite", 0, 0, ARG_RELEASE_INDEFINITE },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "h", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h':
+ print_usage();
+ return;
+
+ case ARG_LIMIT:
+ rtnl_qdisc_plug_set_limit(qdisc, nl_cli_parse_u32(optarg));
+ break;
+
+ case ARG_BUFFER:
+ rtnl_qdisc_plug_buffer(qdisc);
+ break;
+
+ case ARG_RELEASE_ONE:
+ rtnl_qdisc_plug_release_one(qdisc);
+ break;
+
+ case ARG_RELEASE_INDEFINITE:
+ rtnl_qdisc_plug_release_indefinite(qdisc);
+ break;
+ }
+ }
+}
+
+static struct nl_cli_tc_module plug_module =
+{
+ .tm_name = "plug",
+ .tm_type = RTNL_TC_TYPE_QDISC,
+ .tm_parse_argv = plug_parse_argv,
+};
+
+static void __init plug_init(void)
+{
+ nl_cli_tc_register(&plug_module);
+}
+
+static void __exit plug_exit(void)
+{
+ nl_cli_tc_unregister(&plug_module);
+}
diff --git a/lib/data.c b/lib/data.c
index 03cd9fec..1a3a3fbd 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -6,16 +6,28 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup core
+ * @ingroup core_types
* @defgroup data Abstract Data
+ *
+ * Abstract data type representing a binary data blob.
+ *
+ * Related sections in the development guide:
+ * - @core_doc{_abstract_data, Abstract Data}
+ *
* @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/data.h>
+ * ~~~~
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <linux/socket.h>
@@ -98,9 +110,6 @@ struct nl_data *nl_data_clone(struct nl_data *src)
*/
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)
diff --git a/lib/doc.c b/lib/doc.c
deleted file mode 100644
index 7e68bb30..00000000
--- a/lib/doc.c
+++ /dev/null
@@ -1,484 +0,0 @@
-/*
- * 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-2008 Thomas Graf <tgraf@suug.ch>
- */
-
-/**
- * @mainpage
- *
- * @section intro Introduction
- *
- * libnl is a set of libraries to deal with the netlink protocol and some
- * of the high level protocols implemented on top of it. Its goal is to
- * simplify netlink protocol usage and to create an abstraction layer using
- * object based interfaces for various netlink based subsystems.The library
- * was developed and tested on the 2.6.x kernel releases but it may work with
- * older kernel series.
- *
- * @section toc Table of Contents
- *
- * - \subpage core_doc
- * - \subpage route_doc
- * - \subpage genl_doc
- * - \subpage nf_doc
- *
- * @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 *<object name>_alloc_cache(struct nl_sock *sk);
- * @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 <object name>_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 *<object name>_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 <object name>_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 *<object name>_<type>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 <object name>_str2<type>(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.
- *
- * @page core_doc Core Library (-lnl)
- *
- * @section core_intro Introduction
- *
- * The core library contains the fundamentals required to communicate over
- * netlink sockets. It deals with connecting and unconnecting of sockets,
- * sending and receiving of data, provides a customizeable receiving state
- * machine, and provides a abstract data type framework which eases the
- * implementation of object based netlink protocols where objects are added,
- * removed, or modified with the help of netlink messages.
- *
- * @section core_toc Table of Contents
- *
- * - \ref proto_fund
- * - \ref sk_doc
- * - \ref rxtx_doc
- * - \ref cb_doc
- *
- * @section proto_fund Netlink Protocol Fundamentals
- *
- * The netlink protocol is a socket based IPC mechanism used for communication
- * between userspace processes and the kernel. The netlink protocol uses the
- * \c AF_NETLINK address family and defines a protocol type for each subsystem
- * protocol (e.g. NETLINK_ROUTE, NETLINK_NETFILTER, etc). Its addressing
- * schema is based on a 32 bit port number, formerly referred to as PID, which
- * uniquely identifies each peer.
- *
- * The netlink protocol is based on messages each limited to the size of a
- * memory page and consists of the netlink message header (struct nlmsghdr)
- * plus the payload attached to it. The payload can consist of arbitary data
- * but often contains a fixed sized family specifc header followed by a
- * stream of \ref attr_doc. The use of attributes dramatically increases
- * the flexibility of the protocol and allows for the protocol to be
- * extended while maintaining backwards compatibility.
- *
- * The netlink message header (struct nlmsghdr):
- * @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 |
- * +-------------------------------------------------------------+
- * | Port (Address) |
- * +-------------------------------------------------------------+
- * @endcode
- *
- * Netlink differs between requests, notifications, and replies. Requests
- * are messages which have the \c NLM_F_REQUEST flag set and are meant to
- * request an action from the receiver. A request is typically sent from
- * a userspace process to the kernel. Every request should be assigned a
- * sequence number which should be incremented for each request sent on the
- * sending side. Depending on the nature of the request, the receiver may
- * reply to the request with regular netlink messages which should contain
- * the same sequence number as the request it relates to. Notifications are
- * of informal nature and don't expect a reply, therefore the sequence number
- * is typically set to 0. It should be noted that unlike in protocols such as
- * TCP there is no strict enforcment of the sequence number. The sole purpose
- * of sequence numbers is to assist a sender in relating replies to the
- * corresponding requests.
- *
- * @msc
- * A,B;
- * A=>B [label="GET (seq=1, NLM_F_REQUEST)"];
- * A<=B [label="PUT (seq=1)"];
- * ...;
- * A<=B [label="NOTIFY (seq=0)"];
- * @endmsc
- *
- * If the size of a reply exceeds the size of a memory page and thus exceeds
- * the maximum message size, the reply can be split into a series of multipart
- * messages. A multipart message has the \c flag NLM_F_MULTI set and the
- * receiver is expected to continue parsing the reply until the special
- * message type \c NLMSG_DONE is received.
- *
- * @msc
- * A,B;
- * A=>B [label="GET (seq=1, NLM_F_REQUEST)"];
- * A<=B [label="PUT (seq=1, NLM_F_MULTI)"];
- * ...;
- * A<=B [label="PUT (seq=1, NLM_F_MULTI)"];
- * A<=B [label="NLMSG_DONE (seq=1)"];
- * @endmsc
- *
- * Errors can be reported using the standard message type \c NLMSG_ERROR which
- * can carry an error code and the netlink mesage header of the request.
- * Error messages should set their sequence number to the sequence number
- * of the message which caused the error.
- *
- * @msc
- * A,B;
- * A=>B [label="GET (seq=1, NLM_F_REQUEST)"];
- * A<=B [label="NLMSG_ERROR code=EINVAL (seq=1)"];
- * @endmsc
- *
- * The \c NLMSG_ERROR message type is also used to send acknowledge messages.
- * An acknowledge message can be requested by setting the \c NLM_F_ACK flag
- * message except that the error code is set to 0.
- *
- * @msc
- * A,B;
- * A=>B [label="GET (seq=1, NLM_F_REQUEST | NLM_F_ACK)"];
- * A<=B [label="ACK (seq=1)"];
- * @endmsc
- *
- * @section sk_doc Dealing with Netlink Sockets
- *
- * In order to use the netlink protocol, a netlink socket is required. Each
- * socket defines a completely independent context for sending and receiving
- * of messages. The netlink socket and all its related attributes are
- * represented by struct nl_sock.
- *
- * @code
- * nl_socket_alloc() Allocate new socket structure.
- * nl_socket_free(s) Free socket structure.
- * @endcode
- *
- * @subsection local_port Local Port
- * The local port number uniquely identifies the socket and is used to
- * address it. A unique local port is generated automatically when the socket
- * is allocated. It will consist of the Process ID (22 bits) and a random
- * number (10 bits) to allow up to 1024 sockets per process.
- *
- * @code
- * nl_socket_get_local_port(sk) Return the peer's port number.
- * nl_socket_set_local_port(sk, port) Set the peer's port number.
- * @endcode
- *
- * @subsection peer_port Peer Port
- * A peer port can be assigned to the socket which will result in all unicast
- * messages sent over the socket to be addresses to the corresponding peer. If
- * no peer is specified, the kernel will try to automatically bind the socket
- * to a kernel side socket of the same netlink protocol family. It is common
- * practice not to bind the socket to a peer port as typically only one kernel
- * side socket exists per netlink protocol family.
- *
- * @code
- * nl_socket_get_peer_port(sk) Return the local port number.
- * nl_socket_set_peer_port(sk, port) Set the local port number.
- * @endcode
- *
- * @subsection sock_fd File Descriptor
- * The file descriptor of the socket(2).
- *
- * @code
- * nl_socket_get_fd(sk) Return file descriptor.
- * nl_socket_set_buffer_size(sk, rx, tx) Set buffer size of socket.
- * nl_socket_set_nonblocking(sk) Set socket to non-blocking state.
- * @endcode
- *
- * @subsection group_sub Group Subscriptions
- * Each socket can subscribe to multicast groups of the netlink protocol
- * family it is bound to. The socket will then receive a copy of each
- * message sent to any of the groups. Multicast groups are commonly used
- * to implement event notifications. Prior to kernel 2.6.14 the group
- * subscription was performed using a bitmask which limited the number of
- * groups per protocol family to 32. This outdated interface can still be
- * accessed via the function nl_join_groups even though it is not recommended
- * for new code. Starting with 2.6.14 a new method was introduced which
- * supports subscribing to an almost unlimited number of multicast groups.
- *
- * @code
- * nl_socket_add_membership(sk, group) Become a member of a multicast group.
- * nl_socket_drop_membership(sk, group) Drop multicast group membership.
- * nl_join_groups(sk, groupmask) Join a multicast group (obsolete).
- * @endcode
- *
- * @subsection seq_num Sequence Numbers
- * The socket keeps track of the sequence numbers used. The library will
- * automatically verify the sequence number of messages received unless
- * the check was disabled using the function nl_socket_disable_seq_check().
- * When a message is sent using nl_send_auto_complete(), the sequence number
- * is automatically filled in, and replies will be verified correctly.
- *
- * @code
- * nl_socket_disable_seq_check(sk) Disable checking of sequece numbers.
- * nl_socket_use_seq(sk) Use sequence number and bump to next.
- * @endcode
- *
- * @subsection sock_cb Callback Configuration
- * Every socket is associated a callback configuration which enables the
- * applications to hook into various internal functions and control the
- * receiving and sendings semantics. For more information, see section
- * \ref cb_doc.
- *
- * @code
- * nl_socket_alloc_cb(cb) Allocate socket based on callback set.
- * nl_socket_get_cb(sk) Return callback configuration.
- * nl_socket_set_cb(sk, cb) Replace callback configuration.
- * nl_socket_modify_cb(sk, ...) Modify a specific callback function.
- * @endcode
- *
- * @subsection sk_other Other Functions
- * @code
- * nl_socket_enable_auto_ack(sock) Enable automatic request of ACK.
- * nl_socket_disable_auto_ack(sock) Disable automatic request of ACK.
- * nl_socket_enable_msg_peek(sock) Enable message peeking.
- * nl_socket_disable_msg_peek(sock) Disable message peeking.
- * nl_socket_set_passcred(sk, state) Enable/disable credential passing.
- * nl_socket_recv_pktinfo(sk, state) Enable/disable packet information.
- * @endcode
- *
- * @section rxtx_doc Sending and Receiving of Data
- *
- * @subsection recv_semantisc Receiving Semantics
- * @code
- * nl_recvmsgs_default(set)
- * | cb = nl_socket_get_cb(sk)
- * v
- * nl_recvmsgs(sk, 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 NO-OP Message End of Multipart Error
- * | | | | | | |
- * | | 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] |<-----------
- * | +----------------------------------|
- * | |
- * | [is 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
- *
- * @subsection send_semantics Sending Semantisc
- *
- * @code
- * nl_send_auto_complete(sk, msg)
- * | [Automatically completes netlink message header]
- * | [(local port, sequence number) ]
- * |
- * | [Application provies nl_send() replacement]
- * |- - - - - - - - - - - - - - - - - - - - v
- * v cb->cb_send_ow()
- * nl_send(sk, msg)
- * | [If available, add peer port and credentials]
- * v
- * nl_sendmsg(sk, msg, msghdr)
- * |- - - - - - - - - - - - - - - - - - - - v
- * | NL_CB_MSG_OUT()
- * |<- - - - - - - - - - - - - - - - - - - -+
- * v
- * sendmsg()
- * @endcode
- *
- * @section cb_doc Callback Configurations
- * Callbacks and overwriting capabilities are provided to control various
- * semantics of the library. All callback functions are packed together in
- * struct nl_cb which is attached to a netlink socket or passed on to
- * the respective functions directly.
- *
- * @subsection cb_ret_doc Callback Return Values
- * Callback functions can control the flow of the calling layer by returning
- * 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
- *
- * @page route_doc Routing Family
- *
- * @page genl_doc Generic Netlink Family
- *
- * @page nf_doc Netfilter Subsystem
- */
diff --git a/lib/error.c b/lib/error.c
index 9a9fac7f..f30b9a59 100644
--- a/lib/error.c
+++ b/lib/error.c
@@ -9,7 +9,7 @@
* Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
static const char *errmsg[NLE_MAX+1] = {
@@ -43,6 +43,10 @@ static const char *errmsg[NLE_MAX+1] = {
[NLE_NOACCESS] = "No Access",
[NLE_PERM] = "Operation not permitted",
[NLE_PKTLOC_FILE] = "Unable to open packet location file",
+[NLE_PARSE_ERR] = "Unable to parse object",
+[NLE_NODEV] = "No such device",
+[NLE_IMMUTABLE] = "Immutable attribute",
+[NLE_DUMP_INTR] = "Dump inconsistency detected, interrupted",
};
/**
@@ -86,6 +90,7 @@ int nl_syserr2nlerr(int error)
case EADDRINUSE: return NLE_EXIST;
case EEXIST: return NLE_EXIST;
case EADDRNOTAVAIL: return NLE_NOADDR;
+ case ESRCH: /* fall through */
case ENOENT: return NLE_OBJ_NOTFOUND;
case EINTR: return NLE_INTR;
case EAGAIN: return NLE_AGAIN;
@@ -102,6 +107,7 @@ int nl_syserr2nlerr(int error)
case EPERM: return NLE_PERM;
case EBUSY: return NLE_BUSY;
case ERANGE: return NLE_RANGE;
+ case ENODEV: return NLE_NODEV;
default: return NLE_FAILURE;
}
}
diff --git a/lib/fib_lookup/lookup.c b/lib/fib_lookup/lookup.c
index ce9c027e..3d073172 100644
--- a/lib/fib_lookup/lookup.c
+++ b/lib/fib_lookup/lookup.c
@@ -6,16 +6,17 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
+ * @ingroup rtnl
* @defgroup fib_lookup FIB Lookup
* @brief
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/attr.h>
#include <netlink/utils.h>
@@ -123,7 +124,7 @@ errout:
static void result_dump_line(struct nl_object *obj, struct nl_dump_params *p)
{
struct flnl_result *res = (struct flnl_result *) obj;
- char buf[128];
+ char buf[256];
nl_dump_line(p, "table %s prefixlen %u next-hop-selector %u\n",
rtnl_route_table2str(res->fr_table_id, buf, sizeof(buf)),
@@ -132,7 +133,7 @@ static void result_dump_line(struct nl_object *obj, struct nl_dump_params *p)
nl_rtntype2str(res->fr_type, buf, sizeof(buf)));
nl_dump(p, "scope %s error %s (%d)\n",
rtnl_scope2str(res->fr_scope, buf, sizeof(buf)),
- strerror(-res->fr_error), res->fr_error);
+ strerror_r(-res->fr_error, buf, sizeof(buf)), res->fr_error);
}
static void result_dump_details(struct nl_object *obj, struct nl_dump_params *p)
@@ -192,6 +193,7 @@ struct nl_cache *flnl_result_alloc_cache(void)
* Builds a netlink request message to do a lookup
* @arg req Requested match.
* @arg flags additional netlink message flags
+ * @arg result Result pointer
*
* Builds a new netlink message requesting a change of link attributes.
* The netlink message header isn't fully equipped with all relevant
@@ -201,9 +203,7 @@ struct nl_cache *flnl_result_alloc_cache(void)
* 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.
+ * @return 0 on success or a negative error code.
*/
int flnl_lookup_build_request(struct flnl_request *req, int flags,
struct nl_msg **result)
diff --git a/lib/fib_lookup/request.c b/lib/fib_lookup/request.c
index ffcf8f5d..1b021b64 100644
--- a/lib/fib_lookup/request.c
+++ b/lib/fib_lookup/request.c
@@ -16,7 +16,7 @@
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/attr.h>
#include <netlink/utils.h>
diff --git a/lib/genl/ctrl.c b/lib/genl/ctrl.c
index 13016420..ce07f1d4 100644
--- a/lib/genl/ctrl.c
+++ b/lib/genl/ctrl.c
@@ -6,18 +6,22 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup genl_mngt
- * @defgroup ctrl Controller
- * @brief
+ * @ingroup genl
+ * @defgroup genl_ctrl Controller (Resolver)
*
+ * Resolves Generic Netlink family names to numeric identifiers.
+ *
+ * The controller is a component in the kernel that resolves Generic Netlink
+ * family names to their numeric identifiers. This module provides functions
+ * to query the controller to access the resolving functionality.
* @{
*/
-#include <netlink-generic.h>
+#include <netlink-private/genl.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
@@ -29,7 +33,6 @@
#define CTRL_VERSION 0x0001
static struct nl_cache_ops genl_ctrl_ops;
-/** @endcond */
static int ctrl_request_update(struct nl_cache *c, struct nl_sock *h)
{
@@ -45,6 +48,7 @@ static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
[CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 },
[CTRL_ATTR_MAXATTR] = { .type = NLA_U32 },
[CTRL_ATTR_OPS] = { .type = NLA_NESTED },
+ [CTRL_ATTR_MCAST_GROUPS] = { .type = NLA_NESTED },
};
static struct nla_policy family_op_policy[CTRL_ATTR_OP_MAX+1] = {
@@ -52,6 +56,52 @@ static struct nla_policy family_op_policy[CTRL_ATTR_OP_MAX+1] = {
[CTRL_ATTR_OP_FLAGS] = { .type = NLA_U32 },
};
+static struct nla_policy family_grp_policy[CTRL_ATTR_MCAST_GRP_MAX+1] = {
+ [CTRL_ATTR_MCAST_GRP_NAME] = { .type = NLA_STRING },
+ [CTRL_ATTR_MCAST_GRP_ID] = { .type = NLA_U32 },
+};
+
+static int parse_mcast_grps(struct genl_family *family, struct nlattr *grp_attr)
+{
+ struct nlattr *nla;
+ int remaining, err;
+
+ if (!grp_attr)
+ BUG();
+
+ nla_for_each_nested(nla, grp_attr, remaining) {
+ struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1];
+ int id;
+ const char * name;
+
+ err = nla_parse_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, nla,
+ family_grp_policy);
+ if (err < 0)
+ goto errout;
+
+ if (tb[CTRL_ATTR_MCAST_GRP_ID] == NULL) {
+ err = -NLE_MISSING_ATTR;
+ goto errout;
+ }
+ id = nla_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]);
+
+ if (tb[CTRL_ATTR_MCAST_GRP_NAME] == NULL) {
+ err = -NLE_MISSING_ATTR;
+ goto errout;
+ }
+ name = nla_get_string(tb[CTRL_ATTR_MCAST_GRP_NAME]);
+
+ err = genl_family_add_grp(family, id, name);
+ if (err < 0)
+ goto errout;
+ }
+
+ err = 0;
+
+errout:
+ return err;
+}
+
static int ctrl_msg_parser(struct nl_cache_ops *ops, struct genl_cmd *cmd,
struct genl_info *info, void *arg)
{
@@ -126,6 +176,12 @@ static int ctrl_msg_parser(struct nl_cache_ops *ops, struct genl_cmd *cmd,
}
}
+
+ if (info->attrs[CTRL_ATTR_MCAST_GROUPS]) {
+ err = parse_mcast_grps(family, info->attrs[CTRL_ATTR_MCAST_GROUPS]);
+ if (err < 0)
+ goto errout;
+ }
err = pp->pp_cb((struct nl_object *) family, pp);
errout:
@@ -134,26 +190,166 @@ errout:
}
/**
- * @name Cache Management
+ * process responses from from the query sent by genl_ctrl_probe_by_name
+ * @arg nl_msg Returned message.
+ * @arg name genl_family structure to fill out.
+ *
+ * Process returned messages, filling out the missing informatino in the
+ * genl_family structure
+ *
+ * @return Indicator to keep processing frames or not
+ *
+ */
+static int probe_response(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[CTRL_ATTR_MAX+1];
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct genl_family *ret = (struct genl_family *)arg;
+
+ if (genlmsg_parse(nlh, 0, tb, CTRL_ATTR_MAX, ctrl_policy))
+ return NL_SKIP;
+
+ if (tb[CTRL_ATTR_FAMILY_ID])
+ genl_family_set_id(ret, nla_get_u16(tb[CTRL_ATTR_FAMILY_ID]));
+
+ if (tb[CTRL_ATTR_MCAST_GROUPS])
+ if (parse_mcast_grps(ret, tb[CTRL_ATTR_MCAST_GROUPS]) < 0)
+ return NL_SKIP;
+
+ return NL_STOP;
+}
+
+/**
+ * Look up generic netlink family by family name querying the kernel directly
+ * @arg sk Socket.
+ * @arg name Family name.
+ *
+ * Directly query's the kernel for a given family name. The caller will own a
+ * reference on the returned object which needsd to be given back after usage
+ * using genl_family_put.
+ *
+ * Note: This API call differs from genl_ctrl_search_by_name in that it querys
+ * the kernel directly, alowing for module autoload to take place to resolve the
+ * family request. Using an nl_cache prevents that operation
+ *
+ * @return Generic netlink family object or NULL if no match was found.
+ */
+static struct genl_family *genl_ctrl_probe_by_name(struct nl_sock *sk,
+ const char *name)
+{
+ struct nl_msg *msg;
+ struct genl_family *ret;
+ struct nl_cb *cb, *orig;
+ int rc;
+
+ ret = genl_family_alloc();
+ if (!ret)
+ goto out;
+
+ genl_family_set_name(ret, name);
+
+ msg = nlmsg_alloc();
+ if (!msg)
+ goto out_fam_free;
+
+ if (!(orig = nl_socket_get_cb(sk)))
+ goto out_msg_free;
+
+ cb = nl_cb_clone(orig);
+ nl_cb_put(orig);
+ if (!cb)
+ goto out_msg_free;
+
+ if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, GENL_ID_CTRL,
+ 0, 0, CTRL_CMD_GETFAMILY, 1)) {
+ BUG();
+ goto out_cb_free;
+ }
+
+ if (nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, name) < 0)
+ goto out_cb_free;
+
+ rc = nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, probe_response,
+ (void *) ret);
+ if (rc < 0)
+ goto out_cb_free;
+
+ rc = nl_send_auto_complete(sk, msg);
+ if (rc < 0)
+ goto out_cb_free;
+
+ rc = nl_recvmsgs(sk, cb);
+ if (rc < 0)
+ goto out_cb_free;
+
+ /* If search was successful, request may be ACKed after data */
+ rc = wait_for_ack(sk);
+ if (rc < 0)
+ goto out_cb_free;
+
+ if (genl_family_get_id(ret) != 0) {
+ nlmsg_free(msg);
+ nl_cb_put(cb);
+ return ret;
+ }
+
+out_cb_free:
+ nl_cb_put(cb);
+out_msg_free:
+ nlmsg_free(msg);
+out_fam_free:
+ genl_family_put(ret);
+ ret = NULL;
+out:
+ return ret;
+}
+
+
+/** @endcond */
+
+/**
+ * @name Controller Cache
+ *
+ * The controller cache allows to keep a local copy of the list of all
+ * kernel side registered Generic Netlink families to quickly resolve
+ * multiple Generic Netlink family names without requiring to communicate
+ * with the kernel for each resolving iteration.
+ *
* @{
*/
-int genl_ctrl_alloc_cache(struct nl_sock *sock, struct nl_cache **result)
+/**
+ * Allocate a new controller cache
+ * @arg sk Generic Netlink socket
+ * @arg result Pointer to store resulting cache
+ *
+ * Allocates a new cache mirroring the state of the controller and stores it
+ * in \c *result. The allocated cache will contain a list of all currently
+ * registered kernel side Generic Netlink families. The cache is meant to be
+ * used to resolve family names locally.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int genl_ctrl_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
{
- return nl_cache_alloc_and_fill(&genl_ctrl_ops, sock, result);
+ return nl_cache_alloc_and_fill(&genl_ctrl_ops, sk, result);
}
/**
- * Look up generic netlink family by id in the provided cache.
- * @arg cache Generic netlink family cache.
- * @arg id Family identifier.
+ * Search controller cache for a numeric address match
+ * @arg cache Controller cache
+ * @arg id Numeric 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().
+ * Searches a previously allocated controller cache and looks for an entry
+ * that matches the specified numeric family identifier \c id. If a match
+ * is found successfully, the reference count of the matching object is
+ * increased by one before the objet is returned.
*
- * @return Generic netlink family object or NULL if no match was found.
+ * @see genl_ctrl_alloc_cache()
+ * @see genl_ctrl_search_by_name()
+ * @see 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)
{
@@ -173,24 +369,23 @@ struct genl_family *genl_ctrl_search(struct nl_cache *cache, int id)
}
/**
- * @name Resolver
- * @{
- */
-
-/**
- * Look up generic netlink family by family name in the provided cache.
- * @arg cache Generic netlink family cache.
- * @arg name Family name.
+ * Search controller cache for a family name match
+ * @arg cache Controller cache
+ * @arg name Name of Generic Netlink family
*
- * 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().
+ * Searches a previously allocated controller cache and looks for an entry
+ * that matches the specified family \c name. If a match is found successfully,
+ * the reference count of the matching object is increased by one before the
+ * objet is returned.
*
- * @return Generic netlink family object or NULL if no match was found.
+ * @see genl_ctrl_alloc_cache()
+ * @see genl_ctrl_search()
+ * @see 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)
+ const char *name)
{
struct genl_family *fam;
@@ -210,25 +405,33 @@ struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache,
/** @} */
/**
- * Resolve generic netlink family name to its identifier
- * @arg sk Netlink socket.
- * @arg name Name of generic netlink family
+ * @name Direct Resolvers
+ *
+ * These functions communicate directly with the kernel and do not require
+ * a cache to be kept up to date.
+ *
+ * @{
+ */
+
+/**
+ * Resolve Generic Netlink family name to numeric identifier
+ * @arg sk Generic Netlink socket.
+ * @arg name Name of Generic Netlink family
*
- * Resolves the generic netlink family name to its identifer and returns
- * it.
+ * Resolves the Generic Netlink family name to the corresponding numeric
+ * family identifier. This function queries the kernel directly, use
+ * genl_ctrl_search_by_name() if you need to resolve multiple names.
*
- * @return A positive identifier or a negative error code.
+ * @see genl_ctrl_search_by_name()
+ *
+ * @return The numeric family identifier or a negative error code.
*/
int genl_ctrl_resolve(struct nl_sock *sk, const char *name)
{
- struct nl_cache *cache;
struct genl_family *family;
int err;
- if ((err = genl_ctrl_alloc_cache(sk, &cache)) < 0)
- return err;
-
- family = genl_ctrl_search_by_name(cache, name);
+ family = genl_ctrl_probe_by_name(sk, name);
if (family == NULL) {
err = -NLE_OBJ_NOTFOUND;
goto errout;
@@ -237,13 +440,56 @@ int genl_ctrl_resolve(struct nl_sock *sk, const char *name)
err = genl_family_get_id(family);
genl_family_put(family);
errout:
- nl_cache_free(cache);
+ return err;
+}
+
+static int genl_ctrl_grp_by_name(const struct genl_family *family,
+ const char *grp_name)
+{
+ struct genl_family_grp *grp;
+
+ nl_list_for_each_entry(grp, &family->gf_mc_grps, list) {
+ if (!strcmp(grp->name, grp_name)) {
+ return grp->id;
+ }
+ }
+
+ return -NLE_OBJ_NOTFOUND;
+}
+
+/**
+ * Resolve Generic Netlink family group name
+ * @arg sk Generic Netlink socket
+ * @arg family_name Name of Generic Netlink family
+ * @arg grp_name Name of group to resolve
+ *
+ * Looks up the family object and resolves the group name to the numeric
+ * group identifier.
+ *
+ * @return Numeric group identifier or a negative error code.
+ */
+int genl_ctrl_resolve_grp(struct nl_sock *sk, const char *family_name,
+ const char *grp_name)
+{
+ struct genl_family *family;
+ int err;
+
+ family = genl_ctrl_probe_by_name(sk, family_name);
+ if (family == NULL) {
+ err = -NLE_OBJ_NOTFOUND;
+ goto errout;
+ }
+
+ err = genl_ctrl_grp_by_name(family, grp_name);
+ genl_family_put(family);
+errout:
return err;
}
/** @} */
+/** @cond SKIP */
static struct genl_cmd genl_cmds[] = {
{
.c_id = CTRL_CMD_NEWFAMILY,
@@ -275,9 +521,7 @@ static struct genl_ops genl_ops = {
.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",
@@ -298,5 +542,6 @@ static void __exit ctrl_exit(void)
{
genl_unregister(&genl_ctrl_ops);
}
+/** @endcond */
/** @} */
diff --git a/lib/genl/family.c b/lib/genl/family.c
index 4c6c18d3..897c809e 100644
--- a/lib/genl/family.c
+++ b/lib/genl/family.c
@@ -6,18 +6,19 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup genl
- * @defgroup genl_family Generic Netlink Family
- * @brief
+ * @ingroup genl_ctrl
+ * @defgroup genl_family Generic Netlink Family Object
+ *
+ * Object representing a kernel side registered Generic Netlink family
*
* @{
*/
-#include <netlink-generic.h>
+#include <netlink-private/genl.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/family.h>
@@ -32,19 +33,20 @@
#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);
+ nl_init_list_head(&family->gf_mc_grps);
}
static void family_free_data(struct nl_object *c)
{
struct genl_family *family = (struct genl_family *) c;
struct genl_family_op *ops, *tmp;
+ struct genl_family_grp *grp, *t_grp;
if (family == NULL)
return;
@@ -53,6 +55,12 @@ static void family_free_data(struct nl_object *c)
nl_list_del(&ops->o_list);
free(ops);
}
+
+ nl_list_for_each_entry_safe(grp, t_grp, &family->gf_mc_grps, list) {
+ nl_list_del(&grp->list);
+ free(grp);
+ }
+
}
static int family_clone(struct nl_object *_dst, struct nl_object *_src)
@@ -60,6 +68,7 @@ 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;
+ struct genl_family_grp *grp;
int err;
nl_list_for_each_entry(ops, &src->gf_ops, o_list) {
@@ -67,6 +76,13 @@ static int family_clone(struct nl_object *_dst, struct nl_object *_src)
if (err < 0)
return err;
}
+
+ nl_list_for_each_entry(grp, &src->gf_mc_grps, list) {
+ err = genl_family_add_grp(dst, grp->id, grp->name);
+ if (err < 0)
+ return err;
+ }
+
return 0;
}
@@ -79,11 +95,11 @@ static void family_dump_line(struct nl_object *obj, struct nl_dump_params *p)
family->gf_id, family->gf_name, family->gf_version);
}
-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 const 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)
@@ -93,6 +109,7 @@ static char *ops_flags2str(int flags, char *buf, size_t len)
static void family_dump_details(struct nl_object *obj, struct nl_dump_params *p)
{
+ struct genl_family_grp *grp;
struct genl_family *family = (struct genl_family *) obj;
family_dump_line(obj, p);
@@ -118,6 +135,11 @@ static void family_dump_details(struct nl_object *obj, struct nl_dump_params *p)
nl_dump(p, "\n");
}
}
+
+ nl_list_for_each_entry(grp, &family->gf_mc_grps, list) {
+ nl_dump_line(p, " grp %s (0x%02x)\n", grp->name, grp->id);
+ }
+
}
static void family_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
@@ -144,18 +166,32 @@ static int family_compare(struct nl_object *_a, struct nl_object *_b,
return diff;
}
-
+/** @endcond */
/**
- * @name Family Object
+ * @name Object Allocation
* @{
*/
+/**
+ * Allocate new Generic Netlink family object
+ *
+ * @return Newly allocated Generic Netlink family object or NULL.
+ */
struct genl_family *genl_family_alloc(void)
{
return (struct genl_family *) nl_object_alloc(&genl_family_ops);
}
+/**
+ * Release reference on Generic Netlink family object
+ * @arg family Generic Netlink family object
+ *
+ * Reduces the reference counter of a Generic Netlink family object by one.
+ * The object is freed after the last user has returned its reference.
+ *
+ * @see nl_object_put()
+ */
void genl_family_put(struct genl_family *family)
{
nl_object_put((struct nl_object *) family);
@@ -164,10 +200,16 @@ void genl_family_put(struct genl_family *family)
/** @} */
/**
- * @name Attributes
+ * @name Numeric Identifier
* @{
*/
+/**
+ * Return numeric identifier
+ * @arg family Generic Netlink family object
+ *
+ * @return Numeric identifier or 0 if not available.
+ */
unsigned int genl_family_get_id(struct genl_family *family)
{
if (family->ce_mask & FAMILY_ATTR_ID)
@@ -176,12 +218,30 @@ unsigned int genl_family_get_id(struct genl_family *family)
return GENL_ID_GENERATE;
}
+/**
+ * Set the numeric identifier
+ * @arg family Generic Netlink family object
+ * @arg id New numeric identifier
+ */
void genl_family_set_id(struct genl_family *family, unsigned int id)
{
family->gf_id = id;
family->ce_mask |= FAMILY_ATTR_ID;
}
+/** @} */
+
+/**
+ * @name Human Readable Name
+ * @{
+ */
+
+/**
+ * Return human readable name
+ * @arg family Generic Netlink family object
+ *
+ * @return Name of family or NULL if not available
+ */
char *genl_family_get_name(struct genl_family *family)
{
if (family->ce_mask & FAMILY_ATTR_NAME)
@@ -190,12 +250,28 @@ char *genl_family_get_name(struct genl_family *family)
return NULL;
}
+/**
+ * Set human readable name
+ * @arg family Generic Netlink family object
+ * @arg name New human readable name
+ */
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;
}
+/**
+ * @name Interface Version
+ * @{
+ */
+
+/**
+ * Return interface version
+ * @arg family Generic Netlink family object
+ *
+ * @return Interface version or 0 if not available.
+ */
uint8_t genl_family_get_version(struct genl_family *family)
{
if (family->ce_mask & FAMILY_ATTR_VERSION)
@@ -204,12 +280,30 @@ uint8_t genl_family_get_version(struct genl_family *family)
return 0;
}
+/**
+ * Set interface version
+ * @arg family Generic Netlink family object
+ * @arg version New interface version
+ */
void genl_family_set_version(struct genl_family *family, uint8_t version)
{
family->gf_version = version;
family->ce_mask |= FAMILY_ATTR_VERSION;
}
+/** @} */
+
+/**
+ * @name Header Size
+ * @{
+ */
+
+/**
+ * Return user header size expected by kernel component
+ * @arg family Generic Netlink family object
+ *
+ * @return Expected header length or 0 if not available.
+ */
uint32_t genl_family_get_hdrsize(struct genl_family *family)
{
if (family->ce_mask & FAMILY_ATTR_HDRSIZE)
@@ -224,6 +318,13 @@ void genl_family_set_hdrsize(struct genl_family *family, uint32_t hdrsize)
family->ce_mask |= FAMILY_ATTR_HDRSIZE;
}
+/** @} */
+
+/**
+ * @name Maximum Expected Attribute
+ * @{
+ */
+
uint32_t genl_family_get_maxattr(struct genl_family *family)
{
if (family->ce_mask & FAMILY_ATTR_MAXATTR)
@@ -238,6 +339,13 @@ void genl_family_set_maxattr(struct genl_family *family, uint32_t maxattr)
family->ce_mask |= FAMILY_ATTR_MAXATTR;
}
+/** @} */
+
+/**
+ * @name Operations
+ * @{
+ */
+
int genl_family_add_op(struct genl_family *family, int id, int flags)
{
struct genl_family_op *op;
@@ -255,6 +363,23 @@ int genl_family_add_op(struct genl_family *family, int id, int flags)
return 0;
}
+int genl_family_add_grp(struct genl_family *family, uint32_t id,
+ const char *name)
+{
+ struct genl_family_grp *grp;
+
+ grp = calloc(1, sizeof(*grp));
+ if (grp == NULL)
+ return -NLE_NOMEM;
+
+ grp->id = id;
+ strncpy(grp->name, name, GENL_NAMSIZ - 1);
+
+ nl_list_add_tail(&grp->list, &family->gf_mc_grps);
+
+ return 0;
+}
+
/** @} */
/** @cond SKIP */
diff --git a/lib/genl/genl.c b/lib/genl/genl.c
index 055be919..0c9b11eb 100644
--- a/lib/genl/genl.c
+++ b/lib/genl/genl.c
@@ -6,99 +6,42 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @defgroup genl Generic Netlink
+ * @defgroup genl Generic Netlink Library (libnl-genl)
*
- * @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 Example
- * @code
- * #include <netlink/netlink.h>
- * #include <netlink/genl/genl.h>
- * #include <netlink/genl/ctrl.h>
- *
- * struct nl_sock *sock;
- * struct nl_msg *msg;
- * int family;
- *
- * // Allocate a new netlink socket
- * sock = nl_socket_alloc();
- *
- * // Connect to generic netlink socket on kernel side
- * genl_connect(sock);
- *
- * // Ask kernel to resolve family name to family id
- * family = genl_ctrl_resolve(sock, "generic_netlink_family_name");
- *
- * // Construct a generic netlink by allocating a new message, fill in
- * // the header and append a simple integer attribute.
- * msg = nlmsg_alloc();
- * genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_ECHO,
- * CMD_FOO_GET, FOO_VERSION);
- * nla_put_u32(msg, ATTR_FOO, 123);
- *
- * // Send message over netlink socket
- * nl_send_auto_complete(sock, msg);
- *
- * // Free message
- * nlmsg_free(msg);
- *
- * // Prepare socket to receive the answer by specifying the callback
- * // function to be called for valid messages.
- * nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, parse_cb, NULL);
- *
- * // Wait for the answer and receive it
- * nl_recvmsgs_default(sock);
- *
- * static int parse_cb(struct nl_msg *msg, void *arg)
- * {
- * struct nlmsghdr *nlh = nlmsg_hdr(msg);
- * struct nlattr *attrs[ATTR_MAX+1];
- *
- * // Validate message and parse attributes
- * genlmsg_parse(nlh, 0, attrs, ATTR_MAX, policy);
- *
- * if (attrs[ATTR_FOO]) {
- * uint32_t value = nla_get_u32(attrs[ATTR_FOO]);
- * ...
- * }
- *
- * return 0;
- * }
- * @endcode
* @{
*/
-#include <netlink-generic.h>
+#include <netlink-private/genl.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/utils.h>
/**
- * @name Socket Creating
+ * @name Generic Netlink Socket
* @{
*/
+/**
+ * Connect a Generic Netlink socket
+ * @arg sk Unconnected Netlink socket
+ *
+ * This function expects a struct nl_socket object previously allocated via
+ * nl_socket_alloc(). It calls nl_connect() to create the local socket file
+ * descriptor and binds the socket to the \c NETLINK_GENERIC Netlink protocol.
+ *
+ * Using this function is equivalent to:
+ * @code
+ * nl_connect(sk, NETLINK_GENERIC);
+ * @endcode
+ *
+ * @see nl_connect()
+ *
+ * @return 0 on success or a negative error code.
+ */
int genl_connect(struct nl_sock *sk)
{
return nl_connect(sk, NETLINK_GENERIC);
@@ -107,20 +50,34 @@ int genl_connect(struct nl_sock *sk)
/** @} */
/**
- * @name Sending
+ * @name Sending Data
* @{
*/
/**
- * Send trivial generic netlink message
- * @arg sk Netlink socket.
- * @arg family Generic netlink family
- * @arg cmd Command
- * @arg version Version
- * @arg flags Additional netlink message flags.
+ * Send a Generic Netlink message consisting only of a header
+ * @arg sk Generic Netlink socket
+ * @arg family Numeric family identifier
+ * @arg cmd Numeric command identifier
+ * @arg version Interface version
+ * @arg flags Additional Netlink message flags (optional)
+ *
+ * This function is a shortcut for sending a Generic Netlink message without
+ * any message payload. The message will only consist of the Netlink and
+ * Generic Netlink headers. The header is constructed based on the specified
+ * parameters and passed on to nl_send_simple() to send it on the specified
+ * socket.
+ *
+ * @par Example:
+ * @code
+ * #include <netlink/genl/genl.h>
+ * #include <linux/genetlink.h>
+ *
+ * err = genl_send_simple(sk, GENL_ID_CTRL, CTRL_CMD_GETFAMILY, CTRL_VERSION,
+ * NLM_F_DUMP);
+ * @endcode
*
- * Fills out a routing netlink request message and sends it out
- * using nl_send_simple().
+ * @see nl_send_simple()
*
* @return 0 on success or a negative error code.
*/
@@ -137,12 +94,26 @@ int genl_send_simple(struct nl_sock *sk, int family, int cmd,
/** @} */
-
/**
* @name Message Parsing
* @{
*/
+/**
+ * Validate Generic Netlink message headers
+ * @arg nlh Pointer to Netlink message header
+ * @arg hdrlen Length of user header
+ *
+ * Verifies the integrity of the Netlink and Generic Netlink headers by
+ * enforcing the following requirements:
+ * - Valid Netlink message header (nlmsg_valid_hdr())
+ * - Presence of a complete Generic Netlink header
+ * - At least \c hdrlen bytes of payload included after the generic
+ * netlink header.
+ *
+ * @return A positive integer (true) if the headers are valid or
+ * 0 (false) if not.
+ */
int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen)
{
struct genlmsghdr *ghdr;
@@ -157,8 +128,28 @@ int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen)
return 1;
}
+/**
+ * Validate Generic Netlink message including attributes
+ * @arg nlh Pointer to Netlink message header
+ * @arg hdrlen Length of user header
+ * @arg maxtype Maximum attribtue id expected
+ * @arg policy Attribute validation policy
+ *
+ * Verifies the validity of the Netlink and Generic Netlink headers using
+ * genlmsg_valid_hdr() and calls nla_validate() on the message payload to
+ * verify the integrity of eventual attributes.
+ *
+ * @note You may call genlmsg_parse() directly to perform validation and
+ * parsing in a single step.
+ *
+ * @see genlmsg_valid_hdr()
+ * @see nla_validate()
+ * @see genlmsg_parse()
+ *
+ * @return 0 on success or a negative error code.
+ */
int genlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype,
- struct nla_policy *policy)
+ struct nla_policy *policy)
{
struct genlmsghdr *ghdr;
@@ -170,6 +161,33 @@ int genlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype,
genlmsg_attrlen(ghdr, hdrlen), maxtype, policy);
}
+/**
+ * Parse Generic Netlink message including attributes
+ * @arg nlh Pointer to Netlink message header
+ * @arg hdrlen Length of user header
+ * @arg tb Array to store parsed attributes
+ * @arg maxtype Maximum attribute id expected
+ * @arg policy Attribute validation policy
+ *
+ * Verifies the validity of the Netlink and Generic Netlink headers using
+ * genlmsg_valid_hdr() and calls nla_parse() on the message payload to
+ * parse eventual attributes.
+ *
+ * @par Example:
+ * @code
+ * struct nlattr *attrs[MY_TYPE_MAX+1];
+ *
+ * if ((err = genlsmg_parse(nlmsg_nlh(msg), sizeof(struct my_hdr), attrs,
+ * MY_TYPE_MAX, attr_policy)) < 0)
+ * // ERROR
+ * @endcode
+ *
+ * @see genlmsg_valid_hdr()
+ * @see genlmsg_validate()
+ * @see nla_parse()
+ *
+ * @return 0 on success or a negative error code.
+ */
int genlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
int maxtype, struct nla_policy *policy)
{
@@ -184,39 +202,102 @@ int genlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
}
/**
- * Get head of message payload
- * @arg gnlh genetlink messsage header
+ * Return pointer to Generic Netlink header
+ * @arg nlh Netlink message header
+ *
+ * @return Pointer to Generic Netlink message header
*/
-void *genlmsg_data(const struct genlmsghdr *gnlh)
+struct genlmsghdr *genlmsg_hdr(struct nlmsghdr *nlh)
{
- return ((unsigned char *) gnlh + GENL_HDRLEN);
+ return nlmsg_data(nlh);
}
/**
- * Get lenght of message payload
- * @arg gnlh genetlink message header
+ * Return length of message payload including user header
+ * @arg gnlh Generic Netlink message header
+ *
+ * @see genlmsg_data()
+ *
+ * @return Length of user payload including an eventual user header in
+ * number of bytes.
*/
int genlmsg_len(const struct genlmsghdr *gnlh)
{
- struct nlmsghdr *nlh = (struct nlmsghdr *)((unsigned char *)gnlh -
- NLMSG_HDRLEN);
+ const struct nlmsghdr *nlh;
+
+ nlh = (const struct nlmsghdr *)((const unsigned char *) gnlh - NLMSG_HDRLEN);
return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN);
}
+
+/**
+ * Return pointer to user header
+ * @arg gnlh Generic Netlink message header
+ *
+ * Calculates the pointer to the user header based on the pointer to
+ * the Generic Netlink message header.
+ *
+ * @return Pointer to the user header
+ */
+void *genlmsg_user_hdr(const struct genlmsghdr *gnlh)
+{
+ return genlmsg_data(gnlh);
+}
+
+/**
+ * Return pointer to user data
+ * @arg gnlh Generic netlink message header
+ * @arg hdrlen Length of user header
+ *
+ * Calculates the pointer to the user data based on the pointer to
+ * the Generic Netlink message header.
+ *
+ * @see genlmsg_user_datalen()
+ *
+ * @return Pointer to the user data
+ */
+void *genlmsg_user_data(const struct genlmsghdr *gnlh, const int hdrlen)
+{
+ return genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen);
+}
+
+/**
+ * Return length of user data
+ * @arg gnlh Generic Netlink message header
+ * @arg hdrlen Length of user header
+ *
+ * @see genlmsg_user_data()
+ *
+ * @return Length of user data in bytes
+ */
+int genlmsg_user_datalen(const struct genlmsghdr *gnlh, const int hdrlen)
+{
+ return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen);
+}
+
/**
- * Get head of attribute data
- * @arg gnlh generic netlink message header
- * @arg hdrlen length of family specific header
+ * Return pointer to message attributes
+ * @arg gnlh Generic Netlink message header
+ * @arg hdrlen Length of user header
+ *
+ * @see genlmsg_attrlen()
+ *
+ * @return Pointer to the start of the message's attributes section.
*/
struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen)
{
- return genlmsg_data(gnlh) + NLMSG_ALIGN(hdrlen);
+ return genlmsg_user_data(gnlh, hdrlen);
}
/**
- * Get length of attribute data
- * @arg gnlh generic netlink message header
- * @arg hdrlen length of family specific header
+ * Return length of message attributes
+ * @arg gnlh Generic Netlink message header
+ * @arg hdrlen Length of user header
+ *
+ * @see genlmsg_attrdata()
+ *
+ * @return Length of the message section containing attributes in number
+ * of bytes.
*/
int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen)
{
@@ -226,24 +307,45 @@ int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen)
/** @} */
/**
- * @name Message Building
+ * @name Message Construction
* @{
*/
/**
- * 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.
+ * Add Generic Netlink headers to Netlink message
+ * @arg msg Netlink message object
+ * @arg port Netlink port or NL_AUTO_PORT
+ * @arg seq Sequence number of message or NL_AUTO_SEQ
+ * @arg family Numeric family identifier
+ * @arg hdrlen Length of user header
+ * @arg flags Additional Netlink message flags (optional)
+ * @arg cmd Numeric command identifier
+ * @arg version Interface version
+ *
+ * Calls nlmsg_put() on the specified message object to reserve space for
+ * the Netlink header, the Generic Netlink header, and a user header of
+ * specified length. Fills out the header fields with the specified
+ * parameters.
+ *
+ * @par Example:
+ * @code
+ * struct nl_msg *msg;
+ * struct my_hdr *user_hdr;
+ *
+ * if (!(msg = nlmsg_alloc()))
+ * // ERROR
+ *
+ * user_hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family_id,
+ * sizeof(struct my_hdr), 0, MY_CMD_FOO, 0);
+ * if (!user_hdr)
+ * // ERROR
+ * @endcode
+ *
+ * @see nlmsg_put()
+ *
+ * Returns Pointer to user header or NULL if an error occurred.
*/
-void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family,
+void *genlmsg_put(struct nl_msg *msg, uint32_t port, uint32_t seq, int family,
int hdrlen, int flags, uint8_t cmd, uint8_t version)
{
struct nlmsghdr *nlh;
@@ -252,7 +354,7 @@ void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family,
.version = version,
};
- nlh = nlmsg_put(msg, pid, seq, family, GENL_HDRLEN + hdrlen, flags);
+ nlh = nlmsg_put(msg, port, seq, family, GENL_HDRLEN + hdrlen, flags);
if (nlh == NULL)
return NULL;
@@ -265,4 +367,25 @@ void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family,
/** @} */
+/**
+ * @name Deprecated
+ * @{
+ */
+
+/**
+ * Return pointer to message payload
+ * @arg gnlh Generic Netlink message header
+ *
+ * @deprecated This function has been deprecated due to inability to specify
+ * the length of the user header. Use genlmsg_user_hdr()
+ * respectively genlmsg_user_data().
+ *
+ * @return Pointer to payload section
+ */
+void *genlmsg_data(const struct genlmsghdr *gnlh)
+{
+ return ((unsigned char *) gnlh + GENL_HDRLEN);
+}
+
+/** @} */
/** @} */
diff --git a/lib/genl/mngt.c b/lib/genl/mngt.c
index 0ebe74d7..36486636 100644
--- a/lib/genl/mngt.c
+++ b/lib/genl/mngt.c
@@ -6,81 +6,19 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
* @ingroup genl
- * @defgroup genl_mngt Management
+ * @defgroup genl_mngt Family and Command Registration
*
- * @par 1) Registering a generic netlink module
- * @code
- * #include <netlink/genl/mngt.h>
+ * Registering Generic Netlink Families and Commands
*
- * // 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 <netlink-generic.h>
+#include <netlink-private/genl.h>
#include <netlink/netlink.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/mngt.h>
@@ -88,30 +26,38 @@
#include <netlink/genl/ctrl.h>
#include <netlink/utils.h>
+/** @cond SKIP */
+
static NL_LIST_HEAD(genl_ops_list);
-static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
- struct nlmsghdr *nlh, struct nl_parser_param *pp)
+static struct genl_cmd *lookup_cmd(struct genl_ops *ops, int cmd_id)
{
- int i, err;
- struct genlmsghdr *ghdr;
struct genl_cmd *cmd;
+ int i;
- ghdr = nlmsg_data(nlh);
+ for (i = 0; i < ops->o_ncmds; i++) {
+ cmd = &ops->o_cmds[i];
+ if (cmd->c_id == cmd_id)
+ return cmd;
+ }
- if (ops->co_genl == NULL)
- BUG();
+ return NULL;
+}
- 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;
- }
+static int cmd_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *nlh,
+ struct genl_ops *ops, struct nl_cache_ops *cache_ops, void *arg)
+{
+ int err;
+ struct genlmsghdr *ghdr;
+ struct genl_cmd *cmd;
- err = -NLE_MSGTYPE_NOSUPPORT;
- goto errout;
+ ghdr = genlmsg_hdr(nlh);
+
+ if (!(cmd = lookup_cmd(ops, ghdr->cmd))) {
+ err = -NLE_MSGTYPE_NOSUPPORT;
+ goto errout;
+ }
-found:
if (cmd->c_msg_parser == NULL)
err = -NLE_OPNOTSUPP;
else {
@@ -120,37 +66,68 @@ found:
.who = who,
.nlh = nlh,
.genlhdr = ghdr,
- .userhdr = genlmsg_data(ghdr),
+ .userhdr = genlmsg_user_hdr(ghdr),
.attrs = tb,
};
- err = nlmsg_parse(nlh, ops->co_hdrsize, tb, cmd->c_maxattr,
+ err = nlmsg_parse(nlh, GENL_HDRSIZE(ops->o_hdrsize), tb, cmd->c_maxattr,
cmd->c_attr_policy);
if (err < 0)
goto errout;
- err = cmd->c_msg_parser(ops, cmd, &info, pp);
+ err = cmd->c_msg_parser(cache_ops, cmd, &info, arg);
}
errout:
return err;
}
+static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+ struct nlmsghdr *nlh, struct nl_parser_param *pp)
+{
+ if (ops->co_genl == NULL)
+ BUG();
+
+ return cmd_msg_parser(who, nlh, ops->co_genl, ops, pp);
+}
+
+static struct genl_ops *lookup_family(int family)
+{
+ struct genl_ops *ops;
+
+ nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
+ if (ops->o_id == family)
+ return ops;
+ }
+
+ return NULL;
+}
+
+static struct genl_ops *lookup_family_by_name(const char *name)
+{
+ struct genl_ops *ops;
+
+ nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
+ if (!strcmp(ops->o_name, name))
+ return ops;
+ }
+
+ return NULL;
+}
+
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;
- }
+ if ((ops = lookup_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;
}
}
}
@@ -159,15 +136,107 @@ char *genl_op2name(int family, int op, char *buf, size_t len)
return NULL;
}
+/** @endcond */
/**
- * @name Register/Unregister
+ * @name Registration
* @{
*/
/**
- * Register generic netlink operations
- * @arg ops cache operations
+ * Register Generic Netlink family and associated commands
+ * @arg ops Generic Netlink family definition
+ *
+ * Registers the specified Generic Netlink family definition together with
+ * all associated commands. After registration, received Generic Netlink
+ * messages can be passed to genl_handle_msg() which will validate the
+ * messages, look for a matching command and call the respective callback
+ * function automatically.
+ *
+ * @note Consider using genl_register() if the family is used to implement a
+ * cacheable type.
+ *
+ * @see genl_unregister_family();
+ * @see genl_register();
+ *
+ * @return 0 on success or a negative error code.
+ */
+int genl_register_family(struct genl_ops *ops)
+{
+ if (!ops->o_name)
+ return -NLE_INVAL;
+
+ if (ops->o_cmds && ops->o_ncmds <= 0)
+ return -NLE_INVAL;
+
+ if (ops->o_id && lookup_family(ops->o_id))
+ return -NLE_EXIST;
+
+ if (lookup_family_by_name(ops->o_name))
+ return -NLE_EXIST;
+
+ nl_list_add_tail(&ops->o_list, &genl_ops_list);
+
+ return 0;
+}
+
+/**
+ * Unregister Generic Netlink family
+ * @arg ops Generic Netlink family definition
+ *
+ * Unregisters a family and all associated commands that were previously
+ * registered using genl_register_family().
+ *
+ * @see genl_register_family()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int genl_unregister_family(struct genl_ops *ops)
+{
+ nl_list_del(&ops->o_list);
+
+ return 0;
+}
+
+/**
+ * Run a received message through the demultiplexer
+ * @arg msg Generic Netlink message
+ * @arg arg Argument passed on to the message handler callback
+ *
+ * @return 0 on success or a negative error code.
+ */
+int genl_handle_msg(struct nl_msg *msg, void *arg)
+{
+ struct nlmsghdr *nlh = nlmsg_hdr(msg);
+ struct genl_ops *ops;
+
+ if (!genlmsg_valid_hdr(nlh, 0))
+ return -NLE_INVAL;
+
+ if (!(ops = lookup_family(nlh->nlmsg_type)))
+ return -NLE_MSGTYPE_NOSUPPORT;
+
+ return cmd_msg_parser(nlmsg_get_src(msg), nlh, ops, NULL, arg);
+}
+
+/** @} */
+
+/**
+ * @name Registration of Cache Operations
+ * @{
+ */
+
+/**
+ * Register Generic Netlink family backed cache
+ * @arg ops Cache operations definition
+ *
+ * Same as genl_register_family() but additionally registers the specified
+ * cache operations using nl_cache_mngt_register() and associates it with
+ * the Generic Netlink family.
+ *
+ * @see genl_register_family()
+ *
+ * @return 0 on success or a negative error code.
*/
int genl_register(struct nl_cache_ops *ops)
{
@@ -189,13 +258,13 @@ int genl_register(struct nl_cache_ops *ops)
}
ops->co_genl->o_cache_ops = ops;
+ ops->co_genl->o_hdrsize = ops->co_hdrsize - GENL_HDRLEN;
ops->co_genl->o_name = ops->co_msgtypes[0].mt_name;
- ops->co_genl->o_family = ops->co_msgtypes[0].mt_id;
+ ops->co_genl->o_id = 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);
+ if ((err = genl_register_family(ops->co_genl)) < 0)
+ goto errout;
err = nl_cache_mngt_register(ops);
errout:
@@ -203,22 +272,22 @@ errout:
}
/**
- * Unregister generic netlink operations
- * @arg ops cache operations
+ * Unregister cache based Generic Netlink family
+ * @arg ops Cache operations definition
*/
void genl_unregister(struct nl_cache_ops *ops)
{
+ if (!ops)
+ return;
+
nl_cache_mngt_unregister(ops);
- nl_list_del(&ops->co_genl->o_list);
+
+ genl_unregister_family(ops->co_genl);
}
/** @} */
-/**
- * @name Resolving ID/Name
- * @{
- */
-
+/** @cond SKIP */
static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops)
{
struct genl_family *family;
@@ -226,6 +295,10 @@ static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops)
family = genl_ctrl_search_by_name(ctrl, ops->o_name);
if (family != NULL) {
ops->o_id = genl_family_get_id(family);
+
+ if (ops->o_cache_ops)
+ ops->o_cache_ops->co_msgtypes[0].mt_id = ops->o_id;
+
genl_family_put(family);
return 0;
@@ -234,6 +307,47 @@ static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops)
return -NLE_OBJ_NOTFOUND;
}
+int genl_resolve_id(struct genl_ops *ops)
+{
+ struct nl_sock *sk;
+ int err = 0;
+
+ /* Check if resolved already */
+ if (ops->o_id != GENL_ID_GENERATE)
+ return 0;
+
+ if (!ops->o_name)
+ return -NLE_INVAL;
+
+ if (!(sk = nl_socket_alloc()))
+ return -NLE_NOMEM;
+
+ if ((err = genl_connect(sk)) < 0)
+ goto errout_free;
+
+ err = genl_ops_resolve(sk, ops);
+
+errout_free:
+ nl_socket_free(sk);
+
+ return err;
+}
+/** @endcond */
+
+/**
+ * @name Resolving the name of registered families
+ * @{
+ */
+
+/**
+ * Resolve a single Generic Netlink family
+ * @arg sk Generic Netlink socket
+ * @arg ops Generic Netlink family definition
+ *
+ * Resolves the family name to its numeric identifier.
+ *
+ * @return 0 on success or a negative error code.
+ */
int genl_ops_resolve(struct nl_sock *sk, struct genl_ops *ops)
{
struct nl_cache *ctrl;
@@ -249,6 +363,19 @@ errout:
return err;
}
+/**
+ * Resolve all registered Generic Netlink families
+ * @arg sk Generic Netlink socket
+ *
+ * Walks through all local Generic Netlink families that have been registered
+ * using genl_register() and resolves the name of each family to the
+ * corresponding numeric identifier.
+ *
+ * @see genl_register()
+ * @see genl_ops_resolve()
+ *
+ * @return 0 on success or a negative error code.
+ */
int genl_mngt_resolve(struct nl_sock *sk)
{
struct nl_cache *ctrl;
@@ -269,5 +396,4 @@ errout:
/** @} */
-
/** @} */
diff --git a/lib/handlers.c b/lib/handlers.c
index f13b89ea..a6a97bb8 100644
--- a/lib/handlers.c
+++ b/lib/handlers.c
@@ -13,24 +13,19 @@
* @ingroup core
* @defgroup cb Callbacks/Customization
*
- * @details
- * @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);
+ * Related sections in the development guide:
+ * - @core_doc{core_cb, Callback Configuration}
*
- * // 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
* @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/handlers.h>
+ * ~~~~
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/msg.h>
@@ -40,7 +35,7 @@ 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,
@@ -76,7 +71,7 @@ static int nl_overrun_handler_verbose(struct nl_msg *msg, void *arg)
fprintf(ofd, "-- Error: Netlink Overrun: ");
print_header_content(ofd, nlmsg_hdr(msg));
fprintf(ofd, "\n");
-
+
return NL_STOP;
}
@@ -84,9 +79,10 @@ static int nl_error_handler_verbose(struct sockaddr_nl *who,
struct nlmsgerr *e, void *arg)
{
FILE *ofd = arg ? arg : stderr;
+ char buf[256];
fprintf(ofd, "-- Error received: %s\n-- Original message: ",
- strerror(-e->error));
+ strerror_r(-e->error, buf, sizeof(buf)));
print_header_content(ofd, &e->msg);
fprintf(ofd, "\n");
@@ -111,7 +107,7 @@ static int nl_finish_handler_debug(struct nl_msg *msg, void *arg)
fprintf(ofd, "-- Debug: End of multipart message block: ");
print_header_content(ofd, nlmsg_hdr(msg));
fprintf(ofd, "\n");
-
+
return NL_STOP;
}
@@ -121,7 +117,7 @@ static int nl_msg_in_handler_debug(struct nl_msg *msg, void *arg)
fprintf(ofd, "-- Debug: Received Message:\n");
nl_msg_dump(msg, ofd);
-
+
return NL_OK;
}
@@ -215,6 +211,7 @@ struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind)
return NULL;
cb->cb_refcnt = 1;
+ cb->cb_active = NL_CB_TYPE_MAX + 1;
for (i = 0; i <= NL_CB_TYPE_MAX; i++)
nl_cb_set(cb, i, kind, NULL, NULL);
@@ -233,7 +230,7 @@ struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind)
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;
@@ -265,6 +262,17 @@ void nl_cb_put(struct nl_cb *cb)
free(cb);
}
+/**
+ * Obtain type of current active callback
+ * @arg cb callback to query
+ *
+ * @return type or __NL_CB_TYPE_MAX if none active
+ */
+enum nl_cb_type nl_cb_active_type(struct nl_cb *cb)
+{
+ return cb->cb_active;
+}
+
/** @} */
/**
@@ -273,7 +281,7 @@ void nl_cb_put(struct nl_cb *cb)
*/
/**
- * Set up a callback
+ * Set up a callback
* @arg cb callback set
* @arg type callback to modify
* @arg kind kind of implementation
diff --git a/lib/hash.c b/lib/hash.c
new file mode 100644
index 00000000..47c938b9
--- /dev/null
+++ b/lib/hash.c
@@ -0,0 +1,482 @@
+/*
+ * This code was taken from http://ccodearchive.net/info/hash.html
+ * The original file was modified to remove unwanted code
+ * and some changes to fit the current build environment
+ */
+/*
+-------------------------------------------------------------------------------
+lookup3.c, by Bob Jenkins, May 2006, Public Domain.
+
+These are functions for producing 32-bit hashes for hash table lookup.
+hash_word(), hashlittle(), hashlittle2(), hashbig(), mix(), and final()
+are externally useful functions. Routines to test the hash are included
+if SELF_TEST is defined. You can use this free for any purpose. It's in
+the public domain. It has no warranty.
+
+You probably want to use hashlittle(). hashlittle() and hashbig()
+hash byte arrays. hashlittle() is is faster than hashbig() on
+little-endian machines. Intel and AMD are little-endian machines.
+On second thought, you probably want hashlittle2(), which is identical to
+hashlittle() except it returns two 32-bit hashes for the price of one.
+You could implement hashbig2() if you wanted but I haven't bothered here.
+
+If you want to find a hash of, say, exactly 7 integers, do
+ a = i1; b = i2; c = i3;
+ mix(a,b,c);
+ a += i4; b += i5; c += i6;
+ mix(a,b,c);
+ a += i7;
+ final(a,b,c);
+then use c as the hash value. If you have a variable length array of
+4-byte integers to hash, use hash_word(). If you have a byte array (like
+a character string), use hashlittle(). If you have several byte arrays, or
+a mix of things, see the comments above hashlittle().
+
+Why is this so big? I read 12 bytes at a time into 3 4-byte integers,
+then mix those integers. This is fast (you can do a lot more thorough
+mixing with 12*3 instructions on 3 integers than you can with 3 instructions
+on 1 byte), but shoehorning those bytes into integers efficiently is messy.
+-------------------------------------------------------------------------------
+*/
+#include <netlink/hash.h>
+
+#if HAVE_LITTLE_ENDIAN
+#define HASH_LITTLE_ENDIAN 1
+#define HASH_BIG_ENDIAN 0
+#elif HAVE_BIG_ENDIAN
+#define HASH_LITTLE_ENDIAN 0
+#define HASH_BIG_ENDIAN 1
+#else
+#error Unknown endian
+#endif
+
+#define hashsize(n) ((uint32_t)1<<(n))
+#define hashmask(n) (hashsize(n)-1)
+#define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k))))
+
+/*
+-------------------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+
+This is reversible, so any information in (a,b,c) before mix() is
+still in (a,b,c) after mix().
+
+If four pairs of (a,b,c) inputs are run through mix(), or through
+mix() in reverse, there are at least 32 bits of the output that
+are sometimes the same for one pair and different for another pair.
+This was tested for:
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+Some k values for my "a-=c; a^=rot(c,k); c+=b;" arrangement that
+satisfy this are
+ 4 6 8 16 19 4
+ 9 15 3 18 27 15
+ 14 9 3 7 17 3
+Well, "9 15 3 18 27 15" didn't quite get 32 bits diffing
+for "differ" defined as + with a one-bit base and a two-bit delta. I
+used http://burtleburtle.net/bob/hash/avalanche.html to choose
+the operations, constants, and arrangements of the variables.
+
+This does not achieve avalanche. There are input bits of (a,b,c)
+that fail to affect some output bits of (a,b,c), especially of a. The
+most thoroughly mixed value is c, but it doesn't really even achieve
+avalanche in c.
+
+This allows some parallelism. Read-after-writes are good at doubling
+the number of bits affected, so the goal of mixing pulls in the opposite
+direction as the goal of parallelism. I did what I could. Rotates
+seem to cost as much as shifts on every machine I could lay my hands
+on, and rotates are much kinder to the top and bottom bits, so I used
+rotates.
+-------------------------------------------------------------------------------
+*/
+#define mix(a,b,c) \
+{ \
+ a -= c; a ^= rot(c, 4); c += b; \
+ b -= a; b ^= rot(a, 6); a += c; \
+ c -= b; c ^= rot(b, 8); b += a; \
+ a -= c; a ^= rot(c,16); c += b; \
+ b -= a; b ^= rot(a,19); a += c; \
+ c -= b; c ^= rot(b, 4); b += a; \
+}
+
+/*
+-------------------------------------------------------------------------------
+final -- final mixing of 3 32-bit values (a,b,c) into c
+
+Pairs of (a,b,c) values differing in only a few bits will usually
+produce values of c that look totally different. This was tested for
+* pairs that differed by one bit, by two bits, in any combination
+ of top bits of (a,b,c), or in any combination of bottom bits of
+ (a,b,c).
+* "differ" is defined as +, -, ^, or ~^. For + and -, I transformed
+ the output delta to a Gray code (a^(a>>1)) so a string of 1's (as
+ is commonly produced by subtraction) look like a single 1-bit
+ difference.
+* the base values were pseudorandom, all zero but one bit set, or
+ all zero plus a counter that starts at zero.
+
+These constants passed:
+ 14 11 25 16 4 14 24
+ 12 14 25 16 4 14 24
+and these came close:
+ 4 8 15 26 3 22 24
+ 10 8 15 26 3 22 24
+ 11 8 15 26 3 22 24
+-------------------------------------------------------------------------------
+*/
+#define final(a,b,c) \
+{ \
+ c ^= b; c -= rot(b,14); \
+ a ^= c; a -= rot(c,11); \
+ b ^= a; b -= rot(a,25); \
+ c ^= b; c -= rot(b,16); \
+ a ^= c; a -= rot(c,4); \
+ b ^= a; b -= rot(a,14); \
+ c ^= b; c -= rot(b,24); \
+}
+
+/*
+-------------------------------------------------------------------------------
+hashlittle() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ length : the length of the key, counting by bytes
+ val2 : IN: can be any 4-byte value OUT: second 32 bit hash.
+Returns a 32-bit value. Every bit of the key affects every bit of
+the return value. Two keys differing by one or two bits will have
+totally different hash values. Note that the return value is better
+mixed than val2, so use that first.
+
+The best hash table sizes are powers of 2. There is no need to do
+mod a prime (mod is sooo slow!). If you need less than 32 bits,
+use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (uint8_t **)k, do it like this:
+ for (i=0, h=0; i<n; ++i) h = hashlittle( k[i], len[i], h);
+
+By Bob Jenkins, 2006. bob_jenkins@burtleburtle.net. You may use this
+code any way you wish, private, educational, or commercial. It's free.
+
+Use for hash table lookup, or anything where one collision in 2^^32 is
+acceptable. Do NOT use for cryptographic purposes.
+-------------------------------------------------------------------------------
+*/
+
+static uint32_t hashlittle( const void *key, size_t length, uint32_t *val2 )
+{
+ uint32_t a,b,c; /* internal state */
+ union { const void *ptr; size_t i; } u; /* needed for Mac Powerbook G4 */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + *val2;
+
+ u.ptr = key;
+ if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+ const uint8_t *k8;
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]&0xffffff" actually reads beyond the end of the string, but
+ * then masks off the part it's not allowed to read. Because the
+ * string is aligned, the masked-off tail is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ *
+ * Not on my testing with gcc 4.5 on an intel i5 CPU, at least --RR.
+ */
+#if 0
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff; break;
+ case 2 : a+=k[0]&0xffff; break;
+ case 1 : a+=k[0]&0xff; break;
+ case 0 : return c; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */
+ case 1 : a+=k8[0]; break;
+ case 0 : return c;
+ }
+
+#endif /* !valgrind */
+
+ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) {
+ const uint16_t *k = (const uint16_t *)key; /* read 16-bit chunks */
+ const uint8_t *k8;
+
+ /*--------------- all but last block: aligned reads and different mixing */
+ while (length > 12)
+ {
+ a += k[0] + (((uint32_t)k[1])<<16);
+ b += k[2] + (((uint32_t)k[3])<<16);
+ c += k[4] + (((uint32_t)k[5])<<16);
+ mix(a,b,c);
+ length -= 12;
+ k += 6;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ k8 = (const uint8_t *)k;
+ switch(length)
+ {
+ case 12: c+=k[4]+(((uint32_t)k[5])<<16);
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 11: c+=((uint32_t)k8[10])<<16; /* fall through */
+ case 10: c+=k[4];
+ b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 9 : c+=k8[8]; /* fall through */
+ case 8 : b+=k[2]+(((uint32_t)k[3])<<16);
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */
+ case 6 : b+=k[2];
+ a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 5 : b+=k8[4]; /* fall through */
+ case 4 : a+=k[0]+(((uint32_t)k[1])<<16);
+ break;
+ case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */
+ case 2 : a+=k[0];
+ break;
+ case 1 : a+=k8[0];
+ break;
+ case 0 : return c; /* zero length requires no mixing */
+ }
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ a += ((uint32_t)k[1])<<8;
+ a += ((uint32_t)k[2])<<16;
+ a += ((uint32_t)k[3])<<24;
+ b += k[4];
+ b += ((uint32_t)k[5])<<8;
+ b += ((uint32_t)k[6])<<16;
+ b += ((uint32_t)k[7])<<24;
+ c += k[8];
+ c += ((uint32_t)k[9])<<8;
+ c += ((uint32_t)k[10])<<16;
+ c += ((uint32_t)k[11])<<24;
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=((uint32_t)k[11])<<24;
+ case 11: c+=((uint32_t)k[10])<<16;
+ case 10: c+=((uint32_t)k[9])<<8;
+ case 9 : c+=k[8];
+ case 8 : b+=((uint32_t)k[7])<<24;
+ case 7 : b+=((uint32_t)k[6])<<16;
+ case 6 : b+=((uint32_t)k[5])<<8;
+ case 5 : b+=k[4];
+ case 4 : a+=((uint32_t)k[3])<<24;
+ case 3 : a+=((uint32_t)k[2])<<16;
+ case 2 : a+=((uint32_t)k[1])<<8;
+ case 1 : a+=k[0];
+ break;
+ case 0 : return c;
+ }
+ }
+
+ final(a,b,c);
+ *val2 = b;
+ return c;
+}
+
+/*
+ * hashbig():
+ * This is the same as hash_word() on big-endian machines. It is different
+ * from hashlittle() on all machines. hashbig() takes advantage of
+ * big-endian byte ordering.
+ */
+static uint32_t hashbig( const void *key, size_t length, uint32_t *val2)
+{
+ uint32_t a,b,c;
+ union { const void *ptr; size_t i; } u; /* to cast key to (size_t) happily */
+
+ /* Set up the internal state */
+ a = b = c = 0xdeadbeef + ((uint32_t)length) + *val2;
+
+ u.ptr = key;
+ if (HASH_BIG_ENDIAN && ((u.i & 0x3) == 0)) {
+ const uint32_t *k = (const uint32_t *)key; /* read 32-bit chunks */
+ const uint8_t *k8;
+
+ /*------ all but last block: aligned reads and affect 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += k[0];
+ b += k[1];
+ c += k[2];
+ mix(a,b,c);
+ length -= 12;
+ k += 3;
+ }
+
+ /*----------------------------- handle the last (probably partial) block */
+ /*
+ * "k[2]<<8" actually reads beyond the end of the string, but
+ * then shifts out the part it's not allowed to read. Because the
+ * string is aligned, the illegal read is in the same word as the
+ * rest of the string. Every machine with memory protection I've seen
+ * does it on word boundaries, so is OK with this. But VALGRIND will
+ * still catch it and complain. The masking trick does make the hash
+ * noticably faster for short strings (like English words).
+ *
+ * Not on my testing with gcc 4.5 on an intel i5 CPU, at least --RR.
+ */
+#if 0
+ switch(length)
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=k[2]&0xffffff00; b+=k[1]; a+=k[0]; break;
+ case 10: c+=k[2]&0xffff0000; b+=k[1]; a+=k[0]; break;
+ case 9 : c+=k[2]&0xff000000; b+=k[1]; a+=k[0]; break;
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=k[1]&0xffffff00; a+=k[0]; break;
+ case 6 : b+=k[1]&0xffff0000; a+=k[0]; break;
+ case 5 : b+=k[1]&0xff000000; a+=k[0]; break;
+ case 4 : a+=k[0]; break;
+ case 3 : a+=k[0]&0xffffff00; break;
+ case 2 : a+=k[0]&0xffff0000; break;
+ case 1 : a+=k[0]&0xff000000; break;
+ case 0 : return c; /* zero length strings require no mixing */
+ }
+
+#else /* make valgrind happy */
+
+ k8 = (const uint8_t *)k;
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=k[2]; b+=k[1]; a+=k[0]; break;
+ case 11: c+=((uint32_t)k8[10])<<8; /* fall through */
+ case 10: c+=((uint32_t)k8[9])<<16; /* fall through */
+ case 9 : c+=((uint32_t)k8[8])<<24; /* fall through */
+ case 8 : b+=k[1]; a+=k[0]; break;
+ case 7 : b+=((uint32_t)k8[6])<<8; /* fall through */
+ case 6 : b+=((uint32_t)k8[5])<<16; /* fall through */
+ case 5 : b+=((uint32_t)k8[4])<<24; /* fall through */
+ case 4 : a+=k[0]; break;
+ case 3 : a+=((uint32_t)k8[2])<<8; /* fall through */
+ case 2 : a+=((uint32_t)k8[1])<<16; /* fall through */
+ case 1 : a+=((uint32_t)k8[0])<<24; break;
+ case 0 : return c;
+ }
+
+#endif /* !VALGRIND */
+
+ } else { /* need to read the key one byte at a time */
+ const uint8_t *k = (const uint8_t *)key;
+
+ /*--------------- all but the last block: affect some 32 bits of (a,b,c) */
+ while (length > 12)
+ {
+ a += ((uint32_t)k[0])<<24;
+ a += ((uint32_t)k[1])<<16;
+ a += ((uint32_t)k[2])<<8;
+ a += ((uint32_t)k[3]);
+ b += ((uint32_t)k[4])<<24;
+ b += ((uint32_t)k[5])<<16;
+ b += ((uint32_t)k[6])<<8;
+ b += ((uint32_t)k[7]);
+ c += ((uint32_t)k[8])<<24;
+ c += ((uint32_t)k[9])<<16;
+ c += ((uint32_t)k[10])<<8;
+ c += ((uint32_t)k[11]);
+ mix(a,b,c);
+ length -= 12;
+ k += 12;
+ }
+
+ /*-------------------------------- last block: affect all 32 bits of (c) */
+ switch(length) /* all the case statements fall through */
+ {
+ case 12: c+=k[11];
+ case 11: c+=((uint32_t)k[10])<<8;
+ case 10: c+=((uint32_t)k[9])<<16;
+ case 9 : c+=((uint32_t)k[8])<<24;
+ case 8 : b+=k[7];
+ case 7 : b+=((uint32_t)k[6])<<8;
+ case 6 : b+=((uint32_t)k[5])<<16;
+ case 5 : b+=((uint32_t)k[4])<<24;
+ case 4 : a+=k[3];
+ case 3 : a+=((uint32_t)k[2])<<8;
+ case 2 : a+=((uint32_t)k[1])<<16;
+ case 1 : a+=((uint32_t)k[0])<<24;
+ break;
+ case 0 : return c;
+ }
+ }
+
+ final(a,b,c);
+ *val2 = b;
+ return c;
+}
+
+uint32_t nl_hash_any(const void *key, size_t length, uint32_t base)
+{
+ if (HASH_BIG_ENDIAN)
+ return hashbig(key, length, &base);
+ else
+ return hashlittle(key, length, &base);
+}
diff --git a/lib/hashtable.c b/lib/hashtable.c
new file mode 100644
index 00000000..8b159251
--- /dev/null
+++ b/lib/hashtable.c
@@ -0,0 +1,197 @@
+/*
+ * netlink/hashtable.c Netlink hashtable 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) 2012 Cumulus Networks, Inc
+ */
+#include <string.h>
+#include <netlink-private/netlink.h>
+#include <netlink/object.h>
+#include <netlink/hash.h>
+#include <netlink/hashtable.h>
+
+/**
+ * @ingroup core_types
+ * @defgroup hashtable Hashtable
+ * @{
+ */
+
+/**
+ * Allocate hashtable
+ * @arg size Size of hashtable in number of elements
+ *
+ * @return Allocated hashtable or NULL.
+ */
+nl_hash_table_t *nl_hash_table_alloc(int size)
+{
+ nl_hash_table_t *ht;
+
+ ht = calloc(1, sizeof (*ht));
+ if (!ht)
+ goto errout;
+
+ ht->nodes = calloc(size, sizeof (*ht->nodes));
+ if (!ht->nodes) {
+ free(ht);
+ goto errout;
+ }
+
+ ht->size = size;
+
+ return ht;
+errout:
+ return NULL;
+}
+
+/**
+ * Free hashtable including all nodes
+ * @arg ht Hashtable
+ *
+ * @note Reference counter of all objects in the hashtable will be decremented.
+ */
+void nl_hash_table_free(nl_hash_table_t *ht)
+{
+ int i;
+
+ for(i = 0; i < ht->size; i++) {
+ nl_hash_node_t *node = ht->nodes[i];
+ nl_hash_node_t *saved_node;
+
+ while (node) {
+ saved_node = node;
+ node = node->next;
+ nl_object_put(saved_node->obj);
+ free(saved_node);
+ }
+ }
+
+ free(ht->nodes);
+ free(ht);
+}
+
+/**
+ * Lookup identical object in hashtable
+ * @arg ht Hashtable
+ * @arg obj Object to lookup
+ *
+ * Generates hashkey for `obj` and traverses the corresponding chain calling
+ * `nl_object_identical()` on each trying to find a match.
+ *
+ * @return Pointer to object if match was found or NULL.
+ */
+struct nl_object* nl_hash_table_lookup(nl_hash_table_t *ht,
+ struct nl_object *obj)
+{
+ nl_hash_node_t *node;
+ uint32_t key_hash;
+
+ nl_object_keygen(obj, &key_hash, ht->size);
+ node = ht->nodes[key_hash];
+
+ while (node) {
+ if (nl_object_identical(node->obj, obj))
+ return node->obj;
+ node = node->next;
+ }
+
+ return NULL;
+}
+
+/**
+ * Add object to hashtable
+ * @arg ht Hashtable
+ * @arg obj Object to add
+ *
+ * Adds `obj` to the hashtable. Object type must support hashing, otherwise all
+ * objects will be added to the chain `0`.
+ *
+ * @note The reference counter of the object is incremented.
+ *
+ * @return 0 on success or a negative error code
+ * @retval -NLE_EXIST Identical object already present in hashtable
+ */
+int nl_hash_table_add(nl_hash_table_t *ht, struct nl_object *obj)
+{
+ nl_hash_node_t *node;
+ uint32_t key_hash;
+
+ nl_object_keygen(obj, &key_hash, ht->size);
+ node = ht->nodes[key_hash];
+
+ while (node) {
+ if (nl_object_identical(node->obj, obj)) {
+ NL_DBG(2, "Warning: Add to hashtable found duplicate...\n");
+ return -NLE_EXIST;
+ }
+ node = node->next;
+ }
+
+ NL_DBG (5, "adding cache entry of obj %p in table %p, with hash 0x%x\n",
+ obj, ht, key_hash);
+
+ node = malloc(sizeof(nl_hash_node_t));
+ if (!node)
+ return -NLE_NOMEM;
+ nl_object_get(obj);
+ node->obj = obj;
+ node->key = key_hash;
+ node->key_size = sizeof(uint32_t);
+ node->next = ht->nodes[key_hash];
+ ht->nodes[key_hash] = node;
+
+ return 0;
+}
+
+/**
+ * Remove object from hashtable
+ * @arg ht Hashtable
+ * @arg obj Object to remove
+ *
+ * Remove `obj` from hashtable if it exists.
+ *
+ * @note Reference counter of object will be decremented.
+ *
+ * @return 0 on success or a negative error code.
+ * @retval -NLE_OBJ_NOTFOUND Object not present in hashtable.
+ */
+int nl_hash_table_del(nl_hash_table_t *ht, struct nl_object *obj)
+{
+ nl_hash_node_t *node, *prev;
+ uint32_t key_hash;
+
+ nl_object_keygen(obj, &key_hash, ht->size);
+ prev = node = ht->nodes[key_hash];
+
+ while (node) {
+ if (nl_object_identical(node->obj, obj)) {
+ nl_object_put(obj);
+
+ NL_DBG (5, "deleting cache entry of obj %p in table %p, with"
+ " hash 0x%x\n", obj, ht, key_hash);
+
+ if (node == ht->nodes[key_hash])
+ ht->nodes[key_hash] = node->next;
+ else
+ prev->next = node->next;
+
+ free(node);
+
+ return 0;
+ }
+ prev = node;
+ node = node->next;
+ }
+
+ return -NLE_OBJ_NOTFOUND;
+}
+
+uint32_t nl_hash(void *k, size_t length, uint32_t initval)
+{
+ return(__nl_hash(k, length, initval));
+}
+
+/** @} */
diff --git a/lib/idiag/idiag.c b/lib/idiag/idiag.c
new file mode 100644
index 00000000..81d73db1
--- /dev/null
+++ b/lib/idiag/idiag.c
@@ -0,0 +1,274 @@
+/*
+ * lib/idiag/idiag.c Inet Diag 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) 2013 Sassano Systems LLC <joe@sassanosystems.com>
+ */
+
+/**
+ * @defgroup idiag Inet Diag library (libnl-idiag)
+ * @brief
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/idiag/idiagnl.h>
+#include <linux/inet_diag.h>
+
+/**
+ * @name Socket Creation
+ * @{
+ */
+
+/**
+ * Create and connect idiag netlink socket.
+ * @arg sk Netlink socket.
+ *
+ * Creates a NETLINK_INET_DIAG socket, binds the socket, and issues a connection
+ * attemp.
+ *
+ * @see nl_connect()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int idiagnl_connect(struct nl_sock *sk)
+{
+ return nl_connect(sk, NETLINK_INET_DIAG);
+}
+
+/** @} */
+
+/**
+ * @name Sending
+ * @{
+ */
+
+/**
+ * Send trivial idiag netlink message
+ * @arg sk Netlink socket.
+ * @arg flags Message flags
+ * @arg family Address family
+ * @arg states Socket states to query
+ * @arg ext Inet Diag attribute extensions to query
+ *
+ * @return Newly allocated netlink message or NULL.
+ */
+int idiagnl_send_simple(struct nl_sock *sk, int flags, uint8_t family,
+ uint16_t states, uint16_t ext)
+{
+ struct inet_diag_req req;
+ memset(&req, 0, sizeof(req));
+
+ flags |= NLM_F_ROOT;
+
+ req.idiag_family = family;
+ req.idiag_states = states;
+ req.idiag_ext = ext;
+
+ return nl_send_simple(sk, TCPDIAG_GETSOCK, flags, &req, sizeof(req));
+}
+
+/** @} */
+
+/**
+ * @name Inet Diag flag and attribute conversions
+ * @{
+ */
+
+static const struct trans_tbl idiag_states[] = {
+ __ADD(IDIAG_SS_UNKNOWN, unknown)
+ __ADD(IDIAG_SS_ESTABLISHED, established)
+ __ADD(IDIAG_SS_SYN_SENT, syn_sent)
+ __ADD(IDIAG_SS_SYN_RECV, syn_recv)
+ __ADD(IDIAG_SS_FIN_WAIT1, fin_wait)
+ __ADD(IDIAG_SS_FIN_WAIT2, fin_wait2)
+ __ADD(IDIAG_SS_TIME_WAIT, time_wait)
+ __ADD(IDIAG_SS_CLOSE, close)
+ __ADD(IDIAG_SS_CLOSE_WAIT, close_wait)
+ __ADD(IDIAG_SS_LAST_ACK, last_ack)
+ __ADD(IDIAG_SS_LISTEN, listen)
+ __ADD(IDIAG_SS_CLOSING, closing)
+ __ADD(IDIAG_SS_MAX, max)
+ { ((1<<IDIAG_SS_MAX)-1), "all" }
+};
+
+/**
+ * Convert inet diag socket states to strings.
+ * @arg state inetdiag socket state (e.g., IDIAG_SS_ESTABLISHED)
+ * @arg buf output buffer which will hold string result
+ * @arg len length in bytes of the output buffer
+ *
+ * @return string representation of the inetdiag socket state or an empty
+ * string.
+ */
+char * idiagnl_state2str(int state, char *buf, size_t len)
+{
+ return __type2str(state, buf, len, idiag_states,
+ ARRAY_SIZE(idiag_states));
+}
+
+/**
+ * Convert inet diag socket state string to int.
+ * @arg name inetdiag socket state string
+ *
+ * @return the int representation of the socket state strign or a negative error
+ * code.
+ */
+int idiagnl_str2state(const char *name)
+{
+ return __str2type(name, idiag_states, ARRAY_SIZE(idiag_states));
+}
+
+static const struct trans_tbl idiag_timers[] = {
+ __ADD(IDIAG_TIMER_OFF, off)
+ __ADD(IDIAG_TIMER_ON, on)
+ __ADD(IDIAG_TIMER_KEEPALIVE, keepalive)
+ __ADD(IDIAG_TIMER_TIMEWAIT, timewait)
+ __ADD(IDIAG_TIMER_PERSIST, persist)
+ __ADD(IDIAG_TIMER_UNKNOWN, unknown)
+};
+
+/**
+ * Convert inet diag timer types to strings.
+ * @arg timer inetdiag timer (e.g., IDIAG_TIMER_ON)
+ * @arg buf output buffer which will hold string result
+ * @arg len length in bytes of the output buffer
+ *
+ * @return string representation of the inetdiag timer type or an empty string.
+ */
+char * idiagnl_timer2str(int timer, char *buf, size_t len)
+{
+ return __type2str(timer, buf, len, idiag_timers,
+ ARRAY_SIZE(idiag_timers));
+}
+
+/**
+ * Convert inet diag timer string to int.
+ * @arg name inetdiag timer string
+ *
+ * @return the int representation of the timer string or a negative error code.
+ */
+int idiagnl_str2timer(const char *name)
+{
+ return __str2type(name, idiag_timers, ARRAY_SIZE(idiag_timers));
+}
+
+static const struct trans_tbl idiag_attrs[] = {
+ __ADD(IDIAG_ATTR_NONE, none)
+ __ADD(IDIAG_ATTR_MEMINFO, meminfo)
+ __ADD(IDIAG_ATTR_INFO, info)
+ __ADD(IDIAG_ATTR_VEGASINFO, vegasinfo)
+ __ADD(IDIAG_ATTR_CONG, congestion)
+ __ADD(IDIAG_ATTR_TOS, tos)
+ __ADD(IDIAG_ATTR_TCLASS, tclass)
+};
+
+/**
+ * Convert inetdiag extended attributes to strings.
+ * @arg attrs inetdiag attribute (e.g., IDIAG_ATTR_MEMINFO)
+ * @arg buf output buffer which will hold string result
+ * @arg len length in bytes of the output buffer
+ *
+ * @return string representation of attrs or an empty string.
+ */
+char *idiagnl_attrs2str(int attrs, char *buf, size_t len)
+{
+ return __type2str(attrs, buf, len, idiag_attrs, ARRAY_SIZE(idiag_attrs));
+}
+
+static const struct trans_tbl idiagnl_tcpstates[] = {
+ __ADD(TCP_CA_Open, open)
+ __ADD(TCP_CA_Disorder, disorder)
+ __ADD(TCP_CA_CWR, cwr)
+ __ADD(TCP_CA_Recovery, recovery)
+ __ADD(TCP_CA_Loss, loss)
+};
+
+/**
+ * Convert inetdiag tcp states to strings.
+ * @arg state TCP state (e.g., TCP_CA_Open)
+ * @arg buf output buffer which will hold string result
+ * @arg len length in bytes of the output buffer
+ */
+char *idiagnl_tcpstate2str(uint8_t state, char *buf, size_t len)
+{
+ return __type2str(state, buf, len, idiagnl_tcpstates,
+ ARRAY_SIZE(idiagnl_tcpstates));
+}
+
+static const struct trans_tbl idiagnl_tcpopt_attrs[] = {
+ __ADD(TCPI_OPT_TIMESTAMPS, timestamps)
+ __ADD(TCPI_OPT_SACK, sACK)
+ __ADD(TCPI_OPT_WSCALE, wscale)
+ __ADD(TCPI_OPT_ECN, ecn)
+};
+
+/**
+ * Convert TCP option attributes to string
+ * @arg attrs TCP option attributes to convert (e.g., TCPI_OPT_SACK |
+ * TCPI_OPT_WSCALE)
+ * @arg buf Output buffer for string
+ * @arg len Length in bytes of output buffer
+ *
+ * @return buffer with string representation or empty string
+ */
+char *idiagnl_tcpopts2str(uint8_t attrs, char *buf, size_t len)
+{
+ return __flags2str(attrs, buf, len, idiagnl_tcpopt_attrs,
+ ARRAY_SIZE(idiagnl_tcpopt_attrs));
+}
+
+/**
+ * Convert shutdown state to string.
+ * @arg shutdown Shutdown state (e.g., idiag_msg->shutdown)
+ * @arg buf Ouput buffer to hold string representation
+ * @arg len Length in bytes of output buffer
+ *
+ * @return string representation of shutdown state or NULL
+ */
+char * idiagnl_shutdown2str(uint8_t shutdown, char *buf, size_t len)
+{
+ if (shutdown == 0) {
+ snprintf(buf, len, " ");
+ return buf;
+ } else if (shutdown == 1) {
+ snprintf(buf, len, "receive shutdown");
+ return buf;
+ } else if (shutdown == 2) {
+ snprintf(buf, len, "send shutdown");
+ return buf;
+ }
+
+ return NULL;
+}
+
+static const struct trans_tbl idiag_exts[] = {
+ __ADD(IDIAG_ATTR_NONE, none)
+ __ADD(IDIAG_ATTR_MEMINFO, meminfo)
+ __ADD(IDIAG_ATTR_INFO, info)
+ __ADD(IDIAG_ATTR_VEGASINFO, vegasinfo)
+ __ADD(IDIAG_ATTR_CONG, congestion)
+ __ADD(IDIAG_ATTR_TOS, tos)
+ __ADD(IDIAG_ATTR_TCLASS, tclass)
+};
+
+/**
+ * Convert inet diag extension flags to a string.
+ * @arg attrs inet diag extension flags (e.g., (IDIAG_ATTR_MEMINFO |
+ * IDIAG_ATTR_CONG | IDIAG_ATTR_TOS))
+ * @arg buf Output buffer to hold string representation
+ * @arg len length in bytes of the output buffer
+ */
+char *idiagnl_exts2str(uint8_t attrs, char *buf, size_t len)
+{
+ return __flags2str(attrs, buf, len, idiag_exts, ARRAY_SIZE(idiag_exts));
+}
+
+/** @} */
+/** @} */
diff --git a/lib/idiag/idiag_meminfo_obj.c b/lib/idiag/idiag_meminfo_obj.c
new file mode 100644
index 00000000..a60f4979
--- /dev/null
+++ b/lib/idiag/idiag_meminfo_obj.c
@@ -0,0 +1,100 @@
+/*
+ * lib/idiag/idiagnl_meminfo_obj.c Inet Diag Meminfo 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) 2013 Sassano Systems LLC <joe@sassanosystems.com>
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/idiag/meminfo.h>
+
+/**
+ * @ingroup idiag
+ * @defgroup idiagnl_meminfo Inet Diag Memory Info
+ *
+ * @details
+ * @idiagnl_doc{idiagnl_meminfo, Inet Diag Memory Info Documentation}
+ * @{
+ */
+struct idiagnl_meminfo *idiagnl_meminfo_alloc(void)
+{
+ return (struct idiagnl_meminfo *) nl_object_alloc(&idiagnl_meminfo_obj_ops);
+}
+
+void idiagnl_meminfo_get(struct idiagnl_meminfo *minfo)
+{
+ nl_object_get((struct nl_object *) minfo);
+}
+
+void idiagnl_meminfo_put(struct idiagnl_meminfo *minfo)
+{
+ nl_object_put((struct nl_object *) minfo);
+}
+
+/**
+ * @name Attributes
+ * @{
+ */
+uint32_t idiagnl_meminfo_get_rmem(const struct idiagnl_meminfo *minfo)
+{
+ return minfo->idiag_rmem;
+}
+
+void idiagnl_meminfo_set_rmem(struct idiagnl_meminfo *minfo, uint32_t rmem)
+{
+ minfo->idiag_rmem = rmem;
+}
+
+uint32_t idiagnl_meminfo_get_wmem(const struct idiagnl_meminfo *minfo)
+{
+ return minfo->idiag_wmem;
+}
+
+void idiagnl_meminfo_set_wmem(struct idiagnl_meminfo *minfo, uint32_t wmem)
+{
+ minfo->idiag_wmem = wmem;
+}
+
+uint32_t idiagnl_meminfo_get_fmem(const struct idiagnl_meminfo *minfo)
+{
+ return minfo->idiag_fmem;
+}
+
+void idiagnl_meminfo_set_fmem(struct idiagnl_meminfo *minfo, uint32_t fmem)
+{
+ minfo->idiag_fmem = fmem;
+}
+
+uint32_t idiagnl_meminfo_get_tmem(const struct idiagnl_meminfo *minfo)
+{
+ return minfo->idiag_tmem;
+}
+
+void idiagnl_meminfo_set_tmem(struct idiagnl_meminfo *minfo, uint32_t tmem)
+{
+ minfo->idiag_tmem = tmem;
+}
+/** @} */
+
+static int idiagnl_meminfo_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+ struct idiagnl_meminfo *dst = (struct idiagnl_meminfo *) _dst;
+ struct idiagnl_meminfo *src = (struct idiagnl_meminfo *) _src;
+
+ memcpy(dst, src, sizeof(struct idiagnl_meminfo));
+
+ return 0;
+}
+
+/** @cond SKIP */
+struct nl_object_ops idiagnl_meminfo_obj_ops = {
+ .oo_name = "idiag/idiag_meminfo",
+ .oo_size = sizeof(struct idiagnl_meminfo),
+ .oo_clone = idiagnl_meminfo_clone,
+};
+/** @endcond */
+/** @} */
diff --git a/lib/idiag/idiag_msg_obj.c b/lib/idiag/idiag_msg_obj.c
new file mode 100644
index 00000000..19e6c5be
--- /dev/null
+++ b/lib/idiag/idiag_msg_obj.c
@@ -0,0 +1,729 @@
+/*
+ * lib/idiag/idiagnl_msg_obj.c Inet Diag Message 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) 2013 Sassano Systems LLC <joe@sassanosystems.com>
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/idiag/msg.h>
+#include <netlink/idiag/meminfo.h>
+#include <netlink/idiag/vegasinfo.h>
+#include <linux/inet_diag.h>
+
+/**
+ * @ingroup idiag
+ * @defgroup idiagnl_msg Inet Diag Messages
+ *
+ * @details
+ * @idiagnl_doc{idiagnl_msg, Inet Diag Message Documentation}
+ * @{
+ */
+struct idiagnl_msg *idiagnl_msg_alloc(void)
+{
+ return (struct idiagnl_msg *) nl_object_alloc(&idiagnl_msg_obj_ops);
+}
+
+void idiagnl_msg_get(struct idiagnl_msg *msg)
+{
+ nl_object_get((struct nl_object *) msg);
+}
+
+void idiagnl_msg_put(struct idiagnl_msg *msg)
+{
+ nl_object_put((struct nl_object *) msg);
+}
+
+static struct nl_cache_ops idiagnl_msg_ops;
+
+static int idiagnl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+ struct nlmsghdr *nlh, struct nl_parser_param *pp)
+{
+ struct idiagnl_msg *msg = NULL;
+ int err = 0;
+
+ if ((err = idiagnl_msg_parse(nlh, &msg)) < 0)
+ return err;
+
+ err = pp->pp_cb((struct nl_object *) msg, pp);
+ idiagnl_msg_put(msg);
+
+ return err;
+}
+
+static int idiagnl_request_update(struct nl_cache *cache, struct nl_sock *sk)
+{
+ int family = cache->c_iarg1;
+ int states = cache->c_iarg2;
+
+ return idiagnl_send_simple(sk, 0, family, states, IDIAG_ATTR_ALL);
+}
+
+static struct nl_cache_ops idiagnl_msg_ops = {
+ .co_name = "idiag/idiag",
+ .co_hdrsize = sizeof(struct inet_diag_msg),
+ .co_msgtypes = {
+ { IDIAG_TCPDIAG_GETSOCK, NL_ACT_NEW, "new" },
+ { IDIAG_DCCPDIAG_GETSOCK, NL_ACT_NEW, "new" },
+ END_OF_MSGTYPES_LIST,
+ },
+ .co_protocol = NETLINK_INET_DIAG,
+ .co_request_update = idiagnl_request_update,
+ .co_msg_parser = idiagnl_msg_parser,
+ .co_obj_ops = &idiagnl_msg_obj_ops,
+};
+
+static void __init idiagnl_init(void)
+{
+ nl_cache_mngt_register(&idiagnl_msg_ops);
+}
+
+static void __exit idiagnl_exit(void)
+{
+ nl_cache_mngt_unregister(&idiagnl_msg_ops);
+}
+
+/**
+ * @name Cache Management
+ * @{
+ */
+
+/**
+ * Build an inetdiag cache to hold socket state information.
+ * @arg sk Netlink socket
+ * @arg family The address family to query
+ * @arg states Socket states to query
+ * @arg result Result pointer
+ *
+ * @note The caller is responsible for destroying and free the cache after using
+ * it.
+ * @return 0 on success of a negative error code.
+ */
+int idiagnl_msg_alloc_cache(struct nl_sock *sk, int family, int states,
+ struct nl_cache **result)
+{
+ struct nl_cache *cache = NULL;
+ int err;
+
+ if (!(cache = nl_cache_alloc(&idiagnl_msg_ops)))
+ return -NLE_NOMEM;
+
+ cache->c_iarg1 = family;
+ cache->c_iarg2 = states;
+
+ if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
+ free(cache);
+ return err;
+ }
+
+ *result = cache;
+ return 0;
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+uint8_t idiagnl_msg_get_family(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_family;
+}
+
+void idiagnl_msg_set_family(struct idiagnl_msg *msg, uint8_t family)
+{
+ msg->idiag_family = family;
+}
+
+uint8_t idiagnl_msg_get_state(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_state;
+}
+
+void idiagnl_msg_set_state(struct idiagnl_msg *msg, uint8_t state)
+{
+ msg->idiag_state = state;
+}
+
+uint8_t idiagnl_msg_get_timer(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_timer;
+}
+
+void idiagnl_msg_set_timer(struct idiagnl_msg *msg, uint8_t timer)
+{
+ msg->idiag_timer = timer;
+}
+
+uint8_t idiagnl_msg_get_retrans(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_retrans;
+}
+
+void idiagnl_msg_set_retrans(struct idiagnl_msg *msg, uint8_t retrans)
+{
+ msg->idiag_retrans = retrans;
+}
+
+uint16_t idiagnl_msg_get_sport(struct idiagnl_msg *msg)
+{
+ return msg->idiag_sport;
+}
+
+void idiagnl_msg_set_sport(struct idiagnl_msg *msg, uint16_t port)
+{
+ msg->idiag_sport = port;
+}
+
+uint16_t idiagnl_msg_get_dport(struct idiagnl_msg *msg)
+{
+ return msg->idiag_dport;
+}
+
+void idiagnl_msg_set_dport(struct idiagnl_msg *msg, uint16_t port)
+{
+ msg->idiag_dport = port;
+}
+
+struct nl_addr *idiagnl_msg_get_src(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_src;
+}
+
+int idiagnl_msg_set_src(struct idiagnl_msg *msg, struct nl_addr *addr)
+{
+ if (msg->idiag_src)
+ nl_addr_put(msg->idiag_src);
+
+ nl_addr_get(addr);
+ msg->idiag_src = addr;
+
+ return 0;
+}
+
+struct nl_addr *idiagnl_msg_get_dst(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_dst;
+}
+
+int idiagnl_msg_set_dst(struct idiagnl_msg *msg, struct nl_addr *addr)
+{
+ if (msg->idiag_dst)
+ nl_addr_put(msg->idiag_dst);
+
+ nl_addr_get(addr);
+ msg->idiag_dst = addr;
+
+ return 0;
+}
+
+uint32_t idiagnl_msg_get_ifindex(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_ifindex;
+}
+
+void idiagnl_msg_set_ifindex(struct idiagnl_msg *msg, uint32_t ifindex)
+{
+ msg->idiag_ifindex = ifindex;
+}
+
+uint32_t idiagnl_msg_get_expires(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_expires;
+}
+
+void idiagnl_msg_set_expires(struct idiagnl_msg *msg, uint32_t expires)
+{
+ msg->idiag_expires = expires;
+}
+
+uint32_t idiagnl_msg_get_rqueue(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_rqueue;
+}
+
+void idiagnl_msg_set_rqueue(struct idiagnl_msg *msg, uint32_t rqueue)
+{
+ msg->idiag_rqueue = rqueue;
+}
+
+uint32_t idiagnl_msg_get_wqueue(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_wqueue;
+}
+
+void idiagnl_msg_set_wqueue(struct idiagnl_msg *msg, uint32_t wqueue)
+{
+ msg->idiag_wqueue = wqueue;
+}
+
+uint32_t idiagnl_msg_get_uid(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_uid;
+}
+
+void idiagnl_msg_set_uid(struct idiagnl_msg *msg, uint32_t uid)
+{
+ msg->idiag_uid = uid;
+}
+
+uint32_t idiagnl_msg_get_inode(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_inode;
+}
+
+void idiagnl_msg_set_inode(struct idiagnl_msg *msg, uint32_t inode)
+{
+ msg->idiag_inode = inode;
+}
+
+uint8_t idiagnl_msg_get_tos(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_tos;
+}
+
+void idiagnl_msg_set_tos(struct idiagnl_msg *msg, uint8_t tos)
+{
+ msg->idiag_tos = tos;
+}
+
+uint8_t idiagnl_msg_get_tclass(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_tclass;
+}
+
+void idiagnl_msg_set_tclass(struct idiagnl_msg *msg, uint8_t tclass)
+{
+ msg->idiag_tclass = tclass;
+}
+
+uint8_t idiagnl_msg_get_shutdown(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_shutdown;
+}
+
+void idiagnl_msg_set_shutdown(struct idiagnl_msg *msg, uint8_t shutdown)
+{
+ msg->idiag_shutdown = shutdown;
+}
+
+char *idiagnl_msg_get_cong(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_cong;
+}
+
+void idiagnl_msg_set_cong(struct idiagnl_msg *msg, char *cong)
+{
+ msg->idiag_cong = strdup(cong);
+}
+
+struct idiagnl_meminfo *idiagnl_msg_get_meminfo(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_meminfo;
+}
+
+void idiagnl_msg_set_meminfo(struct idiagnl_msg *msg, struct idiagnl_meminfo
+ *minfo)
+{
+ if (msg->idiag_meminfo)
+ idiagnl_meminfo_put(msg->idiag_meminfo);
+
+ idiagnl_meminfo_get(minfo);
+ msg->idiag_meminfo = minfo;
+}
+
+struct idiagnl_vegasinfo *idiagnl_msg_get_vegasinfo(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_vegasinfo;
+}
+
+void idiagnl_msg_set_vegasinfo(struct idiagnl_msg *msg, struct idiagnl_vegasinfo
+ *vinfo)
+{
+ if (msg->idiag_vegasinfo)
+ idiagnl_vegasinfo_put(msg->idiag_vegasinfo);
+
+ idiagnl_vegasinfo_get(vinfo);
+ msg->idiag_vegasinfo = vinfo;
+}
+
+struct tcp_info idiagnl_msg_get_tcpinfo(const struct idiagnl_msg *msg)
+{
+ return msg->idiag_tcpinfo;
+}
+
+void idiagnl_msg_set_tcpinfo(struct idiagnl_msg *msg, struct tcp_info *tinfo)
+{
+ memcpy(&msg->idiag_tcpinfo, tinfo, sizeof(struct tcp_info));
+}
+
+/** @} */
+
+static void idiag_msg_dump_line(struct nl_object *a, struct nl_dump_params *p)
+{
+ struct idiagnl_msg *msg = (struct idiagnl_msg *) a;
+ char buf[64] = { 0 };
+
+ nl_dump_line(p, "family: %s ", nl_af2str(msg->idiag_family, buf, sizeof(buf)));
+ nl_dump(p, "src: %s:%d ", nl_addr2str(msg->idiag_src, buf, sizeof(buf)),
+ ntohs(msg->idiag_sport));
+ nl_dump(p, "dst: %s:%d ", nl_addr2str(msg->idiag_dst, buf, sizeof(buf)),
+ ntohs(msg->idiag_dport));
+ nl_dump(p, "iif: %d ", msg->idiag_ifindex);
+ nl_dump(p, "\n");
+}
+
+static void idiag_msg_dump_details(struct nl_object *a, struct nl_dump_params *p)
+{
+ struct idiagnl_msg *msg = (struct idiagnl_msg *) a;
+ char buf[64], buf2[64];
+
+ nl_dump(p, "\nfamily: %s\n", nl_af2str(msg->idiag_family, buf, sizeof(buf)));
+ nl_dump(p, "state: %s\n",
+ idiagnl_state2str(msg->idiag_state, buf, sizeof(buf)));
+ nl_dump(p, "timer (%s, %s, retransmits: %d)\n",
+ idiagnl_timer2str(msg->idiag_timer, buf, sizeof(buf)),
+ nl_msec2str(msg->idiag_expires, buf2, sizeof(buf2)),
+ msg->idiag_retrans);
+
+ nl_dump(p, "source: %s:%d\n", nl_addr2str(msg->idiag_src, buf, sizeof(buf)),
+ ntohs(msg->idiag_sport));
+ nl_dump(p, "destination: %s:%d\n", nl_addr2str(msg->idiag_dst, buf, sizeof(buf)),
+ ntohs(msg->idiag_dport));
+
+ nl_dump(p, "ifindex: %d\n", msg->idiag_ifindex);
+ nl_dump(p, "rqueue: %-6d wqueue: %-6d\n", msg->idiag_rqueue, msg->idiag_wqueue);
+ nl_dump(p, "uid %d\n", msg->idiag_uid);
+ nl_dump(p, "inode %d\n", msg->idiag_inode);
+ if (msg->idiag_shutdown) {
+ nl_dump(p, "socket shutdown: %s\n",
+ idiagnl_shutdown2str(msg->idiag_shutdown,
+ buf, sizeof(buf)));
+ }
+
+ nl_dump(p, "tos: 0x%x\n", msg->idiag_tos);
+ nl_dump(p, "traffic class: %d\n", msg->idiag_tclass);
+ nl_dump(p, "congestion algorithm: %s\n", msg->idiag_cong);
+}
+
+static void idiag_msg_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
+{
+ struct idiagnl_msg *msg = (struct idiagnl_msg *) obj;
+ char buf[64];
+
+ idiag_msg_dump_details(obj, p);
+
+ nl_dump(p, "tcp info: [\n");
+ nl_dump(p, "\tsocket state: %s\n",
+ idiagnl_state2str(msg->idiag_tcpinfo.tcpi_state,
+ buf, sizeof(buf)));
+ nl_dump(p, "\ttcp state: %s\n",
+ idiagnl_tcpstate2str(msg->idiag_tcpinfo.tcpi_ca_state,
+ buf, sizeof(buf)));
+ nl_dump(p, "\tretransmits: %d\n",
+ msg->idiag_tcpinfo.tcpi_retransmits);
+ nl_dump(p, "\tprobes: %d\n",
+ msg->idiag_tcpinfo.tcpi_probes);
+ nl_dump(p, "\tbackoff: %d\n",
+ msg->idiag_tcpinfo.tcpi_backoff);
+ nl_dump(p, "\toptions: %s\n",
+ idiagnl_tcpopts2str(msg->idiag_tcpinfo.tcpi_options,
+ buf, sizeof(buf)));
+ nl_dump(p, "\tsnd_wscale: %d\n", msg->idiag_tcpinfo.tcpi_snd_wscale);
+ nl_dump(p, "\trcv_wscale: %d\n", msg->idiag_tcpinfo.tcpi_rcv_wscale);
+ nl_dump(p, "\trto: %d\n", msg->idiag_tcpinfo.tcpi_rto);
+ nl_dump(p, "\tato: %d\n", msg->idiag_tcpinfo.tcpi_ato);
+ nl_dump(p, "\tsnd_mss: %s\n", nl_size2str(msg->idiag_tcpinfo.tcpi_snd_mss,
+ buf, sizeof(buf)));
+ nl_dump(p, "\trcv_mss: %s\n", nl_size2str(msg->idiag_tcpinfo.tcpi_rcv_mss,
+ buf, sizeof(buf)));
+ nl_dump(p, "\tunacked: %d\n", msg->idiag_tcpinfo.tcpi_unacked);
+ nl_dump(p, "\tsacked: %d\n", msg->idiag_tcpinfo.tcpi_sacked);
+
+ nl_dump(p, "\tlost: %d\n", msg->idiag_tcpinfo.tcpi_lost);
+ nl_dump(p, "\tretransmit segments: %d\n",
+ msg->idiag_tcpinfo.tcpi_retrans);
+ nl_dump(p, "\tfackets: %d\n",
+ msg->idiag_tcpinfo.tcpi_fackets);
+ nl_dump(p, "\tlast data sent: %s\n",
+ nl_msec2str(msg->idiag_tcpinfo.tcpi_last_data_sent, buf,
+ sizeof(buf)));
+ nl_dump(p, "\tlast ack sent: %s\n",
+ nl_msec2str(msg->idiag_tcpinfo.tcpi_last_ack_sent, buf, sizeof(buf)));
+ nl_dump(p, "\tlast data recv: %s\n",
+ nl_msec2str(msg->idiag_tcpinfo.tcpi_last_data_recv, buf,
+ sizeof(buf)));
+ nl_dump(p, "\tlast ack recv: %s\n",
+ nl_msec2str(msg->idiag_tcpinfo.tcpi_last_ack_recv, buf,
+ sizeof(buf)));
+ nl_dump(p, "\tpath mtu: %s\n",
+ nl_size2str(msg->idiag_tcpinfo.tcpi_pmtu, buf,
+ sizeof(buf)));
+ nl_dump(p, "\trcv ss threshold: %d\n",
+ msg->idiag_tcpinfo.tcpi_rcv_ssthresh);
+ nl_dump(p, "\tsmoothed round trip time: %d\n",
+ msg->idiag_tcpinfo.tcpi_rtt);
+ nl_dump(p, "\tround trip time variation: %d\n",
+ msg->idiag_tcpinfo.tcpi_rttvar);
+ nl_dump(p, "\tsnd ss threshold: %s\n",
+ nl_size2str(msg->idiag_tcpinfo.tcpi_snd_ssthresh, buf,
+ sizeof(buf)));
+ nl_dump(p, "\tsend congestion window: %d\n",
+ msg->idiag_tcpinfo.tcpi_snd_cwnd);
+ nl_dump(p, "\tadvertised mss: %s\n",
+ nl_size2str(msg->idiag_tcpinfo.tcpi_advmss, buf,
+ sizeof(buf)));
+ nl_dump(p, "\treordering: %d\n",
+ msg->idiag_tcpinfo.tcpi_reordering);
+ nl_dump(p, "\trcv rround trip time: %d\n",
+ msg->idiag_tcpinfo.tcpi_rcv_rtt);
+ nl_dump(p, "\treceive queue space: %s\n",
+ nl_size2str(msg->idiag_tcpinfo.tcpi_rcv_space, buf,
+ sizeof(buf)));
+ nl_dump(p, "\ttotal retransmits: %d\n",
+ msg->idiag_tcpinfo.tcpi_total_retrans);
+ nl_dump(p, "]\n");
+
+ if (msg->idiag_meminfo) {
+ nl_dump(p, "meminfo: [\n");
+ nl_dump(p, "\trmem: %s\n",
+ nl_size2str(msg->idiag_meminfo->idiag_rmem,
+ buf,
+ sizeof(buf)));
+ nl_dump(p, "\twmem: %s\n",
+ nl_size2str(msg->idiag_meminfo->idiag_wmem,
+ buf,
+ sizeof(buf)));
+ nl_dump(p, "\tfmem: %s\n",
+ nl_size2str(msg->idiag_meminfo->idiag_fmem,
+ buf,
+ sizeof(buf)));
+ nl_dump(p, "\ttmem: %s\n",
+ nl_size2str(msg->idiag_meminfo->idiag_tmem,
+ buf,
+ sizeof(buf)));
+ nl_dump(p, "]\n");
+ }
+
+ if (msg->idiag_vegasinfo) {
+ nl_dump(p, "vegasinfo: [\n");
+ nl_dump(p, "\tvegas enabled: %d\n",
+ msg->idiag_vegasinfo->tcpv_enabled);
+ if (msg->idiag_vegasinfo->tcpv_enabled) {
+ nl_dump(p, "\trtt cnt: %d",
+ msg->idiag_vegasinfo->tcpv_rttcnt);
+ nl_dump(p, "\trtt (propagation delay): %d",
+ msg->idiag_vegasinfo->tcpv_rtt);
+ nl_dump(p, "\tmin rtt: %d",
+ msg->idiag_vegasinfo->tcpv_minrtt);
+ }
+ nl_dump(p, "]\n");
+ }
+
+ nl_dump(p, "skmeminfo: [\n");
+ nl_dump(p, "\trmem alloc: %d\n",
+ msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_RMEM_ALLOC]);
+ nl_dump(p, "\trcv buf: %s\n",
+ nl_size2str(msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_RCVBUF],
+ buf, sizeof(buf)));
+ nl_dump(p, "\twmem alloc: %d\n",
+ msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_WMEM_ALLOC]);
+ nl_dump(p, "\tsnd buf: %s\n",
+ nl_size2str(msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_SNDBUF],
+ buf, sizeof(buf)));
+ nl_dump(p, "\tfwd alloc: %d\n",
+ msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_FWD_ALLOC]);
+ nl_dump(p, "\twmem queued: %s\n",
+ nl_size2str(msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_WMEM_QUEUED],
+ buf, sizeof(buf)));
+ nl_dump(p, "\topt mem: %d\n",
+ msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_OPTMEM]);
+ nl_dump(p, "\tbacklog: %d\n",
+ msg->idiag_skmeminfo[IDIAG_SK_MEMINFO_BACKLOG]);
+ nl_dump(p, "]\n\n");
+}
+
+static void idiagnl_msg_free(struct nl_object *a)
+{
+ struct idiagnl_msg *msg = (struct idiagnl_msg *) a;
+ if (a == NULL)
+ return;
+
+ free(msg->idiag_cong);
+ nl_addr_put(msg->idiag_src);
+ nl_addr_put(msg->idiag_dst);
+ idiagnl_meminfo_put(msg->idiag_meminfo);
+ idiagnl_vegasinfo_put(msg->idiag_vegasinfo);
+}
+
+static int idiagnl_msg_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+ struct idiagnl_msg *dst = (struct idiagnl_msg *) _dst;
+ struct idiagnl_msg *src = (struct idiagnl_msg *) _src;
+
+ if (src->idiag_src)
+ if (!(dst->idiag_src = nl_addr_clone(src->idiag_src)))
+ return -NLE_NOMEM;
+
+ if (src->idiag_dst)
+ if (!(dst->idiag_dst = nl_addr_clone(src->idiag_dst)))
+ return -NLE_NOMEM;
+
+ return 0;
+}
+
+static struct nla_policy ext_policy[IDIAG_ATTR_MAX] = {
+ [IDIAG_ATTR_MEMINFO] = { .minlen = sizeof(struct inet_diag_meminfo) },
+ [IDIAG_ATTR_INFO] = { .minlen = sizeof(struct tcp_info) },
+ [IDIAG_ATTR_VEGASINFO] = { .minlen = sizeof(struct tcpvegas_info) },
+ [IDIAG_ATTR_CONG] = { .type = NLA_STRING },
+ [IDIAG_ATTR_TOS] = { .type = NLA_U8 },
+ [IDIAG_ATTR_TCLASS] = { .type = NLA_U8 },
+ [IDIAG_ATTR_SKMEMINFO] = { .minlen = (sizeof(uint32_t) * IDIAG_SK_MEMINFO_VARS) },
+ [IDIAG_ATTR_SHUTDOWN] = { .type = NLA_U8 },
+};
+
+int idiagnl_msg_parse(struct nlmsghdr *nlh, struct idiagnl_msg **result)
+{
+ struct idiagnl_msg *msg = NULL;
+ struct inet_diag_msg *raw_msg = NULL;
+ struct nl_addr *src = NULL, *dst = NULL;
+ struct nlattr *tb[IDIAG_ATTR_MAX];
+ int err = 0;
+
+ msg = idiagnl_msg_alloc();
+ if (!msg)
+ goto errout_nomem;
+
+ err = nlmsg_parse(nlh, sizeof(struct inet_diag_msg), tb, IDIAG_ATTR_MAX,
+ ext_policy);
+ if (err < 0)
+ goto errout;
+
+ raw_msg = nlmsg_data(nlh);
+ msg->idiag_family = raw_msg->idiag_family;
+ msg->idiag_state = raw_msg->idiag_state;
+ msg->idiag_timer = raw_msg->idiag_timer;
+ msg->idiag_retrans = raw_msg->idiag_retrans;
+ msg->idiag_expires = raw_msg->idiag_expires;
+ msg->idiag_rqueue = raw_msg->idiag_rqueue;
+ msg->idiag_wqueue = raw_msg->idiag_wqueue;
+ msg->idiag_uid = raw_msg->idiag_uid;
+ msg->idiag_inode = raw_msg->idiag_inode;
+ msg->idiag_sport = raw_msg->id.idiag_sport;
+ msg->idiag_dport = raw_msg->id.idiag_dport;
+ msg->idiag_ifindex = raw_msg->id.idiag_if;
+
+ dst = nl_addr_build(raw_msg->idiag_family, raw_msg->id.idiag_dst,
+ sizeof(raw_msg->id.idiag_dst));
+ if (!dst)
+ goto errout_nomem;
+
+ err = idiagnl_msg_set_dst(msg, dst);
+ if (err < 0)
+ goto errout;
+
+ nl_addr_put(dst);
+
+ src = nl_addr_build(raw_msg->idiag_family, raw_msg->id.idiag_src,
+ sizeof(raw_msg->id.idiag_src));
+ if (!src)
+ goto errout_nomem;
+
+ err = idiagnl_msg_set_src(msg, src);
+ if (err < 0)
+ goto errout;
+
+ nl_addr_put(src);
+
+ if (tb[IDIAG_ATTR_TOS])
+ msg->idiag_tos = nla_get_u8(tb[IDIAG_ATTR_TOS]);
+
+ if (tb[IDIAG_ATTR_TCLASS])
+ msg->idiag_tclass = nla_get_u8(tb[IDIAG_ATTR_TCLASS]);
+
+ if (tb[IDIAG_ATTR_SHUTDOWN])
+ msg->idiag_shutdown = nla_get_u8(tb[IDIAG_ATTR_SHUTDOWN]);
+
+ if (tb[IDIAG_ATTR_CONG])
+ msg->idiag_cong = nla_strdup(tb[IDIAG_ATTR_CONG]);
+
+ if (tb[IDIAG_ATTR_INFO])
+ nla_memcpy(&msg->idiag_tcpinfo, tb[IDIAG_ATTR_INFO],
+ sizeof(msg->idiag_tcpinfo));
+
+ if (tb[IDIAG_ATTR_MEMINFO]) {
+ struct idiagnl_meminfo *minfo = idiagnl_meminfo_alloc();
+ struct inet_diag_meminfo *raw_minfo = NULL;
+
+ if (!minfo)
+ goto errout_nomem;
+
+ raw_minfo = (struct inet_diag_meminfo *)
+ nla_data(tb[IDIAG_ATTR_MEMINFO]);
+
+ idiagnl_meminfo_set_rmem(minfo, raw_minfo->idiag_rmem);
+ idiagnl_meminfo_set_wmem(minfo, raw_minfo->idiag_wmem);
+ idiagnl_meminfo_set_fmem(minfo, raw_minfo->idiag_fmem);
+ idiagnl_meminfo_set_tmem(minfo, raw_minfo->idiag_tmem);
+
+ msg->idiag_meminfo = minfo;
+ }
+
+ if (tb[IDIAG_ATTR_VEGASINFO]) {
+ struct idiagnl_vegasinfo *vinfo = idiagnl_vegasinfo_alloc();
+ struct tcpvegas_info *raw_vinfo = NULL;
+
+ if (!vinfo)
+ goto errout_nomem;
+
+ raw_vinfo = (struct tcpvegas_info *)
+ nla_data(tb[IDIAG_ATTR_VEGASINFO]);
+
+ idiagnl_vegasinfo_set_enabled(vinfo, raw_vinfo->tcpv_enabled);
+ idiagnl_vegasinfo_set_rttcnt(vinfo, raw_vinfo->tcpv_rttcnt);
+ idiagnl_vegasinfo_set_rtt(vinfo, raw_vinfo->tcpv_rtt);
+ idiagnl_vegasinfo_set_minrtt(vinfo, raw_vinfo->tcpv_minrtt);
+
+ msg->idiag_vegasinfo = vinfo;
+ }
+
+ if (tb[IDIAG_ATTR_SKMEMINFO])
+ nla_memcpy(&msg->idiag_skmeminfo, tb[IDIAG_ATTR_SKMEMINFO],
+ sizeof(msg->idiag_skmeminfo));
+
+ *result = msg;
+ return 0;
+
+errout:
+ idiagnl_msg_put(msg);
+ return err;
+
+errout_nomem:
+ err = -NLE_NOMEM;
+ goto errout;
+}
+
+/** @cond SKIP */
+struct nl_object_ops idiagnl_msg_obj_ops = {
+ .oo_name = "idiag/idiag_msg",
+ .oo_size = sizeof(struct idiagnl_msg),
+ .oo_free_data = idiagnl_msg_free,
+ .oo_clone = idiagnl_msg_clone,
+ .oo_dump = {
+ [NL_DUMP_LINE] = idiag_msg_dump_line,
+ [NL_DUMP_DETAILS] = idiag_msg_dump_details,
+ [NL_DUMP_STATS] = idiag_msg_dump_stats,
+ },
+ .oo_attrs2str = idiagnl_attrs2str,
+ .oo_id_attrs = (IDIAG_ATTR_INFO)
+};
+/** @endcond */
+
+/** @} */
diff --git a/lib/idiag/idiag_req_obj.c b/lib/idiag/idiag_req_obj.c
new file mode 100644
index 00000000..d9dab8e8
--- /dev/null
+++ b/lib/idiag/idiag_req_obj.c
@@ -0,0 +1,255 @@
+/*
+ * lib/idiag/idiagnl_req_obj.c Inet Diag Request 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) 2013 Sassano Systems LLC <joe@sassanosystems.com>
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/idiag/req.h>
+#include <linux/inet_diag.h>
+
+/**
+ * @ingroup idiag
+ * @defgroup idiagnl_req Inet Diag Requests
+ *
+ * @details
+ * @idiagnl_doc{idiagnl_req, Inet Diag Request Documentation}
+ * @{
+ */
+struct idiagnl_req *idiagnl_req_alloc(void)
+{
+ return (struct idiagnl_req *) nl_object_alloc(&idiagnl_req_obj_ops);
+}
+
+void idiagnl_req_get(struct idiagnl_req *req)
+{
+ nl_object_get((struct nl_object *) req);
+}
+
+void idiagnl_req_put(struct idiagnl_req *req)
+{
+ nl_object_put((struct nl_object *) req);
+}
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+uint8_t idiagnl_req_get_family(const struct idiagnl_req *req)
+{
+ return req->idiag_family;
+}
+
+void idiagnl_req_set_family(struct idiagnl_req *req, uint8_t family)
+{
+ req->idiag_family = family;
+}
+
+uint8_t idiagnl_req_get_ext(const struct idiagnl_req *req)
+{
+ return req->idiag_ext;
+}
+
+void idiagnl_req_set_ext(struct idiagnl_req *req, uint8_t ext)
+{
+ req->idiag_ext = ext;
+}
+
+uint32_t idiagnl_req_get_ifindex(const struct idiagnl_req *req)
+{
+ return req->idiag_ifindex;
+}
+
+void idiagnl_req_set_ifindex(struct idiagnl_req *req, uint32_t ifindex)
+{
+ req->idiag_states = ifindex;
+}
+
+uint32_t idiagnl_req_get_states(const struct idiagnl_req *req)
+{
+ return req->idiag_states;
+}
+
+void idiagnl_req_set_states(struct idiagnl_req *req, uint32_t states)
+{
+ req->idiag_states = states;
+}
+
+uint32_t idiagnl_req_get_dbs(const struct idiagnl_req *req)
+{
+ return req->idiag_dbs;
+}
+
+void idiagnl_req_set_dbs(struct idiagnl_req *req, uint32_t dbs)
+{
+ req->idiag_dbs = dbs;
+}
+
+struct nl_addr *idiagnl_req_get_src(const struct idiagnl_req *req)
+{
+ return req->idiag_src;
+}
+
+int idiagnl_req_set_src(struct idiagnl_req *req, struct nl_addr *addr)
+{
+ if (req->idiag_src)
+ nl_addr_put(req->idiag_src);
+
+ nl_addr_get(addr);
+ req->idiag_src = addr;
+
+ return 0;
+}
+
+struct nl_addr *idiagnl_req_get_dst(const struct idiagnl_req *req)
+{
+ return req->idiag_dst;
+}
+
+int idiagnl_req_set_dst(struct idiagnl_req *req, struct nl_addr *addr)
+{
+ if (req->idiag_dst)
+ nl_addr_put(req->idiag_dst);
+
+ nl_addr_get(addr);
+ req->idiag_dst = addr;
+
+ return 0;
+}
+
+/** @} */
+
+static void idiag_req_dump_line(struct nl_object *a, struct nl_dump_params *p)
+{
+ struct idiagnl_req *req = (struct idiagnl_req *) a;
+ char buf[64] = { 0 };
+
+ nl_dump_line(p, "%s ", nl_af2str(req->idiag_family, buf, sizeof(buf)));
+ nl_dump(p, "src %s ", nl_addr2str(req->idiag_src, buf, sizeof(buf)));
+ nl_dump(p, "dst %s ", nl_addr2str(req->idiag_dst, buf, sizeof(buf)));
+ nl_dump(p, "iif %d ", req->idiag_ifindex);
+ nl_dump(p, "\n");
+}
+
+static void idiag_req_dump_details(struct nl_object *a, struct nl_dump_params *p)
+{
+ struct idiagnl_req *req = (struct idiagnl_req *) a;
+ char buf[64];
+
+ nl_dump_line(p, " ");
+ nl_dump(p, "%s ", nl_af2str(req->idiag_family, buf, sizeof(buf)));
+ nl_dump(p, "exts %s ",
+ idiagnl_exts2str(req->idiag_ext, buf, sizeof(buf)));
+ nl_dump(p, "src %s ", nl_addr2str(req->idiag_src, buf, sizeof(buf)));
+ nl_dump(p, "dst %s ", nl_addr2str(req->idiag_dst, buf, sizeof(buf)));
+ nl_dump(p, "iif %d ", req->idiag_ifindex);
+ nl_dump(p, "states %s ", idiagnl_state2str(req->idiag_states, buf,
+ sizeof(buf)));
+ nl_dump(p, "dbs %d", req->idiag_dbs);
+ nl_dump(p, "\n");
+}
+
+static void idiag_req_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
+{
+ idiag_req_dump_details(obj, p);
+}
+
+static void idiagnl_req_free(struct nl_object *a)
+{
+ struct idiagnl_req *req = (struct idiagnl_req *) a;
+ if (a == NULL)
+ return;
+
+ nl_addr_put(req->idiag_src);
+ nl_addr_put(req->idiag_dst);
+}
+
+static int idiagnl_req_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+ struct idiagnl_req *dst = (struct idiagnl_req *) _dst;
+ struct idiagnl_req *src = (struct idiagnl_req *) _src;
+
+ if (src->idiag_src)
+ if (!(dst->idiag_src = nl_addr_clone(src->idiag_src)))
+ return -NLE_NOMEM;
+
+ if (src->idiag_dst)
+ if (!(dst->idiag_dst = nl_addr_clone(src->idiag_dst)))
+ return -NLE_NOMEM;
+
+ return 0;
+}
+
+int idiagnl_req_parse(struct nlmsghdr *nlh, struct idiagnl_req **result)
+{
+ struct idiagnl_req *req = NULL;
+ struct inet_diag_req *raw_req = NULL;
+ struct nl_addr *src = NULL, *dst = NULL;
+ int err = 0;
+
+ req = idiagnl_req_alloc();
+ if (!req)
+ goto errout_nomem;
+
+ raw_req = nlmsg_data(nlh);
+ req->idiag_family = raw_req->idiag_family;
+ req->idiag_ext = raw_req->idiag_ext;
+ req->idiag_states = raw_req->idiag_states;
+ req->idiag_dbs = raw_req->idiag_dbs;
+ req->idiag_ifindex = raw_req->id.idiag_if;
+
+ dst = nl_addr_build(raw_req->idiag_family, raw_req->id.idiag_dst,
+ sizeof(raw_req->id.idiag_dst));
+ if (!dst)
+ goto errout_nomem;
+
+ err = idiagnl_req_set_dst(req, dst);
+ if (err < 0)
+ goto errout;
+
+ nl_addr_put(dst);
+
+ src = nl_addr_build(raw_req->idiag_family, raw_req->id.idiag_src,
+ sizeof(raw_req->id.idiag_src));
+ if (!src)
+ goto errout_nomem;
+
+ err = idiagnl_req_set_src(req, src);
+ if (err < 0)
+ goto errout;
+
+ nl_addr_put(src);
+
+ *result = req;
+ return 0;
+
+errout:
+ idiagnl_req_put(req);
+ return err;
+
+errout_nomem:
+ err = -NLE_NOMEM;
+ goto errout;
+}
+
+/** @cond SKIP */
+struct nl_object_ops idiagnl_req_obj_ops = {
+ .oo_name = "idiag/idiag_req",
+ .oo_size = sizeof(struct idiagnl_req),
+ .oo_free_data = idiagnl_req_free,
+ .oo_clone = idiagnl_req_clone,
+ .oo_dump = {
+ [NL_DUMP_LINE] = idiag_req_dump_line,
+ [NL_DUMP_DETAILS] = idiag_req_dump_details,
+ [NL_DUMP_STATS] = idiag_req_dump_stats,
+ },
+};
+/** @endcond */
+
+/** @} */
diff --git a/lib/idiag/idiag_vegasinfo_obj.c b/lib/idiag/idiag_vegasinfo_obj.c
new file mode 100644
index 00000000..5279e832
--- /dev/null
+++ b/lib/idiag/idiag_vegasinfo_obj.c
@@ -0,0 +1,104 @@
+/*
+ * lib/idiag/idiagnl_vegasinfo_obj.c Inet Diag TCP Vegas Info 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) 2013 Sassano Systems LLC <joe@sassanosystems.com>
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/idiag/vegasinfo.h>
+
+/**
+ * @ingroup idiag
+ * @defgroup idiagnl_vegasinfo Inet Diag TCP Vegas Info
+ *
+ * @details
+ * @idiagnl_doc{idiagnl_vegasinfo, Inet Diag TCP Vegas Info Documentation}
+ * @{
+ */
+struct idiagnl_vegasinfo *idiagnl_vegasinfo_alloc(void)
+{
+ return (struct idiagnl_vegasinfo *) nl_object_alloc(&idiagnl_vegasinfo_obj_ops);
+}
+
+void idiagnl_vegasinfo_get(struct idiagnl_vegasinfo *vinfo)
+{
+ nl_object_get((struct nl_object *) vinfo);
+}
+
+void idiagnl_vegasinfo_put(struct idiagnl_vegasinfo *vinfo)
+{
+ nl_object_put((struct nl_object *) vinfo);
+}
+
+/**
+ * @name Attributes
+ * @{
+ */
+uint32_t idiagnl_vegasinfo_get_enabled(const struct idiagnl_vegasinfo *vinfo)
+{
+ return vinfo->tcpv_enabled;
+}
+
+void idiagnl_vegasinfo_set_enabled(struct idiagnl_vegasinfo *vinfo, uint32_t
+ enabled)
+{
+ vinfo->tcpv_enabled = enabled;
+}
+
+uint32_t idiagnl_vegasinfo_get_rttcnt(const struct idiagnl_vegasinfo *vinfo)
+{
+ return vinfo->tcpv_rttcnt;
+}
+
+void idiagnl_vegasinfo_set_rttcnt(struct idiagnl_vegasinfo *vinfo, uint32_t
+ rttcnt)
+{
+ vinfo->tcpv_rttcnt = rttcnt;
+}
+
+uint32_t idiagnl_vegasinfo_get_rtt(const struct idiagnl_vegasinfo *vinfo)
+{
+ return vinfo->tcpv_rtt;
+}
+
+void idiagnl_vegasinfo_set_rtt(struct idiagnl_vegasinfo *vinfo, uint32_t rtt)
+{
+ vinfo->tcpv_rtt = rtt;
+}
+
+uint32_t idiagnl_vegasinfo_get_minrtt(const struct idiagnl_vegasinfo *vinfo)
+{
+ return vinfo->tcpv_minrtt;
+}
+
+void idiagnl_vegasinfo_set_minrtt(struct idiagnl_vegasinfo *vinfo, uint32_t
+ minrtt)
+{
+ vinfo->tcpv_minrtt = minrtt;
+}
+/** @} */
+
+static int idiagnl_vegasinfo_clone(struct nl_object *_dst,
+ struct nl_object *_src)
+{
+ struct idiagnl_vegasinfo *dst = (struct idiagnl_vegasinfo *) _dst;
+ struct idiagnl_vegasinfo *src = (struct idiagnl_vegasinfo *) _src;
+
+ memcpy(dst, src, sizeof(struct idiagnl_vegasinfo));
+
+ return 0;
+}
+
+/** @cond SKIP */
+struct nl_object_ops idiagnl_vegasinfo_obj_ops = {
+ .oo_name = "idiag/idiag_vegasinfo",
+ .oo_size = sizeof(struct idiagnl_vegasinfo),
+ .oo_clone = idiagnl_vegasinfo_clone,
+};
+/** @endcond */
+/** @} */
diff --git a/lib/msg.c b/lib/msg.c
index 9fe9d541..bcf1aa8d 100644
--- a/lib/msg.c
+++ b/lib/msg.c
@@ -6,159 +6,27 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
* @ingroup core
- * @defgroup msg Messages
+ * @defgroup msg Message Construction & Parsing
* Netlink Message Construction/Parsing Interface
*
- * The following information is partly extracted from RFC3549
- * (ftp://ftp.rfc-editor.org/in-notes/rfc3549.txt)
+ * Related sections in the development guide:
+ * - @core_doc{_message_parsing_amp_construction,Message Parsing & Construction}
*
- * @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 Example
- * @code
- * // Various methods exist to create/allocate a new netlink
- * // message.
- * //
- * // nlmsg_alloc() will allocate an empty netlink message with
- * // a maximum payload size which defaults to the page size of
- * // the system. This default size can be modified using the
- * // function nlmsg_set_default_size().
- * 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 the header values:
- * 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(). This
- * // will create a message with a maximum payload size which equals the
- * // length of the existing netlink message, therefore no more data can
- * // be appened without calling nlmsg_expand() first.
- * struct nl_msg *msg = nlmsg_convert(nlh_from_nl_sock);
- *
- * // 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);
- *
- * // Attributes may be added using the attributes interface.
- *
- * // 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
* @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/msg.h>
+ * ~~~~
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/cache.h>
@@ -178,17 +46,28 @@ static void __init init_msg_size(void)
*/
/**
- * length of netlink message not including padding
- * @arg payload length of message payload
+ * Calculates size of netlink message based on payload length.
+ * @arg payload Length of payload
+ *
+ * @return size of netlink message without padding.
*/
-int nlmsg_msg_size(int payload)
+int nlmsg_size(int payload)
{
return NLMSG_HDRLEN + payload;
}
+static int nlmsg_msg_size(int payload)
+{
+ return nlmsg_size(payload);
+}
+
/**
- * length of netlink message including padding
- * @arg payload length of message payload
+ * Calculates size of netlink message including padding based on payload length
+ * @arg payload Length of payload
+ *
+ * This function is idential to nlmsg_size() + nlmsg_padlen().
+ *
+ * @return Size of netlink message including padding.
*/
int nlmsg_total_size(int payload)
{
@@ -196,8 +75,14 @@ int nlmsg_total_size(int payload)
}
/**
- * length of padding at the message's tail
- * @arg payload length of message payload
+ * Size of padding that needs to be added at end of message
+ * @arg payload Length of payload
+ *
+ * Calculates the number of bytes of padding which is required to be added to
+ * the end of the message to ensure that the next netlink message header begins
+ * properly aligned to NLMSG_ALIGNTO.
+ *
+ * @return Number of bytes of padding needed.
*/
int nlmsg_padlen(int payload)
{
@@ -207,13 +92,15 @@ int nlmsg_padlen(int payload)
/** @} */
/**
- * @name Payload Access
+ * @name Access to Message Payload
* @{
*/
/**
- * head of message payload
- * @arg nlh netlink messsage header
+ * Return pointer to message payload
+ * @arg nlh Netlink message header
+ *
+ * @return Pointer to start of message payload.
*/
void *nlmsg_data(const struct nlmsghdr *nlh)
{
@@ -226,14 +113,21 @@ void *nlmsg_tail(const struct nlmsghdr *nlh)
}
/**
- * length of message payload
- * @arg nlh netlink message header
+ * Return length of message payload
+ * @arg nlh Netlink message header
+ *
+ * @return Length of message payload in bytes.
*/
-int nlmsg_len(const struct nlmsghdr *nlh)
+int nlmsg_datalen(const struct nlmsghdr *nlh)
{
return nlh->nlmsg_len - NLMSG_HDRLEN;
}
+static int nlmsg_len(const struct nlmsghdr *nlh)
+{
+ return nlmsg_datalen(nlh);
+}
+
/** @} */
/**
@@ -259,7 +153,7 @@ struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen)
*/
int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen)
{
- return nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen);
+ return max_t(int, nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen), 0);
}
/** @} */
@@ -368,18 +262,19 @@ static struct nl_msg *__nlmsg_alloc(size_t len)
{
struct nl_msg *nm;
+ if (len < sizeof(struct nlmsghdr))
+ len = sizeof(struct nlmsghdr);
+
nm = calloc(1, sizeof(*nm));
if (!nm)
goto errout;
nm->nm_refcnt = 1;
- nm->nm_nlh = malloc(len);
+ nm->nm_nlh = calloc(1, len);
if (!nm->nm_nlh)
goto errout;
- memset(nm->nm_nlh, 0, sizeof(struct nlmsghdr));
-
nm->nm_protocol = -1;
nm->nm_size = len;
nm->nm_nlh->nlmsg_len = nlmsg_total_size(0);
@@ -490,14 +385,11 @@ struct nl_msg *nlmsg_convert(struct nlmsghdr *hdr)
nm = __nlmsg_alloc(NLMSG_ALIGN(hdr->nlmsg_len));
if (!nm)
- goto errout;
+ return NULL;
memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len);
return nm;
-errout:
- nlmsg_free(nm);
- return NULL;
}
/**
@@ -529,8 +421,8 @@ void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad)
if (tlen > len)
memset(buf + len, 0, tlen - len);
- NL_DBG(2, "msg %p: Reserved %zu bytes, pad=%d, nlmsg_len=%d\n",
- n, len, pad, n->nm_nlh->nlmsg_len);
+ NL_DBG(2, "msg %p: Reserved %zu (%zu) bytes, pad=%d, nlmsg_len=%d\n",
+ n, tlen, len, pad, n->nm_nlh->nlmsg_len);
return buf;
}
@@ -677,8 +569,8 @@ void nlmsg_free(struct nl_msg *msg)
if (msg->nm_refcnt <= 0) {
free(msg->nm_nlh);
- free(msg);
NL_DBG(2, "msg %p: Freed\n", msg);
+ free(msg);
}
}
@@ -744,7 +636,7 @@ struct ucred *nlmsg_get_creds(struct nl_msg *msg)
* @{
*/
-static struct trans_tbl nl_msgtypes[] = {
+static const struct trans_tbl nl_msgtypes[] = {
__ADD(NLMSG_NOOP,NOOP)
__ADD(NLMSG_ERROR,ERROR)
__ADD(NLMSG_DONE,DONE)
@@ -836,14 +728,18 @@ int nl_msg_parse(struct nl_msg *msg, void (*cb)(struct nl_object *, void *),
.cb = cb,
.arg = arg,
};
+ int err;
- ops = nl_cache_ops_associate(nlmsg_get_proto(msg),
- nlmsg_hdr(msg)->nlmsg_type);
+ ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg),
+ nlmsg_hdr(msg)->nlmsg_type);
if (ops == NULL)
return -NLE_MSGTYPE_NOSUPPORT;
p.pp_arg = &x;
- return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p);
+ err = nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p);
+ nl_cache_ops_put(ops);
+
+ return err;
}
/** @} */
@@ -866,7 +762,7 @@ 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);
+ limit = 16 - (prefix * 2);
prefix_line(ofd, prefix);
fprintf(ofd, " ");
@@ -876,7 +772,7 @@ static inline void dump_hex(FILE *ofd, char *start, int len, int prefix)
fprintf(ofd, "%02x ", v);
ascii[a++] = isprint(v) ? v : '.';
- if (c == limit-1) {
+ if (++c >= limit) {
fprintf(ofd, "%s\n", ascii);
if (i < (len - 1)) {
prefix_line(ofd, prefix);
@@ -884,8 +780,7 @@ static inline void dump_hex(FILE *ofd, char *start, int len, int prefix)
}
a = c = 0;
memset(ascii, 0, sizeof(ascii));
- } else
- c++;
+ }
}
if (c != 0) {
@@ -904,24 +799,73 @@ static void print_hdr(FILE *ofd, struct nl_msg *msg)
fprintf(ofd, " .nlmsg_len = %d\n", nlh->nlmsg_len);
- ops = nl_cache_ops_associate(nlmsg_get_proto(msg), nlh->nlmsg_type);
+ ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg), nlh->nlmsg_type);
if (ops) {
mt = nl_msgtype_lookup(ops, nlh->nlmsg_type);
if (!mt)
BUG();
snprintf(buf, sizeof(buf), "%s::%s", ops->co_name, mt->mt_name);
+ nl_cache_ops_put(ops);
} else
nl_nlmsgtype2str(nlh->nlmsg_type, buf, sizeof(buf));
- fprintf(ofd, " .nlmsg_type = %d <%s>\n", nlh->nlmsg_type, buf);
- fprintf(ofd, " .nlmsg_flags = %d <%s>\n", nlh->nlmsg_flags,
+ fprintf(ofd, " .type = %d <%s>\n", nlh->nlmsg_type, buf);
+ fprintf(ofd, " .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);
+ fprintf(ofd, " .seq = %d\n", nlh->nlmsg_seq);
+ fprintf(ofd, " .port = %d\n", nlh->nlmsg_pid);
}
+static void print_genl_hdr(FILE *ofd, void *start)
+{
+ struct genlmsghdr *ghdr = start;
+
+ fprintf(ofd, " [GENERIC NETLINK HEADER] %zu octets\n", GENL_HDRLEN);
+ fprintf(ofd, " .cmd = %u\n", ghdr->cmd);
+ fprintf(ofd, " .version = %u\n", ghdr->version);
+ fprintf(ofd, " .unused = %#x\n", ghdr->reserved);
+}
+
+static void *print_genl_msg(struct nl_msg *msg, FILE *ofd, struct nlmsghdr *hdr,
+ struct nl_cache_ops *ops, int *payloadlen)
+{
+ void *data = nlmsg_data(hdr);
+
+ if (*payloadlen < GENL_HDRLEN)
+ return data;
+
+ print_genl_hdr(ofd, data);
+
+ *payloadlen -= GENL_HDRLEN;
+ data += GENL_HDRLEN;
+
+ if (ops) {
+ int hdrsize = ops->co_hdrsize - GENL_HDRLEN;
+
+ if (hdrsize > 0) {
+ if (*payloadlen < hdrsize)
+ return data;
+
+ fprintf(ofd, " [HEADER] %d octets\n", hdrsize);
+ dump_hex(ofd, data, hdrsize, 0);
+
+ *payloadlen -= hdrsize;
+ data += hdrsize;
+ }
+ }
+
+ return data;
+}
+
+static void dump_attr(FILE *ofd, struct nlattr *attr, int prefix)
+{
+ int len = nla_len(attr);
+
+ dump_hex(ofd, nla_data(attr), len, prefix);
+}
+
static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen,
int prefix)
{
@@ -932,14 +876,18 @@ static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen,
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)
+ if (nla->nla_type == 0)
+ fprintf(ofd, " [ATTR PADDING] %d octets\n", alen);
+ else
+ fprintf(ofd, " [ATTR %02d%s] %d octets\n", nla_type(nla),
+ nla_is_nested(nla) ? " NESTED" : "",
+ alen);
+
+ if (nla_is_nested(nla))
dump_attrs(ofd, nla_data(nla), alen, prefix+1);
else
- dump_hex(ofd, nla_data(nla), alen, prefix);
+ dump_attr(ofd, nla, prefix);
padlen = nla_padlen(alen);
if (padlen > 0) {
@@ -957,63 +905,85 @@ static void dump_attrs(FILE *ofd, struct nlattr *attrs, int attrlen,
}
}
-/**
- * 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)
+static void dump_error_msg(struct nl_msg *msg, FILE *ofd)
{
struct nlmsghdr *hdr = nlmsg_hdr(msg);
-
- fprintf(ofd,
- "-------------------------- BEGIN NETLINK MESSAGE "
- "---------------------------\n");
+ struct nlmsgerr *err = nlmsg_data(hdr);
- fprintf(ofd, " [HEADER] %Zu octets\n", sizeof(struct nlmsghdr));
- print_hdr(ofd, msg);
+ fprintf(ofd, " [ERRORMSG] %zu octets\n", sizeof(*err));
- if (hdr->nlmsg_type == NLMSG_ERROR &&
- hdr->nlmsg_len >= nlmsg_msg_size(sizeof(struct nlmsgerr))) {
+ if (nlmsg_len(hdr) >= sizeof(*err)) {
+ char buf[256];
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));
+ strerror_r(-err->error, buf, sizeof(buf)));
+ 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_ops_associate(nlmsg_get_proto(msg),
- hdr->nlmsg_type);
- if (ops) {
- attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize);
- payloadlen -= attrlen;
- }
+ }
+}
+static void print_msg(struct nl_msg *msg, FILE *ofd, struct nlmsghdr *hdr)
+{
+ struct nl_cache_ops *ops;
+ int payloadlen = nlmsg_len(hdr);
+ int attrlen = 0;
+ void *data;
+
+ data = nlmsg_data(hdr);
+ ops = nl_cache_ops_associate_safe(nlmsg_get_proto(msg),
+ hdr->nlmsg_type);
+ if (ops) {
+ attrlen = nlmsg_attrlen(hdr, ops->co_hdrsize);
+ payloadlen -= attrlen;
+ }
+
+ if (msg->nm_protocol == NETLINK_GENERIC)
+ data = print_genl_msg(msg, ofd, hdr, ops, &payloadlen);
+
+ if (payloadlen) {
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);
- }
+ dump_hex(ofd, data, 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);
+ }
+
+ if (ops)
+ nl_cache_ops_put(ops);
+}
+
+/**
+ * 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, " [NETLINK HEADER] %zu octets\n", sizeof(struct nlmsghdr));
+ print_hdr(ofd, msg);
+
+ if (hdr->nlmsg_type == NLMSG_ERROR)
+ dump_error_msg(msg, ofd);
+ else if (nlmsg_len(hdr) > 0)
+ print_msg(msg, ofd, hdr);
+
fprintf(ofd,
- "--------------------------- END NETLINK MESSAGE "
- "---------------------------\n");
+ "--------------------------- END NETLINK MESSAGE ---------------------------\n");
}
/** @} */
diff --git a/lib/netfilter/ct.c b/lib/netfilter/ct.c
index 9d61b6cd..36a83dbe 100644
--- a/lib/netfilter/ct.c
+++ b/lib/netfilter/ct.c
@@ -23,7 +23,7 @@
#include <sys/types.h>
#include <linux/netfilter/nfnetlink_conntrack.h>
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/attr.h>
#include <netlink/netfilter/nfnl.h>
#include <netlink/netfilter/ct.h>
@@ -35,10 +35,18 @@ static uint64_t ntohll(uint64_t x)
{
return x;
}
+static uint64_t htonll(uint64_t x)
+{
+ return x;
+}
#elif __BYTE_ORDER == __LITTLE_ENDIAN
static uint64_t ntohll(uint64_t x)
{
- return __bswap_64(x);
+ return bswap_64(x);
+}
+static uint64_t htonll(uint64_t x)
+{
+ return bswap_64(x);
}
#endif
@@ -55,6 +63,7 @@ static struct nla_policy ct_policy[CTA_MAX+1] = {
[CTA_COUNTERS_REPLY] = { .type = NLA_NESTED },
[CTA_USE] = { .type = NLA_U32 },
[CTA_ID] = { .type = NLA_U32 },
+ [CTA_ZONE] = { .type = NLA_U16 },
//[CTA_NAT_DST]
};
@@ -102,6 +111,11 @@ static struct nla_policy ct_counters_policy[CTA_COUNTERS_MAX+1] = {
[CTA_COUNTERS32_BYTES] = { .type = NLA_U32 },
};
+static struct nla_policy ct_timestamp_policy[CTA_TIMESTAMP_MAX + 1] = {
+ [CTA_TIMESTAMP_START] = { .type = NLA_U64 },
+ [CTA_TIMESTAMP_STOP] = { .type = NLA_U64 },
+};
+
static int ct_parse_ip(struct nfnl_ct *ct, int repl, struct nlattr *attr)
{
struct nlattr *tb[CTA_IP_MAX+1];
@@ -174,15 +188,28 @@ static int ct_parse_proto(struct nfnl_ct *ct, int repl, struct nlattr *attr)
if (tb[CTA_PROTO_DST_PORT])
nfnl_ct_set_dst_port(ct, repl,
ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT])));
- if (tb[CTA_PROTO_ICMP_ID])
- nfnl_ct_set_icmp_id(ct, repl,
- ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID])));
- if (tb[CTA_PROTO_ICMP_TYPE])
- nfnl_ct_set_icmp_type(ct, repl,
+
+ if (ct->ct_family == AF_INET) {
+ if (tb[CTA_PROTO_ICMP_ID])
+ nfnl_ct_set_icmp_id(ct, repl,
+ ntohs(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,
+ if (tb[CTA_PROTO_ICMP_CODE])
+ nfnl_ct_set_icmp_code(ct, repl,
nla_get_u8(tb[CTA_PROTO_ICMP_CODE]));
+ } else if (ct->ct_family == AF_INET6) {
+ if (tb[CTA_PROTO_ICMPV6_ID])
+ nfnl_ct_set_icmp_id(ct, repl,
+ ntohs(nla_get_u16(tb[CTA_PROTO_ICMPV6_ID])));
+ if (tb[CTA_PROTO_ICMPV6_TYPE])
+ nfnl_ct_set_icmp_type(ct, repl,
+ nla_get_u8(tb[CTA_PROTO_ICMPV6_TYPE]));
+ if (tb[CTA_PROTO_ICMPV6_CODE])
+ nfnl_ct_set_icmp_code(ct, repl,
+ nla_get_u8(tb[CTA_PROTO_ICMPV6_CODE]));
+ }
return 0;
}
@@ -287,6 +314,24 @@ int nfnlmsg_ct_group(struct nlmsghdr *nlh)
}
}
+static int ct_parse_timestamp(struct nfnl_ct *ct, struct nlattr *attr)
+{
+ struct nlattr *tb[CTA_TIMESTAMP_MAX + 1];
+ int err;
+
+ err = nla_parse_nested(tb, CTA_TIMESTAMP_MAX, attr,
+ ct_timestamp_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[CTA_TIMESTAMP_START] && tb[CTA_TIMESTAMP_STOP])
+ nfnl_ct_set_timestamp(ct,
+ ntohll(nla_get_u64(tb[CTA_TIMESTAMP_START])),
+ ntohll(nla_get_u64(tb[CTA_TIMESTAMP_STOP])));
+
+ return 0;
+}
+
int nfnlmsg_ct_parse(struct nlmsghdr *nlh, struct nfnl_ct **result)
{
struct nfnl_ct *ct;
@@ -333,6 +378,8 @@ int nfnlmsg_ct_parse(struct nlmsghdr *nlh, struct nfnl_ct **result)
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_ZONE])
+ nfnl_ct_set_zone(ct, ntohs(nla_get_u16(tb[CTA_ZONE])));
if (tb[CTA_COUNTERS_ORIG]) {
err = ct_parse_counters(ct, 0, tb[CTA_COUNTERS_ORIG]);
@@ -346,6 +393,12 @@ int nfnlmsg_ct_parse(struct nlmsghdr *nlh, struct nfnl_ct **result)
goto errout;
}
+ if (tb[CTA_TIMESTAMP]) {
+ err = ct_parse_timestamp(ct, tb[CTA_TIMESTAMP]);
+ if (err < 0)
+ goto errout;
+ }
+
*result = ct;
return 0;
@@ -361,10 +414,9 @@ static int ct_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
int err;
if ((err = nfnlmsg_ct_parse(nlh, &ct)) < 0)
- goto errout;
+ return err;
err = pp->pp_cb((struct nl_object *) ct, pp);
-errout:
nfnl_ct_put(ct);
return err;
}
@@ -426,17 +478,31 @@ static int nfnl_ct_build_tuple(struct nl_msg *msg, const struct nfnl_ct *ct,
NLA_PUT_U16(msg, CTA_PROTO_DST_PORT,
htons(nfnl_ct_get_dst_port(ct, repl)));
- if (nfnl_ct_test_icmp_id(ct, repl))
- NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID,
- htons(nfnl_ct_get_icmp_id(ct, repl)));
-
- if (nfnl_ct_test_icmp_type(ct, repl))
- NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE,
- nfnl_ct_get_icmp_type(ct, repl));
-
- if (nfnl_ct_test_icmp_code(ct, repl))
- NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE,
- nfnl_ct_get_icmp_code(ct, repl));
+ if (family == AF_INET) {
+ if (nfnl_ct_test_icmp_id(ct, repl))
+ NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID,
+ htons(nfnl_ct_get_icmp_id(ct, repl)));
+
+ if (nfnl_ct_test_icmp_type(ct, repl))
+ NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE,
+ nfnl_ct_get_icmp_type(ct, repl));
+
+ if (nfnl_ct_test_icmp_code(ct, repl))
+ NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE,
+ nfnl_ct_get_icmp_code(ct, repl));
+ } else if (family == AF_INET6) {
+ if (nfnl_ct_test_icmp_id(ct, repl))
+ NLA_PUT_U16(msg, CTA_PROTO_ICMPV6_ID,
+ htons(nfnl_ct_get_icmp_id(ct, repl)));
+
+ if (nfnl_ct_test_icmp_type(ct, repl))
+ NLA_PUT_U8(msg, CTA_PROTO_ICMPV6_TYPE,
+ nfnl_ct_get_icmp_type(ct, repl));
+
+ if (nfnl_ct_test_icmp_code(ct, repl))
+ NLA_PUT_U8(msg, CTA_PROTO_ICMPV6_CODE,
+ nfnl_ct_get_icmp_code(ct, repl));
+ }
nla_nest_end(msg, proto);
@@ -461,9 +527,31 @@ static int nfnl_ct_build_message(const struct nfnl_ct *ct, int cmd, int flags,
if ((err = nfnl_ct_build_tuple(msg, ct, 0)) < 0)
goto err_out;
+ /* REPLY tuple is optional, dont add unless at least src/dst specified */
+
+ if ( nfnl_ct_get_src(ct, 1) && nfnl_ct_get_dst(ct, 1) )
+ if ((err = nfnl_ct_build_tuple(msg, ct, 1)) < 0)
+ goto err_out;
+
+ if (nfnl_ct_test_status(ct))
+ NLA_PUT_U32(msg, CTA_STATUS, htonl(nfnl_ct_get_status(ct)));
+
+ if (nfnl_ct_test_timeout(ct))
+ NLA_PUT_U32(msg, CTA_TIMEOUT, htonl(nfnl_ct_get_timeout(ct)));
+
+ if (nfnl_ct_test_mark(ct))
+ NLA_PUT_U32(msg, CTA_MARK, htonl(nfnl_ct_get_mark(ct)));
+
+ if (nfnl_ct_test_id(ct))
+ NLA_PUT_U32(msg, CTA_ID, htonl(nfnl_ct_get_id(ct)));
+
+ if (nfnl_ct_test_zone(ct))
+ NLA_PUT_U16(msg, CTA_ZONE, htons(nfnl_ct_get_zone(ct)));
+
*result = msg;
return 0;
+nla_put_failure:
err_out:
nlmsg_free(msg);
return err;
diff --git a/lib/netfilter/ct_obj.c b/lib/netfilter/ct_obj.c
index ae14c0db..61b6a31b 100644
--- a/lib/netfilter/ct_obj.c
+++ b/lib/netfilter/ct_obj.c
@@ -16,7 +16,7 @@
#include <linux/netfilter/nf_conntrack_common.h>
#include <linux/netfilter/nf_conntrack_tcp.h>
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netfilter/nfnl.h>
#include <netlink/netfilter/ct.h>
@@ -51,6 +51,8 @@
#define CT_ATTR_REPL_ICMP_CODE (1UL << 23)
#define CT_ATTR_REPL_PACKETS (1UL << 24)
#define CT_ATTR_REPL_BYTES (1UL << 25)
+#define CT_ATTR_TIMESTAMP (1UL << 26)
+#define CT_ATTR_ZONE (1UL << 27)
/** @endcond */
static void ct_free_data(struct nl_object *c)
@@ -121,10 +123,10 @@ static void dump_icmp(struct nl_dump_params *p, struct nfnl_ct *ct, int reply)
if (nfnl_ct_test_icmp_type(ct, reply))
nl_dump(p, "icmp type %d ", nfnl_ct_get_icmp_type(ct, reply));
- if (nfnl_ct_test_icmp_type(ct, reply))
+ if (nfnl_ct_test_icmp_code(ct, reply))
nl_dump(p, "code %d ", nfnl_ct_get_icmp_code(ct, reply));
- if (nfnl_ct_test_icmp_type(ct, reply))
+ if (nfnl_ct_test_icmp_id(ct, reply))
nl_dump(p, "id %d ", nfnl_ct_get_icmp_id(ct, reply));
}
@@ -192,6 +194,20 @@ static void ct_dump_line(struct nl_object *a, struct nl_dump_params *p)
if (nfnl_ct_test_mark(ct) && nfnl_ct_get_mark(ct))
nl_dump(p, "mark %u ", nfnl_ct_get_mark(ct));
+ if (nfnl_ct_test_zone(ct))
+ nl_dump(p, "zone %hu ", nfnl_ct_get_zone(ct));
+
+ if (nfnl_ct_test_timestamp(ct)) {
+ const struct nfnl_ct_timestamp *tstamp = nfnl_ct_get_timestamp(ct);
+ int64_t delta_time = tstamp->stop - tstamp->start;
+
+ if (delta_time > 0)
+ delta_time /= NSEC_PER_SEC;
+ else
+ delta_time = 0;
+ nl_dump(p, "delta-time %llu ", delta_time);
+ }
+
nl_dump(p, "\n");
}
@@ -256,18 +272,29 @@ static void ct_dump_stats(struct nl_object *a, struct nl_dump_params *p)
struct nfnl_ct *ct = (struct nfnl_ct *) a;
double res;
char *unit;
+ uint64_t packets;
+ const char * const names[] = {"rx", "tx"};
+ int i;
ct_dump_details(a, p);
- nl_dump_line(p, " # packets volume\n");
-
- res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, 1), &unit);
- nl_dump_line(p, " rx %10llu %7.2f %s\n",
- nfnl_ct_get_packets(ct, 1), res, unit);
+ if (!nfnl_ct_test_bytes(ct, 0) ||
+ !nfnl_ct_test_packets(ct, 0) ||
+ !nfnl_ct_test_bytes(ct, 1) ||
+ !nfnl_ct_test_packets(ct, 1))
+ {
+ nl_dump_line(p, " Statistics are not available.\n");
+ nl_dump_line(p, " Please set sysctl net.netfilter.nf_conntrack_acct=1\n");
+ nl_dump_line(p, " (Require kernel 2.6.27)\n");
+ return;
+ }
- res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, 0), &unit);
- nl_dump_line(p, " tx %10llu %7.2f %s\n",
- nfnl_ct_get_packets(ct, 0), res, unit);
+ nl_dump_line(p, " # packets volume\n");
+ for (i=0; i<=1; i++) {
+ res = nl_cancel_down_bytes(nfnl_ct_get_bytes(ct, i), &unit);
+ packets = nfnl_ct_get_packets(ct, i);
+ nl_dump_line(p, " %s %10" PRIu64 " %7.2f %s\n", names[i], packets, res, unit);
+ }
}
static int ct_compare(struct nl_object *_a, struct nl_object *_b,
@@ -323,7 +350,7 @@ static int ct_compare(struct nl_object *_a, struct nl_object *_b,
return diff;
}
-static struct trans_tbl ct_attrs[] = {
+static const struct trans_tbl ct_attrs[] = {
__ADD(CT_ATTR_FAMILY, family)
__ADD(CT_ATTR_PROTO, proto)
__ADD(CT_ATTR_TCP_STATE, tcpstate)
@@ -430,7 +457,7 @@ uint8_t nfnl_ct_get_tcp_state(const struct nfnl_ct *ct)
return ct->ct_protoinfo.tcp.state;
}
-static struct trans_tbl tcp_states[] = {
+static const struct trans_tbl tcp_states[] = {
__ADD(TCP_CONNTRACK_NONE,NONE)
__ADD(TCP_CONNTRACK_SYN_SENT,SYN_SENT)
__ADD(TCP_CONNTRACK_SYN_RECV,SYN_RECV)
@@ -467,12 +494,17 @@ void nfnl_ct_unset_status(struct nfnl_ct *ct, uint32_t 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;
}
-static struct trans_tbl status_flags[] = {
+static const struct trans_tbl status_flags[] = {
__ADD(IPS_EXPECTED, expected)
__ADD(IPS_SEEN_REPLY, seen_reply)
__ADD(IPS_ASSURED, assured)
@@ -561,6 +593,22 @@ uint32_t nfnl_ct_get_id(const struct nfnl_ct *ct)
return ct->ct_id;
}
+void nfnl_ct_set_zone(struct nfnl_ct *ct, uint16_t zone)
+{
+ ct->ct_zone = zone;
+ ct->ce_mask |= CT_ATTR_ZONE;
+}
+
+int nfnl_ct_test_zone(const struct nfnl_ct *ct)
+{
+ return !!(ct->ce_mask & CT_ATTR_ZONE);
+}
+
+uint16_t nfnl_ct_get_zone(const struct nfnl_ct *ct)
+{
+ return ct->ct_zone;
+}
+
static int ct_set_addr(struct nfnl_ct *ct, struct nl_addr *addr,
int attr, struct nl_addr ** ct_addr)
{
@@ -766,6 +814,23 @@ uint64_t nfnl_ct_get_bytes(const struct nfnl_ct *ct, int repl)
return dir->bytes;
}
+void nfnl_ct_set_timestamp(struct nfnl_ct *ct, uint64_t start, uint64_t stop)
+{
+ ct->ct_tstamp.start = start;
+ ct->ct_tstamp.stop = stop;
+ ct->ce_mask |= CT_ATTR_TIMESTAMP;
+}
+
+int nfnl_ct_test_timestamp(const struct nfnl_ct *ct)
+{
+ return !!(ct->ce_mask & CT_ATTR_TIMESTAMP);
+}
+
+const struct nfnl_ct_timestamp *nfnl_ct_get_timestamp(const struct nfnl_ct *ct)
+{
+ return &ct->ct_tstamp;
+}
+
/** @} */
struct nl_object_ops ct_obj_ops = {
diff --git a/lib/netfilter/exp.c b/lib/netfilter/exp.c
new file mode 100644
index 00000000..9cfdd2bf
--- /dev/null
+++ b/lib/netfilter/exp.c
@@ -0,0 +1,620 @@
+/*
+ * lib/netfilter/exp.c Conntrack Expectation
+ *
+ * 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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ * Copyright (c= 2008 Patrick McHardy <kaber@trash.net>
+ * Copyright (c) 2012 Rich Fought <rich.fought@watchguard.com>
+ */
+
+/**
+ * @ingroup nfnl
+ * @defgroup exp Expectation
+ * @brief
+ * @{
+ */
+
+#include <byteswap.h>
+#include <sys/types.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+
+#include <netlink-private/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/exp.h>
+
+static struct nl_cache_ops nfnl_exp_ops;
+
+static struct nla_policy exp_policy[CTA_EXPECT_MAX+1] = {
+ [CTA_EXPECT_MASTER] = { .type = NLA_NESTED },
+ [CTA_EXPECT_TUPLE] = { .type = NLA_NESTED },
+ [CTA_EXPECT_MASK] = { .type = NLA_NESTED },
+ [CTA_EXPECT_TIMEOUT] = { .type = NLA_U32 },
+ [CTA_EXPECT_ID] = { .type = NLA_U32 },
+ [CTA_EXPECT_HELP_NAME] = { .type = NLA_STRING },
+ [CTA_EXPECT_ZONE] = { .type = NLA_U16 },
+ [CTA_EXPECT_FLAGS] = { .type = NLA_U32 }, // Added in kernel 2.6.37
+ [CTA_EXPECT_CLASS] = { .type = NLA_U32 }, // Added in kernel 3.5
+ [CTA_EXPECT_NAT] = { .type = NLA_NESTED }, // Added in kernel 3.5
+ [CTA_EXPECT_FN] = { .type = NLA_STRING }, // Added in kernel 3.5
+};
+
+static struct nla_policy exp_tuple_policy[CTA_TUPLE_MAX+1] = {
+ [CTA_TUPLE_IP] = { .type = NLA_NESTED },
+ [CTA_TUPLE_PROTO] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy exp_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 exp_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 exp_nat_policy[CTA_EXPECT_NAT_MAX+1] = {
+ [CTA_EXPECT_NAT_DIR] = { .type = NLA_U32 },
+ [CTA_EXPECT_NAT_TUPLE] = { .type = NLA_NESTED },
+};
+
+static int exp_parse_ip(struct nfnl_exp *exp, int tuple, 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, exp_ip_policy);
+ if (err < 0)
+ goto errout;
+
+ if (tb[CTA_IP_V4_SRC]) {
+ addr = nl_addr_alloc_attr(tb[CTA_IP_V4_SRC], AF_INET);
+ if (addr == NULL)
+ goto errout_enomem;
+ err = nfnl_exp_set_src(exp, tuple, addr);
+ nl_addr_put(addr);
+ if (err < 0)
+ goto errout;
+ }
+ if (tb[CTA_IP_V4_DST]) {
+ addr = nl_addr_alloc_attr(tb[CTA_IP_V4_DST], AF_INET);
+ if (addr == NULL)
+ goto errout_enomem;
+ err = nfnl_exp_set_dst(exp, tuple, addr);
+ nl_addr_put(addr);
+ if (err < 0)
+ goto errout;
+ }
+ if (tb[CTA_IP_V6_SRC]) {
+ addr = nl_addr_alloc_attr(tb[CTA_IP_V6_SRC], AF_INET6);
+ if (addr == NULL)
+ goto errout_enomem;
+ err = nfnl_exp_set_src(exp, tuple, addr);
+ nl_addr_put(addr);
+ if (err < 0)
+ goto errout;
+ }
+ if (tb[CTA_IP_V6_DST]) {
+ addr = nl_addr_alloc_attr(tb[CTA_IP_V6_DST], AF_INET6);
+ if (addr == NULL)
+ goto errout_enomem;
+ err = nfnl_exp_set_dst(exp, tuple, addr);
+ nl_addr_put(addr);
+ if (err < 0)
+ goto errout;
+ }
+
+ return 0;
+
+errout_enomem:
+ err = -NLE_NOMEM;
+errout:
+ return err;
+}
+
+static int exp_parse_proto(struct nfnl_exp *exp, int tuple, struct nlattr *attr)
+{
+ struct nlattr *tb[CTA_PROTO_MAX+1];
+ int err;
+ uint16_t srcport = 0, dstport = 0, icmpid = 0;
+ uint8_t icmptype = 0, icmpcode = 0;
+
+ err = nla_parse_nested(tb, CTA_PROTO_MAX, attr, exp_proto_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[CTA_PROTO_NUM])
+ nfnl_exp_set_l4protonum(exp, tuple, nla_get_u8(tb[CTA_PROTO_NUM]));
+
+ if (tb[CTA_PROTO_SRC_PORT])
+ srcport = ntohs(nla_get_u16(tb[CTA_PROTO_SRC_PORT]));
+ if (tb[CTA_PROTO_DST_PORT])
+ dstport = ntohs(nla_get_u16(tb[CTA_PROTO_DST_PORT]));
+ if (tb[CTA_PROTO_SRC_PORT] || tb[CTA_PROTO_DST_PORT])
+ nfnl_exp_set_ports(exp, tuple, srcport, dstport);
+
+ if (tb[CTA_PROTO_ICMP_ID])
+ icmpid = ntohs(nla_get_u16(tb[CTA_PROTO_ICMP_ID]));
+ if (tb[CTA_PROTO_ICMP_TYPE])
+ icmptype = nla_get_u8(tb[CTA_PROTO_ICMP_TYPE]);
+ if (tb[CTA_PROTO_ICMP_CODE])
+ icmpcode = nla_get_u8(tb[CTA_PROTO_ICMP_CODE]);
+ if (tb[CTA_PROTO_ICMP_ID] || tb[CTA_PROTO_ICMP_TYPE] || tb[CTA_PROTO_ICMP_CODE])
+ nfnl_exp_set_icmp(exp, tuple, icmpid, icmptype, icmpcode);
+ return 0;
+}
+
+static int exp_parse_tuple(struct nfnl_exp *exp, int tuple, struct nlattr *attr)
+{
+ struct nlattr *tb[CTA_TUPLE_MAX+1];
+ int err;
+
+ err = nla_parse_nested(tb, CTA_TUPLE_MAX, attr, exp_tuple_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[CTA_TUPLE_IP]) {
+ err = exp_parse_ip(exp, tuple, tb[CTA_TUPLE_IP]);
+ if (err < 0)
+ return err;
+ }
+
+ if (tb[CTA_TUPLE_PROTO]) {
+ err = exp_parse_proto(exp, tuple, tb[CTA_TUPLE_PROTO]);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int exp_parse_nat(struct nfnl_exp *exp, struct nlattr *attr)
+{
+ struct nlattr *tb[CTA_EXPECT_NAT_MAX+1];
+ int err;
+
+ err = nla_parse_nested(tb, CTA_EXPECT_NAT_MAX, attr, exp_nat_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[CTA_EXPECT_NAT_DIR])
+ nfnl_exp_set_nat_dir(exp, nla_get_u32(tb[CTA_EXPECT_NAT_DIR]));
+
+ if (tb[CTA_EXPECT_NAT_TUPLE]) {
+ err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_NAT, tb[CTA_EXPECT_NAT_TUPLE]);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+int nfnlmsg_exp_group(struct nlmsghdr *nlh)
+{
+ switch (nfnlmsg_subtype(nlh)) {
+ case IPCTNL_MSG_EXP_NEW:
+ if (nlh->nlmsg_flags & (NLM_F_CREATE|NLM_F_EXCL))
+ return NFNLGRP_CONNTRACK_EXP_NEW;
+ else
+ return NFNLGRP_CONNTRACK_EXP_UPDATE;
+ case IPCTNL_MSG_EXP_DELETE:
+ return NFNLGRP_CONNTRACK_EXP_DESTROY;
+ default:
+ return NFNLGRP_NONE;
+ }
+}
+
+int nfnlmsg_exp_parse(struct nlmsghdr *nlh, struct nfnl_exp **result)
+{
+ struct nfnl_exp *exp;
+ struct nlattr *tb[CTA_MAX+1];
+ int err;
+
+ exp = nfnl_exp_alloc();
+ if (!exp)
+ return -NLE_NOMEM;
+
+ exp->ce_msgtype = nlh->nlmsg_type;
+
+ err = nlmsg_parse(nlh, sizeof(struct nfgenmsg), tb, CTA_EXPECT_MAX,
+ exp_policy);
+ if (err < 0)
+ goto errout;
+
+ nfnl_exp_set_family(exp, nfnlmsg_family(nlh));
+
+ if (tb[CTA_EXPECT_TUPLE]) {
+ err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_EXPECT, tb[CTA_EXPECT_TUPLE]);
+ if (err < 0)
+ goto errout;
+ }
+ if (tb[CTA_EXPECT_MASTER]) {
+ err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASTER, tb[CTA_EXPECT_MASTER]);
+ if (err < 0)
+ goto errout;
+ }
+ if (tb[CTA_EXPECT_MASK]) {
+ err = exp_parse_tuple(exp, NFNL_EXP_TUPLE_MASK, tb[CTA_EXPECT_MASK]);
+ if (err < 0)
+ goto errout;
+ }
+
+ if (tb[CTA_EXPECT_NAT]) {
+ err = exp_parse_nat(exp, tb[CTA_EXPECT_MASK]);
+ if (err < 0)
+ goto errout;
+ }
+
+ if (tb[CTA_EXPECT_CLASS])
+ nfnl_exp_set_class(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_CLASS])));
+
+ if (tb[CTA_EXPECT_FN])
+ nfnl_exp_set_fn(exp, nla_data(tb[CTA_EXPECT_FN]));
+
+ if (tb[CTA_EXPECT_TIMEOUT])
+ nfnl_exp_set_timeout(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_TIMEOUT])));
+
+ if (tb[CTA_EXPECT_ID])
+ nfnl_exp_set_id(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_ID])));
+
+ if (tb[CTA_EXPECT_HELP_NAME])
+ nfnl_exp_set_helper_name(exp, nla_data(tb[CTA_EXPECT_HELP_NAME]));
+
+ if (tb[CTA_EXPECT_ZONE])
+ nfnl_exp_set_zone(exp, ntohs(nla_get_u16(tb[CTA_EXPECT_ZONE])));
+
+ if (tb[CTA_EXPECT_FLAGS])
+ nfnl_exp_set_flags(exp, ntohl(nla_get_u32(tb[CTA_EXPECT_FLAGS])));
+
+ *result = exp;
+ return 0;
+
+errout:
+ nfnl_exp_put(exp);
+ return err;
+}
+
+static int exp_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+ struct nlmsghdr *nlh, struct nl_parser_param *pp)
+{
+ struct nfnl_exp *exp;
+ int err;
+
+ if ((err = nfnlmsg_exp_parse(nlh, &exp)) < 0)
+ return err;
+
+ err = pp->pp_cb((struct nl_object *) exp, pp);
+ nfnl_exp_put(exp);
+ return err;
+}
+
+int nfnl_exp_dump_request(struct nl_sock *sk)
+{
+ return nfnl_send_simple(sk, NFNL_SUBSYS_CTNETLINK_EXP, IPCTNL_MSG_EXP_GET,
+ NLM_F_DUMP, AF_UNSPEC, 0);
+}
+
+static int exp_request_update(struct nl_cache *cache, struct nl_sock *sk)
+{
+ return nfnl_exp_dump_request(sk);
+}
+
+static int exp_get_tuple_attr(int tuple)
+{
+ int attr = 0;
+
+ switch (tuple) {
+ case CTA_EXPECT_MASTER:
+ attr = NFNL_EXP_TUPLE_MASTER;
+ break;
+ case CTA_EXPECT_MASK:
+ attr = NFNL_EXP_TUPLE_MASK;
+ break;
+ case CTA_EXPECT_NAT:
+ attr = NFNL_EXP_TUPLE_NAT;
+ break;
+ case CTA_EXPECT_TUPLE:
+ default :
+ attr = NFNL_EXP_TUPLE_EXPECT;
+ break;
+ }
+
+ return attr;
+}
+
+static int nfnl_exp_build_tuple(struct nl_msg *msg, const struct nfnl_exp *exp,
+ int cta)
+{
+ struct nlattr *tuple, *ip, *proto;
+ struct nl_addr *addr;
+ int family;
+
+ family = nfnl_exp_get_family(exp);
+
+ int type = exp_get_tuple_attr(cta);
+
+ if (cta == CTA_EXPECT_NAT)
+ tuple = nla_nest_start(msg, CTA_EXPECT_NAT_TUPLE);
+ else
+ tuple = nla_nest_start(msg, cta);
+
+ if (!tuple)
+ goto nla_put_failure;
+
+ ip = nla_nest_start(msg, CTA_TUPLE_IP);
+ if (!ip)
+ goto nla_put_failure;
+
+ addr = nfnl_exp_get_src(exp, type);
+ if (addr)
+ NLA_PUT_ADDR(msg,
+ family == AF_INET ? CTA_IP_V4_SRC : CTA_IP_V6_SRC,
+ addr);
+
+ addr = nfnl_exp_get_dst(exp, type);
+ if (addr)
+ NLA_PUT_ADDR(msg,
+ family == AF_INET ? CTA_IP_V4_DST : CTA_IP_V6_DST,
+ addr);
+
+ nla_nest_end(msg, ip);
+
+ proto = nla_nest_start(msg, CTA_TUPLE_PROTO);
+ if (!proto)
+ goto nla_put_failure;
+
+ if (nfnl_exp_test_l4protonum(exp, type))
+ NLA_PUT_U8(msg, CTA_PROTO_NUM, nfnl_exp_get_l4protonum(exp, type));
+
+ if (nfnl_exp_test_ports(exp, type)) {
+ NLA_PUT_U16(msg, CTA_PROTO_SRC_PORT,
+ htons(nfnl_exp_get_src_port(exp, type)));
+
+ NLA_PUT_U16(msg, CTA_PROTO_DST_PORT,
+ htons(nfnl_exp_get_dst_port(exp, type)));
+ }
+
+ if (nfnl_exp_test_icmp(exp, type)) {
+ NLA_PUT_U16(msg, CTA_PROTO_ICMP_ID,
+ htons(nfnl_exp_get_icmp_id(exp, type)));
+
+ NLA_PUT_U8(msg, CTA_PROTO_ICMP_TYPE,
+ nfnl_exp_get_icmp_type(exp, type));
+
+ NLA_PUT_U8(msg, CTA_PROTO_ICMP_CODE,
+ nfnl_exp_get_icmp_code(exp, type));
+ }
+
+ nla_nest_end(msg, proto);
+
+ nla_nest_end(msg, tuple);
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
+}
+
+static int nfnl_exp_build_nat(struct nl_msg *msg, const struct nfnl_exp *exp)
+{
+ struct nlattr *nat;
+ int err;
+
+ nat = nla_nest_start(msg, CTA_EXPECT_NAT);
+
+ if (nfnl_exp_test_nat_dir(exp)) {
+ NLA_PUT_U32(msg, CTA_EXPECT_NAT_DIR,
+ nfnl_exp_get_nat_dir(exp));
+ }
+
+ if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_NAT)) < 0)
+ goto nla_put_failure;
+
+ nla_nest_end(msg, nat);
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
+}
+
+static int nfnl_exp_build_message(const struct nfnl_exp *exp, int cmd, int flags,
+ struct nl_msg **result)
+{
+ struct nl_msg *msg;
+ int err;
+
+ msg = nfnlmsg_alloc_simple(NFNL_SUBSYS_CTNETLINK_EXP, cmd, flags,
+ nfnl_exp_get_family(exp), 0);
+ if (msg == NULL)
+ return -NLE_NOMEM;
+
+ if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_TUPLE)) < 0)
+ goto err_out;
+
+ if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASTER)) < 0)
+ goto err_out;
+
+ if ((err = nfnl_exp_build_tuple(msg, exp, CTA_EXPECT_MASK)) < 0)
+ goto err_out;
+
+ if (nfnl_exp_test_src(exp, NFNL_EXP_TUPLE_NAT)) {
+ if ((err = nfnl_exp_build_nat(msg, exp)) < 0)
+ goto err_out;
+ }
+
+ if (nfnl_exp_test_class(exp))
+ NLA_PUT_U32(msg, CTA_EXPECT_CLASS, htonl(nfnl_exp_get_class(exp)));
+
+ if (nfnl_exp_test_fn(exp))
+ NLA_PUT_STRING(msg, CTA_EXPECT_FN, nfnl_exp_get_fn(exp));
+
+ if (nfnl_exp_test_id(exp))
+ NLA_PUT_U32(msg, CTA_EXPECT_ID, htonl(nfnl_exp_get_id(exp)));
+
+ if (nfnl_exp_test_timeout(exp))
+ NLA_PUT_U32(msg, CTA_EXPECT_TIMEOUT, htonl(nfnl_exp_get_timeout(exp)));
+
+ if (nfnl_exp_test_helper_name(exp))
+ NLA_PUT_STRING(msg, CTA_EXPECT_HELP_NAME, nfnl_exp_get_helper_name(exp));
+
+ if (nfnl_exp_test_zone(exp))
+ NLA_PUT_U16(msg, CTA_EXPECT_ZONE, htons(nfnl_exp_get_zone(exp)));
+
+ if (nfnl_exp_test_flags(exp))
+ NLA_PUT_U32(msg, CTA_EXPECT_FLAGS, htonl(nfnl_exp_get_flags(exp)));
+
+ *result = msg;
+ return 0;
+
+nla_put_failure:
+err_out:
+ nlmsg_free(msg);
+ return err;
+}
+
+int nfnl_exp_build_add_request(const struct nfnl_exp *exp, int flags,
+ struct nl_msg **result)
+{
+ return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_NEW, flags, result);
+}
+
+int nfnl_exp_add(struct nl_sock *sk, const struct nfnl_exp *exp, int flags)
+{
+ struct nl_msg *msg;
+ int err;
+
+ if ((err = nfnl_exp_build_add_request(exp, flags, &msg)) < 0)
+ return err;
+
+ err = nl_send_auto_complete(sk, msg);
+ nlmsg_free(msg);
+ if (err < 0)
+ return err;
+
+ return wait_for_ack(sk);
+}
+
+int nfnl_exp_build_delete_request(const struct nfnl_exp *exp, int flags,
+ struct nl_msg **result)
+{
+ return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_DELETE, flags, result);
+}
+
+int nfnl_exp_del(struct nl_sock *sk, const struct nfnl_exp *exp, int flags)
+{
+ struct nl_msg *msg;
+ int err;
+
+ if ((err = nfnl_exp_build_delete_request(exp, flags, &msg)) < 0)
+ return err;
+
+ err = nl_send_auto_complete(sk, msg);
+ nlmsg_free(msg);
+ if (err < 0)
+ return err;
+
+ return wait_for_ack(sk);
+}
+
+int nfnl_exp_build_query_request(const struct nfnl_exp *exp, int flags,
+ struct nl_msg **result)
+{
+ return nfnl_exp_build_message(exp, IPCTNL_MSG_EXP_GET, flags, result);
+}
+
+int nfnl_exp_query(struct nl_sock *sk, const struct nfnl_exp *exp, int flags)
+{
+ struct nl_msg *msg;
+ int err;
+
+ if ((err = nfnl_exp_build_query_request(exp, flags, &msg)) < 0)
+ return err;
+
+ err = nl_send_auto_complete(sk, msg);
+ nlmsg_free(msg);
+ if (err < 0)
+ return err;
+
+ return wait_for_ack(sk);
+}
+
+/**
+ * @name Cache Management
+ * @{
+ */
+
+/**
+ * Build a expectation cache holding all expectations currently in the kernel
+ * @arg sk Netlink socket.
+ * @arg result Pointer to store resulting cache.
+ *
+ * Allocates a new cache, initializes it properly and updates it to
+ * contain all expectations currently in the kernel.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nfnl_exp_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
+{
+ return nl_cache_alloc_and_fill(&nfnl_exp_ops, sk, result);
+}
+
+/** @} */
+
+/**
+ * @name Expectation Addition
+ * @{
+ */
+
+/** @} */
+
+static struct nl_af_group exp_groups[] = {
+ { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_NEW },
+ { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_UPDATE },
+ { AF_UNSPEC, NFNLGRP_CONNTRACK_EXP_DESTROY },
+ { END_OF_GROUP_LIST },
+};
+
+#define NFNLMSG_EXP_TYPE(type) NFNLMSG_TYPE(NFNL_SUBSYS_CTNETLINK_EXP, (type))
+static struct nl_cache_ops nfnl_exp_ops = {
+ .co_name = "netfilter/exp",
+ .co_hdrsize = NFNL_HDRLEN,
+ .co_msgtypes = {
+ { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_NEW), NL_ACT_NEW, "new" },
+ { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_GET), NL_ACT_GET, "get" },
+ { NFNLMSG_EXP_TYPE(IPCTNL_MSG_EXP_DELETE), NL_ACT_DEL, "del" },
+ END_OF_MSGTYPES_LIST,
+ },
+ .co_protocol = NETLINK_NETFILTER,
+ .co_groups = exp_groups,
+ .co_request_update = exp_request_update,
+ .co_msg_parser = exp_msg_parser,
+ .co_obj_ops = &exp_obj_ops,
+};
+
+static void __init exp_init(void)
+{
+ nl_cache_mngt_register(&nfnl_exp_ops);
+}
+
+static void __exit exp_exit(void)
+{
+ nl_cache_mngt_unregister(&nfnl_exp_ops);
+}
+
+/** @} */
diff --git a/lib/netfilter/exp_obj.c b/lib/netfilter/exp_obj.c
new file mode 100644
index 00000000..69b4dd31
--- /dev/null
+++ b/lib/netfilter/exp_obj.c
@@ -0,0 +1,888 @@
+/*
+ * lib/netfilter/exp_obj.c Conntrack Expectation 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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ * Copyright (c) 2012 Rich Fought <rich.fought@watchguard.com>
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <linux/netfilter/nfnetlink_conntrack.h>
+#include <linux/netfilter/nf_conntrack_common.h>
+#include <linux/netfilter/nf_conntrack_tcp.h>
+
+#include <netlink-private/netlink.h>
+#include <netlink/netfilter/nfnl.h>
+#include <netlink/netfilter/exp.h>
+
+// The 32-bit attribute mask in the common object header isn't
+// big enough to handle all attributes of an expectation. So
+// we'll for sure specify optional attributes + parent attributes
+// that are required for valid object comparison. Comparison of
+// these parent attributes will include nested attributes.
+
+/** @cond SKIP */
+#define EXP_ATTR_FAMILY (1UL << 0) // 8-bit
+#define EXP_ATTR_TIMEOUT (1UL << 1) // 32-bit
+#define EXP_ATTR_ID (1UL << 2) // 32-bit
+#define EXP_ATTR_HELPER_NAME (1UL << 3) // string
+#define EXP_ATTR_ZONE (1UL << 4) // 16-bit
+#define EXP_ATTR_FLAGS (1UL << 5) // 32-bit
+#define EXP_ATTR_CLASS (1UL << 6) // 32-bit
+#define EXP_ATTR_FN (1UL << 7) // String
+// Tuples
+#define EXP_ATTR_EXPECT_IP_SRC (1UL << 8)
+#define EXP_ATTR_EXPECT_IP_DST (1UL << 9)
+#define EXP_ATTR_EXPECT_L4PROTO_NUM (1UL << 10)
+#define EXP_ATTR_EXPECT_L4PROTO_PORTS (1UL << 11)
+#define EXP_ATTR_EXPECT_L4PROTO_ICMP (1UL << 12)
+#define EXP_ATTR_MASTER_IP_SRC (1UL << 13)
+#define EXP_ATTR_MASTER_IP_DST (1UL << 14)
+#define EXP_ATTR_MASTER_L4PROTO_NUM (1UL << 15)
+#define EXP_ATTR_MASTER_L4PROTO_PORTS (1UL << 16)
+#define EXP_ATTR_MASTER_L4PROTO_ICMP (1UL << 17)
+#define EXP_ATTR_MASK_IP_SRC (1UL << 18)
+#define EXP_ATTR_MASK_IP_DST (1UL << 19)
+#define EXP_ATTR_MASK_L4PROTO_NUM (1UL << 20)
+#define EXP_ATTR_MASK_L4PROTO_PORTS (1UL << 21)
+#define EXP_ATTR_MASK_L4PROTO_ICMP (1UL << 22)
+#define EXP_ATTR_NAT_IP_SRC (1UL << 23)
+#define EXP_ATTR_NAT_IP_DST (1UL << 24)
+#define EXP_ATTR_NAT_L4PROTO_NUM (1UL << 25)
+#define EXP_ATTR_NAT_L4PROTO_PORTS (1UL << 26)
+#define EXP_ATTR_NAT_L4PROTO_ICMP (1UL << 27)
+#define EXP_ATTR_NAT_DIR (1UL << 28)
+/** @endcond */
+
+static void exp_free_data(struct nl_object *c)
+{
+ struct nfnl_exp *exp = (struct nfnl_exp *) c;
+
+ if (exp == NULL)
+ return;
+
+ nl_addr_put(exp->exp_expect.src);
+ nl_addr_put(exp->exp_expect.dst);
+ nl_addr_put(exp->exp_master.src);
+ nl_addr_put(exp->exp_master.dst);
+ nl_addr_put(exp->exp_mask.src);
+ nl_addr_put(exp->exp_mask.dst);
+ nl_addr_put(exp->exp_nat.src);
+ nl_addr_put(exp->exp_nat.dst);
+
+ free(exp->exp_fn);
+ free(exp->exp_helper_name);
+}
+
+static int exp_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+ struct nfnl_exp *dst = (struct nfnl_exp *) _dst;
+ struct nfnl_exp *src = (struct nfnl_exp *) _src;
+ struct nl_addr *addr;
+
+ // Expectation
+ if (src->exp_expect.src) {
+ addr = nl_addr_clone(src->exp_expect.src);
+ if (!addr)
+ return -NLE_NOMEM;
+ dst->exp_expect.src = addr;
+ }
+
+ if (src->exp_expect.dst) {
+ addr = nl_addr_clone(src->exp_expect.dst);
+ if (!addr)
+ return -NLE_NOMEM;
+ dst->exp_expect.dst = addr;
+ }
+
+ // Master CT
+ if (src->exp_master.src) {
+ addr = nl_addr_clone(src->exp_master.src);
+ if (!addr)
+ return -NLE_NOMEM;
+ dst->exp_master.src = addr;
+ }
+
+ if (src->exp_master.dst) {
+ addr = nl_addr_clone(src->exp_master.dst);
+ if (!addr)
+ return -NLE_NOMEM;
+ dst->exp_master.dst = addr;
+ }
+
+ // Mask
+ if (src->exp_mask.src) {
+ addr = nl_addr_clone(src->exp_mask.src);
+ if (!addr)
+ return -NLE_NOMEM;
+ dst->exp_mask.src = addr;
+ }
+
+ if (src->exp_mask.dst) {
+ addr = nl_addr_clone(src->exp_mask.dst);
+ if (!addr)
+ return -NLE_NOMEM;
+ dst->exp_mask.dst = addr;
+ }
+
+ // NAT
+ if (src->exp_nat.src) {
+ addr = nl_addr_clone(src->exp_nat.src);
+ if (!addr)
+ return -NLE_NOMEM;
+ dst->exp_nat.src = addr;
+ }
+
+ if (src->exp_nat.dst) {
+ addr = nl_addr_clone(src->exp_nat.dst);
+ if (!addr)
+ return -NLE_NOMEM;
+ dst->exp_nat.dst = addr;
+ }
+
+ if (src->exp_fn)
+ dst->exp_fn = strdup(src->exp_fn);
+
+ if (src->exp_helper_name)
+ dst->exp_helper_name = strdup(src->exp_helper_name);
+
+ return 0;
+}
+
+static void dump_addr(struct nl_dump_params *p, struct nl_addr *addr, int port)
+{
+ char buf[64];
+
+ if (addr)
+ nl_dump(p, "%s", nl_addr2str(addr, buf, sizeof(buf)));
+
+ if (port)
+ nl_dump(p, ":%u ", port);
+ else if (addr)
+ nl_dump(p, " ");
+}
+
+static void dump_icmp(struct nl_dump_params *p, struct nfnl_exp *exp, int tuple)
+{
+ if (nfnl_exp_test_icmp(exp, tuple)) {
+
+ nl_dump(p, "icmp type %d ", nfnl_exp_get_icmp_type(exp, tuple));
+
+ nl_dump(p, "code %d ", nfnl_exp_get_icmp_code(exp, tuple));
+
+ nl_dump(p, "id %d ", nfnl_exp_get_icmp_id(exp, tuple));
+ }
+}
+
+static void exp_dump_tuples(struct nfnl_exp *exp, struct nl_dump_params *p)
+{
+ struct nl_addr *tuple_src, *tuple_dst;
+ int tuple_sport, tuple_dport;
+ int i = 0;
+ char buf[64];
+
+ for (i = NFNL_EXP_TUPLE_EXPECT; i < NFNL_EXP_TUPLE_MAX; i++) {
+ tuple_src = NULL;
+ tuple_dst = NULL;
+ tuple_sport = 0;
+ tuple_dport = 0;
+
+ // Test needed for NAT case
+ if (nfnl_exp_test_src(exp, i))
+ tuple_src = nfnl_exp_get_src(exp, i);
+ if (nfnl_exp_test_dst(exp, i))
+ tuple_dst = nfnl_exp_get_dst(exp, i);
+
+ // Don't have tests for individual ports/types/codes/ids,
+ if (nfnl_exp_test_l4protonum(exp, i)) {
+ nl_dump(p, "%s ",
+ nl_ip_proto2str(nfnl_exp_get_l4protonum(exp, i), buf, sizeof(buf)));
+ }
+
+ if (nfnl_exp_test_ports(exp, i)) {
+ tuple_sport = nfnl_exp_get_src_port(exp, i);
+ tuple_dport = nfnl_exp_get_dst_port(exp, i);
+ }
+
+ dump_addr(p, tuple_src, tuple_sport);
+ dump_addr(p, tuple_dst, tuple_dport);
+ dump_icmp(p, exp, 0);
+ }
+
+ if (nfnl_exp_test_nat_dir(exp))
+ nl_dump(p, "nat dir %s ", exp->exp_nat_dir);
+
+}
+
+/* FIXME Compatible with /proc/net/nf_conntrack */
+static void exp_dump_line(struct nl_object *a, struct nl_dump_params *p)
+{
+ struct nfnl_exp *exp = (struct nfnl_exp *) a;
+
+ nl_new_line(p);
+
+ exp_dump_tuples(exp, p);
+
+ nl_dump(p, "\n");
+}
+
+static void exp_dump_details(struct nl_object *a, struct nl_dump_params *p)
+{
+ struct nfnl_exp *exp = (struct nfnl_exp *) a;
+ char buf[64];
+ int fp = 0;
+
+ exp_dump_line(a, p);
+
+ nl_dump(p, " id 0x%x ", exp->exp_id);
+ nl_dump_line(p, "family %s ",
+ nl_af2str(exp->exp_family, buf, sizeof(buf)));
+
+ if (nfnl_exp_test_timeout(exp)) {
+ uint64_t timeout_ms = nfnl_exp_get_timeout(exp) * 1000UL;
+ nl_dump(p, "timeout %s ",
+ nl_msec2str(timeout_ms, buf, sizeof(buf)));
+ }
+
+ if (nfnl_exp_test_helper_name(exp))
+ nl_dump(p, "helper %s ", exp->exp_helper_name);
+
+ if (nfnl_exp_test_fn(exp))
+ nl_dump(p, "fn %s ", exp->exp_fn);
+
+ if (nfnl_exp_test_class(exp))
+ nl_dump(p, "class %u ", nfnl_exp_get_class(exp));
+
+ if (nfnl_exp_test_zone(exp))
+ nl_dump(p, "zone %u ", nfnl_exp_get_zone(exp));
+
+ if (nfnl_exp_test_flags(exp))
+ nl_dump(p, "<");
+#define PRINT_FLAG(str) \
+ { nl_dump(p, "%s%s", fp++ ? "," : "", (str)); }
+
+ if (exp->exp_flags & NF_CT_EXPECT_PERMANENT)
+ PRINT_FLAG("PERMANENT");
+ if (exp->exp_flags & NF_CT_EXPECT_INACTIVE)
+ PRINT_FLAG("INACTIVE");
+ if (exp->exp_flags & NF_CT_EXPECT_USERSPACE)
+ PRINT_FLAG("USERSPACE");
+#undef PRINT_FLAG
+
+ if (nfnl_exp_test_flags(exp))
+ nl_dump(p, ">");
+
+ nl_dump(p, "\n");
+}
+
+static int exp_cmp_l4proto_ports (union nfnl_exp_protodata *a, union nfnl_exp_protodata *b) {
+ // Must return 0 for match, 1 for mismatch
+ int d = 0;
+ d = ( (a->port.src != b->port.src) ||
+ (a->port.dst != b->port.dst) );
+
+ return d;
+}
+
+static int exp_cmp_l4proto_icmp (union nfnl_exp_protodata *a, union nfnl_exp_protodata *b) {
+ // Must return 0 for match, 1 for mismatch
+ int d = 0;
+ d = ( (a->icmp.code != b->icmp.code) ||
+ (a->icmp.type != b->icmp.type) ||
+ (a->icmp.id != b->icmp.id) );
+
+ return d;
+}
+
+static int exp_compare(struct nl_object *_a, struct nl_object *_b,
+ uint32_t attrs, int flags)
+{
+ struct nfnl_exp *a = (struct nfnl_exp *) _a;
+ struct nfnl_exp *b = (struct nfnl_exp *) _b;
+ int diff = 0;
+
+#define EXP_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, EXP_ATTR_##ATTR, a, b, EXPR)
+#define EXP_DIFF_VAL(ATTR, FIELD) EXP_DIFF(ATTR, a->FIELD != b->FIELD)
+#define EXP_DIFF_STRING(ATTR, FIELD) EXP_DIFF(ATTR, (strcmp(a->FIELD, b->FIELD) != 0))
+#define EXP_DIFF_ADDR(ATTR, FIELD) \
+ ((flags & LOOSE_COMPARISON) \
+ ? EXP_DIFF(ATTR, nl_addr_cmp_prefix(a->FIELD, b->FIELD)) \
+ : EXP_DIFF(ATTR, nl_addr_cmp(a->FIELD, b->FIELD)))
+#define EXP_DIFF_L4PROTO_PORTS(ATTR, FIELD) \
+ EXP_DIFF(ATTR, exp_cmp_l4proto_ports(&(a->FIELD), &(b->FIELD)))
+#define EXP_DIFF_L4PROTO_ICMP(ATTR, FIELD) \
+ EXP_DIFF(ATTR, exp_cmp_l4proto_icmp(&(a->FIELD), &(b->FIELD)))
+
+ diff |= EXP_DIFF_VAL(FAMILY, exp_family);
+ diff |= EXP_DIFF_VAL(TIMEOUT, exp_timeout);
+ diff |= EXP_DIFF_VAL(ID, exp_id);
+ diff |= EXP_DIFF_VAL(ZONE, exp_zone);
+ diff |= EXP_DIFF_VAL(CLASS, exp_class);
+ diff |= EXP_DIFF_VAL(FLAGS, exp_flags);
+ diff |= EXP_DIFF_VAL(NAT_DIR, exp_nat_dir);
+
+ diff |= EXP_DIFF_STRING(FN, exp_fn);
+ diff |= EXP_DIFF_STRING(HELPER_NAME, exp_helper_name);
+
+ diff |= EXP_DIFF_ADDR(EXPECT_IP_SRC, exp_expect.src);
+ diff |= EXP_DIFF_ADDR(EXPECT_IP_DST, exp_expect.dst);
+ diff |= EXP_DIFF_VAL(EXPECT_L4PROTO_NUM, exp_expect.proto.l4protonum);
+ diff |= EXP_DIFF_L4PROTO_PORTS(EXPECT_L4PROTO_PORTS, exp_expect.proto.l4protodata);
+ diff |= EXP_DIFF_L4PROTO_ICMP(EXPECT_L4PROTO_ICMP, exp_expect.proto.l4protodata);
+
+ diff |= EXP_DIFF_ADDR(MASTER_IP_SRC, exp_master.src);
+ diff |= EXP_DIFF_ADDR(MASTER_IP_DST, exp_master.dst);
+ diff |= EXP_DIFF_VAL(MASTER_L4PROTO_NUM, exp_master.proto.l4protonum);
+ diff |= EXP_DIFF_L4PROTO_PORTS(MASTER_L4PROTO_PORTS, exp_master.proto.l4protodata);
+ diff |= EXP_DIFF_L4PROTO_ICMP(MASTER_L4PROTO_ICMP, exp_master.proto.l4protodata);
+
+ diff |= EXP_DIFF_ADDR(MASK_IP_SRC, exp_mask.src);
+ diff |= EXP_DIFF_ADDR(MASK_IP_DST, exp_mask.dst);
+ diff |= EXP_DIFF_VAL(MASK_L4PROTO_NUM, exp_mask.proto.l4protonum);
+ diff |= EXP_DIFF_L4PROTO_PORTS(MASK_L4PROTO_PORTS, exp_mask.proto.l4protodata);
+ diff |= EXP_DIFF_L4PROTO_ICMP(MASK_L4PROTO_ICMP, exp_mask.proto.l4protodata);
+
+ diff |= EXP_DIFF_ADDR(NAT_IP_SRC, exp_nat.src);
+ diff |= EXP_DIFF_ADDR(NAT_IP_DST, exp_nat.dst);
+ diff |= EXP_DIFF_VAL(NAT_L4PROTO_NUM, exp_nat.proto.l4protonum);
+ diff |= EXP_DIFF_L4PROTO_PORTS(NAT_L4PROTO_PORTS, exp_nat.proto.l4protodata);
+ diff |= EXP_DIFF_L4PROTO_ICMP(NAT_L4PROTO_ICMP, exp_nat.proto.l4protodata);
+
+#undef EXP_DIFF
+#undef EXP_DIFF_VAL
+#undef EXP_DIFF_STRING
+#undef EXP_DIFF_ADDR
+#undef EXP_DIFF_L4PROTO_PORTS
+#undef EXP_DIFF_L4PROTO_ICMP
+
+ return diff;
+}
+
+// CLI arguments?
+static const struct trans_tbl exp_attrs[] = {
+ __ADD(EXP_ATTR_FAMILY, family)
+ __ADD(EXP_ATTR_TIMEOUT, timeout)
+ __ADD(EXP_ATTR_ID, id)
+ __ADD(EXP_ATTR_HELPER_NAME, helpername)
+ __ADD(EXP_ATTR_ZONE, zone)
+ __ADD(EXP_ATTR_CLASS, class)
+ __ADD(EXP_ATTR_FLAGS, flags)
+ __ADD(EXP_ATTR_FN, function)
+ __ADD(EXP_ATTR_EXPECT_IP_SRC, expectipsrc)
+ __ADD(EXP_ATTR_EXPECT_IP_DST, expectipdst)
+ __ADD(EXP_ATTR_EXPECT_L4PROTO_NUM, expectprotonum)
+ __ADD(EXP_ATTR_EXPECT_L4PROTO_PORTS, expectports)
+ __ADD(EXP_ATTR_EXPECT_L4PROTO_ICMP, expecticmp)
+ __ADD(EXP_ATTR_MASTER_IP_SRC, masteripsrc)
+ __ADD(EXP_ATTR_MASTER_IP_DST, masteripdst)
+ __ADD(EXP_ATTR_MASTER_L4PROTO_NUM, masterprotonum)
+ __ADD(EXP_ATTR_MASTER_L4PROTO_PORTS, masterports)
+ __ADD(EXP_ATTR_MASTER_L4PROTO_ICMP, mastericmp)
+ __ADD(EXP_ATTR_MASK_IP_SRC, maskipsrc)
+ __ADD(EXP_ATTR_MASK_IP_DST, maskipdst)
+ __ADD(EXP_ATTR_MASK_L4PROTO_NUM, maskprotonum)
+ __ADD(EXP_ATTR_MASK_L4PROTO_PORTS, maskports)
+ __ADD(EXP_ATTR_MASK_L4PROTO_ICMP, maskicmp)
+ __ADD(EXP_ATTR_NAT_IP_SRC, natipsrc)
+ __ADD(EXP_ATTR_NAT_IP_DST, natipdst)
+ __ADD(EXP_ATTR_NAT_L4PROTO_NUM, natprotonum)
+ __ADD(EXP_ATTR_NAT_L4PROTO_PORTS, natports)
+ __ADD(EXP_ATTR_NAT_L4PROTO_ICMP, naticmp)
+ __ADD(EXP_ATTR_NAT_DIR, natdir)
+};
+
+static char *exp_attrs2str(int attrs, char *buf, size_t len)
+{
+ return __flags2str(attrs, buf, len, exp_attrs, ARRAY_SIZE(exp_attrs));
+}
+
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct nfnl_exp *nfnl_exp_alloc(void)
+{
+ return (struct nfnl_exp *) nl_object_alloc(&exp_obj_ops);
+}
+
+void nfnl_exp_get(struct nfnl_exp *exp)
+{
+ nl_object_get((struct nl_object *) exp);
+}
+
+void nfnl_exp_put(struct nfnl_exp *exp)
+{
+ nl_object_put((struct nl_object *) exp);
+}
+
+/** @} */
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+void nfnl_exp_set_family(struct nfnl_exp *exp, uint8_t family)
+{
+ exp->exp_family = family;
+ exp->ce_mask |= EXP_ATTR_FAMILY;
+}
+
+uint8_t nfnl_exp_get_family(const struct nfnl_exp *exp)
+{
+ if (exp->ce_mask & EXP_ATTR_FAMILY)
+ return exp->exp_family;
+ else
+ return AF_UNSPEC;
+}
+
+void nfnl_exp_set_flags(struct nfnl_exp *exp, uint32_t flags)
+{
+ exp->exp_flags |= flags;
+ exp->ce_mask |= EXP_ATTR_FLAGS;
+}
+
+int nfnl_exp_test_flags(const struct nfnl_exp *exp)
+{
+ return !!(exp->ce_mask & EXP_ATTR_FLAGS);
+}
+
+void nfnl_exp_unset_flags(struct nfnl_exp *exp, uint32_t flags)
+{
+ exp->exp_flags &= ~flags;
+ exp->ce_mask |= EXP_ATTR_FLAGS;
+}
+
+uint32_t nfnl_exp_get_flags(const struct nfnl_exp *exp)
+{
+ return exp->exp_flags;
+}
+
+static const struct trans_tbl flag_table[] = {
+ __ADD(IPS_EXPECTED, expected)
+ __ADD(IPS_SEEN_REPLY, seen_reply)
+ __ADD(IPS_ASSURED, assured)
+};
+
+char * nfnl_exp_flags2str(int flags, char *buf, size_t len)
+{
+ return __flags2str(flags, buf, len, flag_table,
+ ARRAY_SIZE(flag_table));
+}
+
+int nfnl_exp_str2flags(const char *name)
+{
+ return __str2flags(name, flag_table, ARRAY_SIZE(flag_table));
+}
+
+void nfnl_exp_set_timeout(struct nfnl_exp *exp, uint32_t timeout)
+{
+ exp->exp_timeout = timeout;
+ exp->ce_mask |= EXP_ATTR_TIMEOUT;
+}
+
+int nfnl_exp_test_timeout(const struct nfnl_exp *exp)
+{
+ return !!(exp->ce_mask & EXP_ATTR_TIMEOUT);
+}
+
+uint32_t nfnl_exp_get_timeout(const struct nfnl_exp *exp)
+{
+ return exp->exp_timeout;
+}
+
+void nfnl_exp_set_id(struct nfnl_exp *exp, uint32_t id)
+{
+ exp->exp_id = id;
+ exp->ce_mask |= EXP_ATTR_ID;
+}
+
+int nfnl_exp_test_id(const struct nfnl_exp *exp)
+{
+ return !!(exp->ce_mask & EXP_ATTR_ID);
+}
+
+uint32_t nfnl_exp_get_id(const struct nfnl_exp *exp)
+{
+ return exp->exp_id;
+}
+
+int nfnl_exp_set_helper_name(struct nfnl_exp *exp, void *name)
+{
+ free(exp->exp_helper_name);
+ exp->exp_helper_name = strdup(name);
+ if (!exp->exp_helper_name)
+ return -NLE_NOMEM;
+
+ exp->ce_mask |= EXP_ATTR_HELPER_NAME;
+ return 0;
+}
+
+int nfnl_exp_test_helper_name(const struct nfnl_exp *exp)
+{
+ return !!(exp->ce_mask & EXP_ATTR_HELPER_NAME);
+}
+
+const char * nfnl_exp_get_helper_name(const struct nfnl_exp *exp)
+{
+ return exp->exp_helper_name;
+}
+
+void nfnl_exp_set_zone(struct nfnl_exp *exp, uint16_t zone)
+{
+ exp->exp_zone = zone;
+ exp->ce_mask |= EXP_ATTR_ZONE;
+}
+
+int nfnl_exp_test_zone(const struct nfnl_exp *exp)
+{
+ return !!(exp->ce_mask & EXP_ATTR_ZONE);
+}
+
+uint16_t nfnl_exp_get_zone(const struct nfnl_exp *exp)
+{
+ return exp->exp_zone;
+}
+
+void nfnl_exp_set_class(struct nfnl_exp *exp, uint32_t class)
+{
+ exp->exp_class = class;
+ exp->ce_mask |= EXP_ATTR_CLASS;
+}
+
+int nfnl_exp_test_class(const struct nfnl_exp *exp)
+{
+ return !!(exp->ce_mask & EXP_ATTR_CLASS);
+}
+
+uint32_t nfnl_exp_get_class(const struct nfnl_exp *exp)
+{
+ return exp->exp_class;
+}
+
+int nfnl_exp_set_fn(struct nfnl_exp *exp, void *fn)
+{
+ free(exp->exp_fn);
+ exp->exp_fn = strdup(fn);
+ if (!exp->exp_fn)
+ return -NLE_NOMEM;
+
+ exp->ce_mask |= EXP_ATTR_FN;
+ return 0;
+}
+
+int nfnl_exp_test_fn(const struct nfnl_exp *exp)
+{
+ return !!(exp->ce_mask & EXP_ATTR_FN);
+}
+
+const char * nfnl_exp_get_fn(const struct nfnl_exp *exp)
+{
+ return exp->exp_fn;
+}
+
+void nfnl_exp_set_nat_dir(struct nfnl_exp *exp, uint8_t nat_dir)
+{
+ exp->exp_nat_dir = nat_dir;
+ exp->ce_mask |= EXP_ATTR_NAT_DIR;
+}
+
+int nfnl_exp_test_nat_dir(const struct nfnl_exp *exp)
+{
+ return !!(exp->ce_mask & EXP_ATTR_NAT_DIR);
+}
+
+uint8_t nfnl_exp_get_nat_dir(const struct nfnl_exp *exp)
+{
+ return exp->exp_nat_dir;
+}
+
+#define EXP_GET_TUPLE(e, t) \
+ (t == NFNL_EXP_TUPLE_MASTER) ? \
+ &(e->exp_master) : \
+ (t == NFNL_EXP_TUPLE_MASK) ? \
+ &(e->exp_mask) : \
+ (t == NFNL_EXP_TUPLE_NAT) ? \
+ &(e->exp_nat) : &(exp->exp_expect)
+
+static int exp_get_src_attr(int tuple)
+{
+ int attr = 0;
+
+ switch (tuple) {
+ case NFNL_EXP_TUPLE_MASTER:
+ attr = EXP_ATTR_MASTER_IP_SRC;
+ break;
+ case NFNL_EXP_TUPLE_MASK:
+ attr = EXP_ATTR_MASK_IP_SRC;
+ break;
+ case NFNL_EXP_TUPLE_NAT:
+ attr = EXP_ATTR_NAT_IP_SRC;
+ break;
+ case NFNL_EXP_TUPLE_EXPECT:
+ default :
+ attr = EXP_ATTR_EXPECT_IP_SRC;
+ }
+
+ return attr;
+}
+
+static int exp_get_dst_attr(int tuple)
+{
+ int attr = 0;
+
+ switch (tuple) {
+ case NFNL_EXP_TUPLE_MASTER:
+ attr = EXP_ATTR_MASTER_IP_DST;
+ break;
+ case NFNL_EXP_TUPLE_MASK:
+ attr = EXP_ATTR_MASK_IP_DST;
+ break;
+ case NFNL_EXP_TUPLE_NAT:
+ attr = EXP_ATTR_NAT_IP_DST;
+ break;
+ case NFNL_EXP_TUPLE_EXPECT:
+ default :
+ attr = EXP_ATTR_EXPECT_IP_DST;
+ }
+
+ return attr;
+}
+
+
+static int exp_set_addr(struct nfnl_exp *exp, struct nl_addr *addr,
+ int attr, struct nl_addr ** exp_addr)
+{
+ if (exp->ce_mask & EXP_ATTR_FAMILY) {
+ if (addr->a_family != exp->exp_family)
+ return -NLE_AF_MISMATCH;
+ } else
+ nfnl_exp_set_family(exp, addr->a_family);
+
+ if (*exp_addr)
+ nl_addr_put(*exp_addr);
+
+ nl_addr_get(addr);
+ *exp_addr = addr;
+ exp->ce_mask |= attr;
+
+ return 0;
+}
+
+int nfnl_exp_set_src(struct nfnl_exp *exp, int tuple, struct nl_addr *addr)
+{
+ struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple);
+
+ return exp_set_addr(exp, addr, exp_get_src_attr(tuple), &dir->src);
+}
+
+int nfnl_exp_set_dst(struct nfnl_exp *exp, int tuple, struct nl_addr *addr)
+{
+ struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple);
+
+ return exp_set_addr(exp, addr, exp_get_dst_attr(tuple), &dir->dst);
+}
+
+int nfnl_exp_test_src(const struct nfnl_exp *exp, int tuple)
+{
+ return !!(exp->ce_mask & exp_get_src_attr(tuple));
+}
+
+int nfnl_exp_test_dst(const struct nfnl_exp *exp, int tuple)
+{
+ return !!(exp->ce_mask & exp_get_dst_attr(tuple));
+}
+
+struct nl_addr *nfnl_exp_get_src(const struct nfnl_exp *exp, int tuple)
+{
+ const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple);
+
+ if (!(exp->ce_mask & exp_get_src_attr(tuple)))
+ return NULL;
+ return dir->src;
+}
+
+struct nl_addr *nfnl_exp_get_dst(const struct nfnl_exp *exp, int tuple)
+{
+ const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple);
+
+ if (!(exp->ce_mask & exp_get_dst_attr(tuple)))
+ return NULL;
+ return dir->dst;
+}
+
+static int exp_get_l4protonum_attr(int tuple)
+{
+ int attr = 0;
+
+ switch (tuple) {
+ case NFNL_EXP_TUPLE_MASTER:
+ attr = EXP_ATTR_MASTER_L4PROTO_NUM;
+ break;
+ case NFNL_EXP_TUPLE_MASK:
+ attr = EXP_ATTR_MASK_L4PROTO_NUM;
+ break;
+ case NFNL_EXP_TUPLE_NAT:
+ attr = EXP_ATTR_NAT_L4PROTO_NUM;
+ break;
+ case NFNL_EXP_TUPLE_EXPECT:
+ default :
+ attr = EXP_ATTR_EXPECT_L4PROTO_NUM;
+ }
+
+ return attr;
+}
+
+void nfnl_exp_set_l4protonum(struct nfnl_exp *exp, int tuple, uint8_t l4protonum)
+{
+ struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple);
+
+ dir->proto.l4protonum = l4protonum;
+ exp->ce_mask |= exp_get_l4protonum_attr(tuple);
+}
+
+int nfnl_exp_test_l4protonum(const struct nfnl_exp *exp, int tuple)
+{
+ return !!(exp->ce_mask & exp_get_l4protonum_attr(tuple));
+}
+
+uint8_t nfnl_exp_get_l4protonum(const struct nfnl_exp *exp, int tuple)
+{
+ const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple);
+ return dir->proto.l4protonum;
+}
+
+static int exp_get_l4ports_attr(int tuple)
+{
+ int attr = 0;
+
+ switch (tuple) {
+ case NFNL_EXP_TUPLE_MASTER:
+ attr = EXP_ATTR_MASTER_L4PROTO_PORTS;
+ break;
+ case NFNL_EXP_TUPLE_MASK:
+ attr = EXP_ATTR_MASK_L4PROTO_PORTS;
+ break;
+ case NFNL_EXP_TUPLE_NAT:
+ attr = EXP_ATTR_NAT_L4PROTO_PORTS;
+ break;
+ case NFNL_EXP_TUPLE_EXPECT:
+ default :
+ attr = EXP_ATTR_EXPECT_L4PROTO_PORTS;
+ }
+
+ return attr;
+}
+
+void nfnl_exp_set_ports(struct nfnl_exp *exp, int tuple, uint16_t srcport, uint16_t dstport)
+{
+ struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple);
+
+ dir->proto.l4protodata.port.src = srcport;
+ dir->proto.l4protodata.port.dst = dstport;
+
+ exp->ce_mask |= exp_get_l4ports_attr(tuple);
+}
+
+int nfnl_exp_test_ports(const struct nfnl_exp *exp, int tuple)
+{
+ return !!(exp->ce_mask & exp_get_l4ports_attr(tuple));
+}
+
+uint16_t nfnl_exp_get_src_port(const struct nfnl_exp *exp, int tuple)
+{
+ const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple);
+ return dir->proto.l4protodata.port.src;
+}
+
+uint16_t nfnl_exp_get_dst_port(const struct nfnl_exp *exp, int tuple)
+{
+ const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple);
+
+ return dir->proto.l4protodata.port.dst;
+}
+
+static int exp_get_l4icmp_attr(int tuple)
+{
+ int attr = 0;
+
+ switch (tuple) {
+ case NFNL_EXP_TUPLE_MASTER:
+ attr = EXP_ATTR_MASTER_L4PROTO_ICMP;
+ break;
+ case NFNL_EXP_TUPLE_MASK:
+ attr = EXP_ATTR_MASK_L4PROTO_ICMP;
+ break;
+ case NFNL_EXP_TUPLE_NAT:
+ attr = EXP_ATTR_NAT_L4PROTO_ICMP;
+ break;
+ case NFNL_EXP_TUPLE_EXPECT:
+ default :
+ attr = EXP_ATTR_EXPECT_L4PROTO_ICMP;
+ }
+
+ return attr;
+}
+
+void nfnl_exp_set_icmp(struct nfnl_exp *exp, int tuple, uint16_t id, uint8_t type, uint8_t code)
+{
+ struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple);
+
+ dir->proto.l4protodata.icmp.id = id;
+ dir->proto.l4protodata.icmp.type = type;
+ dir->proto.l4protodata.icmp.code = code;
+
+ exp->ce_mask |= exp_get_l4icmp_attr(tuple);
+}
+
+int nfnl_exp_test_icmp(const struct nfnl_exp *exp, int tuple)
+{
+ int attr = exp_get_l4icmp_attr(tuple);
+ return !!(exp->ce_mask & attr);
+}
+
+uint16_t nfnl_exp_get_icmp_id(const struct nfnl_exp *exp, int tuple)
+{
+ const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple);
+
+ return dir->proto.l4protodata.icmp.id;
+}
+
+uint8_t nfnl_exp_get_icmp_type(const struct nfnl_exp *exp, int tuple)
+{
+ const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple);
+
+ return dir->proto.l4protodata.icmp.type;
+}
+
+uint8_t nfnl_exp_get_icmp_code(const struct nfnl_exp *exp, int tuple)
+{
+ const struct nfnl_exp_dir *dir = EXP_GET_TUPLE(exp, tuple);
+
+ return dir->proto.l4protodata.icmp.code;
+}
+
+/** @} */
+
+struct nl_object_ops exp_obj_ops = {
+ .oo_name = "netfilter/exp",
+ .oo_size = sizeof(struct nfnl_exp),
+ .oo_free_data = exp_free_data,
+ .oo_clone = exp_clone,
+ .oo_dump = {
+ [NL_DUMP_LINE] = exp_dump_line,
+ [NL_DUMP_DETAILS] = exp_dump_details,
+ },
+ .oo_compare = exp_compare,
+ .oo_attrs2str = exp_attrs2str,
+};
+
+/** @} */
diff --git a/lib/netfilter/log.c b/lib/netfilter/log.c
index 96ae6c51..1bab9b6b 100644
--- a/lib/netfilter/log.c
+++ b/lib/netfilter/log.c
@@ -21,7 +21,7 @@
#include <sys/types.h>
#include <linux/netfilter/nfnetlink_log.h>
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/attr.h>
#include <netlink/netfilter/nfnl.h>
#include <netlink/netfilter/log.h>
diff --git a/lib/netfilter/log_msg.c b/lib/netfilter/log_msg.c
index cad6ddd0..5ffdaf80 100644
--- a/lib/netfilter/log_msg.c
+++ b/lib/netfilter/log_msg.c
@@ -22,10 +22,11 @@
#include <sys/types.h>
#include <linux/netfilter/nfnetlink_log.h>
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/attr.h>
#include <netlink/netfilter/nfnl.h>
#include <netlink/netfilter/log_msg.h>
+#include <byteswap.h>
#if __BYTE_ORDER == __BIG_ENDIAN
static uint64_t ntohll(uint64_t x)
@@ -35,7 +36,7 @@ static uint64_t ntohll(uint64_t x)
#elif __BYTE_ORDER == __LITTLE_ENDIAN
static uint64_t ntohll(uint64_t x)
{
- return __bswap_64(x);
+ return bswap_64(x);
}
#endif
@@ -173,10 +174,9 @@ static int log_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
int err;
if ((err = nfnlmsg_log_msg_parse(nlh, &msg)) < 0)
- goto errout;
+ return err;
err = pp->pp_cb((struct nl_object *) msg, pp);
-errout:
nfnl_log_msg_put(msg);
return err;
}
diff --git a/lib/netfilter/log_msg_obj.c b/lib/netfilter/log_msg_obj.c
index d2cde4e0..57db9d47 100644
--- a/lib/netfilter/log_msg_obj.c
+++ b/lib/netfilter/log_msg_obj.c
@@ -11,7 +11,7 @@
* Copyright (c) 2007 Secure Computing Corporation
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netfilter/nfnl.h>
#include <netlink/netfilter/netfilter.h>
#include <netlink/netfilter/log_msg.h>
@@ -76,7 +76,7 @@ static void log_msg_dump(struct nl_object *a, struct nl_dump_params *p)
struct nl_cache *link_cache;
char buf[64];
- link_cache = nl_cache_mngt_require("route/link");
+ link_cache = nl_cache_mngt_require_safe("route/link");
nl_new_line(p);
@@ -167,6 +167,9 @@ static void log_msg_dump(struct nl_object *a, struct nl_dump_params *p)
nl_dump(p, "SEQGLOBAL=%d ", msg->log_msg_seq_global);
nl_dump(p, "\n");
+
+ if (link_cache)
+ nl_cache_put(link_cache);
}
/**
diff --git a/lib/netfilter/log_obj.c b/lib/netfilter/log_obj.c
index ff2b63ae..2b114142 100644
--- a/lib/netfilter/log_obj.c
+++ b/lib/netfilter/log_obj.c
@@ -12,7 +12,7 @@
* Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netfilter/nfnl.h>
#include <netlink/netfilter/log.h>
@@ -56,7 +56,7 @@ static void nfnl_log_dump(struct nl_object *a, struct nl_dump_params *p)
nl_dump(p, "\n");
}
-static struct trans_tbl copy_modes[] = {
+static const struct trans_tbl copy_modes[] = {
__ADD(NFNL_LOG_COPY_NONE, none)
__ADD(NFNL_LOG_COPY_META, meta)
__ADD(NFNL_LOG_COPY_PACKET, packet)
@@ -214,7 +214,7 @@ void nfnl_log_unset_flags(struct nfnl_log *log, unsigned int flags)
log->log_flag_mask |= flags;
}
-static struct trans_tbl log_flags[] = {
+static const struct trans_tbl log_flags[] = {
__ADD(NFNL_LOG_FLAG_SEQ, seq)
__ADD(NFNL_LOG_FLAG_SEQ_GLOBAL, seq_global)
};
@@ -254,7 +254,7 @@ static int nfnl_log_compare(struct nl_object *_a, struct nl_object *_b,
return diff;
}
-static struct trans_tbl nfnl_log_attrs[] = {
+static const struct trans_tbl nfnl_log_attrs[] = {
__ADD(LOG_ATTR_GROUP, group)
__ADD(LOG_ATTR_COPY_MODE, copy_mode)
__ADD(LOG_ATTR_COPY_RANGE, copy_range)
diff --git a/lib/netfilter/netfilter.c b/lib/netfilter/netfilter.c
index f88b3559..0062994f 100644
--- a/lib/netfilter/netfilter.c
+++ b/lib/netfilter/netfilter.c
@@ -9,11 +9,11 @@
* Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netfilter/netfilter.h>
#include <linux/netfilter.h>
-static struct trans_tbl nfnl_verdicts[] = {
+static const struct trans_tbl nfnl_verdicts[] = {
__ADD(NF_DROP, NF_DROP)
__ADD(NF_ACCEPT, NF_ACCEPT)
__ADD(NF_STOLEN, NF_STOLEN)
@@ -33,7 +33,7 @@ unsigned int nfnl_str2verdict(const char *name)
return __str2type(name, nfnl_verdicts, ARRAY_SIZE(nfnl_verdicts));
}
-static struct trans_tbl nfnl_inet_hooks[] = {
+static const struct trans_tbl nfnl_inet_hooks[] = {
__ADD(NF_INET_PRE_ROUTING, NF_INET_PREROUTING)
__ADD(NF_INET_LOCAL_IN, NF_INET_LOCAL_IN)
__ADD(NF_INET_FORWARD, NF_INET_FORWARD)
diff --git a/lib/netfilter/nfnl.c b/lib/netfilter/nfnl.c
index ddce4b98..f028a859 100644
--- a/lib/netfilter/nfnl.c
+++ b/lib/netfilter/nfnl.c
@@ -6,13 +6,13 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
* Copyright (c) 2007 Secure Computing Corporation
*/
/**
- * @defgroup nfnl Netfilter Netlink
+ * @defgroup nfnl Netfilter Library (libnl-nf)
*
* @par Message Format
* @code
@@ -61,7 +61,7 @@
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/netfilter/nfnl.h>
diff --git a/lib/netfilter/queue.c b/lib/netfilter/queue.c
index ff1de0e0..56556479 100644
--- a/lib/netfilter/queue.c
+++ b/lib/netfilter/queue.c
@@ -19,7 +19,7 @@
#include <sys/types.h>
#include <linux/netfilter/nfnetlink_queue.h>
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/attr.h>
#include <netlink/netfilter/nfnl.h>
#include <netlink/netfilter/queue.h>
diff --git a/lib/netfilter/queue_msg.c b/lib/netfilter/queue_msg.c
index ab0a58be..33889233 100644
--- a/lib/netfilter/queue_msg.c
+++ b/lib/netfilter/queue_msg.c
@@ -20,10 +20,11 @@
#include <sys/types.h>
#include <linux/netfilter/nfnetlink_queue.h>
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/attr.h>
#include <netlink/netfilter/nfnl.h>
#include <netlink/netfilter/queue_msg.h>
+#include <byteswap.h>
static struct nl_cache_ops nfnl_queue_msg_ops;
@@ -35,7 +36,7 @@ static uint64_t ntohll(uint64_t x)
#elif __BYTE_ORDER == __LITTLE_ENDIAN
static uint64_t ntohll(uint64_t x)
{
- return __bswap_64(x);
+ return bswap_64(x);
}
#endif
@@ -152,22 +153,23 @@ static int queue_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
int err;
if ((err = nfnlmsg_queue_msg_parse(nlh, &msg)) < 0)
- goto errout;
+ return err;
err = pp->pp_cb((struct nl_object *) msg, pp);
-errout:
nfnl_queue_msg_put(msg);
return err;
}
/** @} */
-struct nl_msg *nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *msg)
+static struct nl_msg *
+__nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *msg,
+ uint8_t type)
{
struct nl_msg *nlmsg;
struct nfqnl_msg_verdict_hdr verdict;
- nlmsg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, NFQNL_MSG_VERDICT, 0,
+ nlmsg = nfnlmsg_alloc_simple(NFNL_SUBSYS_QUEUE, type, 0,
nfnl_queue_msg_get_family(msg),
nfnl_queue_msg_get_group(msg));
if (nlmsg == NULL)
@@ -190,6 +192,18 @@ nla_put_failure:
return NULL;
}
+struct nl_msg *
+nfnl_queue_msg_build_verdict(const struct nfnl_queue_msg *msg)
+{
+ return __nfnl_queue_msg_build_verdict(msg, NFQNL_MSG_VERDICT);
+}
+
+struct nl_msg *
+nfnl_queue_msg_build_verdict_batch(const struct nfnl_queue_msg *msg)
+{
+ return __nfnl_queue_msg_build_verdict(msg, NFQNL_MSG_VERDICT_BATCH);
+}
+
/**
* Send a message verdict/mark
* @arg nlh netlink messsage header
@@ -214,6 +228,29 @@ int nfnl_queue_msg_send_verdict(struct nl_sock *nlh,
}
/**
+* Send a message batched verdict/mark
+* @arg nlh netlink messsage header
+* @arg msg queue msg
+* @return 0 on OK or error code
+*/
+int nfnl_queue_msg_send_verdict_batch(struct nl_sock *nlh,
+ const struct nfnl_queue_msg *msg)
+{
+ struct nl_msg *nlmsg;
+ int err;
+
+ nlmsg = nfnl_queue_msg_build_verdict_batch(msg);
+ if (nlmsg == NULL)
+ return -NLE_NOMEM;
+
+ err = nl_send_auto_complete(nlh, nlmsg);
+ nlmsg_free(nlmsg);
+ if (err < 0)
+ return err;
+ return wait_for_ack(nlh);
+}
+
+/**
* Send a message verdict including the payload
* @arg nlh netlink messsage header
* @arg msg queue msg
@@ -249,7 +286,7 @@ int nfnl_queue_msg_send_verdict_payload(struct nl_sock *nlh,
iov[2].iov_base = (void *) payload_data;
iov[2].iov_len = NLA_ALIGN(payload_len);
- nl_auto_complete(nlh, nlmsg);
+ nl_complete_msg(nlh, nlmsg);
err = nl_send_iovec(nlh, nlmsg, iov, 3);
nlmsg_free(nlmsg);
diff --git a/lib/netfilter/queue_msg_obj.c b/lib/netfilter/queue_msg_obj.c
index 97813e88..8085f1b3 100644
--- a/lib/netfilter/queue_msg_obj.c
+++ b/lib/netfilter/queue_msg_obj.c
@@ -9,7 +9,7 @@
* Copyright (c) 2007, 2008 Patrick McHardy <kaber@trash.net>
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netfilter/nfnl.h>
#include <netlink/netfilter/netfilter.h>
#include <netlink/netfilter/queue_msg.h>
@@ -66,7 +66,7 @@ static void nfnl_queue_msg_dump(struct nl_object *a, struct nl_dump_params *p)
struct nl_cache *link_cache;
char buf[64];
- link_cache = nl_cache_mngt_require("route/link");
+ link_cache = nl_cache_mngt_require_safe("route/link");
nl_new_line(p);
@@ -152,6 +152,9 @@ static void nfnl_queue_msg_dump(struct nl_object *a, struct nl_dump_params *p)
buf, sizeof(buf)));
nl_dump(p, "\n");
+
+ if (link_cache)
+ nl_cache_put(link_cache);
}
/**
@@ -451,7 +454,7 @@ unsigned int nfnl_queue_msg_get_verdict(const struct nfnl_queue_msg *msg)
return msg->queue_msg_verdict;
}
-static struct trans_tbl nfnl_queue_msg_attrs[] = {
+static const struct trans_tbl nfnl_queue_msg_attrs[] = {
__ADD(QUEUE_MSG_ATTR_GROUP, group)
__ADD(QUEUE_MSG_ATTR_FAMILY, family)
__ADD(QUEUE_MSG_ATTR_PACKETID, packetid)
diff --git a/lib/netfilter/queue_obj.c b/lib/netfilter/queue_obj.c
index ee03836d..5edcc684 100644
--- a/lib/netfilter/queue_obj.c
+++ b/lib/netfilter/queue_obj.c
@@ -16,7 +16,7 @@
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netfilter/nfnl.h>
#include <netlink/netfilter/queue.h>
@@ -52,7 +52,7 @@ static void nfnl_queue_dump(struct nl_object *a, struct nl_dump_params *p)
nl_dump(p, "\n");
}
-static struct trans_tbl copy_modes[] = {
+static const struct trans_tbl copy_modes[] = {
__ADD(NFNL_QUEUE_COPY_NONE, none)
__ADD(NFNL_QUEUE_COPY_META, meta)
__ADD(NFNL_QUEUE_COPY_PACKET, packet)
@@ -184,7 +184,7 @@ static int nfnl_queue_compare(struct nl_object *_a, struct nl_object *_b,
return diff;
}
-static struct trans_tbl nfnl_queue_attrs[] = {
+static const struct trans_tbl nfnl_queue_attrs[] = {
__ADD(QUEUE_ATTR_GROUP, group)
__ADD(QUEUE_ATTR_MAXLEN, maxlen)
__ADD(QUEUE_ATTR_COPY_MODE, copy_mode)
diff --git a/lib/nl.c b/lib/nl.c
index c453b609..25fd59cf 100644
--- a/lib/nl.c
+++ b/lib/nl.c
@@ -6,82 +6,27 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @defgroup core Core
- *
- * @details
- * @par 1) Connecting the socket
- * @code
- * // Bind and connect the socket to a protocol, NETLINK_ROUTE in this example.
- * nl_connect(sk, 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(sk, 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(sk, 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(sk, 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 socket and the message
- * // flags NLM_F_REQUEST and NLM_F_ACK (if not disabled in the socket)
- * nl_send_auto_complete(sk, 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(sk, 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(sk, &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(sk, my_callback_configuration);
- *
- * // nl_recvmsgs_default() acts just like nl_recvmsg() but uses the callback
- * // configuration stored in the socket.
- * nl_recvmsgs_default(sk);
- *
- * // 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(sk);
- * @endcode
- *
- * @par 4) Closing
- * @code
- * // Close the socket first to release kernel memory
- * nl_close(sk);
- * @endcode
- *
+ * @defgroup core Core Library (libnl)
+ *
+ * Socket handling, connection management, sending and receiving of data,
+ * message construction and parsing, object caching system, ...
+ *
+ * This is the API reference of the core library. It is not meant as a guide
+ * but as a reference. Please refer to the core library guide for detailed
+ * documentation on the library architecture and examples:
+ *
+ * * @ref_asciidoc{core,_,Netlink Core Library Development Guide}
+ *
+ *
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/socket.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/handlers.h>
@@ -89,28 +34,83 @@
#include <netlink/attr.h>
/**
+ * @defgroup core_types Data Types
+ *
+ * Core library data types
+ * @{
+ * @}
+ *
+ * @defgroup send_recv Send & Receive Data
+ *
+ * Connection management, sending & receiving of data
+ *
+ * Related sections in the development guide:
+ * - @core_doc{core_send_recv, Sending & Receiving}
+ * - @core_doc{core_sockets, Sockets}
+ *
+ * @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/netlink.h>
+ * ~~~~
+ */
+
+/**
* @name Connection Management
* @{
*/
/**
- * Create and connect netlink socket.
- * @arg sk Netlink socket.
- * @arg protocol Netlink protocol to use.
+ * Create file descriptor and bind socket.
+ * @arg sk Netlink socket (required)
+ * @arg protocol Netlink protocol to use (required)
+ *
+ * Creates a new Netlink socket using `socket()` and binds the socket to the
+ * protocol and local port specified in the `sk` socket object. Fails if
+ * the socket is already connected.
+ *
+ * @note If available, the `close-on-exec` (`SOCK_CLOEXEC`) feature is enabled
+ * automatically on the new file descriptor. This causes the socket to
+ * be closed automatically if any of the `exec` family functions succeed.
+ * This is essential for multi threaded programs.
+ *
+ * @note The local port (`nl_socket_get_local_port()`) is unspecified after
+ * creating a new socket. It only gets determined when accessing the
+ * port the first time or during `nl_connect()`. When nl_connect()
+ * fails during `bind()` due to `ADDRINUSE`, it will retry with
+ * different ports if the port is unspecified. Unless you want to enforce
+ * the use of a specific local port, don't access the local port (or
+ * reset it to `unspecified` by calling `nl_socket_set_local_port(sk, 0)`).
+ * This capability is indicated by
+ * `%NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE`.
*
- * Creates a netlink socket using the specified protocol, binds the socket
- * and issues a connection attempt.
+ * @see nl_socket_alloc()
+ * @see nl_close()
*
* @return 0 on success or a negative error code.
+ *
+ * @retval -NLE_BAD_SOCK Socket is already connected
*/
int nl_connect(struct nl_sock *sk, int protocol)
{
- int err;
+ int err, flags = 0;
+ int errsv;
socklen_t addrlen;
- sk->s_fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+#ifdef SOCK_CLOEXEC
+ flags |= SOCK_CLOEXEC;
+#endif
+
+ if (sk->s_fd != -1)
+ return -NLE_BAD_SOCK;
+
+ sk->s_fd = socket(AF_NETLINK, SOCK_RAW | flags, protocol);
if (sk->s_fd < 0) {
- err = -nl_syserr2nlerr(errno);
+ errsv = errno;
+ NL_DBG(4, "nl_connect(%p): socket() failed with %d\n", sk, errsv);
+ err = -nl_syserr2nlerr(errsv);
goto errout;
}
@@ -120,11 +120,45 @@ int nl_connect(struct nl_sock *sk, int protocol)
goto errout;
}
- err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
- sizeof(sk->s_local));
- if (err < 0) {
- err = -nl_syserr2nlerr(errno);
- goto errout;
+ if (_nl_socket_is_local_port_unspecified (sk)) {
+ uint32_t port;
+ uint32_t used_ports[32] = { 0 };
+
+ while (1) {
+ port = _nl_socket_generate_local_port_no_release(sk);
+
+ if (port == UINT32_MAX) {
+ NL_DBG(4, "nl_connect(%p): no more unused local ports.\n", sk);
+ _nl_socket_used_ports_release_all(used_ports);
+ err = -NLE_EXIST;
+ goto errout;
+ }
+ err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
+ sizeof(sk->s_local));
+ if (err == 0)
+ break;
+
+ errsv = errno;
+ if (errsv == EADDRINUSE) {
+ NL_DBG(4, "nl_connect(%p): local port %u already in use. Retry.\n", sk, (unsigned) port);
+ _nl_socket_used_ports_set(used_ports, port);
+ } else {
+ NL_DBG(4, "nl_connect(%p): bind() for port %u failed with %d\n", sk, (unsigned) port, errsv);
+ _nl_socket_used_ports_release_all(used_ports);
+ err = -nl_syserr2nlerr(errsv);
+ goto errout;
+ }
+ }
+ _nl_socket_used_ports_release_all(used_ports);
+ } else {
+ err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
+ sizeof(sk->s_local));
+ if (err != 0) {
+ errsv = errno;
+ NL_DBG(4, "nl_connect(%p): bind() failed with %d\n", sk, errsv);
+ err = -nl_syserr2nlerr(errsv);
+ goto errout;
+ }
}
addrlen = sizeof(sk->s_local);
@@ -149,15 +183,24 @@ int nl_connect(struct nl_sock *sk, int protocol)
return 0;
errout:
- close(sk->s_fd);
- sk->s_fd = -1;
+ if (sk->s_fd != -1) {
+ close(sk->s_fd);
+ sk->s_fd = -1;
+ }
return err;
}
/**
- * Close/Disconnect netlink socket.
- * @arg sk Netlink socket.
+ * Close Netlink socket
+ * @arg sk Netlink socket (required)
+ *
+ * Closes the Netlink socket using `close()`.
+ *
+ * @note The socket is closed automatically if a `struct nl_sock` object is
+ * freed using `nl_socket_free()`.
+ *
+ * @see nl_connect()
*/
void nl_close(struct nl_sock *sk)
{
@@ -177,16 +220,38 @@ void nl_close(struct nl_sock *sk)
*/
/**
- * Send raw data over netlink socket.
- * @arg sk Netlink socket.
- * @arg buf Data buffer.
- * @arg size Size of data buffer.
- * @return Number of characters written on success or a negative error code.
+ * Transmit raw data over Netlink socket.
+ * @arg sk Netlink socket (required)
+ * @arg buf Buffer carrying data to send (required)
+ * @arg size Size of buffer (required)
+ *
+ * Transmits "raw" data over the specified Netlink socket. Unlike the other
+ * transmit functions it does not modify the data in any way. It directly
+ * passes the buffer \c buf of \c size to sendto().
+ *
+ * The message is addressed to the peer as specified in the socket by either
+ * the nl_socket_set_peer_port() or nl_socket_set_peer_groups() function.
+ *
+ * @note Because there is no indication on the message boundaries of the data
+ * being sent, the \c NL_CB_MSG_OUT callback handler will not be invoked
+ * for data that is being sent using this function.
+ *
+ * @see nl_socket_set_peer_port()
+ * @see nl_socket_set_peer_groups()
+ * @see nl_sendmsg()
+ *
+ * @return Number of bytes sent or a negative error code.
*/
int nl_sendto(struct nl_sock *sk, void *buf, size_t size)
{
int ret;
+ if (!buf)
+ return -NLE_INVAL;
+
+ if (sk->s_fd < 0)
+ return -NLE_BAD_SOCK;
+
ret = sendto(sk->s_fd, buf, size, 0, (struct sockaddr *)
&sk->s_peer, sizeof(sk->s_peer));
if (ret < 0)
@@ -196,23 +261,55 @@ int nl_sendto(struct nl_sock *sk, void *buf, size_t size)
}
/**
- * Send netlink message with control over sendmsg() message header.
- * @arg sk Netlink socket.
- * @arg msg Netlink message to be sent.
- * @arg hdr Sendmsg() message header.
- * @return Number of characters sent on sucess or a negative error code.
+ * Transmit Netlink message using sendmsg()
+ * @arg sk Netlink socket (required)
+ * @arg msg Netlink message to be sent (required)
+ * @arg hdr sendmsg() message header (required)
+ *
+ * Transmits the message specified in \c hdr over the Netlink socket using the
+ * sendmsg() system call.
+ *
+ * @attention
+ * The `msg` argument will *not* be used to derive the message payload that
+ * is being sent out. The `msg` argument is *only* passed on to the
+ * `NL_CB_MSG_OUT` callback. The caller is responsible to initialize the
+ * `hdr` struct properly and have it point to the message payload and
+ * socket address.
+ *
+ * @note
+ * This function uses `nlmsg_set_src()` to modify the `msg` argument prior to
+ * invoking the `NL_CB_MSG_OUT` callback to provide the local port number.
+ *
+ * @callback This function triggers the `NL_CB_MSG_OUT` callback.
+ *
+ * @attention
+ * Think twice before using this function. It provides a low level access to
+ * the Netlink socket. Among other limitations, it does not add credentials
+ * even if enabled or respect the destination address specified in the `msg`
+ * object.
+ *
+ * @see nl_socket_set_local_port()
+ * @see nl_send_auto()
+ * @see nl_send_iovec()
+ *
+ * @return Number of bytes sent on success or a negative error code.
+ *
+ * @lowlevel
*/
int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr)
{
struct nl_cb *cb;
int ret;
+ if (sk->s_fd < 0)
+ return -NLE_BAD_SOCK;
+
nlmsg_set_src(msg, &sk->s_local);
cb = sk->s_cb;
if (cb->cb_set[NL_CB_MSG_OUT])
- if (nl_cb_call(cb, NL_CB_MSG_OUT, msg) != NL_OK)
- return 0;
+ if ((ret = nl_cb_call(cb, NL_CB_MSG_OUT, msg)) != NL_OK)
+ return ret;
ret = sendmsg(sk->s_fd, hdr, 0);
if (ret < 0)
@@ -224,13 +321,23 @@ int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr)
/**
- * Send netlink message.
- * @arg sk Netlink socket.
- * @arg msg Netlink message to be sent.
- * @arg iov iovec to be sent.
- * @arg iovlen number of struct iovec to be sent.
- * @see nl_sendmsg()
- * @return Number of characters sent on success or a negative error code.
+ * Transmit Netlink message (taking IO vector)
+ * @arg sk Netlink socket (required)
+ * @arg msg Netlink message to be sent (required)
+ * @arg iov IO vector to be sent (required)
+ * @arg iovlen Number of struct iovec to be sent (required)
+ *
+ * This function is identical to nl_send() except that instead of taking a
+ * `struct nl_msg` object it takes an IO vector. Please see the description
+ * of `nl_send()`.
+ *
+ * @callback This function triggers the `NL_CB_MSG_OUT` callback.
+ *
+ * @see nl_send()
+ *
+ * @return Number of bytes sent on success or a negative error code.
+ *
+ * @lowlevel
*/
int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen)
{
@@ -269,34 +376,86 @@ int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, uns
return nl_sendmsg(sk, msg, &hdr);
}
-
-
/**
-* Send netlink message.
-* @arg sk Netlink socket.
-* @arg msg Netlink message to be sent.
-* @see nl_sendmsg()
-* @return Number of characters sent on success or a negative error code.
+ * Transmit Netlink message
+ * @arg sk Netlink socket (required)
+ * @arg msg Netlink message (required)
+ *
+ * Transmits the Netlink message `msg` over the Netlink socket using the
+ * `sendmsg()` system call. This function is based on `nl_send_iovec()` but
+ * takes care of initializing a `struct iovec` based on the `msg` object.
+ *
+ * The message is addressed to the peer as specified in the socket by either
+ * the nl_socket_set_peer_port() or nl_socket_set_peer_groups() function.
+ * The peer address can be overwritten by specifying an address in the `msg`
+ * object using nlmsg_set_dst().
+ *
+ * If present in the `msg`, credentials set by the nlmsg_set_creds() function
+ * are added to the control buffer of the message.
+ *
+ * @par Overwriting Capability:
+ * Calls to this function can be overwritten by providing an alternative using
+ * the nl_cb_overwrite_send() function.
+ *
+ * @callback This function triggers the `NL_CB_MSG_OUT` callback.
+ *
+ * @attention
+ * Unlike `nl_send_auto()`, this function does *not* finalize the message in
+ * terms of automatically adding needed flags or filling out port numbers.
+ *
+ * @see nl_send_auto()
+ * @see nl_send_iovec()
+ * @see nl_socket_set_peer_port()
+ * @see nl_socket_set_peer_groups()
+ * @see nlmsg_set_dst()
+ * @see nlmsg_set_creds()
+ * @see nl_cb_overwrite_send()
+ *
+ * @return Number of bytes sent on success or a negative error code.
*/
int nl_send(struct nl_sock *sk, struct nl_msg *msg)
{
- struct iovec iov = {
- .iov_base = (void *) nlmsg_hdr(msg),
- .iov_len = nlmsg_hdr(msg)->nlmsg_len,
- };
+ struct nl_cb *cb = sk->s_cb;
- return nl_send_iovec(sk, msg, &iov, 1);
+ if (cb->cb_send_ow)
+ return cb->cb_send_ow(sk, msg);
+ else {
+ struct iovec iov = {
+ .iov_base = (void *) nlmsg_hdr(msg),
+ .iov_len = nlmsg_hdr(msg)->nlmsg_len,
+ };
+
+ return nl_send_iovec(sk, msg, &iov, 1);
+ }
}
-void nl_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
+/**
+ * Finalize Netlink message
+ * @arg sk Netlink socket (required)
+ * @arg msg Netlink message (required)
+ *
+ * This function finalizes a Netlink message by completing the message with
+ * desirable flags and values depending on the socket configuration.
+ *
+ * - If not yet filled out, the source address of the message (`nlmsg_pid`)
+ * will be set to the local port number of the socket.
+ * - If not yet specified, the next available sequence number is assigned
+ * to the message (`nlmsg_seq`).
+ * - If not yet specified, the protocol field of the message will be set to
+ * the protocol field of the socket.
+ * - The `NLM_F_REQUEST` Netlink message flag will be set.
+ * - The `NLM_F_ACK` flag will be set if Auto-ACK mode is enabled on the
+ * socket.
+ */
+void nl_complete_msg(struct nl_sock *sk, struct nl_msg *msg)
{
struct nlmsghdr *nlh;
nlh = nlmsg_hdr(msg);
- if (nlh->nlmsg_pid == 0)
- nlh->nlmsg_pid = sk->s_local.nl_pid;
+ if (nlh->nlmsg_pid == NL_AUTO_PORT)
+ nlh->nlmsg_pid = nl_socket_get_local_port(sk);
- if (nlh->nlmsg_seq == 0)
+ if (nlh->nlmsg_seq == NL_AUTO_SEQ)
nlh->nlmsg_seq = sk->s_seq_next++;
if (msg->nm_protocol == -1)
@@ -309,42 +468,83 @@ void nl_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
}
/**
- * Send netlink message and check & extend header values as needed.
- * @arg sk Netlink socket.
- * @arg msg Netlink message to be sent.
+ * Finalize and transmit Netlink message
+ * @arg sk Netlink socket (required)
+ * @arg msg Netlink message (required)
*
- * 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.
+ * Finalizes the message by passing it to `nl_complete_msg()` and transmits it
+ * by passing it to `nl_send()`.
*
+ * @callback This function triggers the `NL_CB_MSG_OUT` callback.
+ *
+ * @see nl_complete_msg()
* @see nl_send()
- * @return Number of characters sent or a negative error code.
+ *
+ * @return Number of bytes sent or a negative error code.
*/
-int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
+int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg)
{
- struct nl_cb *cb = sk->s_cb;
+ nl_complete_msg(sk, msg);
- nl_auto_complete(sk, msg);
+ return nl_send(sk, msg);
+}
- if (cb->cb_send_ow)
- return cb->cb_send_ow(sk, msg);
- else
- return nl_send(sk, msg);
+/**
+ * Finalize and transmit Netlink message and wait for ACK or error message
+ * @arg sk Netlink socket (required)
+ * @arg msg Netlink message (required)
+ *
+ * Passes the `msg` to `nl_send_auto()` to finalize and transmit it. Frees the
+ * message and waits (sleeps) for the ACK or error message to be received.
+ *
+ * @attention
+ * Disabling Auto-ACK (nl_socket_disable_auto_ack()) will cause this function
+ * to return immediately after transmitting the message. However, the peer may
+ * still be returning an error message in response to the request. It is the
+ * responsibility of the caller to handle such messages.
+ *
+ * @callback This function triggers the `NL_CB_MSG_OUT` callback.
+ *
+ * @attention
+ * This function frees the `msg` object after transmitting it by calling
+ * `nlmsg_free()`.
+ *
+ * @see nl_send_auto().
+ * @see nl_wait_for_ack()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nl_send_sync(struct nl_sock *sk, struct nl_msg *msg)
+{
+ int err;
+
+ err = nl_send_auto(sk, msg);
+ nlmsg_free(msg);
+ if (err < 0)
+ return err;
+
+ return wait_for_ack(sk);
}
/**
- * Send simple netlink message using nl_send_auto_complete()
- * @arg sk Netlink socket.
- * @arg type Netlink message type.
- * @arg flags Netlink message flags.
- * @arg buf Data buffer.
- * @arg size Size of data buffer.
+ * Construct and transmit a Netlink message
+ * @arg sk Netlink socket (required)
+ * @arg type Netlink message type (required)
+ * @arg flags Netlink message flags (optional)
+ * @arg buf Data buffer (optional)
+ * @arg size Size of data buffer (optional)
+ *
+ * Allocates a new Netlink message based on `type` and `flags`. If `buf`
+ * points to payload of length `size` that payload will be appended to the
+ * message.
*
- * Builds a netlink message with the specified type and flags and
- * appends the specified data as payload to the message.
+ * Sends out the message using `nl_send_auto()` and frees the message
+ * afterwards.
+ *
+ * @see nl_send_auto()
*
- * @see nl_send_auto_complete()
* @return Number of characters sent on success or a negative error code.
+ * @retval -NLE_NOMEM Unable to allocate Netlink message
*/
int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf,
size_t size)
@@ -361,9 +561,8 @@ int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf,
if (err < 0)
goto errout;
}
-
- err = nl_send_auto_complete(sk, msg);
+ err = nl_send_auto(sk, msg);
errout:
nlmsg_free(msg);
@@ -379,27 +578,54 @@ errout:
/**
* Receive data from netlink socket
- * @arg sk Netlink socket.
- * @arg nla Destination pointer for peer's netlink address.
- * @arg buf Destination pointer for message content.
- * @arg creds Destination pointer for credentials.
+ * @arg sk Netlink socket (required)
+ * @arg nla Netlink socket structure to hold address of peer (required)
+ * @arg buf Destination pointer for message content (required)
+ * @arg creds Destination pointer for credentials (optional)
+ *
+ * Receives data from a connected netlink socket using recvmsg() and returns
+ * the number of bytes read. The read data is stored in a newly allocated
+ * buffer that is assigned to \c *buf. The peer's netlink address will be
+ * stored in \c *nla.
+ *
+ * This function blocks until data is available to be read unless the socket
+ * has been put into non-blocking mode using nl_socket_set_nonblocking() in
+ * which case this function will return immediately with a return value of 0.
+ *
+ * The buffer size used when reading from the netlink socket and thus limiting
+ * the maximum size of a netlink message that can be read defaults to the size
+ * of a memory page (getpagesize()). The buffer size can be modified on a per
+ * socket level using the function nl_socket_set_msg_buf_size().
*
- * 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.
+ * If message peeking is enabled using nl_socket_enable_msg_peek() the size of
+ * the message to be read will be determined using the MSG_PEEK flag prior to
+ * performing the actual read. This leads to an additional recvmsg() call for
+ * every read operation which has performance implications and is not
+ * recommended for high throughput protocols.
*
- * A non-blocking sockets causes the function to return immediately with
- * a return value of 0 if no data is available.
+ * An eventual interruption of the recvmsg() system call is automatically
+ * handled by retrying the operation.
*
- * @return Number of octets read, 0 on EOF or a negative error code.
+ * If receiving of credentials has been enabled using the function
+ * nl_socket_set_passcred(), this function will allocate a new struct ucred
+ * filled with the received credentials and assign it to \c *creds. The caller
+ * is responsible for freeing the buffer.
+ *
+ * @note The caller is responsible to free the returned data buffer and if
+ * enabled, the credentials buffer.
+ *
+ * @see nl_socket_set_nonblocking()
+ * @see nl_socket_set_msg_buf_size()
+ * @see nl_socket_enable_msg_peek()
+ * @see nl_socket_set_passcred()
+ *
+ * @return Number of bytes read, 0 on EOF, 0 on no data event (non-blocking
+ * mode), or a negative error code.
*/
int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla,
unsigned char **buf, struct ucred **creds)
{
- int n;
+ ssize_t n;
int flags = 0;
static int page_size = 0;
struct iovec iov;
@@ -408,85 +634,127 @@ int nl_recv(struct nl_sock *sk, struct sockaddr_nl *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;
+ struct ucred* tmpcreds = NULL;
+ int retval = 0;
+
+ if (!buf || !nla)
+ return -NLE_INVAL;
if (sk->s_flags & NL_MSG_PEEK)
- flags |= MSG_PEEK;
+ flags |= MSG_PEEK | MSG_TRUNC;
if (page_size == 0)
- page_size = getpagesize();
+ page_size = getpagesize() * 4;
- iov.iov_len = page_size;
- iov.iov_base = *buf = malloc(iov.iov_len);
+ iov.iov_len = sk->s_bufsize ? : page_size;
+ iov.iov_base = malloc(iov.iov_len);
+
+ if (!iov.iov_base) {
+ retval = -NLE_NOMEM;
+ goto abort;
+ }
- if (sk->s_flags & NL_SOCK_PASSCRED) {
+ if (creds && (sk->s_flags & NL_SOCK_PASSCRED)) {
msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred));
- msg.msg_control = calloc(1, msg.msg_controllen);
+ msg.msg_control = malloc(msg.msg_controllen);
+ if (!msg.msg_control) {
+ retval = -NLE_NOMEM;
+ goto abort;
+ }
}
retry:
n = recvmsg(sk->s_fd, &msg, flags);
- if (!n)
+ if (!n) {
+ retval = 0;
goto abort;
- else if (n < 0) {
+ }
+ 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");
+ }
+ retval = -nl_syserr2nlerr(errno);
+ goto abort;
+ }
+
+ if (msg.msg_flags & MSG_CTRUNC) {
+ void *tmp;
+ msg.msg_controllen *= 2;
+ tmp = realloc(msg.msg_control, msg.msg_controllen);
+ if (!tmp) {
+ retval = -NLE_NOMEM;
goto abort;
- } else {
- free(msg.msg_control);
- free(*buf);
- return -nl_syserr2nlerr(errno);
}
+ msg.msg_control = tmp;
+ goto retry;
}
- if (iov.iov_len < n ||
- msg.msg_flags & MSG_TRUNC) {
+ if (iov.iov_len < n || (msg.msg_flags & MSG_TRUNC)) {
+ void *tmp;
/* Provided buffer is not long enough, enlarge it
+ * to size of n (which should be total length of the message)
* 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);
+ iov.iov_len = n;
+ tmp = realloc(iov.iov_base, iov.iov_len);
+ if (!tmp) {
+ retval = -NLE_NOMEM;
+ goto abort;
+ }
+ iov.iov_base = tmp;
+ flags = 0;
goto retry;
- } else if (flags != 0) {
+ }
+
+ 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 -NLE_NOADDR;
+ retval = -NLE_NOADDR;
+ goto abort;
}
- 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));
+ if (creds && (sk->s_flags & NL_SOCK_PASSCRED)) {
+ struct cmsghdr *cmsg;
+
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if (cmsg->cmsg_level != SOL_SOCKET)
+ continue;
+ if (cmsg->cmsg_type != SCM_CREDENTIALS)
+ continue;
+ tmpcreds = malloc(sizeof(*tmpcreds));
+ if (!tmpcreds) {
+ retval = -NLE_NOMEM;
+ goto abort;
+ }
+ memcpy(tmpcreds, CMSG_DATA(cmsg), sizeof(*tmpcreds));
break;
}
}
- free(msg.msg_control);
- return n;
-
+ retval = n;
abort:
free(msg.msg_control);
- free(*buf);
- return 0;
+
+ if (retval <= 0) {
+ free(iov.iov_base);
+ iov.iov_base = NULL;
+ free(tmpcreds);
+ tmpcreds = NULL;
+ } else
+ *buf = iov.iov_base;
+
+ if (creds)
+ *creds = tmpcreds;
+
+ return retval;
}
+/** @cond SKIP */
#define NL_CB_CALL(cb, type, msg) \
do { \
err = nl_cb_call(cb, type, msg); \
@@ -502,12 +770,19 @@ do { \
goto out; \
} \
} while (0)
+/** @endcond */
static int recvmsgs(struct nl_sock *sk, struct nl_cb *cb)
{
- int n, err = 0, multipart = 0;
+ int n, err = 0, multipart = 0, interrupted = 0, nrecv = 0;
unsigned char *buf = NULL;
struct nlmsghdr *hdr;
+
+ /*
+ nla is passed on to not only to nl_recv() but may also be passed
+ to a function pointer provided by the caller which may or may not
+ initialize the variable. Thomas Graf.
+ */
struct sockaddr_nl nla = {0};
struct nl_msg *msg = NULL;
struct ucred *creds = NULL;
@@ -526,7 +801,7 @@ continue_reading:
hdr = (struct nlmsghdr *) buf;
while (nlmsg_ok(hdr, n)) {
- NL_DBG(3, "recgmsgs(%p): Processing valid message...\n", sk);
+ NL_DBG(3, "recvmsgs(%p): Processing valid message...\n", sk);
nlmsg_free(msg);
msg = nlmsg_convert(hdr);
@@ -540,6 +815,8 @@ continue_reading:
if (creds)
nlmsg_set_creds(msg, creds);
+ nrecv++;
+
/* 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])
@@ -548,14 +825,18 @@ continue_reading:
/* 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])
+ if (cb->cb_set[NL_CB_SEQ_CHECK]) {
NL_CB_CALL(cb, NL_CB_SEQ_CHECK, msg);
- else if (hdr->nlmsg_seq != sk->s_seq_expect) {
- if (cb->cb_set[NL_CB_INVALID])
- NL_CB_CALL(cb, NL_CB_INVALID, msg);
- else {
- err = -NLE_SEQ_MISMATCH;
- goto out;
+
+ /* Only do sequence checking if auto-ack mode is enabled */
+ } else if (!(sk->s_flags & NL_NO_AUTO_ACK)) {
+ if (hdr->nlmsg_seq != sk->s_seq_expect) {
+ if (cb->cb_set[NL_CB_INVALID])
+ NL_CB_CALL(cb, NL_CB_INVALID, msg);
+ else {
+ err = -NLE_SEQ_MISMATCH;
+ goto out;
+ }
}
}
@@ -573,6 +854,19 @@ continue_reading:
if (hdr->nlmsg_flags & NLM_F_MULTI)
multipart = 1;
+
+ if (hdr->nlmsg_flags & NLM_F_DUMP_INTR) {
+ if (cb->cb_set[NL_CB_DUMP_INTR])
+ NL_CB_CALL(cb, NL_CB_DUMP_INTR, msg);
+ else {
+ /*
+ * We have to continue reading to clear
+ * all messages until a NLMSG_DONE is
+ * received and report the inconsistency.
+ */
+ interrupted = 1;
+ }
+ }
/* Other side wishes to see an ack for this message */
if (hdr->nlmsg_flags & NLM_F_ACK) {
@@ -583,7 +877,7 @@ continue_reading:
}
}
- /* messages terminates a multpart message, this is
+ /* messages terminates a multipart 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. */
@@ -620,7 +914,7 @@ continue_reading:
else if (hdr->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *e = nlmsg_data(hdr);
- if (hdr->nlmsg_len < nlmsg_msg_size(sizeof(*e))) {
+ if (hdr->nlmsg_len < nlmsg_size(sizeof(*e))) {
/* Truncated error message, the default action
* is to stop parsing. The user may overrule
* this action by returning NL_SKIP or
@@ -680,10 +974,36 @@ out:
free(buf);
free(creds);
+ if (interrupted)
+ err = -NLE_DUMP_INTR;
+
+ if (!err)
+ err = nrecv;
+
return err;
}
/**
+ * Receive a set of messages from a netlink socket and report parsed messages
+ * @arg sk Netlink socket.
+ * @arg cb set of callbacks to control behaviour.
+ *
+ * This function is identical to nl_recvmsgs() to the point that it will
+ * return the number of parsed messages instead of 0 on success.
+ *
+ * @see nl_recvmsgs()
+ *
+ * @return Number of received messages or a negative error code from nl_recv().
+ */
+int nl_recvmsgs_report(struct nl_sock *sk, struct nl_cb *cb)
+{
+ if (cb->cb_recvmsgs_ow)
+ return cb->cb_recvmsgs_ow(sk, cb);
+ else
+ return recvmsgs(sk, cb);
+}
+
+/**
* Receive a set of messages from a netlink socket.
* @arg sk Netlink socket.
* @arg cb set of callbacks to control behaviour.
@@ -696,14 +1016,18 @@ out:
* A non-blocking sockets causes the function to return immediately if
* no data is available.
*
+ * @see nl_recvmsgs_report()
+ *
* @return 0 on success or a negative error code from nl_recv().
*/
int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb)
{
- if (cb->cb_recvmsgs_ow)
- return cb->cb_recvmsgs_ow(sk, cb);
- else
- return recvmsgs(sk, cb);
+ int err;
+
+ if ((err = nl_recvmsgs_report(sk, cb)) > 0)
+ err = 0;
+
+ return err;
}
/**
@@ -747,6 +1071,102 @@ int nl_wait_for_ack(struct nl_sock *sk)
return err;
}
+/** @cond SKIP */
+struct pickup_param
+{
+ int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *,
+ struct nlmsghdr *, struct nl_parser_param *);
+ struct nl_object *result;
+};
+
+static int __store_answer(struct nl_object *obj, struct nl_parser_param *p)
+{
+ struct pickup_param *pp = p->pp_arg;
+ /*
+ * the parser will put() the object at the end, expecting the cache
+ * to take the reference.
+ */
+ nl_object_get(obj);
+ pp->result = obj;
+
+ return 0;
+}
+
+static int __pickup_answer(struct nl_msg *msg, void *arg)
+{
+ struct pickup_param *pp = arg;
+ struct nl_parser_param parse_arg = {
+ .pp_cb = __store_answer,
+ .pp_arg = pp,
+ };
+
+ return pp->parser(NULL, &msg->nm_src, msg->nm_nlh, &parse_arg);
+}
+
+/** @endcond */
+
+/**
+ * Pickup netlink answer, parse is and return object
+ * @arg sk Netlink socket
+ * @arg parser Parser function to parse answer
+ * @arg result Result pointer to return parsed object
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nl_pickup(struct nl_sock *sk,
+ int (*parser)(struct nl_cache_ops *, struct sockaddr_nl *,
+ struct nlmsghdr *, struct nl_parser_param *),
+ struct nl_object **result)
+{
+ struct nl_cb *cb;
+ int err;
+ struct pickup_param pp = {
+ .parser = parser,
+ };
+
+ cb = nl_cb_clone(sk->s_cb);
+ if (cb == NULL)
+ return -NLE_NOMEM;
+
+ nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, __pickup_answer, &pp);
+
+ err = nl_recvmsgs(sk, cb);
+ if (err < 0)
+ goto errout;
+
+ *result = pp.result;
+errout:
+ nl_cb_put(cb);
+
+ return err;
+}
+
+/** @} */
+
+/**
+ * @name Deprecated
+ * @{
+ */
+
+/**
+ * @deprecated Please use nl_complete_msg()
+ */
+void nl_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
+{
+ nl_complete_msg(sk, msg);
+}
+
+/**
+ * @deprecated Please use nl_send_auto()
+ */
+int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
+{
+ return nl_send_auto(sk, msg);
+}
+
+
+/** @} */
+
/** @} */
/** @} */
diff --git a/lib/object.c b/lib/object.c
index d881ac99..52bc8736 100644
--- a/lib/object.c
+++ b/lib/object.c
@@ -6,16 +6,28 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup cache
- * @defgroup object Object
+ * @ingroup core_types
+ * @defgroup object Object (Cacheable)
+ *
+ * Generic object data type, for inheritance purposes to implement cacheable
+ * data types.
+ *
+ * Related sections in the development guide:
+ *
* @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/object.h>
+ * ~~~~
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/object.h>
@@ -63,19 +75,23 @@ struct nl_object *nl_object_alloc(struct nl_object_ops *ops)
}
/**
- * Allocate a new object of kind specified by the name
+ * Allocate new object of kind specified by the name
* @arg kind name of object type
- * @return The new object or nULL
+ * @arg result Result pointer
+ *
+ * @return 0 on success or a negative error code.
*/
int nl_object_alloc_name(const char *kind, struct nl_object **result)
{
struct nl_cache_ops *ops;
- ops = nl_cache_ops_lookup(kind);
+ ops = nl_cache_ops_lookup_safe(kind);
if (!ops)
return -NLE_OPNOTSUPP;
- if (!(*result = nl_object_alloc(ops->co_obj_ops)))
+ *result = nl_object_alloc(ops->co_obj_ops);
+ nl_cache_ops_put(ops);
+ if (!*result)
return -NLE_NOMEM;
return 0;
@@ -94,10 +110,14 @@ struct nl_derived_object {
struct nl_object *nl_object_clone(struct nl_object *obj)
{
struct nl_object *new;
- struct nl_object_ops *ops = obj_ops(obj);
+ struct nl_object_ops *ops;
int doff = offsetof(struct nl_derived_object, data);
int size;
+ if (!obj)
+ return NULL;
+
+ ops = obj_ops(obj);
new = nl_object_alloc(ops);
if (!new)
return NULL;
@@ -125,6 +145,23 @@ struct nl_object *nl_object_clone(struct nl_object *obj)
}
/**
+ * Merge a cacheable object
+ * @arg dst object to be merged into
+ * @arg src new object to be merged into dst
+ *
+ * @return 0 or a negative error code.
+ */
+int nl_object_update(struct nl_object *dst, struct nl_object *src)
+{
+ struct nl_object_ops *ops = obj_ops(dst);
+
+ if (ops->oo_update)
+ return ops->oo_update(dst, src);
+
+ return -NLE_OPNOTSUPP;
+}
+
+/**
* Free a cacheable object
* @arg obj object to free
*
@@ -132,7 +169,12 @@ struct nl_object *nl_object_clone(struct nl_object *obj)
*/
void nl_object_free(struct nl_object *obj)
{
- struct nl_object_ops *ops = obj_ops(obj);
+ struct nl_object_ops *ops;
+
+ if (!obj)
+ return;
+
+ ops = obj_ops(obj);
if (obj->ce_refcnt > 0)
NL_DBG(1, "Warning: Freeing object in use...\n");
@@ -143,9 +185,9 @@ void nl_object_free(struct nl_object *obj)
if (ops->oo_free_data)
ops->oo_free_data(obj);
- free(obj);
-
NL_DBG(4, "Freed object %p\n", obj);
+
+ free(obj);
}
/** @} */
@@ -245,9 +287,22 @@ int nl_object_is_marked(struct nl_object *obj)
*/
void nl_object_dump(struct nl_object *obj, struct nl_dump_params *params)
{
+ if (params->dp_buf)
+ memset(params->dp_buf, 0, params->dp_buflen);
+
dump_from_ops(obj, params);
}
+void nl_object_dump_buf(struct nl_object *obj, char *buf, size_t len)
+{
+ struct nl_dump_params dp = {
+ .dp_buf = buf,
+ .dp_buflen = len,
+ };
+
+ return nl_object_dump(obj, &dp);
+}
+
/**
* Check if the identifiers of two objects are identical
* @arg a an object
@@ -258,14 +313,24 @@ void nl_object_dump(struct nl_object *obj, struct nl_dump_params *params)
int nl_object_identical(struct nl_object *a, struct nl_object *b)
{
struct nl_object_ops *ops = obj_ops(a);
- int req_attrs;
+ uint32_t req_attrs;
/* Both objects must be of same type */
if (ops != obj_ops(b))
return 0;
- req_attrs = ops->oo_id_attrs;
- if (req_attrs == ~0)
+ if (ops->oo_id_attrs_get) {
+ int req_attrs_a = ops->oo_id_attrs_get(a);
+ int req_attrs_b = ops->oo_id_attrs_get(b);
+ if (req_attrs_a != req_attrs_b)
+ return 0;
+ req_attrs = req_attrs_a;
+ } else if (ops->oo_id_attrs) {
+ req_attrs = ops->oo_id_attrs;
+ } else {
+ req_attrs = 0xFFFFFFFF;
+ }
+ if (req_attrs == 0xFFFFFFFF)
req_attrs = a->ce_mask & b->ce_mask;
/* Both objects must provide all required attributes to uniquely
@@ -361,6 +426,27 @@ char *nl_object_attr_list(struct nl_object *obj, char *buf, size_t len)
return nl_object_attrs2str(obj, obj->ce_mask, buf, len);
}
+/**
+ * Generate object hash key
+ * @arg obj the object
+ * @arg hashkey destination buffer to be used for key stream
+ * @arg hashtbl_sz hash table size
+ *
+ * @return hash key in destination buffer
+ */
+void nl_object_keygen(struct nl_object *obj, uint32_t *hashkey,
+ uint32_t hashtbl_sz)
+{
+ struct nl_object_ops *ops = obj_ops(obj);
+
+ if (ops->oo_keygen)
+ ops->oo_keygen(obj, hashkey, hashtbl_sz);
+ else
+ *hashkey = 0;
+
+ return;
+}
+
/** @} */
/**
@@ -368,16 +454,91 @@ char *nl_object_attr_list(struct nl_object *obj, char *buf, size_t len)
* @{
*/
+/**
+ * Return number of references held
+ * @arg obj object
+ *
+ * @return The number of references held to this object
+ */
int nl_object_get_refcnt(struct nl_object *obj)
{
return obj->ce_refcnt;
}
+/**
+ * Return cache the object is associated with
+ * @arg obj object
+ *
+ * @note The returned pointer is not protected with a reference counter,
+ * it is your responsibility.
+ *
+ * @return Pointer to cache or NULL if not associated with a cache.
+ */
struct nl_cache *nl_object_get_cache(struct nl_object *obj)
{
return obj->ce_cache;
}
+/**
+ * Return the object's type
+ * @arg obj object
+ *
+ * FIXME: link to list of object types
+ *
+ * @return Name of the object type
+ */
+const char *nl_object_get_type(const struct nl_object *obj)
+{
+ if (!obj->ce_ops)
+ BUG();
+
+ return obj->ce_ops->oo_name;
+}
+
+/**
+ * Return the netlink message type the object was derived from
+ * @arg obj object
+ *
+ * @return Netlink message type or 0.
+ */
+int nl_object_get_msgtype(const struct nl_object *obj)
+{
+ return obj->ce_msgtype;
+}
+
+/**
+ * Return object operations structure
+ * @arg obj object
+ *
+ * @return Pointer to the object operations structure
+ */
+struct nl_object_ops *nl_object_get_ops(const struct nl_object *obj)
+{
+ return obj->ce_ops;
+}
+
+/**
+ * Return object id attribute mask
+ * @arg obj object
+ *
+ * @return object id attribute mask
+ */
+uint32_t nl_object_get_id_attrs(struct nl_object *obj)
+{
+ struct nl_object_ops *ops = obj_ops(obj);
+ uint32_t id_attrs;
+
+ if (!ops)
+ return 0;
+
+ if (ops->oo_id_attrs_get)
+ id_attrs = ops->oo_id_attrs_get(obj);
+ else
+ id_attrs = ops->oo_id_attrs;
+
+ return id_attrs;
+}
+
/** @} */
/** @} */
diff --git a/lib/route/act.c b/lib/route/act.c
new file mode 100644
index 00000000..85ce445c
--- /dev/null
+++ b/lib/route/act.c
@@ -0,0 +1,580 @@
+/*
+ * lib/route/act.c Action
+ *
+ * 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) 2013 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+/**
+ * @ingroup tc
+ * @defgroup act Action
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/link.h>
+
+
+static struct nl_object_ops act_obj_ops;
+static struct nl_cache_ops rtnl_act_ops;
+
+int rtnl_act_append(struct rtnl_act **head, struct rtnl_act *new)
+{
+ struct rtnl_act *p_act;
+ int count = 1;
+
+ if (*head == NULL) {
+ *head = new;
+ return 0;
+ }
+
+ p_act = *head;
+ while (p_act->a_next) {
+ ++count;
+ p_act = p_act->a_next;
+ }
+
+ if (count > TCA_ACT_MAX_PRIO)
+ return -NLE_RANGE;
+
+ p_act->a_next = new;
+ return 0;
+}
+
+int rtnl_act_remove(struct rtnl_act **head, struct rtnl_act *act)
+{
+ struct rtnl_act *a, **ap;
+
+ for (ap = head; (a = *ap) != NULL; ap = &a->a_next)
+ if (a == act)
+ break;
+ if (a) {
+ *ap = a->a_next;
+ a->a_next = NULL;
+ return 0;
+ }
+
+ return -NLE_OBJ_NOTFOUND;
+}
+
+static int rtnl_act_fill_one(struct nl_msg *msg, struct rtnl_act *act, int order)
+{
+ struct rtnl_tc *tc = TC_CAST(act);
+ struct rtnl_tc_ops *ops;
+ struct nlattr *nest;
+ int err = -NLE_NOMEM;
+
+ nest = nla_nest_start(msg, order);
+ if (!nest)
+ goto nla_put_failure;
+
+ if (tc->ce_mask & TCA_ATTR_KIND)
+ NLA_PUT_STRING(msg, TCA_ACT_KIND, tc->tc_kind);
+
+ ops = rtnl_tc_get_ops(tc);
+ if (ops && (ops->to_msg_fill || ops->to_msg_fill_raw)) {
+ struct nlattr *opts;
+ void *data = rtnl_tc_data(tc);
+
+ if (ops->to_msg_fill) {
+ if (!(opts = nla_nest_start(msg, TCA_ACT_OPTIONS)))
+ goto nla_put_failure;
+
+ if ((err = ops->to_msg_fill(tc, data, msg)) < 0)
+ goto nla_put_failure;
+
+ nla_nest_end(msg, opts);
+ } else if ((err = ops->to_msg_fill_raw(tc, data, msg)) < 0)
+ goto nla_put_failure;
+ }
+ nla_nest_end(msg, nest);
+ return 0;
+
+nla_put_failure:
+ return err;
+}
+
+int rtnl_act_fill(struct nl_msg *msg, int attrtype, struct rtnl_act *act)
+{
+ struct rtnl_act *p_act = act;
+ struct nlattr *nest;
+ int err, order = 0;
+
+ nest = nla_nest_start(msg, attrtype);
+ if (!nest)
+ return -NLE_MSGSIZE;
+
+ while (p_act) {
+ err = rtnl_act_fill_one(msg, p_act, ++order);
+ if (err)
+ return err;
+ p_act = p_act->a_next;
+ }
+
+ nla_nest_end(msg, nest);
+ return 0;
+}
+
+static int rtnl_act_msg_build(struct rtnl_act *act, int type, int flags,
+ struct nl_msg **result)
+{
+ struct nl_msg *msg;
+ struct tcamsg tcahdr = {
+ .tca_family = AF_UNSPEC,
+ };
+ int err = -NLE_MSGSIZE;
+
+ msg = nlmsg_alloc_simple(type, flags);
+ if (!msg)
+ return -NLE_NOMEM;
+
+ if (nlmsg_append(msg, &tcahdr, sizeof(tcahdr), NLMSG_ALIGNTO) < 0)
+ goto nla_put_failure;
+
+ err = rtnl_act_fill(msg, TCA_ACT_TAB, act);
+ if (err < 0)
+ goto nla_put_failure;
+
+ *result = msg;
+ return 0;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return err;
+}
+
+static int act_build(struct rtnl_act *act, int type, int flags,
+ struct nl_msg **result)
+{
+ int err;
+
+ err = rtnl_act_msg_build(act, type, flags, result);
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct rtnl_act *rtnl_act_alloc(void)
+{
+ struct rtnl_tc *tc;
+
+ tc = TC_CAST(nl_object_alloc(&act_obj_ops));
+ if (tc)
+ tc->tc_type = RTNL_TC_TYPE_ACT;
+
+ return (struct rtnl_act *) tc;
+}
+
+void rtnl_act_get(struct rtnl_act *act)
+{
+ nl_object_get(OBJ_CAST(act));
+}
+
+void rtnl_act_put(struct rtnl_act *act)
+{
+ nl_object_put((struct nl_object *) act);
+}
+
+/** @} */
+
+/**
+ * @name Addition/Modification/Deletion
+ * @{
+ */
+
+/**
+ * Build a netlink message requesting the addition of an action
+ * @arg act Action to add
+ * @arg flags Additional netlink message flags
+ * @arg result Pointer to store resulting netlink message
+ *
+ * The behaviour of this function is identical to rtnl_act_add() with
+ * the exception that it will not send the message but return it int the
+ * provided return pointer instead.
+ *
+ * @see rtnl_act_add()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_act_build_add_request(struct rtnl_act *act, int flags,
+ struct nl_msg **result)
+{
+ return act_build(act, RTM_NEWACTION, flags, result);
+}
+
+/**
+ * Add/Update action
+ * @arg sk Netlink socket
+ * @arg act Action to add/update
+ * @arg flags Additional netlink message flags
+ *
+ * Builds a \c RTM_NEWACTION netlink message requesting the addition
+ * of a new action and sends the message to the kernel. The
+ * configuration of the action is derived from the attributes of
+ * the specified traffic class.
+ *
+ * The following flags may be specified:
+ * - \c NLM_F_CREATE: Create action if it does not exist,
+ * otherwise -NLE_OBJ_NOTFOUND is returned.
+ * - \c NLM_F_EXCL: Return -NLE_EXISTS if an action with
+ * matching handle exists already.
+ *
+ * Existing actions with matching handles will be updated, unless
+ * the flag \c NLM_F_EXCL is specified. If no matching action
+ * exists, it will be created if the flag \c NLM_F_CREATE is set,
+ * otherwise the error -NLE_OBJ_NOTFOUND is returned.
+ *
+ * After sending, the function will wait for the ACK or an eventual
+ * error message to be received and will therefore block until the
+ * operation has been completed.
+ *
+ * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
+ * this function to return immediately after sending. In this case,
+ * it is the responsibility of the caller to handle any error
+ * messages returned.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_act_add(struct nl_sock *sk, struct rtnl_act *act, int flags)
+{
+ struct nl_msg *msg;
+ int err;
+
+ if ((err = rtnl_act_build_add_request(act, flags, &msg)) < 0)
+ return err;
+
+ return nl_send_sync(sk, msg);
+}
+
+/**
+ * Build a netlink message to change action attributes
+ * @arg act Action to change
+ * @arg flags additional netlink message flags
+ * @arg result Pointer to store resulting message.
+ *
+ * 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 0 on success or a negative error code.
+ */
+int rtnl_act_build_change_request(struct rtnl_act *act, int flags,
+ struct nl_msg **result)
+{
+ return act_build(act, RTM_NEWACTION, NLM_F_REPLACE | flags, result);
+}
+
+/**
+ * Change an action
+ * @arg sk Netlink socket.
+ * @arg act action to change
+ * @arg flags additional netlink message flags
+ *
+ * Builds a netlink message by calling rtnl_act_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_act_change(struct nl_sock *sk, struct rtnl_act *act, int flags)
+{
+ struct nl_msg *msg;
+ int err;
+
+ if ((err = rtnl_act_build_change_request(act, flags, &msg)) < 0)
+ return err;
+
+ return nl_send_sync(sk, msg);
+}
+
+/**
+ * Build netlink message requesting the deletion of an action
+ * @arg act Action to delete
+ * @arg flags Additional netlink message flags
+ * @arg result Pointer to store resulting netlink message
+ *
+ * The behaviour of this function is identical to rtnl_act_delete() with
+ * the exception that it will not send the message but return it in the
+ * provided return pointer instead.
+ *
+ * @see rtnl_act_delete()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_act_build_delete_request(struct rtnl_act *act, int flags,
+ struct nl_msg **result)
+{
+ return act_build(act, RTM_DELACTION, flags, result);
+}
+
+/**
+ * Delete action
+ * @arg sk Netlink socket
+ * @arg act Action to delete
+ * @arg flags Additional netlink message flags
+ *
+ * Builds a \c RTM_DELACTION netlink message requesting the deletion
+ * of an action and sends the message to the kernel.
+ *
+ * The message is constructed out of the following attributes:
+ * - \c ifindex (required)
+ * - \c prio (required)
+ * - \c protocol (required)
+ * - \c handle (required)
+ * - \c parent (optional, if not specified parent equals root-qdisc)
+ * - \c kind (optional, must match if provided)
+ *
+ * All other action attributes including all class type specific
+ * attributes are ignored.
+ *
+ * After sending, the function will wait for the ACK or an eventual
+ * error message to be received and will therefore block until the
+ * operation has been completed.
+ *
+ * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
+ * this function to return immediately after sending. In this case,
+ * it is the responsibility of the caller to handle any error
+ * messages returned.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_act_delete(struct nl_sock *sk, struct rtnl_act *act, int flags)
+{
+ struct nl_msg *msg;
+ int err;
+
+ if ((err = rtnl_act_build_delete_request(act, flags, &msg)) < 0)
+ return err;
+
+ return nl_send_sync(sk, msg);
+}
+
+/** @} */
+
+static void act_dump_line(struct rtnl_tc *tc, struct nl_dump_params *p)
+{
+}
+
+void rtnl_act_put_all(struct rtnl_act **head)
+{
+ struct rtnl_act *curr, *next;
+
+ curr = *head;
+ while (curr) {
+ next = curr->a_next;
+ rtnl_act_put(curr);
+ curr = next;
+ }
+ *head = NULL;
+}
+
+int rtnl_act_parse(struct rtnl_act **head, struct nlattr *tb)
+{
+ struct rtnl_act *act;
+ struct rtnl_tc_ops *ops;
+ struct nlattr *tb2[TCA_ACT_MAX + 1];
+ struct nlattr *nla[TCA_ACT_MAX_PRIO + 1];
+ char kind[TCKINDSIZ];
+ int err, i;
+
+ err = nla_parse(nla, TCA_ACT_MAX_PRIO, nla_data(tb),
+ NLMSG_ALIGN(nla_len(tb)), NULL);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < TCA_ACT_MAX_PRIO; i++) {
+ struct rtnl_tc *tc;
+
+ if (nla[i] == NULL)
+ continue;
+
+ act = rtnl_act_alloc();
+ if (!act) {
+ err = -NLE_NOMEM;
+ goto err_free;
+ }
+ tc = TC_CAST(act);
+ err = nla_parse(tb2, TCA_ACT_MAX, nla_data(nla[i]),
+ nla_len(nla[i]), NULL);
+ if (err < 0)
+ goto err_free;
+
+ if (tb2[TCA_ACT_KIND] == NULL) {
+ err = -NLE_MISSING_ATTR;
+ goto err_free;
+ }
+
+ nla_strlcpy(kind, tb2[TCA_ACT_KIND], sizeof(kind));
+ rtnl_tc_set_kind(tc, kind);
+
+ if (tb2[TCA_ACT_OPTIONS]) {
+ tc->tc_opts = nl_data_alloc_attr(tb2[TCA_ACT_OPTIONS]);
+ if (!tc->tc_opts) {
+ err = -NLE_NOMEM;
+ goto err_free;
+ }
+ tc->ce_mask |= TCA_ATTR_OPTS;
+ }
+
+ ops = rtnl_tc_get_ops(tc);
+ if (ops && ops->to_msg_parser) {
+ void *data = rtnl_tc_data(tc);
+
+ if (!data) {
+ err = -NLE_NOMEM;
+ goto err_free;
+ }
+
+ err = ops->to_msg_parser(tc, data);
+ if (err < 0)
+ goto err_free;
+ }
+ err = rtnl_act_append(head, act);
+ if (err < 0)
+ goto err_free;
+ }
+ return 0;
+
+err_free:
+ rtnl_act_put (act);
+ rtnl_act_put_all(head);
+
+ return err;
+}
+
+static int rtnl_act_msg_parse(struct nlmsghdr *n, struct rtnl_act **act)
+{
+ struct rtnl_tc *tc = TC_CAST(*act);
+ struct nl_cache *link_cache;
+ struct nlattr *tb[TCAA_MAX + 1];
+ struct tcamsg *tm;
+ int err;
+
+ tc->ce_msgtype = n->nlmsg_type;
+
+ err = nlmsg_parse(n, sizeof(*tm), tb, TCAA_MAX, NULL);
+ if (err < 0)
+ return err;
+
+ tm = nlmsg_data(n);
+ tc->tc_family = tm->tca_family;
+
+ if (tb[TCA_ACT_TAB] == NULL)
+ return -NLE_MISSING_ATTR;
+
+ err = rtnl_act_parse(act, tb[TCA_ACT_TAB]);
+ if (err < 0)
+ return err;
+
+ if ((link_cache = __nl_cache_mngt_require("route/link"))) {
+ struct rtnl_link *link;
+
+ if ((link = rtnl_link_get(link_cache, tc->tc_ifindex))) {
+ rtnl_tc_set_link(tc, link);
+
+ /* rtnl_tc_set_link incs refcnt */
+ rtnl_link_put(link);
+ }
+ }
+
+ return 0;
+}
+static int act_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+ struct nlmsghdr *nlh, struct nl_parser_param *pp)
+{
+ struct rtnl_act *act, *p_act;
+ int err;
+
+ if (!(act = rtnl_act_alloc()))
+ return -NLE_NOMEM;
+
+ if ((err = rtnl_act_msg_parse(nlh, &act)) < 0)
+ goto errout;
+
+ p_act = act;
+ while(p_act) {
+ err = pp->pp_cb(OBJ_CAST(act), pp);
+ if (err)
+ break;
+ p_act = p_act->a_next;
+ }
+errout:
+ rtnl_act_put(act);
+
+ return err;
+}
+
+static int act_request_update(struct nl_cache *cache, struct nl_sock *sk)
+{
+ struct tcamsg tcahdr = {
+ .tca_family = AF_UNSPEC,
+ };
+
+ return nl_send_simple(sk, RTM_GETACTION, NLM_F_DUMP, &tcahdr,
+ sizeof(tcahdr));
+}
+
+static struct rtnl_tc_type_ops act_ops = {
+ .tt_type = RTNL_TC_TYPE_ACT,
+ .tt_dump_prefix = "act",
+ .tt_dump = {
+ [NL_DUMP_LINE] = act_dump_line,
+ },
+};
+
+static struct nl_cache_ops rtnl_act_ops = {
+ .co_name = "route/act",
+ .co_hdrsize = sizeof(struct tcmsg),
+ .co_msgtypes = {
+ { RTM_NEWACTION, NL_ACT_NEW, "new" },
+ { RTM_DELACTION, NL_ACT_DEL, "del" },
+ { RTM_GETACTION, NL_ACT_GET, "get" },
+ END_OF_MSGTYPES_LIST,
+ },
+ .co_protocol = NETLINK_ROUTE,
+ .co_request_update = act_request_update,
+ .co_msg_parser = act_msg_parser,
+ .co_obj_ops = &act_obj_ops,
+};
+
+static struct nl_object_ops act_obj_ops = {
+ .oo_name = "route/act",
+ .oo_size = sizeof(struct rtnl_act),
+ .oo_free_data = rtnl_tc_free_data,
+ .oo_clone = rtnl_tc_clone,
+ .oo_dump = {
+ [NL_DUMP_LINE] = rtnl_tc_dump_line,
+ [NL_DUMP_DETAILS] = rtnl_tc_dump_details,
+ [NL_DUMP_STATS] = rtnl_tc_dump_stats,
+ },
+ .oo_compare = rtnl_tc_compare,
+ .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
+};
+
+static void __init act_init(void)
+{
+ rtnl_tc_type_register(&act_ops);
+ nl_cache_mngt_register(&rtnl_act_ops);
+}
+
+static void __exit act_exit(void)
+{
+ nl_cache_mngt_unregister(&rtnl_act_ops);
+ rtnl_tc_type_unregister(&act_ops);
+}
+
+/** @} */
diff --git a/lib/route/act/mirred.c b/lib/route/act/mirred.c
new file mode 100644
index 00000000..d047e24b
--- /dev/null
+++ b/lib/route/act/mirred.c
@@ -0,0 +1,242 @@
+/*
+ * lib/route/cls/mirred.c mirred action
+ *
+ * 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) 2013 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+/**
+ * @ingroup act
+ * @defgroup act_mirred Mirror and Redirect
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/act/mirred.h>
+
+static struct nla_policy mirred_policy[TCA_MIRRED_MAX + 1] = {
+ [TCA_MIRRED_PARMS] = { .minlen = sizeof(struct tc_mirred) },
+};
+
+static int mirred_msg_parser(struct rtnl_tc *tc, void *data)
+{
+ struct rtnl_mirred *u = data;
+ struct nlattr *tb[TCA_MIRRED_MAX + 1];
+ int err;
+
+ err = tca_parse(tb, TCA_MIRRED_MAX, tc, mirred_policy);
+ if (err < 0)
+ return err;
+
+ if (!tb[TCA_MIRRED_PARMS])
+ return -NLE_MISSING_ATTR;
+
+ nla_memcpy(&u->m_parm, tb[TCA_MIRRED_PARMS], sizeof(u->m_parm));
+ return 0;
+}
+
+static void mirred_free_data(struct rtnl_tc *tc, void *data)
+{
+}
+
+static int mirred_clone(void *_dst, void *_src)
+{
+ struct rtnl_mirred *dst = _dst, *src = _src;
+
+ memcpy(&dst->m_parm, &src->m_parm, sizeof(src->m_parm));
+ return 0;
+}
+
+static void mirred_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
+{
+ struct rtnl_mirred *u = data;
+ if (!u)
+ return;
+
+ nl_dump(p, " index %u", u->m_parm.ifindex);
+
+ if (u->m_parm.eaction == TCA_EGRESS_MIRROR)
+ nl_dump(p, " egress mirror");
+ else if (u->m_parm.eaction == TCA_EGRESS_REDIR)
+ nl_dump(p, " egress redirect");
+
+ switch(u->m_parm.action) {
+ case TC_ACT_UNSPEC:
+ nl_dump(p, " unspecified");
+ break;
+ case TC_ACT_PIPE:
+ nl_dump(p, " pipe");
+ break;
+ case TC_ACT_STOLEN:
+ nl_dump(p, " stolen");
+ break;
+ case TC_ACT_SHOT:
+ nl_dump(p, " shot");
+ break;
+ case TC_ACT_QUEUED:
+ nl_dump(p, " queued");
+ break;
+ case TC_ACT_REPEAT:
+ nl_dump(p, " repeat");
+ break;
+ }
+}
+
+static void mirred_dump_details(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
+{
+}
+
+static void mirred_dump_stats(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
+{
+ struct rtnl_mirred *u = data;
+
+ if (!u)
+ return;
+ /* TODO */
+}
+
+
+static int mirred_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+ struct rtnl_mirred *u = data;
+
+ if (!u)
+ return 0;
+
+ NLA_PUT(msg, TCA_MIRRED_PARMS, sizeof(u->m_parm), &u->m_parm);
+ return 0;
+
+nla_put_failure:
+ return -NLE_NOMEM;
+}
+
+/**
+ * @name Attribute Modifications
+ * @{
+ */
+
+int rtnl_mirred_set_action(struct rtnl_act *act, int action)
+{
+ struct rtnl_mirred *u;
+
+ if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act))))
+ return -NLE_NOMEM;
+
+ if (action > TCA_INGRESS_MIRROR || action < TCA_EGRESS_REDIR)
+ return -NLE_INVAL;
+
+ switch (action) {
+ case TCA_EGRESS_MIRROR:
+ case TCA_EGRESS_REDIR:
+ u->m_parm.eaction = action;
+ break;
+ case TCA_INGRESS_REDIR:
+ case TCA_INGRESS_MIRROR:
+ default:
+ return NLE_OPNOTSUPP;
+ }
+ return 0;
+}
+
+int rtnl_mirred_get_action(struct rtnl_act *act)
+{
+ struct rtnl_mirred *u;
+
+ if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act))))
+ return -NLE_NOMEM;
+ return u->m_parm.eaction;
+}
+
+int rtnl_mirred_set_ifindex(struct rtnl_act *act, uint32_t ifindex)
+{
+ struct rtnl_mirred *u;
+
+ if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act))))
+ return -NLE_NOMEM;
+
+ u->m_parm.ifindex = ifindex;
+ return 0;
+}
+
+uint32_t rtnl_mirred_get_ifindex(struct rtnl_act *act)
+{
+ struct rtnl_mirred *u;
+
+ if ((u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act))))
+ return u->m_parm.ifindex;
+ return 0;
+}
+
+int rtnl_mirred_set_policy(struct rtnl_act *act, int policy)
+{
+ struct rtnl_mirred *u;
+
+ if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act))))
+ return -NLE_NOMEM;
+
+ if (policy > TC_ACT_REPEAT || policy < TC_ACT_OK)
+ return -NLE_INVAL;
+
+ switch (u->m_parm.eaction) {
+ case TCA_EGRESS_MIRROR:
+ case TCA_EGRESS_REDIR:
+ u->m_parm.action = policy;
+ break;
+ case TCA_INGRESS_REDIR:
+ case TCA_INGRESS_MIRROR:
+ default:
+ return NLE_OPNOTSUPP;
+ }
+ return 0;
+}
+
+int rtnl_mirred_get_policy(struct rtnl_act *act)
+{
+ struct rtnl_mirred *u;
+
+ if (!(u = (struct rtnl_mirred *) rtnl_tc_data(TC_CAST(act))))
+ return -NLE_NOMEM;
+ return u->m_parm.action;
+}
+
+/** @} */
+
+static struct rtnl_tc_ops mirred_ops = {
+ .to_kind = "mirred",
+ .to_type = RTNL_TC_TYPE_ACT,
+ .to_size = sizeof(struct rtnl_mirred),
+ .to_msg_parser = mirred_msg_parser,
+ .to_free_data = mirred_free_data,
+ .to_clone = mirred_clone,
+ .to_msg_fill = mirred_msg_fill,
+ .to_dump = {
+ [NL_DUMP_LINE] = mirred_dump_line,
+ [NL_DUMP_DETAILS] = mirred_dump_details,
+ [NL_DUMP_STATS] = mirred_dump_stats,
+ },
+};
+
+static void __init mirred_init(void)
+{
+ rtnl_tc_register(&mirred_ops);
+}
+
+static void __exit mirred_exit(void)
+{
+ rtnl_tc_unregister(&mirred_ops);
+}
+
+/** @} */
diff --git a/lib/route/addr.c b/lib/route/addr.c
index 2e72f6e9..e6e91d28 100644
--- a/lib/route/addr.c
+++ b/lib/route/addr.c
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2003-2006 Baruch Even <baruch@ev-en.org>,
* Mediatrix Telecom, inc. <ericb@mediatrix.com>
*/
@@ -106,7 +106,7 @@
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/route/rtnl.h>
#include <netlink/route/addr.h>
@@ -151,6 +151,7 @@ static void addr_free_data(struct nl_object *obj)
nl_addr_put(addr->a_bcast);
nl_addr_put(addr->a_multicast);
nl_addr_put(addr->a_anycast);
+ rtnl_link_put(addr->a_link);
}
static int addr_clone(struct nl_object *_dst, struct nl_object *_src)
@@ -158,6 +159,11 @@ 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_link) {
+ nl_object_get(OBJ_CAST(src->a_link));
+ dst->a_link = src->a_link;
+ }
+
if (src->a_peer)
if (!(dst->a_peer = nl_addr_clone(src->a_peer)))
return -NLE_NOMEM;
@@ -193,7 +199,9 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
struct rtnl_addr *addr;
struct ifaddrmsg *ifa;
struct nlattr *tb[IFA_MAX+1];
- int err, peer_prefix = 0, family;
+ int err, family;
+ struct nl_cache *link_cache;
+ struct nl_addr *plen_addr = NULL;
addr = rtnl_addr_alloc();
if (!addr)
@@ -208,8 +216,9 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
ifa = nlmsg_data(nlh);
addr->a_family = family = ifa->ifa_family;
addr->a_prefixlen = ifa->ifa_prefixlen;
- addr->a_flags = ifa->ifa_flags;
addr->a_scope = ifa->ifa_scope;
+ addr->a_flags = tb[IFA_FLAGS] ? nla_get_u32(tb[IFA_FLAGS]) :
+ ifa->ifa_flags;
addr->a_ifindex = ifa->ifa_index;
addr->ce_mask = (ADDR_ATTR_FAMILY | ADDR_ATTR_PREFIXLEN |
@@ -220,6 +229,7 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
addr->ce_mask |= ADDR_ATTR_LABEL;
}
+ /* IPv6 only */
if (tb[IFA_CACHEINFO]) {
struct ifa_cacheinfo *ca;
@@ -236,6 +246,7 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
if (!addr->a_local)
goto errout_nomem;
addr->ce_mask |= ADDR_ATTR_LOCAL;
+ plen_addr = addr->a_local;
}
if (tb[IFA_ADDRESS]) {
@@ -255,13 +266,15 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
} else {
addr->a_peer = a;
addr->ce_mask |= ADDR_ATTR_PEER;
- peer_prefix = 1;
}
+
+ plen_addr = a;
}
- nl_addr_set_prefixlen(peer_prefix ? addr->a_peer : addr->a_local,
- addr->a_prefixlen);
+ if (plen_addr)
+ nl_addr_set_prefixlen(plen_addr, addr->a_prefixlen);
+ /* IPv4 only */
if (tb[IFA_BROADCAST]) {
addr->a_bcast = nl_addr_alloc_attr(tb[IFA_BROADCAST], family);
if (!addr->a_bcast)
@@ -270,6 +283,7 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
addr->ce_mask |= ADDR_ATTR_BROADCAST;
}
+ /* IPv6 only */
if (tb[IFA_MULTICAST]) {
addr->a_multicast = nl_addr_alloc_attr(tb[IFA_MULTICAST],
family);
@@ -279,6 +293,7 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
addr->ce_mask |= ADDR_ATTR_MULTICAST;
}
+ /* IPv6 only */
if (tb[IFA_ANYCAST]) {
addr->a_anycast = nl_addr_alloc_attr(tb[IFA_ANYCAST],
family);
@@ -288,6 +303,17 @@ static int addr_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
addr->ce_mask |= ADDR_ATTR_ANYCAST;
}
+ if ((link_cache = __nl_cache_mngt_require("route/link"))) {
+ struct rtnl_link *link;
+
+ if ((link = rtnl_link_get(link_cache, addr->a_ifindex))) {
+ rtnl_addr_set_link(addr, link);
+
+ /* rtnl_addr_set_link incs refcnt */
+ rtnl_link_put(link);
+ }
+ }
+
err = pp->pp_cb((struct nl_object *) addr, pp);
errout:
rtnl_addr_put(addr);
@@ -310,7 +336,7 @@ static void addr_dump_line(struct nl_object *obj, struct nl_dump_params *p)
struct nl_cache *link_cache;
char buf[128];
- link_cache = nl_cache_mngt_require("route/link");
+ link_cache = nl_cache_mngt_require_safe("route/link");
if (addr->ce_mask & ADDR_ATTR_LOCAL)
nl_dump_line(p, "%s",
@@ -339,6 +365,9 @@ static void addr_dump_line(struct nl_object *obj, struct nl_dump_params *p)
nl_dump(p, " <%s>", buf);
nl_dump(p, "\n");
+
+ if (link_cache)
+ nl_cache_put(link_cache);
}
static void addr_dump_details(struct nl_object *obj, struct nl_dump_params *p)
@@ -400,81 +429,6 @@ static void addr_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
addr_dump_details(obj, p);
}
-static void 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];
-
- nl_dump_line(p, "ADDR_FAMILY=%s\n",
- nl_af2str(addr->a_family, buf, sizeof(buf)));
-
- if (addr->ce_mask & ADDR_ATTR_LOCAL)
- nl_dump_line(p, "ADDR_LOCAL=%s\n",
- nl_addr2str(addr->a_local, buf, sizeof(buf)));
-
- if (addr->ce_mask & ADDR_ATTR_PEER)
- nl_dump_line(p, "ADDR_PEER=%s\n",
- nl_addr2str(addr->a_peer, buf, sizeof(buf)));
-
- if (addr->ce_mask & ADDR_ATTR_BROADCAST)
- nl_dump_line(p, "ADDR_BROADCAST=%s\n",
- nl_addr2str(addr->a_bcast, buf, sizeof(buf)));
-
- if (addr->ce_mask & ADDR_ATTR_ANYCAST)
- nl_dump_line(p, "ADDR_ANYCAST=%s\n",
- nl_addr2str(addr->a_anycast, buf, sizeof(buf)));
-
- if (addr->ce_mask & ADDR_ATTR_MULTICAST)
- nl_dump_line(p, "ADDR_MULTICAST=%s\n",
- nl_addr2str(addr->a_multicast, buf,
- sizeof(buf)));
-
- if (addr->ce_mask & ADDR_ATTR_PREFIXLEN)
- nl_dump_line(p, "ADDR_PREFIXLEN=%u\n",
- addr->a_prefixlen);
- link_cache = nl_cache_mngt_require("route/link");
-
- nl_dump_line(p, "ADDR_IFINDEX=%u\n", addr->a_ifindex);
- if (link_cache)
- nl_dump_line(p, "ADDR_IFNAME=%s\n",
- rtnl_link_i2name(link_cache, addr->a_ifindex,
- buf, sizeof(buf)));
-
- if (addr->ce_mask & ADDR_ATTR_SCOPE)
- nl_dump_line(p, "ADDR_SCOPE=%s\n",
- rtnl_scope2str(addr->a_scope, buf, sizeof(buf)));
-
- if (addr->ce_mask & ADDR_ATTR_LABEL)
- nl_dump_line(p, "ADDR_LABEL=%s\n", addr->a_label);
-
- rtnl_addr_flags2str(addr->a_flags, buf, sizeof(buf));
- if (buf[0])
- nl_dump_line(p, "ADDR_FLAGS=%s\n", buf);
-
- if (addr->ce_mask & ADDR_ATTR_CACHEINFO) {
- struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo;
-
- nl_dump_line(p, "ADDR_CACHEINFO_VALID=%s\n",
- ci->aci_valid == 0xFFFFFFFFU ? "forever" :
- nl_msec2str(ci->aci_valid * 1000,
- buf, sizeof(buf)));
-
- nl_dump_line(p, "ADDR_CACHEINFO_PREFERED=%s\n",
- ci->aci_prefered == 0xFFFFFFFFU ? "forever" :
- nl_msec2str(ci->aci_prefered * 1000,
- buf, sizeof(buf)));
-
- nl_dump_line(p, "ADDR_CACHEINFO_CREATED=%s\n",
- nl_msec2str(addr->a_cacheinfo.aci_cstamp * 10,
- buf, sizeof(buf)));
-
- nl_dump_line(p, "ADDR_CACHEINFO_LASTUPDATE=%s\n",
- nl_msec2str(addr->a_cacheinfo.aci_tstamp * 10,
- buf, sizeof(buf)));
- }
-}
-
static int addr_compare(struct nl_object *_a, struct nl_object *_b,
uint32_t attrs, int flags)
{
@@ -506,7 +460,7 @@ static int addr_compare(struct nl_object *_a, struct nl_object *_b,
return diff;
}
-static struct trans_tbl addr_attrs[] = {
+static const struct trans_tbl addr_attrs[] = {
__ADD(ADDR_ATTR_FAMILY, family)
__ADD(ADDR_ATTR_PREFIXLEN, prefixlen)
__ADD(ADDR_ATTR_FLAGS, flags)
@@ -553,6 +507,42 @@ int rtnl_addr_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
return nl_cache_alloc_and_fill(&rtnl_addr_ops, sk, result);
}
+/**
+ * Search address in cache
+ * @arg cache Address cache
+ * @arg ifindex Interface index of address
+ * @arg addr Local address part
+ *
+ * Searches address cache previously allocated with rtnl_addr_alloc_cache()
+ * for an address with a matching local address.
+ *
+ * The reference counter is incremented before returning the address, therefore
+ * the reference must be given back with rtnl_addr_put() after usage.
+ *
+ * @return Address object or NULL if no match was found.
+ */
+struct rtnl_addr *rtnl_addr_get(struct nl_cache *cache, int ifindex,
+ struct nl_addr *addr)
+{
+ struct rtnl_addr *a;
+
+ if (cache->c_ops != &rtnl_addr_ops)
+ return NULL;
+
+ nl_list_for_each_entry(a, &cache->c_items, ce_list) {
+ if (ifindex && a->a_ifindex != ifindex)
+ continue;
+
+ if (a->ce_mask & ADDR_ATTR_LOCAL &&
+ !nl_addr_cmp(a->a_local, addr)) {
+ nl_object_get((struct nl_object *) a);
+ return a;
+ }
+ }
+
+ return NULL;
+}
+
/** @} */
static int build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags,
@@ -563,6 +553,7 @@ static int build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags,
.ifa_family = tmpl->a_family,
.ifa_index = tmpl->a_ifindex,
.ifa_prefixlen = tmpl->a_prefixlen,
+ .ifa_flags = tmpl->a_flags,
};
if (tmpl->ce_mask & ADDR_ATTR_SCOPE)
@@ -607,6 +598,19 @@ static int build_addr_msg(struct rtnl_addr *tmpl, int cmd, int flags,
NLA_PUT(msg, IFA_CACHEINFO, sizeof(ca), &ca);
}
+ if (tmpl->a_flags & ~0xFF) {
+ /* only set the IFA_FLAGS attribute, if they actually contain additional
+ * flags that are not already set to am.ifa_flags.
+ *
+ * Older kernels refuse RTM_NEWADDR and RTM_NEWROUTE messages with EINVAL
+ * if they contain unknown netlink attributes. See net/core/rtnetlink.c, which
+ * was fixed by kernel commit 661d2967b3f1b34eeaa7e212e7b9bbe8ee072b59.
+ *
+ * With this workaround, libnl will function correctly with older kernels,
+ * unless there is a new libnl user that wants to set these flags. In this
+ * case it's up to the user to workaround this issue. */
+ NLA_PUT_U32(msg, IFA_FLAGS, tmpl->a_flags);
+ }
*result = msg;
return 0;
@@ -646,7 +650,7 @@ nla_put_failure:
int rtnl_addr_build_add_request(struct rtnl_addr *addr, int flags,
struct nl_msg **result)
{
- int required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY |
+ uint32_t required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY |
ADDR_ATTR_PREFIXLEN | ADDR_ATTR_LOCAL;
if ((addr->ce_mask & required) != required)
@@ -719,7 +723,7 @@ int rtnl_addr_add(struct nl_sock *sk, struct rtnl_addr *addr, int flags)
int rtnl_addr_build_delete_request(struct rtnl_addr *addr, int flags,
struct nl_msg **result)
{
- int required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY;
+ uint32_t required = ADDR_ATTR_IFINDEX | ADDR_ATTR_FAMILY;
if ((addr->ce_mask & required) != required)
return -NLE_MISSING_ATTR;
@@ -794,6 +798,29 @@ int rtnl_addr_get_ifindex(struct rtnl_addr *addr)
return addr->a_ifindex;
}
+void rtnl_addr_set_link(struct rtnl_addr *addr, struct rtnl_link *link)
+{
+ rtnl_link_put(addr->a_link);
+
+ if (!link)
+ return;
+
+ nl_object_get(OBJ_CAST(link));
+ addr->a_link = link;
+ addr->a_ifindex = link->l_index;
+ addr->ce_mask |= ADDR_ATTR_IFINDEX;
+}
+
+struct rtnl_link *rtnl_addr_get_link(struct rtnl_addr *addr)
+{
+ if (addr->a_link) {
+ nl_object_get(OBJ_CAST(addr->a_link));
+ return addr->a_link;
+ }
+
+ return NULL;
+}
+
void rtnl_addr_set_family(struct rtnl_addr *addr, int family)
{
addr->a_family = family;
@@ -805,10 +832,39 @@ int rtnl_addr_get_family(struct rtnl_addr *addr)
return addr->a_family;
}
-void rtnl_addr_set_prefixlen(struct rtnl_addr *addr, int prefix)
+/**
+ * Set the prefix length / netmask
+ * @arg addr Address
+ * @arg prefixlen Length of prefix (netmask)
+ *
+ * Modifies the length of the prefix. If the address object contains a peer
+ * address the prefix length will apply to it, otherwise the prefix length
+ * will apply to the local address of the address.
+ *
+ * If the address object contains a peer or local address the corresponding
+ * `struct nl_addr` will be updated with the new prefix length.
+ *
+ * @note Specifying a length of 0 will remove the prefix length alltogether.
+ *
+ * @see rtnl_addr_get_prefixlen()
+ */
+void rtnl_addr_set_prefixlen(struct rtnl_addr *addr, int prefixlen)
{
- addr->a_prefixlen = prefix;
- addr->ce_mask |= ADDR_ATTR_PREFIXLEN;
+ addr->a_prefixlen = prefixlen;
+
+ if (prefixlen)
+ addr->ce_mask |= ADDR_ATTR_PREFIXLEN;
+ else
+ addr->ce_mask &= ~ADDR_ATTR_PREFIXLEN;
+
+ /*
+ * The prefix length always applies to the peer address if
+ * a peer address is present.
+ */
+ if (addr->a_peer)
+ nl_addr_set_prefixlen(addr->a_peer, prefixlen);
+ else if (addr->a_local)
+ nl_addr_set_prefixlen(addr->a_local, prefixlen);
}
int rtnl_addr_get_prefixlen(struct rtnl_addr *addr)
@@ -849,17 +905,25 @@ unsigned int rtnl_addr_get_flags(struct rtnl_addr *addr)
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 -NLE_AF_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);
+ if (new) {
+ if (addr->ce_mask & ADDR_ATTR_FAMILY) {
+ if (new->a_family != addr->a_family)
+ return -NLE_AF_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);
+ } else {
+ if (*pos)
+ nl_addr_put(*pos);
+
+ *pos = NULL;
+ addr->ce_mask &= ~flag;
+ }
return 0;
}
@@ -868,14 +932,18 @@ int rtnl_addr_set_local(struct rtnl_addr *addr, struct nl_addr *local)
{
int err;
+ /* Prohibit local address with prefix length if peer address is present */
+ if ((addr->ce_mask & ADDR_ATTR_PEER) && local &&
+ nl_addr_get_prefixlen(local))
+ return -NLE_INVAL;
+
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;
- }
+ /* Never overwrite the prefix length if a peer address is present */
+ if (!(addr->ce_mask & ADDR_ATTR_PEER))
+ rtnl_addr_set_prefixlen(addr, local ? nl_addr_get_prefixlen(local) : 0);
return 0;
}
@@ -887,10 +955,16 @@ struct nl_addr *rtnl_addr_get_local(struct rtnl_addr *addr)
int rtnl_addr_set_peer(struct rtnl_addr *addr, struct nl_addr *peer)
{
- return __assign_addr(addr, &addr->a_peer, peer, ADDR_ATTR_PEER);
+ int err;
+
+ if (peer && peer->a_family != AF_INET)
+ return -NLE_AF_NOSUPPORT;
- addr->a_prefixlen = nl_addr_get_prefixlen(addr->a_peer);
- addr->ce_mask |= ADDR_ATTR_PREFIXLEN;
+ err = __assign_addr(addr, &addr->a_peer, peer, ADDR_ATTR_PEER);
+ if (err < 0)
+ return err;
+
+ rtnl_addr_set_prefixlen(addr, peer ? nl_addr_get_prefixlen(peer) : 0);
return 0;
}
@@ -902,6 +976,9 @@ struct nl_addr *rtnl_addr_get_peer(struct rtnl_addr *addr)
int rtnl_addr_set_broadcast(struct rtnl_addr *addr, struct nl_addr *bcast)
{
+ if (bcast && bcast->a_family != AF_INET)
+ return -NLE_AF_NOSUPPORT;
+
return __assign_addr(addr, &addr->a_bcast, bcast, ADDR_ATTR_BROADCAST);
}
@@ -912,6 +989,9 @@ struct nl_addr *rtnl_addr_get_broadcast(struct rtnl_addr *addr)
int rtnl_addr_set_multicast(struct rtnl_addr *addr, struct nl_addr *multicast)
{
+ if (multicast && multicast->a_family != AF_INET6)
+ return -NLE_AF_NOSUPPORT;
+
return __assign_addr(addr, &addr->a_multicast, multicast,
ADDR_ATTR_MULTICAST);
}
@@ -923,6 +1003,9 @@ struct nl_addr *rtnl_addr_get_multicast(struct rtnl_addr *addr)
int rtnl_addr_set_anycast(struct rtnl_addr *addr, struct nl_addr *anycast)
{
+ if (anycast && anycast->a_family != AF_INET6)
+ return -NLE_AF_NOSUPPORT;
+
return __assign_addr(addr, &addr->a_anycast, anycast,
ADDR_ATTR_ANYCAST);
}
@@ -977,7 +1060,7 @@ uint32_t rtnl_addr_get_last_update_time(struct rtnl_addr *addr)
* @{
*/
-static struct trans_tbl addr_flags[] = {
+static const struct trans_tbl addr_flags[] = {
__ADD(IFA_F_SECONDARY, secondary)
__ADD(IFA_F_NODAD, nodad)
__ADD(IFA_F_OPTIMISTIC, optimistic)
@@ -985,6 +1068,8 @@ static struct trans_tbl addr_flags[] = {
__ADD(IFA_F_DEPRECATED, deprecated)
__ADD(IFA_F_TENTATIVE, tentative)
__ADD(IFA_F_PERMANENT, permanent)
+ __ADD(IFA_F_MANAGETEMPADDR, mngtmpaddr)
+ __ADD(IFA_F_NOPREFIXROUTE, noprefixroute)
};
char *rtnl_addr_flags2str(int flags, char *buf, size_t size)
@@ -1010,7 +1095,6 @@ static struct nl_object_ops addr_obj_ops = {
[NL_DUMP_LINE] = addr_dump_line,
[NL_DUMP_DETAILS] = addr_dump_details,
[NL_DUMP_STATS] = addr_dump_stats,
- [NL_DUMP_ENV] = addr_dump_env,
},
.oo_compare = addr_compare,
.oo_attrs2str = addr_attrs2str,
diff --git a/lib/route/class.c b/lib/route/class.c
index ddf2d2e8..56ad1d86 100644
--- a/lib/route/class.c
+++ b/lib/route/class.c
@@ -1,61 +1,59 @@
/*
- * lib/route/class.c Queueing Classes
+ * lib/route/class.c Traffic 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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
*/
/**
* @ingroup tc
- * @defgroup class Queueing Classes
+ * @defgroup class Traffic Classes
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
-#include <netlink/route/tc.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/class.h>
-#include <netlink/route/class-modules.h>
#include <netlink/route/qdisc.h>
#include <netlink/route/classifier.h>
#include <netlink/utils.h>
static struct nl_cache_ops rtnl_class_ops;
+static struct nl_object_ops class_obj_ops;
+
+static void class_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p)
+{
+ struct rtnl_class *class = (struct rtnl_class *) tc;
+ char buf[32];
+
+ if (class->c_info)
+ nl_dump(p, "child-qdisc %s ",
+ rtnl_tc_handle2str(class->c_info, buf, sizeof(buf)));
+}
+
static int class_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
- struct nlmsghdr *n, struct nl_parser_param *pp)
+ struct nlmsghdr *nlh, struct nl_parser_param *pp)
{
- int err;
struct rtnl_class *class;
- struct rtnl_class_ops *cops;
-
- class = rtnl_class_alloc();
- if (!class) {
- err = -NLE_NOMEM;
- goto errout;
- }
- class->ce_msgtype = n->nlmsg_type;
+ int err;
- err = tca_msg_parser(n, (struct rtnl_tca *) class);
- if (err < 0)
- goto errout_free;
+ if (!(class = rtnl_class_alloc()))
+ return -NLE_NOMEM;
- cops = rtnl_class_lookup_ops(class);
- if (cops && cops->co_msg_parser) {
- err = cops->co_msg_parser(class);
- if (err < 0)
- goto errout_free;
- }
+ if ((err = rtnl_tc_msg_parse(nlh, TC_CAST(class))) < 0)
+ goto errout;
- err = pp->pp_cb((struct nl_object *) class, pp);
-errout_free:
- rtnl_class_put(class);
+ err = pp->pp_cb(OBJ_CAST(class), pp);
errout:
+ rtnl_class_put(class);
+
return err;
}
@@ -71,75 +69,104 @@ static int class_request_update(struct nl_cache *cache, struct nl_sock *sk)
}
/**
- * @name Addition/Modification
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct rtnl_class *rtnl_class_alloc(void)
+{
+ struct rtnl_tc *tc;
+
+ tc = TC_CAST(nl_object_alloc(&class_obj_ops));
+ if (tc)
+ tc->tc_type = RTNL_TC_TYPE_CLASS;
+
+ return (struct rtnl_class *) tc;
+}
+
+void rtnl_class_put(struct rtnl_class *class)
+{
+ nl_object_put((struct nl_object *) class);
+}
+
+/** @} */
+
+
+/**
+ * @name Addition/Modification/Deletion
* @{
*/
static int class_build(struct rtnl_class *class, int type, int flags,
struct nl_msg **result)
{
- struct rtnl_class_ops *cops;
- int err;
+ uint32_t needed = TCA_ATTR_PARENT | TCA_ATTR_HANDLE;
- err = tca_build_msg((struct rtnl_tca *) class, type, flags, result);
- if (err < 0)
- return err;
-
- 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(*result, TCA_OPTIONS, opts);
- nlmsg_free(opts);
- if (err < 0)
- goto errout;
- }
+ if ((class->ce_mask & needed) == needed &&
+ TC_H_MAJ(class->c_parent) && TC_H_MAJ(class->c_handle) &&
+ TC_H_MAJ(class->c_parent) != TC_H_MAJ(class->c_handle)) {
+ APPBUG("TC_H_MAJ(parent) must match TC_H_MAJ(handle)");
+ return -NLE_INVAL;
}
- return 0;
-errout:
- nlmsg_free(*result);
- return err;
+ return rtnl_tc_msg_build(TC_CAST(class), type, flags, result);
}
/**
- * Build a netlink message to add a new class
- * @arg class class to add
- * @arg flags additional netlink message flags
- * @arg result Pointer to store resulting message.
+ * Build a netlink message requesting the addition of a traffic class
+ * @arg class Traffic class to add
+ * @arg flags Additional netlink message flags
+ * @arg result Pointer to store resulting netlink message
*
- * 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.
+ * The behaviour of this function is identical to rtnl_class_add() with
+ * the exception that it will not send the message but return it int the
+ * provided return pointer instead.
*
- * Common message flags
- * - NLM_F_REPLACE - replace possibly existing classes
+ * @see rtnl_class_add()
*
* @return 0 on success or a negative error code.
*/
int rtnl_class_build_add_request(struct rtnl_class *class, int flags,
struct nl_msg **result)
{
- return class_build(class, RTM_NEWTCLASS, NLM_F_CREATE | flags, result);
+ return class_build(class, RTM_NEWTCLASS, flags, result);
}
/**
- * Add a new class
- * @arg sk Netlink socket.
- * @arg class class to delete
- * @arg flags additional netlink message flags
+ * Add/Update traffic class
+ * @arg sk Netlink socket
+ * @arg class Traffic class to add
+ * @arg flags Additional netlink message flags
+ *
+ * Builds a \c RTM_NEWTCLASS netlink message requesting the addition
+ * of a new traffic class and sends the message to the kernel. The
+ * configuration of the traffic class is derived from the attributes
+ * of the specified traffic class.
+ *
+ * The following flags may be specified:
+ * - \c NLM_F_CREATE: Create traffic class if it does not exist,
+ * otherwise -NLE_OBJ_NOTFOUND is returned.
+ * - \c NLM_F_EXCL: Return -NLE_EXISTS if a traffic class with
+ * matching handle exists already.
+ *
+ * Existing traffic classes with matching handles will be updated,
+ * unless the flag \c NLM_F_EXCL is specified. If no matching traffic
+ * class exists, it will be created if the flag \c NLM_F_CREATE is set,
+ * otherwise the error -NLE_OBJ_NOTFOUND is returned.
+ *
+ * If the parent qdisc does not support classes, the error
+ * \c NLE_OPNOTSUPP is returned.
*
- * 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.
+ * After sending, the function will wait for the ACK or an eventual
+ * error message to be received and will therefore block until the
+ * operation has been completed.
*
- * Common message flags
- * - NLM_F_REPLACE - replace possibly existing classes
+ * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
+ * this function to return immediately after sending. In this case,
+ * it is the responsibility of the caller to handle any error
+ * messages returned.
*
- * @return 0 on success or a negative error code
+ * @return 0 on success or a negative error code.
*/
int rtnl_class_add(struct nl_sock *sk, struct rtnl_class *class, int flags)
{
@@ -149,32 +176,44 @@ int rtnl_class_add(struct nl_sock *sk, struct rtnl_class *class, int flags)
if ((err = rtnl_class_build_add_request(class, flags, &msg)) < 0)
return err;
- err = nl_send_auto_complete(sk, msg);
- nlmsg_free(msg);
- if (err < 0)
- return err;
-
- return wait_for_ack(sk);
+ return nl_send_sync(sk, msg);
}
-int rtnl_class_build_delete_request(struct rtnl_class *class,
- struct nl_msg **result)
+/**
+ * Build netlink message requesting the deletion of a traffic class
+ * @arg class Traffic class to delete
+ * @arg result Pointer to store resulting netlink message
+ *
+ * The behaviour of this function is identical to rtnl_class_delete() with
+ * the exception that it will not send the message but return it in the
+ * provided return pointer instead.
+ *
+ * @see rtnl_class_delete()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_class_build_delete_request(struct rtnl_class *class, struct nl_msg **result)
{
struct nl_msg *msg;
struct tcmsg tchdr;
- int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
+ uint32_t required = TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE;
- if ((class->ce_mask & required) != required)
- BUG();
+ if ((class->ce_mask & required) != required) {
+ APPBUG("ifindex and handle must be specified");
+ return -NLE_MISSING_ATTR;
+ }
- msg = nlmsg_alloc_simple(RTM_DELTCLASS, 0);
- if (!msg)
+ if (!(msg = nlmsg_alloc_simple(RTM_DELTCLASS, 0)))
return -NLE_NOMEM;
+ memset(&tchdr, 0, sizeof(tchdr));
tchdr.tcm_family = AF_UNSPEC;
- tchdr.tcm_handle = class->c_handle;
- tchdr.tcm_parent = class->c_parent;
tchdr.tcm_ifindex = class->c_ifindex;
+ tchdr.tcm_handle = class->c_handle;
+
+ if (class->ce_mask & TCA_ATTR_PARENT)
+ tchdr.tcm_parent = class->c_parent;
+
if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
nlmsg_free(msg);
return -NLE_MSGSIZE;
@@ -185,15 +224,30 @@ int rtnl_class_build_delete_request(struct rtnl_class *class,
}
/**
- * Delete a class
- * @arg sk Netlink socket.
- * @arg class class to delete
+ * Delete traffic class
+ * @arg sk Netlink socket
+ * @arg class Traffic class to delete
*
- * Builds a netlink message by calling rtnl_class_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.
+ * Builds a \c RTM_DELTCLASS netlink message requesting the deletion
+ * of a traffic class and sends the message to the kernel.
*
- * @return 0 on success or a negative error code
+ * The message is constructed out of the following attributes:
+ * - \c ifindex and \c handle (required)
+ * - \c parent (optional, must match if provided)
+ *
+ * All other class attributes including all class type specific
+ * attributes are ignored.
+ *
+ * After sending, the function will wait for the ACK or an eventual
+ * error message to be received and will therefore block until the
+ * operation has been completed.
+ *
+ * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
+ * this function to return immediately after sending. In this case,
+ * it is the responsibility of the caller to handle any error
+ * messages returned.
+ *
+ * @return 0 on success or a negative error code.
*/
int rtnl_class_delete(struct nl_sock *sk, struct rtnl_class *class)
{
@@ -203,40 +257,70 @@ int rtnl_class_delete(struct nl_sock *sk, struct rtnl_class *class)
if ((err = rtnl_class_build_delete_request(class, &msg)) < 0)
return err;
- err = nl_send_auto_complete(sk, msg);
- nlmsg_free(msg);
- if (err < 0)
- return err;
+ return nl_send_sync(sk, msg);
+}
+
+/** @} */
+
+/**
+ * @name Leaf Qdisc
+ * @{
+ */
+
+/**
+ * Lookup the leaf qdisc of a traffic class
+ * @arg class the parent traffic class
+ * @arg cache a qdisc cache allocated using rtnl_qdisc_alloc_cache()
+ *
+ * @return Matching Qdisc or NULL if the traffic 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 wait_for_ack(sk);
+ return leaf;
}
/** @} */
/**
- * @name Cache Management
+ * @name Cache Related Functions
* @{
*/
/**
- * Build a class cache including all classes attached to the specified interface
- * @arg sk Netlink socket.
- * @arg ifindex interface index of the link the classes are
- * attached to.
+ * Allocate a cache and fill it with all configured traffic classes
+ * @arg sk Netlink socket
+ * @arg ifindex Interface index of the network device
+ * @arg result Pointer to store the created cache
*
- * Allocates a new cache, initializes it properly and updates it to
- * include all classes attached to the specified interface.
+ * Allocates a new traffic class cache and fills it with a list of all
+ * configured traffic classes on a specific network device. Release the
+ * cache with nl_cache_free().
*
- * @return The cache or NULL if an error has occured.
+ * @return 0 on success or a negative error code.
*/
int rtnl_class_alloc_cache(struct nl_sock *sk, int ifindex,
struct nl_cache **result)
{
struct nl_cache * cache;
int err;
+
+ if (!ifindex) {
+ APPBUG("ifindex must be specified");
+ return -NLE_INVAL;
+ }
- cache = nl_cache_alloc(&rtnl_class_ops);
- if (!cache)
+ if (!(cache = nl_cache_alloc(&rtnl_class_ops)))
return -NLE_NOMEM;
cache->c_iarg1 = ifindex;
@@ -251,14 +335,23 @@ int rtnl_class_alloc_cache(struct nl_sock *sk, int ifindex,
}
/**
- * Look up class by its handle in the provided cache
- * @arg cache class cache
- * @arg ifindex interface the class is attached to
- * @arg handle class handle
- * @return pointer to class inside the cache or NULL if no match was found.
+ * Search traffic class by interface index and handle
+ * @arg cache Traffic class cache
+ * @arg ifindex Interface index
+ * @arg handle ID of traffic class
+ *
+ * Searches a traffic class cache previously allocated with
+ * rtnl_class_alloc_cache() and searches for a traffi class matching
+ * the interface index and handle.
+ *
+ * The reference counter is incremented before returning the traffic
+ * class, therefore the reference must be given back with rtnl_class_put()
+ * after usage.
+ *
+ * @return Traffic class or NULL if no match was found.
*/
struct rtnl_class *rtnl_class_get(struct nl_cache *cache, int ifindex,
- uint32_t handle)
+ uint32_t handle)
{
struct rtnl_class *class;
@@ -276,6 +369,80 @@ struct rtnl_class *rtnl_class_get(struct nl_cache *cache, int ifindex,
/** @} */
+/**
+ * @name Deprecated Functions
+ * @{
+ */
+
+/**
+ * Call a callback for each child of a class
+ *
+ * @deprecated Use of this function is deprecated, it does not allow
+ * to handle the out of memory situation that can occur.
+ */
+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_tc_set_parent(TC_CAST(filter), class->c_handle);
+ rtnl_tc_set_ifindex(TC_CAST(filter), class->c_ifindex);
+ rtnl_tc_set_kind(TC_CAST(filter), class->c_kind);
+
+ nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg);
+ rtnl_class_put(filter);
+}
+
+/**
+ * Call a callback for each classifier attached to the class
+ *
+ * @deprecated Use of this function is deprecated, it does not allow
+ * to handle the out of memory situation that can occur.
+ */
+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_tc_set_ifindex((struct rtnl_tc *) filter, class->c_ifindex);
+ rtnl_tc_set_parent((struct rtnl_tc *) filter, class->c_parent);
+
+ nl_cache_foreach_filter(cache, (struct nl_object *) filter, cb, arg);
+ rtnl_cls_put(filter);
+}
+
+/** @} */
+
+static struct rtnl_tc_type_ops class_ops = {
+ .tt_type = RTNL_TC_TYPE_CLASS,
+ .tt_dump_prefix = "class",
+ .tt_dump = {
+ [NL_DUMP_DETAILS] = class_dump_details,
+ },
+};
+
+static struct nl_object_ops class_obj_ops = {
+ .oo_name = "route/class",
+ .oo_size = sizeof(struct rtnl_class),
+ .oo_free_data = rtnl_tc_free_data,
+ .oo_clone = rtnl_tc_clone,
+ .oo_dump = {
+ [NL_DUMP_LINE] = rtnl_tc_dump_line,
+ [NL_DUMP_DETAILS] = rtnl_tc_dump_details,
+ [NL_DUMP_STATS] = rtnl_tc_dump_stats,
+ },
+ .oo_compare = rtnl_tc_compare,
+ .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
+};
+
static struct nl_cache_ops rtnl_class_ops = {
.co_name = "route/class",
.co_hdrsize = sizeof(struct tcmsg),
@@ -286,6 +453,7 @@ static struct nl_cache_ops rtnl_class_ops = {
END_OF_MSGTYPES_LIST,
},
.co_protocol = NETLINK_ROUTE,
+ .co_groups = tc_groups,
.co_request_update = &class_request_update,
.co_msg_parser = &class_msg_parser,
.co_obj_ops = &class_obj_ops,
@@ -293,12 +461,14 @@ static struct nl_cache_ops rtnl_class_ops = {
static void __init class_init(void)
{
+ rtnl_tc_type_register(&class_ops);
nl_cache_mngt_register(&rtnl_class_ops);
}
static void __exit class_exit(void)
{
nl_cache_mngt_unregister(&rtnl_class_ops);
+ rtnl_tc_type_unregister(&class_ops);
}
/** @} */
diff --git a/lib/route/class_api.c b/lib/route/class_api.c
deleted file mode 100644
index 374cf0f8..00000000
--- a/lib/route/class_api.c
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * 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-2008 Thomas Graf <tgraf@suug.ch>
- */
-
-/**
- * @ingroup class
- * @defgroup class_api Class Modules
- * @{
- */
-
-#include <netlink-local.h>
-#include <netlink-tc.h>
-#include <netlink/netlink.h>
-#include <netlink/route/tc.h>
-#include <netlink/route/class.h>
-#include <netlink/route/class-modules.h>
-#include <netlink/utils.h>
-
-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 -NLE_EXIST;
-
- 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 -NLE_OBJ_NOTFOUND;
-
- *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
deleted file mode 100644
index 5c2e5be6..00000000
--- a/lib/route/class_obj.c
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * 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 <tgraf@suug.ch>
- */
-
-/**
- * @ingroup class
- * @defgroup class_obj Class Object
- * @{
- */
-
-#include <netlink-local.h>
-#include <netlink-tc.h>
-#include <netlink/netlink.h>
-#include <netlink/route/tc.h>
-#include <netlink/route/class.h>
-#include <netlink/route/class-modules.h>
-#include <netlink/route/qdisc.h>
-#include <netlink/route/classifier.h>
-#include <netlink/utils.h>
-
-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 void class_dump_line(struct nl_object *obj, struct nl_dump_params *p)
-{
- struct rtnl_class *class = (struct rtnl_class *) obj;
- struct rtnl_class_ops *cops;
-
- tca_dump_line((struct rtnl_tca *) class, "class", p);
-
- cops = rtnl_class_lookup_ops(class);
- if (cops && cops->co_dump[NL_DUMP_LINE])
- cops->co_dump[NL_DUMP_LINE](class, p);
- nl_dump(p, "\n");
-}
-
-static void class_dump_details(struct nl_object *obj, struct nl_dump_params *p)
-{
- struct rtnl_class *class = (struct rtnl_class *) obj;
- struct rtnl_class_ops *cops;
-
- class_dump_line(obj, p);
- tca_dump_details((struct rtnl_tca *) class, p);
-
- if (class->c_info) {
- char buf[32];
- nl_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_DETAILS])
- cops->co_dump[NL_DUMP_DETAILS](class, p);
- else if (!class->c_info)
- nl_dump(p, "noop (no leaf qdisc)");
-
- nl_dump(p, "\n");
-}
-
-static void 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;
-
- class_dump_details(obj, p);
- tca_dump_stats((struct rtnl_tca *) class, p);
- nl_dump(p, "\n");
-
- cops = rtnl_class_lookup_ops(class);
- if (cops && cops->co_dump[NL_DUMP_STATS])
- cops->co_dump[NL_DUMP_STATS](class, p);
-}
-
-/**
- * @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_LINE] = class_dump_line,
- [NL_DUMP_DETAILS] = class_dump_details,
- [NL_DUMP_STATS] = class_dump_stats,
- },
- .oo_compare = tca_compare,
- .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
-};
-
-/** @} */
diff --git a/lib/route/classid.c b/lib/route/classid.c
new file mode 100644
index 00000000..f2d3a01c
--- /dev/null
+++ b/lib/route/classid.c
@@ -0,0 +1,456 @@
+/*
+ * lib/route/classid.c ClassID 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) 2010-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup tc
+ * @defgroup classid ClassID Management
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/route/tc.h>
+
+struct classid_map
+{
+ uint32_t classid;
+ char * name;
+ struct nl_list_head name_list;
+};
+
+#define CLASSID_NAME_HT_SIZ 256
+
+static struct nl_list_head tbl_name[CLASSID_NAME_HT_SIZ];
+
+static void *id_root = NULL;
+
+static int compare_id(const void *pa, const void *pb)
+{
+ const struct classid_map *ma = pa;
+ const struct classid_map *mb = pb;
+
+ if (ma->classid < mb->classid)
+ return -1;
+
+ if (ma->classid > mb->classid)
+ return 1;
+
+ return 0;
+}
+
+/* djb2 */
+static unsigned int classid_tbl_hash(const char *str)
+{
+ unsigned long hash = 5381;
+ int c;
+
+ while ((c = *str++))
+ hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
+
+ return hash % CLASSID_NAME_HT_SIZ;
+}
+
+static int classid_lookup(const char *name, uint32_t *result)
+{
+ struct classid_map *map;
+ int n = classid_tbl_hash(name);
+
+ nl_list_for_each_entry(map, &tbl_name[n], name_list) {
+ if (!strcasecmp(map->name, name)) {
+ *result = map->classid;
+ return 0;
+ }
+ }
+
+ return -NLE_OBJ_NOTFOUND;
+}
+
+static char *name_lookup(const uint32_t classid)
+{
+ void *res;
+ struct classid_map cm = {
+ .classid = classid,
+ .name = "search entry",
+ };
+
+ if ((res = tfind(&cm, &id_root, &compare_id)))
+ return (*(struct classid_map **) res)->name;
+
+ return NULL;
+}
+
+/**
+ * @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 (TC_H_INGRESS == handle)
+ snprintf(buf, len, "ingress");
+ else {
+ char *name;
+
+ if ((name = name_lookup(handle)))
+ snprintf(buf, len, "%s", name);
+ else if (0 == TC_H_MAJ(handle))
+ snprintf(buf, len, ":%x", TC_H_MIN(handle));
+ else if (0 == TC_H_MIN(handle))
+ snprintf(buf, len, "%x:", TC_H_MAJ(handle) >> 16);
+ else
+ snprintf(buf, len, "%x:%x",
+ TC_H_MAJ(handle) >> 16, TC_H_MIN(handle));
+ }
+
+ return buf;
+}
+
+/**
+ * Convert a charactering strint to a traffic control handle
+ * @arg str 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:
+ * - NAME
+ * - root
+ * - none
+ * - MAJ:
+ * - :MIN
+ * - NAME:MIN
+ * - MAJ:MIN
+ * - MAJMIN
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_tc_str2handle(const char *str, uint32_t *res)
+{
+ char *colon, *end;
+ uint32_t h;
+ int err;
+
+ if (!strcasecmp(str, "root")) {
+ *res = TC_H_ROOT;
+ return 0;
+ }
+
+ if (!strcasecmp(str, "none")) {
+ *res = TC_H_UNSPEC;
+ return 0;
+ }
+
+ if (!strcasecmp(str, "ingress")) {
+ *res = TC_H_INGRESS;
+ return 0;
+ }
+
+ h = strtoul(str, &colon, 16);
+
+ /* MAJ is not a number */
+ if (colon == str) {
+not_a_number:
+ if (*colon == ':') {
+ /* :YYYY */
+ h = 0;
+ } else {
+ size_t len;
+ char name[64] = { 0 };
+
+ if (!(colon = strpbrk(str, ":"))) {
+ /* NAME */
+ return classid_lookup(str, res);
+ } else {
+ /* NAME:YYYY */
+ len = colon - str;
+ if (len >= sizeof(name))
+ return -NLE_INVAL;
+
+ memcpy(name, str, len);
+
+ if ((err = classid_lookup(name, &h)) < 0)
+ return err;
+
+ /* Name must point to a qdisc alias */
+ if (TC_H_MIN(h))
+ return -NLE_INVAL;
+
+ /* NAME: is not allowed */
+ if (colon[1] == '\0')
+ return -NLE_INVAL;
+
+ goto update;
+ }
+ }
+ }
+
+ if (':' == *colon) {
+ /* check if we would lose bits */
+ if (TC_H_MAJ(h))
+ return -NLE_RANGE;
+ h <<= 16;
+
+ if ('\0' == colon[1]) {
+ /* XXXX: */
+ *res = h;
+ } else {
+ /* XXXX:YYYY */
+ uint32_t l;
+
+update:
+ l = strtoul(colon+1, &end, 16);
+
+ /* check if we overlap with major part */
+ if (TC_H_MAJ(l))
+ return -NLE_RANGE;
+
+ if ('\0' != *end)
+ return -NLE_INVAL;
+
+ *res = (h | l);
+ }
+ } else if ('\0' == *colon) {
+ /* XXXXYYYY */
+ *res = h;
+ } else
+ goto not_a_number;
+
+ return 0;
+}
+
+static void free_nothing(void *arg)
+{
+}
+
+static void classid_map_free(struct classid_map *map)
+{
+ if (!map)
+ return;
+
+ free(map->name);
+ free(map);
+}
+
+static void clear_hashtable(void)
+{
+ int i;
+
+ for (i = 0; i < CLASSID_NAME_HT_SIZ; i++) {
+ struct classid_map *map, *n;
+
+ nl_list_for_each_entry_safe(map, n, &tbl_name[i], name_list)
+ classid_map_free(map);
+
+ nl_init_list_head(&tbl_name[i]);
+
+ }
+
+ if (id_root) {
+ tdestroy(&id_root, &free_nothing);
+ id_root = NULL;
+ }
+}
+
+static int classid_map_add(uint32_t classid, const char *name)
+{
+ struct classid_map *map;
+ int n;
+
+ if (!(map = calloc(1, sizeof(*map))))
+ return -NLE_NOMEM;
+
+ map->classid = classid;
+ map->name = strdup(name);
+
+ n = classid_tbl_hash(map->name);
+ nl_list_add_tail(&map->name_list, &tbl_name[n]);
+
+ if (!tsearch((void *) map, &id_root, &compare_id)) {
+ classid_map_free(map);
+ return -NLE_NOMEM;
+ }
+
+ return 0;
+}
+
+/**
+ * (Re-)read classid file
+ *
+ * Rereads the contents of the classid file (typically found at the location
+ * /etc/libnl/classid) and refreshes the classid maps.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_tc_read_classid_file(void)
+{
+ static time_t last_read;
+ struct stat st;
+ char buf[256], *path;
+ FILE *fd;
+ int err;
+
+ if (build_sysconf_path(&path, "classid") < 0)
+ return -NLE_NOMEM;
+
+ /* if stat fails, just (re-)read the file */
+ if (stat(path, &st) == 0) {
+ /* Don't re-read file if file is unchanged */
+ if (last_read == st.st_mtime) {
+ err = 0;
+ goto errout;
+ }
+ }
+
+ if (!(fd = fopen(path, "r"))) {
+ err = -nl_syserr2nlerr(errno);
+ goto errout;
+ }
+
+ clear_hashtable();
+
+ while (fgets(buf, sizeof(buf), fd)) {
+ uint32_t classid;
+ char *ptr, *tok;
+
+ /* ignore comments and empty lines */
+ if (*buf == '#' || *buf == '\n' || *buf == '\r')
+ continue;
+
+ /* token 1 */
+ if (!(tok = strtok_r(buf, " \t", &ptr))) {
+ err = -NLE_INVAL;
+ goto errout_close;
+ }
+
+ if ((err = rtnl_tc_str2handle(tok, &classid)) < 0)
+ goto errout_close;
+
+ if (!(tok = strtok_r(NULL, " \t\n\r#", &ptr))) {
+ err = -NLE_INVAL;
+ goto errout_close;
+ }
+
+ if ((err = classid_map_add(classid, tok)) < 0)
+ goto errout_close;
+ }
+
+ err = 0;
+ last_read = st.st_mtime;
+
+errout_close:
+ fclose(fd);
+errout:
+ free(path);
+
+ return err;
+
+}
+
+int rtnl_classid_generate(const char *name, uint32_t *result, uint32_t parent)
+{
+ static uint32_t base = 0x4000 << 16;
+ uint32_t classid;
+ char *path;
+ FILE *fd;
+ int err = 0;
+
+ if (parent == TC_H_ROOT || parent == TC_H_INGRESS) {
+ do {
+ base += (1 << 16);
+ if (base == TC_H_MAJ(TC_H_ROOT))
+ base = 0x4000 << 16;
+ } while (name_lookup(base));
+
+ classid = base;
+ } else {
+ classid = TC_H_MAJ(parent);
+ do {
+ if (TC_H_MIN(++classid) == TC_H_MIN(TC_H_ROOT))
+ return -NLE_RANGE;
+ } while (name_lookup(classid));
+ }
+
+ NL_DBG(2, "Generated new classid %#x\n", classid);
+
+ if (build_sysconf_path(&path, "classid") < 0)
+ return -NLE_NOMEM;
+
+ if (!(fd = fopen(path, "a"))) {
+ err = -nl_syserr2nlerr(errno);
+ goto errout;
+ }
+
+ fprintf(fd, "%x:", TC_H_MAJ(classid) >> 16);
+ if (TC_H_MIN(classid))
+ fprintf(fd, "%x", TC_H_MIN(classid));
+ fprintf(fd, "\t\t\t%s\n", name);
+
+ fclose(fd);
+
+ if ((err = classid_map_add(classid, name)) < 0) {
+ /*
+ * Error adding classid map, re-read classid file is best
+ * option here. It is likely to fail as well but better
+ * than nothing, entry was added to the file already anyway.
+ */
+ rtnl_tc_read_classid_file();
+ }
+
+ *result = classid;
+ err = 0;
+errout:
+ free(path);
+
+ return err;
+}
+
+/** @} */
+
+static void __init classid_init(void)
+{
+ int err, i;
+
+ for (i = 0; i < CLASSID_NAME_HT_SIZ; i++)
+ nl_init_list_head(&tbl_name[i]);
+
+ if ((err = rtnl_tc_read_classid_file()) < 0)
+ NL_DBG(1, "Failed to read classid file: %s\n", nl_geterror(err));
+}
+
+static void free_map(void *map) {
+ free(((struct classid_map *)map)->name);
+ free(map);
+};
+
+static void __exit classid_exit(void)
+{
+ tdestroy(id_root, free_map);
+}
+/** @} */
diff --git a/lib/route/cls.c b/lib/route/cls.c
index cbf03456..649a7d04 100644
--- a/lib/route/cls.c
+++ b/lib/route/cls.c
@@ -6,156 +6,180 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
*/
/**
* @ingroup tc
* @defgroup cls Classifiers
- *
- * @par Classifier Identification
- * - protocol
- * - priority
- * - parent
- * - interface
- * - kind
- * - handle
- *
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
-#include <netlink/route/tc.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/classifier.h>
-#include <netlink/route/classifier-modules.h>
#include <netlink/route/link.h>
+/** @cond SKIP */
+#define CLS_ATTR_PRIO (TCA_ATTR_MAX << 1)
+#define CLS_ATTR_PROTOCOL (TCA_ATTR_MAX << 2)
+/** @endcond */
+
+static struct nl_object_ops cls_obj_ops;
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, struct nl_parser_param *pp)
+
+static int cls_build(struct rtnl_cls *cls, int type, int flags,
+ struct nl_msg **result)
{
- struct rtnl_cls_ops *cops;
- struct rtnl_cls *cls;
- int err;
+ int err, prio, proto;
+ struct tcmsg *tchdr;
+ uint32_t required = TCA_ATTR_IFINDEX;
- cls = rtnl_cls_alloc();
- if (!cls) {
- err = -NLE_NOMEM;
- goto errout;
+ if ((cls->ce_mask & required) != required) {
+ APPBUG("ifindex must be specified");
+ return -NLE_MISSING_ATTR;
}
- cls->ce_msgtype = nlh->nlmsg_type;
- err = tca_msg_parser(nlh, (struct rtnl_tca *) cls);
+ err = rtnl_tc_msg_build(TC_CAST(cls), type, flags, result);
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));
+ return err;
- cops = rtnl_cls_lookup_ops(cls);
- if (cops && cops->co_msg_parser && (err = cops->co_msg_parser(cls)) < 0)
- goto errout_free;
+ tchdr = nlmsg_data(nlmsg_hdr(*result));
+ prio = rtnl_cls_get_prio(cls);
+ proto = rtnl_cls_get_protocol(cls);
+ tchdr->tcm_info = TC_H_MAKE(prio << 16, htons(proto));
- err = pp->pp_cb((struct nl_object *) cls, pp);
-errout_free:
- rtnl_cls_put(cls);
-errout:
- return err;
+ return 0;
}
-static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk)
+/**
+ * @name Allocation/Freeing
+ * @{
+ */
+
+struct rtnl_cls *rtnl_cls_alloc(void)
{
- struct tcmsg tchdr = {
- .tcm_family = AF_UNSPEC,
- .tcm_ifindex = cache->c_iarg1,
- .tcm_parent = cache->c_iarg2,
- };
+ struct rtnl_tc *tc;
- return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr,
- sizeof(tchdr));
-}
+ tc = TC_CAST(nl_object_alloc(&cls_obj_ops));
+ if (tc)
+ tc->tc_type = RTNL_TC_TYPE_CLS;
+ return (struct rtnl_cls *) tc;
+}
-static int cls_build(struct rtnl_cls *cls, int type, int flags,
- struct nl_msg **result)
+void rtnl_cls_put(struct rtnl_cls *cls)
{
- struct rtnl_cls_ops *cops;
- int err, prio, proto;
- struct tcmsg *tchdr;
-
- err = tca_build_msg((struct rtnl_tca *) cls, type, flags, result);
- if (err < 0)
- return err;
+ nl_object_put((struct nl_object *) cls);
+}
- tchdr = nlmsg_data(nlmsg_hdr(*result));
- 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;
+/**
+ * @name Attributes
+ * @{
+ */
- if (!(opts = nlmsg_alloc())) {
- err = -NLE_NOMEM;
- goto errout;
- }
+void rtnl_cls_set_prio(struct rtnl_cls *cls, uint16_t prio)
+{
+ cls->c_prio = prio;
+ cls->ce_mask |= CLS_ATTR_PRIO;
+}
- if (!(err = cops->co_get_opts(cls, opts)))
- err = nla_put_nested(*result, TCA_OPTIONS, opts);
+uint16_t rtnl_cls_get_prio(struct rtnl_cls *cls)
+{
+ if (cls->ce_mask & CLS_ATTR_PRIO)
+ return cls->c_prio;
+ else
+ return 0;
+}
- nlmsg_free(opts);
- if (err < 0)
- goto errout;
- }
+void rtnl_cls_set_protocol(struct rtnl_cls *cls, uint16_t protocol)
+{
+ cls->c_protocol = protocol;
+ cls->ce_mask |= CLS_ATTR_PROTOCOL;
+}
- return 0;
-errout:
- nlmsg_free(*result);
- return err;
+uint16_t rtnl_cls_get_protocol(struct rtnl_cls *cls)
+{
+ if (cls->ce_mask & CLS_ATTR_PROTOCOL)
+ return cls->c_protocol;
+ else
+ return ETH_P_ALL;
}
+/** @} */
+
+
/**
- * @name Classifier Addition/Modification/Deletion
+ * @name Addition/Modification/Deletion
* @{
*/
/**
- * Build a netlink message to add a new classifier
- * @arg cls classifier to add
- * @arg flags additional netlink message flags
- * @arg result Pointer to store resulting message.
+ * Build a netlink message requesting the addition of a classifier
+ * @arg cls Classifier to add
+ * @arg flags Additional netlink message flags
+ * @arg result Pointer to store resulting netlink message
*
- * 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.
+ * The behaviour of this function is identical to rtnl_cls_add() with
+ * the exception that it will not send the message but return it int the
+ * provided return pointer instead.
+ *
+ * @see rtnl_cls_add()
*
* @return 0 on success or a negative error code.
*/
int rtnl_cls_build_add_request(struct rtnl_cls *cls, int flags,
struct nl_msg **result)
{
- return cls_build(cls, RTM_NEWTFILTER, NLM_F_CREATE | flags, result);
+ if (!(flags & NLM_F_CREATE) && !(cls->ce_mask & CLS_ATTR_PRIO)) {
+ APPBUG("prio must be specified if not a new classifier");
+ return -NLE_MISSING_ATTR;
+ }
+
+ return cls_build(cls, RTM_NEWTFILTER, flags, result);
}
/**
- * Add a new classifier
- * @arg sk Netlink socket.
- * @arg cls classifier to add
- * @arg flags additional netlink message flags
+ * Add/Update classifier
+ * @arg sk Netlink socket
+ * @arg cls Classifier to add/update
+ * @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.
+ * Builds a \c RTM_NEWTFILTER netlink message requesting the addition
+ * of a new classifier and sends the message to the kernel. The
+ * configuration of the classifier is derived from the attributes of
+ * the specified traffic class.
*
- * @return 0 on sucess or a negative error if an error occured.
+ * The following flags may be specified:
+ * - \c NLM_F_CREATE: Create classifier if it does not exist,
+ * otherwise -NLE_OBJ_NOTFOUND is returned.
+ * - \c NLM_F_EXCL: Return -NLE_EXISTS if a classifier with
+ * matching handle exists already.
+ *
+ * Existing classifiers with matching handles will be updated, unless
+ * the flag \c NLM_F_EXCL is specified. If no matching classifier
+ * exists, it will be created if the flag \c NLM_F_CREATE is set,
+ * otherwise the error -NLE_OBJ_NOTFOUND is returned.
+ *
+ * If the parent qdisc does not support classes, the error
+ * \c NLE_OPNOTSUPP is returned.
+ *
+ * After sending, the function will wait for the ACK or an eventual
+ * error message to be received and will therefore block until the
+ * operation has been completed.
+ *
+ * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
+ * this function to return immediately after sending. In this case,
+ * it is the responsibility of the caller to handle any error
+ * messages returned.
+ *
+ * @return 0 on success or a negative error code.
*/
int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
{
@@ -164,13 +188,8 @@ int rtnl_cls_add(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
if ((err = rtnl_cls_build_add_request(cls, flags, &msg)) < 0)
return err;
-
- err = nl_send_auto_complete(sk, msg);
- nlmsg_free(msg);
- if (err < 0)
- return err;
- return nl_wait_for_ack(sk);
+ return nl_send_sync(sk, msg);
}
/**
@@ -212,45 +231,66 @@ int rtnl_cls_change(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
if ((err = rtnl_cls_build_change_request(cls, flags, &msg)) < 0)
return err;
- err = nl_send_auto_complete(sk, msg);
- nlmsg_free(msg);
- if (err < 0)
- return err;
-
- return nl_wait_for_ack(sk);
+ return nl_send_sync(sk, msg);
}
/**
- * Build a netlink request message to delete a classifier
- * @arg cls classifier to delete
- * @arg flags additional netlink message flags
- * @arg result Pointer to store resulting message.
+ * Build netlink message requesting the deletion of a classifier
+ * @arg cls Classifier to delete
+ * @arg flags Additional netlink message flags
+ * @arg result Pointer to store resulting netlink message
*
- * 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.
+ * The behaviour of this function is identical to rtnl_cls_delete() with
+ * the exception that it will not send the message but return it in the
+ * provided return pointer instead.
+ *
+ * @see rtnl_cls_delete()
*
* @return 0 on success or a negative error code.
*/
int rtnl_cls_build_delete_request(struct rtnl_cls *cls, int flags,
struct nl_msg **result)
{
+ uint32_t required = CLS_ATTR_PRIO;
+
+ if ((cls->ce_mask & required) != required) {
+ APPBUG("prio must be specified");
+ return -NLE_MISSING_ATTR;
+ }
+
return cls_build(cls, RTM_DELTFILTER, flags, result);
}
-
/**
- * Delete a classifier
- * @arg sk Netlink socket.
- * @arg cls classifier to delete
- * @arg flags additional netlink message flags
+ * Delete classifier
+ * @arg sk Netlink socket
+ * @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.
+ * Builds a \c RTM_DELTFILTER netlink message requesting the deletion
+ * of a classifier and sends the message to the kernel.
*
- * @return 0 on sucess or a negative error if an error occured.
+ * The message is constructed out of the following attributes:
+ * - \c ifindex (required)
+ * - \c prio (required)
+ * - \c protocol (required)
+ * - \c handle (required)
+ * - \c parent (optional, if not specified parent equals root-qdisc)
+ * - \c kind (optional, must match if provided)
+ *
+ * All other classifier attributes including all class type specific
+ * attributes are ignored.
+ *
+ * After sending, the function will wait for the ACK or an eventual
+ * error message to be received and will therefore block until the
+ * operation has been completed.
+ *
+ * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
+ * this function to return immediately after sending. In this case,
+ * it is the responsibility of the caller to handle any error
+ * messages returned.
+ *
+ * @return 0 on success or a negative error code.
*/
int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
{
@@ -260,35 +300,28 @@ int rtnl_cls_delete(struct nl_sock *sk, struct rtnl_cls *cls, int flags)
if ((err = rtnl_cls_build_delete_request(cls, flags, &msg)) < 0)
return err;
- err = nl_send_auto_complete(sk, msg);
- nlmsg_free(msg);
- if (err < 0)
- return err;
-
- return nl_wait_for_ack(sk);
+ return nl_send_sync(sk, msg);
}
/** @} */
/**
- * @name Cache Management
+ * @name Cache Related Functions
* @{
*/
/**
- * Build a classifier cache including all classifiers attached to the
- * specified class/qdisc on eht specified interface.
- * @arg sk Netlink socket.
- * @arg ifindex interface index of the link the classes are
- * attached to.
- * @arg parent parent qdisc/class
- * @arg result Pointer to store resulting cache.
+ * Allocate a cache and fill it with all configured classifiers
+ * @arg sk Netlink socket
+ * @arg ifindex Interface index of the network device
+ * @arg parent Parent qdisc/traffic class class
+ * @arg result Pointer to store the created cache
*
- * Allocates a new cache, initializes it properly and updates it to
- * include all classes attached to the specified interface.
+ * Allocates a new classifier cache and fills it with a list of all
+ * configured classifier attached to the specified parent qdisc/traffic
+ * class on the specified network device. Release the cache with
+ * nl_cache_free().
*
- * @note The caller is responsible for destroying and freeing the
- * cache after using it.
* @return 0 on success or a negative error code.
*/
int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent, struct nl_cache **result)
@@ -313,6 +346,61 @@ int rtnl_cls_alloc_cache(struct nl_sock *sk, int ifindex, uint32_t parent, st
/** @} */
+static void cls_dump_line(struct rtnl_tc *tc, struct nl_dump_params *p)
+{
+ struct rtnl_cls *cls = (struct rtnl_cls *) tc;
+ char buf[32];
+
+ nl_dump(p, " prio %u protocol %s", cls->c_prio,
+ nl_ether_proto2str(cls->c_protocol, buf, sizeof(buf)));
+}
+
+static int cls_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+ struct nlmsghdr *nlh, struct nl_parser_param *pp)
+{
+ struct rtnl_cls *cls;
+ int err;
+
+ if (!(cls = rtnl_cls_alloc()))
+ return -NLE_NOMEM;
+
+ if ((err = rtnl_tc_msg_parse(nlh, TC_CAST(cls))) < 0)
+ goto errout;
+
+ cls->c_prio = TC_H_MAJ(cls->c_info) >> 16;
+ if (cls->c_prio)
+ cls->ce_mask |= CLS_ATTR_PRIO;
+ cls->c_protocol = ntohs(TC_H_MIN(cls->c_info));
+ if (cls->c_protocol)
+ cls->ce_mask |= CLS_ATTR_PROTOCOL;
+
+ err = pp->pp_cb(OBJ_CAST(cls), pp);
+errout:
+ rtnl_cls_put(cls);
+
+ return err;
+}
+
+static int cls_request_update(struct nl_cache *cache, struct nl_sock *sk)
+{
+ struct tcmsg tchdr = {
+ .tcm_family = AF_UNSPEC,
+ .tcm_ifindex = cache->c_iarg1,
+ .tcm_parent = cache->c_iarg2,
+ };
+
+ return nl_send_simple(sk, RTM_GETTFILTER, NLM_F_DUMP, &tchdr,
+ sizeof(tchdr));
+}
+
+static struct rtnl_tc_type_ops cls_ops = {
+ .tt_type = RTNL_TC_TYPE_CLS,
+ .tt_dump_prefix = "cls",
+ .tt_dump = {
+ [NL_DUMP_LINE] = cls_dump_line,
+ },
+};
+
static struct nl_cache_ops rtnl_cls_ops = {
.co_name = "route/cls",
.co_hdrsize = sizeof(struct tcmsg),
@@ -323,19 +411,36 @@ static struct nl_cache_ops rtnl_cls_ops = {
END_OF_MSGTYPES_LIST,
},
.co_protocol = NETLINK_ROUTE,
+ .co_groups = tc_groups,
.co_request_update = cls_request_update,
.co_msg_parser = cls_msg_parser,
.co_obj_ops = &cls_obj_ops,
};
+static struct nl_object_ops cls_obj_ops = {
+ .oo_name = "route/cls",
+ .oo_size = sizeof(struct rtnl_cls),
+ .oo_free_data = rtnl_tc_free_data,
+ .oo_clone = rtnl_tc_clone,
+ .oo_dump = {
+ [NL_DUMP_LINE] = rtnl_tc_dump_line,
+ [NL_DUMP_DETAILS] = rtnl_tc_dump_details,
+ [NL_DUMP_STATS] = rtnl_tc_dump_stats,
+ },
+ .oo_compare = rtnl_tc_compare,
+ .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
+};
+
static void __init cls_init(void)
{
+ rtnl_tc_type_register(&cls_ops);
nl_cache_mngt_register(&rtnl_cls_ops);
}
static void __exit cls_exit(void)
{
nl_cache_mngt_unregister(&rtnl_cls_ops);
+ rtnl_tc_type_unregister(&cls_ops);
}
/** @} */
diff --git a/lib/route/cls/.gitignore b/lib/route/cls/.gitignore
new file mode 100644
index 00000000..30f4521a
--- /dev/null
+++ b/lib/route/cls/.gitignore
@@ -0,0 +1,2 @@
+ematch_syntax.[ch]
+ematch_grammar.[ch]
diff --git a/lib/route/cls/basic.c b/lib/route/cls/basic.c
index 1460b72c..6af3844b 100644
--- a/lib/route/cls/basic.c
+++ b/lib/route/cls/basic.c
@@ -6,12 +6,12 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2013 Thomas Graf <tgraf@suug.ch>
*/
/**
* @ingroup cls
- * @defgroup basic Basic Classifier
+ * @defgroup cls_basic Basic Classifier
*
* @par Introduction
* The basic classifier is the simplest form of a classifier. It does
@@ -22,115 +22,140 @@
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/classifier.h>
-#include <netlink/route/classifier-modules.h>
+#include <netlink/route/action.h>
#include <netlink/route/cls/basic.h>
#include <netlink/route/cls/ematch.h>
struct rtnl_basic
{
- uint32_t b_classid;
+ uint32_t b_target;
struct rtnl_ematch_tree * b_ematch;
int b_mask;
+ struct rtnl_act * b_act;
};
/** @cond SKIP */
-#define BASIC_ATTR_CLASSID 0x001
+#define BASIC_ATTR_TARGET 0x001
#define BASIC_ATTR_EMATCH 0x002
+#define BASIC_ATTR_ACTION 0x004
/** @endcond */
-static struct nla_policy basic_policy[TCA_FW_MAX+1] = {
+static struct nla_policy basic_policy[TCA_BASIC_MAX+1] = {
[TCA_BASIC_CLASSID] = { .type = NLA_U32 },
[TCA_BASIC_EMATCHES] = { .type = NLA_NESTED },
- [TCA_BASIC_ACT] = { .type = NLA_NESTED },
- [TCA_BASIC_POLICE] = { .type = NLA_NESTED },
};
-static int basic_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
+static int basic_clone(void *_dst, void *_src)
{
return -NLE_OPNOTSUPP;
}
-static void basic_free_data(struct rtnl_cls *cls)
+static void basic_free_data(struct rtnl_tc *tc, void *data)
{
- struct rtnl_basic *basic = rtnl_cls_data(cls);
+ struct rtnl_basic *b = data;
- rtnl_ematch_tree_free(basic->b_ematch);
+ if (!b)
+ return;
+
+ if (b->b_act)
+ rtnl_act_put_all(&b->b_act);
+ rtnl_ematch_tree_free(b->b_ematch);
}
-static int basic_msg_parser(struct rtnl_cls *cls)
+static int basic_msg_parser(struct rtnl_tc *tc, void *data)
{
struct nlattr *tb[TCA_BASIC_MAX + 1];
- struct rtnl_basic *basic = rtnl_cls_data(cls);
+ struct rtnl_basic *b = data;
int err;
- err = tca_parse(tb, TCA_BASIC_MAX, (struct rtnl_tca *) cls, basic_policy);
+ err = tca_parse(tb, TCA_BASIC_MAX, tc, basic_policy);
if (err < 0)
return err;
if (tb[TCA_BASIC_CLASSID]) {
- basic->b_classid = nla_get_u32(tb[TCA_BASIC_CLASSID]);
- basic->b_mask |= BASIC_ATTR_CLASSID;
+ b->b_target = nla_get_u32(tb[TCA_BASIC_CLASSID]);
+ b->b_mask |= BASIC_ATTR_TARGET;
}
if (tb[TCA_BASIC_EMATCHES]) {
- if ((err = rtnl_ematch_parse(tb[TCA_BASIC_EMATCHES],
- &basic->b_ematch)) < 0)
+ if ((err = rtnl_ematch_parse_attr(tb[TCA_BASIC_EMATCHES],
+ &b->b_ematch)) < 0)
return err;
- if (basic->b_ematch)
- basic->b_mask |= BASIC_ATTR_EMATCH;
+ if (b->b_ematch)
+ b->b_mask |= BASIC_ATTR_EMATCH;
}
-
if (tb[TCA_BASIC_ACT]) {
- /* XXX */
- }
-
- if (tb[TCA_BASIC_POLICE]) {
- /* XXX */
+ b->b_mask |= BASIC_ATTR_ACTION;
+ err = rtnl_act_parse(&b->b_act, tb[TCA_BASIC_ACT]);
+ if (err)
+ return err;
}
return 0;
}
-static void basic_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
+static void basic_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_basic *b = rtnl_cls_data(cls);
+ struct rtnl_basic *b = data;
char buf[32];
+ if (!b)
+ return;
+
if (b->b_mask & BASIC_ATTR_EMATCH)
nl_dump(p, " ematch");
else
nl_dump(p, " match-all");
- if (b->b_mask & BASIC_ATTR_CLASSID)
- nl_dump(p, " classify-to %s",
- rtnl_tc_handle2str(b->b_classid, buf, sizeof(buf)));
+ if (b->b_mask & BASIC_ATTR_TARGET)
+ nl_dump(p, " target %s",
+ rtnl_tc_handle2str(b->b_target, buf, sizeof(buf)));
}
-static void basic_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
+static void basic_dump_details(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_basic *b = rtnl_cls_data(cls);
+ struct rtnl_basic *b = data;
+
+ if (!b)
+ return;
if (b->b_mask & BASIC_ATTR_EMATCH) {
- nl_dump(p, "\n");
nl_dump_line(p, " ematch ");
rtnl_ematch_tree_dump(b->b_ematch, p);
} else
nl_dump(p, "no options.\n");
}
-static int basic_get_opts(struct rtnl_cls *cls, struct nl_msg *msg)
+static int basic_msg_fill(struct rtnl_tc *tc, void *data,
+ struct nl_msg *msg)
{
- struct rtnl_basic *b = rtnl_cls_data(cls);
+ struct rtnl_basic *b = data;
+
+ if (!b)
+ return 0;
- if (!(b->b_mask & BASIC_ATTR_CLASSID))
- return -NLE_MISSING_ATTR;
+ if (b->b_mask & BASIC_ATTR_TARGET)
+ NLA_PUT_U32(msg, TCA_BASIC_CLASSID, b->b_target);
- NLA_PUT_U32(msg, TCA_BASIC_CLASSID, b->b_classid);
+ if (b->b_mask & BASIC_ATTR_EMATCH &&
+ rtnl_ematch_fill_attr(msg, TCA_BASIC_EMATCHES, b->b_ematch) < 0)
+ goto nla_put_failure;
+
+ if (b->b_mask & BASIC_ATTR_ACTION) {
+ int err;
+
+ err = rtnl_act_fill(msg, TCA_BASIC_ACT, b->b_act);
+ if (err)
+ return err;
+ }
return 0;
@@ -143,26 +168,33 @@ nla_put_failure:
* @{
*/
-int rtnl_basic_set_classid(struct rtnl_cls *cls, uint32_t classid)
+void rtnl_basic_set_target(struct rtnl_cls *cls, uint32_t target)
{
- struct rtnl_basic *b = rtnl_cls_data(cls);
+ struct rtnl_basic *b;
- b->b_classid = classid;
- b->b_mask |= BASIC_ATTR_CLASSID;
+ if (!(b = rtnl_tc_data(TC_CAST(cls))))
+ return;
- return 0;
+ b->b_target = target;
+ b->b_mask |= BASIC_ATTR_TARGET;
}
-uint32_t rtnl_basic_get_classid(struct rtnl_cls *cls)
+uint32_t rtnl_basic_get_target(struct rtnl_cls *cls)
{
- struct rtnl_basic *b = rtnl_cls_data(cls);
+ struct rtnl_basic *b;
+
+ if (!(b = rtnl_tc_data(TC_CAST(cls))))
+ return 0;
- return b->b_classid;
+ return b->b_target;
}
-int rtnl_basic_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree)
+void rtnl_basic_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree)
{
- struct rtnl_basic *b = rtnl_cls_data(cls);
+ struct rtnl_basic *b;
+
+ if (!(b = rtnl_tc_data(TC_CAST(cls))))
+ return;
if (b->b_ematch) {
rtnl_ematch_tree_free(b->b_ematch);
@@ -173,26 +205,67 @@ int rtnl_basic_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree)
if (tree)
b->b_mask |= BASIC_ATTR_EMATCH;
-
- return 0;
}
struct rtnl_ematch_tree *rtnl_basic_get_ematch(struct rtnl_cls *cls)
{
- struct rtnl_basic *b = rtnl_cls_data(cls);
+ struct rtnl_basic *b;
+
+ if (!(b = rtnl_tc_data(TC_CAST(cls))))
+ return NULL;
+
return b->b_ematch;
}
+int rtnl_basic_add_action(struct rtnl_cls *cls, struct rtnl_act *act)
+{
+ struct rtnl_basic *b;
+
+ if (!act)
+ return 0;
+
+ if (!(b = rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
+
+ b->b_mask |= BASIC_ATTR_ACTION;
+ /* In case user frees it */
+ rtnl_act_get(act);
+ return rtnl_act_append(&b->b_act, act);
+}
+
+int rtnl_basic_del_action(struct rtnl_cls *cls, struct rtnl_act *act)
+{
+ struct rtnl_basic *b;
+ int ret;
+
+ if (!act)
+ return 0;
+
+ if (!(b = rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
+
+ if (!(b->b_mask & BASIC_ATTR_ACTION))
+ return -NLE_INVAL;
+ ret = rtnl_act_remove(&b->b_act, act);
+ if (ret)
+ return ret;
+
+ if (!b->b_act)
+ b->b_mask &= ~BASIC_ATTR_ACTION;
+ rtnl_act_put(act);
+ return 0;
+}
/** @} */
-static struct rtnl_cls_ops basic_ops = {
- .co_kind = "basic",
- .co_size = sizeof(struct rtnl_basic),
- .co_msg_parser = basic_msg_parser,
- .co_clone = basic_clone,
- .co_free_data = basic_free_data,
- .co_get_opts = basic_get_opts,
- .co_dump = {
+static struct rtnl_tc_ops basic_ops = {
+ .to_kind = "basic",
+ .to_type = RTNL_TC_TYPE_CLS,
+ .to_size = sizeof(struct rtnl_basic),
+ .to_msg_parser = basic_msg_parser,
+ .to_clone = basic_clone,
+ .to_free_data = basic_free_data,
+ .to_msg_fill = basic_msg_fill,
+ .to_dump = {
[NL_DUMP_LINE] = basic_dump_line,
[NL_DUMP_DETAILS] = basic_dump_details,
},
@@ -200,12 +273,12 @@ static struct rtnl_cls_ops basic_ops = {
static void __init basic_init(void)
{
- rtnl_cls_register(&basic_ops);
+ rtnl_tc_register(&basic_ops);
}
static void __exit basic_exit(void)
{
- rtnl_cls_unregister(&basic_ops);
+ rtnl_tc_unregister(&basic_ops);
}
/** @} */
diff --git a/lib/route/cls/cgroup.c b/lib/route/cls/cgroup.c
index e5f38b82..c5b75612 100644
--- a/lib/route/cls/cgroup.c
+++ b/lib/route/cls/cgroup.c
@@ -6,23 +6,23 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2009-2013 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup cls_api
- * @defgroup cgroup Control Groups Classifier
+ * @ingroup cls
+ * @defgroup cls_cgroup Control Groups Classifier
*
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/attr.h>
#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/classifier.h>
-#include <netlink/route/classifier-modules.h>
#include <netlink/route/cls/cgroup.h>
#include <netlink/route/cls/ematch.h>
@@ -34,29 +34,36 @@ static struct nla_policy cgroup_policy[TCA_CGROUP_MAX+1] = {
[TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED },
};
-static void cgroup_free_data(struct rtnl_cls *cls)
+static int cgroup_clone(void *dst, void *src)
{
- struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+ return -NLE_OPNOTSUPP;
+}
+
+static void cgroup_free_data(struct rtnl_tc *tc, void *data)
+{
+ struct rtnl_cgroup *c = data;
- rtnl_ematch_tree_free(cg->cg_ematch);
+ if (!c)
+ return;
+
+ rtnl_ematch_tree_free(c->cg_ematch);
}
-static int cgroup_msg_parser(struct rtnl_cls *cls)
+static int cgroup_msg_parser(struct rtnl_tc *tc, void *data)
{
- struct rtnl_cgroup *cg = rtnl_cls_data(cls);
struct nlattr *tb[TCA_CGROUP_MAX + 1];
+ struct rtnl_cgroup *c = data;
int err;
- err = tca_parse(tb, TCA_CGROUP_MAX, (struct rtnl_tca *) cls,
- cgroup_policy);
+ err = tca_parse(tb, TCA_CGROUP_MAX, tc, cgroup_policy);
if (err < 0)
return err;
if (tb[TCA_CGROUP_EMATCHES]) {
- if ((err = rtnl_ematch_parse(tb[TCA_CGROUP_EMATCHES],
- &cg->cg_ematch)) < 0)
+ if ((err = rtnl_ematch_parse_attr(tb[TCA_CGROUP_EMATCHES],
+ &c->cg_ematch)) < 0)
return err;
- cg->cg_mask |= CGROUP_ATTR_EMATCH;
+ c->cg_mask |= CGROUP_ATTR_EMATCH;
}
#if 0
@@ -68,61 +75,102 @@ static int cgroup_msg_parser(struct rtnl_cls *cls)
return 0;
}
-static void cgroup_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
+static void cgroup_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+ struct rtnl_cgroup *c = data;
+
+ if (!c)
+ return;
- if (cg->cg_mask & CGROUP_ATTR_EMATCH)
+ if (c->cg_mask & CGROUP_ATTR_EMATCH)
nl_dump(p, " ematch");
else
nl_dump(p, " match-all");
}
-static void cgroup_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
+static void cgroup_dump_details(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+ struct rtnl_cgroup *c = data;
+
+ if (!c)
+ return;
- if (cg->cg_mask & CGROUP_ATTR_EMATCH) {
- nl_dump(p, "\n");
+ if (c->cg_mask & CGROUP_ATTR_EMATCH) {
nl_dump_line(p, " ematch ");
- rtnl_ematch_tree_dump(cg->cg_ematch, p);
- }
+
+ if (c->cg_ematch)
+ rtnl_ematch_tree_dump(c->cg_ematch, p);
+ else
+ nl_dump(p, "<no tree>");
+ } else
+ nl_dump(p, "no options");
+}
+
+static int cgroup_fill_msg(struct rtnl_tc *tc, void *data,
+ struct nl_msg *msg)
+{
+ struct rtnl_cgroup *c = data;
+
+ if (!c)
+ BUG();
+
+ if (!(tc->ce_mask & TCA_ATTR_HANDLE))
+ return -NLE_MISSING_ATTR;
+
+ if (c->cg_mask & CGROUP_ATTR_EMATCH)
+ return rtnl_ematch_fill_attr(msg, TCA_CGROUP_EMATCHES,
+ c->cg_ematch);
+
+ return 0;
}
+
/**
* @name Attribute Modifications
* @{
*/
-int rtnl_cgroup_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree)
+void rtnl_cgroup_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree)
{
- struct rtnl_cgroup *cg = rtnl_cls_data(cls);
+ struct rtnl_cgroup *c;
+
+ if (!(c = rtnl_tc_data(TC_CAST(cls))))
+ BUG();
- if (cg->cg_ematch) {
- rtnl_ematch_tree_free(cg->cg_ematch);
- cg->cg_mask &= ~CGROUP_ATTR_EMATCH;
+ if (c->cg_ematch) {
+ rtnl_ematch_tree_free(c->cg_ematch);
+ c->cg_mask &= ~CGROUP_ATTR_EMATCH;
}
- cg->cg_ematch = tree;
+ c->cg_ematch = tree;
if (tree)
- cg->cg_mask |= CGROUP_ATTR_EMATCH;
-
- return 0;
+ c->cg_mask |= CGROUP_ATTR_EMATCH;
}
struct rtnl_ematch_tree *rtnl_cgroup_get_ematch(struct rtnl_cls *cls)
{
- struct rtnl_cgroup *cg = rtnl_cls_data(cls);
- return cg->cg_ematch;
+ struct rtnl_cgroup *c;
+
+ if (!(c = rtnl_tc_data(TC_CAST(cls))))
+ BUG();
+
+ return c->cg_ematch;
}
-static struct rtnl_cls_ops cgroup_ops = {
- .co_kind = "cgroup",
- .co_size = sizeof(struct rtnl_cgroup),
- .co_msg_parser = cgroup_msg_parser,
- .co_free_data = cgroup_free_data,
- .co_dump = {
+/** @} */
+
+static struct rtnl_tc_ops cgroup_ops = {
+ .to_kind = "cgroup",
+ .to_type = RTNL_TC_TYPE_CLS,
+ .to_size = sizeof(struct rtnl_cgroup),
+ .to_clone = cgroup_clone,
+ .to_msg_parser = cgroup_msg_parser,
+ .to_free_data = cgroup_free_data,
+ .to_msg_fill = cgroup_fill_msg,
+ .to_dump = {
[NL_DUMP_LINE] = cgroup_dump_line,
[NL_DUMP_DETAILS] = cgroup_dump_details,
},
@@ -130,12 +178,12 @@ static struct rtnl_cls_ops cgroup_ops = {
static void __init cgroup_init(void)
{
- rtnl_cls_register(&cgroup_ops);
+ rtnl_tc_register(&cgroup_ops);
}
static void __exit cgroup_exit(void)
{
- rtnl_cls_unregister(&cgroup_ops);
+ rtnl_tc_unregister(&cgroup_ops);
}
/** @} */
diff --git a/lib/route/cls/ematch.c b/lib/route/cls/ematch.c
index cb77b16d..6cbe2744 100644
--- a/lib/route/cls/ematch.c
+++ b/lib/route/cls/ematch.c
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2013 Thomas Graf <tgraf@suug.ch>
*/
/**
@@ -16,15 +16,18 @@
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/route/classifier.h>
-#include <netlink/route/classifier-modules.h>
#include <netlink/route/cls/ematch.h>
+#include <netlink/route/cls/ematch/cmp.h>
+
+#include "ematch_syntax.h"
+#include "ematch_grammar.h"
/**
- * @name Module Registration
+ * @name Module API
* @{
*/
@@ -34,6 +37,9 @@ static NL_LIST_HEAD(ematch_ops_list);
* Register ematch module
* @arg ops Module operations.
*
+ * This function must be called by each ematch module at initialization
+ * time. It registers the calling module as available module.
+ *
* @return 0 on success or a negative error code.
*/
int rtnl_ematch_register(struct rtnl_ematch_ops *ops)
@@ -41,35 +47,19 @@ int rtnl_ematch_register(struct rtnl_ematch_ops *ops)
if (rtnl_ematch_lookup_ops(ops->eo_kind))
return -NLE_EXIST;
+ NL_DBG(1, "ematch module \"%s\" registered\n", ops->eo_name);
+
nl_list_add_tail(&ops->eo_list, &ematch_ops_list);
return 0;
}
/**
- * Unregister ematch module
- * @arg ops Module operations.
- *
- * @return 0 on success or a negative error code.
- */
-int rtnl_ematch_unregister(struct rtnl_ematch_ops *ops)
-{
- struct rtnl_ematch_ops *o;
-
- nl_list_for_each_entry(o, &ematch_ops_list, eo_list) {
- if (ops->eo_kind == o->eo_kind) {
- nl_list_del(&o->eo_list);
- return 0;
- }
- }
-
- return -NLE_OBJ_NOTFOUND;
-}
-
-/**
- * Lookup ematch module by kind
+ * Lookup ematch module by identification number.
* @arg kind Module kind.
*
+ * Searches the list of registered ematch modules for match and returns it.
+ *
* @return Module operations or NULL if not found.
*/
struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind)
@@ -87,9 +77,11 @@ struct rtnl_ematch_ops *rtnl_ematch_lookup_ops(int kind)
* Lookup ematch module by name
* @arg name Name of ematch module.
*
+ * Searches the list of registered ematch modules for a match and returns it.
+ *
* @return Module operations or NULL if not fuond.
*/
-struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_name(const char *name)
+struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_by_name(const char *name)
{
struct rtnl_ematch_ops *ops;
@@ -106,53 +98,122 @@ struct rtnl_ematch_ops *rtnl_ematch_lookup_ops_name(const char *name)
* @name Match
*/
-struct rtnl_ematch *rtnl_ematch_alloc(struct rtnl_ematch_ops *ops)
+/**
+ * Allocate ematch object.
+ *
+ * Allocates and initializes an ematch object.
+ *
+ * @return New ematch object or NULL.
+ */
+struct rtnl_ematch *rtnl_ematch_alloc(void)
{
struct rtnl_ematch *e;
- size_t len = sizeof(*e) + (ops ? ops->eo_datalen : 0);
- if (!(e = calloc(1, len)))
+ if (!(e = calloc(1, sizeof(*e))))
return NULL;
+ NL_DBG(2, "allocated ematch %p\n", e);
+
NL_INIT_LIST_HEAD(&e->e_list);
NL_INIT_LIST_HEAD(&e->e_childs);
- if (ops) {
- e->e_ops = ops;
- e->e_kind = ops->eo_kind;
- }
-
return e;
}
/**
* Add ematch to the end of the parent's list of children.
- * @arg parent Parent ematch.
- * @arg child Ematch to be added as new child of parent.
+ * @arg parent parent ematch object
+ * @arg child ematch object to be added to parent
+ *
+ * The parent must be a container ematch.
*/
-void rtnl_ematch_add_child(struct rtnl_ematch *parent,
+int rtnl_ematch_add_child(struct rtnl_ematch *parent,
struct rtnl_ematch *child)
{
+ if (parent->e_kind != TCF_EM_CONTAINER)
+ return -NLE_OPNOTSUPP;
+
+ NL_DBG(2, "added ematch %p \"%s\" to container %p\n",
+ child, child->e_ops->eo_name, parent);
+
nl_list_add_tail(&child->e_list, &parent->e_childs);
+
+ return 0;
}
/**
- * Remove ematch from the list it is linked to.
- * @arg ematch Ematch to be unlinked.
+ * Remove ematch from the list of ematches it is linked to.
+ * @arg ematch ematch object
*/
void rtnl_ematch_unlink(struct rtnl_ematch *ematch)
{
+ NL_DBG(2, "unlinked ematch %p from any lists\n", ematch);
+
+ if (!nl_list_empty(&ematch->e_childs))
+ NL_DBG(1, "warning: ematch %p with childs was unlinked\n",
+ ematch);
+
nl_list_del(&ematch->e_list);
+ nl_init_list_head(&ematch->e_list);
}
void rtnl_ematch_free(struct rtnl_ematch *ematch)
{
- if (!ematch)
- return;
-
+ NL_DBG(2, "freed ematch %p\n", ematch);
+ rtnl_ematch_unlink(ematch);
+ free(ematch->e_data);
free(ematch);
}
+int rtnl_ematch_set_ops(struct rtnl_ematch *ematch, struct rtnl_ematch_ops *ops)
+{
+ if (ematch->e_ops)
+ return -NLE_EXIST;
+
+ ematch->e_ops = ops;
+ ematch->e_kind = ops->eo_kind;
+
+ if (ops->eo_datalen) {
+ ematch->e_data = calloc(1, ops->eo_datalen);
+ if (!ematch->e_data)
+ return -NLE_NOMEM;
+
+ ematch->e_datalen = ops->eo_datalen;
+ }
+
+ return 0;
+}
+
+int rtnl_ematch_set_kind(struct rtnl_ematch *ematch, uint16_t kind)
+{
+ struct rtnl_ematch_ops *ops;
+
+ if (ematch->e_kind)
+ return -NLE_EXIST;
+
+ ematch->e_kind = kind;
+
+ if ((ops = rtnl_ematch_lookup_ops(kind)))
+ rtnl_ematch_set_ops(ematch, ops);
+
+ return 0;
+}
+
+int rtnl_ematch_set_name(struct rtnl_ematch *ematch, const char *name)
+{
+ struct rtnl_ematch_ops *ops;
+
+ if (ematch->e_kind)
+ return -NLE_EXIST;
+
+ if (!(ops = rtnl_ematch_lookup_ops_by_name(name)))
+ return -NLE_OPNOTSUPP;
+
+ rtnl_ematch_set_ops(ematch, ops);
+
+ return 0;
+}
+
void rtnl_ematch_set_flags(struct rtnl_ematch *ematch, uint16_t flags)
{
ematch->e_flags |= flags;
@@ -179,16 +240,22 @@ void *rtnl_ematch_data(struct rtnl_ematch *ematch)
* @name Tree
*/
+/**
+ * Allocate ematch tree object
+ * @arg progid program id
+ */
struct rtnl_ematch_tree *rtnl_ematch_tree_alloc(uint16_t progid)
{
struct rtnl_ematch_tree *tree;
if (!(tree = calloc(1, sizeof(*tree))))
return NULL;
-
+
NL_INIT_LIST_HEAD(&tree->et_list);
tree->et_progid = progid;
+ NL_DBG(2, "allocated new ematch tree %p, progid=%u\n", tree, progid);
+
return tree;
}
@@ -203,17 +270,31 @@ static void free_ematch_list(struct nl_list_head *head)
}
}
+/**
+ * Free ematch tree object
+ * @arg tree ematch tree object
+ *
+ * This function frees the ematch tree and all ematches attached to it.
+ */
void rtnl_ematch_tree_free(struct rtnl_ematch_tree *tree)
{
if (!tree)
return;
free_ematch_list(&tree->et_list);
+
+ NL_DBG(2, "Freed ematch tree %p\n", tree);
+
free(tree);
}
-void rtnl_ematch_tree_add_tail(struct rtnl_ematch_tree *tree,
- struct rtnl_ematch *ematch)
+/**
+ * Add ematch object to the end of the ematch tree
+ * @arg tree ematch tree object
+ * @arg ematch ematch object to add
+ */
+void rtnl_ematch_tree_add(struct rtnl_ematch_tree *tree,
+ struct rtnl_ematch *ematch)
{
nl_list_add_tail(&ematch->e_list, &tree->et_list);
}
@@ -256,7 +337,7 @@ static struct nla_policy tree_policy[TCA_EMATCH_TREE_MAX+1] = {
*
* @return 0 on success or a negative error code.
*/
-int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result)
+int rtnl_ematch_parse_attr(struct nlattr *attr, struct rtnl_ematch_tree **result)
{
struct nlattr *a, *tb[TCA_EMATCH_TREE_MAX+1];
struct tcf_ematch_tree_hdr *thdr;
@@ -264,6 +345,8 @@ int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result)
struct rtnl_ematch **index;
int nmatches = 0, err, remaining;
+ NL_DBG(2, "Parsing attribute %p as ematch tree\n", attr);
+
err = nla_parse_nested(tb, TCA_EMATCH_TREE_MAX, attr, tree_policy);
if (err < 0)
return err;
@@ -274,12 +357,22 @@ int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result)
thdr = nla_data(tb[TCA_EMATCH_TREE_HDR]);
/* Ignore empty trees */
- if (thdr->nmatches == 0)
+ if (thdr->nmatches == 0) {
+ NL_DBG(2, "Ignoring empty ematch configuration\n");
return 0;
+ }
if (!tb[TCA_EMATCH_TREE_LIST])
return -NLE_MISSING_ATTR;
+ NL_DBG(2, "ematch tree found with nmatches=%u, progid=%u\n",
+ thdr->nmatches, thdr->progid);
+
+ /*
+ * Do some basic sanity checking since we will allocate
+ * index[thdr->nmatches]. Calculate how many ematch headers fit into
+ * the provided data and make sure nmatches does not exceed it.
+ */
if (thdr->nmatches > (nla_len(tb[TCA_EMATCH_TREE_LIST]) /
nla_total_size(sizeof(struct tcf_ematch_hdr))))
return -NLE_INVAL;
@@ -299,11 +392,15 @@ int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result)
void *data;
size_t len;
+ NL_DBG(3, "parsing ematch attribute %d, len=%u\n",
+ nmatches+1, nla_len(a));
+
if (nla_len(a) < sizeof(*hdr)) {
err = -NLE_INVAL;
goto errout;
}
+ /* Quit as soon as we've parsed more matches than expected */
if (nmatches >= thdr->nmatches) {
err = -NLE_RANGE;
goto errout;
@@ -313,13 +410,20 @@ int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result)
data = nla_data(a) + NLA_ALIGN(sizeof(*hdr));
len = nla_len(a) - NLA_ALIGN(sizeof(*hdr));
- ops = rtnl_ematch_lookup_ops(hdr->kind);
- if (ops && ops->eo_datalen && len < ops->eo_datalen) {
+ NL_DBG(3, "ematch attribute matchid=%u, kind=%u, flags=%u\n",
+ hdr->matchid, hdr->kind, hdr->flags);
+
+ /*
+ * Container matches contain a reference to another sequence
+ * of matches. Ensure that the reference is within boundries.
+ */
+ if (hdr->kind == TCF_EM_CONTAINER &&
+ *((uint32_t *) data) >= thdr->nmatches) {
err = -NLE_INVAL;
goto errout;
}
- if (!(ematch = rtnl_ematch_alloc(ops))) {
+ if (!(ematch = rtnl_ematch_alloc())) {
err = -NLE_NOMEM;
goto errout;
}
@@ -328,15 +432,23 @@ int rtnl_ematch_parse(struct nlattr *attr, struct rtnl_ematch_tree **result)
ematch->e_kind = hdr->kind;
ematch->e_flags = hdr->flags;
- if (ops && (err = ops->eo_parse(ematch, data, len)) < 0)
- goto errout;
+ if ((ops = rtnl_ematch_lookup_ops(hdr->kind))) {
+ if (ops->eo_minlen && len < ops->eo_minlen) {
+ rtnl_ematch_free(ematch);
+ err = -NLE_INVAL;
+ goto errout;
+ }
- if (hdr->kind == TCF_EM_CONTAINER &&
- container_ref(ematch) >= thdr->nmatches) {
- err = -NLE_INVAL;
- goto errout;
+ rtnl_ematch_set_ops(ematch, ops);
+
+ if (ops->eo_parse &&
+ (err = ops->eo_parse(ematch, data, len)) < 0) {
+ rtnl_ematch_free(ematch);
+ goto errout;
+ }
}
+ NL_DBG(3, "index[%d] = %p\n", nmatches, ematch);
index[nmatches++] = ematch;
}
@@ -367,7 +479,7 @@ static void dump_ematch_sequence(struct nl_list_head *head,
nl_list_for_each_entry(match, head, e_list) {
if (match->e_flags & TCF_EM_INVERT)
- nl_dump(p, "NOT ");
+ nl_dump(p, "!");
if (match->e_kind == TCF_EM_CONTAINER) {
nl_dump(p, "(");
@@ -376,12 +488,10 @@ static void dump_ematch_sequence(struct nl_list_head *head,
} else if (!match->e_ops) {
nl_dump(p, "[unknown ematch %d]", match->e_kind);
} else {
- nl_dump(p, "%s(", match->e_ops->eo_name);
-
if (match->e_ops->eo_dump)
match->e_ops->eo_dump(match, p);
-
- nl_dump(p, ")");
+ else
+ nl_dump(p, "[data]");
}
switch (match->e_flags & TCF_EM_REL_MASK) {
@@ -401,10 +511,190 @@ static void dump_ematch_sequence(struct nl_list_head *head,
void rtnl_ematch_tree_dump(struct rtnl_ematch_tree *tree,
struct nl_dump_params *p)
{
+ if (!tree)
+ BUG();
+
dump_ematch_sequence(&tree->et_list, p);
nl_dump(p, "\n");
}
+static int update_container_index(struct nl_list_head *list, int *index)
+{
+ struct rtnl_ematch *e;
+
+ nl_list_for_each_entry(e, list, e_list)
+ e->e_index = (*index)++;
+
+ nl_list_for_each_entry(e, list, e_list) {
+ if (e->e_kind == TCF_EM_CONTAINER) {
+ int err;
+
+ if (nl_list_empty(&e->e_childs))
+ return -NLE_OBJ_NOTFOUND;
+
+ *((uint32_t *) e->e_data) = *index;
+
+ err = update_container_index(&e->e_childs, index);
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int fill_ematch_sequence(struct nl_msg *msg, struct nl_list_head *list)
+{
+ struct rtnl_ematch *e;
+
+ nl_list_for_each_entry(e, list, e_list) {
+ struct tcf_ematch_hdr match = {
+ .matchid = e->e_id,
+ .kind = e->e_kind,
+ .flags = e->e_flags,
+ };
+ struct nlattr *attr;
+ int err = 0;
+
+ if (!(attr = nla_nest_start(msg, e->e_index + 1)))
+ return -NLE_NOMEM;
+
+ if (nlmsg_append(msg, &match, sizeof(match), 0) < 0)
+ return -NLE_NOMEM;
+
+ if (e->e_ops->eo_fill)
+ err = e->e_ops->eo_fill(e, msg);
+ else if (e->e_flags & TCF_EM_SIMPLE)
+ err = nlmsg_append(msg, e->e_data, 4, 0);
+ else if (e->e_datalen > 0)
+ err = nlmsg_append(msg, e->e_data, e->e_datalen, 0);
+
+ NL_DBG(3, "msg %p: added ematch [%d] id=%d kind=%d flags=%d\n",
+ msg, e->e_index, match.matchid, match.kind, match.flags);
+
+ if (err < 0)
+ return -NLE_NOMEM;
+
+ nla_nest_end(msg, attr);
+ }
+
+ nl_list_for_each_entry(e, list, e_list) {
+ if (e->e_kind == TCF_EM_CONTAINER &&
+ fill_ematch_sequence(msg, &e->e_childs) < 0)
+ return -NLE_NOMEM;
+ }
+
+ return 0;
+}
+
+int rtnl_ematch_fill_attr(struct nl_msg *msg, int attrid,
+ struct rtnl_ematch_tree *tree)
+{
+ struct tcf_ematch_tree_hdr thdr = {
+ .progid = tree->et_progid,
+ };
+ struct nlattr *list, *topattr;
+ int err, index = 0;
+
+ /* Assign index number to each ematch to allow for references
+ * to be made while constructing the sequence of matches. */
+ err = update_container_index(&tree->et_list, &index);
+ if (err < 0)
+ return err;
+
+ if (!(topattr = nla_nest_start(msg, attrid)))
+ goto nla_put_failure;
+
+ thdr.nmatches = index;
+ NLA_PUT(msg, TCA_EMATCH_TREE_HDR, sizeof(thdr), &thdr);
+
+ if (!(list = nla_nest_start(msg, TCA_EMATCH_TREE_LIST)))
+ goto nla_put_failure;
+
+ if (fill_ematch_sequence(msg, &tree->et_list) < 0)
+ goto nla_put_failure;
+
+ nla_nest_end(msg, list);
+
+ nla_nest_end(msg, topattr);
+
+ return 0;
+
+nla_put_failure:
+ return -NLE_NOMEM;
+}
+
/** @} */
+extern int ematch_parse(void *, char **, struct nl_list_head *);
+
+int rtnl_ematch_parse_expr(const char *expr, char **errp,
+ struct rtnl_ematch_tree **result)
+{
+ struct rtnl_ematch_tree *tree;
+ YY_BUFFER_STATE buf = NULL;
+ yyscan_t scanner = NULL;
+ int err;
+
+ NL_DBG(2, "Parsing ematch expression \"%s\"\n", expr);
+
+ if (!(tree = rtnl_ematch_tree_alloc(RTNL_EMATCH_PROGID)))
+ return -NLE_FAILURE;
+
+ if ((err = ematch_lex_init(&scanner)) < 0) {
+ err = -NLE_FAILURE;
+ goto errout;
+ }
+
+ buf = ematch__scan_string(expr, scanner);
+
+ if ((err = ematch_parse(scanner, errp, &tree->et_list)) != 0) {
+ ematch__delete_buffer(buf, scanner);
+ err = -NLE_PARSE_ERR;
+ goto errout;
+ }
+
+ ematch_lex_destroy(scanner);
+ *result = tree;
+
+ return 0;
+
+errout:
+ if (scanner)
+ ematch_lex_destroy(scanner);
+
+ rtnl_ematch_tree_free(tree);
+
+ return err;
+}
+
+static const char *layer_txt[] = {
+ [TCF_LAYER_LINK] = "eth",
+ [TCF_LAYER_NETWORK] = "ip",
+ [TCF_LAYER_TRANSPORT] = "tcp",
+};
+
+char *rtnl_ematch_offset2txt(uint8_t layer, uint16_t offset, char *buf, size_t len)
+{
+ snprintf(buf, len, "%s+%u",
+ (layer <= TCF_LAYER_MAX) ? layer_txt[layer] : "?",
+ offset);
+
+ return buf;
+}
+
+static const char *operand_txt[] = {
+ [TCF_EM_OPND_EQ] = "=",
+ [TCF_EM_OPND_LT] = "<",
+ [TCF_EM_OPND_GT] = ">",
+};
+
+char *rtnl_ematch_opnd2txt(uint8_t opnd, char *buf, size_t len)
+{
+ snprintf(buf, len, "%s",
+ opnd < ARRAY_SIZE(operand_txt) ? operand_txt[opnd] : "?");
+
+ return buf;
+}
+
/** @} */
diff --git a/lib/route/cls/ematch/cmp.c b/lib/route/cls/ematch/cmp.c
index ec25320f..2997cdbc 100644
--- a/lib/route/cls/ematch/cmp.c
+++ b/lib/route/cls/ematch/cmp.c
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2013 Thomas Graf <tgraf@suug.ch>
*/
/**
@@ -16,88 +16,70 @@
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/route/cls/ematch.h>
#include <linux/tc_ematch/tc_em_cmp.h>
-void rtnl_ematch_cmp_set(struct rtnl_ematch *ematch,
- struct tcf_em_cmp *cfg)
+void rtnl_ematch_cmp_set(struct rtnl_ematch *e, struct tcf_em_cmp *cfg)
{
- memcpy(rtnl_ematch_data(ematch), cfg, sizeof(*cfg));
+ memcpy(rtnl_ematch_data(e), cfg, sizeof(*cfg));
}
-struct tcf_em_cmp *rtnl_ematch_cmp_get(struct rtnl_ematch *ematch)
+struct tcf_em_cmp *rtnl_ematch_cmp_get(struct rtnl_ematch *e)
{
- return rtnl_ematch_data(ematch);
+ return rtnl_ematch_data(e);
}
-static const char *align_txt(struct tcf_em_cmp *cmp)
+static int cmp_parse(struct rtnl_ematch *e, void *data, size_t len)
{
- switch (cmp->align) {
- case TCF_EM_ALIGN_U8:
- return "u8";
- case TCF_EM_ALIGN_U16:
- return (cmp->flags & TCF_EM_CMP_TRANS) ? "h16" : "u16";
- case TCF_EM_ALIGN_U32:
- return (cmp->flags & TCF_EM_CMP_TRANS) ? "h32" : "u32";
- default:
- return (cmp->flags & TCF_EM_CMP_TRANS) ? "h?" : "u?";
- }
-}
+ memcpy(rtnl_ematch_data(e), data, len);
-static const char *layer_txt(struct tcf_em_cmp *cmp)
-{
- switch (cmp->layer) {
- case TCF_LAYER_LINK:
- return "link";
- case TCF_LAYER_NETWORK:
- return "network";
- case TCF_LAYER_TRANSPORT:
- return "transport";
- default:
- return "?";
- }
+ return 0;
}
-static const char *relation_txt(struct tcf_em_cmp *cmp)
-{
- switch (cmp->opnd) {
- case TCF_EM_OPND_EQ:
- return "eq";
- case TCF_EM_OPND_LT:
- return "lt";
- case TCF_EM_OPND_GT:
- return "gt";
- default:
- return "?";
- }
-}
+static const char *align_txt[] = {
+ [TCF_EM_ALIGN_U8] = "u8",
+ [TCF_EM_ALIGN_U16] = "u16",
+ [TCF_EM_ALIGN_U32] = "u32"
+};
-static int cmp_parse(struct rtnl_ematch *m, void *data, size_t len)
-{
- memcpy(rtnl_ematch_data(m), data, len);
+static const char *layer_txt[] = {
+ [TCF_LAYER_LINK] = "eth",
+ [TCF_LAYER_NETWORK] = "ip",
+ [TCF_LAYER_TRANSPORT] = "tcp"
+};
- return 0;
-}
+static const char *operand_txt[] = {
+ [TCF_EM_OPND_EQ] = "=",
+ [TCF_EM_OPND_LT] = "<",
+ [TCF_EM_OPND_GT] = ">",
+};
-static void cmp_dump(struct rtnl_ematch *m, struct nl_dump_params *p)
+static void cmp_dump(struct rtnl_ematch *e, struct nl_dump_params *p)
{
- struct tcf_em_cmp *cmp = rtnl_ematch_data(m);
+ struct tcf_em_cmp *cmp = rtnl_ematch_data(e);
+
+ if (cmp->flags & TCF_EM_CMP_TRANS)
+ nl_dump(p, "ntoh%c(", (cmp->align == TCF_EM_ALIGN_U32) ? 'l' : 's');
- nl_dump(p, "%s at %s+%u ",
- align_txt(cmp), layer_txt(cmp), cmp->off);
+ nl_dump(p, "%s at %s+%u",
+ align_txt[cmp->align], layer_txt[cmp->layer], cmp->off);
if (cmp->mask)
- nl_dump(p, "& 0x%x ", cmp->mask);
+ nl_dump(p, " & 0x%x", cmp->mask);
- nl_dump(p, "%s %u", relation_txt(cmp), cmp->val);
+ if (cmp->flags & TCF_EM_CMP_TRANS)
+ nl_dump(p, ")");
+
+ nl_dump(p, " %s %u", operand_txt[cmp->opnd], cmp->val);
}
static struct rtnl_ematch_ops cmp_ops = {
.eo_kind = TCF_EM_CMP,
.eo_name = "cmp",
+ .eo_minlen = sizeof(struct tcf_em_cmp),
.eo_datalen = sizeof(struct tcf_em_cmp),
.eo_parse = cmp_parse,
.eo_dump = cmp_dump,
@@ -108,9 +90,4 @@ static void __init cmp_init(void)
rtnl_ematch_register(&cmp_ops);
}
-static void __exit cmp_exit(void)
-{
- rtnl_ematch_unregister(&cmp_ops);
-}
-
/** @} */
diff --git a/lib/route/cls/ematch/container.c b/lib/route/cls/ematch/container.c
index 54d836fe..813391ab 100644
--- a/lib/route/cls/ematch/container.c
+++ b/lib/route/cls/ematch/container.c
@@ -6,34 +6,42 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2013 Thomas Graf <tgraf@suug.ch>
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/route/cls/ematch.h>
-static int container_parse(struct rtnl_ematch *m, void *data, size_t len)
+static int container_parse(struct rtnl_ematch *e, void *data, size_t len __attribute__((unused)))
{
- memcpy(m->e_data, data, sizeof(uint32_t));
+ /*
+ The kernel may provide more than 4 bytes of data in the future and we want
+ older libnl versions to be ok with that. We want interfaces to be growable
+ so we only ever enforce a minimum data length and copy as much as we are
+ aware of. Thomas Graf.
+ */
+ memcpy(e->e_data, data, sizeof(uint32_t));
return 0;
}
+static int container_fill(struct rtnl_ematch *e, struct nl_msg *msg)
+{
+ return nlmsg_append(msg, e->e_data, sizeof(uint32_t), 0);
+}
+
static struct rtnl_ematch_ops container_ops = {
.eo_kind = TCF_EM_CONTAINER,
.eo_name = "container",
+ .eo_minlen = sizeof(uint32_t),
.eo_datalen = sizeof(uint32_t),
.eo_parse = container_parse,
+ .eo_fill = container_fill,
};
static void __init container_init(void)
{
rtnl_ematch_register(&container_ops);
}
-
-static void __exit container_exit(void)
-{
- rtnl_ematch_unregister(&container_ops);
-}
diff --git a/lib/route/cls/ematch/meta.c b/lib/route/cls/ematch/meta.c
new file mode 100644
index 00000000..44f11b92
--- /dev/null
+++ b/lib/route/cls/ematch/meta.c
@@ -0,0 +1,334 @@
+/*
+ * lib/route/cls/ematch/meta.c Metadata Match
+ *
+ * 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) 2010-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup ematch
+ * @defgroup em_meta Metadata Match
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/route/cls/ematch.h>
+#include <netlink/route/cls/ematch/meta.h>
+
+struct rtnl_meta_value
+{
+ uint8_t mv_type;
+ uint8_t mv_shift;
+ uint16_t mv_id;
+ size_t mv_len;
+};
+
+struct meta_data
+{
+ struct rtnl_meta_value * left;
+ struct rtnl_meta_value * right;
+ uint8_t opnd;
+};
+
+static struct rtnl_meta_value *meta_alloc(uint8_t type, uint16_t id,
+ uint8_t shift, void *data,
+ size_t len)
+{
+ struct rtnl_meta_value *value;
+
+ if (!(value = calloc(1, sizeof(*value) + len)))
+ return NULL;
+
+ value->mv_type = type;
+ value->mv_id = id;
+ value->mv_shift = shift;
+ value->mv_len = len;
+
+ memcpy(value + 1, data, len);
+
+ return value;
+}
+
+struct rtnl_meta_value *rtnl_meta_value_alloc_int(uint64_t value)
+{
+ return meta_alloc(TCF_META_TYPE_INT, TCF_META_ID_VALUE, 0, &value, 8);
+}
+
+struct rtnl_meta_value *rtnl_meta_value_alloc_var(void *data, size_t len)
+{
+ return meta_alloc(TCF_META_TYPE_VAR, TCF_META_ID_VALUE, 0, data, len);
+}
+
+struct rtnl_meta_value *rtnl_meta_value_alloc_id(uint8_t type, uint16_t id,
+ uint8_t shift, uint64_t mask)
+{
+ size_t masklen = 0;
+
+ if (id > TCF_META_ID_MAX)
+ return NULL;
+
+ if (mask) {
+ if (type == TCF_META_TYPE_VAR)
+ return NULL;
+
+ masklen = 8;
+ }
+
+ return meta_alloc(type, id, shift, &mask, masklen);
+}
+
+void rtnl_meta_value_put(struct rtnl_meta_value *mv)
+{
+ free(mv);
+}
+
+void rtnl_ematch_meta_set_lvalue(struct rtnl_ematch *e, struct rtnl_meta_value *v)
+{
+ struct meta_data *m = rtnl_ematch_data(e);
+ m->left = v;
+}
+
+void rtnl_ematch_meta_set_rvalue(struct rtnl_ematch *e, struct rtnl_meta_value *v)
+{
+ struct meta_data *m = rtnl_ematch_data(e);
+ m->right = v;
+}
+
+void rtnl_ematch_meta_set_operand(struct rtnl_ematch *e, uint8_t opnd)
+{
+ struct meta_data *m = rtnl_ematch_data(e);
+ m->opnd = opnd;
+}
+
+static struct nla_policy meta_policy[TCA_EM_META_MAX+1] = {
+ [TCA_EM_META_HDR] = { .minlen = sizeof(struct tcf_meta_hdr) },
+ [TCA_EM_META_LVALUE] = { .minlen = 1, },
+ [TCA_EM_META_RVALUE] = { .minlen = 1, },
+};
+
+static int meta_parse(struct rtnl_ematch *e, void *data, size_t len)
+{
+ struct meta_data *m = rtnl_ematch_data(e);
+ struct nlattr *tb[TCA_EM_META_MAX+1];
+ struct rtnl_meta_value *v;
+ struct tcf_meta_hdr *hdr;
+ void *vdata = NULL;
+ size_t vlen = 0;
+ int err;
+
+ if ((err = nla_parse(tb, TCA_EM_META_MAX, data, len, meta_policy)) < 0)
+ return err;
+
+ if (!tb[TCA_EM_META_HDR])
+ return -NLE_MISSING_ATTR;
+
+ hdr = nla_data(tb[TCA_EM_META_HDR]);
+
+ if (tb[TCA_EM_META_LVALUE]) {
+ vdata = nla_data(tb[TCA_EM_META_LVALUE]);
+ vlen = nla_len(tb[TCA_EM_META_LVALUE]);
+ }
+
+ v = meta_alloc(TCF_META_TYPE(hdr->left.kind),
+ TCF_META_ID(hdr->left.kind),
+ hdr->left.shift, vdata, vlen);
+ if (!v)
+ return -NLE_NOMEM;
+
+ m->left = v;
+
+ vlen = 0;
+ if (tb[TCA_EM_META_RVALUE]) {
+ vdata = nla_data(tb[TCA_EM_META_RVALUE]);
+ vlen = nla_len(tb[TCA_EM_META_RVALUE]);
+ }
+
+ v = meta_alloc(TCF_META_TYPE(hdr->right.kind),
+ TCF_META_ID(hdr->right.kind),
+ hdr->right.shift, vdata, vlen);
+ if (!v) {
+ rtnl_meta_value_put(m->left);
+ return -NLE_NOMEM;
+ }
+
+ m->right = v;
+ m->opnd = hdr->left.op;
+
+ return 0;
+}
+
+static const struct trans_tbl meta_int[] = {
+ __ADD(TCF_META_ID_RANDOM, random)
+ __ADD(TCF_META_ID_LOADAVG_0, loadavg_0)
+ __ADD(TCF_META_ID_LOADAVG_1, loadavg_1)
+ __ADD(TCF_META_ID_LOADAVG_2, loadavg_2)
+ __ADD(TCF_META_ID_DEV, dev)
+ __ADD(TCF_META_ID_PRIORITY, prio)
+ __ADD(TCF_META_ID_PROTOCOL, proto)
+ __ADD(TCF_META_ID_PKTTYPE, pkttype)
+ __ADD(TCF_META_ID_PKTLEN, pktlen)
+ __ADD(TCF_META_ID_DATALEN, datalen)
+ __ADD(TCF_META_ID_MACLEN, maclen)
+ __ADD(TCF_META_ID_NFMARK, mark)
+ __ADD(TCF_META_ID_TCINDEX, tcindex)
+ __ADD(TCF_META_ID_RTCLASSID, rtclassid)
+ __ADD(TCF_META_ID_RTIIF, rtiif)
+ __ADD(TCF_META_ID_SK_FAMILY, sk_family)
+ __ADD(TCF_META_ID_SK_STATE, sk_state)
+ __ADD(TCF_META_ID_SK_REUSE, sk_reuse)
+ __ADD(TCF_META_ID_SK_REFCNT, sk_refcnt)
+ __ADD(TCF_META_ID_SK_RCVBUF, sk_rcvbuf)
+ __ADD(TCF_META_ID_SK_SNDBUF, sk_sndbuf)
+ __ADD(TCF_META_ID_SK_SHUTDOWN, sk_sutdown)
+ __ADD(TCF_META_ID_SK_PROTO, sk_proto)
+ __ADD(TCF_META_ID_SK_TYPE, sk_type)
+ __ADD(TCF_META_ID_SK_RMEM_ALLOC, sk_rmem_alloc)
+ __ADD(TCF_META_ID_SK_WMEM_ALLOC, sk_wmem_alloc)
+ __ADD(TCF_META_ID_SK_WMEM_QUEUED, sk_wmem_queued)
+ __ADD(TCF_META_ID_SK_RCV_QLEN, sk_rcv_qlen)
+ __ADD(TCF_META_ID_SK_SND_QLEN, sk_snd_qlen)
+ __ADD(TCF_META_ID_SK_ERR_QLEN, sk_err_qlen)
+ __ADD(TCF_META_ID_SK_FORWARD_ALLOCS, sk_forward_allocs)
+ __ADD(TCF_META_ID_SK_ALLOCS, sk_allocs)
+ __ADD(TCF_META_ID_SK_ROUTE_CAPS, sk_route_caps)
+ __ADD(TCF_META_ID_SK_HASH, sk_hash)
+ __ADD(TCF_META_ID_SK_LINGERTIME, sk_lingertime)
+ __ADD(TCF_META_ID_SK_ACK_BACKLOG, sk_ack_backlog)
+ __ADD(TCF_META_ID_SK_MAX_ACK_BACKLOG, sk_max_ack_backlog)
+ __ADD(TCF_META_ID_SK_PRIO, sk_prio)
+ __ADD(TCF_META_ID_SK_RCVLOWAT, sk_rcvlowat)
+ __ADD(TCF_META_ID_SK_RCVTIMEO, sk_rcvtimeo)
+ __ADD(TCF_META_ID_SK_SNDTIMEO, sk_sndtimeo)
+ __ADD(TCF_META_ID_SK_SENDMSG_OFF, sk_sendmsg_off)
+ __ADD(TCF_META_ID_SK_WRITE_PENDING, sk_write_pending)
+ __ADD(TCF_META_ID_VLAN_TAG, vlan)
+ __ADD(TCF_META_ID_RXHASH, rxhash)
+};
+
+static char *int_id2str(int id, char *buf, size_t size)
+{
+ return __type2str(id, buf, size, meta_int, ARRAY_SIZE(meta_int));
+}
+
+static const struct trans_tbl meta_var[] = {
+ __ADD(TCF_META_ID_DEV,devname)
+ __ADD(TCF_META_ID_SK_BOUND_IF,sk_bound_if)
+};
+
+static char *var_id2str(int id, char *buf, size_t size)
+{
+ return __type2str(id, buf, size, meta_var, ARRAY_SIZE(meta_var));
+}
+
+static void dump_value(struct rtnl_meta_value *v, struct nl_dump_params *p)
+{
+ char buf[32];
+
+ switch (v->mv_type) {
+ case TCF_META_TYPE_INT:
+ if (v->mv_id == TCF_META_ID_VALUE) {
+ nl_dump(p, "%u",
+ *(uint32_t *) (v + 1));
+ } else {
+ nl_dump(p, "%s",
+ int_id2str(v->mv_id, buf, sizeof(buf)));
+
+ if (v->mv_shift)
+ nl_dump(p, " >> %u", v->mv_shift);
+
+ if (v->mv_len == 4)
+ nl_dump(p, " & %#x", *(uint32_t *) (v + 1));
+ else if (v->mv_len == 8)
+ nl_dump(p, " & %#x", *(uint64_t *) (v + 1));
+ }
+ break;
+
+ case TCF_META_TYPE_VAR:
+ if (v->mv_id == TCF_META_ID_VALUE) {
+ nl_dump(p, "%s", (char *) (v + 1));
+ } else {
+ nl_dump(p, "%s",
+ var_id2str(v->mv_id, buf, sizeof(buf)));
+
+ if (v->mv_shift)
+ nl_dump(p, " >> %u", v->mv_shift);
+ }
+ break;
+ }
+}
+
+static void meta_dump(struct rtnl_ematch *e, struct nl_dump_params *p)
+{
+ struct meta_data *m = rtnl_ematch_data(e);
+ char buf[32];
+
+ nl_dump(p, "meta(");
+ dump_value(m->left, p);
+
+ nl_dump(p, " %s ", rtnl_ematch_opnd2txt(m->opnd, buf, sizeof(buf)));
+
+ dump_value(m->right, p);
+ nl_dump(p, ")");
+}
+
+static int meta_fill(struct rtnl_ematch *e, struct nl_msg *msg)
+{
+ struct meta_data *m = rtnl_ematch_data(e);
+ struct tcf_meta_hdr hdr;
+
+ if (!(m->left && m->right))
+ return -NLE_MISSING_ATTR;
+
+ memset(&hdr, 0, sizeof(hdr));
+ hdr.left.kind = (m->left->mv_type << 12) & TCF_META_TYPE_MASK;
+ hdr.left.kind |= m->left->mv_id & TCF_META_ID_MASK;
+ hdr.left.shift = m->left->mv_shift;
+ hdr.left.op = m->opnd;
+ hdr.right.kind = (m->right->mv_type << 12) & TCF_META_TYPE_MASK;
+ hdr.right.kind |= m->right->mv_id & TCF_META_ID_MASK;
+
+ NLA_PUT(msg, TCA_EM_META_HDR, sizeof(hdr), &hdr);
+
+ if (m->left->mv_len)
+ NLA_PUT(msg, TCA_EM_META_LVALUE, m->left->mv_len, (m->left + 1));
+
+ if (m->right->mv_len)
+ NLA_PUT(msg, TCA_EM_META_RVALUE, m->right->mv_len, (m->right + 1));
+
+ return 0;
+
+nla_put_failure:
+ return -NLE_NOMEM;
+}
+
+static void meta_free(struct rtnl_ematch *e)
+{
+ struct meta_data *m = rtnl_ematch_data(e);
+ free(m->left);
+ free(m->right);
+}
+
+static struct rtnl_ematch_ops meta_ops = {
+ .eo_kind = TCF_EM_META,
+ .eo_name = "meta",
+ .eo_minlen = sizeof(struct tcf_meta_hdr),
+ .eo_datalen = sizeof(struct meta_data),
+ .eo_parse = meta_parse,
+ .eo_dump = meta_dump,
+ .eo_fill = meta_fill,
+ .eo_free = meta_free,
+};
+
+static void __init meta_init(void)
+{
+ rtnl_ematch_register(&meta_ops);
+}
+
+/** @} */
diff --git a/lib/route/cls/ematch/nbyte.c b/lib/route/cls/ematch/nbyte.c
new file mode 100644
index 00000000..88524893
--- /dev/null
+++ b/lib/route/cls/ematch/nbyte.c
@@ -0,0 +1,139 @@
+/*
+ * lib/route/cls/ematch/nbyte.c Nbyte comparison
+ *
+ * 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) 2010-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup ematch
+ * @defgroup em_nbyte N-Byte Comparison
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/route/cls/ematch.h>
+#include <netlink/route/cls/ematch/nbyte.h>
+
+struct nbyte_data
+{
+ struct tcf_em_nbyte cfg;
+ uint8_t * pattern;
+};
+
+void rtnl_ematch_nbyte_set_offset(struct rtnl_ematch *e, uint8_t layer,
+ uint16_t offset)
+{
+ struct nbyte_data *n = rtnl_ematch_data(e);
+ n->cfg.off = offset;
+ n->cfg.layer = layer;
+}
+
+uint16_t rtnl_ematch_nbyte_get_offset(struct rtnl_ematch *e)
+{
+ return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.off;
+}
+
+uint8_t rtnl_ematch_nbyte_get_layer(struct rtnl_ematch *e)
+{
+ return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.layer;
+}
+
+void rtnl_ematch_nbyte_set_pattern(struct rtnl_ematch *e,
+ uint8_t *pattern, size_t len)
+{
+ struct nbyte_data *n = rtnl_ematch_data(e);
+
+ if (n->pattern)
+ free(n->pattern);
+
+ n->pattern = pattern;
+ n->cfg.len = len;
+}
+
+uint8_t *rtnl_ematch_nbyte_get_pattern(struct rtnl_ematch *e)
+{
+ return ((struct nbyte_data *) rtnl_ematch_data(e))->pattern;
+}
+
+size_t rtnl_ematch_nbyte_get_len(struct rtnl_ematch *e)
+{
+ return ((struct nbyte_data *) rtnl_ematch_data(e))->cfg.len;
+}
+
+static const char *layer_txt(struct tcf_em_nbyte *nbyte)
+{
+ switch (nbyte->layer) {
+ case TCF_LAYER_LINK:
+ return "link";
+ case TCF_LAYER_NETWORK:
+ return "net";
+ case TCF_LAYER_TRANSPORT:
+ return "trans";
+ default:
+ return "?";
+ }
+}
+
+static int nbyte_parse(struct rtnl_ematch *e, void *data, size_t len)
+{
+ struct nbyte_data *n = rtnl_ematch_data(e);
+ size_t hdrlen = sizeof(struct tcf_em_nbyte);
+ size_t plen = len - hdrlen;
+
+ memcpy(&n->cfg, data, hdrlen);
+ if (plen > 0) {
+ if (!(n->pattern = calloc(1, plen)))
+ return -NLE_NOMEM;
+
+ memcpy(n->pattern, data + hdrlen, plen);
+ }
+
+ return 0;
+}
+
+static void nbyte_dump(struct rtnl_ematch *e, struct nl_dump_params *p)
+{
+ struct nbyte_data *n = rtnl_ematch_data(e);
+ int i;
+
+ nl_dump(p, "pattern(%u:[", n->cfg.len);
+
+ for (i = 0; i < n->cfg.len; i++) {
+ nl_dump(p, "%02x", n->pattern[i]);
+ if (i+1 < n->cfg.len)
+ nl_dump(p, " ");
+ }
+
+ nl_dump(p, "] at %s+%u)", layer_txt(&n->cfg), n->cfg.off);
+}
+
+static void nbyte_free(struct rtnl_ematch *e)
+{
+ struct nbyte_data *n = rtnl_ematch_data(e);
+ free(n->pattern);
+}
+
+static struct rtnl_ematch_ops nbyte_ops = {
+ .eo_kind = TCF_EM_NBYTE,
+ .eo_name = "nbyte",
+ .eo_minlen = sizeof(struct tcf_em_nbyte),
+ .eo_datalen = sizeof(struct nbyte_data),
+ .eo_parse = nbyte_parse,
+ .eo_dump = nbyte_dump,
+ .eo_free = nbyte_free,
+};
+
+static void __init nbyte_init(void)
+{
+ rtnl_ematch_register(&nbyte_ops);
+}
+
+/** @} */
diff --git a/lib/route/cls/ematch/text.c b/lib/route/cls/ematch/text.c
new file mode 100644
index 00000000..e8cdcaec
--- /dev/null
+++ b/lib/route/cls/ematch/text.c
@@ -0,0 +1,183 @@
+/*
+ * lib/route/cls/ematch/text.c Text Search
+ *
+ * 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) 2010-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup ematch
+ * @defgroup em_text Text Search
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/route/cls/ematch.h>
+#include <netlink/route/cls/ematch/text.h>
+
+struct text_data
+{
+ struct tcf_em_text cfg;
+ char * pattern;
+};
+
+void rtnl_ematch_text_set_from(struct rtnl_ematch *e, uint8_t layer,
+ uint16_t offset)
+{
+ struct text_data *t = rtnl_ematch_data(e);
+ t->cfg.from_offset = offset;
+ t->cfg.from_layer = layer;
+}
+
+uint16_t rtnl_ematch_text_get_from_offset(struct rtnl_ematch *e)
+{
+ return ((struct text_data *) rtnl_ematch_data(e))->cfg.from_offset;
+}
+
+uint8_t rtnl_ematch_text_get_from_layer(struct rtnl_ematch *e)
+{
+ return ((struct text_data *) rtnl_ematch_data(e))->cfg.from_layer;
+}
+
+void rtnl_ematch_text_set_to(struct rtnl_ematch *e, uint8_t layer,
+ uint16_t offset)
+{
+ struct text_data *t = rtnl_ematch_data(e);
+ t->cfg.to_offset = offset;
+ t->cfg.to_layer = layer;
+}
+
+uint16_t rtnl_ematch_text_get_to_offset(struct rtnl_ematch *e)
+{
+ return ((struct text_data *) rtnl_ematch_data(e))->cfg.to_offset;
+}
+
+uint8_t rtnl_ematch_text_get_to_layer(struct rtnl_ematch *e)
+{
+ return ((struct text_data *) rtnl_ematch_data(e))->cfg.to_layer;
+}
+
+void rtnl_ematch_text_set_pattern(struct rtnl_ematch *e,
+ char *pattern, size_t len)
+{
+ struct text_data *t = rtnl_ematch_data(e);
+
+ if (t->pattern)
+ free(t->pattern);
+
+ t->pattern = pattern;
+ t->cfg.pattern_len = len;
+}
+
+char *rtnl_ematch_text_get_pattern(struct rtnl_ematch *e)
+{
+ return ((struct text_data *) rtnl_ematch_data(e))->pattern;
+}
+
+size_t rtnl_ematch_text_get_len(struct rtnl_ematch *e)
+{
+ return ((struct text_data *) rtnl_ematch_data(e))->cfg.pattern_len;
+}
+
+void rtnl_ematch_text_set_algo(struct rtnl_ematch *e, const char *algo)
+{
+ struct text_data *t = rtnl_ematch_data(e);
+
+ strncpy(t->cfg.algo, algo, sizeof(t->cfg.algo));
+}
+
+char *rtnl_ematch_text_get_algo(struct rtnl_ematch *e)
+{
+ struct text_data *t = rtnl_ematch_data(e);
+
+ return t->cfg.algo[0] ? t->cfg.algo : NULL;
+}
+
+static int text_parse(struct rtnl_ematch *e, void *data, size_t len)
+{
+ struct text_data *t = rtnl_ematch_data(e);
+ size_t hdrlen = sizeof(struct tcf_em_text);
+ size_t plen = len - hdrlen;
+
+ memcpy(&t->cfg, data, hdrlen);
+
+ if (t->cfg.pattern_len > plen)
+ return -NLE_INVAL;
+
+ if (t->cfg.pattern_len > 0) {
+ if (!(t->pattern = calloc(1, t->cfg.pattern_len)))
+ return -NLE_NOMEM;
+
+ memcpy(t->pattern, data + hdrlen, t->cfg.pattern_len);
+ }
+
+ return 0;
+}
+
+static void text_dump(struct rtnl_ematch *e, struct nl_dump_params *p)
+{
+ struct text_data *t = rtnl_ematch_data(e);
+ char buf[64];
+
+ nl_dump(p, "text(%s \"%s\"",
+ t->cfg.algo[0] ? t->cfg.algo : "no-algo",
+ t->pattern ? : "no-pattern");
+
+ if (t->cfg.from_layer || t->cfg.from_offset) {
+ nl_dump(p, " from %s",
+ rtnl_ematch_offset2txt(t->cfg.from_layer,
+ t->cfg.from_offset,
+ buf, sizeof(buf)));
+ }
+
+ if (t->cfg.to_layer || t->cfg.to_offset) {
+ nl_dump(p, " to %s",
+ rtnl_ematch_offset2txt(t->cfg.to_layer,
+ t->cfg.to_offset,
+ buf, sizeof(buf)));
+ }
+
+ nl_dump(p, ")");
+}
+
+static int text_fill(struct rtnl_ematch *e, struct nl_msg *msg)
+{
+ struct text_data *t = rtnl_ematch_data(e);
+ int err;
+
+ if ((err = nlmsg_append(msg, &t->cfg, sizeof(t->cfg), 0)) < 0)
+ return err;
+
+ return nlmsg_append(msg, t->pattern, t->cfg.pattern_len, 0);
+}
+
+static void text_free(struct rtnl_ematch *e)
+{
+ struct text_data *t = rtnl_ematch_data(e);
+ free(t->pattern);
+}
+
+static struct rtnl_ematch_ops text_ops = {
+ .eo_kind = TCF_EM_TEXT,
+ .eo_name = "text",
+ .eo_minlen = sizeof(struct tcf_em_text),
+ .eo_datalen = sizeof(struct text_data),
+ .eo_parse = text_parse,
+ .eo_dump = text_dump,
+ .eo_fill = text_fill,
+ .eo_free = text_free,
+};
+
+static void __init text_init(void)
+{
+ rtnl_ematch_register(&text_ops);
+}
+
+/** @} */
diff --git a/lib/route/cls/ematch_grammar.l b/lib/route/cls/ematch_grammar.l
new file mode 100644
index 00000000..96ef1a0b
--- /dev/null
+++ b/lib/route/cls/ematch_grammar.l
@@ -0,0 +1,162 @@
+/*
+ * lib/route/cls/ematch_grammar.l ematch expression grammar
+ *
+ * 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) 2010-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+%{
+ #include <netlink-private/netlink.h>
+ #include <netlink-private/tc.h>
+ #include <netlink/netlink.h>
+ #include <netlink/route/cls/ematch.h>
+ #include <netlink/route/cls/ematch/cmp.h>
+ #include "ematch_syntax.h"
+%}
+
+%option 8bit
+%option reentrant
+%option warn
+%option noyywrap
+%option noinput
+%option nounput
+%option bison-bridge
+%option prefix="ematch_"
+
+%x QUOTE
+
+%%
+
+[ \t\r\n]+
+
+\" {
+ NL_DBG(4, "Beginning of quote\n");
+ yylval->q.len = 32;
+ if (!(yylval->q.data = calloc(1, yylval->q.len)))
+ return ERROR;
+
+ yylval->q.index = 0;
+ BEGIN(QUOTE);
+ }
+
+<QUOTE>[^\\\n\"]+ {
+ memcpy(yylval->q.data + yylval->q.index, yytext,
+ strlen(yytext));
+ yylval->q.index += strlen(yytext);
+ }
+
+<QUOTE>\" {
+ BEGIN(0);
+ return QUOTED;
+ }
+
+
+[[:digit:]]+ |
+0[xX][[:xdigit:]]+ {
+ yylval->i = strtoul(yytext, NULL, 0);
+ return NUMBER;
+ }
+
+eq |
+"=" return KW_EQ;
+gt |
+">" return KW_GT;
+lt |
+"<" return KW_LT;
+
+[aA][nN][dD] |
+"&&" { yylval->i = TCF_EM_REL_AND; return LOGIC; }
+[oO][rR] |
+"||" { yylval->i = TCF_EM_REL_OR; return LOGIC; }
+[nN][oO][tT] |
+"!" return NOT;
+
+[cC][mM][pP] { yylval->i = TCF_EM_CMP; return EMATCH_CMP; }
+[pP][aA][tT][tT][eE][rR][nN] { yylval->i = TCF_EM_NBYTE; return EMATCH_NBYTE; }
+[tT][eE][xX][tT] { yylval->i = TCF_EM_TEXT; return EMATCH_TEXT; }
+[mM][eE][tT][aA] { yylval->i = TCF_EM_META; return EMATCH_META; }
+
+"(" return KW_OPEN;
+")" return KW_CLOSE;
+[mM][aA][sS][kK] |
+"&" return KW_MASK;
+[sS][hH][iI][fF][tT] |
+">>" return KW_SHIFT;
+[aA][tT] return KW_AT;
+"+" return KW_PLUS;
+[fF][rR][oO][mM] return KW_FROM;
+[tT][oO] return KW_TO;
+
+[uU]8 { yylval->i = TCF_EM_ALIGN_U8; return ALIGN; }
+[uU]16 { yylval->i = TCF_EM_ALIGN_U16; return ALIGN; }
+[uU]32 { yylval->i = TCF_EM_ALIGN_U32; return ALIGN; }
+
+[lL][iI][nN][kK] |
+[eE][tT][hH] { yylval->i = TCF_LAYER_LINK; return LAYER; }
+[nN][eE][tT] |
+[iI][pP]6 |
+[iI][pP] { yylval->i = TCF_LAYER_NETWORK; return LAYER; }
+[tT][rR][aA][nN][sS][pP][oO][rR][tT] |
+[tT][cC][pP] { yylval->i = TCF_LAYER_TRANSPORT; return LAYER; }
+
+random return META_RANDOM;
+loadavg_0 return META_LOADAVG_0;
+loadavg_1 return META_LOADAVG_1;
+loadavg_2 return META_LOADAVG_2;
+dev return META_DEV;
+prio return META_PRIO;
+proto return META_PROTO;
+pkttype return META_PKTTYPE;
+pktlen return META_PKTLEN;
+datalen return META_DATALEN;
+maclen return META_MACLEN;
+mark return META_MARK;
+tcindex return META_TCINDEX;
+rtclassid return META_RTCLASSID;
+rtiif return META_RTIIF;
+sk_family return META_SK_FAMILY;
+sk_state return META_SK_STATE;
+sk_reuse return META_SK_REUSE;
+sk_refcnt return META_SK_REFCNT;
+sk_rcvbuf return META_SK_RCVBUF;
+sk_sndbuf return META_SK_SNDBUF;
+sk_shutdown return META_SK_SHUTDOWN;
+sk_proto return META_SK_PROTO;
+sk_type return META_SK_TYPE;
+sk_rmem_alloc return META_SK_RMEM_ALLOC;
+sk_wmem_alloc return META_SK_WMEM_ALLOC;
+sk_wmem_queued return META_SK_WMEM_QUEUED;
+sk_rcv_qlen return META_SK_RCV_QLEN;
+sk_snd_qlen return META_SK_SND_QLEN;
+sk_err_qlen return META_SK_ERR_QLEN;
+sk_forward_allocs return META_SK_FORWARD_ALLOCS;
+sk_allocs return META_SK_ALLOCS;
+sk_route_caps return META_SK_ROUTE_CAPS;
+sk_hash return META_SK_HASH;
+sk_lingertime return META_SK_LINGERTIME;
+sk_ack_backlog return META_SK_ACK_BACKLOG;
+sk_max_ack_backlog return META_SK_MAX_ACK_BACKLOG;
+sk_prio return META_SK_PRIO;
+sk_rcvlowat return META_SK_RCVLOWAT;
+sk_rcvtimeo return META_SK_RCVTIMEO;
+sk_sndtimeo return META_SK_SNDTIMEO;
+sk_sendmsg_off return META_SK_SENDMSG_OFF;
+sk_write_pending return META_SK_WRITE_PENDING;
+vlan return META_VLAN;
+rxhash return META_RXHASH;
+
+devname return META_DEVNAME;
+sk_bound_if return META_SK_BOUND_IF;
+
+
+[^ \t\r\n+()=<>&|\"]+ {
+ yylval->s = strdup(yytext);
+ if (yylval->s == NULL)
+ return ERROR;
+ NL_DBG(4, "lex STR=%s\n", yylval->s);
+ return STR;
+ }
diff --git a/lib/route/cls/ematch_syntax.y b/lib/route/cls/ematch_syntax.y
new file mode 100644
index 00000000..da210392
--- /dev/null
+++ b/lib/route/cls/ematch_syntax.y
@@ -0,0 +1,501 @@
+/*
+ * lib/route/cls/ematch_syntax.y ematch expression syntax
+ *
+ * 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) 2010-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+%{
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/route/pktloc.h>
+#include <netlink/route/cls/ematch.h>
+#include <netlink/route/cls/ematch/cmp.h>
+#include <netlink/route/cls/ematch/nbyte.h>
+#include <netlink/route/cls/ematch/text.h>
+#include <netlink/route/cls/ematch/meta.h>
+
+#define META_ALLOC rtnl_meta_value_alloc_id
+#define META_ID(name) TCF_META_ID_##name
+#define META_INT TCF_META_TYPE_INT
+#define META_VAR TCF_META_TYPE_VAR
+%}
+
+%error-verbose
+%define api.pure
+%name-prefix "ematch_"
+
+%parse-param {void *scanner}
+%parse-param {char **errp}
+%parse-param {struct nl_list_head *root}
+%lex-param {void *scanner}
+
+%union {
+ struct tcf_em_cmp cmp;
+ struct ematch_quoted q;
+ struct rtnl_ematch * e;
+ struct rtnl_pktloc * loc;
+ struct rtnl_meta_value *mv;
+ uint32_t i;
+ uint64_t i64;
+ char * s;
+}
+
+%{
+extern int ematch_lex(YYSTYPE *, void *);
+
+static void yyerror(void *scanner, char **errp, struct nl_list_head *root, const char *msg)
+{
+ if (msg)
+ *errp = strdup(msg);
+ else
+ *errp = NULL;
+}
+%}
+
+%token <i> ERROR LOGIC NOT OPERAND NUMBER ALIGN LAYER
+%token <i> KW_OPEN "("
+%token <i> KW_CLOSE ")"
+%token <i> KW_PLUS "+"
+%token <i> KW_MASK "mask"
+%token <i> KW_SHIFT ">>"
+%token <i> KW_AT "at"
+%token <i> EMATCH_CMP "cmp"
+%token <i> EMATCH_NBYTE "pattern"
+%token <i> EMATCH_TEXT "text"
+%token <i> EMATCH_META "meta"
+%token <i> KW_EQ "="
+%token <i> KW_GT ">"
+%token <i> KW_LT "<"
+%token <i> KW_FROM "from"
+%token <i> KW_TO "to"
+
+%token <i> META_RANDOM "random"
+%token <i> META_LOADAVG_0 "loadavg_0"
+%token <i> META_LOADAVG_1 "loadavg_1"
+%token <i> META_LOADAVG_2 "loadavg_2"
+%token <i> META_DEV "dev"
+%token <i> META_PRIO "prio"
+%token <i> META_PROTO "proto"
+%token <i> META_PKTTYPE "pkttype"
+%token <i> META_PKTLEN "pktlen"
+%token <i> META_DATALEN "datalen"
+%token <i> META_MACLEN "maclen"
+%token <i> META_MARK "mark"
+%token <i> META_TCINDEX "tcindex"
+%token <i> META_RTCLASSID "rtclassid"
+%token <i> META_RTIIF "rtiif"
+%token <i> META_SK_FAMILY "sk_family"
+%token <i> META_SK_STATE "sk_state"
+%token <i> META_SK_REUSE "sk_reuse"
+%token <i> META_SK_REFCNT "sk_refcnt"
+%token <i> META_SK_RCVBUF "sk_rcvbuf"
+%token <i> META_SK_SNDBUF "sk_sndbuf"
+%token <i> META_SK_SHUTDOWN "sk_shutdown"
+%token <i> META_SK_PROTO "sk_proto"
+%token <i> META_SK_TYPE "sk_type"
+%token <i> META_SK_RMEM_ALLOC "sk_rmem_alloc"
+%token <i> META_SK_WMEM_ALLOC "sk_wmem_alloc"
+%token <i> META_SK_WMEM_QUEUED "sk_wmem_queued"
+%token <i> META_SK_RCV_QLEN "sk_rcv_qlen"
+%token <i> META_SK_SND_QLEN "sk_snd_qlen"
+%token <i> META_SK_ERR_QLEN "sk_err_qlen"
+%token <i> META_SK_FORWARD_ALLOCS "sk_forward_allocs"
+%token <i> META_SK_ALLOCS "sk_allocs"
+%token <i> META_SK_ROUTE_CAPS "sk_route_caps"
+%token <i> META_SK_HASH "sk_hash"
+%token <i> META_SK_LINGERTIME "sk_lingertime"
+%token <i> META_SK_ACK_BACKLOG "sk_ack_backlog"
+%token <i> META_SK_MAX_ACK_BACKLOG "sk_max_ack_backlog"
+%token <i> META_SK_PRIO "sk_prio"
+%token <i> META_SK_RCVLOWAT "sk_rcvlowat"
+%token <i> META_SK_RCVTIMEO "sk_rcvtimeo"
+%token <i> META_SK_SNDTIMEO "sk_sndtimeo"
+%token <i> META_SK_SENDMSG_OFF "sk_sendmsg_off"
+%token <i> META_SK_WRITE_PENDING "sk_write_pending"
+%token <i> META_VLAN "vlan"
+%token <i> META_RXHASH "rxhash"
+%token <i> META_DEVNAME "devname"
+%token <i> META_SK_BOUND_IF "sk_bound_if"
+
+%token <s> STR
+
+%token <q> QUOTED
+
+%type <i> align operand shift meta_int_id meta_var_id
+%type <i64> mask
+%type <e> expr match ematch
+%type <cmp> cmp_expr cmp_match
+%type <loc> pktloc text_from text_to
+%type <q> pattern
+%type <mv> meta_value
+
+%destructor { free($$); NL_DBG(2, "string destructor\n"); } <s>
+%destructor { rtnl_pktloc_put($$); NL_DBG(2, "pktloc destructor\n"); } <loc>
+%destructor { free($$.data); NL_DBG(2, "quoted destructor\n"); } <q>
+%destructor { rtnl_meta_value_put($$); NL_DBG(2, "meta value destructor\n"); } <mv>
+
+%start input
+
+%%
+
+input:
+ /* empty */
+ | expr
+ {
+ nl_list_add_tail(root, &$1->e_list);
+ }
+ ;
+
+expr:
+ match
+ {
+ $$ = $1;
+ }
+ | match LOGIC expr
+ {
+ rtnl_ematch_set_flags($1, $2);
+
+ /* make ematch new head */
+ nl_list_add_tail(&$1->e_list, &$3->e_list);
+
+ $$ = $1;
+ }
+ ;
+
+match:
+ NOT ematch
+ {
+ rtnl_ematch_set_flags($2, TCF_EM_INVERT);
+ $$ = $2;
+ }
+ | ematch
+ {
+ $$ = $1;
+ }
+ ;
+
+ematch:
+ /* CMP */
+ cmp_match
+ {
+ struct rtnl_ematch *e;
+
+ if (!(e = rtnl_ematch_alloc())) {
+ *errp = strdup("Unable to allocate ematch object");
+ YYABORT;
+ }
+
+ if (rtnl_ematch_set_kind(e, TCF_EM_CMP) < 0)
+ BUG();
+
+ rtnl_ematch_cmp_set(e, &$1);
+ $$ = e;
+ }
+ | EMATCH_NBYTE "(" pktloc KW_EQ pattern ")"
+ {
+ struct rtnl_ematch *e;
+
+ if (!(e = rtnl_ematch_alloc())) {
+ *errp = strdup("Unable to allocate ematch object");
+ YYABORT;
+ }
+
+ if (rtnl_ematch_set_kind(e, TCF_EM_NBYTE) < 0)
+ BUG();
+
+ rtnl_ematch_nbyte_set_offset(e, $3->layer, $3->offset);
+ rtnl_pktloc_put($3);
+ rtnl_ematch_nbyte_set_pattern(e, (uint8_t *) $5.data, $5.index);
+
+ $$ = e;
+ }
+ | EMATCH_TEXT "(" STR QUOTED text_from text_to ")"
+ {
+ struct rtnl_ematch *e;
+
+ if (!(e = rtnl_ematch_alloc())) {
+ *errp = strdup("Unable to allocate ematch object");
+ YYABORT;
+ }
+
+ if (rtnl_ematch_set_kind(e, TCF_EM_TEXT) < 0)
+ BUG();
+
+ rtnl_ematch_text_set_algo(e, $3);
+ rtnl_ematch_text_set_pattern(e, $4.data, $4.index);
+
+ if ($5) {
+ rtnl_ematch_text_set_from(e, $5->layer, $5->offset);
+ rtnl_pktloc_put($5);
+ }
+
+ if ($6) {
+ rtnl_ematch_text_set_to(e, $6->layer, $6->offset);
+ rtnl_pktloc_put($6);
+ }
+
+ $$ = e;
+ }
+ | EMATCH_META "(" meta_value operand meta_value ")"
+ {
+ struct rtnl_ematch *e;
+
+ if (!(e = rtnl_ematch_alloc())) {
+ *errp = strdup("Unable to allocate ematch object");
+ YYABORT;
+ }
+
+ if (rtnl_ematch_set_kind(e, TCF_EM_META) < 0)
+ BUG();
+
+ rtnl_ematch_meta_set_lvalue(e, $3);
+ rtnl_ematch_meta_set_rvalue(e, $5);
+ rtnl_ematch_meta_set_operand(e, $4);
+
+ $$ = e;
+ }
+ /* CONTAINER */
+ | "(" expr ")"
+ {
+ struct rtnl_ematch *e;
+
+ if (!(e = rtnl_ematch_alloc())) {
+ *errp = strdup("Unable to allocate ematch object");
+ YYABORT;
+ }
+
+ if (rtnl_ematch_set_kind(e, TCF_EM_CONTAINER) < 0)
+ BUG();
+
+ /* Make e->childs the list head of a the ematch sequence */
+ nl_list_add_tail(&e->e_childs, &$2->e_list);
+
+ $$ = e;
+ }
+ ;
+
+/*
+ * CMP match
+ *
+ * match := cmp(expr) | expr
+ * expr := pktloc (=|>|<) NUMBER
+ * pktloc := alias | definition
+ *
+ */
+cmp_match:
+ EMATCH_CMP "(" cmp_expr ")"
+ { $$ = $3; }
+ | cmp_expr
+ { $$ = $1; }
+ ;
+
+cmp_expr:
+ pktloc operand NUMBER
+ {
+ if ($1->align == TCF_EM_ALIGN_U16 ||
+ $1->align == TCF_EM_ALIGN_U32)
+ $$.flags = TCF_EM_CMP_TRANS;
+
+ memset(&$$, 0, sizeof($$));
+
+ $$.mask = $1->mask;
+ $$.off = $1->offset;
+ $$.align = $1->align;
+ $$.layer = $1->layer;
+ $$.opnd = $2;
+ $$.val = $3;
+
+ rtnl_pktloc_put($1);
+ }
+ ;
+
+text_from:
+ /* empty */
+ { $$ = NULL; }
+ | "from" pktloc
+ { $$ = $2; }
+ ;
+
+text_to:
+ /* empty */
+ { $$ = NULL; }
+ | "to" pktloc
+ { $$ = $2; }
+ ;
+
+meta_value:
+ QUOTED
+ { $$ = rtnl_meta_value_alloc_var($1.data, $1.len); }
+ | NUMBER
+ { $$ = rtnl_meta_value_alloc_int($1); }
+ | meta_int_id shift mask
+ { $$ = META_ALLOC(META_INT, $1, $2, $3); }
+ | meta_var_id shift
+ { $$ = META_ALLOC(META_VAR, $1, $2, 0); }
+ ;
+
+meta_int_id:
+ META_RANDOM { $$ = META_ID(RANDOM); }
+ |META_LOADAVG_0 { $$ = META_ID(LOADAVG_0); }
+ |META_LOADAVG_1 { $$ = META_ID(LOADAVG_1); }
+ |META_LOADAVG_2 { $$ = META_ID(LOADAVG_2); }
+ | META_DEV { $$ = META_ID(DEV); }
+ | META_PRIO { $$ = META_ID(PRIORITY); }
+ | META_PROTO { $$ = META_ID(PROTOCOL); }
+ | META_PKTTYPE { $$ = META_ID(PKTTYPE); }
+ | META_PKTLEN { $$ = META_ID(PKTLEN); }
+ | META_DATALEN { $$ = META_ID(DATALEN); }
+ | META_MACLEN { $$ = META_ID(MACLEN); }
+ | META_MARK { $$ = META_ID(NFMARK); }
+ | META_TCINDEX { $$ = META_ID(TCINDEX); }
+ | META_RTCLASSID { $$ = META_ID(RTCLASSID); }
+ | META_RTIIF { $$ = META_ID(RTIIF); }
+ | META_SK_FAMILY { $$ = META_ID(SK_FAMILY); }
+ | META_SK_STATE { $$ = META_ID(SK_STATE); }
+ | META_SK_REUSE { $$ = META_ID(SK_REUSE); }
+ | META_SK_REFCNT { $$ = META_ID(SK_REFCNT); }
+ | META_SK_RCVBUF { $$ = META_ID(SK_RCVBUF); }
+ | META_SK_SNDBUF { $$ = META_ID(SK_SNDBUF); }
+ | META_SK_SHUTDOWN { $$ = META_ID(SK_SHUTDOWN); }
+ | META_SK_PROTO { $$ = META_ID(SK_PROTO); }
+ | META_SK_TYPE { $$ = META_ID(SK_TYPE); }
+ | META_SK_RMEM_ALLOC { $$ = META_ID(SK_RMEM_ALLOC); }
+ | META_SK_WMEM_ALLOC { $$ = META_ID(SK_WMEM_ALLOC); }
+ | META_SK_WMEM_QUEUED { $$ = META_ID(SK_WMEM_QUEUED); }
+ | META_SK_RCV_QLEN { $$ = META_ID(SK_RCV_QLEN); }
+ | META_SK_SND_QLEN { $$ = META_ID(SK_SND_QLEN); }
+ | META_SK_ERR_QLEN { $$ = META_ID(SK_ERR_QLEN); }
+ | META_SK_FORWARD_ALLOCS { $$ = META_ID(SK_FORWARD_ALLOCS); }
+ | META_SK_ALLOCS { $$ = META_ID(SK_ALLOCS); }
+ | META_SK_ROUTE_CAPS { $$ = META_ID(SK_ROUTE_CAPS); }
+ | META_SK_HASH { $$ = META_ID(SK_HASH); }
+ | META_SK_LINGERTIME { $$ = META_ID(SK_LINGERTIME); }
+ | META_SK_ACK_BACKLOG { $$ = META_ID(SK_ACK_BACKLOG); }
+ | META_SK_MAX_ACK_BACKLOG { $$ = META_ID(SK_MAX_ACK_BACKLOG); }
+ | META_SK_PRIO { $$ = META_ID(SK_PRIO); }
+ | META_SK_RCVLOWAT { $$ = META_ID(SK_RCVLOWAT); }
+ | META_SK_RCVTIMEO { $$ = META_ID(SK_RCVTIMEO); }
+ | META_SK_SNDTIMEO { $$ = META_ID(SK_SNDTIMEO); }
+ | META_SK_SENDMSG_OFF { $$ = META_ID(SK_SENDMSG_OFF); }
+ | META_SK_WRITE_PENDING { $$ = META_ID(SK_WRITE_PENDING); }
+ | META_VLAN { $$ = META_ID(VLAN_TAG); }
+ | META_RXHASH { $$ = META_ID(RXHASH); }
+ ;
+
+meta_var_id:
+ META_DEVNAME { $$ = META_ID(DEV); }
+ | META_SK_BOUND_IF { $$ = META_ID(SK_BOUND_IF); }
+ ;
+
+/*
+ * pattern
+ */
+pattern:
+ QUOTED
+ {
+ $$ = $1;
+ }
+ | STR
+ {
+ struct nl_addr *addr;
+
+ if (nl_addr_parse($1, AF_UNSPEC, &addr) == 0) {
+ $$.len = nl_addr_get_len(addr);
+
+ $$.index = min_t(int, $$.len, nl_addr_get_prefixlen(addr)/8);
+
+ if (!($$.data = calloc(1, $$.len))) {
+ nl_addr_put(addr);
+ YYABORT;
+ }
+
+ memcpy($$.data, nl_addr_get_binary_addr(addr), $$.len);
+ nl_addr_put(addr);
+ } else {
+ if (asprintf(errp, "invalid pattern \"%s\"", $1) == -1)
+ *errp = NULL;
+ YYABORT;
+ }
+ }
+ ;
+
+/*
+ * packet location
+ */
+
+pktloc:
+ STR
+ {
+ struct rtnl_pktloc *loc;
+
+ if (rtnl_pktloc_lookup($1, &loc) < 0) {
+ if (asprintf(errp, "Packet location \"%s\" not found", $1) == -1)
+ *errp = NULL;
+ YYABORT;
+ }
+
+ $$ = loc;
+ }
+ /* [u8|u16|u32|NUM at] LAYER + OFFSET [mask MASK] */
+ | align LAYER "+" NUMBER mask
+ {
+ struct rtnl_pktloc *loc;
+
+ if ($5 && (!$1 || $1 > TCF_EM_ALIGN_U32)) {
+ *errp = strdup("mask only allowed for alignments u8|u16|u32");
+ YYABORT;
+ }
+
+ if (!(loc = rtnl_pktloc_alloc())) {
+ *errp = strdup("Unable to allocate packet location object");
+ YYABORT;
+ }
+
+ loc->name = strdup("<USER-DEFINED>");
+ loc->align = $1;
+ loc->layer = $2;
+ loc->offset = $4;
+ loc->mask = $5;
+
+ $$ = loc;
+ }
+ ;
+
+align:
+ /* empty */
+ { $$ = 0; }
+ | ALIGN "at"
+ { $$ = $1; }
+ | NUMBER "at"
+ { $$ = $1; }
+ ;
+
+mask:
+ /* empty */
+ { $$ = 0; }
+ | KW_MASK NUMBER
+ { $$ = $2; }
+ ;
+
+shift:
+ /* empty */
+ { $$ = 0; }
+ | KW_SHIFT NUMBER
+ { $$ = $2; }
+ ;
+
+operand:
+ KW_EQ
+ { $$ = TCF_EM_OPND_EQ; }
+ | KW_GT
+ { $$ = TCF_EM_OPND_GT; }
+ | KW_LT
+ { $$ = TCF_EM_OPND_LT; }
+ ;
diff --git a/lib/route/cls/fw.c b/lib/route/cls/fw.c
index 8cf25b97..b569d4f0 100644
--- a/lib/route/cls/fw.c
+++ b/lib/route/cls/fw.c
@@ -6,23 +6,23 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2006 Petr Gotthard <petr.gotthard@siemens.com>
* Copyright (c) 2006 Siemens AG Oesterreich
*/
/**
- * @ingroup cls_api
- * @defgroup fw Firewall Classifier
+ * @ingroup cls
+ * @defgroup cls_fw Firewall Classifier
*
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/classifier.h>
-#include <netlink/route/classifier-modules.h>
#include <netlink/route/cls/fw.h>
/** @cond SKIP */
@@ -30,21 +30,23 @@
#define FW_ATTR_ACTION 0x002
#define FW_ATTR_POLICE 0x004
#define FW_ATTR_INDEV 0x008
+#define FW_ATTR_MASK 0x010
/** @endcond */
static struct nla_policy fw_policy[TCA_FW_MAX+1] = {
[TCA_FW_CLASSID] = { .type = NLA_U32 },
[TCA_FW_INDEV] = { .type = NLA_STRING,
.maxlen = IFNAMSIZ },
+ [TCA_FW_MASK] = { .type = NLA_U32 },
};
-static int fw_msg_parser(struct rtnl_cls *cls)
+static int fw_msg_parser(struct rtnl_tc *tc, void *data)
{
- struct rtnl_fw *f = rtnl_cls_data(cls);
struct nlattr *tb[TCA_FW_MAX + 1];
+ struct rtnl_fw *f = data;
int err;
- err = tca_parse(tb, TCA_FW_MAX, (struct rtnl_tca *) cls, fw_policy);
+ err = tca_parse(tb, TCA_FW_MAX, tc, fw_policy);
if (err < 0)
return err;
@@ -72,21 +74,25 @@ static int fw_msg_parser(struct rtnl_cls *cls)
f->cf_mask |= FW_ATTR_INDEV;
}
+ if (tb[TCA_FW_MASK]) {
+ f->cf_fwmask = nla_get_u32(tb[TCA_FW_MASK]);
+ f->cf_mask |= FW_ATTR_MASK;
+ }
+
return 0;
}
-static void fw_free_data(struct rtnl_cls *cls)
+static void fw_free_data(struct rtnl_tc *tc, void *data)
{
- struct rtnl_fw *f = rtnl_cls_data(cls);
+ struct rtnl_fw *f = data;
nl_data_free(f->cf_act);
nl_data_free(f->cf_police);
}
-static int fw_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
+static int fw_clone(void *_dst, void *_src)
{
- struct rtnl_fw *dst = rtnl_cls_data(_dst);
- struct rtnl_fw *src = rtnl_cls_data(_src);
+ struct rtnl_fw *dst = _dst, *src = _src;
if (src->cf_act && !(dst->cf_act = nl_data_clone(src->cf_act)))
return -NLE_NOMEM;
@@ -97,28 +103,41 @@ static int fw_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
return 0;
}
-static void fw_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
+static void fw_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_fw *f = rtnl_cls_data(cls);
- char buf[32];
+ struct rtnl_fw *f = data;
+
+ if (!f)
+ return;
+
+ if (f->cf_mask & FW_ATTR_CLASSID) {
+ char buf[32];
- if (f->cf_mask & FW_ATTR_CLASSID)
nl_dump(p, " target %s",
rtnl_tc_handle2str(f->cf_classid, buf, sizeof(buf)));
+ }
+
+ if (f->cf_mask & FW_ATTR_MASK)
+ nl_dump(p, " mask 0x%x", f->cf_fwmask);
}
-static void fw_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
+static void fw_dump_details(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_fw *f = rtnl_cls_data(cls);
+ struct rtnl_fw *f = data;
- if (f->cf_mask & FW_ATTR_INDEV)
+ if (f && f->cf_mask & FW_ATTR_INDEV)
nl_dump(p, "indev %s ", f->cf_indev);
}
-static int fw_get_opts(struct rtnl_cls *cls, struct nl_msg *msg)
+static int fw_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
{
- struct rtnl_fw *f = rtnl_cls_data(cls);
-
+ struct rtnl_fw *f = data;
+
+ if (!f)
+ return 0;
+
if (f->cf_mask & FW_ATTR_CLASSID)
NLA_PUT_U32(msg, TCA_FW_CLASSID, f->cf_classid);
@@ -131,10 +150,13 @@ static int fw_get_opts(struct rtnl_cls *cls, struct nl_msg *msg)
if (f->cf_mask & FW_ATTR_INDEV)
NLA_PUT_STRING(msg, TCA_FW_INDEV, f->cf_indev);
+ if (f->cf_mask & FW_ATTR_MASK)
+ NLA_PUT_U32(msg, TCA_FW_MASK, f->cf_fwmask);
+
return 0;
nla_put_failure:
- return -NLE_NOMEM;
+ return -NLE_MSGSIZE;
}
/**
@@ -144,7 +166,10 @@ nla_put_failure:
int rtnl_fw_set_classid(struct rtnl_cls *cls, uint32_t classid)
{
- struct rtnl_fw *f = rtnl_cls_data(cls);
+ struct rtnl_fw *f;
+
+ if (!(f = rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
f->cf_classid = classid;
f->cf_mask |= FW_ATTR_CLASSID;
@@ -152,16 +177,30 @@ int rtnl_fw_set_classid(struct rtnl_cls *cls, uint32_t classid)
return 0;
}
+int rtnl_fw_set_mask(struct rtnl_cls *cls, uint32_t mask)
+{
+ struct rtnl_fw *f;
+
+ if (!(f = rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
+
+ f->cf_fwmask = mask;
+ f->cf_mask |= FW_ATTR_MASK;
+
+ return 0;
+}
+
/** @} */
-static struct rtnl_cls_ops fw_ops = {
- .co_kind = "fw",
- .co_size = sizeof(struct rtnl_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 = {
+static struct rtnl_tc_ops fw_ops = {
+ .to_kind = "fw",
+ .to_type = RTNL_TC_TYPE_CLS,
+ .to_size = sizeof(struct rtnl_fw),
+ .to_msg_parser = fw_msg_parser,
+ .to_msg_fill = fw_msg_fill,
+ .to_free_data = fw_free_data,
+ .to_clone = fw_clone,
+ .to_dump = {
[NL_DUMP_LINE] = fw_dump_line,
[NL_DUMP_DETAILS] = fw_dump_details,
},
@@ -169,12 +208,12 @@ static struct rtnl_cls_ops fw_ops = {
static void __init fw_init(void)
{
- rtnl_cls_register(&fw_ops);
+ rtnl_tc_register(&fw_ops);
}
static void __exit fw_exit(void)
{
- rtnl_cls_unregister(&fw_ops);
+ rtnl_tc_unregister(&fw_ops);
}
/** @} */
diff --git a/lib/route/cls/police.c b/lib/route/cls/police.c
index 051c6b2a..1f5d2844 100644
--- a/lib/route/cls/police.c
+++ b/lib/route/cls/police.c
@@ -6,16 +6,15 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
-#include <netlink/route/tc.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/classifier.h>
-#include <netlink/route/classifier-modules.h>
#include <netlink/route/cls/police.h>
/**
@@ -23,7 +22,7 @@
* @{
*/
-static struct trans_tbl police_types[] = {
+static const struct trans_tbl police_types[] = {
__ADD(TC_POLICE_UNSPEC,unspec)
__ADD(TC_POLICE_OK,ok)
__ADD(TC_POLICE_RECLASSIFY,reclassify)
diff --git a/lib/route/cls/u32.c b/lib/route/cls/u32.c
index 80b88513..0a4e83cf 100644
--- a/lib/route/cls/u32.c
+++ b/lib/route/cls/u32.c
@@ -6,27 +6,27 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
* Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com>
* Copyright (c) 2005-2006 Siemens AG Oesterreich
*/
/**
- * @ingroup cls_api
- * @defgroup u32 Universal 32-bit Classifier
+ * @ingroup cls
+ * @defgroup cls_u32 Universal 32-bit Classifier
*
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/attr.h>
#include <netlink/utils.h>
-#include <netlink/route/tc.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/classifier.h>
-#include <netlink/route/classifier-modules.h>
#include <netlink/route/cls/u32.h>
+#include <netlink/route/action.h>
/** @cond SKIP */
#define U32_ATTR_DIVISOR 0x001
@@ -64,13 +64,13 @@ static struct nla_policy u32_policy[TCA_U32_MAX+1] = {
[TCA_U32_PCNT] = { .minlen = sizeof(struct tc_u32_pcnt) },
};
-static int u32_msg_parser(struct rtnl_cls *cls)
+static int u32_msg_parser(struct rtnl_tc *tc, void *data)
{
- struct rtnl_u32 *u = rtnl_cls_data(cls);
+ struct rtnl_u32 *u = data;
struct nlattr *tb[TCA_U32_MAX + 1];
int err;
- err = tca_parse(tb, TCA_U32_MAX, (struct rtnl_tca *) cls, u32_policy);
+ err = tca_parse(tb, TCA_U32_MAX, tc, u32_policy);
if (err < 0)
return err;
@@ -102,10 +102,10 @@ static int u32_msg_parser(struct rtnl_cls *cls)
}
if (tb[TCA_U32_ACT]) {
- u->cu_act = nl_data_alloc_attr(tb[TCA_U32_ACT]);
- if (!u->cu_act)
- goto errout_nomem;
u->cu_mask |= U32_ATTR_ACTION;
+ err = rtnl_act_parse(&u->cu_act, tb[TCA_U32_ACT]);
+ if (err)
+ return err;
}
if (tb[TCA_U32_POLICE]) {
@@ -117,7 +117,7 @@ static int u32_msg_parser(struct rtnl_cls *cls)
if (tb[TCA_U32_PCNT]) {
struct tc_u32_sel *sel;
- int pcnt_size;
+ size_t pcnt_size;
if (!tb[TCA_U32_SEL]) {
err = -NLE_MISSING_ATTR;
@@ -151,27 +151,31 @@ errout:
return err;
}
-static void u32_free_data(struct rtnl_cls *cls)
+static void u32_free_data(struct rtnl_tc *tc, void *data)
{
- struct rtnl_u32 *u = rtnl_cls_data(cls);
+ struct rtnl_u32 *u = data;
+ if (u->cu_act)
+ rtnl_act_put_all(&u->cu_act);
nl_data_free(u->cu_selector);
- nl_data_free(u->cu_act);
nl_data_free(u->cu_police);
nl_data_free(u->cu_pcnt);
}
-static int u32_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
+static int u32_clone(void *_dst, void *_src)
{
- struct rtnl_u32 *dst = rtnl_cls_data(_dst);
- struct rtnl_u32 *src = rtnl_cls_data(_src);
+ struct rtnl_u32 *dst = _dst, *src = _src;
if (src->cu_selector &&
!(dst->cu_selector = nl_data_clone(src->cu_selector)))
return -NLE_NOMEM;
- if (src->cu_act && !(dst->cu_act = nl_data_clone(src->cu_act)))
- return -NLE_NOMEM;
+ if (src->cu_act) {
+ if (!(dst->cu_act = rtnl_act_alloc()))
+ return -NLE_NOMEM;
+
+ memcpy(dst->cu_act, src->cu_act, sizeof(struct rtnl_act));
+ }
if (src->cu_police && !(dst->cu_police = nl_data_clone(src->cu_police)))
return -NLE_NOMEM;
@@ -182,10 +186,14 @@ static int u32_clone(struct rtnl_cls *_dst, struct rtnl_cls *_src)
return 0;
}
-static void u32_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
+static void u32_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_u32 *u = rtnl_cls_data(cls);
+ struct rtnl_u32 *u = data;
char buf[32];
+
+ if (!u)
+ return;
if (u->cu_mask & U32_ATTR_DIVISOR)
nl_dump(p, " divisor %u", u->cu_divisor);
@@ -195,7 +203,7 @@ static void u32_dump_line(struct rtnl_cls *cls, struct nl_dump_params *p)
}
static void print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel,
- struct rtnl_cls *cls, struct rtnl_u32 *u)
+ struct rtnl_u32 *u)
{
int i;
struct tc_u32_key *key;
@@ -253,11 +261,15 @@ static void print_selector(struct nl_dump_params *p, struct tc_u32_sel *sel,
}
}
-static void u32_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
+static void u32_dump_details(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_u32 *u = rtnl_cls_data(cls);
+ struct rtnl_u32 *u = data;
struct tc_u32_sel *s;
+ if (!u)
+ return;
+
if (!(u->cu_mask & U32_ATTR_SELECTOR)) {
nl_dump(p, "no-selector\n");
return;
@@ -277,33 +289,32 @@ static void u32_dump_details(struct rtnl_cls *cls, struct nl_dump_params *p)
if (u->cu_mask & U32_ATTR_INDEV)
nl_dump(p, "indev %s ", u->cu_indev);
- print_selector(p, s, cls, u);
+ print_selector(p, s, u);
nl_dump(p, "\n");
-
-#if 0
-#define U32_ATTR_ACTION 0x040
-#define U32_ATTR_POLICE 0x080
-
- struct nl_data act;
- struct nl_data police;
-#endif
}
-static void u32_dump_stats(struct rtnl_cls *cls, struct nl_dump_params *p)
+static void u32_dump_stats(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_u32 *u = rtnl_cls_data(cls);
+ struct rtnl_u32 *u = data;
+
+ if (!u)
+ return;
if (u->cu_mask & U32_ATTR_PCNT) {
struct tc_u32_pcnt *pc = u->cu_pcnt->d_data;
nl_dump(p, "\n");
- nl_dump_line(p, " hit %8llu count %8llu\n",
+ nl_dump_line(p, " hit %8" PRIu64 " count %8" PRIu64 "\n",
pc->rhit, pc->rcnt);
}
}
-static int u32_get_opts(struct rtnl_cls *cls, struct nl_msg *msg)
+static int u32_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
{
- struct rtnl_u32 *u = rtnl_cls_data(cls);
+ struct rtnl_u32 *u = data;
+
+ if (!u)
+ return 0;
if (u->cu_mask & U32_ATTR_DIVISOR)
NLA_PUT_U32(msg, TCA_U32_DIVISOR, u->cu_divisor);
@@ -320,8 +331,13 @@ static int u32_get_opts(struct rtnl_cls *cls, struct nl_msg *msg)
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_ACTION) {
+ int err;
+
+ err = rtnl_act_fill(msg, TCA_U32_ACT, u->cu_act);
+ if (err)
+ return err;
+ }
if (u->cu_mask & U32_ATTR_POLICE)
NLA_PUT_DATA(msg, TCA_U32_POLICE, u->cu_police);
@@ -345,12 +361,15 @@ void rtnl_u32_set_handle(struct rtnl_cls *cls, int htid, int hash,
{
uint32_t handle = (htid << 20) | (hash << 12) | nodeid;
- tca_set_handle((struct rtnl_tca *) cls, handle );
+ rtnl_tc_set_handle((struct rtnl_tc *) cls, handle );
}
int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid)
{
- struct rtnl_u32 *u = rtnl_cls_data(cls);
+ struct rtnl_u32 *u;
+
+ if (!(u = rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
u->cu_classid = classid;
u->cu_mask |= U32_ATTR_CLASSID;
@@ -358,6 +377,130 @@ int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid)
return 0;
}
+int rtnl_u32_set_divisor(struct rtnl_cls *cls, uint32_t divisor)
+{
+ struct rtnl_u32 *u;
+
+ if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
+
+ u->cu_divisor = divisor;
+ u->cu_mask |= U32_ATTR_DIVISOR;
+ return 0;
+}
+
+int rtnl_u32_set_link(struct rtnl_cls *cls, uint32_t link)
+{
+ struct rtnl_u32 *u;
+
+ if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
+
+ u->cu_link = link;
+ u->cu_mask |= U32_ATTR_LINK;
+ return 0;
+}
+
+int rtnl_u32_set_hashtable(struct rtnl_cls *cls, uint32_t ht)
+{
+ struct rtnl_u32 *u;
+
+ if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
+
+ u->cu_hash = ht;
+ u->cu_mask |= U32_ATTR_HASH;
+ return 0;
+}
+
+int rtnl_u32_set_hashmask(struct rtnl_cls *cls, uint32_t hashmask, uint32_t offset)
+{
+ struct rtnl_u32 *u;
+ struct tc_u32_sel *sel;
+ int err;
+
+ hashmask = htonl(hashmask);
+
+ if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
+
+ sel = u32_selector_alloc(u);
+ if (!sel)
+ return -NLE_NOMEM;
+
+ err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
+ if(err < 0)
+ return err;
+
+ sel = u32_selector(u);
+
+ sel->hmask = hashmask;
+ sel->hoff = offset;
+ return 0;
+}
+
+int rtnl_u32_set_cls_terminal(struct rtnl_cls *cls)
+{
+ struct rtnl_u32 *u;
+ struct tc_u32_sel *sel;
+ int err;
+
+ if (!(u = (struct rtnl_u32 *) rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
+
+ sel = u32_selector_alloc(u);
+ if (!sel)
+ return -NLE_NOMEM;
+
+ err = nl_data_append(u->cu_selector, NULL, sizeof(struct tc_u32_key));
+ if(err < 0)
+ return err;
+
+ sel = u32_selector(u);
+
+ sel->flags |= TC_U32_TERMINAL;
+ return 0;
+}
+
+int rtnl_u32_add_action(struct rtnl_cls *cls, struct rtnl_act *act)
+{
+ struct rtnl_u32 *u;
+
+ if (!act)
+ return 0;
+
+ if (!(u = rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
+
+ u->cu_mask |= U32_ATTR_ACTION;
+ /* In case user frees it */
+ rtnl_act_get(act);
+ return rtnl_act_append(&u->cu_act, act);
+}
+
+int rtnl_u32_del_action(struct rtnl_cls *cls, struct rtnl_act *act)
+{
+ struct rtnl_u32 *u;
+ int ret;
+
+ if (!act)
+ return 0;
+
+ if (!(u = rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
+
+ if (!(u->cu_mask & U32_ATTR_ACTION))
+ return -NLE_INVAL;
+
+ ret = rtnl_act_remove(&u->cu_act, act);
+ if (ret)
+ return ret;
+
+ if (!u->cu_act)
+ u->cu_mask &= ~U32_ATTR_ACTION;
+ rtnl_act_put(act);
+ return 0;
+}
/** @} */
/**
@@ -368,7 +511,10 @@ int rtnl_u32_set_classid(struct rtnl_cls *cls, uint32_t classid)
int rtnl_u32_set_flags(struct rtnl_cls *cls, int flags)
{
struct tc_u32_sel *sel;
- struct rtnl_u32 *u = rtnl_cls_data(cls);
+ struct rtnl_u32 *u;
+
+ if (!(u = rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
sel = u32_selector_alloc(u);
if (!sel)
@@ -398,9 +544,12 @@ 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 = rtnl_cls_data(cls);
+ struct rtnl_u32 *u;
int err;
+ if (!(u = rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
+
sel = u32_selector_alloc(u);
if (!sel)
return -NLE_NOMEM;
@@ -422,6 +571,42 @@ int rtnl_u32_add_key(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
return 0;
}
+/**
+ * Get the 32-bit key from the selector
+ *
+ * @arg cls classifier to be retrieve
+ * @arg index the index of the array of keys, start with 0
+ * @arg val pointer to store value after masked (network byte-order)
+ * @arg mask pointer to store the mask (network byte-order)
+ * @arg off pointer to store the offset
+ * @arg offmask pointer to store offset mask
+ *
+*/
+int rtnl_u32_get_key(struct rtnl_cls *cls, uint8_t index,
+ uint32_t *val, uint32_t *mask, int *off, int *offmask)
+{
+ struct tc_u32_sel *sel;
+ struct rtnl_u32 *u;
+
+ if (!(u = rtnl_tc_data(TC_CAST(cls))))
+ return -NLE_NOMEM;
+
+ if (!(u->cu_mask & U32_ATTR_SELECTOR))
+ return -NLE_INVAL;
+
+ /* the selector might have been moved by realloc */
+ sel = u32_selector(u);
+ if (index >= sel->nkeys)
+ return -NLE_RANGE;
+
+ *mask = sel->keys[index].mask;
+ *val = sel->keys[index].val;
+ *off = sel->keys[index].off;
+ *offmask = sel->keys[index].offmask;
+ return 0;
+}
+
+
int rtnl_u32_add_key_uint8(struct rtnl_cls *cls, uint8_t val, uint8_t mask,
int off, int offmask)
{
@@ -469,14 +654,14 @@ int rtnl_u32_add_key_uint32(struct rtnl_cls *cls, uint32_t val, uint32_t mask,
off & ~3, offmask);
}
-int rtnl_u32_add_key_in_addr(struct rtnl_cls *cls, struct in_addr *addr,
+int rtnl_u32_add_key_in_addr(struct rtnl_cls *cls, const 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,
+int rtnl_u32_add_key_in6_addr(struct rtnl_cls *cls, const struct in6_addr *addr,
uint8_t bitmask, int off, int offmask)
{
int i, err;
@@ -501,14 +686,15 @@ int rtnl_u32_add_key_in6_addr(struct rtnl_cls *cls, struct in6_addr *addr,
/** @} */
-static struct rtnl_cls_ops u32_ops = {
- .co_kind = "u32",
- .co_size = sizeof(struct rtnl_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 = {
+static struct rtnl_tc_ops u32_ops = {
+ .to_kind = "u32",
+ .to_type = RTNL_TC_TYPE_CLS,
+ .to_size = sizeof(struct rtnl_u32),
+ .to_msg_parser = u32_msg_parser,
+ .to_free_data = u32_free_data,
+ .to_clone = u32_clone,
+ .to_msg_fill = u32_msg_fill,
+ .to_dump = {
[NL_DUMP_LINE] = u32_dump_line,
[NL_DUMP_DETAILS] = u32_dump_details,
[NL_DUMP_STATS] = u32_dump_stats,
@@ -517,12 +703,12 @@ static struct rtnl_cls_ops u32_ops = {
static void __init u32_init(void)
{
- rtnl_cls_register(&u32_ops);
+ rtnl_tc_register(&u32_ops);
}
static void __exit u32_exit(void)
{
- rtnl_cls_unregister(&u32_ops);
+ rtnl_tc_unregister(&u32_ops);
}
/** @} */
diff --git a/lib/route/cls_api.c b/lib/route/cls_api.c
deleted file mode 100644
index 73f05dfb..00000000
--- a/lib/route/cls_api.c
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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-2008 Thomas Graf <tgraf@suug.ch>
- */
-
-/**
- * @ingroup cls
- * @defgroup cls_api Classifier Modules
- * @{
- */
-
-#include <netlink-local.h>
-#include <netlink-tc.h>
-#include <netlink/netlink.h>
-#include <netlink/utils.h>
-#include <netlink/route/tc.h>
-#include <netlink/route/classifier.h>
-#include <netlink/route/classifier-modules.h>
-#include <netlink/route/link.h>
-
-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 -NLE_EXIST;
-
- 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 -NLE_OBJ_NOTFOUND;
-
- *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
deleted file mode 100644
index c8218c07..00000000
--- a/lib/route/cls_obj.c
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * 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 <tgraf@suug.ch>
- */
-
-/**
- * @ingroup cls
- * @defgroup cls_obj Classifier Object
- * @{
- */
-
-#include <netlink-local.h>
-#include <netlink-tc.h>
-#include <netlink/netlink.h>
-#include <netlink/utils.h>
-#include <netlink/route/tc.h>
-#include <netlink/route/classifier.h>
-#include <netlink/route/classifier-modules.h>
-#include <netlink/route/link.h>
-
-/** @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);
-
- nl_data_free(cls->c_subdata);
-}
-
-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;
-
- if (src->c_subdata) {
- if (!(dst->c_subdata = nl_data_clone(src->c_subdata))) {
- err = -NLE_NOMEM;
- goto errout;
- }
- }
-
- cops = rtnl_cls_lookup_ops(src);
- if (cops && cops->co_clone)
- err = cops->co_clone(dst, src);
-errout:
- return err;
-}
-
-static void cls_dump_line(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;
-
- tca_dump_line((struct rtnl_tca *) cls, "cls", p);
-
- nl_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_LINE])
- cops->co_dump[NL_DUMP_LINE](cls, p);
- nl_dump(p, "\n");
-}
-
-static void cls_dump_details(struct nl_object *obj, struct nl_dump_params *p)
-{
- struct rtnl_cls *cls = (struct rtnl_cls *) obj;
- struct rtnl_cls_ops *cops;
-
- cls_dump_line(obj, p);
- tca_dump_details((struct rtnl_tca *) cls, p);
-
- cops = rtnl_cls_lookup_ops(cls);
- if (cops && cops->co_dump[NL_DUMP_DETAILS])
- cops->co_dump[NL_DUMP_DETAILS](cls, p);
- else
- nl_dump(p, "no options\n");
-}
-
-static void 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;
-
- cls_dump_details(obj, p);
- tca_dump_stats((struct rtnl_tca *) cls, p);
- nl_dump(p, "\n");
-
- cops = rtnl_cls_lookup_ops(cls);
- if (cops && cops->co_dump[NL_DUMP_STATS])
- cops->co_dump[NL_DUMP_STATS](cls, p);
-}
-
-/**
- * @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);
-}
-
-int rtnl_cls_get_ifindex(struct rtnl_cls *cls)
-{
- return cls->c_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);
-}
-
-uint32_t rtnl_cls_get_parent(struct rtnl_cls *cls)
-{
- return cls->c_parent;
-}
-
-int rtnl_cls_set_kind(struct rtnl_cls *cls, const char *kind)
-{
- if (cls->ce_mask & TCA_ATTR_KIND)
- return -NLE_EXIST;
-
- tca_set_kind((struct rtnl_tca *) cls, kind);
-
- /* Force allocation of data */
- rtnl_cls_data(cls);
-
- return 0;
-}
-
-struct rtnl_cls_ops *rtnl_cls_get_ops(struct rtnl_cls *cls)
-{
- return cls->c_ops;
-}
-
-void rtnl_cls_set_prio(struct rtnl_cls *cls, uint16_t prio)
-{
- cls->c_prio = prio;
- cls->ce_mask |= CLS_ATTR_PRIO;
-}
-
-uint16_t 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, uint16_t protocol)
-{
- cls->c_protocol = protocol;
- cls->ce_mask |= CLS_ATTR_PROTOCOL;
-}
-
-uint16_t rtnl_cls_get_protocol(struct rtnl_cls *cls)
-{
- if (cls->ce_mask & CLS_ATTR_PROTOCOL)
- return cls->c_protocol;
- else
- return ETH_P_ALL;
-}
-
-void *rtnl_cls_data(struct rtnl_cls *cls)
-{
- if (!cls->c_subdata) {
- struct rtnl_cls_ops *ops = cls->c_ops;
-
- if (!ops) {
- if (!cls->c_kind[0])
- BUG();
-
- ops = __rtnl_cls_lookup_ops(cls->c_kind);
- if (ops == NULL)
- return NULL;
-
- cls->c_ops = ops;
- }
-
- if (!ops->co_size)
- BUG();
-
- if (!(cls->c_subdata = nl_data_alloc(NULL, ops->co_size)))
- return NULL;
- }
-
- return nl_data_get(cls->c_subdata);
-}
-
-/** @} */
-
-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_LINE] = cls_dump_line,
- [NL_DUMP_DETAILS] = cls_dump_details,
- [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
index cf488e5a..3d31ffcb 100644
--- a/lib/route/link.c
+++ b/lib/route/link.c
@@ -6,188 +6,199 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
* @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(sk);
- *
- * // 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(sk, 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(sk, nlmsg_hdr(msg));
- * nlmsg_free(msg);
- *
- * // Don't forget to give back the link object ;->
- * rtnl_link_put(old);
- * @endcode
- *
- * @par 3) Link Type Specific Attributes
- * @code
- * // Some link types offer additional parameters and statistics specific
- * // to their type. F.e. a VLAN link can be configured like this:
- * //
- * // Allocate a new link and set the info type to "vlan". This is required
- * // to prepare the link to hold vlan specific attributes.
- * struct rtnl_link *request = rtnl_link_alloc();
- * rtnl_link_set_info_type(request, "vlan");
- *
- * // Now vlan specific attributes can be set:
- * rtnl_link_vlan_set_id(request, 10);
- * rtnl_link_vlan_set_ingress_map(request, 2, 8);
- *
- * // Of course the attributes can also be read, check the info type
- * // to make sure you are using the right access functions:
- * char *type = rtnl_link_get_info_type(link);
- * if (!strcmp(type, "vlan"))
- * int id = rtnl_link_vlan_get_id(link);
- * @endcode
+ *
+ * @details
+ * @route_doc{route_link, Link Documentation}
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/attr.h>
#include <netlink/utils.h>
#include <netlink/object.h>
+#include <netlink/hashtable.h>
+#include <netlink/data.h>
#include <netlink/route/rtnl.h>
#include <netlink/route/link.h>
-#include <netlink/route/link/info-api.h>
+#include <netlink-private/route/link/api.h>
/** @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
-#define LINK_ATTR_OPERSTATE 0x10000
-#define LINK_ATTR_LINKMODE 0x20000
-#define LINK_ATTR_LINKINFO 0x40000
+#define LINK_ATTR_MTU (1 << 0)
+#define LINK_ATTR_LINK (1 << 1)
+#define LINK_ATTR_TXQLEN (1 << 2)
+#define LINK_ATTR_WEIGHT (1 << 3)
+#define LINK_ATTR_MASTER (1 << 4)
+#define LINK_ATTR_QDISC (1 << 5)
+#define LINK_ATTR_MAP (1 << 6)
+#define LINK_ATTR_ADDR (1 << 7)
+#define LINK_ATTR_BRD (1 << 8)
+#define LINK_ATTR_FLAGS (1 << 9)
+#define LINK_ATTR_IFNAME (1 << 10)
+#define LINK_ATTR_IFINDEX (1 << 11)
+#define LINK_ATTR_FAMILY (1 << 12)
+#define LINK_ATTR_ARPTYPE (1 << 13)
+#define LINK_ATTR_STATS (1 << 14)
+#define LINK_ATTR_CHANGE (1 << 15)
+#define LINK_ATTR_OPERSTATE (1 << 16)
+#define LINK_ATTR_LINKMODE (1 << 17)
+#define LINK_ATTR_LINKINFO (1 << 18)
+#define LINK_ATTR_IFALIAS (1 << 19)
+#define LINK_ATTR_NUM_VF (1 << 20)
+#define LINK_ATTR_PROMISCUITY (1 << 21)
+#define LINK_ATTR_NUM_TX_QUEUES (1 << 22)
+#define LINK_ATTR_NUM_RX_QUEUES (1 << 23)
+#define LINK_ATTR_GROUP (1 << 24)
+#define LINK_ATTR_CARRIER (1 << 25)
+#define LINK_ATTR_PROTINFO (1 << 26)
+#define LINK_ATTR_AF_SPEC (1 << 27)
+#define LINK_ATTR_PHYS_PORT_ID (1 << 28)
+#define LINK_ATTR_NS_FD (1 << 29)
+#define LINK_ATTR_NS_PID (1 << 30)
static struct nl_cache_ops rtnl_link_ops;
static struct nl_object_ops link_obj_ops;
/** @endcond */
+static struct rtnl_link_af_ops *af_lookup_and_alloc(struct rtnl_link *link,
+ int family)
+{
+ struct rtnl_link_af_ops *af_ops;
+ void *data;
+
+ af_ops = rtnl_link_af_ops_lookup(family);
+ if (!af_ops)
+ return NULL;
+
+ if (!(data = rtnl_link_af_alloc(link, af_ops))) {
+ rtnl_link_af_ops_put(af_ops);
+ return NULL;
+ }
+
+ return af_ops;
+}
+
+static int af_free(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
+ void *data, void *arg)
+{
+ if (ops->ao_free)
+ ops->ao_free(link, data);
+
+ rtnl_link_af_ops_put(ops);
+
+ return 0;
+}
+
+static int af_clone(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
+ void *data, void *arg)
+{
+ struct rtnl_link *dst = arg;
+
+ if (ops->ao_clone &&
+ !(dst->l_af_data[ops->ao_family] = ops->ao_clone(dst, data)))
+ return -NLE_NOMEM;
+
+ return 0;
+}
+
+static int af_fill(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
+ void *data, void *arg)
+{
+ struct nl_msg *msg = arg;
+ struct nlattr *af_attr;
+ int err;
+
+ if (!ops->ao_fill_af)
+ return 0;
+
+ if (!(af_attr = nla_nest_start(msg, ops->ao_family)))
+ return -NLE_MSGSIZE;
+
+ if ((err = ops->ao_fill_af(link, arg, data)) < 0)
+ return err;
+
+ nla_nest_end(msg, af_attr);
+
+ return 0;
+}
+
+static int af_dump_line(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
+ void *data, void *arg)
+{
+ struct nl_dump_params *p = arg;
+
+ if (ops->ao_dump[NL_DUMP_LINE])
+ ops->ao_dump[NL_DUMP_LINE](link, p, data);
+
+ return 0;
+}
+
+static int af_dump_details(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
+ void *data, void *arg)
+{
+ struct nl_dump_params *p = arg;
+
+ if (ops->ao_dump[NL_DUMP_DETAILS])
+ ops->ao_dump[NL_DUMP_DETAILS](link, p, data);
+
+ return 0;
+}
+
+static int af_dump_stats(struct rtnl_link *link, struct rtnl_link_af_ops *ops,
+ void *data, void *arg)
+{
+ struct nl_dump_params *p = arg;
+
+ if (ops->ao_dump[NL_DUMP_STATS])
+ ops->ao_dump[NL_DUMP_STATS](link, p, data);
+
+ return 0;
+}
+
+static int do_foreach_af(struct rtnl_link *link,
+ int (*cb)(struct rtnl_link *,
+ struct rtnl_link_af_ops *, void *, void *),
+ void *arg)
+{
+ int i, err;
+
+ for (i = 0; i < AF_MAX; i++) {
+ if (link->l_af_data[i]) {
+ struct rtnl_link_af_ops *ops;
+
+ if (!(ops = rtnl_link_af_ops_lookup(i)))
+ BUG();
+
+ err = cb(link, ops, link->l_af_data[i], arg);
+
+ rtnl_link_af_ops_put(ops);
+
+ if (err < 0)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
static void release_link_info(struct rtnl_link *link)
{
struct rtnl_link_info_ops *io = link->l_info_ops;
if (io != NULL) {
- io->io_refcnt--;
- io->io_free(link);
+ if (io->io_free)
+ io->io_free(link);
+ else {
+ /* Catch missing io_free() implementations */
+ BUG_ON(link->l_info);
+ }
+ rtnl_link_info_ops_put(io);
link->l_info_ops = NULL;
}
}
@@ -202,8 +213,18 @@ static void link_free_data(struct nl_object *c)
if ((io = link->l_info_ops) != NULL)
release_link_info(link);
+ /* proto info af reference */
+ rtnl_link_af_ops_put(link->l_af_ops);
+
nl_addr_put(link->l_addr);
nl_addr_put(link->l_bcast);
+
+ free(link->l_ifalias);
+ free(link->l_info_kind);
+
+ do_foreach_af(link, af_free, NULL);
+
+ nl_data_free(link->l_phys_port_id);
}
}
@@ -221,30 +242,57 @@ static int link_clone(struct nl_object *_dst, struct nl_object *_src)
if (!(dst->l_bcast = nl_addr_clone(src->l_bcast)))
return -NLE_NOMEM;
+ if (src->l_ifalias)
+ if (!(dst->l_ifalias = strdup(src->l_ifalias)))
+ return -NLE_NOMEM;
+
+ if (src->l_info_kind)
+ if (!(dst->l_info_kind = strdup(src->l_info_kind)))
+ return -NLE_NOMEM;
+
if (src->l_info_ops && src->l_info_ops->io_clone) {
err = src->l_info_ops->io_clone(dst, src);
if (err < 0)
return err;
}
+ if ((err = do_foreach_af(src, af_clone, dst)) < 0)
+ return err;
+
+ if (src->l_phys_port_id)
+ if (!(dst->l_phys_port_id = nl_data_clone(src->l_phys_port_id)))
+ return -NLE_NOMEM;
+
return 0;
}
-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_OPERSTATE]= { .type = NLA_U8 },
- [IFLA_LINKMODE] = { .type = NLA_U8 },
- [IFLA_LINKINFO] = { .type = NLA_NESTED },
- [IFLA_QDISC] = { .type = NLA_STRING,
- .maxlen = IFQDISCSIZ },
- [IFLA_STATS] = { .minlen = sizeof(struct rtnl_link_stats) },
- [IFLA_MAP] = { .minlen = sizeof(struct rtnl_link_ifmap) },
+struct nla_policy rtln_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_OPERSTATE] = { .type = NLA_U8 },
+ [IFLA_LINKMODE] = { .type = NLA_U8 },
+ [IFLA_LINKINFO] = { .type = NLA_NESTED },
+ [IFLA_QDISC] = { .type = NLA_STRING,
+ .maxlen = IFQDISCSIZ },
+ [IFLA_STATS] = { .minlen = sizeof(struct rtnl_link_stats) },
+ [IFLA_STATS64] = { .minlen = sizeof(struct rtnl_link_stats64)},
+ [IFLA_MAP] = { .minlen = sizeof(struct rtnl_link_ifmap) },
+ [IFLA_IFALIAS] = { .type = NLA_STRING, .maxlen = IFALIASZ },
+ [IFLA_NUM_VF] = { .type = NLA_U32 },
+ [IFLA_AF_SPEC] = { .type = NLA_NESTED },
+ [IFLA_PROMISCUITY] = { .type = NLA_U32 },
+ [IFLA_NUM_TX_QUEUES] = { .type = NLA_U32 },
+ [IFLA_NUM_RX_QUEUES] = { .type = NLA_U32 },
+ [IFLA_GROUP] = { .type = NLA_U32 },
+ [IFLA_CARRIER] = { .type = NLA_U8 },
+ [IFLA_PHYS_PORT_ID] = { .type = NLA_UNSPEC },
+ [IFLA_NET_NS_PID] = { .type = NLA_U32 },
+ [IFLA_NET_NS_FD] = { .type = NLA_U32 },
};
static struct nla_policy link_info_policy[IFLA_INFO_MAX+1] = {
@@ -253,68 +301,86 @@ static struct nla_policy link_info_policy[IFLA_INFO_MAX+1] = {
[IFLA_INFO_XSTATS] = { .type = NLA_NESTED },
};
-static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
- struct nlmsghdr *n, struct nl_parser_param *pp)
+int rtnl_link_info_parse(struct rtnl_link *link, struct nlattr **tb)
{
- struct rtnl_link *link;
- struct ifinfomsg *ifi;
- struct nlattr *tb[IFLA_MAX+1];
- int err;
-
- link = rtnl_link_alloc();
- if (link == NULL) {
- err = -NLE_NOMEM;
- 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 = -NLE_MISSING_ATTR;
- goto errout;
- }
+ if (tb[IFLA_IFNAME] == NULL)
+ return -NLE_MISSING_ATTR;
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_RX_BYTES] = st->rx_bytes;
link->l_stats[RTNL_LINK_TX_BYTES] = st->tx_bytes;
+ link->l_stats[RTNL_LINK_RX_ERRORS] = st->rx_errors;
link->l_stats[RTNL_LINK_TX_ERRORS] = st->tx_errors;
+ link->l_stats[RTNL_LINK_RX_DROPPED] = st->rx_dropped;
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_MULTICAST] = st->multicast;
+ link->l_stats[RTNL_LINK_COLLISIONS] = st->collisions;
+
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_FIFO_ERR] = st->rx_fifo_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_FIFO_ERR] = st->tx_fifo_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->l_stats[RTNL_LINK_RX_COMPRESSED] = st->rx_compressed;
+ link->l_stats[RTNL_LINK_TX_COMPRESSED] = st->tx_compressed;
+
+ link->ce_mask |= LINK_ATTR_STATS;
+ }
+
+ if (tb[IFLA_STATS64]) {
+ /*
+ * This structure contains 64bit parameters, and per the
+ * documentation in lib/attr.c, must not be accessed
+ * directly (because of alignment to 4 instead of 8).
+ * Therefore, copy the data to the stack and access it from
+ * there, where it will be aligned to 8.
+ */
+ struct rtnl_link_stats64 st;
+
+ nla_memcpy(&st, tb[IFLA_STATS64],
+ sizeof(struct rtnl_link_stats64));
+
+ link->l_stats[RTNL_LINK_RX_PACKETS] = st.rx_packets;
+ link->l_stats[RTNL_LINK_TX_PACKETS] = st.tx_packets;
+ link->l_stats[RTNL_LINK_RX_BYTES] = st.rx_bytes;
+ link->l_stats[RTNL_LINK_TX_BYTES] = st.tx_bytes;
+ link->l_stats[RTNL_LINK_RX_ERRORS] = st.rx_errors;
+ link->l_stats[RTNL_LINK_TX_ERRORS] = st.tx_errors;
+ link->l_stats[RTNL_LINK_RX_DROPPED] = st.rx_dropped;
+ link->l_stats[RTNL_LINK_TX_DROPPED] = st.tx_dropped;
+ link->l_stats[RTNL_LINK_MULTICAST] = st.multicast;
+ link->l_stats[RTNL_LINK_COLLISIONS] = st.collisions;
+
+ 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_FIFO_ERR] = st.rx_fifo_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_FIFO_ERR] = st.tx_fifo_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_RX_COMPRESSED] = st.rx_compressed;
+ link->l_stats[RTNL_LINK_TX_COMPRESSED] = st.tx_compressed;
link->ce_mask |= LINK_ATTR_STATS;
}
@@ -331,10 +397,8 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
if (tb[IFLA_ADDRESS]) {
link->l_addr = nl_addr_alloc_attr(tb[IFLA_ADDRESS], AF_UNSPEC);
- if (link->l_addr == NULL) {
- err = -NLE_NOMEM;
- goto errout;
- }
+ if (link->l_addr == NULL)
+ return -NLE_NOMEM;
nl_addr_set_family(link->l_addr,
nl_addr_guess_family(link->l_addr));
link->ce_mask |= LINK_ATTR_ADDR;
@@ -343,10 +407,8 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
if (tb[IFLA_BROADCAST]) {
link->l_bcast = nl_addr_alloc_attr(tb[IFLA_BROADCAST],
AF_UNSPEC);
- if (link->l_bcast == NULL) {
- err = -NLE_NOMEM;
- goto errout;
- }
+ if (link->l_bcast == NULL)
+ return -NLE_NOMEM;
nl_addr_set_family(link->l_bcast,
nl_addr_guess_family(link->l_bcast));
link->ce_mask |= LINK_ATTR_BRD;
@@ -378,6 +440,11 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
link->ce_mask |= LINK_ATTR_MASTER;
}
+ if (tb[IFLA_CARRIER]) {
+ link->l_carrier = nla_get_u8(tb[IFLA_CARRIER]);
+ link->ce_mask |= LINK_ATTR_CARRIER;
+ }
+
if (tb[IFLA_OPERSTATE]) {
link->l_operstate = nla_get_u8(tb[IFLA_OPERSTATE]);
link->ce_mask |= LINK_ATTR_OPERSTATE;
@@ -388,6 +455,82 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
link->ce_mask |= LINK_ATTR_LINKMODE;
}
+ if (tb[IFLA_IFALIAS]) {
+ link->l_ifalias = nla_strdup(tb[IFLA_IFALIAS]);
+ if (link->l_ifalias == NULL)
+ return -NLE_NOMEM;
+ link->ce_mask |= LINK_ATTR_IFALIAS;
+ }
+
+ if (tb[IFLA_NET_NS_FD]) {
+ link->l_ns_fd = nla_get_u32(tb[IFLA_NET_NS_FD]);
+ link->ce_mask |= LINK_ATTR_NS_FD;
+ }
+
+ if (tb[IFLA_NET_NS_PID]) {
+ link->l_ns_pid = nla_get_u32(tb[IFLA_NET_NS_PID]);
+ link->ce_mask |= LINK_ATTR_NS_PID;
+ }
+
+ return 0;
+}
+
+static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+ struct nlmsghdr *n, struct nl_parser_param *pp)
+{
+ struct rtnl_link *link;
+ struct ifinfomsg *ifi;
+ struct nlattr *tb[IFLA_MAX+1];
+ struct rtnl_link_af_ops *af_ops = NULL;
+ int err, family;
+ struct nla_policy real_link_policy[IFLA_MAX+1];
+
+ memcpy(&real_link_policy, rtln_link_policy, sizeof(rtln_link_policy));
+
+ link = rtnl_link_alloc();
+ if (link == NULL) {
+ err = -NLE_NOMEM;
+ goto errout;
+ }
+
+ link->ce_msgtype = n->nlmsg_type;
+
+ if (!nlmsg_valid_hdr(n, sizeof(*ifi)))
+ return -NLE_MSG_TOOSHORT;
+
+ ifi = nlmsg_data(n);
+ link->l_family = 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 ((af_ops = af_lookup_and_alloc(link, family))) {
+ if (af_ops->ao_protinfo_policy) {
+ memcpy(&real_link_policy[IFLA_PROTINFO],
+ af_ops->ao_protinfo_policy,
+ sizeof(struct nla_policy));
+ }
+
+ link->l_af_ops = af_ops;
+ }
+
+ err = nlmsg_parse(n, sizeof(*ifi), tb, IFLA_MAX, real_link_policy);
+ if (err < 0)
+ return err;
+
+ err = rtnl_link_info_parse(link, tb);
+ if (err < 0)
+ return err;
+
+ if (tb[IFLA_NUM_VF]) {
+ link->l_num_vf = nla_get_u32(tb[IFLA_NUM_VF]);
+ link->ce_mask |= LINK_ATTR_NUM_VF;
+ }
+
if (tb[IFLA_LINKINFO]) {
struct nlattr *li[IFLA_INFO_MAX+1];
@@ -396,42 +539,123 @@ static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
if (err < 0)
goto errout;
- if (li[IFLA_INFO_KIND] &&
- (li[IFLA_INFO_DATA] || li[IFLA_INFO_XSTATS])) {
+ if (li[IFLA_INFO_KIND]) {
struct rtnl_link_info_ops *ops;
- char *kind;
+ char *kind = nla_get_string(li[IFLA_INFO_KIND]);
+ int af;
+
+ err = rtnl_link_set_type(link, kind);
+ if (err < 0)
+ goto errout;
+
+ if ((af = nl_str2af(kind)) >= 0 &&
+ !af_ops && (af_ops = af_lookup_and_alloc(link, af))) {
+
+ if (af_ops->ao_protinfo_policy) {
+ tb[IFLA_PROTINFO] = (struct nlattr *)af_ops->ao_protinfo_policy;
+ }
+ link->l_family = family = af;
+ link->l_af_ops = af_ops;
+ }
- kind = nla_get_string(li[IFLA_INFO_KIND]);
ops = rtnl_link_info_ops_lookup(kind);
- if (ops != NULL) {
- ops->io_refcnt++;
- link->l_info_ops = ops;
- err = ops->io_parse(link, li[IFLA_INFO_DATA],
- li[IFLA_INFO_XSTATS]);
+ link->l_info_ops = ops;
+
+ if (ops) {
+ if (ops->io_parse &&
+ (li[IFLA_INFO_DATA] || li[IFLA_INFO_XSTATS])) {
+ err = ops->io_parse(link, li[IFLA_INFO_DATA],
+ li[IFLA_INFO_XSTATS]);
+ if (err < 0)
+ goto errout;
+ } else {
+ /* XXX: Warn about unparsed info? */
+ }
+ }
+ }
+ link->ce_mask |= LINK_ATTR_LINKINFO;
+ }
+
+ if (tb[IFLA_PROTINFO] && af_ops && af_ops->ao_parse_protinfo) {
+ err = af_ops->ao_parse_protinfo(link, tb[IFLA_PROTINFO],
+ link->l_af_data[link->l_family]);
+ if (err < 0)
+ goto errout;
+ link->ce_mask |= LINK_ATTR_PROTINFO;
+ }
+
+ if (tb[IFLA_AF_SPEC]) {
+ struct nlattr *af_attr;
+ int remaining;
+
+ nla_for_each_nested(af_attr, tb[IFLA_AF_SPEC], remaining) {
+ af_ops = af_lookup_and_alloc(link, nla_type(af_attr));
+ if (af_ops && af_ops->ao_parse_af) {
+ char *af_data = link->l_af_data[nla_type(af_attr)];
+
+ err = af_ops->ao_parse_af(link, af_attr, af_data);
if (err < 0)
goto errout;
- } else {
- /* XXX: Warn about unparsed info? */
}
+
}
+ link->ce_mask |= LINK_ATTR_AF_SPEC;
+ }
+
+ if (tb[IFLA_PROMISCUITY]) {
+ link->l_promiscuity = nla_get_u32(tb[IFLA_PROMISCUITY]);
+ link->ce_mask |= LINK_ATTR_PROMISCUITY;
+ }
+
+ if (tb[IFLA_NUM_TX_QUEUES]) {
+ link->l_num_tx_queues = nla_get_u32(tb[IFLA_NUM_TX_QUEUES]);
+ link->ce_mask |= LINK_ATTR_NUM_TX_QUEUES;
+ }
+
+ if (tb[IFLA_NUM_RX_QUEUES]) {
+ link->l_num_rx_queues = nla_get_u32(tb[IFLA_NUM_RX_QUEUES]);
+ link->ce_mask |= LINK_ATTR_NUM_RX_QUEUES;
+ }
+
+ if (tb[IFLA_GROUP]) {
+ link->l_group = nla_get_u32(tb[IFLA_GROUP]);
+ link->ce_mask |= LINK_ATTR_GROUP;
+ }
+
+ if (tb[IFLA_PHYS_PORT_ID]) {
+ link->l_phys_port_id = nl_data_alloc_attr(tb[IFLA_PHYS_PORT_ID]);
+ if (link->l_phys_port_id == NULL) {
+ err = -NLE_NOMEM;
+ goto errout;
+ }
+ link->ce_mask |= LINK_ATTR_PHYS_PORT_ID;
}
err = pp->pp_cb((struct nl_object *) link, pp);
errout:
+ rtnl_link_af_ops_put(af_ops);
rtnl_link_put(link);
return err;
}
static int link_request_update(struct nl_cache *cache, struct nl_sock *sk)
{
- return nl_rtgen_request(sk, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP);
+ int family = cache->c_iarg1;
+
+ return nl_rtgen_request(sk, RTM_GETLINK, family, NLM_F_DUMP);
}
static void link_dump_line(struct nl_object *obj, struct nl_dump_params *p)
{
char buf[128];
- struct nl_cache *cache = dp_cache(obj);
+ struct nl_cache *cache = obj->ce_cache;
struct rtnl_link *link = (struct rtnl_link *) obj;
+ int fetched_cache = 0;
+
+ if (!cache) {
+ cache = nl_cache_mngt_require_safe("route/link");
+ fetched_cache = 1;
+ }
nl_dump_line(p, "%s %s ", link->l_name,
nl_llproto2str(link->l_arptype, buf, sizeof(buf)));
@@ -440,10 +664,13 @@ static void link_dump_line(struct nl_object *obj, struct nl_dump_params *p)
nl_dump(p, "%s ", nl_addr2str(link->l_addr, buf, sizeof(buf)));
if (link->ce_mask & LINK_ATTR_MASTER) {
- struct rtnl_link *master = rtnl_link_get(cache, link->l_master);
- nl_dump(p, "master %s ", master ? master->l_name : "inv");
- if (master)
- rtnl_link_put(master);
+ if (cache) {
+ struct rtnl_link *master = rtnl_link_get(cache, link->l_master);
+ nl_dump(p, "master %s ", master ? master->l_name : "inv");
+ if (master)
+ rtnl_link_put(master);
+ } else
+ nl_dump(p, "master %d ", link->l_master);
}
rtnl_link_flags2str(link->l_flags, buf, sizeof(buf));
@@ -451,16 +678,27 @@ static void link_dump_line(struct nl_object *obj, struct nl_dump_params *p)
nl_dump(p, "<%s> ", buf);
if (link->ce_mask & LINK_ATTR_LINK) {
- struct rtnl_link *ll = rtnl_link_get(cache, link->l_link);
- nl_dump(p, "slave-of %s ", ll ? ll->l_name : "NONE");
- if (ll)
- rtnl_link_put(ll);
+ if (cache) {
+ struct rtnl_link *ll = rtnl_link_get(cache, link->l_link);
+ nl_dump(p, "slave-of %s ", ll ? ll->l_name : "NONE");
+ if (ll)
+ rtnl_link_put(ll);
+ } else
+ nl_dump(p, "slave-of %d ", link->l_link);
}
+ if (link->ce_mask & LINK_ATTR_GROUP)
+ nl_dump(p, "group %u ", link->l_group);
+
if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_LINE])
link->l_info_ops->io_dump[NL_DUMP_LINE](link, p);
+ do_foreach_af(link, af_dump_line, p);
+
nl_dump(p, "\n");
+
+ if (fetched_cache)
+ nl_cache_put(cache);
}
static void link_dump_details(struct nl_object *obj, struct nl_dump_params *p)
@@ -482,10 +720,22 @@ static void link_dump_details(struct nl_object *obj, struct nl_dump_params *p)
if (link->ce_mask & LINK_ATTR_IFINDEX)
nl_dump(p, "index %u ", link->l_index);
+ if (link->ce_mask & LINK_ATTR_PROMISCUITY && link->l_promiscuity > 0)
+ nl_dump(p, "promisc-mode (%u users) ", link->l_promiscuity);
nl_dump(p, "\n");
+
+ if (link->ce_mask & LINK_ATTR_IFALIAS)
+ nl_dump_line(p, " alias %s\n", link->l_ifalias);
+
nl_dump_line(p, " ");
+ if (link->ce_mask & LINK_ATTR_NUM_TX_QUEUES)
+ nl_dump(p, "txq %u ", link->l_num_tx_queues);
+
+ if (link->ce_mask & LINK_ATTR_NUM_RX_QUEUES)
+ nl_dump(p, "rxq %u ", link->l_num_rx_queues);
+
if (link->ce_mask & LINK_ATTR_BRD)
nl_dump(p, "brd %s ", nl_addr2str(link->l_bcast, buf,
sizeof(buf)));
@@ -496,11 +746,21 @@ static void link_dump_details(struct nl_object *obj, struct nl_dump_params *p)
nl_dump(p, "state %s ", buf);
}
- nl_dump(p, "mode %s\n",
+ if (link->ce_mask & LINK_ATTR_NUM_VF)
+ nl_dump(p, "num-vf %u ", link->l_num_vf);
+
+ nl_dump(p, "mode %s ",
rtnl_link_mode2str(link->l_linkmode, buf, sizeof(buf)));
+ nl_dump(p, "carrier %s",
+ rtnl_link_carrier2str(link->l_carrier, buf, sizeof(buf)));
+
+ nl_dump(p, "\n");
+
if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_DETAILS])
link->l_info_ops->io_dump[NL_DUMP_DETAILS](link, p);
+
+ do_foreach_af(link, af_dump_details, p);
}
static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
@@ -516,7 +776,7 @@ static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
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");
+ strcpy(fmt, " RX %X.2f %s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n");
fmt[9] = *unit == 'B' ? '9' : '7';
nl_dump_line(p, fmt, res, unit,
@@ -528,7 +788,7 @@ static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
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");
+ strcpy(fmt, " TX %X.2f %s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" PRIu64 "\n");
fmt[9] = *unit == 'B' ? '9' : '7';
nl_dump_line(p, fmt, res, unit,
@@ -560,77 +820,12 @@ static void link_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
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]);
+ link->l_stats[RTNL_LINK_COLLISIONS]);
if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_STATS])
link->l_info_ops->io_dump[NL_DUMP_STATS](link, p);
-}
-
-static void 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;
- nl_dump_line(p, "LINK_NAME=%s\n", link->l_name);
- nl_dump_line(p, "LINK_IFINDEX=%u\n", link->l_index);
- nl_dump_line(p, "LINK_FAMILY=%s\n",
- nl_af2str(link->l_family, buf, sizeof(buf)));
- nl_dump_line(p, "LINK_TYPE=%s\n",
- nl_llproto2str(link->l_arptype, buf, sizeof(buf)));
- if (link->ce_mask & LINK_ATTR_ADDR)
- nl_dump_line(p, "LINK_ADDRESS=%s\n",
- nl_addr2str(link->l_addr, buf, sizeof(buf)));
- nl_dump_line(p, "LINK_MTU=%u\n", link->l_mtu);
- nl_dump_line(p, "LINK_TXQUEUELEN=%u\n", link->l_txqlen);
- nl_dump_line(p, "LINK_WEIGHT=%u\n", link->l_weight);
-
- rtnl_link_flags2str(link->l_flags & ~IFF_RUNNING, buf, sizeof(buf));
- if (buf[0])
- nl_dump_line(p, "LINK_FLAGS=%s\n", buf);
-
- if (link->ce_mask & LINK_ATTR_QDISC)
- nl_dump_line(p, "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);
-
- nl_dump_line(p, "LINK_LINK_IFINDEX=%d\n", link->l_link);
- if (ll) {
- nl_dump_line(p, "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);
- nl_dump_line(p, "LINK_MASTER=%s\n",
- master ? master->l_name : "none");
- if (master)
- rtnl_link_put(master);
- }
-
- if (link->ce_mask & LINK_ATTR_BRD)
- nl_dump_line(p, "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++;
- }
- nl_dump_line(p, "%s=%" PRIu64 "\n", buf, link->l_stats[i]);
- }
- }
-
- if (link->l_info_ops && link->l_info_ops->io_dump[NL_DUMP_ENV])
- link->l_info_ops->io_dump[NL_DUMP_ENV](link, p);
+ do_foreach_af(link, af_dump_stats, p);
}
#if 0
@@ -679,6 +874,29 @@ static int link_handle_event(struct nl_object *a, struct rtnl_link_event_cb *cb)
}
#endif
+
+static void link_keygen(struct nl_object *obj, uint32_t *hashkey,
+ uint32_t table_sz)
+{
+ struct rtnl_link *link = (struct rtnl_link *) obj;
+ unsigned int lkey_sz;
+ struct link_hash_key {
+ uint32_t l_index;
+ uint32_t l_family;
+ } __attribute__((packed)) lkey;
+
+ lkey_sz = sizeof(lkey);
+ lkey.l_index = link->l_index;
+ lkey.l_family = link->l_family;
+
+ *hashkey = nl_hash(&lkey, lkey_sz, 0) % table_sz;
+
+ NL_DBG(5, "link %p key (dev %d fam %d) keysz %d, hash 0x%x\n",
+ link, lkey.l_index, lkey.l_family, lkey_sz, *hashkey);
+
+ return;
+}
+
static int link_compare(struct nl_object *_a, struct nl_object *_b,
uint32_t attrs, int flags)
{
@@ -701,6 +919,12 @@ static int link_compare(struct nl_object *_a, struct nl_object *_b,
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));
+ diff |= LINK_DIFF(IFALIAS, strcmp(a->l_ifalias, b->l_ifalias));
+ diff |= LINK_DIFF(NUM_VF, a->l_num_vf != b->l_num_vf);
+ diff |= LINK_DIFF(PROMISCUITY, a->l_promiscuity != b->l_promiscuity);
+ diff |= LINK_DIFF(NUM_TX_QUEUES,a->l_num_tx_queues != b->l_num_tx_queues);
+ diff |= LINK_DIFF(NUM_RX_QUEUES,a->l_num_rx_queues != b->l_num_rx_queues);
+ diff |= LINK_DIFF(GROUP, a->l_group != b->l_group);
if (flags & LOOSE_COMPARISON)
diff |= LINK_DIFF(FLAGS,
@@ -708,12 +932,25 @@ static int link_compare(struct nl_object *_a, struct nl_object *_b,
else
diff |= LINK_DIFF(FLAGS, a->l_flags != b->l_flags);
-#undef LINK_DIFF
+ /*
+ * Compare LINK_ATTR_PROTINFO af_data
+ */
+ if (a->l_family == b->l_family) {
+ if (rtnl_link_af_data_compare(a, b, a->l_family) != 0)
+ goto protinfo_mismatch;
+ }
+out:
return diff;
+
+protinfo_mismatch:
+ diff |= LINK_DIFF(PROTINFO, 1);
+ goto out;
+
+#undef LINK_DIFF
}
-static struct trans_tbl link_attrs[] = {
+static const struct trans_tbl link_attrs[] = {
__ADD(LINK_ATTR_MTU, mtu)
__ADD(LINK_ATTR_LINK, link)
__ADD(LINK_ATTR_TXQLEN, txqlen)
@@ -732,6 +969,14 @@ static struct trans_tbl link_attrs[] = {
__ADD(LINK_ATTR_CHANGE, change)
__ADD(LINK_ATTR_OPERSTATE, operstate)
__ADD(LINK_ATTR_LINKMODE, linkmode)
+ __ADD(LINK_ATTR_IFALIAS, ifalias)
+ __ADD(LINK_ATTR_NUM_VF, num_vf)
+ __ADD(LINK_ATTR_PROMISCUITY, promiscuity)
+ __ADD(LINK_ATTR_NUM_TX_QUEUES, num_tx_queues)
+ __ADD(LINK_ATTR_NUM_RX_QUEUES, num_rx_queues)
+ __ADD(LINK_ATTR_GROUP, group)
+ __ADD(LINK_ATTR_CARRIER, carrier)
+ __ADD(LINK_ATTR_PHYS_PORT_ID, phys_port_id)
};
static char *link_attrs2str(int attrs, char *buf, size_t len)
@@ -741,24 +986,7 @@ static char *link_attrs2str(int attrs, char *buf, size_t len)
}
/**
- * @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
+ * @name Get / List
* @{
*/
@@ -766,27 +994,61 @@ void rtnl_link_put(struct rtnl_link *link)
/**
* Allocate link cache and fill in all configured links.
* @arg sk Netlink socket.
+ * @arg family Link address family or AF_UNSPEC
* @arg result Pointer to store resulting cache.
*
- * Allocates a new link cache, initializes it properly and updates it
- * to include all links currently configured in the kernel.
+ * Allocates and initializes a new link cache. If \c sk is valid, a netlink
+ * message is sent to the kernel requesting a full dump of all configured
+ * links. The returned messages are parsed and filled into the cache. If
+ * the operation succeeds, the resulting cache will contain a link object for
+ * each link configured in the kernel. If \c sk is NULL, returns 0 but the
+ * cache is still empty.
*
+ * If \c family is set to an address family other than \c AF_UNSPEC the
+ * contents of the cache can be limited to a specific address family.
+ * Currently the following address families are supported:
+ * - AF_BRIDGE
+ * - AF_INET6
+ *
+ * @route_doc{link_list, Get List of Links}
+ * @see rtnl_link_get()
+ * @see rtnl_link_get_by_name()
* @return 0 on success or a negative error code.
*/
-int rtnl_link_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
+int rtnl_link_alloc_cache(struct nl_sock *sk, int family, struct nl_cache **result)
{
- return nl_cache_alloc_and_fill(&rtnl_link_ops, sk, result);
+ struct nl_cache * cache;
+ int err;
+
+ cache = nl_cache_alloc(&rtnl_link_ops);
+ if (!cache)
+ return -NLE_NOMEM;
+
+ cache->c_iarg1 = family;
+
+ if (sk && (err = nl_cache_refill(sk, cache)) < 0) {
+ nl_cache_free(cache);
+ return err;
+ }
+
+ *result = cache;
+ return 0;
}
/**
- * Look up link by interface index in the provided cache
- * @arg cache link cache
- * @arg ifindex link interface index
+ * Lookup link in cache by interface index
+ * @arg cache Link cache
+ * @arg ifindex Interface index
+ *
+ * Searches through the provided cache looking for a link with matching
+ * interface index.
*
- * The caller owns a reference on the returned object and
- * must give the object back via rtnl_link_put().
+ * @attention The reference counter of the returned link object will be
+ * incremented. Use rtnl_link_put() to release the reference.
*
- * @return pointer to link inside the cache or NULL if no match was found.
+ * @route_doc{link_list, Get List of Links}
+ * @see rtnl_link_get_by_name()
+ * @return Link object or NULL if no match was found.
*/
struct rtnl_link *rtnl_link_get(struct nl_cache *cache, int ifindex)
{
@@ -806,14 +1068,19 @@ struct rtnl_link *rtnl_link_get(struct nl_cache *cache, int ifindex)
}
/**
- * Look up link by link name in the provided cache
- * @arg cache link cache
- * @arg name link name
+ * Lookup link in cache by link name
+ * @arg cache Link cache
+ * @arg name Name of link
*
- * The caller owns a reference on the returned object and
- * must give the object back via rtnl_link_put().
+ * Searches through the provided cache looking for a link with matching
+ * link name
*
- * @return pointer to link inside the cache or NULL if no match was found.
+ * @attention The reference counter of the returned link object will be
+ * incremented. Use rtnl_link_put() to release the reference.
+ *
+ * @route_doc{link_list, Get List of Links}
+ * @see rtnl_link_get()
+ * @return Link object or NULL if no match was found.
*/
struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache,
const char *name)
@@ -833,91 +1100,44 @@ struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache,
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
+ * Construct RTM_GETLINK netlink message
+ * @arg ifindex Interface index
+ * @arg name Name of link
+ * @arg result Pointer to store resulting netlink message
*
- * 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.
+ * The behaviour of this function is identical to rtnl_link_get_kernel()
+ * with the exception that it will not send the message but return it in
+ * the provided return pointer instead.
*
- * @return New netlink message
- * @note Not all attributes can be changed, see
- * \ref link_changeable "Changeable Attributes" for more details.
+ * @see rtnl_link_get_kernel()
+ *
+ * @return 0 on success or a negative error code.
*/
-int rtnl_link_build_change_request(struct rtnl_link *old,
- struct rtnl_link *tmpl, int flags,
- struct nl_msg **result)
+int rtnl_link_build_get_request(int ifindex, const char *name,
+ struct nl_msg **result)
{
+ struct ifinfomsg ifi;
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;
+ if (ifindex <= 0 && !name) {
+ APPBUG("ifindex or name must be specified");
+ return -NLE_MISSING_ATTR;
}
- msg = nlmsg_alloc_simple(RTM_SETLINK, flags);
- if (!msg)
+ memset(&ifi, 0, sizeof(ifi));
+
+ if (!(msg = nlmsg_alloc_simple(RTM_GETLINK, 0)))
return -NLE_NOMEM;
+ if (ifindex > 0)
+ ifi.ifi_index = ifindex;
+
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);
-
- if (tmpl->ce_mask & LINK_ATTR_OPERSTATE)
- NLA_PUT_U8(msg, IFLA_OPERSTATE, tmpl->l_operstate);
-
- if (tmpl->ce_mask & LINK_ATTR_LINKMODE)
- NLA_PUT_U8(msg, IFLA_LINKMODE, tmpl->l_linkmode);
-
- if ((tmpl->ce_mask & LINK_ATTR_LINKINFO) && tmpl->l_info_ops &&
- tmpl->l_info_ops->io_put_attrs) {
- struct nlattr *info;
-
- if (!(info = nla_nest_start(msg, IFLA_LINKINFO)))
- goto nla_put_failure;
-
- NLA_PUT_STRING(msg, IFLA_INFO_KIND, tmpl->l_info_ops->io_name);
-
- if (tmpl->l_info_ops->io_put_attrs(msg, tmpl) < 0)
- goto nla_put_failure;
-
- nla_nest_end(msg, info);
- }
+ if (name)
+ NLA_PUT_STRING(msg, IFLA_IFNAME, name);
*result = msg;
return 0;
@@ -928,55 +1148,62 @@ nla_put_failure:
}
/**
- * Change link attributes
- * @arg sk Netlink socket.
- * @arg old link to be changed
- * @arg tmpl template with requested changes
- * @arg flags additional netlink message flags
+ * Get a link object directly from kernel
+ * @arg sk Netlink socket
+ * @arg ifindex Interface index
+ * @arg name Name of link
+ * @arg result Pointer to store resulting link object
*
- * 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.
+ * This function builds a \c RTM_GETLINK netlink message to request
+ * a specific link directly from the kernel. The returned answer is
+ * parsed into a struct rtnl_link object and returned via the result
+ * pointer or -NLE_OBJ_NOTFOUND is returned if no matching link was
+ * found.
*
- * @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.
+ * @route_doc{link_direct_lookup, Lookup Single Link (Direct Lookup)}
+ * @return 0 on success or a negative error code.
*/
-int rtnl_link_change(struct nl_sock *sk, struct rtnl_link *old,
- struct rtnl_link *tmpl, int flags)
+int rtnl_link_get_kernel(struct nl_sock *sk, int ifindex, const char *name,
+ struct rtnl_link **result)
{
- struct nl_msg *msg;
+ struct nl_msg *msg = NULL;
+ struct nl_object *obj;
int err;
-
- if ((err = rtnl_link_build_change_request(old, tmpl, flags, &msg)) < 0)
+
+ if ((err = rtnl_link_build_get_request(ifindex, name, &msg)) < 0)
return err;
-
- err = nl_send_auto_complete(sk, msg);
+
+ err = nl_send_auto(sk, msg);
nlmsg_free(msg);
if (err < 0)
return err;
- return wait_for_ack(sk);
-}
+ if ((err = nl_pickup(sk, link_msg_parser, &obj)) < 0)
+ return err;
-/** @} */
+ /* We have used link_msg_parser(), object is definitely a link */
+ *result = (struct rtnl_link *) obj;
-/**
- * @name Name <-> Index Translations
- * @{
- */
+ /* If an object has been returned, we also need to wait for the ACK */
+ if (err == 0 && obj)
+ wait_for_ack(sk);
+
+ return 0;
+}
/**
- * 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
+ * Translate interface index to corresponding link name
+ * @arg cache Link cache
+ * @arg ifindex Interface index
+ * @arg dst String to store name
+ * @arg len Length of destination string
*
* Translates the specified interface index to the corresponding
- * link name and stores the name in the destination buffer.
+ * link name and stores the name in the destination string.
*
- * @return link name or NULL if no match was found.
+ * @route_doc{link_translate_ifindex, Translating interface index to link name}
+ * @see rtnl_link_name2i()
+ * @return Name of link or NULL if no match was found.
*/
char * rtnl_link_i2name(struct nl_cache *cache, int ifindex, char *dst,
size_t len)
@@ -993,11 +1220,13 @@ char * rtnl_link_i2name(struct nl_cache *cache, int ifindex, char *dst,
}
/**
- * Translate a link name to the corresponding interface index
- * @arg cache link cache
- * @arg name link name
+ * Translate link name to corresponding interface index
+ * @arg cache Link cache
+ * @arg name Name of link
*
- * @return interface index or 0 if no match was found.
+ * @route_doc{link_translate_ifindex, Translating interface index to link name}
+ * @see rtnl_link_i2name()
+ * @return Interface index or 0 if no match was found.
*/
int rtnl_link_name2i(struct nl_cache *cache, const char *name)
{
@@ -1015,171 +1244,454 @@ int rtnl_link_name2i(struct nl_cache *cache, const char *name)
/** @} */
-/**
- * @name Link Flags Translations
- * @{
- */
+int rtnl_link_fill_info(struct nl_msg *msg, struct rtnl_link *link)
+{
+ if (link->ce_mask & LINK_ATTR_ADDR)
+ NLA_PUT_ADDR(msg, IFLA_ADDRESS, link->l_addr);
-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)
- __ADD(IFF_ECHO, echo)
-};
+ if (link->ce_mask & LINK_ATTR_BRD)
+ NLA_PUT_ADDR(msg, IFLA_BROADCAST, link->l_bcast);
-char * rtnl_link_flags2str(int flags, char *buf, size_t len)
-{
- return __flags2str(flags, buf, len, link_flags,
- ARRAY_SIZE(link_flags));
+ if (link->ce_mask & LINK_ATTR_MTU)
+ NLA_PUT_U32(msg, IFLA_MTU, link->l_mtu);
+
+ if (link->ce_mask & LINK_ATTR_TXQLEN)
+ NLA_PUT_U32(msg, IFLA_TXQLEN, link->l_txqlen);
+
+ if (link->ce_mask & LINK_ATTR_WEIGHT)
+ NLA_PUT_U32(msg, IFLA_WEIGHT, link->l_weight);
+
+ if (link->ce_mask & LINK_ATTR_IFNAME)
+ NLA_PUT_STRING(msg, IFLA_IFNAME, link->l_name);
+
+ if (link->ce_mask & LINK_ATTR_OPERSTATE)
+ NLA_PUT_U8(msg, IFLA_OPERSTATE, link->l_operstate);
+
+ if (link->ce_mask & LINK_ATTR_CARRIER)
+ NLA_PUT_U8(msg, IFLA_CARRIER, link->l_carrier);
+
+ if (link->ce_mask & LINK_ATTR_LINKMODE)
+ NLA_PUT_U8(msg, IFLA_LINKMODE, link->l_linkmode);
+
+ if (link->ce_mask & LINK_ATTR_IFALIAS)
+ NLA_PUT_STRING(msg, IFLA_IFALIAS, link->l_ifalias);
+
+ if (link->ce_mask & LINK_ATTR_LINK)
+ NLA_PUT_U32(msg, IFLA_LINK, link->l_link);
+
+ if (link->ce_mask & LINK_ATTR_MASTER)
+ NLA_PUT_U32(msg, IFLA_MASTER, link->l_master);
+
+ if (link->ce_mask & LINK_ATTR_NUM_TX_QUEUES)
+ NLA_PUT_U32(msg, IFLA_NUM_TX_QUEUES, link->l_num_tx_queues);
+
+ if (link->ce_mask & LINK_ATTR_NUM_RX_QUEUES)
+ NLA_PUT_U32(msg, IFLA_NUM_RX_QUEUES, link->l_num_rx_queues);
+
+ if (link->ce_mask & LINK_ATTR_NS_FD)
+ NLA_PUT_U32(msg, IFLA_NET_NS_FD, link->l_ns_fd);
+
+ if (link->ce_mask & LINK_ATTR_NS_PID)
+ NLA_PUT_U32(msg, IFLA_NET_NS_PID, link->l_ns_pid);
+
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
}
-int rtnl_link_str2flags(const char *name)
+static int build_link_msg(int cmd, struct ifinfomsg *hdr,
+ struct rtnl_link *link, int flags, struct nl_msg **result)
{
- return __str2flags(name, link_flags, ARRAY_SIZE(link_flags));
-}
+ struct nl_msg *msg;
+ struct nlattr *af_spec;
-/** @} */
+ msg = nlmsg_alloc_simple(cmd, flags);
+ if (!msg)
+ return -NLE_NOMEM;
+
+ if (nlmsg_append(msg, hdr, sizeof(*hdr), NLMSG_ALIGNTO) < 0)
+ goto nla_put_failure;
+
+ if (rtnl_link_fill_info(msg, link))
+ goto nla_put_failure;
+
+ if (link->ce_mask & LINK_ATTR_GROUP)
+ NLA_PUT_U32(msg, IFLA_GROUP, link->l_group);
+
+ if (link->ce_mask & LINK_ATTR_LINKINFO) {
+ struct nlattr *info;
+
+ if (!(info = nla_nest_start(msg, IFLA_LINKINFO)))
+ goto nla_put_failure;
+
+ NLA_PUT_STRING(msg, IFLA_INFO_KIND, link->l_info_kind);
+
+ if (link->l_info_ops) {
+ if (link->l_info_ops->io_put_attrs &&
+ link->l_info_ops->io_put_attrs(msg, link) < 0)
+ goto nla_put_failure;
+ }
+
+ nla_nest_end(msg, info);
+ }
+
+ if (!(af_spec = nla_nest_start(msg, IFLA_AF_SPEC)))
+ goto nla_put_failure;
+
+ if (do_foreach_af(link, af_fill, msg) < 0)
+ goto nla_put_failure;
+
+ nla_nest_end(msg, af_spec);
+
+ *result = msg;
+ return 0;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -NLE_MSGSIZE;
+}
/**
- * @name Link Statistics Translations
+ * @name Add / Modify
* @{
*/
-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)
+/**
+ * Build a netlink message requesting the addition of new virtual link
+ * @arg link new link to add
+ * @arg flags additional netlink message flags
+ * @arg result pointer to store resulting netlink message
+ *
+ * The behaviour of this function is identical to rtnl_link_add() with
+ * the exception that it will not send the message but return it in the
+ * provided return pointer instead.
+ *
+ * @see rtnl_link_add()
+ *
+ * @note This operation is not supported on all kernel versions.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_build_add_request(struct rtnl_link *link, int flags,
+ struct nl_msg **result)
{
- return __type2str(st, buf, len, link_stats, ARRAY_SIZE(link_stats));
+ struct ifinfomsg ifi = {
+ .ifi_family = link->l_family,
+ .ifi_index = link->l_index,
+ .ifi_flags = link->l_flags,
+ };
+
+ return build_link_msg(RTM_NEWLINK, &ifi, link, flags, result);
}
-int rtnl_link_str2stat(const char *name)
+/**
+ * Add virtual link
+ * @arg sk netlink socket.
+ * @arg link new link to add
+ * @arg flags additional netlink message flags
+ *
+ * Builds a \c RTM_NEWLINK netlink message requesting the addition of
+ * a new virtual link.
+ *
+ * After sending, the function will wait for the ACK or an eventual
+ * error message to be received and will therefore block until the
+ * operation has been completed.
+ *
+ * @copydoc auto_ack_warning
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_add(struct nl_sock *sk, struct rtnl_link *link, int flags)
{
- return __str2type(name, link_stats, ARRAY_SIZE(link_stats));
-}
+ struct nl_msg *msg;
+ int err;
+
+ err = rtnl_link_build_add_request(link, flags, &msg);
+ if (err < 0)
+ return err;
-/** @} */
+ return nl_send_sync(sk, msg);
+}
/**
- * @name Link Operstate Translations
- * @{
+ * Build a netlink message requesting the modification of link
+ * @arg orig original link to change
+ * @arg changes link containing the changes to be made
+ * @arg flags additional netlink message flags
+ * @arg result pointer to store resulting netlink message
+ *
+ * The behaviour of this function is identical to rtnl_link_change() with
+ * the exception that it will not send the message but return it in the
+ * provided return pointer instead.
+ *
+ * @see rtnl_link_change()
+ *
+ * @note The resulting message will have message type set to RTM_NEWLINK
+ * which may not work with older kernels. You may have to modify it
+ * to RTM_SETLINK (does not allow changing link info attributes) to
+ * have the change request work with older kernels.
+ *
+ * @return 0 on success or a negative error code.
*/
+int rtnl_link_build_change_request(struct rtnl_link *orig,
+ struct rtnl_link *changes, int flags,
+ struct nl_msg **result)
+{
+ struct ifinfomsg ifi = {
+ .ifi_family = orig->l_family,
+ .ifi_index = orig->l_index,
+ };
+ int err;
-static struct trans_tbl link_operstates[] = {
- __ADD(IF_OPER_UNKNOWN, unknown)
- __ADD(IF_OPER_NOTPRESENT, notpresent)
- __ADD(IF_OPER_DOWN, down)
- __ADD(IF_OPER_LOWERLAYERDOWN, lowerlayerdown)
- __ADD(IF_OPER_TESTING, testing)
- __ADD(IF_OPER_DORMANT, dormant)
- __ADD(IF_OPER_UP, up)
-};
+ if (changes->ce_mask & LINK_ATTR_FLAGS) {
+ ifi.ifi_flags = orig->l_flags & ~changes->l_flag_mask;
+ ifi.ifi_flags |= changes->l_flags;
+ }
-char *rtnl_link_operstate2str(int st, char *buf, size_t len)
-{
- return __type2str(st, buf, len, link_operstates,
- ARRAY_SIZE(link_operstates));
+ if (changes->l_family && changes->l_family != orig->l_family) {
+ APPBUG("link change: family is immutable");
+ return -NLE_IMMUTABLE;
+ }
+
+ /* Avoid unnecessary name change requests */
+ if (orig->ce_mask & LINK_ATTR_IFINDEX &&
+ orig->ce_mask & LINK_ATTR_IFNAME &&
+ changes->ce_mask & LINK_ATTR_IFNAME &&
+ !strcmp(orig->l_name, changes->l_name))
+ changes->ce_mask &= ~LINK_ATTR_IFNAME;
+
+ if ((err = build_link_msg(RTM_NEWLINK, &ifi, changes, flags, result)) < 0)
+ goto errout;
+
+ return 0;
+
+errout:
+ return err;
}
-int rtnl_link_str2operstate(const char *name)
+/**
+ * Change link
+ * @arg sk netlink socket.
+ * @arg orig original link to be changed
+ * @arg changes link containing the changes to be made
+ * @arg flags additional netlink message flags
+ *
+ * Builds a \c RTM_NEWLINK netlink message requesting the change of
+ * a network link. If -EOPNOTSUPP is returned by the kernel, the
+ * message type will be changed to \c RTM_SETLINK and the message is
+ * resent to work around older kernel versions.
+ *
+ * The link to be changed is looked up based on the interface index
+ * supplied in the \p orig link. Optionaly the link name is used but
+ * only if no interface index is provided, otherwise providing an
+ * link name will result in the link name being changed.
+ *
+ * If no matching link exists, the function will return
+ * -NLE_OBJ_NOTFOUND.
+ *
+ * After sending, the function will wait for the ACK or an eventual
+ * error message to be received and will therefore block until the
+ * operation has been completed.
+ *
+ * @copydoc auto_ack_warning
+ *
+ * @note The link name can only be changed if the link has been put
+ * in opertional down state. (~IF_UP)
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_change(struct nl_sock *sk, struct rtnl_link *orig,
+ struct rtnl_link *changes, int flags)
{
- return __str2type(name, link_operstates,
- ARRAY_SIZE(link_operstates));
+ struct nl_msg *msg;
+ int err;
+
+ err = rtnl_link_build_change_request(orig, changes, flags, &msg);
+ if (err < 0)
+ return err;
+
+retry:
+ err = nl_send_auto_complete(sk, msg);
+ if (err < 0)
+ goto errout;
+
+ err = wait_for_ack(sk);
+ if (err == -NLE_OPNOTSUPP && msg->nm_nlh->nlmsg_type == RTM_NEWLINK) {
+ msg->nm_nlh->nlmsg_type = RTM_SETLINK;
+ goto retry;
+ }
+
+errout:
+ nlmsg_free(msg);
+ return err;
}
/** @} */
/**
- * @name Link Mode Translations
+ * @name Delete
* @{
*/
-static struct trans_tbl link_modes[] = {
- __ADD(IF_LINK_MODE_DEFAULT, default)
- __ADD(IF_LINK_MODE_DORMANT, dormant)
-};
-
-char *rtnl_link_mode2str(int st, char *buf, size_t len)
+/**
+ * Build a netlink message requesting the deletion of a link
+ * @arg link Link to delete
+ * @arg result Pointer to store resulting netlink message
+ *
+ * The behaviour of this function is identical to rtnl_link_delete() with
+ * the exception that it will not send the message but return it in the
+ * provided return pointer instead.
+ *
+ * @see rtnl_link_delete()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_build_delete_request(const struct rtnl_link *link,
+ struct nl_msg **result)
{
- return __type2str(st, buf, len, link_modes, ARRAY_SIZE(link_modes));
+ struct nl_msg *msg;
+ struct ifinfomsg ifi = {
+ .ifi_index = link->l_index,
+ };
+
+ if (!(link->ce_mask & (LINK_ATTR_IFINDEX | LINK_ATTR_IFNAME))) {
+ APPBUG("ifindex or name must be specified");
+ return -NLE_MISSING_ATTR;
+ }
+
+ if (!(msg = nlmsg_alloc_simple(RTM_DELLINK, 0)))
+ return -NLE_NOMEM;
+
+ if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
+ goto nla_put_failure;
+
+ if (link->ce_mask & LINK_ATTR_IFNAME)
+ NLA_PUT_STRING(msg, IFLA_IFNAME, link->l_name);
+
+ *result = msg;
+ return 0;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -NLE_MSGSIZE;
}
-int rtnl_link_str2mode(const char *name)
+/**
+ * Delete link
+ * @arg sk Netlink socket
+ * @arg link Link to delete
+ *
+ * Builds a \c RTM_DELLINK netlink message requesting the deletion of
+ * a network link which has been previously added to the kernel and
+ * sends the message to the kernel.
+ *
+ * If no matching link exists, the function will return
+ * -NLE_OBJ_NOTFOUND.
+ *
+ * After sending, the function will wait for the ACK or an eventual
+ * error message to be received and will therefore block until the
+ * operation has been completed.
+ *
+ * @copydoc auto_ack_warning
+ *
+ * @note Only virtual links such as dummy interface or vlan interfaces
+ * can be deleted. It is not possible to delete physical interfaces
+ * such as ethernet interfaces or the loopback device.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_delete(struct nl_sock *sk, const struct rtnl_link *link)
{
- return __str2type(name, link_modes, ARRAY_SIZE(link_modes));
+ struct nl_msg *msg;
+ int err;
+
+ if ((err = rtnl_link_build_delete_request(link, &msg)) < 0)
+ return err;
+
+ return nl_send_sync(sk, msg);
}
/** @} */
/**
- * @name Attributes
+ * @name Link Object
* @{
*/
-void rtnl_link_set_qdisc(struct rtnl_link *link, const char *qdisc)
+/**
+ * Allocate link object
+ *
+ * @see rtnl_link_put()
+ * @return New link object or NULL if allocation failed
+ */
+struct rtnl_link *rtnl_link_alloc(void)
{
- strncpy(link->l_qdisc, qdisc, sizeof(link->l_qdisc) - 1);
- link->ce_mask |= LINK_ATTR_QDISC;
+ return (struct rtnl_link *) nl_object_alloc(&link_obj_ops);
}
-char *rtnl_link_get_qdisc(struct rtnl_link *link)
+/**
+ * Return a link object reference
+ * @arg link Link object
+ */
+void rtnl_link_put(struct rtnl_link *link)
{
- if (link->ce_mask & LINK_ATTR_QDISC)
- return link->l_qdisc;
- else
- return NULL;
+ nl_object_put((struct nl_object *) link);
}
+/**
+ * Set name of link object
+ * @arg link Link object
+ * @arg name New name
+ *
+ * @note To change the name of a link in the kernel, set the interface
+ * index to the link you wish to change, modify the link name using
+ * this function and pass the link object to rtnl_link_change() or
+ * rtnl_link_add().
+ *
+ * @route_doc{link_attr_name, Link Name}
+ * @see rtnl_link_get_name()
+ * @see rtnl_link_set_ifindex()
+ */
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;
}
+/**
+ * Return name of link object
+ * @arg link Link object
+ *
+ * @route_doc{link_attr_name, Link Name}
+ * @see rtnl_link_set_name()
+ * @return Link name or NULL if name is not specified
+ */
char *rtnl_link_get_name(struct rtnl_link *link)
{
- if (link->ce_mask & LINK_ATTR_IFNAME)
- return link->l_name;
- else
- return NULL;
+ return link->ce_mask & LINK_ATTR_IFNAME ? link->l_name : NULL;
+}
+
+/**
+ * Set the group identifier of a link object
+ * @arg link Link object
+ * @arg group Group identifier
+ */
+void rtnl_link_set_group(struct rtnl_link *link, uint32_t group)
+{
+ link->l_group = group;
+ link->ce_mask |= LINK_ATTR_GROUP;
+}
+
+/**
+ * Return the group identifier of link object
+ * @arg link Link object
+ *
+ * @return Group identifier or 0 if not set.
+ */
+uint32_t rtnl_link_get_group(struct rtnl_link *link)
+{
+ return link->l_group;
}
static inline void __assign_addr(struct rtnl_link *link, struct nl_addr **pos,
@@ -1194,32 +1706,75 @@ static inline void __assign_addr(struct rtnl_link *link, struct nl_addr **pos,
link->ce_mask |= flag;
}
+/**
+ * Set link layer address of link object
+ * @arg link Link object
+ * @arg addr New link layer address
+ *
+ * The function increments the reference counter of the address object
+ * and overwrites any existing link layer address previously assigned.
+ *
+ * @route_doc{link_attr_address, Link layer address}
+ * @see rtnl_link_get_addr()
+ */
void rtnl_link_set_addr(struct rtnl_link *link, struct nl_addr *addr)
{
__assign_addr(link, &link->l_addr, addr, LINK_ATTR_ADDR);
}
+/**
+ * Return link layer address of link object
+ * @arg link Link object
+ *
+ * @copydoc pointer_lifetime_warning
+ * @route_doc{link_attr_address, Link Layer Address}
+ * @see rtnl_link_set_addr()
+ * @return Link layer address or NULL if not set.
+ */
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;
+ return link->ce_mask & LINK_ATTR_ADDR ? link->l_addr : NULL;
}
-void rtnl_link_set_broadcast(struct rtnl_link *link, struct nl_addr *brd)
+/**
+ * Set link layer broadcast address of link object
+ * @arg link Link object
+ * @arg addr New broadcast address
+ *
+ * The function increments the reference counter of the address object
+ * and overwrites any existing link layer broadcast address previously
+ * assigned.
+ *
+ * @route_doc{link_attr_broadcast, Link Layer Broadcast Address}
+ * @see rtnl_link_get_broadcast()
+ */
+void rtnl_link_set_broadcast(struct rtnl_link *link, struct nl_addr *addr)
{
- __assign_addr(link, &link->l_bcast, brd, LINK_ATTR_BRD);
+ __assign_addr(link, &link->l_bcast, addr, LINK_ATTR_BRD);
}
+/**
+ * Return link layer broadcast address of link object
+ * @arg link Link object
+ *
+ * @copydoc pointer_lifetime_warning
+ * @route_doc{link_attr_address, Link Layer Address}
+ * @see rtnl_link_set_broadcast()
+ * @return Link layer address or NULL if not set.
+ */
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;
+ return link->ce_mask & LINK_ATTR_BRD ? link->l_bcast : NULL;
}
+/**
+ * Set flags of link object
+ * @arg link Link object
+ * @arg flags Flags
+ *
+ * @see rtnl_link_get_flags()
+ * @see rtnl_link_unset_flags()
+ */
void rtnl_link_set_flags(struct rtnl_link *link, unsigned int flags)
{
link->l_flag_mask |= flags;
@@ -1227,6 +1782,14 @@ void rtnl_link_set_flags(struct rtnl_link *link, unsigned int flags)
link->ce_mask |= LINK_ATTR_FLAGS;
}
+/**
+ * Unset flags of link object
+ * @arg link Link object
+ * @arg flags Flags
+ *
+ * @see rtnl_link_set_flags()
+ * @see rtnl_link_get_flags()
+ */
void rtnl_link_unset_flags(struct rtnl_link *link, unsigned int flags)
{
link->l_flag_mask |= flags;
@@ -1234,86 +1797,166 @@ void rtnl_link_unset_flags(struct rtnl_link *link, unsigned int flags)
link->ce_mask |= LINK_ATTR_FLAGS;
}
+/**
+ * Return flags of link object
+ * @arg link Link object
+ *
+ * @route_doc{link_attr_flags, Link Flags}
+ * @see rtnl_link_set_flags()
+ * @see rtnl_link_unset_flags()
+ * @return Link flags or 0 if none have been set.
+ */
unsigned int rtnl_link_get_flags(struct rtnl_link *link)
{
return link->l_flags;
}
+/**
+ * Set address family of link object
+ *
+ * @see rtnl_link_get_family()
+ */
void rtnl_link_set_family(struct rtnl_link *link, int family)
{
link->l_family = family;
link->ce_mask |= LINK_ATTR_FAMILY;
+
+ if (link->l_af_ops) {
+ af_free(link, link->l_af_ops,
+ link->l_af_data[link->l_af_ops->ao_family], NULL);
+ link->l_af_data[link->l_af_ops->ao_family] = NULL;
+ }
+
+ link->l_af_ops = af_lookup_and_alloc(link, family);
}
+/**
+ * Return address family of link object
+ * @arg link Link object
+ *
+ * @see rtnl_link_set_family()
+ * @return Address family or \c AF_UNSPEC if not specified.
+ */
int rtnl_link_get_family(struct rtnl_link *link)
{
- if (link->l_family & LINK_ATTR_FAMILY)
- return link->l_family;
- else
- return AF_UNSPEC;
+ return link->ce_mask & LINK_ATTR_FAMILY ? link->l_family : AF_UNSPEC;
}
+/**
+ * Set hardware type of link object
+ * @arg link Link object
+ * @arg arptype New hardware type \c (ARPHRD_*)
+ *
+ * @route_doc{link_attr_arptype, Hardware Type}
+ * @copydoc read_only_attribute
+ * @see rtnl_link_get_arptype()
+ */
void rtnl_link_set_arptype(struct rtnl_link *link, unsigned int arptype)
{
link->l_arptype = arptype;
+ link->ce_mask |= LINK_ATTR_ARPTYPE;
}
+/**
+ * Get hardware type of link object
+ * @arg link Link object
+ *
+ * @route_doc{link_attr_arptype, Hardware Type}
+ * @see rtnl_link_set_arptype()
+ * @return Hardware type \c (ARPHRD_ETHER *) or \c ARPHRD_VOID
+ */
unsigned int rtnl_link_get_arptype(struct rtnl_link *link)
{
- return link->l_arptype;
+ if (link->ce_mask & LINK_ATTR_ARPTYPE)
+ return link->l_arptype;
+ else
+ return ARPHRD_VOID;
}
+/**
+ * Set interface index of link object
+ * @arg link Link object
+ * @arg ifindex Interface index
+ *
+ * @route_doc{link_attr_ifindex, Interface Index}
+ * @see rtnl_link_get_ifindex()
+ */
void rtnl_link_set_ifindex(struct rtnl_link *link, int ifindex)
{
link->l_index = ifindex;
link->ce_mask |= LINK_ATTR_IFINDEX;
}
+
+/**
+ * Return interface index of link object
+ * @arg link Link object
+ *
+ * @route_doc{link_attr_ifindex, Interface Index}
+ * @see rtnl_link_set_ifindex()
+ * @return Interface index or 0 if not set.
+ */
int rtnl_link_get_ifindex(struct rtnl_link *link)
{
return link->l_index;
}
+/**
+ * Set Maximum Transmission Unit of link object
+ * @arg link Link object
+ * @arg mtu New MTU value in number of bytes
+ *
+ * @route_doc{link_attr_mtu, Maximum Transmission Unit}
+ * @see rtnl_link_get_mtu()
+ */
void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu)
{
link->l_mtu = mtu;
link->ce_mask |= LINK_ATTR_MTU;
}
+/**
+ * Return maximum transmission unit of link object
+ * @arg link Link object
+ *
+ * @route_doc{link_attr_mtu, Maximum Transmission Unit}
+ * @see rtnl_link_set_mtu()
+ * @return MTU in bytes or 0 if not set
+ */
unsigned int rtnl_link_get_mtu(struct rtnl_link *link)
{
- if (link->ce_mask & LINK_ATTR_MTU)
- return link->l_mtu;
- else
- return 0;
+ return link->l_mtu;
}
+/**
+ * Set transmission queue length
+ * @arg link Link object
+ * @arg txqlen New queue length
+ *
+ * The unit is dependant on the link type. The most common units is number
+ * of packets.
+ *
+ * @route_doc{link_attr_txqlen, Transmission Queue Length}
+ */
void rtnl_link_set_txqlen(struct rtnl_link *link, unsigned int txqlen)
{
link->l_txqlen = txqlen;
link->ce_mask |= LINK_ATTR_TXQLEN;
}
+/**
+ * Return transmission queue length
+ * @arg link Link object
+ *
+ * The unit is dependant on the link type. The most common units is number
+ * of packets.
+ *
+ * @route_doc{link_attr_txqlen, Transmission Queue Length}
+ * @return queue length or 0 if not specified.
+ */
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;
+ return link->ce_mask & LINK_ATTR_TXQLEN ? link->l_txqlen : 0;
}
void rtnl_link_set_link(struct rtnl_link *link, int ifindex)
@@ -1327,96 +1970,745 @@ int rtnl_link_get_link(struct rtnl_link *link)
return link->l_link;
}
+/**
+ * Set master link of link object
+ * @arg link Link object
+ * @arg ifindex Interface index of master link
+ *
+ * @see rtnl_link_get_master()
+ */
void rtnl_link_set_master(struct rtnl_link *link, int ifindex)
{
link->l_master = ifindex;
link->ce_mask |= LINK_ATTR_MASTER;
}
+/**
+ * Return master link of link object
+ * @arg link Link object
+ *
+ * @see rtnl_link_set_master()
+ * @return Interface index of master link or 0 if not specified
+ */
int rtnl_link_get_master(struct rtnl_link *link)
{
return link->l_master;
}
-void rtnl_link_set_operstate(struct rtnl_link *link, uint8_t operstate)
+/**
+ * Set carrier of link object
+ * @arg link Link object
+ * @arg status New carrier status
+ *
+ * @see rtnl_link_get_carrier()
+ */
+void rtnl_link_set_carrier(struct rtnl_link *link, uint8_t status)
+{
+ link->l_carrier = status;
+ link->ce_mask |= LINK_ATTR_CARRIER;
+}
+
+/**
+ * Return carrier status of link object
+ * @arg link Link object
+ *
+ * @see rtnl_link_set_master()
+ * @return Carrier state.
+ */
+uint8_t rtnl_link_get_carrier(struct rtnl_link *link)
{
- link->l_operstate = operstate;
+ return link->l_carrier;
+}
+
+/**
+ * Set operational status of link object
+ * @arg link Link object
+ * @arg status New opertional status
+ *
+ * @route_doc{link_attr_operstate, Operational Status}}
+ * @see rtnl_link_get_operstate()
+ */
+void rtnl_link_set_operstate(struct rtnl_link *link, uint8_t status)
+{
+ link->l_operstate = status;
link->ce_mask |= LINK_ATTR_OPERSTATE;
}
+/**
+ * Return operational status of link object
+ * @arg link Link object
+ *
+ * @route_doc{link_attr_operstate, Operational Status}
+ * @see rtnl_link_set_operstate()
+ * @return Opertional state or \c IF_OPER_UNKNOWN
+ */
uint8_t rtnl_link_get_operstate(struct rtnl_link *link)
{
- if (link->ce_mask & LINK_ATTR_OPERSTATE)
- return link->l_operstate;
- else
- return IF_OPER_UNKNOWN;
+ return link->l_operstate;
}
-void rtnl_link_set_linkmode(struct rtnl_link *link, uint8_t linkmode)
+/**
+ * Set link mode of link object
+ * @arg link Link object
+ * @arg mode New link mode
+ *
+ * @route_doc{link_attr_mode, Mode}
+ * @see rtnl_link_get_linkmode()
+ */
+void rtnl_link_set_linkmode(struct rtnl_link *link, uint8_t mode)
{
- link->l_linkmode = linkmode;
+ link->l_linkmode = mode;
link->ce_mask |= LINK_ATTR_LINKMODE;
}
+/**
+ * Return link mode of link object
+ * @arg link Link object
+ *
+ * @route_doc{link_attr_mode, Mode}
+ * @see rtnl_link_get_linkmode()
+ * @return Link mode or \c IF_LINK_MODE_DEFAULT
+ */
uint8_t rtnl_link_get_linkmode(struct rtnl_link *link)
{
- if (link->ce_mask & LINK_ATTR_LINKMODE)
- return link->l_linkmode;
- else
- return IF_LINK_MODE_DEFAULT;
+ return link->l_linkmode;
+}
+
+/**
+ * Return alias name of link object (SNMP IfAlias)
+ * @arg link Link object
+ *
+ * @route_doc{link_attr_alias, Alias}
+ * @see rtnl_link_set_ifalias()
+ * @return Alias name or NULL if not set.
+ */
+const char *rtnl_link_get_ifalias(struct rtnl_link *link)
+{
+ return link->l_ifalias;
+}
+
+/**
+ * Set alias name of link object (SNMP IfAlias)
+ * @arg link Link object
+ * @arg alias Alias name or NULL to unset
+ *
+ * Sets the alias name of the link to the specified name. The alias
+ * name can be unset by specyfing NULL as the alias. The name will
+ * be strdup()ed, so no need to provide a persistent character string.
+ *
+ * @route_doc{link_attr_alias, Alias}
+ * @see rtnl_link_get_ifalias()
+ */
+void rtnl_link_set_ifalias(struct rtnl_link *link, const char *alias)
+{
+ free(link->l_ifalias);
+
+ if (alias) {
+ link->l_ifalias = strdup(alias);
+ link->ce_mask |= LINK_ATTR_IFALIAS;
+ } else {
+ link->l_ifalias = NULL;
+ link->ce_mask &= ~LINK_ATTR_IFALIAS;
+ }
+}
+
+/**
+ * Set queueing discipline name of link object
+ * @arg link Link object
+ * @arg name Name of queueing discipline
+ *
+ * @copydoc read_only_attribute
+ *
+ * For more information on how to modify the qdisc of a link, see section
+ * @ref_route{route_tc, Traffic Control}.
+ *
+ * @route_doc{link_attr_qdisc, Queueing Discipline Name}
+ * @see rtnl_link_get_qdisc()
+ */
+void rtnl_link_set_qdisc(struct rtnl_link *link, const char *name)
+{
+ strncpy(link->l_qdisc, name, sizeof(link->l_qdisc) - 1);
+ link->ce_mask |= LINK_ATTR_QDISC;
+}
+
+/**
+ * Return name of queueing discipline of link object
+ * @arg link Link object
+ *
+ * @route_doc{link_attr_qdisc, Queueing Discipline Name}
+ * @see rtnl_link_set_qdisc()
+ * @return Name of qdisc or NULL if not specified.
+ */
+char *rtnl_link_get_qdisc(struct rtnl_link *link)
+{
+ return link->ce_mask & LINK_ATTR_QDISC ? link->l_qdisc : NULL;
}
-uint64_t rtnl_link_get_stat(struct rtnl_link *link, int id)
+
+/**
+ * Return number of PCI virtual functions of link object
+ * @arg link Link object
+ * @arg num_vf Pointer to store number of VFs
+ *
+ * @return 0 on success or -NLE_OPNOTSUPP if not available
+ */
+int rtnl_link_get_num_vf(struct rtnl_link *link, uint32_t *num_vf)
+{
+ if (link->ce_mask & LINK_ATTR_NUM_VF) {
+ *num_vf = link->l_num_vf;
+ return 0;
+ } else
+ return -NLE_OPNOTSUPP;
+}
+
+/**
+ * Return value of link statistics counter
+ * @arg link Link object
+ * @arg id Identifier of statistical counter
+ *
+ * @return Value of counter or 0 if not specified.
+ */
+uint64_t rtnl_link_get_stat(struct rtnl_link *link, rtnl_link_stat_id_t id)
{
- if (id < 0 || id > RTNL_LINK_STATS_MAX)
+ if (id > RTNL_LINK_STATS_MAX)
return 0;
return link->l_stats[id];
}
/**
- * Specify the info type of a link
- * @arg link link object
- * @arg type info type
+ * Set value of link statistics counter
+ * @arg link Link object
+ * @arg id Identifier of statistical counter
+ * @arg value New value
*
- * Looks up the info type and prepares the link to store info type
- * specific attributes. If an info type has been assigned already
- * it will be released with all changes lost.
+ * \note Changing the value of a statistical counter will not change the
+ * value in the kernel.
*
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_set_stat(struct rtnl_link *link, rtnl_link_stat_id_t id,
+ const uint64_t value)
+{
+ if (id > RTNL_LINK_STATS_MAX)
+ return -NLE_INVAL;
+
+ link->l_stats[id] = value;
+
+ return 0;
+}
+
+/**
+ * Set type of link object
+ * @arg link Link object
+ * @arg type Name of link type
+ *
+ * Looks up the link type module and prepares the link to store type
+ * specific attributes. If a type has been assigned already it will
+ * be released with all link type specific attributes lost.
+ *
+ * @route_doc{link_modules, Link Modules}
* @return 0 on success or a negative errror code.
*/
-int rtnl_link_set_info_type(struct rtnl_link *link, const char *type)
+int rtnl_link_set_type(struct rtnl_link *link, const char *type)
{
struct rtnl_link_info_ops *io;
int err;
+ char *kind;
- if ((io = rtnl_link_info_ops_lookup(type)) == NULL)
- return -NLE_OPNOTSUPP;
-
+ free(link->l_info_kind);
+ link->ce_mask &= ~LINK_ATTR_LINKINFO;
if (link->l_info_ops)
release_link_info(link);
- if ((err = io->io_alloc(link)) < 0)
- return err;
+ if (!type)
+ return 0;
+
+ kind = strdup(type);
+ if (!kind)
+ return -NLE_NOMEM;
+
+ io = rtnl_link_info_ops_lookup(type);
+ if (io) {
+ if (io->io_alloc && (err = io->io_alloc(link)) < 0)
+ goto errout;
+
+ link->l_info_ops = io;
+ }
- link->l_info_ops = io;
+ link->l_info_kind = kind;
+ link->ce_mask |= LINK_ATTR_LINKINFO;
return 0;
+
+errout:
+ free(kind);
+ return err;
+}
+
+/**
+ * Return type of link
+ * @arg link Link object
+ *
+ * @route_doc{link_modules, Link Modules}
+ * @return Name of link type or NULL if not specified.
+ */
+char *rtnl_link_get_type(struct rtnl_link *link)
+{
+ return link->l_info_kind;
+}
+
+/**
+ * Set link promiscuity count
+ * @arg link Link object
+ * @arg count New promiscuity count
+ *
+ * @copydoc read_only_attribute
+ *
+ * @see rtnl_link_get_promiscuity()
+ */
+void rtnl_link_set_promiscuity(struct rtnl_link *link, uint32_t count)
+{
+ link->l_promiscuity = count;
+ link->ce_mask |= LINK_ATTR_PROMISCUITY;
+}
+
+/**
+ * Return link promiscuity count
+ * @arg link Link object
+ *
+ * @see rtnl_link_set_promiscuity()
+ * @return Link promiscuity count or 0
+ */
+uint32_t rtnl_link_get_promiscuity(struct rtnl_link *link)
+{
+ return link->l_promiscuity;
+}
+
+/**
+ * Set number of TX queues
+ * @arg link Link object
+ * @arg nqueues Number of queues
+ *
+ * Sets the number of TX queues of the link object. The value is considered
+ * by the kernel when creating network devices that can be created via
+ * netlink. The value will be passed on to alloc_netdev_mqs()
+ *
+ * Therefore use of rtnl_link_set_num_tx_queues() only makes sense in
+ * combination with rtnl_link_add() or if the link object is used as a filter.
+ *
+ * @see rtnl_link_get_num_tx_queues()
+ */
+void rtnl_link_set_num_tx_queues(struct rtnl_link *link, uint32_t nqueues)
+{
+ link->l_num_tx_queues = nqueues;
+ link->ce_mask |= LINK_ATTR_NUM_TX_QUEUES;
}
/**
- * Return info type of a link
- * @arg link link object
+ * Return number of TX queues
+ * @arg link Link object
*
- * @note The returned pointer is only valid as long as the link exists
- * @return Info type name or NULL if unknown.
+ * @return Number of TX queues or 0
+ */
+uint32_t rtnl_link_get_num_tx_queues(struct rtnl_link *link)
+{
+ return link->l_num_tx_queues;
+}
+
+/**
+ * Set number of RX queues
+ * @arg link Link object
+ * @arg nqueues Number of queues
+ *
+ * Sets the number of RX queues of the link object. The value is considered
+ * by the kernel when creating network devices that can be created via
+ * netlink. The value will be passed on to alloc_netdev_mqs()
+ *
+ * Therefore use of rtnl_link_set_num_rx_queues() only makes sense in
+ * combination with rtnl_link_add() or if the link object is used as a filter.
+ *
+ * @see rtnl_link_get_num_rx_queues()
+ */
+void rtnl_link_set_num_rx_queues(struct rtnl_link *link, uint32_t nqueues)
+{
+ link->l_num_rx_queues = nqueues;
+ link->ce_mask |= LINK_ATTR_NUM_RX_QUEUES;
+}
+
+/**
+ * Return number of RX queues
+ * @arg link Link object
+ *
+ * @return Number of RX queues or 0
+ */
+uint32_t rtnl_link_get_num_rx_queues(struct rtnl_link *link)
+{
+ return link->l_num_rx_queues;
+}
+
+/**
+ * Return physical port id of link object
+ * @arg link Link object
+ *
+ * @return Physical port id or NULL if not set.
+ */
+struct nl_data *rtnl_link_get_phys_port_id(struct rtnl_link *link)
+{
+ return link->l_phys_port_id;
+}
+
+void rtnl_link_set_ns_fd(struct rtnl_link *link, int fd)
+{
+ link->l_ns_fd = fd;
+ link->ce_mask |= LINK_ATTR_NS_FD;
+}
+
+int rtnl_link_get_ns_fd(struct rtnl_link *link)
+{
+ return link->l_ns_fd;
+}
+
+void rtnl_link_set_ns_pid(struct rtnl_link *link, pid_t pid)
+{
+ link->l_ns_pid = pid;
+ link->ce_mask |= LINK_ATTR_NS_PID;
+}
+
+pid_t rtnl_link_get_ns_pid(struct rtnl_link *link)
+{
+ return link->l_ns_pid;
+}
+
+/** @} */
+
+/**
+ * @name Master/Slave
+ * @{
+ */
+
+/**
+ * Enslave slave link to master link
+ * @arg sock netlink socket
+ * @arg master ifindex of master link
+ * @arg slave ifindex of slave link
+ *
+ * This function is identical to rtnl_link_enslave() except that
+ * it takes interface indices instead of rtnl_link objects.
+ *
+ * @see rtnl_link_enslave()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_enslave_ifindex(struct nl_sock *sock, int master, int slave)
+{
+ struct rtnl_link *link;
+ int err;
+
+ if (!(link = rtnl_link_alloc()))
+ return -NLE_NOMEM;
+
+ rtnl_link_set_ifindex(link, slave);
+ rtnl_link_set_master(link, master);
+
+ if ((err = rtnl_link_change(sock, link, link, 0)) < 0)
+ goto errout;
+
+ rtnl_link_put(link);
+
+ /*
+ * Due to the kernel not signaling whether this opertion is
+ * supported or not, we will retrieve the attribute to see if the
+ * request was successful. If the master assigned remains unchanged
+ * we will return NLE_OPNOTSUPP to allow performing backwards
+ * compatibility of some sort.
+ */
+ if ((err = rtnl_link_get_kernel(sock, slave, NULL, &link)) < 0)
+ return err;
+
+ if (rtnl_link_get_master(link) != master)
+ err = -NLE_OPNOTSUPP;
+
+errout:
+ rtnl_link_put(link);
+
+ return err;
+}
+
+/**
+ * Enslave slave link to master link
+ * @arg sock netlink socket
+ * @arg master master link
+ * @arg slave slave link
+ *
+ * Constructs a RTM_NEWLINK or RTM_SETLINK message adding the slave to
+ * the master and sends the request via the specified netlink socket.
+ *
+ * @note The feature of enslaving/releasing via netlink has only been added
+ * recently to the kernel (Feb 2011). Also, the kernel does not signal
+ * if the operation is not supported. Therefore this function will
+ * verify if the master assignment has changed and will return
+ * -NLE_OPNOTSUPP if it did not.
+ *
+ * @see rtnl_link_enslave_ifindex()
+ * @see rtnl_link_release()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_enslave(struct nl_sock *sock, struct rtnl_link *master,
+ struct rtnl_link *slave)
+{
+ return rtnl_link_enslave_ifindex(sock, rtnl_link_get_ifindex(master),
+ rtnl_link_get_ifindex(slave));
+}
+
+/**
+ * Release slave link from its master
+ * @arg sock netlink socket
+ * @arg slave slave link
+ *
+ * This function is identical to rtnl_link_release() except that
+ * it takes an interface index instead of a rtnl_link object.
+ *
+ * @see rtnl_link_release()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_release_ifindex(struct nl_sock *sock, int slave)
+{
+ return rtnl_link_enslave_ifindex(sock, 0, slave);
+}
+
+/**
+ * Release slave link from its master
+ * @arg sock netlink socket
+ * @arg slave slave link
+ *
+ * Constructs a RTM_NEWLINK or RTM_SETLINK message releasing the slave from
+ * its master and sends the request via the specified netlink socket.
+ *
+ * @note The feature of enslaving/releasing via netlink has only been added
+ * recently to the kernel (Feb 2011). Also, the kernel does not signal
+ * if the operation is not supported. Therefore this function will
+ * verify if the master assignment has changed and will return
+ * -NLE_OPNOTSUPP if it did not.
+ *
+ * @see rtnl_link_release_ifindex()
+ * @see rtnl_link_enslave()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_release(struct nl_sock *sock, struct rtnl_link *slave)
+{
+ return rtnl_link_release_ifindex(sock, rtnl_link_get_ifindex(slave));
+}
+
+/** @} */
+
+/**
+ * @name Utilities
+ * @{
+ */
+
+static const 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)
+ __ADD(IFF_ECHO, echo)
+};
+
+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));
+}
+
+static const 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_COLLISIONS, collisions)
+ __ADD(RTNL_LINK_MULTICAST, multicast)
+ __ADD(RTNL_LINK_IP6_INPKTS, Ip6InReceives)
+ __ADD(RTNL_LINK_IP6_INHDRERRORS, Ip6InHdrErrors)
+ __ADD(RTNL_LINK_IP6_INTOOBIGERRORS, Ip6InTooBigErrors)
+ __ADD(RTNL_LINK_IP6_INNOROUTES, Ip6InNoRoutes)
+ __ADD(RTNL_LINK_IP6_INADDRERRORS, Ip6InAddrErrors)
+ __ADD(RTNL_LINK_IP6_INUNKNOWNPROTOS, Ip6InUnknownProtos)
+ __ADD(RTNL_LINK_IP6_INTRUNCATEDPKTS, Ip6InTruncatedPkts)
+ __ADD(RTNL_LINK_IP6_INDISCARDS, Ip6InDiscards)
+ __ADD(RTNL_LINK_IP6_INDELIVERS, Ip6InDelivers)
+ __ADD(RTNL_LINK_IP6_OUTFORWDATAGRAMS, Ip6OutForwDatagrams)
+ __ADD(RTNL_LINK_IP6_OUTPKTS, Ip6OutRequests)
+ __ADD(RTNL_LINK_IP6_OUTDISCARDS, Ip6OutDiscards)
+ __ADD(RTNL_LINK_IP6_OUTNOROUTES, Ip6OutNoRoutes)
+ __ADD(RTNL_LINK_IP6_REASMTIMEOUT, Ip6ReasmTimeout)
+ __ADD(RTNL_LINK_IP6_REASMREQDS, Ip6ReasmReqds)
+ __ADD(RTNL_LINK_IP6_REASMOKS, Ip6ReasmOKs)
+ __ADD(RTNL_LINK_IP6_REASMFAILS, Ip6ReasmFails)
+ __ADD(RTNL_LINK_IP6_FRAGOKS, Ip6FragOKs)
+ __ADD(RTNL_LINK_IP6_FRAGFAILS, Ip6FragFails)
+ __ADD(RTNL_LINK_IP6_FRAGCREATES, Ip6FragCreates)
+ __ADD(RTNL_LINK_IP6_INMCASTPKTS, Ip6InMcastPkts)
+ __ADD(RTNL_LINK_IP6_OUTMCASTPKTS, Ip6OutMcastPkts)
+ __ADD(RTNL_LINK_IP6_INBCASTPKTS, Ip6InBcastPkts)
+ __ADD(RTNL_LINK_IP6_OUTBCASTPKTS, Ip6OutBcastPkts)
+ __ADD(RTNL_LINK_IP6_INOCTETS, Ip6InOctets)
+ __ADD(RTNL_LINK_IP6_OUTOCTETS, Ip6OutOctets)
+ __ADD(RTNL_LINK_IP6_INMCASTOCTETS, Ip6InMcastOctets)
+ __ADD(RTNL_LINK_IP6_OUTMCASTOCTETS, Ip6OutMcastOctets)
+ __ADD(RTNL_LINK_IP6_INBCASTOCTETS, Ip6InBcastOctets)
+ __ADD(RTNL_LINK_IP6_OUTBCASTOCTETS, Ip6OutBcastOctets)
+ __ADD(RTNL_LINK_ICMP6_INMSGS, ICMP6_InMsgs)
+ __ADD(RTNL_LINK_ICMP6_INERRORS, ICMP6_InErrors)
+ __ADD(RTNL_LINK_ICMP6_OUTMSGS, ICMP6_OutMsgs)
+ __ADD(RTNL_LINK_ICMP6_OUTERRORS, ICMP6_OutErrors)
+ __ADD(RTNL_LINK_ICMP6_CSUMERRORS, ICMP6_InCsumErrors)
+ __ADD(RTNL_LINK_IP6_CSUMERRORS, Ip6_InCsumErrors)
+ __ADD(RTNL_LINK_IP6_NOECTPKTS, Ip6_InNoECTPkts)
+ __ADD(RTNL_LINK_IP6_ECT1PKTS, Ip6_InECT1Pkts)
+ __ADD(RTNL_LINK_IP6_ECT0PKTS, Ip6_InECT0Pkts)
+ __ADD(RTNL_LINK_IP6_CEPKTS, Ip6_InCEPkts)
+};
+
+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));
+}
+
+static const struct trans_tbl link_operstates[] = {
+ __ADD(IF_OPER_UNKNOWN, unknown)
+ __ADD(IF_OPER_NOTPRESENT, notpresent)
+ __ADD(IF_OPER_DOWN, down)
+ __ADD(IF_OPER_LOWERLAYERDOWN, lowerlayerdown)
+ __ADD(IF_OPER_TESTING, testing)
+ __ADD(IF_OPER_DORMANT, dormant)
+ __ADD(IF_OPER_UP, up)
+};
+
+char *rtnl_link_operstate2str(uint8_t st, char *buf, size_t len)
+{
+ return __type2str(st, buf, len, link_operstates,
+ ARRAY_SIZE(link_operstates));
+}
+
+int rtnl_link_str2operstate(const char *name)
+{
+ return __str2type(name, link_operstates,
+ ARRAY_SIZE(link_operstates));
+}
+
+static const struct trans_tbl link_modes[] = {
+ __ADD(IF_LINK_MODE_DEFAULT, default)
+ __ADD(IF_LINK_MODE_DORMANT, dormant)
+};
+
+static const struct trans_tbl carrier_states[] = {
+ __ADD(IF_CARRIER_DOWN, down)
+ __ADD(IF_CARRIER_UP, up)
+};
+
+char *rtnl_link_mode2str(uint8_t st, char *buf, size_t len)
+{
+ return __type2str(st, buf, len, link_modes, ARRAY_SIZE(link_modes));
+}
+
+int rtnl_link_str2mode(const char *name)
+{
+ return __str2type(name, link_modes, ARRAY_SIZE(link_modes));
+}
+
+char *rtnl_link_carrier2str(uint8_t st, char *buf, size_t len)
+{
+ return __type2str(st, buf, len, carrier_states,
+ ARRAY_SIZE(carrier_states));
+}
+
+int rtnl_link_str2carrier(const char *name)
+{
+ return __str2type(name, carrier_states, ARRAY_SIZE(carrier_states));
+}
+
+/** @} */
+
+/**
+ * @name Deprecated Functions
+ */
+
+/**
+ * @deprecated Use of this function is deprecated, use rtnl_link_set_type()
+ */
+int rtnl_link_set_info_type(struct rtnl_link *link, const char *type)
+{
+ return rtnl_link_set_type(link, type);
+}
+
+/**
+ * @deprecated Use of this function is deprecated, use rtnl_link_get_type()
*/
char *rtnl_link_get_info_type(struct rtnl_link *link)
{
- if (link->l_info_ops)
- return link->l_info_ops->io_name;
- else
- return NULL;
+ return rtnl_link_get_type(link);
+}
+
+/**
+ * @deprecated The weight attribute is unused and obsoleted in all recent kernels
+ */
+void rtnl_link_set_weight(struct rtnl_link *link, unsigned int weight)
+{
+ link->l_weight = weight;
+ link->ce_mask |= LINK_ATTR_WEIGHT;
+}
+
+/**
+ * @deprecated The weight attribute is unused and obsoleted in all recent kernels
+ */
+unsigned int rtnl_link_get_weight(struct rtnl_link *link)
+{
+ return link->l_weight;
}
/** @} */
@@ -1430,15 +2722,16 @@ static struct nl_object_ops link_obj_ops = {
[NL_DUMP_LINE] = link_dump_line,
[NL_DUMP_DETAILS] = link_dump_details,
[NL_DUMP_STATS] = link_dump_stats,
- [NL_DUMP_ENV] = link_dump_env,
},
.oo_compare = link_compare,
+ .oo_keygen = link_keygen,
.oo_attrs2str = link_attrs2str,
- .oo_id_attrs = LINK_ATTR_IFINDEX,
+ .oo_id_attrs = LINK_ATTR_IFINDEX | LINK_ATTR_FAMILY,
};
static struct nl_af_group link_groups[] = {
{ AF_UNSPEC, RTNLGRP_LINK },
+ { AF_BRIDGE, RTNLGRP_LINK },
{ END_OF_GROUP_LIST },
};
@@ -1449,6 +2742,7 @@ static struct nl_cache_ops rtnl_link_ops = {
{ RTM_NEWLINK, NL_ACT_NEW, "new" },
{ RTM_DELLINK, NL_ACT_DEL, "del" },
{ RTM_GETLINK, NL_ACT_GET, "get" },
+ { RTM_SETLINK, NL_ACT_CHANGE, "set" },
END_OF_MSGTYPES_LIST,
},
.co_protocol = NETLINK_ROUTE,
diff --git a/lib/route/link/api.c b/lib/route/link/api.c
index a0e7679f..6d1e12f8 100644
--- a/lib/route/link/api.c
+++ b/lib/route/link/api.c
@@ -11,8 +11,8 @@
/**
* @ingroup link
- * @defgroup link_info Link Info API
- * @brief
+ * @defgroup link_API Link Modules API
+ * @brief API for modules implementing specific link types/semantics.
*
* @par 1) Registering/Unregistering a new link info type
* @code
@@ -39,60 +39,359 @@
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/link.h>
-#include <netlink/route/link/info-api.h>
+#include <netlink-private/route/link/api.h>
-static struct rtnl_link_info_ops *info_ops;
+static NL_LIST_HEAD(info_ops);
-struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *name)
+/* lock protecting info_ops and af_ops */
+static NL_RW_LOCK(info_lock);
+
+static struct rtnl_link_info_ops *__rtnl_link_info_ops_lookup(const char *name)
{
struct rtnl_link_info_ops *ops;
- for (ops = info_ops; ops; ops = ops->io_next)
+ nl_list_for_each_entry(ops, &info_ops, io_list)
if (!strcmp(ops->io_name, name))
return ops;
return NULL;
}
+/**
+ * @name Link Info Modules
+ * @{
+ */
+
+/**
+ * Return operations of a specific link info type
+ * @arg name Name of link info type.
+ *
+ * @note The returned pointer must be given back using rtnl_link_info_ops_put()
+ *
+ * @return Pointer to operations or NULL if unavailable.
+ */
+struct rtnl_link_info_ops *rtnl_link_info_ops_lookup(const char *name)
+{
+ struct rtnl_link_info_ops *ops;
+
+ nl_write_lock(&info_lock);
+ if ((ops = __rtnl_link_info_ops_lookup(name)))
+ ops->io_refcnt++;
+ nl_write_unlock(&info_lock);
+
+ return ops;
+}
+
+/**
+ * Give back reference to a set of operations.
+ * @arg ops Link info operations.
+ */
+void rtnl_link_info_ops_put(struct rtnl_link_info_ops *ops)
+{
+ if (ops)
+ ops->io_refcnt--;
+}
+
+/**
+ * Register operations for a link info type
+ * @arg ops Link info operations
+ *
+ * This function must be called by modules implementing a specific link
+ * info type. It will make the operations implemented by the module
+ * available for everyone else.
+ *
+ * @return 0 on success or a negative error code.
+ * @return -NLE_INVAL Link info name not specified.
+ * @return -NLE_EXIST Operations for address family already registered.
+ */
int rtnl_link_register_info(struct rtnl_link_info_ops *ops)
{
+ int err = 0;
+
if (ops->io_name == NULL)
return -NLE_INVAL;
- if (rtnl_link_info_ops_lookup(ops->io_name))
- return -NLE_EXIST;
+ nl_write_lock(&info_lock);
+ if (__rtnl_link_info_ops_lookup(ops->io_name)) {
+ err = -NLE_EXIST;
+ goto errout;
+ }
NL_DBG(1, "Registered link info operations %s\n", ops->io_name);
- ops->io_next = info_ops;
- info_ops = ops;
+ nl_list_add_tail(&ops->io_list, &info_ops);
+errout:
+ nl_write_unlock(&info_lock);
- return 0;
+ return err;
}
+/**
+ * Unregister operations for a link info type
+ * @arg ops Link info operations
+ *
+ * This function must be called if a module implementing a specific link
+ * info type is unloaded or becomes unavailable. It must provide a
+ * set of operations which have previously been registered using
+ * rtnl_link_register_info().
+ *
+ * @return 0 on success or a negative error code
+ * @return _NLE_OPNOTSUPP Link info operations not registered.
+ * @return -NLE_BUSY Link info operations still in use.
+ */
int rtnl_link_unregister_info(struct rtnl_link_info_ops *ops)
{
- struct rtnl_link_info_ops *t, **tp;
+ struct rtnl_link_info_ops *t;
+ int err = -NLE_OPNOTSUPP;
+
+ nl_write_lock(&info_lock);
+
+ nl_list_for_each_entry(t, &info_ops, io_list) {
+ if (t == ops) {
+ if (t->io_refcnt > 0) {
+ err = -NLE_BUSY;
+ goto errout;
+ }
+
+ nl_list_del(&t->io_list);
+
+ NL_DBG(1, "Unregistered link info operations %s\n",
+ ops->io_name);
+ err = 0;
+ goto errout;
+ }
+ }
+
+errout:
+ nl_write_unlock(&info_lock);
+
+ return err;
+}
+
+/** @} */
+
+/**
+ * @name Link Address Family Modules
+ * @{
+ */
+
+static struct rtnl_link_af_ops *af_ops[AF_MAX];
+
+/**
+ * Return operations of a specific link address family
+ * @arg family Address family
+ *
+ * @note The returned pointer must be given back using rtnl_link_af_ops_put()
+ *
+ * @return Pointer to operations or NULL if unavailable.
+ */
+struct rtnl_link_af_ops *rtnl_link_af_ops_lookup(const unsigned int family)
+{
+ if (family == AF_UNSPEC || family >= AF_MAX)
+ return NULL;
+
+ nl_write_lock(&info_lock);
+ if (af_ops[family])
+ af_ops[family]->ao_refcnt++;
+ nl_write_unlock(&info_lock);
+
+ return af_ops[family];
+}
+
+/**
+ * Give back reference to a set of operations.
+ * @arg ops Address family operations.
+ */
+void rtnl_link_af_ops_put(struct rtnl_link_af_ops *ops)
+{
+ if (ops)
+ ops->ao_refcnt--;
+}
+
+/**
+ * Allocate and return data buffer for link address family modules
+ * @arg link Link object
+ * @arg ops Address family operations
+ *
+ * This function must be called by link address family modules in all
+ * cases where the API does not provide the data buffer as argument
+ * already. This typically includes set functions the module provides.
+ * Calling this function is strictly required to ensure proper allocation
+ * of the buffer upon first use. Link objects will NOT proactively
+ * allocate a data buffer for each registered link address family.
+ *
+ * @return Pointer to data buffer or NULL on error.
+ */
+void *rtnl_link_af_alloc(struct rtnl_link *link,
+ const struct rtnl_link_af_ops *ops)
+{
+ int family;
+
+ if (!link || !ops)
+ BUG();
+
+ family = ops->ao_family;
+
+ if (!link->l_af_data[family]) {
+ if (!ops->ao_alloc)
+ BUG();
+
+ link->l_af_data[family] = ops->ao_alloc(link);
+ if (!link->l_af_data[family])
+ return NULL;
+ }
+
+ return link->l_af_data[family];
+}
+
+/**
+ * Return data buffer for link address family modules
+ * @arg link Link object
+ * @arg ops Address family operations
+ *
+ * This function returns a pointer to the data buffer for the specified link
+ * address family module or NULL if the buffer was not allocated yet. This
+ * function is typically used by get functions of modules which are not
+ * interested in having the data buffer allocated if no values have been set
+ * yet.
+ *
+ * @return Pointer to data buffer or NULL on error.
+ */
+void *rtnl_link_af_data(const struct rtnl_link *link,
+ const struct rtnl_link_af_ops *ops)
+{
+ if (!link || !ops)
+ BUG();
+
+ return link->l_af_data[ops->ao_family];
+}
+
+/**
+ * Register operations for a link address family
+ * @arg ops Address family operations
+ *
+ * This function must be called by modules implementing a specific link
+ * address family. It will make the operations implemented by the module
+ * available for everyone else.
+ *
+ * @return 0 on success or a negative error code.
+ * @return -NLE_INVAL Address family is out of range (0..AF_MAX)
+ * @return -NLE_EXIST Operations for address family already registered.
+ */
+int rtnl_link_af_register(struct rtnl_link_af_ops *ops)
+{
+ int err = 0;
+
+ if (ops->ao_family == AF_UNSPEC || ops->ao_family >= AF_MAX)
+ return -NLE_INVAL;
+
+ nl_write_lock(&info_lock);
+ if (af_ops[ops->ao_family]) {
+ err = -NLE_EXIST;
+ goto errout;
+ }
- for (tp = &info_ops; (t=*tp) != NULL; tp = &t->io_next)
- if (t == ops)
- break;
+ ops->ao_refcnt = 0;
+ af_ops[ops->ao_family] = ops;
- if (!t)
- return -NLE_OPNOTSUPP;
+ NL_DBG(1, "Registered link address family operations %u\n",
+ ops->ao_family);
- if (t->io_refcnt > 0)
- return -NLE_BUSY;
+errout:
+ nl_write_unlock(&info_lock);
- NL_DBG(1, "Unregistered link info perations %s\n", ops->io_name);
+ return err;
+}
+
+/**
+ * Unregister operations for a link address family
+ * @arg ops Address family operations
+ *
+ * This function must be called if a module implementing a specific link
+ * address family is unloaded or becomes unavailable. It must provide a
+ * set of operations which have previously been registered using
+ * rtnl_link_af_register().
+ *
+ * @return 0 on success or a negative error code
+ * @return -NLE_INVAL ops is NULL
+ * @return -NLE_OBJ_NOTFOUND Address family operations not registered.
+ * @return -NLE_BUSY Address family operations still in use.
+ */
+int rtnl_link_af_unregister(struct rtnl_link_af_ops *ops)
+{
+ int err = -NLE_INVAL;
+
+ if (!ops)
+ return err;
- *tp = t->io_next;
- return 0;
+ nl_write_lock(&info_lock);
+ if (!af_ops[ops->ao_family]) {
+ err = -NLE_OBJ_NOTFOUND;
+ goto errout;
+ }
+
+ if (ops->ao_refcnt > 0) {
+ err = -NLE_BUSY;
+ goto errout;
+ }
+
+ af_ops[ops->ao_family] = NULL;
+
+ NL_DBG(1, "Unregistered link address family operations %u\n",
+ ops->ao_family);
+
+errout:
+ nl_write_unlock(&info_lock);
+
+ return err;
}
+/**
+ * Compare af data for a link address family
+ * @arg a Link object a
+ * @arg b Link object b
+ * @arg family af data family
+ *
+ * This function will compare af_data between two links
+ * a and b of family given by arg family
+ *
+ * @return 0 if address family specific data matches or is not present
+ * or != 0 if it mismatches.
+ */
+int rtnl_link_af_data_compare(struct rtnl_link *a, struct rtnl_link *b,
+ int family)
+{
+ struct rtnl_link_af_ops *af_ops;
+ int ret = 0;
+
+ if (!a->l_af_data[family] && !b->l_af_data[family])
+ return 0;
+
+ if (!a->l_af_data[family] || !b->l_af_data[family])
+ return ~0;
+
+ af_ops = rtnl_link_af_ops_lookup(family);
+ if (!af_ops)
+ return ~0;
+
+ if (af_ops->ao_compare == NULL) {
+ ret = ~0;
+ goto out;
+ }
+
+ ret = af_ops->ao_compare(a, b, family, ~0, 0);
+
+out:
+ rtnl_link_af_ops_put(af_ops);
+
+ return ret;
+}
+
+/** @} */
+
/** @} */
diff --git a/lib/route/link/bonding.c b/lib/route/link/bonding.c
new file mode 100644
index 00000000..f4c520bc
--- /dev/null
+++ b/lib/route/link/bonding.c
@@ -0,0 +1,227 @@
+/*
+ * lib/route/link/bonding.c Bonding Link 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) 2011-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup bonding Bonding
+ *
+ * @details
+ * \b Link Type Name: "bond"
+ *
+ * @route_doc{link_bonding, Bonding Documentation}
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink-private/route/link/api.h>
+
+/**
+ * Allocate link object of type bond
+ *
+ * @return Allocated link object or NULL.
+ */
+struct rtnl_link *rtnl_link_bond_alloc(void)
+{
+ struct rtnl_link *link;
+ int err;
+
+ if (!(link = rtnl_link_alloc()))
+ return NULL;
+
+ if ((err = rtnl_link_set_type(link, "bond")) < 0) {
+ rtnl_link_put(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+/**
+ * Create a new kernel bonding device
+ * @arg sock netlink socket
+ * @arg name name of bonding device or NULL
+ * @arg opts bonding options (currently unused)
+ *
+ * Creates a new bonding device in the kernel. If no name is
+ * provided, the kernel will automatically pick a name of the
+ * form "type%d" (e.g. bond0, vlan1, etc.)
+ *
+ * The \a opts argument is currently unused. In the future, it
+ * may be used to carry additional bonding options to be set
+ * when creating the bonding device.
+ *
+ * @note When letting the kernel assign a name, it will become
+ * difficult to retrieve the interface afterwards because
+ * you have to guess the name the kernel has chosen. It is
+ * therefore not recommended to not provide a device name.
+ *
+ * @see rtnl_link_bond_enslave()
+ * @see rtnl_link_bond_release()
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_bond_add(struct nl_sock *sock, const char *name,
+ struct rtnl_link *opts)
+{
+ struct rtnl_link *link;
+ int err;
+
+ if (!(link = rtnl_link_bond_alloc()))
+ return -NLE_NOMEM;
+
+ if (!name && opts)
+ name = rtnl_link_get_name(opts);
+
+ if (name)
+ rtnl_link_set_name(link, name);
+
+ err = rtnl_link_add(sock, link, NLM_F_CREATE);
+
+ rtnl_link_put(link);
+
+ return err;
+}
+
+/**
+ * Add a link to a bond (enslave)
+ * @arg sock netlink socket
+ * @arg master ifindex of bonding master
+ * @arg slave ifindex of slave link to add to bond
+ *
+ * This function is identical to rtnl_link_bond_enslave() except that
+ * it takes interface indices instead of rtnl_link objcets.
+ *
+ * @see rtnl_link_bond_enslave()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_bond_enslave_ifindex(struct nl_sock *sock, int master,
+ int slave)
+{
+ struct rtnl_link *link;
+ int err;
+
+ if (!(link = rtnl_link_bond_alloc()))
+ return -NLE_NOMEM;
+
+ rtnl_link_set_ifindex(link, slave);
+ rtnl_link_set_master(link, master);
+
+ if ((err = rtnl_link_change(sock, link, link, 0)) < 0)
+ goto errout;
+
+ rtnl_link_put(link);
+
+ /*
+ * Due to the kernel not signaling whether this opertion is
+ * supported or not, we will retrieve the attribute to see if the
+ * request was successful. If the master assigned remains unchanged
+ * we will return NLE_OPNOTSUPP to allow performing backwards
+ * compatibility of some sort.
+ */
+ if ((err = rtnl_link_get_kernel(sock, slave, NULL, &link)) < 0)
+ return err;
+
+ if (rtnl_link_get_master(link) != master)
+ err = -NLE_OPNOTSUPP;
+
+errout:
+ rtnl_link_put(link);
+
+ return err;
+}
+
+/**
+ * Add a link to a bond (enslave)
+ * @arg sock netlink socket
+ * @arg master bonding master
+ * @arg slave slave link to add to bond
+ *
+ * Constructs a RTM_NEWLINK or RTM_SETLINK message adding the slave to
+ * the master and sends the request via the specified netlink socket.
+ *
+ * @note The feature of enslaving/releasing via netlink has only been added
+ * recently to the kernel (Feb 2011). Also, the kernel does not signal
+ * if the operation is not supported. Therefore this function will
+ * verify if the master assignment has changed and will return
+ * -NLE_OPNOTSUPP if it did not.
+ *
+ * @see rtnl_link_bond_enslave_ifindex()
+ * @see rtnl_link_bond_release()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_bond_enslave(struct nl_sock *sock, struct rtnl_link *master,
+ struct rtnl_link *slave)
+{
+ return rtnl_link_bond_enslave_ifindex(sock,
+ rtnl_link_get_ifindex(master),
+ rtnl_link_get_ifindex(slave));
+}
+
+/**
+ * Release a link from a bond
+ * @arg sock netlink socket
+ * @arg slave slave link to be released
+ *
+ * This function is identical to rtnl_link_bond_release() except that
+ * it takes an interface index instead of a rtnl_link object.
+ *
+ * @see rtnl_link_bond_release()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_bond_release_ifindex(struct nl_sock *sock, int slave)
+{
+ return rtnl_link_bond_enslave_ifindex(sock, 0, slave);
+}
+
+/**
+ * Release a link from a bond
+ * @arg sock netlink socket
+ * @arg slave slave link to be released
+ *
+ * Constructs a RTM_NEWLINK or RTM_SETLINK message releasing the slave from
+ * its master and sends the request via the specified netlink socket.
+ *
+ * @note The feature of enslaving/releasing via netlink has only been added
+ * recently to the kernel (Feb 2011). Also, the kernel does not signal
+ * if the operation is not supported. Therefore this function will
+ * verify if the master assignment has changed and will return
+ * -NLE_OPNOTSUPP if it did not.
+ *
+ * @see rtnl_link_bond_release_ifindex()
+ * @see rtnl_link_bond_enslave()
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_bond_release(struct nl_sock *sock, struct rtnl_link *slave)
+{
+ return rtnl_link_bond_release_ifindex(sock,
+ rtnl_link_get_ifindex(slave));
+}
+
+static struct rtnl_link_info_ops bonding_info_ops = {
+ .io_name = "bond",
+};
+
+static void __init bonding_init(void)
+{
+ rtnl_link_register_info(&bonding_info_ops);
+}
+
+static void __exit bonding_exit(void)
+{
+ rtnl_link_unregister_info(&bonding_info_ops);
+}
+
+/** @} */
diff --git a/lib/route/link/bridge.c b/lib/route/link/bridge.c
new file mode 100644
index 00000000..4ca5f6e6
--- /dev/null
+++ b/lib/route/link/bridge.c
@@ -0,0 +1,526 @@
+/*
+ * lib/route/link/bridge.c AF_BRIDGE link support
+ *
+ * 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) 2010-2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup bridge Bridging
+ *
+ * @details
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/route/rtnl.h>
+#include <netlink/route/link/bridge.h>
+#include <netlink-private/route/link/api.h>
+#include <linux/if_bridge.h>
+
+/** @cond SKIP */
+#define BRIDGE_ATTR_PORT_STATE (1 << 0)
+#define BRIDGE_ATTR_PRIORITY (1 << 1)
+#define BRIDGE_ATTR_COST (1 << 2)
+#define BRIDGE_ATTR_FLAGS (1 << 3)
+
+#define PRIV_FLAG_NEW_ATTRS (1 << 0)
+
+struct bridge_data
+{
+ uint8_t b_port_state;
+ uint8_t b_priv_flags; /* internal flags */
+ uint16_t b_priority;
+ uint32_t b_cost;
+ uint32_t b_flags;
+ uint32_t b_flags_mask;
+ uint32_t ce_mask; /* HACK to support attr macros */
+};
+
+static struct rtnl_link_af_ops bridge_ops;
+
+#define IS_BRIDGE_LINK_ASSERT(link) \
+ if (!rtnl_link_is_bridge(link)) { \
+ APPBUG("A function was expecting a link object of type bridge."); \
+ return -NLE_OPNOTSUPP; \
+ }
+
+static inline struct bridge_data *bridge_data(struct rtnl_link *link)
+{
+ return rtnl_link_af_data(link, &bridge_ops);
+}
+
+static void *bridge_alloc(struct rtnl_link *link)
+{
+ return calloc(1, sizeof(struct bridge_data));
+}
+
+static void *bridge_clone(struct rtnl_link *link, void *data)
+{
+ struct bridge_data *bd;
+
+ if ((bd = bridge_alloc(link)))
+ memcpy(bd, data, sizeof(*bd));
+
+ return bd;
+}
+
+static void bridge_free(struct rtnl_link *link, void *data)
+{
+ free(data);
+}
+
+static struct nla_policy br_attrs_policy[IFLA_BRPORT_MAX+1] = {
+ [IFLA_BRPORT_STATE] = { .type = NLA_U8 },
+ [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 },
+ [IFLA_BRPORT_COST] = { .type = NLA_U32 },
+ [IFLA_BRPORT_MODE] = { .type = NLA_U8 },
+ [IFLA_BRPORT_GUARD] = { .type = NLA_U8 },
+ [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 },
+ [IFLA_BRPORT_FAST_LEAVE] = { .type = NLA_U8 },
+};
+
+static void check_flag(struct rtnl_link *link, struct nlattr *attrs[],
+ int type, int flag)
+{
+ if (attrs[type] && nla_get_u8(attrs[type]))
+ rtnl_link_bridge_set_flags(link, flag);
+}
+
+static int bridge_parse_protinfo(struct rtnl_link *link, struct nlattr *attr,
+ void *data)
+{
+ struct bridge_data *bd = data;
+ struct nlattr *br_attrs[IFLA_BRPORT_MAX+1];
+ int err;
+
+ /* Backwards compatibility */
+ if (!nla_is_nested(attr)) {
+ if (nla_len(attr) < 1)
+ return -NLE_RANGE;
+
+ bd->b_port_state = nla_get_u8(attr);
+ bd->ce_mask |= BRIDGE_ATTR_PORT_STATE;
+
+ return 0;
+ }
+
+ if ((err = nla_parse_nested(br_attrs, IFLA_BRPORT_MAX, attr,
+ br_attrs_policy)) < 0)
+ return err;
+
+ bd->b_priv_flags |= PRIV_FLAG_NEW_ATTRS;
+
+ if (br_attrs[IFLA_BRPORT_STATE]) {
+ bd->b_port_state = nla_get_u8(br_attrs[IFLA_BRPORT_STATE]);
+ bd->ce_mask |= BRIDGE_ATTR_PORT_STATE;
+ }
+
+ if (br_attrs[IFLA_BRPORT_PRIORITY]) {
+ bd->b_priority = nla_get_u16(br_attrs[IFLA_BRPORT_PRIORITY]);
+ bd->ce_mask |= BRIDGE_ATTR_PRIORITY;
+ }
+
+ if (br_attrs[IFLA_BRPORT_COST]) {
+ bd->b_cost = nla_get_u32(br_attrs[IFLA_BRPORT_COST]);
+ bd->ce_mask |= BRIDGE_ATTR_COST;
+ }
+
+ check_flag(link, br_attrs, IFLA_BRPORT_MODE, RTNL_BRIDGE_HAIRPIN_MODE);
+ check_flag(link, br_attrs, IFLA_BRPORT_GUARD, RTNL_BRIDGE_BPDU_GUARD);
+ check_flag(link, br_attrs, IFLA_BRPORT_PROTECT, RTNL_BRIDGE_ROOT_BLOCK);
+ check_flag(link, br_attrs, IFLA_BRPORT_FAST_LEAVE, RTNL_BRIDGE_FAST_LEAVE);
+
+ return 0;
+}
+
+static void bridge_dump_details(struct rtnl_link *link,
+ struct nl_dump_params *p, void *data)
+{
+ struct bridge_data *bd = data;
+
+ nl_dump_line(p, " bridge: ");
+
+ if (bd->ce_mask & BRIDGE_ATTR_PORT_STATE)
+ nl_dump(p, "port-state %u ", bd->b_port_state);
+
+ if (bd->ce_mask & BRIDGE_ATTR_PRIORITY)
+ nl_dump(p, "prio %u ", bd->b_priority);
+
+ if (bd->ce_mask & BRIDGE_ATTR_COST)
+ nl_dump(p, "cost %u ", bd->b_cost);
+
+ nl_dump(p, "\n");
+}
+
+static int bridge_compare(struct rtnl_link *_a, struct rtnl_link *_b,
+ int family, uint32_t attrs, int flags)
+{
+ struct bridge_data *a = bridge_data(_a);
+ struct bridge_data *b = bridge_data(_b);
+ int diff = 0;
+
+#define BRIDGE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, BRIDGE_ATTR_##ATTR, a, b, EXPR)
+ diff |= BRIDGE_DIFF(PORT_STATE, a->b_port_state != b->b_port_state);
+ diff |= BRIDGE_DIFF(PRIORITY, a->b_priority != b->b_priority);
+ diff |= BRIDGE_DIFF(COST, a->b_cost != b->b_cost);
+
+ if (flags & LOOSE_COMPARISON)
+ diff |= BRIDGE_DIFF(FLAGS,
+ (a->b_flags ^ b->b_flags) & b->b_flags_mask);
+ else
+ diff |= BRIDGE_DIFF(FLAGS, a->b_flags != b->b_flags);
+#undef BRIDGE_DIFF
+
+ return diff;
+}
+/** @endcond */
+
+/**
+ * Allocate link object of type bridge
+ *
+ * @return Allocated link object or NULL.
+ */
+struct rtnl_link *rtnl_link_bridge_alloc(void)
+{
+ struct rtnl_link *link;
+ int err;
+
+ if (!(link = rtnl_link_alloc()))
+ return NULL;
+
+ if ((err = rtnl_link_set_type(link, "bridge")) < 0) {
+ rtnl_link_put(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+/**
+ * Create a new kernel bridge device
+ * @arg sk netlink socket
+ * @arg name name of the bridge device or NULL
+ *
+ * Creates a new bridge device in the kernel. If no name is
+ * provided, the kernel will automatically pick a name of the
+ * form "type%d" (e.g. bridge0, vlan1, etc.)
+ *
+ * @return 0 on success or a negative error code
+*/
+int rtnl_link_bridge_add(struct nl_sock *sk, const char *name)
+{
+ int err;
+ struct rtnl_link *link;
+
+ if (!(link = rtnl_link_bridge_alloc()))
+ return -NLE_NOMEM;
+
+ if(name)
+ rtnl_link_set_name(link, name);
+
+ err = rtnl_link_add(sk, link, NLM_F_CREATE);
+ rtnl_link_put(link);
+
+ return err;
+}
+
+/**
+ * Check if a link is a bridge
+ * @arg link Link object
+ *
+ * @return 1 if the link is a bridge, 0 otherwise.
+ */
+int rtnl_link_is_bridge(struct rtnl_link *link)
+{
+ return link->l_family == AF_BRIDGE &&
+ link->l_af_ops == &bridge_ops;
+}
+
+/**
+ * Check if bridge has extended information
+ * @arg link Link object of type bridge
+ *
+ * Checks if the bridge object has been constructed based on
+ * information that is only available in newer kernels. This
+ * affectes the following functions:
+ * - rtnl_link_bridge_get_cost()
+ * - rtnl_link_bridge_get_priority()
+ * - rtnl_link_bridge_get_flags()
+ *
+ * @return 1 if extended information is available, otherwise 0 is returned.
+ */
+int rtnl_link_bridge_has_ext_info(struct rtnl_link *link)
+{
+ struct bridge_data *bd;
+
+ if (!rtnl_link_is_bridge(link))
+ return 0;
+
+ bd = bridge_data(link);
+ return !!(bd->b_priv_flags & PRIV_FLAG_NEW_ATTRS);
+}
+
+/**
+ * Set Spanning Tree Protocol (STP) port state
+ * @arg link Link object of type bridge
+ * @arg state New STP port state
+ *
+ * The value of state must be one of the following:
+ * - BR_STATE_DISABLED
+ * - BR_STATE_LISTENING
+ * - BR_STATE_LEARNING
+ * - BR_STATE_FORWARDING
+ * - BR_STATE_BLOCKING
+ *
+ * @see rtnl_link_bridge_get_port_state()
+ *
+ * @return 0 on success or a negative error code.
+ * @retval -NLE_OPNOTSUPP Link is not a bridge
+ * @retval -NLE_INVAL Invalid state value (0..BR_STATE_BLOCKING)
+ */
+int rtnl_link_bridge_set_port_state(struct rtnl_link *link, uint8_t state)
+{
+ struct bridge_data *bd = bridge_data(link);
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ if (state > BR_STATE_BLOCKING)
+ return -NLE_INVAL;
+
+ bd->b_port_state = state;
+ bd->ce_mask |= BRIDGE_ATTR_PORT_STATE;
+
+ return 0;
+}
+
+/**
+ * Get Spanning Tree Protocol (STP) port state
+ * @arg link Link object of type bridge
+ *
+ * @see rtnl_link_bridge_set_port_state()
+ *
+ * @return The STP port state or a negative error code.
+ * @retval -NLE_OPNOTSUPP Link is not a bridge
+ */
+int rtnl_link_bridge_get_port_state(struct rtnl_link *link)
+{
+ struct bridge_data *bd = bridge_data(link);
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ return bd->b_port_state;
+}
+
+/**
+ * Set priority
+ * @arg link Link object of type bridge
+ * @arg prio Bridge priority
+ *
+ * @see rtnl_link_bridge_get_priority()
+ *
+ * @return 0 on success or a negative error code.
+ * @retval -NLE_OPNOTSUPP Link is not a bridge
+ */
+int rtnl_link_bridge_set_priority(struct rtnl_link *link, uint16_t prio)
+{
+ struct bridge_data *bd = bridge_data(link);
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ bd->b_priority = prio;
+ bd->ce_mask |= BRIDGE_ATTR_PRIORITY;
+
+ return 0;
+}
+
+/**
+ * Get priority
+ * @arg link Link object of type bridge
+ *
+ * @see rtnl_link_bridge_set_priority()
+ *
+ * @return 0 on success or a negative error code.
+ * @retval -NLE_OPNOTSUPP Link is not a bridge
+ */
+int rtnl_link_bridge_get_priority(struct rtnl_link *link)
+{
+ struct bridge_data *bd = bridge_data(link);
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ return bd->b_priority;
+}
+
+/**
+ * Set Spanning Tree Protocol (STP) path cost
+ * @arg link Link object of type bridge
+ * @arg cost New STP path cost value
+ *
+ * @see rtnl_link_bridge_get_cost()
+ *
+ * @return The bridge priority or a negative error code.
+ * @retval -NLE_OPNOTSUPP Link is not a bridge
+ */
+int rtnl_link_bridge_set_cost(struct rtnl_link *link, uint32_t cost)
+{
+ struct bridge_data *bd = bridge_data(link);
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ bd->b_cost = cost;
+ bd->ce_mask |= BRIDGE_ATTR_COST;
+
+ return 0;
+}
+
+/**
+ * Get Spanning Tree Protocol (STP) path cost
+ * @arg link Link object of type bridge
+ * @arg cost Pointer to store STP cost value
+ *
+ * @see rtnl_link_bridge_set_cost()
+ *
+ * @return 0 on success or a negative error code.
+ * @retval -NLE_OPNOTSUPP Link is not a bridge
+ * @retval -NLE_INVAL `cost` is not a valid pointer
+ */
+int rtnl_link_bridge_get_cost(struct rtnl_link *link, uint32_t *cost)
+{
+ struct bridge_data *bd = bridge_data(link);
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ if (!cost)
+ return -NLE_INVAL;
+
+ *cost = bd->b_cost;
+
+ return 0;
+}
+
+/**
+ * Unset flags
+ * @arg link Link object of type bridge
+ * @arg flags Bridging flags to unset
+ *
+ * @see rtnl_link_bridge_set_flags()
+ * @see rtnl_link_bridge_get_flags()
+ *
+ * @return 0 on success or a negative error code.
+ * @retval -NLE_OPNOTSUPP Link is not a bridge
+ */
+int rtnl_link_bridge_unset_flags(struct rtnl_link *link, unsigned int flags)
+{
+ struct bridge_data *bd = bridge_data(link);
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ bd->b_flags_mask |= flags;
+ bd->b_flags &= ~flags;
+ bd->ce_mask |= BRIDGE_ATTR_FLAGS;
+
+ return 0;
+}
+
+/**
+ * Set flags
+ * @arg link Link object of type bridge
+ * @arg flags Bridging flags to set
+ *
+ * Valid flags are:
+ * - RTNL_BRIDGE_HAIRPIN_MODE
+ * - RTNL_BRIDGE_BPDU_GUARD
+ * - RTNL_BRIDGE_ROOT_BLOCK
+ * - RTNL_BRIDGE_FAST_LEAVE
+ *
+ * @see rtnl_link_bridge_unset_flags()
+ * @see rtnl_link_bridge_get_flags()
+ *
+ * @return 0 on success or a negative error code.
+ * @retval -NLE_OPNOTSUPP Link is not a bridge
+ */
+int rtnl_link_bridge_set_flags(struct rtnl_link *link, unsigned int flags)
+{
+ struct bridge_data *bd = bridge_data(link);
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ bd->b_flags_mask |= flags;
+ bd->b_flags |= flags;
+ bd->ce_mask |= BRIDGE_ATTR_FLAGS;
+
+ return 0;
+}
+
+/**
+ * Get flags
+ * @arg link Link object of type bridge
+ *
+ * @see rtnl_link_bridge_set_flags()
+ * @see rtnl_link_bridge_unset_flags()
+ *
+ * @return Flags or a negative error code.
+ * @retval -NLE_OPNOTSUPP Link is not a bridge
+ */
+int rtnl_link_bridge_get_flags(struct rtnl_link *link)
+{
+ struct bridge_data *bd = bridge_data(link);
+
+ IS_BRIDGE_LINK_ASSERT(link);
+
+ return bd->b_flags;
+}
+
+static const struct trans_tbl bridge_flags[] = {
+ __ADD(RTNL_BRIDGE_HAIRPIN_MODE, hairpin_mode)
+ __ADD(RTNL_BRIDGE_BPDU_GUARD, bpdu_guard)
+ __ADD(RTNL_BRIDGE_ROOT_BLOCK, root_block)
+ __ADD(RTNL_BRIDGE_FAST_LEAVE, fast_leave)
+};
+
+/**
+ * @name Flag Translation
+ * @{
+ */
+
+char *rtnl_link_bridge_flags2str(int flags, char *buf, size_t len)
+{
+ return __flags2str(flags, buf, len, bridge_flags, ARRAY_SIZE(bridge_flags));
+}
+
+int rtnl_link_bridge_str2flags(const char *name)
+{
+ return __str2flags(name, bridge_flags, ARRAY_SIZE(bridge_flags));
+}
+
+/** @} */
+
+static struct rtnl_link_af_ops bridge_ops = {
+ .ao_family = AF_BRIDGE,
+ .ao_alloc = &bridge_alloc,
+ .ao_clone = &bridge_clone,
+ .ao_free = &bridge_free,
+ .ao_parse_protinfo = &bridge_parse_protinfo,
+ .ao_dump[NL_DUMP_DETAILS] = &bridge_dump_details,
+ .ao_compare = &bridge_compare,
+};
+
+static void __init bridge_init(void)
+{
+ rtnl_link_af_register(&bridge_ops);
+}
+
+static void __exit bridge_exit(void)
+{
+ rtnl_link_af_unregister(&bridge_ops);
+}
+
+/** @} */
diff --git a/lib/route/link/can.c b/lib/route/link/can.c
new file mode 100644
index 00000000..60da42db
--- /dev/null
+++ b/lib/route/link/can.c
@@ -0,0 +1,784 @@
+/*
+ * lib/route/link/can.c CAN Link Info
+ *
+ * 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) 2012 Benedikt Spranger <b.spranger@linutronix.de>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup can CAN
+ * Controller Area Network link module
+ *
+ * @details
+ * \b Link Type Name: "can"
+ *
+ * @route_doc{link_can, CAN Documentation}
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <netlink/route/link/can.h>
+
+#include <linux/can/netlink.h>
+
+/** @cond SKIP */
+#define CAN_HAS_BITTIMING (1<<0)
+#define CAN_HAS_BITTIMING_CONST (1<<1)
+#define CAN_HAS_CLOCK (1<<2)
+#define CAN_HAS_STATE (1<<3)
+#define CAN_HAS_CTRLMODE (1<<4)
+#define CAN_HAS_RESTART_MS (1<<5)
+#define CAN_HAS_RESTART (1<<6)
+#define CAN_HAS_BERR_COUNTER (1<<7)
+
+struct can_info {
+ uint32_t ci_state;
+ uint32_t ci_restart;
+ uint32_t ci_restart_ms;
+ struct can_ctrlmode ci_ctrlmode;
+ struct can_bittiming ci_bittiming;
+ struct can_bittiming_const ci_bittiming_const;
+ struct can_clock ci_clock;
+ struct can_berr_counter ci_berr_counter;
+ uint32_t ci_mask;
+};
+
+/** @endcond */
+
+static struct nla_policy can_policy[IFLA_CAN_MAX + 1] = {
+ [IFLA_CAN_STATE] = { .type = NLA_U32 },
+ [IFLA_CAN_CTRLMODE] = { .minlen = sizeof(struct can_ctrlmode) },
+ [IFLA_CAN_RESTART_MS] = { .type = NLA_U32 },
+ [IFLA_CAN_RESTART] = { .type = NLA_U32 },
+ [IFLA_CAN_BITTIMING] = { .minlen = sizeof(struct can_bittiming) },
+ [IFLA_CAN_BITTIMING_CONST]
+ = { .minlen = sizeof(struct can_bittiming_const) },
+ [IFLA_CAN_CLOCK] = { .minlen = sizeof(struct can_clock) },
+ [IFLA_CAN_BERR_COUNTER] = { .minlen = sizeof(struct can_berr_counter) },
+};
+
+static int can_alloc(struct rtnl_link *link)
+{
+ struct can_info *ci;
+
+ ci = calloc(1, sizeof(*ci));
+ if (!ci)
+ return -NLE_NOMEM;
+
+ link->l_info = ci;
+
+ return 0;
+}
+
+static int can_parse(struct rtnl_link *link, struct nlattr *data,
+ struct nlattr *xstats)
+{
+ struct nlattr *tb[IFLA_CAN_MAX+1];
+ struct can_info *ci;
+ int err;
+
+ NL_DBG(3, "Parsing CAN link info");
+
+ if ((err = nla_parse_nested(tb, IFLA_CAN_MAX, data, can_policy)) < 0)
+ goto errout;
+
+ if ((err = can_alloc(link)) < 0)
+ goto errout;
+
+ ci = link->l_info;
+
+ if (tb[IFLA_CAN_STATE]) {
+ ci->ci_state = nla_get_u32(tb[IFLA_CAN_STATE]);
+ ci->ci_mask |= CAN_HAS_STATE;
+ }
+
+ if (tb[IFLA_CAN_RESTART]) {
+ ci->ci_restart = nla_get_u32(tb[IFLA_CAN_RESTART]);
+ ci->ci_mask |= CAN_HAS_RESTART;
+ }
+
+ if (tb[IFLA_CAN_RESTART_MS]) {
+ ci->ci_restart_ms = nla_get_u32(tb[IFLA_CAN_RESTART_MS]);
+ ci->ci_mask |= CAN_HAS_RESTART_MS;
+ }
+
+ if (tb[IFLA_CAN_CTRLMODE]) {
+ nla_memcpy(&ci->ci_ctrlmode, tb[IFLA_CAN_CTRLMODE],
+ sizeof(ci->ci_ctrlmode));
+ ci->ci_mask |= CAN_HAS_CTRLMODE;
+ }
+
+ if (tb[IFLA_CAN_BITTIMING]) {
+ nla_memcpy(&ci->ci_bittiming, tb[IFLA_CAN_BITTIMING],
+ sizeof(ci->ci_bittiming));
+ ci->ci_mask |= CAN_HAS_BITTIMING;
+ }
+
+ if (tb[IFLA_CAN_BITTIMING_CONST]) {
+ nla_memcpy(&ci->ci_bittiming_const,
+ tb[IFLA_CAN_BITTIMING_CONST],
+ sizeof(ci->ci_bittiming_const));
+ ci->ci_mask |= CAN_HAS_BITTIMING_CONST;
+ }
+
+ if (tb[IFLA_CAN_CLOCK]) {
+ nla_memcpy(&ci->ci_clock, tb[IFLA_CAN_CLOCK],
+ sizeof(ci->ci_clock));
+ ci->ci_mask |= CAN_HAS_CLOCK;
+ }
+
+ if (tb[IFLA_CAN_BERR_COUNTER]) {
+ nla_memcpy(&ci->ci_berr_counter, tb[IFLA_CAN_BERR_COUNTER],
+ sizeof(ci->ci_berr_counter));
+ ci->ci_mask |= CAN_HAS_BERR_COUNTER;
+ }
+
+ err = 0;
+errout:
+ return err;
+}
+
+static void can_free(struct rtnl_link *link)
+{
+ struct can_info *ci = link->l_info;
+
+ free(ci);
+ link->l_info = NULL;
+}
+
+static char *print_can_state (uint32_t state)
+{
+ char *text;
+
+ switch (state)
+ {
+ case CAN_STATE_ERROR_ACTIVE:
+ text = "error active";
+ break;
+ case CAN_STATE_ERROR_WARNING:
+ text = "error warning";
+ break;
+ case CAN_STATE_ERROR_PASSIVE:
+ text = "error passive";
+ break;
+ case CAN_STATE_BUS_OFF:
+ text = "bus off";
+ break;
+ case CAN_STATE_STOPPED:
+ text = "stopped";
+ break;
+ case CAN_STATE_SLEEPING:
+ text = "sleeping";
+ break;
+ default:
+ text = "unknown state";
+ }
+
+ return text;
+}
+
+static void can_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ struct can_info *ci = link->l_info;
+ char buf [64];
+
+ rtnl_link_can_ctrlmode2str(ci->ci_ctrlmode.flags, buf, sizeof(buf));
+ nl_dump(p, "bitrate %d %s <%s>",
+ ci->ci_bittiming.bitrate, print_can_state(ci->ci_state), buf);
+}
+
+static void can_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ struct can_info *ci = link->l_info;
+ char buf [64];
+
+ rtnl_link_can_ctrlmode2str(ci->ci_ctrlmode.flags, buf, sizeof(buf));
+ nl_dump(p, " bitrate %d %s <%s>",
+ ci->ci_bittiming.bitrate, print_can_state(ci->ci_state), buf);
+
+ if (ci->ci_mask & CAN_HAS_RESTART) {
+ if (ci->ci_restart)
+ nl_dump_line(p," restarting\n");
+ }
+
+ if (ci->ci_mask & CAN_HAS_RESTART_MS) {
+ nl_dump_line(p," restart interval %d ms\n",
+ ci->ci_restart_ms);
+ }
+
+ if (ci->ci_mask & CAN_HAS_BITTIMING) {
+ nl_dump_line(p," sample point %f %%\n",
+ ((float) ci->ci_bittiming.sample_point)/10);
+ nl_dump_line(p," time quanta %d ns\n",
+ ci->ci_bittiming.tq);
+ nl_dump_line(p," propagation segment %d tq\n",
+ ci->ci_bittiming.prop_seg);
+ nl_dump_line(p," phase buffer segment1 %d tq\n",
+ ci->ci_bittiming.phase_seg1);
+ nl_dump_line(p," phase buffer segment2 %d tq\n",
+ ci->ci_bittiming.phase_seg2);
+ nl_dump_line(p," synchronisation jump width %d tq\n",
+ ci->ci_bittiming.sjw);
+ nl_dump_line(p," bitrate prescaler %d\n",
+ ci->ci_bittiming.brp);
+ }
+
+ if (ci->ci_mask & CAN_HAS_BITTIMING_CONST) {
+ nl_dump_line(p," minimum tsig1 %d tq\n",
+ ci->ci_bittiming_const.tseg1_min);
+ nl_dump_line(p," maximum tsig1 %d tq\n",
+ ci->ci_bittiming_const.tseg1_max);
+ nl_dump_line(p," minimum tsig2 %d tq\n",
+ ci->ci_bittiming_const.tseg2_min);
+ nl_dump_line(p," maximum tsig2 %d tq\n",
+ ci->ci_bittiming_const.tseg2_max);
+ nl_dump_line(p," maximum sjw %d tq\n",
+ ci->ci_bittiming_const.sjw_max);
+ nl_dump_line(p," minimum brp %d\n",
+ ci->ci_bittiming_const.brp_min);
+ nl_dump_line(p," maximum brp %d\n",
+ ci->ci_bittiming_const.brp_max);
+ nl_dump_line(p," brp increment %d\n",
+ ci->ci_bittiming_const.brp_inc);
+ }
+
+ if (ci->ci_mask & CAN_HAS_CLOCK) {
+ nl_dump_line(p," base freq %d Hz\n", ci->ci_clock);
+
+ }
+
+ if (ci->ci_mask & CAN_HAS_BERR_COUNTER) {
+ nl_dump_line(p," bus error RX %d\n",
+ ci->ci_berr_counter.rxerr);
+ nl_dump_line(p," bus error TX %d\n",
+ ci->ci_berr_counter.txerr);
+ }
+
+ return;
+}
+
+static int can_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+ struct can_info *cdst, *csrc = src->l_info;
+ int ret;
+
+ dst->l_info = NULL;
+ ret = rtnl_link_set_type(dst, "can");
+ if (ret < 0)
+ return ret;
+
+ cdst = malloc(sizeof(*cdst));
+ if (!cdst)
+ return -NLE_NOMEM;
+
+ *cdst = *csrc;
+ dst->l_info = cdst;
+
+ return 0;
+}
+
+static int can_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+ struct can_info *ci = link->l_info;
+ struct nlattr *data;
+
+ data = nla_nest_start(msg, IFLA_INFO_DATA);
+ if (!data)
+ return -NLE_MSGSIZE;
+
+ if (ci->ci_mask & CAN_HAS_RESTART)
+ NLA_PUT_U32(msg, CAN_HAS_RESTART, ci->ci_restart);
+
+ if (ci->ci_mask & CAN_HAS_RESTART_MS)
+ NLA_PUT_U32(msg, CAN_HAS_RESTART_MS, ci->ci_restart_ms);
+
+ if (ci->ci_mask & CAN_HAS_CTRLMODE)
+ NLA_PUT(msg, CAN_HAS_CTRLMODE, sizeof(ci->ci_ctrlmode),
+ &ci->ci_ctrlmode);
+
+ if (ci->ci_mask & CAN_HAS_BITTIMING)
+ NLA_PUT(msg, CAN_HAS_BITTIMING, sizeof(ci->ci_bittiming),
+ &ci->ci_bittiming);
+
+ if (ci->ci_mask & CAN_HAS_BITTIMING_CONST)
+ NLA_PUT(msg, CAN_HAS_BITTIMING_CONST,
+ sizeof(ci->ci_bittiming_const),
+ &ci->ci_bittiming_const);
+
+ if (ci->ci_mask & CAN_HAS_CLOCK)
+ NLA_PUT(msg, CAN_HAS_CLOCK, sizeof(ci->ci_clock),
+ &ci->ci_clock);
+
+ nla_nest_end(msg, data);
+
+nla_put_failure:
+
+ return 0;
+}
+
+static struct rtnl_link_info_ops can_info_ops = {
+ .io_name = "can",
+ .io_alloc = can_alloc,
+ .io_parse = can_parse,
+ .io_dump = {
+ [NL_DUMP_LINE] = can_dump_line,
+ [NL_DUMP_DETAILS] = can_dump_details,
+ },
+ .io_clone = can_clone,
+ .io_put_attrs = can_put_attrs,
+ .io_free = can_free,
+};
+
+/** @cond SKIP */
+#define IS_CAN_LINK_ASSERT(link) \
+ if ((link)->l_info_ops != &can_info_ops) { \
+ APPBUG("Link is not a CAN link. set type \"can\" first."); \
+ return -NLE_OPNOTSUPP; \
+ }
+/** @endcond */
+
+/**
+ * @name CAN Object
+ * @{
+ */
+
+/**
+ * Check if link is a CAN link
+ * @arg link Link object
+ *
+ * @return True if link is a CAN link, otherwise false is returned.
+ */
+int rtnl_link_is_can(struct rtnl_link *link)
+{
+ return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "can");
+}
+
+/**
+ * Restart CAN device
+ * @arg link Link object
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_restart(struct rtnl_link *link)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+
+ ci->ci_restart = 1;
+ ci->ci_restart |= CAN_HAS_RESTART;
+
+ return 0;
+}
+
+/**
+ * Get CAN base frequency
+ * @arg link Link object
+ * @arg freq frequency in Hz
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_freq(struct rtnl_link *link, uint32_t *freq)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+ if (!freq)
+ return -NLE_INVAL;
+
+ if (ci->ci_mask & CAN_HAS_CLOCK)
+ *freq = ci->ci_clock.freq;
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Get CAN state
+ * @arg link Link object
+ * @arg state CAN bus state
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_state(struct rtnl_link *link, uint32_t *state)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+ if (!state)
+ return -NLE_INVAL;
+
+ *state = ci->ci_state;
+
+ return 0;
+}
+
+/**
+ * Get CAN RX bus error count
+ * @arg link Link object
+ *
+ * @return RX bus error count on success or a negative error code
+ */
+int rtnl_link_can_berr_rx(struct rtnl_link *link)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+
+ if (ci->ci_mask & CAN_HAS_BERR_COUNTER)
+ return ci->ci_berr_counter.rxerr;
+ else
+ return -NLE_AGAIN;
+}
+
+/**
+ * Get CAN TX bus error count
+ * @arg link Link object
+ *
+ * @return TX bus error count on success or a negative error code
+ */
+int rtnl_link_can_berr_tx(struct rtnl_link *link)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+
+ if (ci->ci_mask & CAN_HAS_BERR_COUNTER)
+ return ci->ci_berr_counter.txerr;
+ else
+ return -NLE_AGAIN;
+}
+
+/**
+ * Get CAN bus error count
+ * @arg link Link object
+ * @arg berr Bus error count
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_berr(struct rtnl_link *link, struct can_berr_counter *berr)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+ if (!berr)
+ return -NLE_INVAL;
+
+ if (ci->ci_mask & CAN_HAS_BERR_COUNTER)
+ *berr = ci->ci_berr_counter;
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Get CAN harware-dependent bit-timing constant
+ * @arg link Link object
+ * @arg bt_const Bit-timing constant
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_get_bt_const(struct rtnl_link *link,
+ struct can_bittiming_const *bt_const)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+ if (!bt_const)
+ return -NLE_INVAL;
+
+ if (ci->ci_mask & CAN_HAS_BITTIMING_CONST)
+ *bt_const = ci->ci_bittiming_const;
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Get CAN device bit-timing
+ * @arg link Link object
+ * @arg bit_timing CAN bit-timing
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_get_bittiming(struct rtnl_link *link,
+ struct can_bittiming *bit_timing)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+ if (!bit_timing)
+ return -NLE_INVAL;
+
+ if (ci->ci_mask & CAN_HAS_BITTIMING)
+ *bit_timing = ci->ci_bittiming;
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Set CAN device bit-timing
+ * @arg link Link object
+ * @arg bit_timing CAN bit-timing
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_set_bittiming(struct rtnl_link *link,
+ struct can_bittiming *bit_timing)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+ if (!bit_timing)
+ return -NLE_INVAL;
+
+ ci->ci_bittiming = *bit_timing;
+ ci->ci_mask |= CAN_HAS_BITTIMING;
+
+ return 0;
+}
+
+/**
+ * Get CAN device bit-timing
+ * @arg link Link object
+ * @arg bitrate CAN bitrate
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_get_bitrate(struct rtnl_link *link, uint32_t *bitrate)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+ if (!bitrate)
+ return -NLE_INVAL;
+
+ if (ci->ci_mask & CAN_HAS_BITTIMING)
+ *bitrate = ci->ci_bittiming.bitrate;
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Set CAN device bit-rate
+ * @arg link Link object
+ * @arg bitrate CAN bitrate
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_set_bitrate(struct rtnl_link *link, uint32_t bitrate)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+
+ ci->ci_bittiming.bitrate = bitrate;
+ ci->ci_mask |= CAN_HAS_BITTIMING;
+
+ return 0;
+}
+
+/**
+ * Get CAN device sample point
+ * @arg link Link object
+ * @arg sp CAN sample point
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_get_sample_point(struct rtnl_link *link, uint32_t *sp)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+ if (!sp)
+ return -NLE_INVAL;
+
+ if (ci->ci_mask & CAN_HAS_BITTIMING)
+ *sp = ci->ci_bittiming.sample_point;
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Set CAN device sample point
+ * @arg link Link object
+ * @arg sp CAN sample point
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_set_sample_point(struct rtnl_link *link, uint32_t sp)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+
+ ci->ci_bittiming.sample_point = sp;
+ ci->ci_mask |= CAN_HAS_BITTIMING;
+
+ return 0;
+}
+
+/**
+ * Get CAN device restart intervall
+ * @arg link Link object
+ * @arg interval Restart intervall in ms
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_get_restart_ms(struct rtnl_link *link, uint32_t *interval)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+ if (!interval)
+ return -NLE_INVAL;
+
+ if (ci->ci_mask & CAN_HAS_RESTART_MS)
+ *interval = ci->ci_restart_ms;
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Set CAN device restart intervall
+ * @arg link Link object
+ * @arg interval Restart intervall in ms
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_set_restart_ms(struct rtnl_link *link, uint32_t interval)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+
+ ci->ci_restart_ms = interval;
+ ci->ci_mask |= CAN_HAS_RESTART_MS;
+
+ return 0;
+}
+
+/**
+ * Get CAN control mode
+ * @arg link Link object
+ * @arg ctrlmode CAN control mode
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_get_ctrlmode(struct rtnl_link *link, uint32_t *ctrlmode)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+ if (!ctrlmode)
+ return -NLE_INVAL;
+
+ if (ci->ci_mask & CAN_HAS_CTRLMODE)
+ *ctrlmode = ci->ci_ctrlmode.flags;
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Set a CAN Control Mode
+ * @arg link Link object
+ * @arg ctrlmode CAN control mode
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_set_ctrlmode(struct rtnl_link *link, uint32_t ctrlmode)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+
+ ci->ci_ctrlmode.flags |= ctrlmode;
+ ci->ci_ctrlmode.mask |= ctrlmode;
+ ci->ci_mask |= CAN_HAS_CTRLMODE;
+
+ return 0;
+}
+
+/**
+ * Unset a CAN Control Mode
+ * @arg link Link object
+ * @arg ctrlmode CAN control mode
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_can_unset_ctrlmode(struct rtnl_link *link, uint32_t ctrlmode)
+{
+ struct can_info *ci = link->l_info;
+
+ IS_CAN_LINK_ASSERT(link);
+
+ ci->ci_ctrlmode.flags &= ~ctrlmode;
+ ci->ci_ctrlmode.mask |= ctrlmode;
+ ci->ci_mask |= CAN_HAS_CTRLMODE;
+
+ return 0;
+}
+
+/** @} */
+
+/**
+ * @name Control Mode Translation
+ * @{
+ */
+
+static const struct trans_tbl can_ctrlmode[] = {
+ __ADD(CAN_CTRLMODE_LOOPBACK, loopback)
+ __ADD(CAN_CTRLMODE_LISTENONLY, listen-only)
+ __ADD(CAN_CTRLMODE_3_SAMPLES, triple-sampling)
+ __ADD(CAN_CTRLMODE_ONE_SHOT, one-shot)
+ __ADD(CAN_CTRLMODE_BERR_REPORTING, berr-reporting)
+};
+
+char *rtnl_link_can_ctrlmode2str(int ctrlmode, char *buf, size_t len)
+{
+ return __flags2str(ctrlmode, buf, len, can_ctrlmode,
+ ARRAY_SIZE(can_ctrlmode));
+}
+
+int rtnl_link_can_str2ctrlmode(const char *name)
+{
+ return __str2flags(name, can_ctrlmode, ARRAY_SIZE(can_ctrlmode));
+}
+
+/** @} */
+
+static void __init can_init(void)
+{
+ rtnl_link_register_info(&can_info_ops);
+}
+
+static void __exit can_exit(void)
+{
+ rtnl_link_unregister_info(&can_info_ops);
+}
+
+/** @} */
diff --git a/lib/route/link/dummy.c b/lib/route/link/dummy.c
new file mode 100644
index 00000000..1fd9f5a3
--- /dev/null
+++ b/lib/route/link/dummy.c
@@ -0,0 +1,40 @@
+/*
+ * lib/route/link/dummy.c Dummy 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) 2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup dummy Dummy
+ *
+ * @details
+ * \b Link Type Name: "dummy"
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink-private/route/link/api.h>
+
+static struct rtnl_link_info_ops dummy_info_ops = {
+ .io_name = "dummy",
+};
+
+static void __init dummy_init(void)
+{
+ rtnl_link_register_info(&dummy_info_ops);
+}
+
+static void __exit dummy_exit(void)
+{
+ rtnl_link_unregister_info(&dummy_info_ops);
+}
+
+/** @} */
diff --git a/lib/route/link/inet.c b/lib/route/link/inet.c
new file mode 100644
index 00000000..e94342f8
--- /dev/null
+++ b/lib/route/link/inet.c
@@ -0,0 +1,295 @@
+/*
+ * lib/route/link/inet.c AF_INET link operations
+ *
+ * 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup link_API
+ * @defgroup link_inet IPv4 Link Module
+ * @brief Implementation of IPv4 specific link attributes
+ *
+ *
+ *
+ * @par Example: Reading the value of IPV4_DEVCONF_FORWARDING
+ * @code
+ * struct nl_cache *cache;
+ * struct rtnl_link *link;
+ * uint32_t value;
+ *
+ * // Allocate a link cache
+ * rtnl_link_alloc_cache(sock, AF_UNSPEC, &cache);
+ *
+ * // Search for the link we wish to see the value from
+ * link = rtnl_link_get_by_name(cache, "eth0");
+ *
+ * // Read the value of the setting IPV4_DEVCONF_FORWARDING
+ * if (rtnl_link_inet_get_conf(link, IPV4_DEVCONF_FORWARDING, &value) < 0)
+ * // Error: Unable to read config setting
+ *
+ * printf("forwarding is %s\n", value ? "enabled" : "disabled");
+ * @endcode
+ *
+ * @par Example: Changing the value of IPV4_DEVCONF_FOWARDING
+ * @code
+ * //
+ * // ... Continueing from the previous example ...
+ * //
+ *
+ * struct rtnl_link *new;
+ *
+ * // Allocate a new link to store the changes we wish to make.
+ * new = rtnl_link_alloc();
+ *
+ * // Set IPV4_DEVCONF_FORWARDING to '1'
+ * rtnl_link_inet_set_conf(new, IPV4_DEVCONF_FORWARDING, 1);
+ *
+ * // Send the change request to the kernel.
+ * rtnl_link_change(sock, link, new, 0);
+ * @endcode
+ *
+ * @{
+ */
+
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+
+/** @cond SKIP */
+struct inet_data
+{
+ uint8_t i_confset[IPV4_DEVCONF_MAX];
+ uint32_t i_conf[IPV4_DEVCONF_MAX];
+};
+/** @endcond */
+
+static void *inet_alloc(struct rtnl_link *link)
+{
+ return calloc(1, sizeof(struct inet_data));
+}
+
+static void *inet_clone(struct rtnl_link *link, void *data)
+{
+ struct inet_data *id;
+
+ if ((id = inet_alloc(link)))
+ memcpy(id, data, sizeof(*id));
+
+ return id;
+}
+
+static void inet_free(struct rtnl_link *link, void *data)
+{
+ free(data);
+}
+
+static struct nla_policy inet_policy[IFLA_INET6_MAX+1] = {
+ [IFLA_INET_CONF] = { .minlen = 4 },
+};
+
+static int inet_parse_af(struct rtnl_link *link, struct nlattr *attr, void *data)
+{
+ struct inet_data *id = data;
+ struct nlattr *tb[IFLA_INET_MAX+1];
+ int err;
+
+ err = nla_parse_nested(tb, IFLA_INET_MAX, attr, inet_policy);
+ if (err < 0)
+ return err;
+ if (tb[IFLA_INET_CONF] && nla_len(tb[IFLA_INET_CONF]) % 4)
+ return -EINVAL;
+
+ if (tb[IFLA_INET_CONF]) {
+ int i;
+ int len = min_t(int, IPV4_DEVCONF_MAX, nla_len(tb[IFLA_INET_CONF]) / 4);
+
+ for (i = 0; i < len; i++)
+ id->i_confset[i] = 1;
+ nla_memcpy(&id->i_conf, tb[IFLA_INET_CONF], sizeof(id->i_conf));
+ }
+
+ return 0;
+}
+
+static int inet_fill_af(struct rtnl_link *link, struct nl_msg *msg, void *data)
+{
+ struct inet_data *id = data;
+ struct nlattr *nla;
+ int i;
+
+ if (!(nla = nla_nest_start(msg, IFLA_INET_CONF)))
+ return -NLE_MSGSIZE;
+
+ for (i = 0; i < IPV4_DEVCONF_MAX; i++)
+ if (id->i_confset[i])
+ NLA_PUT_U32(msg, i+1, id->i_conf[i]);
+
+ nla_nest_end(msg, nla);
+
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
+}
+
+static const struct trans_tbl inet_devconf[] = {
+ __ADD(IPV4_DEVCONF_FORWARDING, forwarding)
+ __ADD(IPV4_DEVCONF_MC_FORWARDING, mc_forwarding)
+ __ADD(IPV4_DEVCONF_PROXY_ARP, proxy_arp)
+ __ADD(IPV4_DEVCONF_ACCEPT_REDIRECTS, accept_redirects)
+ __ADD(IPV4_DEVCONF_SECURE_REDIRECTS, secure_redirects)
+ __ADD(IPV4_DEVCONF_SEND_REDIRECTS, send_redirects)
+ __ADD(IPV4_DEVCONF_SHARED_MEDIA, shared_media)
+ __ADD(IPV4_DEVCONF_RP_FILTER, rp_filter)
+ __ADD(IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route)
+ __ADD(IPV4_DEVCONF_BOOTP_RELAY, bootp_relay)
+ __ADD(IPV4_DEVCONF_LOG_MARTIANS, log_martians)
+ __ADD(IPV4_DEVCONF_TAG, tag)
+ __ADD(IPV4_DEVCONF_ARPFILTER, arpfilter)
+ __ADD(IPV4_DEVCONF_MEDIUM_ID, medium_id)
+ __ADD(IPV4_DEVCONF_NOXFRM, noxfrm)
+ __ADD(IPV4_DEVCONF_NOPOLICY, nopolicy)
+ __ADD(IPV4_DEVCONF_FORCE_IGMP_VERSION, force_igmp_version)
+ __ADD(IPV4_DEVCONF_ARP_ANNOUNCE, arp_announce)
+ __ADD(IPV4_DEVCONF_ARP_IGNORE, arp_ignore)
+ __ADD(IPV4_DEVCONF_PROMOTE_SECONDARIES, promote_secondaries)
+ __ADD(IPV4_DEVCONF_ARP_ACCEPT, arp_accept)
+ __ADD(IPV4_DEVCONF_ARP_NOTIFY, arp_notify)
+ __ADD(IPV4_DEVCONF_ACCEPT_LOCAL, accept_local)
+ __ADD(IPV4_DEVCONF_SRC_VMARK, src_vmark)
+ __ADD(IPV4_DEVCONF_PROXY_ARP_PVLAN, proxy_arp_pvlan)
+ __ADD(IPV4_DEVCONF_ROUTE_LOCALNET, route_localnet)
+ __ADD(IPV4_DEVCONF_IGMPV2_UNSOLICITED_REPORT_INTERVAL, igmpv2_unsolicited_report_interval)
+ __ADD(IPV4_DEVCONF_IGMPV3_UNSOLICITED_REPORT_INTERVAL, igmpv3_unsolicited_report_interval)
+};
+
+const char *rtnl_link_inet_devconf2str(int type, char *buf, size_t len)
+{
+ return __type2str(type, buf, len, inet_devconf,
+ ARRAY_SIZE(inet_devconf));
+}
+
+int rtnl_link_inet_str2devconf(const char *name)
+{
+ return __str2type(name, inet_devconf, ARRAY_SIZE(inet_devconf));
+}
+
+static void inet_dump_details(struct rtnl_link *link,
+ struct nl_dump_params *p, void *data)
+{
+ struct inet_data *id = data;
+ char buf[64];
+ int i, n = 0;
+
+ nl_dump_line(p, " ipv4 devconf:\n");
+ nl_dump_line(p, " ");
+
+ for (i = 0; i < IPV4_DEVCONF_MAX; i++) {
+ nl_dump_line(p, "%-19s %3u",
+ rtnl_link_inet_devconf2str(i+1, buf, sizeof(buf)),
+ id->i_confset[i] ? id->i_conf[i] : 0);
+
+ if (++n == 3) {
+ nl_dump(p, "\n");
+ nl_dump_line(p, " ");
+ n = 0;
+ } else
+ nl_dump(p, " ");
+ }
+
+ if (n != 0)
+ nl_dump(p, "\n");
+}
+
+static struct rtnl_link_af_ops inet_ops = {
+ .ao_family = AF_INET,
+ .ao_alloc = &inet_alloc,
+ .ao_clone = &inet_clone,
+ .ao_free = &inet_free,
+ .ao_parse_af = &inet_parse_af,
+ .ao_fill_af = &inet_fill_af,
+ .ao_dump[NL_DUMP_DETAILS] = &inet_dump_details,
+};
+
+/**
+ * Get value of a ipv4 link configuration setting
+ * @arg link Link object
+ * @arg cfgid Configuration identifier
+ * @arg res Result pointer
+ *
+ * Stores the value of the specified configuration setting in the provided
+ * result pointer.
+ *
+ * @return 0 on success or a negative error code.
+ * @return -NLE_RANGE cfgid is out of range, 1..IPV4_DEVCONF_MAX
+ * @return -NLE_NOATTR configuration setting not available
+ * @return -NLE_INVAL cfgid not set. If the link was received via netlink,
+ * it means that the cfgid is not supported.
+ */
+int rtnl_link_inet_get_conf(struct rtnl_link *link, const unsigned int cfgid,
+ uint32_t *res)
+{
+ struct inet_data *id;
+
+ if (cfgid == 0 || cfgid > IPV4_DEVCONF_MAX)
+ return -NLE_RANGE;
+
+ if (!(id = rtnl_link_af_alloc(link, &inet_ops)))
+ return -NLE_NOATTR;
+
+ if (!id->i_confset[cfgid - 1])
+ return -NLE_INVAL;
+ *res = id->i_conf[cfgid - 1];
+
+ return 0;
+}
+
+/**
+ * Change value of a ipv4 link configuration setting
+ * @arg link Link object
+ * @arg cfgid Configuration identifier
+ * @arg value New value
+ *
+ * Changes the value in the per link ipv4 configuration array.
+ *
+ * @return 0 on success or a negative error code.
+ * @return -NLE_RANGE cfgid is out of range, 1..IPV4_DEVCONF_MAX
+ * @return -NLE_NOMEM memory allocation failed
+ */
+int rtnl_link_inet_set_conf(struct rtnl_link *link, const unsigned int cfgid,
+ uint32_t value)
+{
+ struct inet_data *id;
+
+ if (!(id = rtnl_link_af_alloc(link, &inet_ops)))
+ return -NLE_NOMEM;
+
+ if (cfgid == 0 || cfgid > IPV4_DEVCONF_MAX)
+ return -NLE_RANGE;
+
+ id->i_confset[cfgid - 1] = 1;
+ id->i_conf[cfgid - 1] = value;
+
+ return 0;
+}
+
+
+static void __init inet_init(void)
+{
+ rtnl_link_af_register(&inet_ops);
+}
+
+static void __exit inet_exit(void)
+{
+ rtnl_link_af_unregister(&inet_ops);
+}
+
+/** @} */
diff --git a/lib/route/link/inet6.c b/lib/route/link/inet6.c
new file mode 100644
index 00000000..6fa2741c
--- /dev/null
+++ b/lib/route/link/inet6.c
@@ -0,0 +1,484 @@
+/*
+ * lib/route/link/inet6.c AF_INET6 link operations
+ *
+ * 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+
+struct inet6_data
+{
+ uint32_t i6_flags;
+ struct ifla_cacheinfo i6_cacheinfo;
+ uint32_t i6_conf[DEVCONF_MAX];
+};
+
+static void *inet6_alloc(struct rtnl_link *link)
+{
+ return calloc(1, sizeof(struct inet6_data));
+}
+
+static void *inet6_clone(struct rtnl_link *link, void *data)
+{
+ struct inet6_data *i6;
+
+ if ((i6 = inet6_alloc(link)))
+ memcpy(i6, data, sizeof(*i6));
+
+ return i6;
+}
+
+static void inet6_free(struct rtnl_link *link, void *data)
+{
+ free(data);
+}
+
+static struct nla_policy inet6_policy[IFLA_INET6_MAX+1] = {
+ [IFLA_INET6_FLAGS] = { .type = NLA_U32 },
+ [IFLA_INET6_CACHEINFO] = { .minlen = sizeof(struct ifla_cacheinfo) },
+ [IFLA_INET6_CONF] = { .minlen = 4 },
+ [IFLA_INET6_STATS] = { .minlen = 8 },
+ [IFLA_INET6_ICMP6STATS] = { .minlen = 8 },
+};
+
+static const uint8_t map_stat_id_from_IPSTATS_MIB_v1[__IPSTATS_MIB_MAX] = {
+ /* 14a196807482e6fc74f15fc03176d5c08880588f^:include/linux/snmp.h
+ * version before the API change in commit 14a196807482e6fc74f15fc03176d5c08880588f.
+ * This version was valid since commit edf391ff17232f097d72441c9ad467bcb3b5db18, which
+ * predates support for parsing IFLA_PROTINFO in libnl3. Such an even older meaning of
+ * the flags is not supported in libnl3. */
+ [ 1] = RTNL_LINK_IP6_INPKTS, /* IPSTATS_MIB_INPKTS */
+ [ 2] = RTNL_LINK_IP6_INHDRERRORS, /* IPSTATS_MIB_INHDRERRORS */
+ [ 3] = RTNL_LINK_IP6_INTOOBIGERRORS, /* IPSTATS_MIB_INTOOBIGERRORS */
+ [ 4] = RTNL_LINK_IP6_INNOROUTES, /* IPSTATS_MIB_INNOROUTES */
+ [ 5] = RTNL_LINK_IP6_INADDRERRORS, /* IPSTATS_MIB_INADDRERRORS */
+ [ 6] = RTNL_LINK_IP6_INUNKNOWNPROTOS, /* IPSTATS_MIB_INUNKNOWNPROTOS */
+ [ 7] = RTNL_LINK_IP6_INTRUNCATEDPKTS, /* IPSTATS_MIB_INTRUNCATEDPKTS */
+ [ 8] = RTNL_LINK_IP6_INDISCARDS, /* IPSTATS_MIB_INDISCARDS */
+ [ 9] = RTNL_LINK_IP6_INDELIVERS, /* IPSTATS_MIB_INDELIVERS */
+ [10] = RTNL_LINK_IP6_OUTFORWDATAGRAMS, /* IPSTATS_MIB_OUTFORWDATAGRAMS */
+ [11] = RTNL_LINK_IP6_OUTPKTS, /* IPSTATS_MIB_OUTPKTS */
+ [12] = RTNL_LINK_IP6_OUTDISCARDS, /* IPSTATS_MIB_OUTDISCARDS */
+ [13] = RTNL_LINK_IP6_OUTNOROUTES, /* IPSTATS_MIB_OUTNOROUTES */
+ [14] = RTNL_LINK_IP6_REASMTIMEOUT, /* IPSTATS_MIB_REASMTIMEOUT */
+ [15] = RTNL_LINK_IP6_REASMREQDS, /* IPSTATS_MIB_REASMREQDS */
+ [16] = RTNL_LINK_IP6_REASMOKS, /* IPSTATS_MIB_REASMOKS */
+ [17] = RTNL_LINK_IP6_REASMFAILS, /* IPSTATS_MIB_REASMFAILS */
+ [18] = RTNL_LINK_IP6_FRAGOKS, /* IPSTATS_MIB_FRAGOKS */
+ [19] = RTNL_LINK_IP6_FRAGFAILS, /* IPSTATS_MIB_FRAGFAILS */
+ [20] = RTNL_LINK_IP6_FRAGCREATES, /* IPSTATS_MIB_FRAGCREATES */
+ [21] = RTNL_LINK_IP6_INMCASTPKTS, /* IPSTATS_MIB_INMCASTPKTS */
+ [22] = RTNL_LINK_IP6_OUTMCASTPKTS, /* IPSTATS_MIB_OUTMCASTPKTS */
+ [23] = RTNL_LINK_IP6_INBCASTPKTS, /* IPSTATS_MIB_INBCASTPKTS */
+ [24] = RTNL_LINK_IP6_OUTBCASTPKTS, /* IPSTATS_MIB_OUTBCASTPKTS */
+ [25] = RTNL_LINK_IP6_INOCTETS, /* IPSTATS_MIB_INOCTETS */
+ [26] = RTNL_LINK_IP6_OUTOCTETS, /* IPSTATS_MIB_OUTOCTETS */
+ [27] = RTNL_LINK_IP6_INMCASTOCTETS, /* IPSTATS_MIB_INMCASTOCTETS */
+ [28] = RTNL_LINK_IP6_OUTMCASTOCTETS, /* IPSTATS_MIB_OUTMCASTOCTETS */
+ [29] = RTNL_LINK_IP6_INBCASTOCTETS, /* IPSTATS_MIB_INBCASTOCTETS */
+ [30] = RTNL_LINK_IP6_OUTBCASTOCTETS, /* IPSTATS_MIB_OUTBCASTOCTETS */
+};
+
+static const uint8_t map_stat_id_from_IPSTATS_MIB_v2[__IPSTATS_MIB_MAX] = {
+ /* d8ec26d7f8287f5788a494f56e8814210f0e64be:include/uapi/linux/snmp.h
+ * version since the API change in commit 14a196807482e6fc74f15fc03176d5c08880588f */
+ [ 1] = RTNL_LINK_IP6_INPKTS, /* IPSTATS_MIB_INPKTS */
+ [ 2] = RTNL_LINK_IP6_INOCTETS, /* IPSTATS_MIB_INOCTETS */
+ [ 3] = RTNL_LINK_IP6_INDELIVERS, /* IPSTATS_MIB_INDELIVERS */
+ [ 4] = RTNL_LINK_IP6_OUTFORWDATAGRAMS, /* IPSTATS_MIB_OUTFORWDATAGRAMS */
+ [ 5] = RTNL_LINK_IP6_OUTPKTS, /* IPSTATS_MIB_OUTPKTS */
+ [ 6] = RTNL_LINK_IP6_OUTOCTETS, /* IPSTATS_MIB_OUTOCTETS */
+ [ 7] = RTNL_LINK_IP6_INHDRERRORS, /* IPSTATS_MIB_INHDRERRORS */
+ [ 8] = RTNL_LINK_IP6_INTOOBIGERRORS, /* IPSTATS_MIB_INTOOBIGERRORS */
+ [ 9] = RTNL_LINK_IP6_INNOROUTES, /* IPSTATS_MIB_INNOROUTES */
+ [10] = RTNL_LINK_IP6_INADDRERRORS, /* IPSTATS_MIB_INADDRERRORS */
+ [11] = RTNL_LINK_IP6_INUNKNOWNPROTOS, /* IPSTATS_MIB_INUNKNOWNPROTOS */
+ [12] = RTNL_LINK_IP6_INTRUNCATEDPKTS, /* IPSTATS_MIB_INTRUNCATEDPKTS */
+ [13] = RTNL_LINK_IP6_INDISCARDS, /* IPSTATS_MIB_INDISCARDS */
+ [14] = RTNL_LINK_IP6_OUTDISCARDS, /* IPSTATS_MIB_OUTDISCARDS */
+ [15] = RTNL_LINK_IP6_OUTNOROUTES, /* IPSTATS_MIB_OUTNOROUTES */
+ [16] = RTNL_LINK_IP6_REASMTIMEOUT, /* IPSTATS_MIB_REASMTIMEOUT */
+ [17] = RTNL_LINK_IP6_REASMREQDS, /* IPSTATS_MIB_REASMREQDS */
+ [18] = RTNL_LINK_IP6_REASMOKS, /* IPSTATS_MIB_REASMOKS */
+ [19] = RTNL_LINK_IP6_REASMFAILS, /* IPSTATS_MIB_REASMFAILS */
+ [20] = RTNL_LINK_IP6_FRAGOKS, /* IPSTATS_MIB_FRAGOKS */
+ [21] = RTNL_LINK_IP6_FRAGFAILS, /* IPSTATS_MIB_FRAGFAILS */
+ [22] = RTNL_LINK_IP6_FRAGCREATES, /* IPSTATS_MIB_FRAGCREATES */
+ [23] = RTNL_LINK_IP6_INMCASTPKTS, /* IPSTATS_MIB_INMCASTPKTS */
+ [24] = RTNL_LINK_IP6_OUTMCASTPKTS, /* IPSTATS_MIB_OUTMCASTPKTS */
+ [25] = RTNL_LINK_IP6_INBCASTPKTS, /* IPSTATS_MIB_INBCASTPKTS */
+ [26] = RTNL_LINK_IP6_OUTBCASTPKTS, /* IPSTATS_MIB_OUTBCASTPKTS */
+ [27] = RTNL_LINK_IP6_INMCASTOCTETS, /* IPSTATS_MIB_INMCASTOCTETS */
+ [28] = RTNL_LINK_IP6_OUTMCASTOCTETS, /* IPSTATS_MIB_OUTMCASTOCTETS */
+ [29] = RTNL_LINK_IP6_INBCASTOCTETS, /* IPSTATS_MIB_INBCASTOCTETS */
+ [30] = RTNL_LINK_IP6_OUTBCASTOCTETS, /* IPSTATS_MIB_OUTBCASTOCTETS */
+ [31] = RTNL_LINK_IP6_CSUMERRORS, /* IPSTATS_MIB_CSUMERRORS */
+ [32] = RTNL_LINK_IP6_NOECTPKTS, /* IPSTATS_MIB_NOECTPKTS */
+ [33] = RTNL_LINK_IP6_ECT1PKTS, /* IPSTATS_MIB_ECT1PKTS */
+ [34] = RTNL_LINK_IP6_ECT0PKTS, /* IPSTATS_MIB_ECT0PKTS */
+ [35] = RTNL_LINK_IP6_CEPKTS, /* IPSTATS_MIB_CEPKTS */
+};
+
+static int inet6_parse_protinfo(struct rtnl_link *link, struct nlattr *attr,
+ void *data)
+{
+ struct inet6_data *i6 = data;
+ struct nlattr *tb[IFLA_INET6_MAX+1];
+ int err;
+
+ err = nla_parse_nested(tb, IFLA_INET6_MAX, attr, inet6_policy);
+ if (err < 0)
+ return err;
+ if (tb[IFLA_INET6_CONF] && nla_len(tb[IFLA_INET6_CONF]) % 4)
+ return -EINVAL;
+ if (tb[IFLA_INET6_STATS] && nla_len(tb[IFLA_INET6_STATS]) % 8)
+ return -EINVAL;
+ if (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) % 8)
+ return -EINVAL;
+
+ if (tb[IFLA_INET6_FLAGS])
+ i6->i6_flags = nla_get_u32(tb[IFLA_INET6_FLAGS]);
+
+ if (tb[IFLA_INET6_CACHEINFO])
+ nla_memcpy(&i6->i6_cacheinfo, tb[IFLA_INET6_CACHEINFO],
+ sizeof(i6->i6_cacheinfo));
+
+ if (tb[IFLA_INET6_CONF])
+ nla_memcpy(&i6->i6_conf, tb[IFLA_INET6_CONF],
+ sizeof(i6->i6_conf));
+
+ /*
+ * Due to 32bit data alignment, these addresses must be copied to an
+ * aligned location prior to access.
+ */
+ if (tb[IFLA_INET6_STATS]) {
+ unsigned char *cnt = nla_data(tb[IFLA_INET6_STATS]);
+ uint64_t stat;
+ int i;
+ int len = nla_len(tb[IFLA_INET6_STATS]) / 8;
+ const uint8_t *map_stat_id = map_stat_id_from_IPSTATS_MIB_v2;
+
+ if (len < 32 ||
+ (tb[IFLA_INET6_ICMP6STATS] && nla_len(tb[IFLA_INET6_ICMP6STATS]) < 6)) {
+ /* kernel commit 14a196807482e6fc74f15fc03176d5c08880588f reordered the values.
+ * The later commit 6a5dc9e598fe90160fee7de098fa319665f5253e added values
+ * IPSTATS_MIB_CSUMERRORS/ICMP6_MIB_CSUMERRORS. If the netlink is shorter
+ * then this, assume that the kernel uses the previous meaning of the
+ * enumeration. */
+ map_stat_id = map_stat_id_from_IPSTATS_MIB_v1;
+ }
+
+ len = min_t(int, __IPSTATS_MIB_MAX, len);
+ for (i = 1; i < len; i++) {
+ memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat));
+ rtnl_link_set_stat(link, map_stat_id[i], stat);
+ }
+ }
+
+ if (tb[IFLA_INET6_ICMP6STATS]) {
+ unsigned char *cnt = nla_data(tb[IFLA_INET6_ICMP6STATS]);
+ uint64_t stat;
+ int i;
+ int len = min_t(int, __ICMP6_MIB_MAX, nla_len(tb[IFLA_INET6_ICMP6STATS]) / 8);
+
+ for (i = 1; i < len; i++) {
+ memcpy(&stat, &cnt[i * sizeof(stat)], sizeof(stat));
+ rtnl_link_set_stat(link, RTNL_LINK_ICMP6_INMSGS + i - 1,
+ stat);
+ }
+ }
+
+ return 0;
+}
+
+/* These live in include/net/if_inet6.h and should be moved to include/linux */
+#define IF_RA_OTHERCONF 0x80
+#define IF_RA_MANAGED 0x40
+#define IF_RA_RCVD 0x20
+#define IF_RS_SENT 0x10
+#define IF_READY 0x80000000
+
+static const struct trans_tbl inet6_flags[] = {
+ __ADD(IF_RA_OTHERCONF, ra_otherconf)
+ __ADD(IF_RA_MANAGED, ra_managed)
+ __ADD(IF_RA_RCVD, ra_rcvd)
+ __ADD(IF_RS_SENT, rs_sent)
+ __ADD(IF_READY, ready)
+};
+
+static char *inet6_flags2str(int flags, char *buf, size_t len)
+{
+ return __flags2str(flags, buf, len, inet6_flags,
+ ARRAY_SIZE(inet6_flags));
+}
+
+static const struct trans_tbl inet6_devconf[] = {
+ __ADD(DEVCONF_FORWARDING, forwarding)
+ __ADD(DEVCONF_HOPLIMIT, hoplimit)
+ __ADD(DEVCONF_MTU6, mtu6)
+ __ADD(DEVCONF_ACCEPT_RA, accept_ra)
+ __ADD(DEVCONF_ACCEPT_REDIRECTS, accept_redirects)
+ __ADD(DEVCONF_AUTOCONF, autoconf)
+ __ADD(DEVCONF_DAD_TRANSMITS, dad_transmits)
+ __ADD(DEVCONF_RTR_SOLICITS, rtr_solicits)
+ __ADD(DEVCONF_RTR_SOLICIT_INTERVAL, rtr_solicit_interval)
+ __ADD(DEVCONF_RTR_SOLICIT_DELAY, rtr_solicit_delay)
+ __ADD(DEVCONF_USE_TEMPADDR, use_tempaddr)
+ __ADD(DEVCONF_TEMP_VALID_LFT, temp_valid_lft)
+ __ADD(DEVCONF_TEMP_PREFERED_LFT, temp_prefered_lft)
+ __ADD(DEVCONF_REGEN_MAX_RETRY, regen_max_retry)
+ __ADD(DEVCONF_MAX_DESYNC_FACTOR, max_desync_factor)
+ __ADD(DEVCONF_MAX_ADDRESSES, max_addresses)
+ __ADD(DEVCONF_FORCE_MLD_VERSION, force_mld_version)
+ __ADD(DEVCONF_ACCEPT_RA_DEFRTR, accept_ra_defrtr)
+ __ADD(DEVCONF_ACCEPT_RA_PINFO, accept_ra_pinfo)
+ __ADD(DEVCONF_ACCEPT_RA_RTR_PREF, accept_ra_rtr_pref)
+ __ADD(DEVCONF_RTR_PROBE_INTERVAL, rtr_probe_interval)
+ __ADD(DEVCONF_ACCEPT_RA_RT_INFO_MAX_PLEN, accept_ra_rt_info)
+ __ADD(DEVCONF_PROXY_NDP, proxy_ndp)
+ __ADD(DEVCONF_OPTIMISTIC_DAD, optimistic_dad)
+ __ADD(DEVCONF_ACCEPT_SOURCE_ROUTE, accept_source_route)
+ __ADD(DEVCONF_MC_FORWARDING, mc_forwarding)
+ __ADD(DEVCONF_DISABLE_IPV6, disable_ipv6)
+ __ADD(DEVCONF_ACCEPT_DAD, accept_dad)
+ __ADD(DEVCONF_FORCE_TLLAO, force_tllao)
+};
+
+static char *inet6_devconf2str(int type, char *buf, size_t len)
+{
+ return __type2str(type, buf, len, inet6_devconf,
+ ARRAY_SIZE(inet6_devconf));
+}
+
+
+static void inet6_dump_details(struct rtnl_link *link,
+ struct nl_dump_params *p, void *data)
+{
+ struct inet6_data *i6 = data;
+ char buf[64], buf2[64];
+ int i, n = 0;
+
+ nl_dump_line(p, " ipv6 max-reasm-len %s",
+ nl_size2str(i6->i6_cacheinfo.max_reasm_len, buf, sizeof(buf)));
+
+ nl_dump(p, " <%s>\n",
+ inet6_flags2str(i6->i6_flags, buf, sizeof(buf)));
+
+
+ nl_dump_line(p, " create-stamp %.2fs reachable-time %s",
+ (double) i6->i6_cacheinfo.tstamp / 100.,
+ nl_msec2str(i6->i6_cacheinfo.reachable_time, buf, sizeof(buf)));
+
+ nl_dump(p, " retrans-time %s\n",
+ nl_msec2str(i6->i6_cacheinfo.retrans_time, buf, sizeof(buf)));
+
+ nl_dump_line(p, " devconf:\n");
+ nl_dump_line(p, " ");
+
+ for (i = 0; i < DEVCONF_MAX; i++) {
+ uint32_t value = i6->i6_conf[i];
+ int x, offset;
+
+ switch (i) {
+ case DEVCONF_TEMP_VALID_LFT:
+ case DEVCONF_TEMP_PREFERED_LFT:
+ nl_msec2str((uint64_t) value * 1000., buf2, sizeof(buf2));
+ break;
+
+ case DEVCONF_RTR_PROBE_INTERVAL:
+ case DEVCONF_RTR_SOLICIT_INTERVAL:
+ case DEVCONF_RTR_SOLICIT_DELAY:
+ nl_msec2str(value, buf2, sizeof(buf2));
+ break;
+
+ default:
+ snprintf(buf2, sizeof(buf2), "%u", value);
+ break;
+
+ }
+
+ inet6_devconf2str(i, buf, sizeof(buf));
+
+ offset = 23 - strlen(buf2);
+ if (offset < 0)
+ offset = 0;
+
+ for (x = strlen(buf); x < offset; x++)
+ buf[x] = ' ';
+
+ strncpy(&buf[offset], buf2, strlen(buf2));
+
+ nl_dump_line(p, "%s", buf);
+
+ if (++n == 3) {
+ nl_dump(p, "\n");
+ nl_dump_line(p, " ");
+ n = 0;
+ } else
+ nl_dump(p, " ");
+ }
+
+ if (n != 0)
+ nl_dump(p, "\n");
+}
+
+static void inet6_dump_stats(struct rtnl_link *link,
+ struct nl_dump_params *p, void *data)
+{
+ double octets;
+ char *octetsUnit;
+
+ nl_dump(p, " IPv6: InPkts InOctets "
+ " InDiscards InDelivers\n");
+ nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INPKTS]);
+
+ octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INOCTETS],
+ &octetsUnit);
+ if (octets)
+ nl_dump(p, "%14.2f %3s ", octets, octetsUnit);
+ else
+ nl_dump(p, "%16" PRIu64 " B ", 0);
+
+ nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n",
+ link->l_stats[RTNL_LINK_IP6_INDISCARDS],
+ link->l_stats[RTNL_LINK_IP6_INDELIVERS]);
+
+ nl_dump(p, " OutPkts OutOctets "
+ " OutDiscards OutForwards\n");
+
+ nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTPKTS]);
+
+ octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTOCTETS],
+ &octetsUnit);
+ if (octets)
+ nl_dump(p, "%14.2f %3s ", octets, octetsUnit);
+ else
+ nl_dump(p, "%16" PRIu64 " B ", 0);
+
+ nl_dump(p, "%18" PRIu64 " %18" PRIu64 "\n",
+ link->l_stats[RTNL_LINK_IP6_OUTDISCARDS],
+ link->l_stats[RTNL_LINK_IP6_OUTFORWDATAGRAMS]);
+
+ nl_dump(p, " InMcastPkts InMcastOctets "
+ " InBcastPkts InBcastOctests\n");
+
+ nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INMCASTPKTS]);
+
+ octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INMCASTOCTETS],
+ &octetsUnit);
+ if (octets)
+ nl_dump(p, "%14.2f %3s ", octets, octetsUnit);
+ else
+ nl_dump(p, "%16" PRIu64 " B ", 0);
+
+ nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_INBCASTPKTS]);
+ octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_INBCASTOCTETS],
+ &octetsUnit);
+ if (octets)
+ nl_dump(p, "%14.2f %3s\n", octets, octetsUnit);
+ else
+ nl_dump(p, "%16" PRIu64 " B\n", 0);
+
+ nl_dump(p, " OutMcastPkts OutMcastOctets "
+ " OutBcastPkts OutBcastOctests\n");
+
+ nl_dump(p, " %18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTMCASTPKTS]);
+
+ octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTMCASTOCTETS],
+ &octetsUnit);
+ if (octets)
+ nl_dump(p, "%14.2f %3s ", octets, octetsUnit);
+ else
+ nl_dump(p, "%16" PRIu64 " B ", 0);
+
+ nl_dump(p, "%18" PRIu64 " ", link->l_stats[RTNL_LINK_IP6_OUTBCASTPKTS]);
+ octets = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_IP6_OUTBCASTOCTETS],
+ &octetsUnit);
+ if (octets)
+ nl_dump(p, "%14.2f %3s\n", octets, octetsUnit);
+ else
+ nl_dump(p, "%16" PRIu64 " B\n", 0);
+
+ nl_dump(p, " ReasmOKs ReasmFails "
+ " ReasmReqds ReasmTimeout\n");
+ nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
+ link->l_stats[RTNL_LINK_IP6_REASMOKS],
+ link->l_stats[RTNL_LINK_IP6_REASMFAILS],
+ link->l_stats[RTNL_LINK_IP6_REASMREQDS],
+ link->l_stats[RTNL_LINK_IP6_REASMTIMEOUT]);
+
+ nl_dump(p, " FragOKs FragFails "
+ " FragCreates\n");
+ nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
+ link->l_stats[RTNL_LINK_IP6_FRAGOKS],
+ link->l_stats[RTNL_LINK_IP6_FRAGFAILS],
+ link->l_stats[RTNL_LINK_IP6_FRAGCREATES]);
+
+ nl_dump(p, " InHdrErrors InTooBigErrors "
+ " InNoRoutes InAddrErrors\n");
+ nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
+ link->l_stats[RTNL_LINK_IP6_INHDRERRORS],
+ link->l_stats[RTNL_LINK_IP6_INTOOBIGERRORS],
+ link->l_stats[RTNL_LINK_IP6_INNOROUTES],
+ link->l_stats[RTNL_LINK_IP6_INADDRERRORS]);
+
+ nl_dump(p, " InUnknownProtos InTruncatedPkts "
+ " OutNoRoutes InCsumErrors\n");
+ nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
+ link->l_stats[RTNL_LINK_IP6_INUNKNOWNPROTOS],
+ link->l_stats[RTNL_LINK_IP6_INTRUNCATEDPKTS],
+ link->l_stats[RTNL_LINK_IP6_OUTNOROUTES],
+ link->l_stats[RTNL_LINK_IP6_CSUMERRORS]);
+
+ nl_dump(p, " InNoECTPkts InECT1Pkts "
+ " InECT0Pkts InCEPkts\n");
+ nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
+ link->l_stats[RTNL_LINK_IP6_NOECTPKTS],
+ link->l_stats[RTNL_LINK_IP6_ECT1PKTS],
+ link->l_stats[RTNL_LINK_IP6_ECT0PKTS],
+ link->l_stats[RTNL_LINK_IP6_CEPKTS]);
+
+ nl_dump(p, " ICMPv6: InMsgs InErrors "
+ " OutMsgs OutErrors InCsumErrors\n");
+ nl_dump(p, " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 " %18" PRIu64 "\n",
+ link->l_stats[RTNL_LINK_ICMP6_INMSGS],
+ link->l_stats[RTNL_LINK_ICMP6_INERRORS],
+ link->l_stats[RTNL_LINK_ICMP6_OUTMSGS],
+ link->l_stats[RTNL_LINK_ICMP6_OUTERRORS],
+ link->l_stats[RTNL_LINK_ICMP6_CSUMERRORS]);
+}
+
+static const struct nla_policy protinfo_policy = {
+ .type = NLA_NESTED,
+};
+
+static struct rtnl_link_af_ops inet6_ops = {
+ .ao_family = AF_INET6,
+ .ao_alloc = &inet6_alloc,
+ .ao_clone = &inet6_clone,
+ .ao_free = &inet6_free,
+ .ao_parse_protinfo = &inet6_parse_protinfo,
+ .ao_parse_af = &inet6_parse_protinfo,
+ .ao_dump[NL_DUMP_DETAILS] = &inet6_dump_details,
+ .ao_dump[NL_DUMP_STATS] = &inet6_dump_stats,
+ .ao_protinfo_policy = &protinfo_policy,
+};
+
+static void __init inet6_init(void)
+{
+ rtnl_link_af_register(&inet6_ops);
+}
+
+static void __exit inet6_exit(void)
+{
+ rtnl_link_af_unregister(&inet6_ops);
+}
diff --git a/lib/route/link/ip6tnl.c b/lib/route/link/ip6tnl.c
new file mode 100644
index 00000000..9fe13674
--- /dev/null
+++ b/lib/route/link/ip6tnl.c
@@ -0,0 +1,688 @@
+/*
+ * lib/route/link/ip6tnl.c IP6TNL Link Info
+ *
+ * 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) 2014 Susant Sahani <susant@redhat.com>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup ip6tnl IP6TNL
+ * ip6tnl link module
+ *
+ * @details
+ * \b Link Type Name: "ip6tnl"
+ *
+ * @route_doc{link_ip6tnl, IP6TNL Documentation}
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <linux/if_tunnel.h>
+#include <netinet/in.h>
+
+#define IP6_TNL_ATTR_LINK (1 << 0)
+#define IP6_TNL_ATTR_LOCAL (1 << 1)
+#define IP6_TNL_ATTR_REMOTE (1 << 2)
+#define IP6_TNL_ATTR_TTL (1 << 3)
+#define IP6_TNL_ATTR_TOS (1 << 4)
+#define IP6_TNL_ATTR_ENCAPLIMIT (1 << 5)
+#define IP6_TNL_ATTR_FLAGS (1 << 6)
+#define IP6_TNL_ATTR_PROTO (1 << 7)
+#define IP6_TNL_ATTR_FLOWINFO (1 << 8)
+
+struct ip6_tnl_info
+{
+ uint8_t ttl;
+ uint8_t tos;
+ uint8_t encap_limit;
+ uint8_t proto;
+ uint32_t flags;
+ uint32_t link;
+ uint32_t flowinfo;
+ struct in6_addr local;
+ struct in6_addr remote;
+ uint32_t ip6_tnl_mask;
+};
+
+static struct nla_policy ip6_tnl_policy[IFLA_IPTUN_MAX + 1] = {
+ [IFLA_IPTUN_LINK] = { .type = NLA_U32 },
+ [IFLA_IPTUN_LOCAL] = { .minlen = sizeof(struct in6_addr) },
+ [IFLA_IPTUN_REMOTE] = { .minlen = sizeof(struct in6_addr) },
+ [IFLA_IPTUN_TTL] = { .type = NLA_U8 },
+ [IFLA_IPTUN_TOS] = { .type = NLA_U8 },
+ [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NLA_U8 },
+ [IFLA_IPTUN_FLOWINFO] = { .type = NLA_U32 },
+ [IFLA_IPTUN_FLAGS] = { .type = NLA_U32 },
+ [IFLA_IPTUN_PROTO] = { .type = NLA_U8 },
+};
+
+static int ip6_tnl_alloc(struct rtnl_link *link)
+{
+ struct ip6_tnl_info *ip6_tnl;
+
+ ip6_tnl = calloc(1, sizeof(*ip6_tnl));
+ if (!ip6_tnl)
+ return -NLE_NOMEM;
+
+ link->l_info = ip6_tnl;
+
+ return 0;
+}
+
+static int ip6_tnl_parse(struct rtnl_link *link, struct nlattr *data,
+ struct nlattr *xstats)
+{
+ struct nlattr *tb[IFLA_IPTUN_MAX + 1];
+ struct ip6_tnl_info *ip6_tnl;
+ int err;
+
+ NL_DBG(3, "Parsing IP6_TNL link info");
+
+ err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, ip6_tnl_policy);
+ if (err < 0)
+ goto errout;
+
+ err = ip6_tnl_alloc(link);
+ if (err < 0)
+ goto errout;
+
+ ip6_tnl = link->l_info;
+
+ if (tb[IFLA_IPTUN_LINK]) {
+ ip6_tnl->link = nla_get_u32(tb[IFLA_IPTUN_LINK]);
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LINK;
+ }
+
+ if (tb[IFLA_IPTUN_LOCAL]) {
+ nla_memcpy(&ip6_tnl->local, tb[IFLA_IPTUN_LOCAL], sizeof(struct in6_addr));
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LOCAL;
+ }
+
+ if (tb[IFLA_IPTUN_REMOTE]) {
+ nla_memcpy(&ip6_tnl->remote, tb[IFLA_IPTUN_REMOTE], sizeof(struct in6_addr));
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_REMOTE;
+ }
+
+ if (tb[IFLA_IPTUN_TTL]) {
+ ip6_tnl->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]);
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TTL;
+ }
+
+ if (tb[IFLA_IPTUN_TOS]) {
+ ip6_tnl->tos = nla_get_u8(tb[IFLA_IPTUN_TOS]);
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TOS;
+ }
+
+ if (tb[IFLA_IPTUN_ENCAP_LIMIT]) {
+ ip6_tnl->encap_limit = nla_get_u8(tb[IFLA_IPTUN_ENCAP_LIMIT]);
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_ENCAPLIMIT;
+ }
+
+ if (tb[IFLA_IPTUN_FLAGS]) {
+ ip6_tnl->flags = nla_get_u32(tb[IFLA_IPTUN_FLAGS]);
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLAGS;
+ }
+
+ if (tb[IFLA_IPTUN_FLOWINFO]) {
+ ip6_tnl->flowinfo = nla_get_u32(tb[IFLA_IPTUN_FLOWINFO]);
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLOWINFO;
+ }
+
+ if (tb[IFLA_IPTUN_PROTO]) {
+ ip6_tnl->proto = nla_get_u8(tb[IFLA_IPTUN_PROTO]);
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_PROTO;
+ }
+
+ err = 0;
+
+errout:
+ return err;
+}
+
+static int ip6_tnl_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+ struct nlattr *data;
+
+ data = nla_nest_start(msg, IFLA_INFO_DATA);
+ if (!data)
+ return -NLE_MSGSIZE;
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LINK)
+ NLA_PUT_U32(msg, IFLA_IPTUN_LINK, ip6_tnl->link);
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LOCAL)
+ NLA_PUT(msg, IFLA_IPTUN_LOCAL, sizeof(struct in6_addr), &ip6_tnl->local);
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_REMOTE)
+ NLA_PUT(msg, IFLA_IPTUN_REMOTE, sizeof(struct in6_addr), &ip6_tnl->remote);
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TTL)
+ NLA_PUT_U8(msg, IFLA_IPTUN_TTL, ip6_tnl->ttl);
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TOS)
+ NLA_PUT_U8(msg, IFLA_IPTUN_TOS, ip6_tnl->tos);
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_ENCAPLIMIT)
+ NLA_PUT_U8(msg, IFLA_IPTUN_ENCAP_LIMIT, ip6_tnl->encap_limit);
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLAGS)
+ NLA_PUT_U32(msg, IFLA_IPTUN_FLAGS, ip6_tnl->flags);
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLOWINFO)
+ NLA_PUT_U32(msg, IFLA_IPTUN_FLOWINFO, ip6_tnl->flowinfo);
+
+ /* kernel crashes if this attribure is missing temporary fix */
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_PROTO)
+ NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, ip6_tnl->proto);
+ else
+ NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, 0);
+
+ nla_nest_end(msg, data);
+
+nla_put_failure:
+ return 0;
+}
+
+static void ip6_tnl_free(struct rtnl_link *link)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ free(ip6_tnl);
+ link->l_info = NULL;
+}
+
+static void ip6_tnl_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ nl_dump(p, "ip6_tnl : %s", link->l_name);
+}
+
+static void ip6_tnl_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+ char *name, addr[INET6_ADDRSTRLEN];
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LINK) {
+ nl_dump(p, " link ");
+ name = rtnl_link_get_name(link);
+ if (name)
+ nl_dump_line(p, "%s\n", name);
+ else
+ nl_dump_line(p, "%u\n", ip6_tnl->link);
+ }
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_LOCAL) {
+ nl_dump(p, " local ");
+
+ if(inet_ntop(AF_INET6, &ip6_tnl->local, addr, INET6_ADDRSTRLEN))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", ip6_tnl->local);
+ }
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_REMOTE) {
+ nl_dump(p, " remote ");
+
+ if(inet_ntop(AF_INET6, &ip6_tnl->remote, addr, INET6_ADDRSTRLEN))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", ip6_tnl->remote);
+ }
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TTL) {
+ nl_dump(p, " ttl ");
+ nl_dump_line(p, "%d\n", ip6_tnl->ttl);
+ }
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_TOS) {
+ nl_dump(p, " tos ");
+ nl_dump_line(p, "%d\n", ip6_tnl->tos);
+ }
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_ENCAPLIMIT) {
+ nl_dump(p, " encaplimit ");
+ nl_dump_line(p, "%d\n", ip6_tnl->encap_limit);
+ }
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLAGS) {
+ nl_dump(p, " flags ");
+ nl_dump_line(p, " (%x)\n", ip6_tnl->flags);
+ }
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_FLOWINFO) {
+ nl_dump(p, " flowinfo ");
+ nl_dump_line(p, " (%x)\n", ip6_tnl->flowinfo);
+ }
+
+ if (ip6_tnl->ip6_tnl_mask & IP6_TNL_ATTR_PROTO) {
+ nl_dump(p, " proto ");
+ nl_dump_line(p, " (%x)\n", ip6_tnl->proto);
+ }
+}
+
+static int ip6_tnl_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+ struct ip6_tnl_info *ip6_tnl_dst, *ip6_tnl_src = src->l_info;
+ int err;
+
+ dst->l_info = NULL;
+
+ err = rtnl_link_set_type(dst, "ip6tnl");
+ if (err < 0)
+ return err;
+
+ ip6_tnl_dst = dst->l_info;
+
+ if (!ip6_tnl_dst || !ip6_tnl_src)
+ BUG();
+
+ memcpy(ip6_tnl_dst, ip6_tnl_src, sizeof(struct ip6_tnl_info));
+
+ return 0;
+}
+
+static struct rtnl_link_info_ops ip6_tnl_info_ops = {
+ .io_name = "ip6tnl",
+ .io_alloc = ip6_tnl_alloc,
+ .io_parse = ip6_tnl_parse,
+ .io_dump = {
+ [NL_DUMP_LINE] = ip6_tnl_dump_line,
+ [NL_DUMP_DETAILS] = ip6_tnl_dump_details,
+ },
+ .io_clone = ip6_tnl_clone,
+ .io_put_attrs = ip6_tnl_put_attrs,
+ .io_free = ip6_tnl_free,
+};
+
+#define IS_IP6_TNL_LINK_ASSERT(link)\
+ if ((link)->l_info_ops != &ip6_tnl_info_ops) {\
+ APPBUG("Link is not a ip6_tnl link. set type \"ip6tnl\" first.");\
+ return -NLE_OPNOTSUPP;\
+ }
+
+struct rtnl_link *rtnl_link_ip6_tnl_alloc(void)
+{
+ struct rtnl_link *link;
+ int err;
+
+ link = rtnl_link_alloc();
+ if (!link)
+ return NULL;
+
+ err = rtnl_link_set_type(link, "ip6tnl");
+ if (err < 0) {
+ rtnl_link_put(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+/**
+ * Check if link is a IP6_TNL link
+ * @arg link Link object
+ *
+ * @return True if link is a IP6_TNL link, otherwise false is returned.
+ */
+int rtnl_link_is_ip6_tnl(struct rtnl_link *link)
+{
+ return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "ip6tnl");
+}
+
+/**
+ * Create a new ip6_tnl tunnel device
+ * @arg sock netlink socket
+ * @arg name name of the tunnel device
+ *
+ * Creates a new ip6_tnl tunnel device in the kernel
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ip6_tnl_add(struct nl_sock *sk, const char *name)
+{
+ struct rtnl_link *link;
+ int err;
+
+ link = rtnl_link_ip6_tnl_alloc();
+ if (!link)
+ return -NLE_NOMEM;
+
+ if(name)
+ rtnl_link_set_name(link, name);
+
+ err = rtnl_link_add(sk, link, NLM_F_CREATE);
+ rtnl_link_put(link);
+
+ return err;
+}
+
+/**
+ * Set IP6_TNL tunnel interface index
+ * @arg link Link object
+ * @arg index interface index
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ip6_tnl_set_link(struct rtnl_link *link, uint32_t index)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ ip6_tnl->link = index;
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LINK;
+
+ return 0;
+}
+
+/**
+ * Get IP6_TNL tunnel interface index
+ * @arg link Link object
+ *
+ * @return interface index value
+ */
+uint32_t rtnl_link_ip6_tnl_get_link(struct rtnl_link *link)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ return ip6_tnl->link;
+}
+
+/**
+ * Set IP6_TNL tunnel local address
+ * @arg link Link object
+ * @arg addr local address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ip6_tnl_set_local(struct rtnl_link *link, struct in6_addr *addr)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ memcpy(&ip6_tnl->local, addr, sizeof(struct in6_addr));
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_LOCAL;
+
+ return 0;
+}
+
+/**
+ * Get IP6_TNL tunnel local address
+ * @arg link Link object
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ip6_tnl_get_local(struct rtnl_link *link, struct in6_addr *addr)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ memcpy(addr, &ip6_tnl->local, sizeof(struct in6_addr));
+
+ return 0;
+}
+
+/**
+ * Set IP6_TNL tunnel remote address
+ * @arg link Link object
+ * @arg remote remote address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ip6_tnl_set_remote(struct rtnl_link *link, struct in6_addr *addr)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ memcpy(&ip6_tnl->remote, addr, sizeof(struct in6_addr));
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_REMOTE;
+
+ return 0;
+}
+
+/**
+ * Get IP6_TNL tunnel remote address
+ * @arg link Link object
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ip6_tnl_get_remote(struct rtnl_link *link, struct in6_addr *addr)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ memcpy(addr, &ip6_tnl->remote, sizeof(struct in6_addr));
+
+ return 0;
+}
+
+/**
+ * Set IP6_TNL tunnel ttl
+ * @arg link Link object
+ * @arg ttl tunnel ttl
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ip6_tnl_set_ttl(struct rtnl_link *link, uint8_t ttl)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ ip6_tnl->ttl = ttl;
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TTL;
+
+ return 0;
+}
+
+/**
+ * Get IP6_TNL tunnel ttl
+ * @arg link Link object
+ *
+ * @return ttl value
+ */
+uint8_t rtnl_link_ip6_tnl_get_ttl(struct rtnl_link *link)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ return ip6_tnl->ttl;
+}
+
+/**
+ * Set IP6_TNL tunnel tos
+ * @arg link Link object
+ * @arg tos tunnel tos
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ip6_tnl_set_tos(struct rtnl_link *link, uint8_t tos)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ ip6_tnl->tos = tos;
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_TOS;
+
+ return 0;
+}
+
+/**
+ * Get IP6_TNL tunnel tos
+ * @arg link Link object
+ *
+ * @return tos value
+ */
+uint8_t rtnl_link_ip6_tnl_get_tos(struct rtnl_link *link)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ return ip6_tnl->tos;
+}
+
+/**
+ * Set IP6_TNL tunnel encap limit
+ * @arg link Link object
+ * @arg encap_limit encaplimit value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ip6_tnl_set_encaplimit(struct rtnl_link *link, uint8_t encap_limit)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ ip6_tnl->encap_limit = encap_limit;
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_ENCAPLIMIT;
+
+ return 0;
+}
+
+/**
+ * Get IP6_TNL encaplimit
+ * @arg link Link object
+ *
+ * @return encaplimit value
+ */
+uint8_t rtnl_link_ip6_tnl_get_encaplimit(struct rtnl_link *link)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ return ip6_tnl->encap_limit;
+}
+
+/**
+ * Set IP6_TNL tunnel flowinfo
+ * @arg link Link object
+ * @arg flowinfo flowinfo value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ip6_tnl_set_flowinfo(struct rtnl_link *link, uint32_t flowinfo)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ ip6_tnl->flowinfo = flowinfo;
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLOWINFO;
+
+ return 0;
+}
+
+/**
+ * Get IP6_TNL flowinfo
+ * @arg link Link object
+ *
+ * @return flowinfo value
+ */
+uint32_t rtnl_link_ip6_tnl_get_flowinfo(struct rtnl_link *link)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ return ip6_tnl->flowinfo;
+}
+
+/**
+ * Set IP6_TNL tunnel flags
+ * @arg link Link object
+ * @arg flags tunnel flags
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ip6_tnl_set_flags(struct rtnl_link *link, uint32_t flags)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ ip6_tnl->flags = flags;
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_FLAGS;
+
+ return 0;
+}
+
+/**
+ * Get IP6_TNL path flags
+ * @arg link Link object
+ *
+ * @return flags value
+ */
+uint32_t rtnl_link_ip6_tnl_get_flags(struct rtnl_link *link)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ return ip6_tnl->flags;
+}
+
+/**
+ * Set IP6_TNL tunnel proto
+ * @arg link Link object
+ * @arg proto tunnel proto
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ip6_tnl_set_proto(struct rtnl_link *link, uint8_t proto)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ ip6_tnl->proto = proto;
+ ip6_tnl->ip6_tnl_mask |= IP6_TNL_ATTR_PROTO;
+
+ return 0;
+}
+
+/**
+ * Get IP6_TNL proto
+ * @arg link Link object
+ *
+ * @return proto value
+ */
+uint8_t rtnl_link_ip6_tnl_get_proto(struct rtnl_link *link)
+{
+ struct ip6_tnl_info *ip6_tnl = link->l_info;
+
+ IS_IP6_TNL_LINK_ASSERT(link);
+
+ return ip6_tnl->proto;
+}
+
+static void __init ip6_tnl_init(void)
+{
+ rtnl_link_register_info(&ip6_tnl_info_ops);
+}
+
+static void __exit ip6_tnl_exit(void)
+{
+ rtnl_link_unregister_info(&ip6_tnl_info_ops);
+}
diff --git a/lib/route/link/ipgre.c b/lib/route/link/ipgre.c
new file mode 100644
index 00000000..74dbb9d3
--- /dev/null
+++ b/lib/route/link/ipgre.c
@@ -0,0 +1,727 @@
+/*
+ * lib/route/link/ipgre.c IPGRE Link Info
+ *
+ * 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) 2014 Susant Sahani <susant@redhat.com>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup ipgre IPGRE
+ * ipgre link module
+ *
+ * @details
+ * \b Link Type Name: "ipgre"
+ *
+ * @route_doc{link_ipgre, IPGRE Documentation}
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <linux/if_tunnel.h>
+
+#define IPGRE_ATTR_LINK (1 << 0)
+#define IPGRE_ATTR_IFLAGS (1 << 1)
+#define IPGRE_ATTR_OFLAGS (1 << 2)
+#define IPGRE_ATTR_IKEY (1 << 3)
+#define IPGRE_ATTR_OKEY (1 << 4)
+#define IPGRE_ATTR_LOCAL (1 << 5)
+#define IPGRE_ATTR_REMOTE (1 << 6)
+#define IPGRE_ATTR_TTL (1 << 7)
+#define IPGRE_ATTR_TOS (1 << 8)
+#define IPGRE_ATTR_PMTUDISC (1 << 9)
+
+struct ipgre_info
+{
+ uint8_t ttl;
+ uint8_t tos;
+ uint8_t pmtudisc;
+ uint16_t iflags;
+ uint16_t oflags;
+ uint32_t ikey;
+ uint32_t okey;
+ uint32_t link;
+ uint32_t local;
+ uint32_t remote;
+ uint32_t ipgre_mask;
+};
+
+static struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = {
+ [IFLA_GRE_LINK] = { .type = NLA_U32 },
+ [IFLA_GRE_IFLAGS] = { .type = NLA_U16 },
+ [IFLA_GRE_OFLAGS] = { .type = NLA_U16 },
+ [IFLA_GRE_IKEY] = { .type = NLA_U32 },
+ [IFLA_GRE_OKEY] = { .type = NLA_U32 },
+ [IFLA_GRE_LOCAL] = { .type = NLA_U32 },
+ [IFLA_GRE_REMOTE] = { .type = NLA_U32 },
+ [IFLA_GRE_TTL] = { .type = NLA_U8 },
+ [IFLA_GRE_TOS] = { .type = NLA_U8 },
+ [IFLA_GRE_PMTUDISC] = { .type = NLA_U8 },
+};
+
+static int ipgre_alloc(struct rtnl_link *link)
+{
+ struct ipgre_info *ipgre;
+
+ ipgre = calloc(1, sizeof(*ipgre));
+ if (!ipgre)
+ return -NLE_NOMEM;
+
+ link->l_info = ipgre;
+
+ return 0;
+}
+
+static int ipgre_parse(struct rtnl_link *link, struct nlattr *data,
+ struct nlattr *xstats)
+{
+ struct nlattr *tb[IFLA_IPTUN_MAX + 1];
+ struct ipgre_info *ipgre;
+ int err;
+
+ NL_DBG(3, "Parsing IPGRE link info");
+
+ err = nla_parse_nested(tb, IFLA_GRE_MAX, data, ipgre_policy);
+ if (err < 0)
+ goto errout;
+
+ err = ipgre_alloc(link);
+ if (err < 0)
+ goto errout;
+
+ ipgre = link->l_info;
+
+ if (tb[IFLA_GRE_LINK]) {
+ ipgre->link = nla_get_u32(tb[IFLA_GRE_LINK]);
+ ipgre->ipgre_mask |= IPGRE_ATTR_LINK;
+ }
+
+ if (tb[IFLA_GRE_IFLAGS]) {
+ ipgre->iflags = nla_get_u16(tb[IFLA_GRE_IFLAGS]);
+ ipgre->ipgre_mask |= IPGRE_ATTR_IFLAGS;
+ }
+
+ if (tb[IFLA_GRE_OFLAGS]) {
+ ipgre->oflags = nla_get_u16(tb[IFLA_GRE_OFLAGS]);
+ ipgre->ipgre_mask |= IPGRE_ATTR_OFLAGS;
+ }
+
+ if (tb[IFLA_GRE_IKEY]) {
+ ipgre->ikey = nla_get_u32(tb[IFLA_GRE_IKEY]);
+ ipgre->ipgre_mask |= IPGRE_ATTR_IKEY;
+ }
+
+ if (tb[IFLA_GRE_OKEY]) {
+ ipgre->okey = nla_get_u32(tb[IFLA_GRE_OKEY]);
+ ipgre->ipgre_mask |= IPGRE_ATTR_OKEY;
+ }
+
+ if (tb[IFLA_GRE_LOCAL]) {
+ ipgre->local = nla_get_u32(tb[IFLA_GRE_LOCAL]);
+ ipgre->ipgre_mask |= IPGRE_ATTR_LOCAL;
+ }
+
+ if (tb[IFLA_GRE_LOCAL]) {
+ ipgre->remote = nla_get_u32(tb[IFLA_GRE_LOCAL]);
+ ipgre->ipgre_mask |= IPGRE_ATTR_REMOTE;
+ }
+
+ if (tb[IFLA_GRE_TTL]) {
+ ipgre->ttl = nla_get_u8(tb[IFLA_GRE_TTL]);
+ ipgre->ipgre_mask |= IPGRE_ATTR_TTL;
+ }
+
+ if (tb[IFLA_GRE_TOS]) {
+ ipgre->tos = nla_get_u8(tb[IFLA_GRE_TOS]);
+ ipgre->ipgre_mask |= IPGRE_ATTR_TOS;
+ }
+
+ if (tb[IFLA_GRE_PMTUDISC]) {
+ ipgre->pmtudisc = nla_get_u8(tb[IFLA_GRE_PMTUDISC]);
+ ipgre->ipgre_mask |= IPGRE_ATTR_PMTUDISC;
+ }
+
+ err = 0;
+
+ errout:
+ return err;
+}
+
+static int ipgre_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+ struct ipgre_info *ipgre = link->l_info;
+ struct nlattr *data;
+
+ data = nla_nest_start(msg, IFLA_INFO_DATA);
+ if (!data)
+ return -NLE_MSGSIZE;
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_LINK)
+ NLA_PUT_U32(msg, IFLA_GRE_LINK, ipgre->link);
+
+ if (ipgre->ipgre_mask & IFLA_GRE_IFLAGS)
+ NLA_PUT_U16(msg, IFLA_GRE_IFLAGS, ipgre->iflags);
+
+ if (ipgre->ipgre_mask & IFLA_GRE_OFLAGS)
+ NLA_PUT_U16(msg, IFLA_GRE_OFLAGS, ipgre->oflags);
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_IKEY)
+ NLA_PUT_U32(msg, IFLA_GRE_IKEY, ipgre->ikey);
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_OKEY)
+ NLA_PUT_U32(msg, IFLA_GRE_OKEY, ipgre->okey);
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_LOCAL)
+ NLA_PUT_U32(msg, IFLA_GRE_LOCAL, ipgre->local);
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_REMOTE)
+ NLA_PUT_U32(msg, IFLA_GRE_REMOTE, ipgre->remote);
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_TTL)
+ NLA_PUT_U8(msg, IFLA_GRE_TTL, ipgre->ttl);
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_TOS)
+ NLA_PUT_U8(msg, IFLA_GRE_TOS, ipgre->tos);
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_PMTUDISC)
+ NLA_PUT_U8(msg, IFLA_GRE_PMTUDISC, ipgre->pmtudisc);
+
+ nla_nest_end(msg, data);
+
+ nla_put_failure:
+
+ return 0;
+}
+
+static void ipgre_free(struct rtnl_link *link)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ free(ipgre);
+ link->l_info = NULL;
+}
+
+static void ipgre_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ nl_dump(p, "ipgre : %s", link->l_name);
+}
+
+static void ipgre_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ struct ipgre_info *ipgre = link->l_info;
+ char *name, addr[INET_ADDRSTRLEN];
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_LINK) {
+ nl_dump(p, " link ");
+ name = rtnl_link_get_name(link);
+ if (name)
+ nl_dump_line(p, "%s\n", name);
+ else
+ nl_dump_line(p, "%u\n", ipgre->link);
+ }
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_IFLAGS) {
+ nl_dump(p, " iflags ");
+ nl_dump_line(p, "%x\n", ipgre->iflags);
+ }
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_OFLAGS) {
+ nl_dump(p, " oflags ");
+ nl_dump_line(p, "%x\n", ipgre->oflags);
+ }
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_IKEY) {
+ nl_dump(p, " ikey ");
+ nl_dump_line(p, "%x\n",ipgre->ikey);
+ }
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_OKEY) {
+ nl_dump(p, " okey ");
+ nl_dump_line(p, "%x\n", ipgre->okey);
+ }
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_LOCAL) {
+ nl_dump(p, " local ");
+ if(inet_ntop(AF_INET, &ipgre->local, addr, sizeof(addr)))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", ntohs(ipgre->local));
+ }
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_REMOTE) {
+ nl_dump(p, " remote ");
+ if(inet_ntop(AF_INET, &ipgre->remote, addr, sizeof(addr)))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", ntohs(ipgre->remote));
+ }
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_TTL) {
+ nl_dump(p, " ttl ");
+ nl_dump_line(p, "%u\n", ipgre->ttl);
+ }
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_TOS) {
+ nl_dump(p, " tos ");
+ nl_dump_line(p, "%u\n", ipgre->tos);
+ }
+
+ if (ipgre->ipgre_mask & IPGRE_ATTR_PMTUDISC) {
+ nl_dump(p, " pmtudisc ");
+ nl_dump_line(p, "enabled (%#x)\n", ipgre->pmtudisc);
+ }
+}
+
+static int ipgre_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+ struct ipgre_info *ipgre_dst, *ipgre_src = src->l_info;
+ int err;
+
+ dst->l_info = NULL;
+
+ err = rtnl_link_set_type(dst, "gre");
+ if (err < 0)
+ return err;
+
+ ipgre_dst = dst->l_info;
+
+ if (!ipgre_dst || !ipgre_src)
+ BUG();
+
+ memcpy(ipgre_dst, ipgre_src, sizeof(struct ipgre_info));
+
+ return 0;
+}
+
+static struct rtnl_link_info_ops ipgre_info_ops = {
+ .io_name = "gre",
+ .io_alloc = ipgre_alloc,
+ .io_parse = ipgre_parse,
+ .io_dump = {
+ [NL_DUMP_LINE] = ipgre_dump_line,
+ [NL_DUMP_DETAILS] = ipgre_dump_details,
+ },
+ .io_clone = ipgre_clone,
+ .io_put_attrs = ipgre_put_attrs,
+ .io_free = ipgre_free,
+};
+
+#define IS_IPGRE_LINK_ASSERT(link) \
+ if ((link)->l_info_ops != &ipgre_info_ops) { \
+ APPBUG("Link is not a ipgre link. set type \"gre\" first.");\
+ return -NLE_OPNOTSUPP; \
+ }
+
+struct rtnl_link *rtnl_link_ipgre_alloc(void)
+{
+ struct rtnl_link *link;
+ int err;
+
+ link = rtnl_link_alloc();
+ if (!link)
+ return NULL;
+
+ err = rtnl_link_set_type(link, "gre");
+ if (err < 0) {
+ rtnl_link_put(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+/**
+ * Check if link is a IPGRE link
+ * @arg link Link object
+ *
+ * @return True if link is a IPGRE link, otherwise 0 is returned.
+ */
+int rtnl_link_is_ipgre(struct rtnl_link *link)
+{
+ return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "gre");
+}
+/**
+ * Create a new ipip tunnel device
+ * @arg sock netlink socket
+ * @arg name name of the tunnel deviceL
+ *
+ * Creates a new ipip tunnel device in the kernel
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipgre_add(struct nl_sock *sk, const char *name)
+{
+ struct rtnl_link *link;
+ int err;
+
+ link = rtnl_link_ipgre_alloc();
+ if (!link)
+ return -NLE_NOMEM;
+
+ if(name)
+ rtnl_link_set_name(link, name);
+
+ err = rtnl_link_add(sk, link, NLM_F_CREATE);
+ rtnl_link_put(link);
+
+ return err;
+}
+/**
+ * Set IPGRE tunnel interface index
+ * @arg link Link object
+ * @arg index interface index
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipgre_set_link(struct rtnl_link *link, uint32_t index)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ ipgre->link = index;
+ ipgre->ipgre_mask |= IPGRE_ATTR_LINK;
+
+ return 0;
+}
+
+/**
+ * Get IPGRE tunnel interface index
+ * @arg link Link object
+ *
+ * @return interface index
+ */
+uint32_t rtnl_link_ipgre_get_link(struct rtnl_link *link)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ return ipgre->link;
+}
+
+/**
+ * Set IPGRE tunnel set iflags
+ * @arg link Link object
+ * @arg iflags gre iflags
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipgre_set_iflags(struct rtnl_link *link, uint16_t iflags)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ ipgre->iflags = iflags;
+ ipgre->ipgre_mask |= IPGRE_ATTR_IFLAGS;
+
+ return 0;
+}
+
+/**
+ * Get IPGRE tunnel iflags
+ * @arg link Link object
+ *
+ * @return iflags
+ */
+uint16_t rtnl_link_ipgre_get_iflags(struct rtnl_link *link)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ return ipgre->iflags;
+}
+
+/**
+ * Set IPGRE tunnel set oflags
+ * @arg link Link object
+ * @arg iflags gre oflags
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipgre_set_oflags(struct rtnl_link *link, uint16_t oflags)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ ipgre->oflags = oflags;
+ ipgre->ipgre_mask |= IPGRE_ATTR_OFLAGS;
+
+ return 0;
+}
+
+/**
+ * Get IPGRE tunnel oflags
+ * @arg link Link object
+ *
+ * @return oflags
+ */
+uint16_t rtnl_link_ipgre_get_oflags(struct rtnl_link *link)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ return ipgre->oflags;
+}
+
+/**
+ * Set IPGRE tunnel set ikey
+ * @arg link Link object
+ * @arg ikey gre ikey
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipgre_set_ikey(struct rtnl_link *link, uint32_t ikey)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ ipgre->ikey = ikey;
+ ipgre->ipgre_mask |= IPGRE_ATTR_IKEY;
+
+ return 0;
+}
+
+/**
+ * Get IPGRE tunnel ikey
+ * @arg link Link object
+ *
+ * @return ikey
+ */
+uint32_t rtnl_link_ipgre_get_ikey(struct rtnl_link *link)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ return ipgre->ikey;
+}
+
+/**
+ * Set IPGRE tunnel set okey
+ * @arg link Link object
+ * @arg okey gre okey
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipgre_set_okey(struct rtnl_link *link, uint32_t okey)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ ipgre->okey = okey;
+ ipgre->ipgre_mask |= IPGRE_ATTR_OKEY;
+
+ return 0;
+}
+
+/**
+ * Get IPGRE tunnel okey
+ * @arg link Link object
+ *
+ * @return okey value
+ */
+uint32_t rtnl_link_ipgre_get_okey(struct rtnl_link *link)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ return ipgre->okey;
+}
+
+/**
+ * Set IPGRE tunnel local address
+ * @arg link Link object
+ * @arg addr local address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipgre_set_local(struct rtnl_link *link, uint32_t addr)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ ipgre->local = addr;
+ ipgre->ipgre_mask |= IPGRE_ATTR_LOCAL;
+
+ return 0;
+}
+
+/**
+ * Get IPGRE tunnel local address
+ * @arg link Link object
+ *
+ * @return local address
+ */
+uint32_t rtnl_link_ipgre_get_local(struct rtnl_link *link)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ return ipgre->local;
+}
+
+/**
+ * Set IPGRE tunnel remote address
+ * @arg link Link object
+ * @arg remote remote address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipgre_set_remote(struct rtnl_link *link, uint32_t remote)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ ipgre->remote = remote;
+ ipgre->ipgre_mask |= IPGRE_ATTR_REMOTE;
+
+ return 0;
+}
+
+/**
+ * Get IPGRE tunnel remote address
+ * @arg link Link object
+ *
+ * @return remote address on success or a negative error code
+ */
+uint32_t rtnl_link_ipgre_get_remote(struct rtnl_link *link)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ return ipgre->remote;
+}
+
+/**
+ * Set IPGRE tunnel ttl
+ * @arg link Link object
+ * @arg ttl tunnel ttl
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipgre_set_ttl(struct rtnl_link *link, uint8_t ttl)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ ipgre->ttl = ttl;
+ ipgre->ipgre_mask |= IPGRE_ATTR_TTL;
+
+ return 0;
+}
+
+/**
+ * Set IPGRE tunnel ttl
+ * @arg link Link object
+ *
+ * @return ttl value
+ */
+uint8_t rtnl_link_ipgre_get_ttl(struct rtnl_link *link)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ return ipgre->ttl;
+}
+
+/**
+ * Set IPGRE tunnel tos
+ * @arg link Link object
+ * @arg tos tunnel tos
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipgre_set_tos(struct rtnl_link *link, uint8_t tos)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ ipgre->tos = tos;
+ ipgre->ipgre_mask |= IPGRE_ATTR_TOS;
+
+ return 0;
+}
+
+/**
+ * Get IPGRE tunnel tos
+ * @arg link Link object
+ *
+ * @return tos value
+ */
+uint8_t rtnl_link_ipgre_get_tos(struct rtnl_link *link)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ return ipgre->tos;
+}
+
+/**
+ * Set IPGRE tunnel path MTU discovery
+ * @arg link Link object
+ * @arg pmtudisc path MTU discovery
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipgre_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ ipgre->pmtudisc = pmtudisc;
+ ipgre->ipgre_mask |= IPGRE_ATTR_PMTUDISC;
+
+ return 0;
+}
+
+/**
+ * Get IPGRE path MTU discovery
+ * @arg link Link object
+ *
+ * @return pmtudisc value
+ */
+uint8_t rtnl_link_get_pmtudisc(struct rtnl_link *link)
+{
+ struct ipgre_info *ipgre = link->l_info;
+
+ IS_IPGRE_LINK_ASSERT(link);
+
+ return ipgre->pmtudisc;
+}
+
+static void __init ipgre_init(void)
+{
+ rtnl_link_register_info(&ipgre_info_ops);
+}
+
+static void __exit ipgre_exit(void)
+{
+ rtnl_link_unregister_info(&ipgre_info_ops);
+}
diff --git a/lib/route/link/ipip.c b/lib/route/link/ipip.c
new file mode 100644
index 00000000..ecf86ad7
--- /dev/null
+++ b/lib/route/link/ipip.c
@@ -0,0 +1,528 @@
+/*
+ * lib/route/link/ipip.c IPIP Link Info
+ *
+ * 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) 2014 Susant Sahani <susant@redhat.com>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup ipip IPIP
+ * ipip link module
+ *
+ * @details
+ * \b Link Type Name: "ipip"
+ *
+ * @route_doc{link_ipip, IPIP Documentation}
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <linux/if_tunnel.h>
+
+#define IPIP_ATTR_LINK (1 << 0)
+#define IPIP_ATTR_LOCAL (1 << 1)
+#define IPIP_ATTR_REMOTE (1 << 2)
+#define IPIP_ATTR_TTL (1 << 3)
+#define IPIP_ATTR_TOS (1 << 4)
+#define IPIP_ATTR_PMTUDISC (1 << 5)
+
+struct ipip_info
+{
+ uint8_t ttl;
+ uint8_t tos;
+ uint8_t pmtudisc;
+ uint32_t link;
+ uint32_t local;
+ uint32_t remote;
+ uint32_t ipip_mask;
+};
+
+static struct nla_policy ipip_policy[IFLA_IPTUN_MAX + 1] = {
+ [IFLA_IPTUN_LINK] = { .type = NLA_U32 },
+ [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 },
+ [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 },
+ [IFLA_IPTUN_TTL] = { .type = NLA_U8 },
+ [IFLA_IPTUN_TOS] = { .type = NLA_U8 },
+ [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 },
+};
+
+static int ipip_alloc(struct rtnl_link *link)
+{
+ struct ipip_info *ipip;
+
+ ipip = calloc(1, sizeof(*ipip));
+ if (!ipip)
+ return -NLE_NOMEM;
+
+ link->l_info = ipip;
+
+ return 0;
+}
+
+static int ipip_parse(struct rtnl_link *link, struct nlattr *data,
+ struct nlattr *xstats)
+{
+ struct nlattr *tb[IFLA_IPTUN_MAX + 1];
+ struct ipip_info *ipip;
+ int err;
+
+ NL_DBG(3, "Parsing IPIP link info");
+
+ err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, ipip_policy);
+ if (err < 0)
+ goto errout;
+
+ err = ipip_alloc(link);
+ if (err < 0)
+ goto errout;
+
+ ipip = link->l_info;
+
+ if (tb[IFLA_IPTUN_LINK]) {
+ ipip->link = nla_get_u32(tb[IFLA_IPTUN_LINK]);
+ ipip->ipip_mask |= IPIP_ATTR_LINK;
+ }
+
+ if (tb[IFLA_IPTUN_LOCAL]) {
+ ipip->local = nla_get_u32(tb[IFLA_IPTUN_LOCAL]);
+ ipip->ipip_mask |= IPIP_ATTR_LOCAL;
+ }
+
+ if (tb[IFLA_IPTUN_REMOTE]) {
+ ipip->remote = nla_get_u32(tb[IFLA_IPTUN_REMOTE]);
+ ipip->ipip_mask |= IPIP_ATTR_REMOTE;
+ }
+
+ if (tb[IFLA_IPTUN_TTL]) {
+ ipip->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]);
+ ipip->ipip_mask |= IPIP_ATTR_TTL;
+ }
+
+ if (tb[IFLA_IPTUN_TOS]) {
+ ipip->tos = nla_get_u8(tb[IFLA_IPTUN_TOS]);
+ ipip->ipip_mask |= IPIP_ATTR_TOS;
+ }
+
+ if (tb[IFLA_IPTUN_PMTUDISC]) {
+ ipip->pmtudisc = nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]);
+ ipip->ipip_mask |= IPIP_ATTR_PMTUDISC;
+ }
+
+ err = 0;
+
+errout:
+ return err;
+}
+
+static int ipip_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+ struct ipip_info *ipip = link->l_info;
+ struct nlattr *data;
+
+ data = nla_nest_start(msg, IFLA_INFO_DATA);
+ if (!data)
+ return -NLE_MSGSIZE;
+
+ if (ipip->ipip_mask & IPIP_ATTR_LINK)
+ NLA_PUT_U32(msg, IFLA_IPTUN_LINK, ipip->link);
+
+ if (ipip->ipip_mask & IPIP_ATTR_LOCAL)
+ NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, ipip->local);
+
+ if (ipip->ipip_mask & IPIP_ATTR_REMOTE)
+ NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, ipip->remote);
+
+ if (ipip->ipip_mask & IPIP_ATTR_TTL)
+ NLA_PUT_U8(msg, IFLA_IPTUN_TTL, ipip->ttl);
+
+ if (ipip->ipip_mask & IPIP_ATTR_TOS)
+ NLA_PUT_U8(msg, IFLA_IPTUN_TOS, ipip->tos);
+
+ if (ipip->ipip_mask & IPIP_ATTR_PMTUDISC)
+ NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, ipip->pmtudisc);
+
+ nla_nest_end(msg, data);
+
+nla_put_failure:
+ return 0;
+}
+
+static void ipip_free(struct rtnl_link *link)
+{
+ struct ipip_info *ipip = link->l_info;
+
+ free(ipip);
+ link->l_info = NULL;
+}
+
+static void ipip_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ nl_dump(p, "ipip : %s", link->l_name);
+}
+
+static void ipip_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ struct ipip_info *ipip = link->l_info;
+ char *name, addr[INET_ADDRSTRLEN];
+
+ if (ipip->ipip_mask & IPIP_ATTR_LINK) {
+ nl_dump(p, " link ");
+ name = rtnl_link_get_name(link);
+ if (name)
+ nl_dump_line(p, "%s\n", name);
+ else
+ nl_dump_line(p, "%u\n", ipip->link);
+ }
+
+ if (ipip->ipip_mask & IPIP_ATTR_LOCAL) {
+ nl_dump(p, " local ");
+ if(inet_ntop(AF_INET, &ipip->local, addr, sizeof(addr)))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", ntohs(ipip->local));
+ }
+
+ if (ipip->ipip_mask & IPIP_ATTR_REMOTE) {
+ nl_dump(p, " remote ");
+ if(inet_ntop(AF_INET, &ipip->remote, addr, sizeof(addr)))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", ntohs(ipip->remote));
+ }
+
+ if (ipip->ipip_mask & IPIP_ATTR_TTL) {
+ nl_dump(p, " ttl ");
+ nl_dump_line(p, "%u\n", ipip->ttl);
+ }
+
+ if (ipip->ipip_mask & IPIP_ATTR_TOS) {
+ nl_dump(p, " tos ");
+ nl_dump_line(p, "%u\n", ipip->tos);
+ }
+
+ if (ipip->ipip_mask & IPIP_ATTR_PMTUDISC) {
+ nl_dump(p, " pmtudisc ");
+ nl_dump_line(p, "enabled (%#x)\n", ipip->pmtudisc);
+ }
+}
+
+static int ipip_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+ struct ipip_info *ipip_dst, *ipip_src = src->l_info;
+ int err;
+
+ dst->l_info = NULL;
+
+ err = rtnl_link_set_type(dst, "ipip");
+ if (err < 0)
+ return err;
+
+ ipip_dst = dst->l_info;
+
+ if (!ipip_dst || !ipip_src)
+ BUG();
+
+ memcpy(ipip_dst, ipip_src, sizeof(struct ipip_info));
+
+ return 0;
+}
+
+static struct rtnl_link_info_ops ipip_info_ops = {
+ .io_name = "ipip",
+ .io_alloc = ipip_alloc,
+ .io_parse = ipip_parse,
+ .io_dump = {
+ [NL_DUMP_LINE] = ipip_dump_line,
+ [NL_DUMP_DETAILS] = ipip_dump_details,
+ },
+ .io_clone = ipip_clone,
+ .io_put_attrs = ipip_put_attrs,
+ .io_free = ipip_free,
+};
+
+#define IS_IPIP_LINK_ASSERT(link) \
+ if ((link)->l_info_ops != &ipip_info_ops) { \
+ APPBUG("Link is not a ipip link. set type \"ipip\" first."); \
+ return -NLE_OPNOTSUPP; \
+ }
+
+struct rtnl_link *rtnl_link_ipip_alloc(void)
+{
+ struct rtnl_link *link;
+ int err;
+
+ link = rtnl_link_alloc();
+ if (!link)
+ return NULL;
+
+ err = rtnl_link_set_type(link, "ipip");
+ if (err < 0) {
+ rtnl_link_put(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+/**
+ * Check if link is a IPIP link
+ * @arg link Link object
+ *
+ * @return True if link is a IPIP link, otherwise false is returned.
+ */
+int rtnl_link_is_ipip(struct rtnl_link *link)
+{
+ return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "ipip");
+}
+
+/**
+ * Create a new ipip tunnel device
+ * @arg sock netlink socket
+ * @arg name name of the tunnel deviceL
+ *
+ * Creates a new ipip tunnel device in the kernel
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipip_add(struct nl_sock *sk, const char *name)
+{
+ struct rtnl_link *link;
+ int err;
+
+ link = rtnl_link_ipip_alloc();
+ if (!link)
+ return -NLE_NOMEM;
+
+ if(name)
+ rtnl_link_set_name(link, name);
+
+ err = rtnl_link_add(sk, link, NLM_F_CREATE);
+ rtnl_link_put(link);
+
+ return err;
+}
+
+/**
+ * Set IPIP tunnel interface index
+ * @arg link Link object
+ * @arg index interface index
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipip_set_link(struct rtnl_link *link, uint32_t index)
+{
+ struct ipip_info *ipip = link->l_info;
+
+ IS_IPIP_LINK_ASSERT(link);
+
+ ipip->link = index;
+ ipip->ipip_mask |= IPIP_ATTR_LINK;
+
+ return 0;
+}
+
+/**
+ * Get IPIP tunnel interface index
+ * @arg link Link object
+ *
+ * @return interface index value
+ */
+uint32_t rtnl_link_ipip_get_link(struct rtnl_link *link)
+{
+ struct ipip_info *ipip = link->l_info;
+
+ IS_IPIP_LINK_ASSERT(link);
+
+ return ipip->link;
+}
+
+/**
+ * Set IPIP tunnel local address
+ * @arg link Link object
+ * @arg addr local address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipip_set_local(struct rtnl_link *link, uint32_t addr)
+{
+ struct ipip_info *ipip = link->l_info;
+
+ IS_IPIP_LINK_ASSERT(link);
+
+ ipip->local = addr;
+ ipip->ipip_mask |= IPIP_ATTR_LOCAL;
+
+ return 0;
+}
+
+/**
+ * Get IPIP tunnel local address
+ * @arg link Link object
+ *
+ * @return local address value
+ */
+uint32_t rtnl_link_ipip_get_local(struct rtnl_link *link)
+{
+ struct ipip_info *ipip = link->l_info;
+
+ IS_IPIP_LINK_ASSERT(link);
+
+ return ipip->local;
+}
+
+/**
+ * Set IPIP tunnel remote address
+ * @arg link Link object
+ * @arg remote remote address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipip_set_remote(struct rtnl_link *link, uint32_t addr)
+{
+ struct ipip_info *ipip = link->l_info;
+
+ IS_IPIP_LINK_ASSERT(link);
+
+ ipip->remote = addr;
+ ipip->ipip_mask |= IPIP_ATTR_REMOTE;
+
+ return 0;
+}
+
+/**
+ * Get IPIP tunnel remote address
+ * @arg link Link object
+ *
+ * @return remote address
+ */
+uint32_t rtnl_link_ipip_get_remote(struct rtnl_link *link)
+{
+ struct ipip_info *ipip = link->l_info;
+
+ IS_IPIP_LINK_ASSERT(link);
+
+ return ipip->remote;
+}
+
+/**
+ * Set IPIP tunnel ttl
+ * @arg link Link object
+ * @arg ttl tunnel ttl
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipip_set_ttl(struct rtnl_link *link, uint8_t ttl)
+{
+ struct ipip_info *ipip = link->l_info;
+
+ IS_IPIP_LINK_ASSERT(link);
+
+ ipip->ttl = ttl;
+ ipip->ipip_mask |= IPIP_ATTR_TTL;
+
+ return 0;
+}
+
+/**
+ * Get IPIP tunnel ttl
+ * @arg link Link object
+ *
+ * @return ttl value
+ */
+uint8_t rtnl_link_ipip_get_ttl(struct rtnl_link *link)
+{
+ struct ipip_info *ipip = link->l_info;
+
+ IS_IPIP_LINK_ASSERT(link);
+
+ return ipip->ttl;
+}
+
+/**
+ * Set IPIP tunnel tos
+ * @arg link Link object
+ * @arg tos tunnel tos
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipip_set_tos(struct rtnl_link *link, uint8_t tos)
+{
+ struct ipip_info *ipip = link->l_info;
+
+ IS_IPIP_LINK_ASSERT(link);
+
+ ipip->tos = tos;
+ ipip->ipip_mask |= IPIP_ATTR_TOS;
+
+ return 0;
+}
+
+/**
+ * Get IPIP tunnel tos
+ * @arg link Link object
+ *
+ * @return tos value
+ */
+uint8_t rtnl_link_ipip_get_tos(struct rtnl_link *link)
+{
+ struct ipip_info *ipip = link->l_info;
+
+ IS_IPIP_LINK_ASSERT(link);
+
+ return ipip->tos;
+}
+
+/**
+ * Set IPIP tunnel path MTU discovery
+ * @arg link Link object
+ * @arg pmtudisc path MTU discovery
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipip_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc)
+{
+ struct ipip_info *ipip = link->l_info;
+
+ IS_IPIP_LINK_ASSERT(link);
+
+ ipip->pmtudisc = pmtudisc;
+ ipip->ipip_mask |= IPIP_ATTR_PMTUDISC;
+
+ return 0;
+}
+
+/**
+ * Get IPIP path MTU discovery
+ * @arg link Link object
+ *
+ * @return pmtudisc value
+ */
+uint8_t rtnl_link_ipip_get_pmtudisc(struct rtnl_link *link)
+{
+ struct ipip_info *ipip = link->l_info;
+
+ IS_IPIP_LINK_ASSERT(link);
+
+ return ipip->pmtudisc;
+}
+
+static void __init ipip_init(void)
+{
+ rtnl_link_register_info(&ipip_info_ops);
+}
+
+static void __exit ipip_exit(void)
+{
+ rtnl_link_unregister_info(&ipip_info_ops);
+}
diff --git a/lib/route/link/ipvti.c b/lib/route/link/ipvti.c
new file mode 100644
index 00000000..71f61c36
--- /dev/null
+++ b/lib/route/link/ipvti.c
@@ -0,0 +1,477 @@
+ /*
+ * lib/route/link/ipvti.c IPVTI Link Info
+ *
+ * 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) 2014 Susant Sahani <susant@redhat.com>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup ipvti IPVTI
+ * ipvti link module
+ *
+ * @details
+ * \b Link Type Name: "ipvti"
+ *
+ * @route_doc{link_ipvti, IPVTI Documentation}
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <linux/if_tunnel.h>
+
+#define IPVTI_ATTR_LINK (1 << 0)
+#define IPVTI_ATTR_IKEY (1 << 1)
+#define IPVTI_ATTR_OKEY (1 << 2)
+#define IPVTI_ATTR_LOCAL (1 << 3)
+#define IPVTI_ATTR_REMOTE (1 << 4)
+
+struct ipvti_info
+{
+ uint32_t link;
+ uint32_t ikey;
+ uint32_t okey;
+ uint32_t local;
+ uint32_t remote;
+ uint32_t ipvti_mask;
+};
+
+static struct nla_policy ipvti_policy[IFLA_GRE_MAX + 1] = {
+ [IFLA_VTI_LINK] = { .type = NLA_U32 },
+ [IFLA_VTI_IKEY] = { .type = NLA_U32 },
+ [IFLA_VTI_OKEY] = { .type = NLA_U32 },
+ [IFLA_VTI_LOCAL] = { .type = NLA_U32 },
+ [IFLA_VTI_REMOTE] = { .type = NLA_U32 },
+};
+
+static int ipvti_alloc(struct rtnl_link *link)
+{
+ struct ipvti_info *ipvti;
+
+ ipvti = calloc(1, sizeof(*ipvti));
+ if (!ipvti)
+ return -NLE_NOMEM;
+
+ link->l_info = ipvti;
+
+ return 0;
+}
+
+static int ipvti_parse(struct rtnl_link *link, struct nlattr *data,
+ struct nlattr *xstats)
+{
+ struct nlattr *tb[IFLA_IPTUN_MAX + 1];
+ struct ipvti_info *ipvti;
+ int err;
+
+ NL_DBG(3, "Parsing IPVTI link info");
+
+ err = nla_parse_nested(tb, IFLA_GRE_MAX, data, ipvti_policy);
+ if (err < 0)
+ goto errout;
+
+ err = ipvti_alloc(link);
+ if (err < 0)
+ goto errout;
+
+ ipvti = link->l_info;
+
+ if (tb[IFLA_VTI_LINK]) {
+ ipvti->link = nla_get_u32(tb[IFLA_VTI_LINK]);
+ ipvti->ipvti_mask |= IPVTI_ATTR_LINK;
+ }
+
+ if (tb[IFLA_VTI_IKEY]) {
+ ipvti->ikey = nla_get_u32(tb[IFLA_VTI_IKEY]);
+ ipvti->ipvti_mask |= IPVTI_ATTR_IKEY;
+ }
+
+ if (tb[IFLA_VTI_OKEY]) {
+ ipvti->okey = nla_get_u32(tb[IFLA_VTI_OKEY]);
+ ipvti->ipvti_mask |= IPVTI_ATTR_OKEY;
+ }
+
+ if (tb[IFLA_VTI_LOCAL]) {
+ ipvti->local = nla_get_u32(tb[IFLA_VTI_LOCAL]);
+ ipvti->ipvti_mask |= IPVTI_ATTR_LOCAL;
+ }
+
+ if (tb[IFLA_VTI_REMOTE]) {
+ ipvti->remote = nla_get_u32(tb[IFLA_VTI_REMOTE]);
+ ipvti->ipvti_mask |= IPVTI_ATTR_REMOTE;
+ }
+
+ err = 0;
+
+ errout:
+ return err;
+}
+
+static int ipvti_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+ struct ipvti_info *ipvti = link->l_info;
+ struct nlattr *data;
+
+ data = nla_nest_start(msg, IFLA_INFO_DATA);
+ if (!data)
+ return -NLE_MSGSIZE;
+
+ if (ipvti->ipvti_mask & IPVTI_ATTR_LINK)
+ NLA_PUT_U32(msg, IFLA_VTI_LINK, ipvti->link);
+
+ if (ipvti->ipvti_mask & IPVTI_ATTR_IKEY)
+ NLA_PUT_U32(msg, IFLA_VTI_IKEY, ipvti->ikey);
+
+ if (ipvti->ipvti_mask & IFLA_VTI_IKEY)
+ NLA_PUT_U32(msg, IFLA_VTI_OKEY, ipvti->okey);
+
+ if (ipvti->ipvti_mask & IPVTI_ATTR_LOCAL)
+ NLA_PUT_U32(msg, IFLA_VTI_LOCAL, ipvti->local);
+
+ if (ipvti->ipvti_mask & IPVTI_ATTR_REMOTE)
+ NLA_PUT_U32(msg, IFLA_VTI_REMOTE, ipvti->remote);
+
+ nla_nest_end(msg, data);
+
+nla_put_failure:
+
+ return 0;
+}
+
+static void ipvti_free(struct rtnl_link *link)
+{
+ struct ipvti_info *ipvti = link->l_info;
+
+ free(ipvti);
+ link->l_info = NULL;
+}
+
+static void ipvti_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ nl_dump(p, "ipvti : %s", link->l_name);
+}
+
+static void ipvti_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ struct ipvti_info *ipvti = link->l_info;
+ char *name, addr[INET_ADDRSTRLEN];
+
+ if (ipvti->ipvti_mask & IPVTI_ATTR_LINK) {
+ nl_dump(p, " link ");
+ name = rtnl_link_get_name(link);
+ if (name)
+ nl_dump_line(p, "%s\n", name);
+ else
+ nl_dump_line(p, "%u\n", ipvti->link);
+ }
+
+ if (ipvti->ipvti_mask & IPVTI_ATTR_IKEY) {
+ nl_dump(p, " ikey ");
+ nl_dump_line(p, "%x\n",ipvti->ikey);
+ }
+
+ if (ipvti->ipvti_mask & IPVTI_ATTR_OKEY) {
+ nl_dump(p, " okey ");
+ nl_dump_line(p, "%x\n", ipvti->okey);
+ }
+
+ if (ipvti->ipvti_mask & IPVTI_ATTR_LOCAL) {
+ nl_dump(p, " local ");
+ if(inet_ntop(AF_INET, &ipvti->local, addr, sizeof(addr)))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", ntohs(ipvti->local));
+ }
+
+ if (ipvti->ipvti_mask & IPVTI_ATTR_REMOTE) {
+ nl_dump(p, " remote ");
+ if(inet_ntop(AF_INET, &ipvti->remote, addr, sizeof(addr)))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", ntohs(ipvti->remote));
+ }
+}
+
+static int ipvti_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+ struct ipvti_info *ipvti_dst, *ipvti_src = src->l_info;
+ int err;
+
+ dst->l_info = NULL;
+
+ err = rtnl_link_set_type(dst, "vti");
+ if (err < 0)
+ return err;
+
+ ipvti_dst = dst->l_info;
+
+ if (!ipvti_dst || !ipvti_src)
+ BUG();
+
+ memcpy(ipvti_dst, ipvti_src, sizeof(struct ipvti_info));
+
+ return 0;
+}
+
+static struct rtnl_link_info_ops ipvti_info_ops = {
+ .io_name = "vti",
+ .io_alloc = ipvti_alloc,
+ .io_parse = ipvti_parse,
+ .io_dump = {
+ [NL_DUMP_LINE] = ipvti_dump_line,
+ [NL_DUMP_DETAILS] = ipvti_dump_details,
+ },
+ .io_clone = ipvti_clone,
+ .io_put_attrs = ipvti_put_attrs,
+ .io_free = ipvti_free,
+};
+
+#define IS_IPVTI_LINK_ASSERT(link) \
+ if ((link)->l_info_ops != &ipvti_info_ops) { \
+ APPBUG("Link is not a ipvti link. set type \vti\" first."); \
+ return -NLE_OPNOTSUPP; \
+ }
+
+struct rtnl_link *rtnl_link_ipvti_alloc(void)
+{
+ struct rtnl_link *link;
+ int err;
+
+ link = rtnl_link_alloc();
+ if (!link)
+ return NULL;
+
+ err = rtnl_link_set_type(link, "vti");
+ if (err < 0) {
+ rtnl_link_put(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+/**
+ * Check if link is a IPVTI link
+ * @arg link Link object
+ *
+ * @return True if link is a IPVTI link, otherwise 0 is returned.
+ */
+int rtnl_link_is_ipvti(struct rtnl_link *link)
+{
+ return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vti");
+}
+/**
+ * Create a new ipvti tunnel device
+ * @arg sock netlink socket
+ * @arg name name of the tunnel deviceL
+ *
+ * Creates a new ipvti tunnel device in the kernel
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipvti_add(struct nl_sock *sk, const char *name)
+{
+ struct rtnl_link *link;
+ int err;
+
+ link = rtnl_link_ipvti_alloc();
+ if (!link)
+ return -NLE_NOMEM;
+
+ if(name)
+ rtnl_link_set_name(link, name);
+
+ err = rtnl_link_add(sk, link, NLM_F_CREATE);
+ rtnl_link_put(link);
+
+ return err;
+}
+/**
+ * Set IPVTI tunnel interface index
+ * @arg link Link object
+ * @arg index interface index
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipvti_set_link(struct rtnl_link *link, uint32_t index)
+{
+ struct ipvti_info *ipvti = link->l_info;
+
+ IS_IPVTI_LINK_ASSERT(link);
+
+ ipvti->link = index;
+ ipvti->ipvti_mask |= IPVTI_ATTR_LINK;
+
+ return 0;
+}
+
+/**
+ * Get IPVTI tunnel interface index
+ * @arg link Link object
+ *
+ * @return interface index
+ */
+uint32_t rtnl_link_ipvti_get_link(struct rtnl_link *link)
+{
+ struct ipvti_info *ipvti = link->l_info;
+
+ IS_IPVTI_LINK_ASSERT(link);
+
+ return ipvti->link;
+}
+
+/**
+ * Set IPVTI tunnel set ikey
+ * @arg link Link object
+ * @arg ikey gre ikey
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipvti_set_ikey(struct rtnl_link *link, uint32_t ikey)
+{
+ struct ipvti_info *ipvti = link->l_info;
+
+ IS_IPVTI_LINK_ASSERT(link);
+
+ ipvti->ikey = ikey;
+ ipvti->ipvti_mask |= IPVTI_ATTR_IKEY;
+
+ return 0;
+}
+
+/**
+ * Get IPVTI tunnel ikey
+ * @arg link Link object
+ *
+ * @return ikey
+ */
+uint32_t rtnl_link_ipvti_get_ikey(struct rtnl_link *link)
+{
+ struct ipvti_info *ipvti = link->l_info;
+
+ IS_IPVTI_LINK_ASSERT(link);
+
+ return ipvti->ikey;
+}
+
+/**
+ * Set IPVTI tunnel set okey
+ * @arg link Link object
+ * @arg okey gre okey
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipvti_set_okey(struct rtnl_link *link, uint32_t okey)
+{
+ struct ipvti_info *ipvti = link->l_info;
+
+ IS_IPVTI_LINK_ASSERT(link);
+
+ ipvti->okey = okey;
+ ipvti->ipvti_mask |= IPVTI_ATTR_OKEY;
+
+ return 0;
+}
+
+/**
+ * Get IPVTI tunnel okey
+ * @arg link Link object
+ *
+ * @return okey value
+ */
+uint32_t rtnl_link_ipvti_get_okey(struct rtnl_link *link)
+{
+ struct ipvti_info *ipvti = link->l_info;
+
+ IS_IPVTI_LINK_ASSERT(link);
+
+ return ipvti->okey;
+}
+
+/**
+ * Set IPVTI tunnel local address
+ * @arg link Link object
+ * @arg addr local address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipvti_set_local(struct rtnl_link *link, uint32_t addr)
+{
+ struct ipvti_info *ipvti = link->l_info;
+
+ IS_IPVTI_LINK_ASSERT(link);
+
+ ipvti->local = addr;
+ ipvti->ipvti_mask |= IPVTI_ATTR_LOCAL;
+
+ return 0;
+}
+
+/**
+ * Get IPVTI tunnel local address
+ * @arg link Link object
+ *
+ * @return local address
+ */
+uint32_t rtnl_link_ipvti_get_local(struct rtnl_link *link)
+{
+ struct ipvti_info *ipvti = link->l_info;
+
+ IS_IPVTI_LINK_ASSERT(link);
+
+ return ipvti->local;
+}
+
+/**
+ * Set IPVTI tunnel remote address
+ * @arg link Link object
+ * @arg remote remote address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_ipvti_set_remote(struct rtnl_link *link, uint32_t remote)
+{
+ struct ipvti_info *ipvti = link->l_info;
+
+ IS_IPVTI_LINK_ASSERT(link);
+
+ ipvti->remote = remote;
+ ipvti->ipvti_mask |= IPVTI_ATTR_REMOTE;
+
+ return 0;
+}
+
+/**
+ * Get IPVTI tunnel remote address
+ * @arg link Link object
+ *
+ * @return remote address on success or a negative error code
+ */
+uint32_t rtnl_link_ipvti_get_remote(struct rtnl_link *link)
+{
+ struct ipvti_info *ipvti = link->l_info;
+
+ IS_IPVTI_LINK_ASSERT(link);
+
+ return ipvti->remote;
+}
+
+static void __init ipvti_init(void)
+{
+ rtnl_link_register_info(&ipvti_info_ops);
+}
+
+static void __exit ipvti_exit(void)
+{
+ rtnl_link_unregister_info(&ipvti_info_ops);
+}
diff --git a/lib/route/link/macvlan.c b/lib/route/link/macvlan.c
new file mode 100644
index 00000000..23409035
--- /dev/null
+++ b/lib/route/link/macvlan.c
@@ -0,0 +1,367 @@
+/*
+ * lib/route/link/macvlan.c MACVLAN Link Info
+ *
+ * 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) 2013 Michael Braun <michael-dev@fami-braun.de>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup macvlan MACVLAN
+ * MAC-based Virtual LAN link module
+ *
+ * @details
+ * \b Link Type Name: "macvlan"
+ *
+ * @route_doc{link_macvlan, MACVLAN Documentation}
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <netlink/route/link/macvlan.h>
+
+#include <linux/if_link.h>
+
+/** @cond SKIP */
+#define MACVLAN_HAS_MODE (1<<0)
+#define MACVLAN_HAS_FLAGS (1<<1)
+
+struct macvlan_info
+{
+ uint32_t mvi_mode;
+ uint16_t mvi_flags; // there currently is only one flag and kernel has no flags_mask yet
+ uint32_t mvi_mask;
+};
+
+/** @endcond */
+
+static struct nla_policy macvlan_policy[IFLA_MACVLAN_MAX+1] = {
+ [IFLA_MACVLAN_MODE] = { .type = NLA_U32 },
+ [IFLA_MACVLAN_FLAGS] = { .type = NLA_U16 },
+};
+
+static int macvlan_alloc(struct rtnl_link *link)
+{
+ struct macvlan_info *mvi;
+
+ if ((mvi = calloc(1, sizeof(*mvi))) == NULL)
+ return -NLE_NOMEM;
+
+ link->l_info = mvi;
+
+ return 0;
+}
+
+static int macvlan_parse(struct rtnl_link *link, struct nlattr *data,
+ struct nlattr *xstats)
+{
+ struct nlattr *tb[IFLA_MACVLAN_MAX+1];
+ struct macvlan_info *mvi;
+ int err;
+
+ NL_DBG(3, "Parsing MACVLAN link info");
+
+ if ((err = nla_parse_nested(tb, IFLA_MACVLAN_MAX, data, macvlan_policy)) < 0)
+ goto errout;
+
+ if ((err = macvlan_alloc(link)) < 0)
+ goto errout;
+
+ mvi = link->l_info;
+
+ if (tb[IFLA_MACVLAN_MODE]) {
+ mvi->mvi_mode = nla_get_u32(tb[IFLA_MACVLAN_MODE]);
+ mvi->mvi_mask |= MACVLAN_HAS_MODE;
+ }
+
+ if (tb[IFLA_MACVLAN_FLAGS]) {
+ mvi->mvi_mode = nla_get_u16(tb[IFLA_MACVLAN_FLAGS]);
+ mvi->mvi_mask |= MACVLAN_HAS_FLAGS;
+ }
+
+ err = 0;
+errout:
+ return err;
+}
+
+static void macvlan_free(struct rtnl_link *link)
+{
+ free(link->l_info);
+ link->l_info = NULL;
+}
+
+static void macvlan_dump(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ char buf[64];
+ struct macvlan_info *mvi = link->l_info;
+
+ if (mvi->mvi_mask & MACVLAN_HAS_MODE) {
+ rtnl_link_macvlan_mode2str(mvi->mvi_mode, buf, sizeof(buf));
+ nl_dump(p, "macvlan-mode %s", buf);
+ }
+
+ if (mvi->mvi_mask & MACVLAN_HAS_FLAGS) {
+ rtnl_link_macvlan_flags2str(mvi->mvi_flags, buf, sizeof(buf));
+ nl_dump(p, "macvlan-flags %s", buf);
+ }
+}
+
+static int macvlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+ struct macvlan_info *vdst, *vsrc = src->l_info;
+ int err;
+
+ dst->l_info = NULL;
+ if ((err = rtnl_link_set_type(dst, "macvlan")) < 0)
+ return err;
+ vdst = dst->l_info;
+
+ if (!vdst || !vsrc)
+ return -NLE_NOMEM;
+
+ memcpy(vdst, vsrc, sizeof(struct macvlan_info));
+
+ return 0;
+}
+
+static int macvlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+ struct macvlan_info *mvi = link->l_info;
+ struct nlattr *data;
+
+ if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+ return -NLE_MSGSIZE;
+
+ if (mvi->mvi_mask & MACVLAN_HAS_MODE)
+ NLA_PUT_U32(msg, IFLA_MACVLAN_MODE, mvi->mvi_mode);
+
+ if (mvi->mvi_mask & MACVLAN_HAS_FLAGS)
+ NLA_PUT_U16(msg, IFLA_MACVLAN_FLAGS, mvi->mvi_flags);
+
+ nla_nest_end(msg, data);
+
+nla_put_failure:
+
+ return 0;
+}
+
+static struct rtnl_link_info_ops macvlan_info_ops = {
+ .io_name = "macvlan",
+ .io_alloc = macvlan_alloc,
+ .io_parse = macvlan_parse,
+ .io_dump = {
+ [NL_DUMP_LINE] = macvlan_dump,
+ [NL_DUMP_DETAILS] = macvlan_dump,
+ },
+ .io_clone = macvlan_clone,
+ .io_put_attrs = macvlan_put_attrs,
+ .io_free = macvlan_free,
+};
+
+/** @cond SKIP */
+#define IS_MACVLAN_LINK_ASSERT(link) \
+ if ((link)->l_info_ops != &macvlan_info_ops) { \
+ APPBUG("Link is not a macvlan link. set type \"macvlan\" first."); \
+ return -NLE_OPNOTSUPP; \
+ }
+/** @endcond */
+
+/**
+ * @name MACVLAN Object
+ * @{
+ */
+
+/**
+ * Allocate link object of type MACVLAN
+ *
+ * @return Allocated link object or NULL.
+ */
+struct rtnl_link *rtnl_link_macvlan_alloc(void)
+{
+ struct rtnl_link *link;
+ int err;
+
+ if (!(link = rtnl_link_alloc()))
+ return NULL;
+
+ if ((err = rtnl_link_set_type(link, "macvlan")) < 0) {
+ rtnl_link_put(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+/**
+ * Check if link is a MACVLAN link
+ * @arg link Link object
+ *
+ * @return True if link is a MACVLAN link, otherwise false is returned.
+ */
+int rtnl_link_is_macvlan(struct rtnl_link *link)
+{
+ return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "macvlan");
+}
+
+/**
+ * Set MACVLAN MODE
+ * @arg link Link object
+ * @arg mode MACVLAN mode
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_macvlan_set_mode(struct rtnl_link *link, uint32_t mode)
+{
+ struct macvlan_info *mvi = link->l_info;
+
+ IS_MACVLAN_LINK_ASSERT(link);
+
+ mvi->mvi_mode = mode;
+ mvi->mvi_mask |= MACVLAN_HAS_MODE;
+
+ return 0;
+}
+
+/**
+ * Get MACVLAN Mode
+ * @arg link Link object
+ *
+ * @return MACVLAN mode, 0 if not set or a negative error code.
+ */
+uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *link)
+{
+ struct macvlan_info *mvi = link->l_info;
+
+ IS_MACVLAN_LINK_ASSERT(link);
+
+ if (mvi->mvi_mask & MACVLAN_HAS_MODE)
+ return mvi->mvi_mode;
+ else
+ return 0;
+}
+
+/**
+ * Set MACVLAN flags
+ * @arg link Link object
+ * @arg flags MACVLAN flags
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_macvlan_set_flags(struct rtnl_link *link, uint16_t flags)
+{
+ struct macvlan_info *mvi = link->l_info;
+
+ IS_MACVLAN_LINK_ASSERT(link);
+
+ mvi->mvi_flags |= flags;
+ mvi->mvi_mask |= MACVLAN_HAS_FLAGS;
+
+ return 0;
+}
+
+/**
+ * Unset MACVLAN flags
+ * @arg link Link object
+ * @arg flags MACVLAN flags
+ *
+ * Note: kernel currently only has a single flag and lacks flags_mask to
+ * indicate which flags shall be changed (it always all).
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_link_macvlan_unset_flags(struct rtnl_link *link, uint16_t flags)
+{
+ struct macvlan_info *mvi = link->l_info;
+
+ IS_MACVLAN_LINK_ASSERT(link);
+
+ mvi->mvi_flags &= ~flags;
+ mvi->mvi_mask |= MACVLAN_HAS_FLAGS;
+
+ return 0;
+}
+
+/**
+ * Get MACVLAN flags
+ * @arg link Link object
+ *
+ * @return MACVLAN flags, 0 if none set, or a negative error code.
+ */
+uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *link)
+{
+ struct macvlan_info *mvi = link->l_info;
+
+ IS_MACVLAN_LINK_ASSERT(link);
+
+ return mvi->mvi_flags;
+}
+
+/** @} */
+
+static const struct trans_tbl macvlan_flags[] = {
+ __ADD(MACVLAN_FLAG_NOPROMISC, nopromisc)
+};
+
+static const struct trans_tbl macvlan_modes[] = {
+ __ADD(MACVLAN_MODE_PRIVATE, private)
+ __ADD(MACVLAN_MODE_VEPA, vepa)
+ __ADD(MACVLAN_MODE_BRIDGE, bridge)
+ __ADD(MACVLAN_MODE_PASSTHRU, passthru)
+};
+
+/**
+ * @name Flag Translation
+ * @{
+ */
+
+char *rtnl_link_macvlan_flags2str(int flags, char *buf, size_t len)
+{
+ return __flags2str(flags, buf, len, macvlan_flags, ARRAY_SIZE(macvlan_flags));
+}
+
+int rtnl_link_macvlan_str2flags(const char *name)
+{
+ return __str2flags(name, macvlan_flags, ARRAY_SIZE(macvlan_flags));
+}
+
+/** @} */
+
+/**
+ * @name Mode Translation
+ * @{
+ */
+
+char *rtnl_link_macvlan_mode2str(int mode, char *buf, size_t len)
+{
+ return __type2str(mode, buf, len, macvlan_modes, ARRAY_SIZE(macvlan_modes));
+}
+
+int rtnl_link_macvlan_str2mode(const char *name)
+{
+ return __str2type(name, macvlan_modes, ARRAY_SIZE(macvlan_modes));
+}
+
+/** @} */
+
+static void __init macvlan_init(void)
+{
+ rtnl_link_register_info(&macvlan_info_ops);
+}
+
+static void __exit macvlan_exit(void)
+{
+ rtnl_link_unregister_info(&macvlan_info_ops);
+}
+
+/** @} */
diff --git a/lib/route/link/sit.c b/lib/route/link/sit.c
new file mode 100644
index 00000000..694c177f
--- /dev/null
+++ b/lib/route/link/sit.c
@@ -0,0 +1,624 @@
+/*
+ * lib/route/link/sit.c SIT Link Info
+ *
+ * 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) 2014 Susant Sahani <susant@redhat.com>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup sit SIT
+ * sit link module
+ *
+ * @details
+ * \b Link Type Name: "sit"
+ *
+ * @route_doc{link_sit, SIT Documentation}
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <linux/if_tunnel.h>
+
+#define SIT_ATTR_LINK (1 << 0)
+#define SIT_ATTR_LOCAL (1 << 1)
+#define SIT_ATTR_REMOTE (1 << 2)
+#define SIT_ATTR_TTL (1 << 3)
+#define SIT_ATTR_TOS (1 << 4)
+#define SIT_ATTR_PMTUDISC (1 << 5)
+#define SIT_ATTR_FLAGS (1 << 6)
+#define SIT_ATTR_PROTO (1 << 7)
+
+struct sit_info
+{
+ uint8_t ttl;
+ uint8_t tos;
+ uint8_t pmtudisc;
+ uint8_t proto;
+ uint16_t flags;
+ uint32_t link;
+ uint32_t local;
+ uint32_t remote;
+ uint32_t sit_mask;
+};
+
+static struct nla_policy sit_policy[IFLA_IPTUN_MAX + 1] = {
+ [IFLA_IPTUN_LINK] = { .type = NLA_U32 },
+ [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 },
+ [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 },
+ [IFLA_IPTUN_TTL] = { .type = NLA_U8 },
+ [IFLA_IPTUN_TOS] = { .type = NLA_U8 },
+ [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 },
+ [IFLA_IPTUN_FLAGS] = { .type = NLA_U16 },
+ [IFLA_IPTUN_PROTO] = { .type = NLA_U8 },
+};
+
+static int sit_alloc(struct rtnl_link *link)
+{
+ struct sit_info *sit;
+
+ sit = calloc(1, sizeof(*sit));
+ if (!sit)
+ return -NLE_NOMEM;
+
+ link->l_info = sit;
+
+ return 0;
+}
+
+static int sit_parse(struct rtnl_link *link, struct nlattr *data,
+ struct nlattr *xstats)
+{
+ struct nlattr *tb[IFLA_IPTUN_MAX + 1];
+ struct sit_info *sit;
+ int err;
+
+ NL_DBG(3, "Parsing SIT link info");
+
+ err = nla_parse_nested(tb, IFLA_IPTUN_MAX, data, sit_policy);
+ if (err < 0)
+ goto errout;
+
+ err = sit_alloc(link);
+ if (err < 0)
+ goto errout;
+
+ sit = link->l_info;
+
+ if (tb[IFLA_IPTUN_LINK]) {
+ sit->link = nla_get_u32(tb[IFLA_IPTUN_LINK]);
+ sit->sit_mask |= SIT_ATTR_LINK;
+ }
+
+ if (tb[IFLA_IPTUN_LOCAL]) {
+ sit->local = nla_get_u32(tb[IFLA_IPTUN_LOCAL]);
+ sit->sit_mask |= SIT_ATTR_LOCAL;
+ }
+
+ if (tb[IFLA_IPTUN_REMOTE]) {
+ sit->remote = nla_get_u32(tb[IFLA_IPTUN_REMOTE]);
+ sit->sit_mask |= SIT_ATTR_REMOTE;
+ }
+
+ if (tb[IFLA_IPTUN_TTL]) {
+ sit->ttl = nla_get_u8(tb[IFLA_IPTUN_TTL]);
+ sit->sit_mask |= SIT_ATTR_TTL;
+ }
+
+ if (tb[IFLA_IPTUN_TOS]) {
+ sit->tos = nla_get_u8(tb[IFLA_IPTUN_TOS]);
+ sit->sit_mask |= SIT_ATTR_TOS;
+ }
+
+ if (tb[IFLA_IPTUN_PMTUDISC]) {
+ sit->pmtudisc = nla_get_u8(tb[IFLA_IPTUN_PMTUDISC]);
+ sit->sit_mask |= SIT_ATTR_PMTUDISC;
+ }
+
+ if (tb[IFLA_IPTUN_FLAGS]) {
+ sit->flags = nla_get_u16(tb[IFLA_IPTUN_FLAGS]);
+ sit->sit_mask |= SIT_ATTR_FLAGS;
+ }
+
+ if (tb[IFLA_IPTUN_PROTO]) {
+ sit->proto = nla_get_u8(tb[IFLA_IPTUN_PROTO]);
+ sit->sit_mask |= SIT_ATTR_PROTO;
+ }
+
+ err = 0;
+
+ errout:
+ return err;
+}
+
+static int sit_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+ struct sit_info *sit = link->l_info;
+ struct nlattr *data;
+
+ data = nla_nest_start(msg, IFLA_INFO_DATA);
+ if (!data)
+ return -NLE_MSGSIZE;
+
+ if (sit->sit_mask & SIT_ATTR_LINK)
+ NLA_PUT_U32(msg, IFLA_IPTUN_LINK, sit->link);
+
+ if (sit->sit_mask & SIT_ATTR_LOCAL)
+ NLA_PUT_U32(msg, IFLA_IPTUN_LOCAL, sit->local);
+
+ if (sit->sit_mask & SIT_ATTR_REMOTE)
+ NLA_PUT_U32(msg, IFLA_IPTUN_REMOTE, sit->remote);
+
+ if (sit->sit_mask & SIT_ATTR_TTL)
+ NLA_PUT_U8(msg, IFLA_IPTUN_TTL, sit->ttl);
+
+ if (sit->sit_mask & SIT_ATTR_TOS)
+ NLA_PUT_U8(msg, IFLA_IPTUN_TOS, sit->tos);
+
+ if (sit->sit_mask & SIT_ATTR_PMTUDISC)
+ NLA_PUT_U8(msg, IFLA_IPTUN_PMTUDISC, sit->pmtudisc);
+
+ if (sit->sit_mask & SIT_ATTR_FLAGS)
+ NLA_PUT_U16(msg, IFLA_IPTUN_FLAGS, sit->flags);
+
+ if (sit->sit_mask & SIT_ATTR_PROTO)
+ NLA_PUT_U8(msg, IFLA_IPTUN_PROTO, sit->proto);
+
+ nla_nest_end(msg, data);
+
+nla_put_failure:
+
+ return 0;
+}
+
+static void sit_free(struct rtnl_link *link)
+{
+ struct sit_info *sit = link->l_info;
+
+ free(sit);
+ link->l_info = NULL;
+}
+
+static void sit_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ nl_dump(p, "sit : %s", link->l_name);
+}
+
+static void sit_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ struct sit_info *sit = link->l_info;
+ char *name, addr[INET_ADDRSTRLEN];
+
+ if (sit->sit_mask & SIT_ATTR_LINK) {
+ nl_dump(p, " link ");
+ name = rtnl_link_get_name(link);
+ if (name)
+ nl_dump_line(p, "%s\n", name);
+ else
+ nl_dump_line(p, "%u\n", sit->link);
+ }
+
+ if (sit->sit_mask & SIT_ATTR_LOCAL) {
+ nl_dump(p, " local ");
+ if(inet_ntop(AF_INET, &sit->local, addr, sizeof(addr)))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", ntohs(sit->local));
+ }
+
+ if (sit->sit_mask & SIT_ATTR_REMOTE) {
+ nl_dump(p, " remote ");
+ if(inet_ntop(AF_INET, &sit->remote, addr, sizeof(addr)))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", ntohs(sit->remote));
+ }
+
+ if (sit->sit_mask & SIT_ATTR_TTL) {
+ nl_dump(p, " ttl ");
+ nl_dump_line(p, "%u\n", sit->ttl);
+ }
+
+ if (sit->sit_mask & SIT_ATTR_TOS) {
+ nl_dump(p, " tos ");
+ nl_dump_line(p, "%u\n", sit->tos);
+ }
+
+ if (sit->sit_mask & SIT_ATTR_FLAGS) {
+ nl_dump(p, " flags ");
+ nl_dump_line(p, " (%x)\n", sit->flags);
+ }
+
+ if (sit->sit_mask & SIT_ATTR_PROTO) {
+ nl_dump(p, " proto ");
+ nl_dump_line(p, " (%x)\n", sit->proto);
+ }
+}
+
+static int sit_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+ struct sit_info *sit_dst, *sit_src = src->l_info;
+ int err;
+
+ dst->l_info = NULL;
+
+ err = rtnl_link_set_type(dst, "sit");
+ if (err < 0)
+ return err;
+
+ sit_dst = dst->l_info;
+
+ if (!sit_dst || !sit_src)
+ return -NLE_NOMEM;
+
+ memcpy(sit_dst, sit_src, sizeof(struct sit_info));
+
+ return 0;
+}
+
+static struct rtnl_link_info_ops sit_info_ops = {
+ .io_name = "sit",
+ .io_alloc = sit_alloc,
+ .io_parse = sit_parse,
+ .io_dump = {
+ [NL_DUMP_LINE] = sit_dump_line,
+ [NL_DUMP_DETAILS] = sit_dump_details,
+ },
+ .io_clone = sit_clone,
+ .io_put_attrs = sit_put_attrs,
+ .io_free = sit_free,
+};
+
+#define IS_SIT_LINK_ASSERT(link) \
+ if ((link)->l_info_ops != &sit_info_ops) { \
+ APPBUG("Link is not a sit link. set type \"sit\" first."); \
+ return -NLE_OPNOTSUPP; \
+ }
+
+struct rtnl_link *rtnl_link_sit_alloc(void)
+{
+ struct rtnl_link *link;
+ int err;
+
+ link = rtnl_link_alloc();
+ if (!link)
+ return NULL;
+
+ err = rtnl_link_set_type(link, "sit");
+ if (err < 0) {
+ rtnl_link_put(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+/**
+ * Check if link is a SIT link
+ * @arg link Link object
+ *
+ * @return True if link is a SIT link, otherwise false is returned.
+ */
+int rtnl_link_is_sit(struct rtnl_link *link)
+{
+ return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "sit");
+}
+
+/**
+ * Create a new sit tunnel device
+ * @arg sock netlink socket
+ * @arg name name of the tunnel device
+ *
+ * Creates a new sit tunnel device in the kernel
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_sit_add(struct nl_sock *sk, const char *name)
+{
+ struct rtnl_link *link;
+ int err;
+
+ link = rtnl_link_sit_alloc();
+ if (!link)
+ return -NLE_NOMEM;
+
+ if(name)
+ rtnl_link_set_name(link, name);
+
+ err = rtnl_link_add(sk, link, NLM_F_CREATE);
+ rtnl_link_put(link);
+
+ return err;
+}
+
+/**
+ * Set SIT tunnel interface index
+ * @arg link Link object
+ * @arg index interface index
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_sit_set_link(struct rtnl_link *link, uint32_t index)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ sit->link = index;
+ sit->sit_mask |= SIT_ATTR_LINK;
+
+ return 0;
+}
+
+/**
+ * Get SIT tunnel interface index
+ * @arg link Link object
+ *
+ * @return interface index value
+ */
+uint32_t rtnl_link_sit_get_link(struct rtnl_link *link)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ return sit->link;
+}
+
+/**
+ * Set SIT tunnel local address
+ * @arg link Link object
+ * @arg addr local address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_sit_set_local(struct rtnl_link *link, uint32_t addr)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ sit->local = addr;
+ sit->sit_mask |= SIT_ATTR_LOCAL;
+
+ return 0;
+}
+
+/**
+ * Get SIT tunnel local address
+ * @arg link Link object
+ *
+ * @return local address value
+ */
+uint32_t rtnl_link_sit_get_local(struct rtnl_link *link)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ return sit->local;
+}
+
+/**
+ * Set SIT tunnel remote address
+ * @arg link Link object
+ * @arg remote remote address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_sit_set_remote(struct rtnl_link *link, uint32_t addr)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ sit->remote = addr;
+ sit->sit_mask |= SIT_ATTR_REMOTE;
+
+ return 0;
+}
+
+/**
+ * Get SIT tunnel remote address
+ * @arg link Link object
+ *
+ * @return remote address
+ */
+uint32_t rtnl_link_sit_get_remote(struct rtnl_link *link)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ return sit->remote;
+}
+
+/**
+ * Set SIT tunnel ttl
+ * @arg link Link object
+ * @arg ttl tunnel ttl
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_sit_set_ttl(struct rtnl_link *link, uint8_t ttl)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ sit->ttl = ttl;
+ sit->sit_mask |= SIT_ATTR_TTL;
+
+ return 0;
+}
+
+/**
+ * Get SIT tunnel ttl
+ * @arg link Link object
+ *
+ * @return ttl value
+ */
+uint8_t rtnl_link_sit_get_ttl(struct rtnl_link *link)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ return sit->ttl;
+}
+
+/**
+ * Set SIT tunnel tos
+ * @arg link Link object
+ * @arg tos tunnel tos
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_sit_set_tos(struct rtnl_link *link, uint8_t tos)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ sit->tos = tos;
+ sit->sit_mask |= SIT_ATTR_TOS;
+
+ return 0;
+}
+
+/**
+ * Get SIT tunnel tos
+ * @arg link Link object
+ *
+ * @return tos value
+ */
+uint8_t rtnl_link_sit_get_tos(struct rtnl_link *link)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ return sit->tos;
+}
+
+/**
+ * Set SIT tunnel path MTU discovery
+ * @arg link Link object
+ * @arg pmtudisc path MTU discovery
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_sit_set_pmtudisc(struct rtnl_link *link, uint8_t pmtudisc)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ sit->pmtudisc = pmtudisc;
+ sit->sit_mask |= SIT_ATTR_PMTUDISC;
+
+ return 0;
+}
+
+/**
+ * Get SIT path MTU discovery
+ * @arg link Link object
+ *
+ * @return pmtudisc value
+ */
+uint8_t rtnl_link_sit_get_pmtudisc(struct rtnl_link *link)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ return sit->pmtudisc;
+}
+
+/**
+ * Set SIT tunnel flags
+ * @arg link Link object
+ * @arg flags tunnel flags
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_sit_set_flags(struct rtnl_link *link, uint16_t flags)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ sit->flags = flags;
+ sit->sit_mask |= SIT_ATTR_FLAGS;
+
+ return 0;
+}
+
+/**
+ * Get SIT path flags
+ * @arg link Link object
+ *
+ * @return flags value
+ */
+uint16_t rtnl_link_sit_get_flags(struct rtnl_link *link)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ return sit->flags;
+}
+
+/**
+ * Set SIT tunnel proto
+ * @arg link Link object
+ * @arg proto tunnel proto
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_sit_set_proto(struct rtnl_link *link, uint8_t proto)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ sit->proto = proto;
+ sit->sit_mask |= SIT_ATTR_PROTO;
+
+ return 0;
+}
+
+/**
+ * Get SIT proto
+ * @arg link Link object
+ *
+ * @return proto value
+ */
+uint8_t rtnl_link_sit_get_proto(struct rtnl_link *link)
+{
+ struct sit_info *sit = link->l_info;
+
+ IS_SIT_LINK_ASSERT(link);
+
+ return sit->proto;
+}
+
+static void __init sit_init(void)
+{
+ rtnl_link_register_info(&sit_info_ops);
+}
+
+static void __exit sit_exit(void)
+{
+ rtnl_link_unregister_info(&sit_info_ops);
+}
diff --git a/lib/route/link/veth.c b/lib/route/link/veth.c
new file mode 100644
index 00000000..e7e4a268
--- /dev/null
+++ b/lib/route/link/veth.c
@@ -0,0 +1,308 @@
+/*
+ * lib/route/link/veth.c Virtual Ethernet
+ *
+ * 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) 2013 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup veth VETH
+ * Virtual Ethernet
+ *
+ * @details
+ * \b Link Type Name: "veth"
+ *
+ * @route_doc{link_veth, VETH Documentation}
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <netlink/route/link/veth.h>
+
+#include <linux/if_link.h>
+
+static struct nla_policy veth_policy[VETH_INFO_MAX+1] = {
+ [VETH_INFO_PEER] = { .minlen = sizeof(struct ifinfomsg) },
+};
+
+static int veth_parse(struct rtnl_link *link, struct nlattr *data,
+ struct nlattr *xstats)
+{
+ struct nlattr *tb[VETH_INFO_MAX+1];
+ struct nlattr *peer_tb[IFLA_MAX + 1];
+ struct rtnl_link *peer = link->l_info;
+ int err;
+
+ NL_DBG(3, "Parsing veth link info");
+
+ if ((err = nla_parse_nested(tb, VETH_INFO_MAX, data, veth_policy)) < 0)
+ goto errout;
+
+ if (tb[VETH_INFO_PEER]) {
+ struct nlattr *nla_peer;
+ struct ifinfomsg *ifi;
+
+ nla_peer = tb[VETH_INFO_PEER];
+ ifi = nla_data(nla_peer);
+
+ peer->l_family = ifi->ifi_family;
+ peer->l_arptype = ifi->ifi_type;
+ peer->l_index = ifi->ifi_index;
+ peer->l_flags = ifi->ifi_flags;
+ peer->l_change = ifi->ifi_change;
+ err = nla_parse(peer_tb, IFLA_MAX,
+ nla_data(nla_peer) + sizeof(struct ifinfomsg),
+ nla_len(nla_peer) - sizeof(struct ifinfomsg),
+ rtln_link_policy);
+ if (err < 0)
+ goto errout;
+
+ err = rtnl_link_info_parse(peer, peer_tb);
+ if (err < 0)
+ goto errout;
+ }
+
+ err = 0;
+
+errout:
+ return err;
+}
+
+static void veth_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
+{
+}
+
+static void veth_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ struct rtnl_link *peer = link->l_info;
+ char *name;
+ name = rtnl_link_get_name(peer);
+ nl_dump(p, " peer ");
+ if (name)
+ nl_dump_line(p, "%s\n", name);
+ else
+ nl_dump_line(p, "%u\n", peer->l_index);
+}
+
+static int veth_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+ struct rtnl_link *dst_peer = NULL, *src_peer = src->l_info;
+
+ /* we are calling nl_object_clone() recursively, this should
+ * happen only once */
+ if (src_peer) {
+ src_peer->l_info = NULL;
+ dst_peer = (struct rtnl_link *)nl_object_clone(OBJ_CAST(src_peer));
+ if (!dst_peer)
+ return -NLE_NOMEM;
+ src_peer->l_info = src;
+ dst_peer->l_info = dst;
+ }
+ dst->l_info = dst_peer;
+ return 0;
+}
+
+static int veth_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+ struct rtnl_link *peer = link->l_info;
+ struct ifinfomsg ifi;
+ struct nlattr *data, *info_peer;
+
+ memset(&ifi, 0, sizeof ifi);
+ ifi.ifi_family = peer->l_family;
+ ifi.ifi_type = peer->l_arptype;
+ ifi.ifi_index = peer->l_index;
+ ifi.ifi_flags = peer->l_flags;
+ ifi.ifi_change = peer->l_change;
+
+ if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+ return -NLE_MSGSIZE;
+ if (!(info_peer = nla_nest_start(msg, VETH_INFO_PEER)))
+ return -NLE_MSGSIZE;
+ if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
+ return -NLE_MSGSIZE;
+ rtnl_link_fill_info(msg, peer);
+ nla_nest_end(msg, info_peer);
+ nla_nest_end(msg, data);
+
+ return 0;
+}
+
+static int veth_alloc(struct rtnl_link *link)
+{
+ struct rtnl_link *peer;
+ int err;
+
+ /* return early if we are in recursion */
+ if (link->l_info)
+ return 0;
+
+ if (!(peer = rtnl_link_alloc()))
+ return -NLE_NOMEM;
+
+ /* We don't need to hold a reference here, as link and
+ * its peer should always be freed together.
+ */
+ peer->l_info = link;
+ if ((err = rtnl_link_set_type(peer, "veth")) < 0) {
+ rtnl_link_put(peer);
+ return err;
+ }
+
+ link->l_info = peer;
+ return 0;
+}
+
+static void veth_free(struct rtnl_link *link)
+{
+ struct rtnl_link *peer = link->l_info;
+ if (peer) {
+ link->l_info = NULL;
+ /* avoid calling this recursively */
+ peer->l_info = NULL;
+ rtnl_link_put(peer);
+ }
+ /* the caller should finally free link */
+}
+
+static struct rtnl_link_info_ops veth_info_ops = {
+ .io_name = "veth",
+ .io_parse = veth_parse,
+ .io_dump = {
+ [NL_DUMP_LINE] = veth_dump_line,
+ [NL_DUMP_DETAILS] = veth_dump_details,
+ },
+ .io_alloc = veth_alloc,
+ .io_clone = veth_clone,
+ .io_put_attrs = veth_put_attrs,
+ .io_free = veth_free,
+};
+
+/** @cond SKIP */
+
+#define IS_VETH_LINK_ASSERT(link) \
+ if ((link)->l_info_ops != &veth_info_ops) { \
+ APPBUG("Link is not a veth link. set type \"veth\" first."); \
+ return NULL; \
+ }
+/** @endcond */
+
+/**
+ * @name VETH Object
+ * @{
+ */
+
+/**
+ * Allocate link object of type veth
+ *
+ * @return Allocated link object or NULL.
+ */
+struct rtnl_link *rtnl_link_veth_alloc(void)
+{
+ struct rtnl_link *link;
+ int err;
+
+ if (!(link = rtnl_link_alloc()))
+ return NULL;
+ if ((err = rtnl_link_set_type(link, "veth")) < 0) {
+ rtnl_link_put(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+/**
+ * Get the peer link of a veth link
+ *
+ * @return the peer link object.
+ */
+struct rtnl_link *rtnl_link_veth_get_peer(struct rtnl_link *link)
+{
+ IS_VETH_LINK_ASSERT(link);
+ nl_object_get(OBJ_CAST(link->l_info));
+ return link->l_info;
+}
+
+/**
+ * Release a veth link and its peer
+ *
+ */
+void rtnl_link_veth_release(struct rtnl_link *link)
+{
+ veth_free(link);
+ rtnl_link_put(link);
+}
+
+/**
+ * Check if link is a veth link
+ * @arg link Link object
+ *
+ * @return True if link is a veth link, otherwise false is returned.
+ */
+int rtnl_link_is_veth(struct rtnl_link *link)
+{
+ return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "veth");
+}
+
+/**
+ * Create a new kernel veth device
+ * @arg sock netlink socket
+ * @arg name name of the veth device or NULL
+ * @arg peer_name name of its peer or NULL
+ * @arg pid pid of the process in the new netns
+ *
+ * Creates a new veth device pair in the kernel and move the peer
+ * to the network namespace where the process is. If no name is
+ * provided, the kernel will automatically pick a name of the
+ * form "veth%d" (e.g. veth0, veth1, etc.)
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_veth_add(struct nl_sock *sock, const char *name,
+ const char *peer_name, pid_t pid)
+{
+ struct rtnl_link *link, *peer;
+ int err = -NLE_NOMEM;
+
+ if (!(link = rtnl_link_veth_alloc()))
+ return -NLE_NOMEM;
+ peer = link->l_info;
+
+ if (name && peer_name) {
+ rtnl_link_set_name(link, name);
+ rtnl_link_set_name(peer, peer_name);
+ }
+
+ rtnl_link_set_ns_pid(peer, pid);
+ err = rtnl_link_add(sock, link, NLM_F_CREATE | NLM_F_EXCL);
+
+ rtnl_link_put(link);
+ return err;
+}
+
+/** @} */
+
+static void __init veth_init(void)
+{
+ rtnl_link_register_info(&veth_info_ops);
+}
+
+static void __exit veth_exit(void)
+{
+ rtnl_link_unregister_info(&veth_info_ops);
+}
+
+/** @} */
diff --git a/lib/route/link/vlan.c b/lib/route/link/vlan.c
index c466afe6..b9f0c660 100644
--- a/lib/route/link/vlan.c
+++ b/lib/route/link/vlan.c
@@ -6,24 +6,29 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2013 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup link_info
+ * @ingroup link
* @defgroup vlan VLAN
- * @brief
+ * Virtual LAN link module
+ *
+ * @details
+ * \b Link Type Name: "vlan"
+ *
+ * @route_doc{link_vlan, VLAN Documentation}
*
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/attr.h>
#include <netlink/utils.h>
#include <netlink/object.h>
#include <netlink/route/rtnl.h>
-#include <netlink/route/link/info-api.h>
+#include <netlink-private/route/link/api.h>
#include <netlink/route/link/vlan.h>
#include <linux/if_vlan.h>
@@ -33,10 +38,12 @@
#define VLAN_HAS_FLAGS (1<<1)
#define VLAN_HAS_INGRESS_QOS (1<<2)
#define VLAN_HAS_EGRESS_QOS (1<<3)
+#define VLAN_HAS_PROTOCOL (1<<4)
struct vlan_info
{
uint16_t vi_vlan_id;
+ uint16_t vi_protocol;
uint32_t vi_flags;
uint32_t vi_flags_mask;
uint32_t vi_ingress_qos[VLAN_PRIO_MAX+1];
@@ -45,27 +52,15 @@ struct vlan_info
struct vlan_map * vi_egress_qos;
uint32_t vi_mask;
};
-/** @endcond */
-
-static struct trans_tbl vlan_flags[] = {
- __ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr)
-};
-
-char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len)
-{
- return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags));
-}
-int rtnl_link_vlan_str2flags(const char *name)
-{
- return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags));
-}
+/** @endcond */
static struct nla_policy vlan_policy[IFLA_VLAN_MAX+1] = {
[IFLA_VLAN_ID] = { .type = NLA_U16 },
[IFLA_VLAN_FLAGS] = { .minlen = sizeof(struct ifla_vlan_flags) },
[IFLA_VLAN_INGRESS_QOS] = { .type = NLA_NESTED },
[IFLA_VLAN_EGRESS_QOS] = { .type = NLA_NESTED },
+ [IFLA_VLAN_PROTOCOL] = { .type = NLA_U16 },
};
static int vlan_alloc(struct rtnl_link *link)
@@ -102,6 +97,11 @@ static int vlan_parse(struct rtnl_link *link, struct nlattr *data,
vi->vi_mask |= VLAN_HAS_ID;
}
+ if (tb[IFLA_VLAN_PROTOCOL]) {
+ vi->vi_protocol = nla_get_u16(tb[IFLA_VLAN_PROTOCOL]);
+ vi->vi_mask |= VLAN_HAS_PROTOCOL;
+ }
+
if (tb[IFLA_VLAN_FLAGS]) {
struct ifla_vlan_flags flags;
nla_memcpy(&flags, tb[IFLA_VLAN_FLAGS], sizeof(flags));
@@ -122,7 +122,7 @@ static int vlan_parse(struct rtnl_link *link, struct nlattr *data,
return -NLE_INVAL;
map = nla_data(nla);
- if (map->from < 0 || map->from > VLAN_PRIO_MAX) {
+ if (map->from > VLAN_PRIO_MAX) {
return -NLE_INVAL;
}
@@ -145,7 +145,7 @@ static int vlan_parse(struct rtnl_link *link, struct nlattr *data,
/* align to have a little reserve */
vi->vi_egress_size = (i + 32) & ~31;
- vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*map));
+ vi->vi_egress_qos = calloc(vi->vi_egress_size, sizeof(*vi->vi_egress_qos));
if (vi->vi_egress_qos == NULL)
return -NLE_NOMEM;
@@ -189,11 +189,17 @@ static void vlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
static void vlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
{
struct vlan_info *vi = link->l_info;
- int i, printed;
+ int printed;
+ uint32_t i;
char buf[64];
rtnl_link_vlan_flags2str(vi->vi_flags, buf, sizeof(buf));
- nl_dump_line(p, " vlan-info id %d <%s>\n", vi->vi_vlan_id, buf);
+ nl_dump_line(p, " vlan-info id %d <%s>", vi->vi_vlan_id, buf);
+
+ if (vi->vi_mask & VLAN_HAS_PROTOCOL)
+ nl_dump_line(p, " vlan protocol <%d>", vi->vi_protocol);
+
+ nl_dump(p, "\n");
if (vi->vi_mask & VLAN_HAS_INGRESS_QOS) {
nl_dump_line(p,
@@ -241,7 +247,7 @@ static int vlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
int err;
dst->l_info = NULL;
- if ((err = rtnl_link_set_info_type(dst, "vlan")) < 0)
+ if ((err = rtnl_link_set_type(dst, "vlan")) < 0)
return err;
vdst = dst->l_info;
@@ -299,7 +305,7 @@ static int vlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
if (vi->vi_mask & VLAN_HAS_EGRESS_QOS) {
struct ifla_vlan_qos_mapping map;
struct nlattr *qos;
- int i;
+ uint32_t i;
if (!(qos = nla_nest_start(msg, IFLA_VLAN_EGRESS_QOS)))
goto nla_put_failure;
@@ -334,12 +340,63 @@ static struct rtnl_link_info_ops vlan_info_ops = {
.io_free = vlan_free,
};
-int rtnl_link_vlan_set_id(struct rtnl_link *link, int id)
+/** @cond SKIP */
+#define IS_VLAN_LINK_ASSERT(link) \
+ if ((link)->l_info_ops != &vlan_info_ops) { \
+ APPBUG("Link is not a vlan link. set type \"vlan\" first."); \
+ return -NLE_OPNOTSUPP; \
+ }
+/** @endcond */
+
+/**
+ * @name VLAN Object
+ * @{
+ */
+
+/**
+ * Allocate link object of type VLAN
+ *
+ * @return Allocated link object or NULL.
+ */
+struct rtnl_link *rtnl_link_vlan_alloc(void)
+{
+ struct rtnl_link *link;
+ int err;
+
+ if (!(link = rtnl_link_alloc()))
+ return NULL;
+
+ if ((err = rtnl_link_set_type(link, "vlan")) < 0) {
+ rtnl_link_put(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+/**
+ * Check if link is a VLAN link
+ * @arg link Link object
+ *
+ * @return True if link is a VLAN link, otherwise false is returned.
+ */
+int rtnl_link_is_vlan(struct rtnl_link *link)
+{
+ return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vlan");
+}
+
+/**
+ * Set VLAN ID
+ * @arg link Link object
+ * @arg id VLAN identifier
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vlan_set_id(struct rtnl_link *link, uint16_t id)
{
struct vlan_info *vi = link->l_info;
- if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
- return -NLE_OPNOTSUPP;
+ IS_VLAN_LINK_ASSERT(link);
vi->vi_vlan_id = id;
vi->vi_mask |= VLAN_HAS_ID;
@@ -347,12 +404,17 @@ int rtnl_link_vlan_set_id(struct rtnl_link *link, int id)
return 0;
}
+/**
+ * Get VLAN Id
+ * @arg link Link object
+ *
+ * @return VLAN id, 0 if not set or a negative error code.
+ */
int rtnl_link_vlan_get_id(struct rtnl_link *link)
{
struct vlan_info *vi = link->l_info;
- if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
- return -NLE_OPNOTSUPP;
+ IS_VLAN_LINK_ASSERT(link);
if (vi->vi_mask & VLAN_HAS_ID)
return vi->vi_vlan_id;
@@ -360,12 +422,55 @@ int rtnl_link_vlan_get_id(struct rtnl_link *link)
return 0;
}
+/**
+ * Set VLAN protocol
+ * @arg link Link object
+ * @arg protocol VLAN protocol
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vlan_set_protocol(struct rtnl_link *link, uint16_t protocol)
+{
+ struct vlan_info *vi = link->l_info;
+
+ IS_VLAN_LINK_ASSERT(link);
+
+ vi->vi_protocol = protocol;
+ vi->vi_mask |= VLAN_HAS_PROTOCOL;
+
+ return 0;
+}
+
+/**
+ * Get VLAN protocol
+ * @arg link Link object
+ *
+ * @return VLAN protocol, 0 if not set or a negative error code.
+ */
+int rtnl_link_vlan_get_protocol(struct rtnl_link *link)
+{
+ struct vlan_info *vi = link->l_info;
+
+ IS_VLAN_LINK_ASSERT(link);
+
+ if (vi->vi_mask & VLAN_HAS_PROTOCOL)
+ return vi->vi_protocol;
+ else
+ return 0;
+}
+
+/**
+ * Set VLAN flags
+ * @arg link Link object
+ * @arg flags VLAN flags
+ *
+ * @return 0 on success or a negative error code.
+ */
int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags)
{
struct vlan_info *vi = link->l_info;
- if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
- return -NLE_OPNOTSUPP;
+ IS_VLAN_LINK_ASSERT(link);
vi->vi_flags_mask |= flags;
vi->vi_flags |= flags;
@@ -374,12 +479,18 @@ int rtnl_link_vlan_set_flags(struct rtnl_link *link, unsigned int flags)
return 0;
}
+/**
+ * Unset VLAN flags
+ * @arg link Link object
+ * @arg flags VLAN flags
+ *
+ * @return 0 on success or a negative error code.
+ */
int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags)
{
struct vlan_info *vi = link->l_info;
- if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
- return -NLE_OPNOTSUPP;
+ IS_VLAN_LINK_ASSERT(link);
vi->vi_flags_mask |= flags;
vi->vi_flags &= ~flags;
@@ -388,23 +499,34 @@ int rtnl_link_vlan_unset_flags(struct rtnl_link *link, unsigned int flags)
return 0;
}
-unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *link)
+/**
+ * Get VLAN flags
+ * @arg link Link object
+ *
+ * @return VLAN flags, 0 if none set, or a negative error code.
+ */
+int rtnl_link_vlan_get_flags(struct rtnl_link *link)
{
struct vlan_info *vi = link->l_info;
- if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
- return -NLE_OPNOTSUPP;
+ IS_VLAN_LINK_ASSERT(link);
return vi->vi_flags;
}
+/** @} */
+
+/**
+ * @name Quality of Service
+ * @{
+ */
+
int rtnl_link_vlan_set_ingress_map(struct rtnl_link *link, int from,
uint32_t to)
{
struct vlan_info *vi = link->l_info;
- if (link->l_info_ops != &vlan_info_ops || !link->l_info_ops)
- return -NLE_OPNOTSUPP;
+ IS_VLAN_LINK_ASSERT(link);
if (from < 0 || from > VLAN_PRIO_MAX)
return -NLE_INVAL;
@@ -478,6 +600,30 @@ struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *link,
}
}
+/** @} */
+
+static const struct trans_tbl vlan_flags[] = {
+ __ADD(VLAN_FLAG_REORDER_HDR, reorder_hdr)
+};
+
+/**
+ * @name Flag Translation
+ * @{
+ */
+
+char *rtnl_link_vlan_flags2str(int flags, char *buf, size_t len)
+{
+ return __flags2str(flags, buf, len, vlan_flags, ARRAY_SIZE(vlan_flags));
+}
+
+int rtnl_link_vlan_str2flags(const char *name)
+{
+ return __str2flags(name, vlan_flags, ARRAY_SIZE(vlan_flags));
+}
+
+/** @} */
+
+
static void __init vlan_init(void)
{
rtnl_link_register_info(&vlan_info_ops);
diff --git a/lib/route/link/vxlan.c b/lib/route/link/vxlan.c
new file mode 100644
index 00000000..f3e3538d
--- /dev/null
+++ b/lib/route/link/vxlan.c
@@ -0,0 +1,1157 @@
+/*
+ * lib/route/link/vxlan.c VXLAN Link Info
+ *
+ * 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) 2013 Yasunobu Chiba <yasu@dsl.gr.jp>
+ */
+
+/**
+ * @ingroup link
+ * @defgroup vxlan VXLAN
+ * Virtual eXtensible Local Area Network link module
+ *
+ * @details
+ * \b Link Type Name: "vxlan"
+ *
+ * @route_doc{link_vxlan, VXLAN Documentation}
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/utils.h>
+#include <netlink/object.h>
+#include <netlink/route/rtnl.h>
+#include <netlink-private/route/link/api.h>
+#include <netlink/route/link/vxlan.h>
+
+#include <linux/if_link.h>
+
+/** @cond SKIP */
+#define VXLAN_HAS_ID (1<<0)
+#define VXLAN_HAS_GROUP (1<<1)
+#define VXLAN_HAS_LINK (1<<2)
+#define VXLAN_HAS_LOCAL (1<<3)
+#define VXLAN_HAS_TTL (1<<4)
+#define VXLAN_HAS_TOS (1<<5)
+#define VXLAN_HAS_LEARNING (1<<6)
+#define VXLAN_HAS_AGEING (1<<7)
+#define VXLAN_HAS_LIMIT (1<<8)
+#define VXLAN_HAS_PORT_RANGE (1<<9)
+#define VXLAN_HAS_PROXY (1<<10)
+#define VXLAN_HAS_RSC (1<<11)
+#define VXLAN_HAS_L2MISS (1<<12)
+#define VXLAN_HAS_L3MISS (1<<13)
+
+struct vxlan_info
+{
+ uint32_t vxi_id;
+ uint32_t vxi_group;
+ uint32_t vxi_link;
+ uint32_t vxi_local;
+ uint8_t vxi_ttl;
+ uint8_t vxi_tos;
+ uint8_t vxi_learning;
+ uint32_t vxi_ageing;
+ uint32_t vxi_limit;
+ struct ifla_vxlan_port_range vxi_port_range;
+ uint8_t vxi_proxy;
+ uint8_t vxi_rsc;
+ uint8_t vxi_l2miss;
+ uint8_t vxi_l3miss;
+ uint32_t vxi_mask;
+};
+
+/** @endcond */
+
+static struct nla_policy vxlan_policy[IFLA_VXLAN_MAX+1] = {
+ [IFLA_VXLAN_ID] = { .type = NLA_U32 },
+ [IFLA_VXLAN_GROUP] = { .minlen = sizeof(uint32_t) },
+ [IFLA_VXLAN_LINK] = { .type = NLA_U32 },
+ [IFLA_VXLAN_LOCAL] = { .minlen = sizeof(uint32_t) },
+ [IFLA_VXLAN_TTL] = { .type = NLA_U8 },
+ [IFLA_VXLAN_TOS] = { .type = NLA_U8 },
+ [IFLA_VXLAN_LEARNING] = { .type = NLA_U8 },
+ [IFLA_VXLAN_AGEING] = { .type = NLA_U32 },
+ [IFLA_VXLAN_LIMIT] = { .type = NLA_U32 },
+ [IFLA_VXLAN_PORT_RANGE] = { .minlen = sizeof(struct ifla_vxlan_port_range) },
+ [IFLA_VXLAN_PROXY] = { .type = NLA_U8 },
+ [IFLA_VXLAN_RSC] = { .type = NLA_U8 },
+ [IFLA_VXLAN_L2MISS] = { .type = NLA_U8 },
+ [IFLA_VXLAN_L3MISS] = { .type = NLA_U8 },
+};
+
+static int vxlan_alloc(struct rtnl_link *link)
+{
+ struct vxlan_info *vxi;
+
+ if ((vxi = calloc(1, sizeof(*vxi))) == NULL)
+ return -NLE_NOMEM;
+
+ link->l_info = vxi;
+
+ return 0;
+}
+
+static int vxlan_parse(struct rtnl_link *link, struct nlattr *data,
+ struct nlattr *xstats)
+{
+ struct nlattr *tb[IFLA_VXLAN_MAX+1];
+ struct vxlan_info *vxi;
+ int err;
+
+ NL_DBG(3, "Parsing VXLAN link info");
+
+ if ((err = nla_parse_nested(tb, IFLA_VXLAN_MAX, data, vxlan_policy)) < 0)
+ goto errout;
+
+ if ((err = vxlan_alloc(link)) < 0)
+ goto errout;
+
+ vxi = link->l_info;
+
+ if (tb[IFLA_VXLAN_ID]) {
+ vxi->vxi_id = nla_get_u32(tb[IFLA_VXLAN_ID]);
+ vxi->vxi_mask |= VXLAN_HAS_ID;
+ }
+
+ if (tb[IFLA_VXLAN_GROUP]) {
+ nla_memcpy(&vxi->vxi_group, tb[IFLA_VXLAN_GROUP],
+ sizeof(vxi->vxi_group));
+ vxi->vxi_mask |= VXLAN_HAS_GROUP;
+ }
+
+ if (tb[IFLA_VXLAN_LINK]) {
+ vxi->vxi_link = nla_get_u32(tb[IFLA_VXLAN_LINK]);
+ vxi->vxi_mask |= VXLAN_HAS_LINK;
+ }
+
+ if (tb[IFLA_VXLAN_LOCAL]) {
+ nla_memcpy(&vxi->vxi_local, tb[IFLA_VXLAN_LOCAL],
+ sizeof(vxi->vxi_local));
+ vxi->vxi_mask |= VXLAN_HAS_LOCAL;
+ }
+
+ if (tb[IFLA_VXLAN_TTL]) {
+ vxi->vxi_ttl = nla_get_u8(tb[IFLA_VXLAN_TTL]);
+ vxi->vxi_mask |= VXLAN_HAS_TTL;
+ }
+
+ if (tb[IFLA_VXLAN_TOS]) {
+ vxi->vxi_tos = nla_get_u8(tb[IFLA_VXLAN_TOS]);
+ vxi->vxi_mask |= VXLAN_HAS_TOS;
+ }
+
+ if (tb[IFLA_VXLAN_LEARNING]) {
+ vxi->vxi_learning = nla_get_u8(tb[IFLA_VXLAN_LEARNING]);
+ vxi->vxi_mask |= VXLAN_HAS_LEARNING;
+ }
+
+ if (tb[IFLA_VXLAN_AGEING]) {
+ vxi->vxi_ageing = nla_get_u32(tb[IFLA_VXLAN_AGEING]);
+ vxi->vxi_mask |= VXLAN_HAS_AGEING;
+ }
+
+ if (tb[IFLA_VXLAN_LIMIT]) {
+ vxi->vxi_limit = nla_get_u32(tb[IFLA_VXLAN_LIMIT]);
+ vxi->vxi_mask |= VXLAN_HAS_LIMIT;
+ }
+
+ if (tb[IFLA_VXLAN_PORT_RANGE]) {
+ nla_memcpy(&vxi->vxi_port_range, tb[IFLA_VXLAN_PORT_RANGE],
+ sizeof(vxi->vxi_port_range));
+ vxi->vxi_mask |= VXLAN_HAS_PORT_RANGE;
+ }
+
+ if (tb[IFLA_VXLAN_PROXY]) {
+ vxi->vxi_proxy = nla_get_u8(tb[IFLA_VXLAN_PROXY]);
+ vxi->vxi_mask |= VXLAN_HAS_PROXY;
+ }
+
+ if (tb[IFLA_VXLAN_RSC]) {
+ vxi->vxi_rsc = nla_get_u8(tb[IFLA_VXLAN_RSC]);
+ vxi->vxi_mask |= VXLAN_HAS_RSC;
+ }
+
+ if (tb[IFLA_VXLAN_L2MISS]) {
+ vxi->vxi_l2miss = nla_get_u8(tb[IFLA_VXLAN_L2MISS]);
+ vxi->vxi_mask |= VXLAN_HAS_L2MISS;
+ }
+
+ if (tb[IFLA_VXLAN_L3MISS]) {
+ vxi->vxi_l3miss = nla_get_u8(tb[IFLA_VXLAN_L3MISS]);
+ vxi->vxi_mask |= VXLAN_HAS_L3MISS;
+ }
+
+ err = 0;
+
+errout:
+ return err;
+}
+
+static void vxlan_free(struct rtnl_link *link)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ free(vxi);
+ link->l_info = NULL;
+}
+
+static void vxlan_dump_line(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ nl_dump(p, "vxlan-id %u", vxi->vxi_id);
+}
+
+static void vxlan_dump_details(struct rtnl_link *link, struct nl_dump_params *p)
+{
+ struct vxlan_info *vxi = link->l_info;
+ char *name, addr[INET_ADDRSTRLEN];
+
+ nl_dump_line(p, " vxlan-id %u\n", vxi->vxi_id);
+
+ if (vxi->vxi_mask & VXLAN_HAS_GROUP) {
+ nl_dump(p, " group ");
+ if(inet_ntop(AF_INET, &vxi->vxi_group, addr, sizeof(addr)))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", ntohs(vxi->vxi_group));
+ }
+
+ if (vxi->vxi_mask & VXLAN_HAS_LINK) {
+ nl_dump(p, " link ");
+ name = rtnl_link_get_name(link);
+ if (name)
+ nl_dump_line(p, "%s\n", name);
+ else
+ nl_dump_line(p, "%u\n", vxi->vxi_link);
+ }
+
+ if (vxi->vxi_mask & VXLAN_HAS_LOCAL) {
+ nl_dump(p, " local ");
+ if(inet_ntop(AF_INET, &vxi->vxi_local, addr, sizeof(addr)))
+ nl_dump_line(p, "%s\n", addr);
+ else
+ nl_dump_line(p, "%#x\n", ntohs(vxi->vxi_local));
+ }
+
+ if (vxi->vxi_mask & VXLAN_HAS_TTL) {
+ nl_dump(p, " ttl ");
+ if(vxi->vxi_ttl)
+ nl_dump_line(p, "%u\n", vxi->vxi_ttl);
+ else
+ nl_dump_line(p, "inherit\n");
+ }
+
+ if (vxi->vxi_mask & VXLAN_HAS_TOS) {
+ nl_dump(p, " tos ");
+ if (vxi->vxi_tos == 1)
+ nl_dump_line(p, "inherit\n", vxi->vxi_tos);
+ else
+ nl_dump_line(p, "%#x\n", vxi->vxi_tos);
+ }
+
+ if (vxi->vxi_mask & VXLAN_HAS_LEARNING) {
+ nl_dump(p, " learning ");
+ if (vxi->vxi_learning)
+ nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_learning);
+ else
+ nl_dump_line(p, "disabled\n");
+ }
+
+ if (vxi->vxi_mask & VXLAN_HAS_AGEING) {
+ nl_dump(p, " ageing ");
+ if (vxi->vxi_ageing)
+ nl_dump_line(p, "%u seconds\n", vxi->vxi_ageing);
+ else
+ nl_dump_line(p, "disabled\n");
+ }
+
+ if (vxi->vxi_mask & VXLAN_HAS_LIMIT) {
+ nl_dump(p, " limit ");
+ if (vxi->vxi_limit)
+ nl_dump_line(p, "%u\n", vxi->vxi_limit);
+ else
+ nl_dump_line(p, "unlimited\n");
+ }
+
+ if (vxi->vxi_mask & VXLAN_HAS_PORT_RANGE)
+ nl_dump_line(p, " port range %u - %u\n",
+ ntohs(vxi->vxi_port_range.low),
+ ntohs(vxi->vxi_port_range.high));
+
+ if (vxi->vxi_mask & VXLAN_HAS_PROXY) {
+ nl_dump(p, " proxy ");
+ if (vxi->vxi_proxy)
+ nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_proxy);
+ else
+ nl_dump_line(p, "disabled\n");
+ }
+
+ if (vxi->vxi_mask & VXLAN_HAS_RSC) {
+ nl_dump(p, " rsc ");
+ if (vxi->vxi_rsc)
+ nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_rsc);
+ else
+ nl_dump_line(p, "disabled\n");
+ }
+
+ if (vxi->vxi_mask & VXLAN_HAS_L2MISS) {
+ nl_dump(p, " l2miss ");
+ if (vxi->vxi_l2miss)
+ nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_l2miss);
+ else
+ nl_dump_line(p, "disabled\n");
+ }
+
+ if (vxi->vxi_mask & VXLAN_HAS_L3MISS) {
+ nl_dump(p, " l3miss ");
+ if (vxi->vxi_l3miss)
+ nl_dump_line(p, "enabled (%#x)\n", vxi->vxi_l3miss);
+ else
+ nl_dump_line(p, "disabled\n");
+ }
+}
+
+static int vxlan_clone(struct rtnl_link *dst, struct rtnl_link *src)
+{
+ struct vxlan_info *vdst, *vsrc = src->l_info;
+ int err;
+
+ dst->l_info = NULL;
+ if ((err = rtnl_link_set_type(dst, "vxlan")) < 0)
+ return err;
+ vdst = dst->l_info;
+
+ if (!vdst || !vsrc)
+ return -NLE_NOMEM;
+
+ memcpy(vdst, vsrc, sizeof(struct vxlan_info));
+
+ return 0;
+}
+
+static int vxlan_put_attrs(struct nl_msg *msg, struct rtnl_link *link)
+{
+ struct vxlan_info *vxi = link->l_info;
+ struct nlattr *data;
+
+ if (!(data = nla_nest_start(msg, IFLA_INFO_DATA)))
+ return -NLE_MSGSIZE;
+
+ if (vxi->vxi_mask & VXLAN_HAS_ID)
+ NLA_PUT_U32(msg, IFLA_VXLAN_ID, vxi->vxi_id);
+
+ if (vxi->vxi_mask & VXLAN_HAS_GROUP)
+ NLA_PUT(msg, IFLA_VXLAN_GROUP, sizeof(vxi->vxi_group), &vxi->vxi_group);
+
+ if (vxi->vxi_mask & VXLAN_HAS_LINK)
+ NLA_PUT_U32(msg, IFLA_VXLAN_LINK, vxi->vxi_link);
+
+ if (vxi->vxi_mask & VXLAN_HAS_LOCAL)
+ NLA_PUT(msg, IFLA_VXLAN_LOCAL, sizeof(vxi->vxi_local), &vxi->vxi_local);
+
+ if (vxi->vxi_mask & VXLAN_HAS_TTL)
+ NLA_PUT_U8(msg, IFLA_VXLAN_TTL, vxi->vxi_ttl);
+
+ if (vxi->vxi_mask & VXLAN_HAS_TOS)
+ NLA_PUT_U8(msg, IFLA_VXLAN_TOS, vxi->vxi_tos);
+
+ if (vxi->vxi_mask & VXLAN_HAS_LEARNING)
+ NLA_PUT_U8(msg, IFLA_VXLAN_LEARNING, vxi->vxi_learning);
+
+ if (vxi->vxi_mask & VXLAN_HAS_AGEING)
+ NLA_PUT_U32(msg, IFLA_VXLAN_AGEING, vxi->vxi_ageing);
+
+ if (vxi->vxi_mask & VXLAN_HAS_LIMIT)
+ NLA_PUT_U32(msg, IFLA_VXLAN_LIMIT, vxi->vxi_limit);
+
+ if (vxi->vxi_mask & VXLAN_HAS_PORT_RANGE)
+ NLA_PUT(msg, IFLA_VXLAN_PORT_RANGE, sizeof(vxi->vxi_port_range),
+ &vxi->vxi_port_range);
+
+ if (vxi->vxi_mask & VXLAN_HAS_PROXY)
+ NLA_PUT_U8(msg, IFLA_VXLAN_PROXY, vxi->vxi_proxy);
+
+ if (vxi->vxi_mask & VXLAN_HAS_RSC)
+ NLA_PUT_U8(msg, IFLA_VXLAN_RSC, vxi->vxi_rsc);
+
+ if (vxi->vxi_mask & VXLAN_HAS_L2MISS)
+ NLA_PUT_U8(msg, IFLA_VXLAN_L2MISS, vxi->vxi_l2miss);
+
+ if (vxi->vxi_mask & VXLAN_HAS_L3MISS)
+ NLA_PUT_U8(msg, IFLA_VXLAN_L3MISS, vxi->vxi_l3miss);
+
+ nla_nest_end(msg, data);
+
+nla_put_failure:
+
+ return 0;
+}
+
+static struct rtnl_link_info_ops vxlan_info_ops = {
+ .io_name = "vxlan",
+ .io_alloc = vxlan_alloc,
+ .io_parse = vxlan_parse,
+ .io_dump = {
+ [NL_DUMP_LINE] = vxlan_dump_line,
+ [NL_DUMP_DETAILS] = vxlan_dump_details,
+ },
+ .io_clone = vxlan_clone,
+ .io_put_attrs = vxlan_put_attrs,
+ .io_free = vxlan_free,
+};
+
+/** @cond SKIP */
+#define IS_VXLAN_LINK_ASSERT(link) \
+ if ((link)->l_info_ops != &vxlan_info_ops) { \
+ APPBUG("Link is not a vxlan link. set type \"vxlan\" first."); \
+ return -NLE_OPNOTSUPP; \
+ }
+/** @endcond */
+
+/**
+ * @name VXLAN Object
+ * @{
+ */
+
+/**
+ * Allocate link object of type VXLAN
+ *
+ * @return Allocated link object or NULL.
+ */
+struct rtnl_link *rtnl_link_vxlan_alloc(void)
+{
+ struct rtnl_link *link;
+ int err;
+
+ if (!(link = rtnl_link_alloc()))
+ return NULL;
+
+ if ((err = rtnl_link_set_type(link, "vxlan")) < 0) {
+ rtnl_link_put(link);
+ return NULL;
+ }
+
+ return link;
+}
+
+/**
+ * Check if link is a VXLAN link
+ * @arg link Link object
+ *
+ * @return True if link is a VXLAN link, otherwise false is returned.
+ */
+int rtnl_link_is_vxlan(struct rtnl_link *link)
+{
+ return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "vxlan");
+}
+
+/**
+ * Set VXLAN Network Identifier
+ * @arg link Link object
+ * @arg id VXLAN network identifier (or VXLAN segment identifier)
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_id(struct rtnl_link *link, uint32_t id)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (id > VXLAN_ID_MAX)
+ return -NLE_INVAL;
+
+ vxi->vxi_id = id;
+ vxi->vxi_mask |= VXLAN_HAS_ID;
+
+ return 0;
+}
+
+/**
+ * Get VXLAN Network Identifier
+ * @arg link Link object
+ * @arg id Pointer to store network identifier
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_get_id(struct rtnl_link *link, uint32_t *id)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if(!id)
+ return -NLE_INVAL;
+
+ if (vxi->vxi_mask & VXLAN_HAS_ID)
+ *id = vxi->vxi_id;
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Set VXLAN multicast IP address
+ * @arg link Link object
+ * @arg addr Multicast IP address to join
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_group(struct rtnl_link *link, struct nl_addr *addr)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if ((nl_addr_get_family(addr) != AF_INET) ||
+ (nl_addr_get_len(addr) != sizeof(vxi->vxi_group)))
+ return -NLE_INVAL;
+
+ memcpy(&vxi->vxi_group, nl_addr_get_binary_addr(addr),
+ sizeof(vxi->vxi_group));
+ vxi->vxi_mask |= VXLAN_HAS_GROUP;
+
+ return 0;
+}
+
+/**
+ * Get VXLAN multicast IP address
+ * @arg link Link object
+ * @arg addr Pointer to store multicast IP address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_get_group(struct rtnl_link *link, struct nl_addr **addr)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!addr)
+ return -NLE_INVAL;
+
+ if (!(vxi->vxi_mask & VXLAN_HAS_GROUP))
+ return -NLE_AGAIN;
+
+ *addr = nl_addr_build(AF_INET, &vxi->vxi_group, sizeof(vxi->vxi_group));
+
+ return 0;
+}
+
+/**
+ * Set physical device to use for VXLAN
+ * @arg link Link object
+ * @arg index Interface index
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_link(struct rtnl_link *link, uint32_t index)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ vxi->vxi_link = index;
+ vxi->vxi_mask |= VXLAN_HAS_LINK;
+
+ return 0;
+}
+
+/**
+ * Get physical device to use for VXLAN
+ * @arg link Link object
+ * @arg index Pointer to store interface index
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_get_link(struct rtnl_link *link, uint32_t *index)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!index)
+ return -NLE_INVAL;
+
+ if (!(vxi->vxi_mask & VXLAN_HAS_LINK))
+ return -NLE_AGAIN;
+
+ *index = vxi->vxi_link;
+
+ return 0;
+}
+
+/**
+ * Set source address to use for VXLAN
+ * @arg link Link object
+ * @arg addr Local address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_local(struct rtnl_link *link, struct nl_addr *addr)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if ((nl_addr_get_family(addr) != AF_INET) ||
+ (nl_addr_get_len(addr) != sizeof(vxi->vxi_local)))
+ return -NLE_INVAL;
+
+ memcpy(&vxi->vxi_local, nl_addr_get_binary_addr(addr),
+ sizeof(vxi->vxi_local));
+ vxi->vxi_mask |= VXLAN_HAS_LOCAL;
+
+ return 0;
+}
+
+/**
+ * Get source address to use for VXLAN
+ * @arg link Link object
+ * @arg addr Pointer to store local address
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_get_local(struct rtnl_link *link, struct nl_addr **addr)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!addr)
+ return -NLE_INVAL;
+
+ if (!(vxi->vxi_mask & VXLAN_HAS_LOCAL))
+ return -NLE_AGAIN;
+
+ *addr = nl_addr_build(AF_INET, &vxi->vxi_local, sizeof(vxi->vxi_local));
+
+ return 0;
+}
+
+/**
+ * Set IP TTL value to use for VXLAN
+ * @arg link Link object
+ * @arg ttl TTL value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_ttl(struct rtnl_link *link, uint8_t ttl)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ vxi->vxi_ttl = ttl;
+ vxi->vxi_mask |= VXLAN_HAS_TTL;
+
+ return 0;
+}
+
+/**
+ * Get IP TTL value to use for VXLAN
+ * @arg link Link object
+ *
+ * @return TTL value on success or a negative error code
+ */
+int rtnl_link_vxlan_get_ttl(struct rtnl_link *link)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!(vxi->vxi_mask & VXLAN_HAS_TTL))
+ return -NLE_AGAIN;
+
+ return vxi->vxi_ttl;
+}
+
+/**
+ * Set IP ToS value to use for VXLAN
+ * @arg link Link object
+ * @arg tos ToS value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_tos(struct rtnl_link *link, uint8_t tos)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ vxi->vxi_tos = tos;
+ vxi->vxi_mask |= VXLAN_HAS_TOS;
+
+ return 0;
+}
+
+/**
+ * Get IP ToS value to use for VXLAN
+ * @arg link Link object
+ *
+ * @return ToS value on success or a negative error code
+ */
+int rtnl_link_vxlan_get_tos(struct rtnl_link *link)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!(vxi->vxi_mask & VXLAN_HAS_TOS))
+ return -NLE_AGAIN;
+
+ return vxi->vxi_tos;
+}
+
+/**
+ * Set VXLAN learning status
+ * @arg link Link object
+ * @arg learning Learning status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_learning(struct rtnl_link *link, uint8_t learning)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ vxi->vxi_learning = learning;
+ vxi->vxi_mask |= VXLAN_HAS_LEARNING;
+
+ return 0;
+}
+
+/**
+ * Get VXLAN learning status
+ * @arg link Link object
+ *
+ * @return Learning status value on success or a negative error code
+ */
+int rtnl_link_vxlan_get_learning(struct rtnl_link *link)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!(vxi->vxi_mask & VXLAN_HAS_LEARNING))
+ return -NLE_AGAIN;
+
+ return vxi->vxi_learning;
+}
+
+/**
+ * Enable VXLAN address learning
+ * @arg link Link object
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_enable_learning(struct rtnl_link *link)
+{
+ return rtnl_link_vxlan_set_learning(link, 1);
+}
+
+/**
+ * Disable VXLAN address learning
+ * @arg link Link object
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_disable_learning(struct rtnl_link *link)
+{
+ return rtnl_link_vxlan_set_learning(link, 0);
+}
+
+/**
+ * Set expiration timer value to use for VXLAN
+ * @arg link Link object
+ * @arg expiry Expiration timer value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_ageing(struct rtnl_link *link, uint32_t expiry)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ vxi->vxi_ageing = expiry;
+ vxi->vxi_mask |= VXLAN_HAS_AGEING;
+
+ return 0;
+}
+
+/**
+ * Get expiration timer value to use for VXLAN
+ * @arg link Link object
+ * @arg expiry Pointer to store expiration timer value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_get_ageing(struct rtnl_link *link, uint32_t *expiry)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!expiry)
+ return -NLE_INVAL;
+
+ if (vxi->vxi_mask & VXLAN_HAS_AGEING)
+ *expiry = vxi->vxi_ageing;
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Set maximum number of forwarding database entries to use for VXLAN
+ * @arg link Link object
+ * @arg limit Maximum number
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_limit(struct rtnl_link *link, uint32_t limit)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ vxi->vxi_limit = limit;
+ vxi->vxi_mask |= VXLAN_HAS_LIMIT;
+
+ return 0;
+}
+
+/**
+ * Get maximum number of forwarding database entries to use for VXLAN
+ * @arg link Link object
+ * @arg limit Pointer to store maximum number
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_get_limit(struct rtnl_link *link, uint32_t *limit)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!limit)
+ return -NLE_INVAL;
+
+ if (vxi->vxi_mask & VXLAN_HAS_LIMIT)
+ *limit = vxi->vxi_limit;
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Set range of UDP port numbers to use for VXLAN
+ * @arg link Link object
+ * @arg range Port number range
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_port_range(struct rtnl_link *link,
+ struct ifla_vxlan_port_range *range)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!range)
+ return -NLE_INVAL;
+
+ memcpy(&vxi->vxi_port_range, range, sizeof(vxi->vxi_port_range));
+ vxi->vxi_mask |= VXLAN_HAS_PORT_RANGE;
+
+ return 0;
+}
+
+/**
+ * Get range of UDP port numbers to use for VXLAN
+ * @arg link Link object
+ * @arg range Pointer to store port range
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_get_port_range(struct rtnl_link *link,
+ struct ifla_vxlan_port_range *range)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!range)
+ return -NLE_INVAL;
+
+ if (vxi->vxi_mask & VXLAN_HAS_PORT_RANGE)
+ memcpy(range, &vxi->vxi_port_range, sizeof(*range));
+ else
+ return -NLE_AGAIN;
+
+ return 0;
+}
+
+/**
+ * Set ARP proxy status to use for VXLAN
+ * @arg link Link object
+ * @arg proxy Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_proxy(struct rtnl_link *link, uint8_t proxy)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ vxi->vxi_proxy = proxy;
+ vxi->vxi_mask |= VXLAN_HAS_PROXY;
+
+ return 0;
+}
+
+/**
+ * Get ARP proxy status to use for VXLAN
+ * @arg link Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_vxlan_get_proxy(struct rtnl_link *link)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!(vxi->vxi_mask & VXLAN_HAS_PROXY))
+ return -NLE_AGAIN;
+
+ return vxi->vxi_proxy;
+}
+
+/**
+ * Enable ARP proxy
+ * @arg link Link object
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_enable_proxy(struct rtnl_link *link)
+{
+ return rtnl_link_vxlan_set_proxy(link, 1);
+}
+
+/**
+ * Disable ARP proxy
+ * @arg link Link object
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_disable_proxy(struct rtnl_link *link)
+{
+ return rtnl_link_vxlan_set_proxy(link, 0);
+}
+
+/**
+ * Set Route Short Circuit status to use for VXLAN
+ * @arg link Link object
+ * @arg rsc Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_rsc(struct rtnl_link *link, uint8_t rsc)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ vxi->vxi_rsc = rsc;
+ vxi->vxi_mask |= VXLAN_HAS_RSC;
+
+ return 0;
+}
+
+/**
+ * Get Route Short Circuit status to use for VXLAN
+ * @arg link Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_vxlan_get_rsc(struct rtnl_link *link)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!(vxi->vxi_mask & VXLAN_HAS_RSC))
+ return -NLE_AGAIN;
+
+ return vxi->vxi_rsc;
+}
+
+/**
+ * Enable Route Short Circuit
+ * @arg link Link object
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_enable_rsc(struct rtnl_link *link)
+{
+ return rtnl_link_vxlan_set_rsc(link, 1);
+}
+
+/**
+ * Disable Route Short Circuit
+ * @arg link Link object
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_disable_rsc(struct rtnl_link *link)
+{
+ return rtnl_link_vxlan_set_rsc(link, 0);
+}
+
+/**
+ * Set netlink LLADDR miss notification status to use for VXLAN
+ * @arg link Link object
+ * @arg miss Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_l2miss(struct rtnl_link *link, uint8_t miss)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ vxi->vxi_l2miss = miss;
+ vxi->vxi_mask |= VXLAN_HAS_L2MISS;
+
+ return 0;
+}
+
+/**
+ * Get netlink LLADDR miss notification status to use for VXLAN
+ * @arg link Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_vxlan_get_l2miss(struct rtnl_link *link)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!(vxi->vxi_mask & VXLAN_HAS_L2MISS))
+ return -NLE_AGAIN;
+
+ return vxi->vxi_l2miss;
+}
+
+/**
+ * Enable netlink LLADDR miss notifications
+ * @arg link Link object
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_enable_l2miss(struct rtnl_link *link)
+{
+ return rtnl_link_vxlan_set_l2miss(link, 1);
+}
+
+/**
+ * Disable netlink LLADDR miss notifications
+ * @arg link Link object
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_disable_l2miss(struct rtnl_link *link)
+{
+ return rtnl_link_vxlan_set_l2miss(link, 0);
+}
+
+/**
+ * Set netlink IP ADDR miss notification status to use for VXLAN
+ * @arg link Link object
+ * @arg miss Status value
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_set_l3miss(struct rtnl_link *link, uint8_t miss)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ vxi->vxi_l3miss = miss;
+ vxi->vxi_mask |= VXLAN_HAS_L3MISS;
+
+ return 0;
+}
+
+/**
+ * Get netlink IP ADDR miss notification status to use for VXLAN
+ * @arg link Link object
+ *
+ * @return Status value on success or a negative error code
+ */
+int rtnl_link_vxlan_get_l3miss(struct rtnl_link *link)
+{
+ struct vxlan_info *vxi = link->l_info;
+
+ IS_VXLAN_LINK_ASSERT(link);
+
+ if (!(vxi->vxi_mask & VXLAN_HAS_L3MISS))
+ return -NLE_AGAIN;
+
+ return vxi->vxi_l3miss;
+}
+
+/**
+ * Enable netlink IP DDR miss notifications
+ * @arg link Link object
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_enable_l3miss(struct rtnl_link *link)
+{
+ return rtnl_link_vxlan_set_l3miss(link, 1);
+}
+
+/**
+ * Disable netlink IP ADDR miss notifications
+ * @arg link Link object
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_link_vxlan_disable_l3miss(struct rtnl_link *link)
+{
+ return rtnl_link_vxlan_set_l3miss(link, 0);
+}
+
+/** @} */
+
+static void __init vxlan_init(void)
+{
+ rtnl_link_register_info(&vxlan_info_ops);
+}
+
+static void __exit vxlan_exit(void)
+{
+ rtnl_link_unregister_info(&vxlan_info_ops);
+}
+
+/** @} */
diff --git a/lib/route/neigh.c b/lib/route/neigh.c
index d4dc82c1..ad26b4d0 100644
--- a/lib/route/neigh.c
+++ b/lib/route/neigh.c
@@ -32,6 +32,7 @@
*
* @par Neighbour Flags
* @code
+ * NTF_USE
* NTF_PROXY
* NTF_ROUTER
* @endcode
@@ -147,12 +148,14 @@
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
+#include <netlink/hashtable.h>
#include <netlink/route/rtnl.h>
#include <netlink/route/neighbour.h>
#include <netlink/route/link.h>
+#include <netlink/hashtable.h>
/** @cond SKIP */
#define NEIGH_ATTR_FLAGS 0x01
@@ -164,6 +167,7 @@
#define NEIGH_ATTR_FAMILY 0x40
#define NEIGH_ATTR_TYPE 0x80
#define NEIGH_ATTR_PROBES 0x100
+#define NEIGH_ATTR_MASTER 0x200
static struct nl_cache_ops rtnl_neigh_ops;
static struct nl_object_ops neigh_obj_ops;
@@ -196,6 +200,59 @@ static int neigh_clone(struct nl_object *_dst, struct nl_object *_src)
return 0;
}
+static void neigh_keygen(struct nl_object *obj, uint32_t *hashkey,
+ uint32_t table_sz)
+{
+ struct rtnl_neigh *neigh = (struct rtnl_neigh *) obj;
+ unsigned int nkey_sz;
+ struct nl_addr *addr = NULL;
+ struct neigh_hash_key {
+ uint32_t n_family;
+ uint32_t n_ifindex;
+ char n_addr[0];
+ } __attribute__((packed)) *nkey;
+#ifdef NL_DEBUG
+ char buf[INET6_ADDRSTRLEN+5];
+#endif
+
+ if (neigh->n_family == AF_BRIDGE) {
+ if (neigh->n_lladdr)
+ addr = neigh->n_lladdr;
+ } else if (neigh->n_dst) {
+ addr = neigh->n_dst;
+ }
+
+ nkey_sz = sizeof(*nkey);
+ if (addr)
+ nkey_sz += nl_addr_get_len(addr);
+
+ nkey = calloc(1, nkey_sz);
+ if (!nkey) {
+ *hashkey = 0;
+ return;
+ }
+ nkey->n_family = neigh->n_family;
+ if (neigh->n_family == AF_BRIDGE)
+ nkey->n_ifindex = neigh->n_master;
+ else
+ nkey->n_ifindex = neigh->n_ifindex;
+ if (addr)
+ memcpy(nkey->n_addr,
+ nl_addr_get_binary_addr(addr),
+ nl_addr_get_len(addr));
+
+ *hashkey = nl_hash(nkey, nkey_sz, 0) % table_sz;
+
+ NL_DBG(5, "neigh %p key (fam %d dev %d addr %s) keysz %d hash 0x%x\n",
+ neigh, nkey->n_family, nkey->n_ifindex,
+ nl_addr2str(addr, buf, sizeof(buf)),
+ nkey_sz, *hashkey);
+
+ free(nkey);
+
+ return;
+}
+
static int neigh_compare(struct nl_object *_a, struct nl_object *_b,
uint32_t attrs, int flags)
{
@@ -210,6 +267,7 @@ static int neigh_compare(struct nl_object *_a, struct nl_object *_b,
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));
+ diff |= NEIGH_DIFF(MASTER, a->n_master != b->n_master);
if (flags & LOOSE_COMPARISON) {
diff |= NEIGH_DIFF(STATE,
@@ -226,7 +284,7 @@ static int neigh_compare(struct nl_object *_a, struct nl_object *_b,
return diff;
}
-static struct trans_tbl neigh_attrs[] = {
+static const struct trans_tbl neigh_attrs[] = {
__ADD(NEIGH_ATTR_FLAGS, flags)
__ADD(NEIGH_ATTR_STATE, state)
__ADD(NEIGH_ATTR_LLADDR, lladdr)
@@ -244,6 +302,16 @@ static char *neigh_attrs2str(int attrs, char *buf, size_t len)
ARRAY_SIZE(neigh_attrs));
}
+static uint32_t neigh_id_attrs_get(struct nl_object *obj)
+{
+ struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj;
+
+ if (neigh->n_family == AF_BRIDGE)
+ return (NEIGH_ATTR_LLADDR | NEIGH_ATTR_FAMILY | NEIGH_ATTR_MASTER);
+ else
+ return (NEIGH_ATTR_IFINDEX | NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY);
+}
+
static struct nla_policy neigh_policy[NDA_MAX+1] = {
[NDA_CACHEINFO] = { .minlen = sizeof(struct nda_cacheinfo) },
[NDA_PROBES] = { .type = NLA_U32 },
@@ -253,6 +321,21 @@ static int neigh_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
struct nlmsghdr *n, struct nl_parser_param *pp)
{
struct rtnl_neigh *neigh;
+ int err;
+
+ if ((err = rtnl_neigh_parse(n, &neigh)) < 0)
+ return err;
+
+ err = pp->pp_cb((struct nl_object *) neigh, pp);
+
+ rtnl_neigh_put(neigh);
+ return err;
+}
+
+
+int rtnl_neigh_parse(struct nlmsghdr *n, struct rtnl_neigh **result)
+{
+ struct rtnl_neigh *neigh;
struct nlattr *tb[NDA_MAX + 1];
struct ndmsg *nm;
int err;
@@ -316,7 +399,27 @@ static int neigh_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
neigh->ce_mask |= NEIGH_ATTR_PROBES;
}
- err = pp->pp_cb((struct nl_object *) neigh, pp);
+ /*
+ * Get the bridge index for AF_BRIDGE family entries
+ */
+ if (neigh->n_family == AF_BRIDGE) {
+ struct nl_cache *lcache = nl_cache_mngt_require_safe("route/link");
+ if (lcache ) {
+ struct rtnl_link *link = rtnl_link_get(lcache,
+ neigh->n_ifindex);
+ if (link) {
+ neigh->n_master = link->l_master;
+ rtnl_link_put(link);
+ neigh->ce_mask |= NEIGH_ATTR_MASTER;
+ }
+
+ nl_cache_put(lcache);
+ }
+ }
+
+ *result = neigh;
+ return 0;
+
errout:
rtnl_neigh_put(neigh);
return err;
@@ -324,7 +427,9 @@ errout:
static int neigh_request_update(struct nl_cache *c, struct nl_sock *h)
{
- return nl_rtgen_request(h, RTM_GETNEIGH, AF_UNSPEC, NLM_F_DUMP);
+ int family = c->c_iarg1;
+
+ return nl_rtgen_request(h, RTM_GETNEIGH, family, NLM_F_DUMP);
}
@@ -335,9 +440,10 @@ static void neigh_dump_line(struct nl_object *a, struct nl_dump_params *p)
struct nl_cache *link_cache;
char state[128], flags[64];
- link_cache = nl_cache_mngt_require("route/link");
+ link_cache = nl_cache_mngt_require_safe("route/link");
- nl_dump_line(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst)));
+ if (n->n_family != AF_BRIDGE)
+ nl_dump_line(p, "%s ", nl_addr2str(n->n_dst, dst, sizeof(dst)));
if (link_cache)
nl_dump(p, "dev %s ",
@@ -360,13 +466,16 @@ static void neigh_dump_line(struct nl_object *a, struct nl_dump_params *p)
if (state[0] || flags[0])
nl_dump(p, ">");
nl_dump(p, "\n");
+
+ if (link_cache)
+ nl_cache_put(link_cache);
}
static void neigh_dump_details(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 hz = nl_get_user_hz();
neigh_dump_line(a, p);
@@ -383,51 +492,6 @@ static void neigh_dump_stats(struct nl_object *a, struct nl_dump_params *p)
neigh_dump_details(a, p);
}
-static void neigh_dump_env(struct nl_object *obj, struct nl_dump_params *p)
-{
- struct rtnl_neigh *neigh = (struct rtnl_neigh *) obj;
- char buf[128];
-
- nl_dump_line(p, "NEIGH_FAMILY=%s\n",
- nl_af2str(neigh->n_family, buf, sizeof(buf)));
-
- if (neigh->ce_mask & NEIGH_ATTR_LLADDR)
- nl_dump_line(p, "NEIGHT_LLADDR=%s\n",
- nl_addr2str(neigh->n_lladdr, buf, sizeof(buf)));
-
- if (neigh->ce_mask & NEIGH_ATTR_DST)
- nl_dump_line(p, "NEIGH_DST=%s\n",
- nl_addr2str(neigh->n_dst, buf, sizeof(buf)));
-
- if (neigh->ce_mask & NEIGH_ATTR_IFINDEX) {
- struct nl_cache *link_cache;
-
- nl_dump_line(p, "NEIGH_IFINDEX=%u\n", neigh->n_ifindex);
-
- link_cache = nl_cache_mngt_require("route/link");
- if (link_cache)
- nl_dump_line(p, "NEIGH_IFNAME=%s\n",
- rtnl_link_i2name(link_cache,
- neigh->n_ifindex,
- buf, sizeof(buf)));
- }
-
- if (neigh->ce_mask & NEIGH_ATTR_PROBES)
- nl_dump_line(p, "NEIGH_PROBES=%u\n", neigh->n_probes);
-
- if (neigh->ce_mask & NEIGH_ATTR_TYPE)
- nl_dump_line(p, "NEIGH_TYPE=%s\n",
- nl_rtntype2str(neigh->n_type, buf, sizeof(buf)));
-
- rtnl_neigh_flags2str(neigh->n_flags, buf, sizeof(buf));
- if (buf[0])
- nl_dump_line(p, "NEIGH_FLAGS=%s\n", buf);
-
- rtnl_neigh_state2str(neigh->n_state, buf, sizeof(buf));
- if (buf[0])
- nl_dump_line(p, "NEIGH_STATE=%s\n", buf);
-}
-
/**
* @name Neighbour Object Allocation/Freeage
* @{
@@ -452,7 +516,7 @@ void rtnl_neigh_put(struct rtnl_neigh *neigh)
/**
* Build a neighbour cache including all neighbours currently configured in the kernel.
- * @arg sk Netlink socket.
+ * @arg sock Netlink socket.
* @arg result Pointer to store resulting cache.
*
* Allocates a new neighbour cache, initializes it properly and updates it
@@ -470,6 +534,7 @@ int rtnl_neigh_alloc_cache(struct nl_sock *sock, struct nl_cache **result)
* @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,
@@ -504,10 +569,16 @@ static int build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, int flags,
.ndm_state = NUD_PERMANENT,
};
- if (!(tmpl->ce_mask & NEIGH_ATTR_DST))
- return -NLE_MISSING_ATTR;
+ if (tmpl->n_family != AF_BRIDGE) {
+ if (!(tmpl->ce_mask & NEIGH_ATTR_DST))
+ return -NLE_MISSING_ATTR;
+ nhdr.ndm_family = nl_addr_get_family(tmpl->n_dst);
+ }
+ else
+ nhdr.ndm_family = AF_BRIDGE;
- nhdr.ndm_family = nl_addr_get_family(tmpl->n_dst);
+ if (tmpl->ce_mask & NEIGH_ATTR_FLAGS)
+ nhdr.ndm_flags = tmpl->n_flags;
if (tmpl->ce_mask & NEIGH_ATTR_STATE)
nhdr.ndm_state = tmpl->n_state;
@@ -519,7 +590,8 @@ static int build_neigh_msg(struct rtnl_neigh *tmpl, int cmd, int flags,
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->n_family != AF_BRIDGE)
+ 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);
@@ -655,7 +727,7 @@ int rtnl_neigh_delete(struct nl_sock *sk, struct rtnl_neigh *neigh,
* @{
*/
-static struct trans_tbl neigh_states[] = {
+static const struct trans_tbl neigh_states[] = {
__ADD(NUD_INCOMPLETE, incomplete)
__ADD(NUD_REACHABLE, reachable)
__ADD(NUD_STALE, stale)
@@ -684,7 +756,8 @@ int rtnl_neigh_str2state(const char *name)
* @{
*/
-static struct trans_tbl neigh_flags[] = {
+static const struct trans_tbl neigh_flags[] = {
+ __ADD(NTF_USE, use)
__ADD(NTF_PROXY, proxy)
__ADD(NTF_ROUTER, router)
};
@@ -846,15 +919,17 @@ static struct nl_object_ops neigh_obj_ops = {
[NL_DUMP_LINE] = neigh_dump_line,
[NL_DUMP_DETAILS] = neigh_dump_details,
[NL_DUMP_STATS] = neigh_dump_stats,
- [NL_DUMP_ENV] = neigh_dump_env,
},
.oo_compare = neigh_compare,
+ .oo_keygen = neigh_keygen,
.oo_attrs2str = neigh_attrs2str,
.oo_id_attrs = (NEIGH_ATTR_IFINDEX | NEIGH_ATTR_DST | NEIGH_ATTR_FAMILY),
+ .oo_id_attrs_get = neigh_id_attrs_get
};
static struct nl_af_group neigh_groups[] = {
{ AF_UNSPEC, RTNLGRP_NEIGH },
+ { AF_BRIDGE, RTNLGRP_NEIGH },
{ END_OF_GROUP_LIST },
};
diff --git a/lib/route/neightbl.c b/lib/route/neightbl.c
index 9599faae..f9c9c277 100644
--- a/lib/route/neightbl.c
+++ b/lib/route/neightbl.c
@@ -16,7 +16,7 @@
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/rtnl.h>
@@ -143,7 +143,7 @@ static int neightbl_msg_parser(struct nl_cache_ops *ops,
ntbl->nt_family = rtmsg->rtgen_family;
if (tb[NDTA_NAME] == NULL) {
- return -NLE_MISSING_ATTR;
+ err = -NLE_MISSING_ATTR;
goto errout;
}
@@ -237,7 +237,7 @@ static void neightbl_dump_line(struct nl_object *arg, struct nl_dump_params *p)
if (ntbl->nt_parms.ntp_mask & NEIGHTBLPARM_ATTR_IFINDEX) {
struct nl_cache *link_cache;
- link_cache = nl_cache_mngt_require("route/link");
+ link_cache = nl_cache_mngt_require_safe("route/link");
if (link_cache) {
char buf[32];
@@ -245,6 +245,7 @@ static void neightbl_dump_line(struct nl_object *arg, struct nl_dump_params *p)
rtnl_link_i2name(link_cache,
ntbl->nt_parms.ntp_ifindex,
buf, sizeof(buf)));
+ nl_cache_put(link_cache);
} else
nl_dump(p, "<%u> ", ntbl->nt_parms.ntp_ifindex);
} else
@@ -332,21 +333,32 @@ static void neightbl_dump_stats(struct nl_object *arg, struct nl_dump_params *p)
if (!(ntbl->ce_mask & NEIGHTBL_ATTR_STATS))
return;
- nl_dump_line(p, " lookups %lld hits %lld failed %lld " \
- "allocations %lld destroys %lld\n",
+ nl_dump_line(p, " " \
+ " lookups %" PRIu64 \
+ " hits %" PRIu64 \
+ " failed %" PRIu64 \
+ " allocations %" PRIu64 \
+ " destroys %" PRIu64 \
+ "\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);
- nl_dump_line(p, " hash-grows %lld forced-gc-runs %lld " \
- "periodic-gc-runs %lld\n",
+ nl_dump_line(p, " " \
+ " hash-grows %" PRIu64 \
+ " forced-gc-runs %" PRIu64 \
+ " periodic-gc-runs %" PRIu64 \
+ "\n",
ntbl->nt_stats.ndts_hash_grows,
ntbl->nt_stats.ndts_forced_gc_runs,
ntbl->nt_stats.ndts_periodic_gc_runs);
- nl_dump_line(p, " rcv-unicast-probes %lld rcv-multicast-probes %lld\n",
+ nl_dump_line(p, " " \
+ " rcv-unicast-probes %" PRIu64 \
+ " rcv-multicast-probes %" PRIu64 \
+ "\n",
ntbl->nt_stats.ndts_rcv_probes_ucast,
ntbl->nt_stats.ndts_rcv_probes_mcast);
}
diff --git a/lib/route/nexthop.c b/lib/route/nexthop.c
index 788e255c..d3ca4991 100644
--- a/lib/route/nexthop.c
+++ b/lib/route/nexthop.c
@@ -15,7 +15,7 @@
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/rtnl.h>
@@ -109,7 +109,7 @@ static void nh_dump_line(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
struct nl_cache *link_cache;
char buf[128];
- link_cache = nl_cache_mngt_require("route/link");
+ link_cache = nl_cache_mngt_require_safe("route/link");
nl_dump(dp, "via");
@@ -128,6 +128,9 @@ static void nh_dump_line(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
}
nl_dump(dp, " ");
+
+ if (link_cache)
+ nl_cache_put(link_cache);
}
static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
@@ -135,7 +138,7 @@ static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
struct nl_cache *link_cache;
char buf[128];
- link_cache = nl_cache_mngt_require("route/link");
+ link_cache = nl_cache_mngt_require_safe("route/link");
nl_dump(dp, "nexthop");
@@ -164,44 +167,11 @@ static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
if (nh->ce_mask & NH_ATTR_FLAGS)
nl_dump(dp, " <%s>", rtnl_route_nh_flags2str(nh->rtnh_flags,
buf, sizeof(buf)));
-}
-
-static void nh_dump_env(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
-{
- struct nl_cache *link_cache;
- char buf[128];
-
- link_cache = nl_cache_mngt_require("route/link");
- if (nh->ce_mask & NH_ATTR_GATEWAY)
- nl_dump_line(dp, "ROUTE_NH%d_VIA=%s\n", dp->dp_ivar,
- nl_addr2str(nh->rtnh_gateway, buf, sizeof(buf)));
-
- if(nh->ce_mask & NH_ATTR_IFINDEX) {
- if (link_cache) {
- nl_dump_line(dp, "ROUTE_NH%d_DEV=%s\n", dp->dp_ivar,
- rtnl_link_i2name(link_cache,
- nh->rtnh_ifindex,
- buf, sizeof(buf)));
- } else
- nl_dump_line(dp, "ROUTE_NH%d_DEV=%d\n", dp->dp_ivar,
- nh->rtnh_ifindex);
- }
-
- if (nh->ce_mask & NH_ATTR_WEIGHT)
- nl_dump_line(dp, "ROUTE_NH%d_WEIGHT=%u\n", dp->dp_ivar,
- nh->rtnh_weight);
-
- if (nh->ce_mask & NH_ATTR_REALMS)
- nl_dump_line(dp, "ROUTE_NH%d_REALM=%04x:%04x\n", dp->dp_ivar,
- RTNL_REALM_FROM(nh->rtnh_realms),
- RTNL_REALM_TO(nh->rtnh_realms));
-
- if (nh->ce_mask & NH_ATTR_FLAGS)
- nl_dump_line(dp, "ROUTE_NH%d_FLAGS=<%s>\n", dp->dp_ivar,
- rtnl_route_nh_flags2str(nh->rtnh_flags,
- buf, sizeof(buf)));
+ if (link_cache)
+ nl_cache_put(link_cache);
}
+
void rtnl_route_nh_dump(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
{
switch (dp->dp_type) {
@@ -215,10 +185,6 @@ void rtnl_route_nh_dump(struct rtnl_nexthop *nh, struct nl_dump_params *dp)
nh_dump_details(nh, dp);
break;
- case NL_DUMP_ENV:
- nh_dump_env(nh, dp);
- break;
-
default:
break;
}
@@ -251,6 +217,7 @@ int rtnl_route_nh_get_ifindex(struct rtnl_nexthop *nh)
return nh->rtnh_ifindex;
}
+/* FIXME: Convert to return an int */
void rtnl_route_nh_set_gateway(struct rtnl_nexthop *nh, struct nl_addr *addr)
{
struct nl_addr *old = nh->rtnh_gateway;
@@ -309,7 +276,7 @@ uint32_t rtnl_route_nh_get_realms(struct rtnl_nexthop *nh)
* @{
*/
-static struct trans_tbl nh_flags[] = {
+static const struct trans_tbl nh_flags[] = {
__ADD(RTNH_F_DEAD, dead)
__ADD(RTNH_F_PERVASIVE, pervasive)
__ADD(RTNH_F_ONLINK, onlink)
diff --git a/lib/route/pktloc.c b/lib/route/pktloc.c
index f0d0155c..27d63be2 100644
--- a/lib/route/pktloc.c
+++ b/lib/route/pktloc.c
@@ -2,10 +2,11 @@
* lib/route/pktloc.c Packet Location Aliasing
*
* This library 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 version 2 of the License.
+ * 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) 2008-2010 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2013 Thomas Graf <tgraf@suug.ch>
*/
/**
@@ -20,7 +21,7 @@
* library and provides a well defined set of definitions for most common
* protocol fields.
*
- * @subsection pktloc_examples Examples
+ * @section pktloc_examples Examples
* @par Example 1.1 Looking up a packet location
* @code
* struct rtnl_pktloc *loc;
@@ -30,8 +31,8 @@
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/pktloc.h>
@@ -39,13 +40,13 @@
#include "pktloc_syntax.h"
#include "pktloc_grammar.h"
-/** @cond */
+/** @cond SKIP */
#define PKTLOC_NAME_HT_SIZ 256
static struct nl_list_head pktloc_name_ht[PKTLOC_NAME_HT_SIZ];
/* djb2 */
-unsigned int pktloc_hash(const char *str)
+static unsigned int pktloc_hash(const char *str)
{
unsigned long hash = 5381;
int c;
@@ -56,16 +57,25 @@ unsigned int pktloc_hash(const char *str)
return hash % PKTLOC_NAME_HT_SIZ;
}
-
-void rtnl_pktloc_add(struct rtnl_pktloc *loc)
+static int __pktloc_lookup(const char *name, struct rtnl_pktloc **result)
{
- nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]);
+ struct rtnl_pktloc *loc;
+ int hash;
+
+ hash = pktloc_hash(name);
+ nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) {
+ if (!strcasecmp(loc->name, name)) {
+ loc->refcnt++;
+ *result = loc;
+ return 0;
+ }
+ }
+
+ return -NLE_OBJ_NOTFOUND;
}
extern int pktloc_parse(void *scanner);
-/** @endcond */
-
static void rtnl_pktloc_free(struct rtnl_pktloc *loc)
{
if (!loc)
@@ -77,15 +87,16 @@ static void rtnl_pktloc_free(struct rtnl_pktloc *loc)
static int read_pktlocs(void)
{
- YY_BUFFER_STATE buf;
+ YY_BUFFER_STATE buf = NULL;
yyscan_t scanner = NULL;
static time_t last_read;
- struct stat st = {0};
+ struct stat st;
char *path;
int i, err;
FILE *fd;
- asprintf(&path, "%s/pktloc", SYSCONFDIR);
+ if (build_sysconf_path(&path, "pktloc") < 0)
+ return -NLE_NOMEM;
/* if stat fails, just try to read the file */
if (stat(path, &st) == 0) {
@@ -94,39 +105,54 @@ static int read_pktlocs(void)
return 0;
}
- if (!(fd = fopen(path, "r")))
- return -NLE_PKTLOC_FILE;
+ NL_DBG(2, "Reading packet location file \"%s\"\n", path);
+
+ if (!(fd = fopen(path, "r"))) {
+ err = -NLE_PKTLOC_FILE;
+ goto errout;
+ }
for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++) {
struct rtnl_pktloc *loc, *n;
nl_list_for_each_entry_safe(loc, n, &pktloc_name_ht[i], list)
- rtnl_pktloc_free(loc);
+ rtnl_pktloc_put(loc);
nl_init_list_head(&pktloc_name_ht[i]);
}
- if ((err = pktloc_lex_init(&scanner)) < 0)
- return -NLE_FAILURE;
+ if ((err = pktloc_lex_init(&scanner)) < 0) {
+ err = -NLE_FAILURE;
+ goto errout_close;
+ }
buf = pktloc__create_buffer(fd, YY_BUF_SIZE, scanner);
pktloc__switch_to_buffer(buf, scanner);
- if ((err = pktloc_parse(scanner)) < 0)
- return -NLE_FAILURE;
+ if ((err = pktloc_parse(scanner)) != 0) {
+ pktloc__delete_buffer(buf, scanner);
+ err = -NLE_PARSE_ERR;
+ goto errout_scanner;
+ }
- if (scanner)
- pktloc_lex_destroy(scanner);
+ last_read = st.st_mtime;
+errout_scanner:
+ pktloc_lex_destroy(scanner);
+errout_close:
+ fclose(fd);
+errout:
free(path);
- last_read = st.st_mtime;
- return 0;
+ return err;
}
+/** @endcond */
+
/**
* Lookup packet location alias
* @arg name Name of packet location.
+ * @arg result Result pointer
*
* Tries to find a matching packet location alias for the supplied
* packet location name.
@@ -134,27 +160,90 @@ static int read_pktlocs(void)
* The file containing the packet location definitions is automatically
* re-read if its modification time has changed since the last call.
*
+ * The returned packet location has to be returned after use by calling
+ * rtnl_pktloc_put() in order to allow freeing its memory after the last
+ * user has abandoned it.
+ *
* @return 0 on success or a negative error code.
* @retval NLE_PKTLOC_FILE Unable to open packet location file.
* @retval NLE_OBJ_NOTFOUND No matching packet location alias found.
*/
int rtnl_pktloc_lookup(const char *name, struct rtnl_pktloc **result)
{
- struct rtnl_pktloc *loc;
- int hash, err;
+ int err;
if ((err = read_pktlocs()) < 0)
return err;
+
+ return __pktloc_lookup(name, result);
+}
- hash = pktloc_hash(name);
- nl_list_for_each_entry(loc, &pktloc_name_ht[hash], list) {
- if (!strcasecmp(loc->name, name)) {
- *result = loc;
- return 0;
- }
+/**
+ * Allocate packet location object
+ */
+struct rtnl_pktloc *rtnl_pktloc_alloc(void)
+{
+ struct rtnl_pktloc *loc;
+
+ if (!(loc = calloc(1, sizeof(*loc))))
+ return NULL;
+
+ loc->refcnt = 1;
+ nl_init_list_head(&loc->list);
+
+ return loc;
+}
+
+/**
+ * Return reference of a packet location
+ * @arg loc packet location object.
+ */
+void rtnl_pktloc_put(struct rtnl_pktloc *loc)
+{
+ if (!loc)
+ return;
+
+ loc->refcnt--;
+ if (loc->refcnt <= 0)
+ rtnl_pktloc_free(loc);
+}
+
+/**
+ * Add a packet location to the hash table
+ * @arg loc packet location object
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_pktloc_add(struct rtnl_pktloc *loc)
+{
+ struct rtnl_pktloc *l;
+
+ if (__pktloc_lookup(loc->name, &l) == 0) {
+ rtnl_pktloc_put(l);
+ return -NLE_EXIST;
}
- return -NLE_OBJ_NOTFOUND;
+ NL_DBG(2, "New packet location entry \"%s\" align=%u layer=%u "
+ "offset=%u mask=%#x shift=%u refnt=%u\n",
+ loc->name, loc->align, loc->layer, loc->offset,
+ loc->mask, loc->shift, loc->refcnt);
+
+ nl_list_add_tail(&loc->list, &pktloc_name_ht[pktloc_hash(loc->name)]);
+
+ return 0;
+}
+
+void rtnl_pktloc_foreach(void (*cb)(struct rtnl_pktloc *, void *), void *arg)
+{
+ struct rtnl_pktloc *loc;
+ int i;
+
+ /* ignore errors */
+ read_pktlocs();
+
+ for (i = 0; i < PKTLOC_NAME_HT_SIZ; i++)
+ nl_list_for_each_entry(loc, &pktloc_name_ht[i], list)
+ cb(loc, arg);
}
static int __init pktloc_init(void)
@@ -166,3 +255,5 @@ static int __init pktloc_init(void)
return 0;
}
+
+/** @} */
diff --git a/lib/route/pktloc_grammar.l b/lib/route/pktloc_grammar.l
index f7104304..cbb42b38 100644
--- a/lib/route/pktloc_grammar.l
+++ b/lib/route/pktloc_grammar.l
@@ -1,6 +1,6 @@
%{
- #include <netlink-local.h>
- #include <netlink-tc.h>
+ #include <netlink-private/netlink.h>
+ #include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/pktloc.h>
@@ -11,6 +11,7 @@
%option reentrant
%option warn
%option noyywrap
+%option noinput
%option nounput
%option bison-bridge
%option bison-locations
@@ -30,10 +31,18 @@
"+" { return yylval->i = yytext[0]; }
-[lL][iI][nN][kK] { yylval->i = TCF_LAYER_LINK; return LAYER; }
-[nN][eE][tT] { yylval->i = TCF_LAYER_NETWORK; return LAYER; }
+[uU]8 { yylval->i = TCF_EM_ALIGN_U8; return ALIGN; }
+[uU]16 { yylval->i = TCF_EM_ALIGN_U16; return ALIGN; }
+[uU]32 { yylval->i = TCF_EM_ALIGN_U32; return ALIGN; }
+
+[lL][iI][nN][kK] |
+[eE][tT][hH] { yylval->i = TCF_LAYER_LINK; return LAYER; }
+[nN][eE][tT] |
+[iI][pP] { yylval->i = TCF_LAYER_NETWORK; return LAYER; }
+[tT][rR][aA][nN][sS][pP][oO][rR][tT] |
[tT][cC][pP] { yylval->i = TCF_LAYER_TRANSPORT; return LAYER; }
+
[^ \t\r\n+]+ {
yylval->s = strdup(yytext);
if (yylval->s == NULL)
diff --git a/lib/route/pktloc_syntax.y b/lib/route/pktloc_syntax.y
index 05d609a1..25d87109 100644
--- a/lib/route/pktloc_syntax.y
+++ b/lib/route/pktloc_syntax.y
@@ -1,6 +1,6 @@
%{
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/pktloc.h>
@@ -13,6 +13,7 @@
%parse-param {void *scanner}
%lex-param {void *scanner}
+%expect 1
%union {
struct rtnl_pktloc *l;
@@ -22,18 +23,17 @@
%{
extern int pktloc_lex(YYSTYPE *, YYLTYPE *, void *);
-extern void rtnl_pktloc_add(struct rtnl_pktloc *);
static void yyerror(YYLTYPE *locp, void *scanner, const char *msg)
{
- /* FIXME */
+ NL_DBG(1, "Error while parsing packet location file: %s\n", msg);
}
%}
-%token <i> ERROR NUMBER LAYER
+%token <i> ERROR NUMBER LAYER ALIGN
%token <s> NAME
-%type <i> mask layer
+%type <i> mask layer align shift
%type <l> location
%destructor { free($$); } NAME
@@ -43,56 +43,44 @@ static void yyerror(YYLTYPE *locp, void *scanner, const char *msg)
%%
input:
- def
- { }
- ;
-
-def:
/* empty */
- { }
- | location def
- { }
+ | location input
;
location:
- NAME NAME layer NUMBER mask
+ NAME align layer NUMBER mask shift
{
struct rtnl_pktloc *loc;
- if (!(loc = calloc(1, sizeof(*loc)))) {
- /* FIXME */
- }
-
- if (!strcasecmp($2, "u8"))
- loc->align = TCF_EM_ALIGN_U8;
- else if (!strcasecmp($2, "h8")) {
- loc->align = TCF_EM_ALIGN_U8;
- loc->flags = TCF_EM_CMP_TRANS;
- } else if (!strcasecmp($2, "u16"))
- loc->align = TCF_EM_ALIGN_U16;
- else if (!strcasecmp($2, "h16")) {
- loc->align = TCF_EM_ALIGN_U16;
- loc->flags = TCF_EM_CMP_TRANS;
- } else if (!strcasecmp($2, "u32"))
- loc->align = TCF_EM_ALIGN_U32;
- else if (!strcasecmp($2, "h32")) {
- loc->align = TCF_EM_ALIGN_U32;
- loc->flags = TCF_EM_CMP_TRANS;
+ if (!(loc = rtnl_pktloc_alloc())) {
+ NL_DBG(1, "Allocating a packet location "
+ "object failed.\n");
+ YYABORT;
}
-
- free($2);
loc->name = $1;
+ loc->align = $2;
loc->layer = $3;
loc->offset = $4;
loc->mask = $5;
+ loc->shift = $6;
- rtnl_pktloc_add(loc);
+ if (rtnl_pktloc_add(loc) < 0) {
+ NL_DBG(1, "Duplicate packet location entry "
+ "\"%s\"\n", $1);
+ }
$$ = loc;
}
;
+align:
+ ALIGN
+ { $$ = $1; }
+ | NUMBER
+ { $$ = $1; }
+ ;
+
layer:
/* empty */
{ $$ = TCF_LAYER_NETWORK; }
@@ -106,3 +94,10 @@ mask:
| NUMBER
{ $$ = $1; }
;
+
+shift:
+ /* empty */
+ { $$ = 0; }
+ | NUMBER
+ { $$ = $1; }
+ ;
diff --git a/lib/route/qdisc.c b/lib/route/qdisc.c
index cfeaf050..b8b6fa53 100644
--- a/lib/route/qdisc.c
+++ b/lib/route/qdisc.c
@@ -6,125 +6,44 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
/**
* @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 <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/link.h>
-#include <netlink/route/tc.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/qdisc.h>
#include <netlink/route/class.h>
#include <netlink/route/classifier.h>
-#include <netlink/route/qdisc-modules.h>
static struct nl_cache_ops rtnl_qdisc_ops;
+static struct nl_object_ops qdisc_obj_ops;
static int qdisc_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
struct nlmsghdr *n, struct nl_parser_param *pp)
{
- int err;
struct rtnl_qdisc *qdisc;
- struct rtnl_qdisc_ops *qops;
-
- qdisc = rtnl_qdisc_alloc();
- if (!qdisc) {
- err = -NLE_NOMEM;
- goto errout;
- }
-
- qdisc->ce_msgtype = n->nlmsg_type;
+ int err;
- err = tca_msg_parser(n, (struct rtnl_tca *) qdisc);
- if (err < 0)
- goto errout_free;
+ if (!(qdisc = rtnl_qdisc_alloc()))
+ return -NLE_NOMEM;
- qops = rtnl_qdisc_lookup_ops(qdisc);
- if (qops && qops->qo_msg_parser) {
- err = qops->qo_msg_parser(qdisc);
- if (err < 0)
- goto errout_free;
- }
+ if ((err = rtnl_tc_msg_parse(n, TC_CAST(qdisc))) < 0)
+ goto errout;
- err = pp->pp_cb((struct nl_object *) qdisc, pp);
-errout_free:
- rtnl_qdisc_put(qdisc);
+ err = pp->pp_cb(OBJ_CAST(qdisc), pp);
errout:
+ rtnl_qdisc_put(qdisc);
+
return err;
}
@@ -140,87 +59,107 @@ static int qdisc_request_update(struct nl_cache *c, struct nl_sock *sk)
}
/**
- * @name QDisc Addition
+ * @name Allocation/Freeing
* @{
*/
-static int qdisc_build(struct rtnl_qdisc *qdisc, int type, int flags,
- struct nl_msg **result)
+struct rtnl_qdisc *rtnl_qdisc_alloc(void)
{
- struct rtnl_qdisc_ops *qops;
- int err;
+ struct rtnl_tc *tc;
- err = tca_build_msg((struct rtnl_tca *) qdisc, type, flags, result);
- if (err < 0)
- return err;
+ tc = TC_CAST(nl_object_alloc(&qdisc_obj_ops));
+ if (tc)
+ tc->tc_type = RTNL_TC_TYPE_QDISC;
- 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(*result, TCA_OPTIONS, opts);
- nlmsg_free(opts);
- if (err < 0)
- goto errout;
- }
- }
- /* Some qdiscs don't accept properly nested messages (e.g. netem). To
- * accomodate for this, they can complete the message themselves.
- */
- else if (qops && qops->qo_build_msg) {
- err = qops->qo_build_msg(qdisc, *result);
- if (err < 0)
- goto errout;
- }
+ return (struct rtnl_qdisc *) tc;
+}
- return 0;
-errout:
- nlmsg_free(*result);
+void rtnl_qdisc_put(struct rtnl_qdisc *qdisc)
+{
+ nl_object_put((struct nl_object *) qdisc);
+}
- return err;
+/** @} */
+
+/**
+ * @name Addition / Modification / Deletion
+ * @{
+ */
+
+static int build_qdisc_msg(struct rtnl_qdisc *qdisc, int type, int flags,
+ struct nl_msg **result)
+{
+ if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) {
+ APPBUG("ifindex must be specified");
+ return -NLE_MISSING_ATTR;
+ }
+
+ return rtnl_tc_msg_build(TC_CAST(qdisc), type, flags, result);
}
/**
- * Build a netlink message to add a new qdisc
- * @arg qdisc qdisc to add
- * @arg flags additional netlink message flags
- * @arg result Pointer to store resulting message.
+ * Build a netlink message requesting the addition of a qdisc
+ * @arg qdisc Qdisc to add
+ * @arg flags Additional netlink message flags
+ * @arg result Pointer to store resulting netlink message
*
- * 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.
+ * The behaviour of this function is identical to rtnl_qdisc_add() with
+ * the exception that it will not send the message but return it int the
+ * provided return pointer instead.
*
- * Common message flags used:
- * - NLM_F_REPLACE - replace a potential existing qdisc
+ * @see rtnl_qdisc_add()
*
* @return 0 on success or a negative error code.
*/
int rtnl_qdisc_build_add_request(struct rtnl_qdisc *qdisc, int flags,
struct nl_msg **result)
{
- return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_CREATE | flags, result);
+ if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) {
+ APPBUG("handle or parent must be specified");
+ return -NLE_MISSING_ATTR;
+ }
+
+ return build_qdisc_msg(qdisc, RTM_NEWQDISC, flags, result);
}
/**
- * Add a new qdisc
- * @arg sk Netlink socket.
- * @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
+ * Add qdisc
+ * @arg sk Netlink socket
+ * @arg qdisc Qdisc to add
+ * @arg flags Additional netlink message flags
+ *
+ * Builds a \c RTM_NEWQDISC netlink message requesting the addition
+ * of a new qdisc and sends the message to the kernel. The configuration
+ * of the qdisc is derived from the attributes of the specified qdisc.
+ *
+ * The following flags may be specified:
+ * - \c NLM_F_CREATE: Create qdisc if it does not exist, otherwise
+ * -NLE_OBJ_NOTFOUND is returned.
+ * - \c NLM_F_REPLACE: If another qdisc is already attached to the
+ * parent, replace it even if the handles mismatch.
+ * - \c NLM_F_EXCL: Return -NLE_EXISTS if a qdisc with matching
+ * handle exists already.
+ *
+ * Existing qdiscs with matching handles will be updated, unless the
+ * flag \c NLM_F_EXCL is specified. If their handles do not match, the
+ * error -NLE_EXISTS is returned unless the flag \c NLM_F_REPLACE is
+ * specified in which case the existing qdisc is replaced with the new
+ * one. If no matching qdisc exists, it will be created if the flag
+ * \c NLM_F_CREATE is set, otherwise the error -NLE_OBJ_NOTFOUND is
+ * returned.
+ *
+ * After sending, the function will wait for the ACK or an eventual
+ * error message to be received and will therefore block until the
+ * operation has been completed.
+ *
+ * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
+ * this function to return immediately after sending. In this case,
+ * it is the responsibility of the caller to handle any error
+ * messages returned.
*
- * @return 0 on success or a negative error code
+ * @return 0 on success or a negative error code.
*/
-int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
- int flags)
+int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc, int flags)
{
struct nl_msg *msg;
int err;
@@ -228,86 +167,106 @@ int rtnl_qdisc_add(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
if ((err = rtnl_qdisc_build_add_request(qdisc, flags, &msg)) < 0)
return err;
- err = nl_send_auto_complete(sk, msg);
- nlmsg_free(msg);
- if (err < 0)
- return err;
-
- return wait_for_ack(sk);
+ return nl_send_sync(sk, msg);
}
-/** @} */
-
-/**
- * @name QDisc Modification
- * @{
- */
-
/**
- * Build a netlink message to change attributes of a existing qdisc
- * @arg qdisc qdisc to change
- * @arg new new qdisc attributes
- * @arg result Pointer to store resulting message.
+ * Build netlink message requesting the update of a qdisc
+ * @arg qdisc Qdisc to update
+ * @arg new Qdisc with updated attributes
+ * @arg flags Additional netlink message flags
+ * @arg result Pointer to store resulting netlink message
+ *
+ * The behaviour of this function is identical to rtnl_qdisc_update() with
+ * the exception that it will not send the message but return it in the
+ * provided return pointer instead.
*
- * 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.
+ * @see rtnl_qdisc_update()
*
* @return 0 on success or a negative error code.
*/
-int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
- struct rtnl_qdisc *new,
+int rtnl_qdisc_build_update_request(struct rtnl_qdisc *qdisc,
+ struct rtnl_qdisc *new, int flags,
struct nl_msg **result)
{
- return qdisc_build(qdisc, RTM_NEWQDISC, NLM_F_REPLACE, result);
+ if (flags & (NLM_F_CREATE | NLM_F_EXCL)) {
+ APPBUG("NLM_F_CREATE and NLM_F_EXCL may not be used here, "
+ "use rtnl_qdisc_add()");
+ return -NLE_INVAL;
+ }
+
+ if (!(qdisc->ce_mask & TCA_ATTR_IFINDEX)) {
+ APPBUG("ifindex must be specified");
+ return -NLE_MISSING_ATTR;
+ }
+
+ if (!(qdisc->ce_mask & (TCA_ATTR_HANDLE | TCA_ATTR_PARENT))) {
+ APPBUG("handle or parent must be specified");
+ return -NLE_MISSING_ATTR;
+ }
+
+ rtnl_tc_set_ifindex(TC_CAST(new), qdisc->q_ifindex);
+
+ if (qdisc->ce_mask & TCA_ATTR_HANDLE)
+ rtnl_tc_set_handle(TC_CAST(new), qdisc->q_handle);
+
+ if (qdisc->ce_mask & TCA_ATTR_PARENT)
+ rtnl_tc_set_parent(TC_CAST(new), qdisc->q_parent);
+
+ return build_qdisc_msg(new, RTM_NEWQDISC, flags, result);
}
/**
- * Change attributes of a qdisc
- * @arg sk Netlink socket.
- * @arg qdisc qdisc to change
- * @arg new new qdisc attributes
+ * Update qdisc
+ * @arg sk Netlink socket
+ * @arg qdisc Qdisc to update
+ * @arg new Qdisc with updated attributes
+ * @arg flags Additional netlink message flags
+ *
+ * Builds a \c RTM_NEWQDISC netlink message requesting the update
+ * of an existing qdisc and sends the message to the kernel.
+ *
+ * This function is a varation of rtnl_qdisc_add() to update qdiscs
+ * if the qdisc to be updated is available as qdisc object. The
+ * behaviour is identical to the one of rtnl_qdisc_add except that
+ * before constructing the message, it copies the \c ifindex,
+ * \c handle, and \c parent from the original \p qdisc to the \p new
+ * qdisc.
+ *
+ * After sending, the function will wait for the ACK or an eventual
+ * error message to be received and will therefore block until the
+ * operation has been completed.
+ *
+ * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
+ * this function to return immediately after sending. In this case,
+ * it is the responsibility of the caller to handle any error
+ * messages returned.
*
- * 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
+ * @return 0 on success or a negative error code.
*/
-int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
- struct rtnl_qdisc *new)
+int rtnl_qdisc_update(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
+ struct rtnl_qdisc *new, int flags)
{
struct nl_msg *msg;
int err;
- if ((err = rtnl_qdisc_build_change_request(qdisc, new, &msg)) < 0)
- return err;
-
- err = nl_send_auto_complete(sk, msg);
- nlmsg_free(msg);
+ err = rtnl_qdisc_build_update_request(qdisc, new, flags, &msg);
if (err < 0)
return err;
- return wait_for_ack(sk);
+ return nl_send_sync(sk, msg);
}
-/** @} */
-
-/**
- * @name QDisc Deletion
- * @{
- */
-
/**
- * Build a netlink request message to delete a qdisc
- * @arg qdisc qdisc to delete
- * @arg result Pointer to store resulting message.
+ * Build netlink message requesting the deletion of a qdisc
+ * @arg qdisc Qdisc to delete
+ * @arg result Pointer to store resulting netlink message
*
- * 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.
+ * The behaviour of this function is identical to rtnl_qdisc_delete() with
+ * the exception that it will not send the message but return it in the
+ * provided return pointer instead.
+ *
+ * @see rtnl_qdisc_delete()
*
* @return 0 on success or a negative error code.
*/
@@ -316,38 +275,67 @@ int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *qdisc,
{
struct nl_msg *msg;
struct tcmsg tchdr;
- int required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
+ uint32_t required = TCA_ATTR_IFINDEX | TCA_ATTR_PARENT;
- if ((qdisc->ce_mask & required) != required)
- BUG();
+ if ((qdisc->ce_mask & required) != required) {
+ APPBUG("ifindex and parent must be specified");
+ return -NLE_MISSING_ATTR;
+ }
- msg = nlmsg_alloc_simple(RTM_DELQDISC, 0);
- if (!msg)
+ if (!(msg = nlmsg_alloc_simple(RTM_DELQDISC, 0)))
return -NLE_NOMEM;
+ memset(&tchdr, 0, sizeof(tchdr));
+
tchdr.tcm_family = AF_UNSPEC;
- tchdr.tcm_handle = qdisc->q_handle;
- tchdr.tcm_parent = qdisc->q_parent;
tchdr.tcm_ifindex = qdisc->q_ifindex;
- if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0) {
- nlmsg_free(msg);
- return -NLE_MSGSIZE;
- }
+ tchdr.tcm_parent = qdisc->q_parent;
+
+ if (qdisc->ce_mask & TCA_ATTR_HANDLE)
+ tchdr.tcm_handle = qdisc->q_handle;
+
+ if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0)
+ goto nla_put_failure;
+
+ if (qdisc->ce_mask & TCA_ATTR_KIND)
+ NLA_PUT_STRING(msg, TCA_KIND, qdisc->q_kind);
*result = msg;
return 0;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return -NLE_MSGSIZE;
}
/**
- * Delete a qdisc
- * @arg sk Netlink socket.
- * @arg qdisc qdisc to delete
+ * Delete qdisc
+ * @arg sk Netlink socket
+ * @arg qdisc Qdisc to add
+ *
+ * Builds a \c RTM_NEWQDISC netlink message requesting the deletion
+ * of a qdisc and sends the message to the kernel.
+ *
+ * The message is constructed out of the following attributes:
+ * - \c ifindex and \c parent
+ * - \c handle (optional, must match if provided)
+ * - \c kind (optional, must match if provided)
+ *
+ * All other qdisc attributes including all qdisc type specific
+ * attributes are ignored.
+ *
+ * After sending, the function will wait for the ACK or an eventual
+ * error message to be received and will therefore block until the
+ * operation has been completed.
*
- * 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.
+ * @note It is not possible to delete default qdiscs.
*
- * @return 0 on success or a negative error code
+ * @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
+ * this function to return immediately after sending. In this case,
+ * it is the responsibility of the caller to handle any error
+ * messages returned.
+ *
+ * @return 0 on success or a negative error code.
*/
int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
{
@@ -357,29 +345,23 @@ int rtnl_qdisc_delete(struct nl_sock *sk, struct rtnl_qdisc *qdisc)
if ((err = rtnl_qdisc_build_delete_request(qdisc, &msg)) < 0)
return err;
- err = nl_send_auto_complete(sk, msg);
- nlmsg_free(msg);
- if (err < 0)
- return err;
-
- return wait_for_ack(sk);
+ return nl_send_sync(sk, msg);
}
/** @} */
/**
- * @name Qdisc Cache Management
+ * @name Cache Related Functions
* @{
*/
/**
- * Build a qdisc cache including all qdiscs currently configured in
- * the kernel
- * @arg sk Netlink socket.
- * @arg result Pointer to store resulting message.
+ * Allocate a cache and fill it with all configured qdiscs
+ * @arg sk Netlink socket
+ * @arg result Pointer to store the created cache
*
- * Allocates a new cache, initializes it properly and updates it to
- * include all qdiscs currently configured in the kernel.
+ * Allocates a new qdisc cache and fills it with a list of all configured
+ * qdiscs on all network devices. Release the cache with nl_cache_free().
*
* @return 0 on success or a negative error code.
*/
@@ -389,14 +371,21 @@ int rtnl_qdisc_alloc_cache(struct nl_sock *sk, struct nl_cache **result)
}
/**
- * 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
+ * Search qdisc by interface index and parent
+ * @arg cache Qdisc cache
+ * @arg ifindex Interface index
+ * @arg parent Handle of parent qdisc
+ *
+ * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache()
+ * and searches for a qdisc matching the interface index and parent qdisc.
+ *
+ * The reference counter is incremented before returning the qdisc, therefore
+ * the reference must be given back with rtnl_qdisc_put() after usage.
+ *
* @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 *rtnl_qdisc_get_by_parent(struct nl_cache *cache,
+ int ifindex, uint32_t parent)
{
struct rtnl_qdisc *q;
@@ -414,14 +403,21 @@ struct rtnl_qdisc * rtnl_qdisc_get_by_parent(struct nl_cache *cache,
}
/**
- * 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.
+ * Search qdisc by interface index and handle
+ * @arg cache Qdisc cache
+ * @arg ifindex Interface index
+ * @arg handle Handle
+ *
+ * Searches a qdisc cache previously allocated with rtnl_qdisc_alloc_cache()
+ * and searches for a qdisc matching the interface index and handle.
+ *
+ * The reference counter is incremented before returning the qdisc, therefore
+ * the reference must be given back with rtnl_qdisc_put() after usage.
+ *
+ * @return Qdisc 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 *rtnl_qdisc_get(struct nl_cache *cache, int ifindex,
+ uint32_t handle)
{
struct rtnl_qdisc *q;
@@ -440,6 +436,101 @@ struct rtnl_qdisc * rtnl_qdisc_get(struct nl_cache *cache,
/** @} */
+/**
+ * @name Deprecated Functions
+ * @{
+ */
+
+/**
+ * Call a callback for each child class of a qdisc (deprecated)
+ *
+ * @deprecated Use of this function is deprecated, it does not allow
+ * to handle the out of memory situation that can occur.
+ */
+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_tc_set_parent(TC_CAST(filter), qdisc->q_handle);
+ rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex);
+ rtnl_tc_set_kind(TC_CAST(filter), qdisc->q_kind);
+
+ nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg);
+
+ rtnl_class_put(filter);
+}
+
+/**
+ * Call a callback for each filter attached to the qdisc (deprecated)
+ *
+ * @deprecated Use of this function is deprecated, it does not allow
+ * to handle the out of memory situation that can occur.
+ */
+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;
+
+ if (!(filter = rtnl_cls_alloc()))
+ return;
+
+ rtnl_tc_set_ifindex(TC_CAST(filter), qdisc->q_ifindex);
+ rtnl_tc_set_parent(TC_CAST(filter), qdisc->q_parent);
+
+ nl_cache_foreach_filter(cache, OBJ_CAST(filter), cb, arg);
+ rtnl_cls_put(filter);
+}
+
+/**
+ * Build a netlink message requesting the update of a qdisc
+ *
+ * @deprecated Use of this function is deprecated in favour of
+ * rtnl_qdisc_build_update_request() due to the missing
+ * possibility of specifying additional flags.
+ */
+int rtnl_qdisc_build_change_request(struct rtnl_qdisc *qdisc,
+ struct rtnl_qdisc *new,
+ struct nl_msg **result)
+{
+ return rtnl_qdisc_build_update_request(qdisc, new, NLM_F_REPLACE,
+ result);
+}
+
+/**
+ * Change attributes of a qdisc
+ *
+ * @deprecated Use of this function is deprecated in favour of
+ * rtnl_qdisc_update() due to the missing possibility of
+ * specifying additional flags.
+ */
+int rtnl_qdisc_change(struct nl_sock *sk, struct rtnl_qdisc *qdisc,
+ struct rtnl_qdisc *new)
+{
+ return rtnl_qdisc_update(sk, qdisc, new, NLM_F_REPLACE);
+}
+
+/** @} */
+
+static void qdisc_dump_details(struct rtnl_tc *tc, struct nl_dump_params *p)
+{
+ struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) tc;
+
+ nl_dump(p, "refcnt %u ", qdisc->q_info);
+}
+
+static struct rtnl_tc_type_ops qdisc_ops = {
+ .tt_type = RTNL_TC_TYPE_QDISC,
+ .tt_dump_prefix = "qdisc",
+ .tt_dump = {
+ [NL_DUMP_DETAILS] = qdisc_dump_details,
+ },
+};
+
static struct nl_cache_ops rtnl_qdisc_ops = {
.co_name = "route/qdisc",
.co_hdrsize = sizeof(struct tcmsg),
@@ -450,19 +541,36 @@ static struct nl_cache_ops rtnl_qdisc_ops = {
END_OF_MSGTYPES_LIST,
},
.co_protocol = NETLINK_ROUTE,
+ .co_groups = tc_groups,
.co_request_update = qdisc_request_update,
.co_msg_parser = qdisc_msg_parser,
.co_obj_ops = &qdisc_obj_ops,
};
+static struct nl_object_ops qdisc_obj_ops = {
+ .oo_name = "route/qdisc",
+ .oo_size = sizeof(struct rtnl_qdisc),
+ .oo_free_data = rtnl_tc_free_data,
+ .oo_clone = rtnl_tc_clone,
+ .oo_dump = {
+ [NL_DUMP_LINE] = rtnl_tc_dump_line,
+ [NL_DUMP_DETAILS] = rtnl_tc_dump_details,
+ [NL_DUMP_STATS] = rtnl_tc_dump_stats,
+ },
+ .oo_compare = rtnl_tc_compare,
+ .oo_id_attrs = (TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE),
+};
+
static void __init qdisc_init(void)
{
+ rtnl_tc_type_register(&qdisc_ops);
nl_cache_mngt_register(&rtnl_qdisc_ops);
}
static void __exit qdisc_exit(void)
{
nl_cache_mngt_unregister(&rtnl_qdisc_ops);
+ rtnl_tc_type_unregister(&qdisc_ops);
}
/** @} */
diff --git a/lib/route/qdisc/blackhole.c b/lib/route/qdisc/blackhole.c
new file mode 100644
index 00000000..339cf781
--- /dev/null
+++ b/lib/route/qdisc/blackhole.c
@@ -0,0 +1,37 @@
+/*
+ * lib/route/qdisc/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-2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup qdisc
+ * @defgroup qdisc_blackhole Blackhole
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink/netlink.h>
+#include <netlink-private/route/tc-api.h>
+
+static struct rtnl_tc_ops blackhole_ops = {
+ .to_kind = "blackhole",
+ .to_type = RTNL_TC_TYPE_QDISC,
+};
+
+static void __init blackhole_init(void)
+{
+ rtnl_tc_register(&blackhole_ops);
+}
+
+static void __exit blackhole_exit(void)
+{
+ rtnl_tc_unregister(&blackhole_ops);
+}
+
+/** @} */
diff --git a/lib/route/sch/cbq.c b/lib/route/qdisc/cbq.c
index 1aaa58db..95f17618 100644
--- a/lib/route/sch/cbq.c
+++ b/lib/route/qdisc/cbq.c
@@ -1,34 +1,33 @@
/*
- * lib/route/sch/cbq.c Class Based Queueing
+ * lib/route/qdisc/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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/qdisc.h>
-#include <netlink/route/qdisc-modules.h>
#include <netlink/route/class.h>
-#include <netlink/route/class-modules.h>
#include <netlink/route/link.h>
-#include <netlink/route/sch/cbq.h>
+#include <netlink/route/qdisc/cbq.h>
#include <netlink/route/cls/police.h>
/**
- * @ingroup qdisc_api
- * @ingroup class_api
- * @defgroup cbq Class Based Queueing (CBQ)
+ * @ingroup qdisc
+ * @ingroup class
+ * @defgroup qdisc_cbq Class Based Queueing (CBQ)
* @{
*/
-static struct trans_tbl ovl_strategies[] = {
+static const struct trans_tbl ovl_strategies[] = {
__ADD(TC_CBQ_OVL_CLASSIC,classic)
__ADD(TC_CBQ_OVL_DELAY,delay)
__ADD(TC_CBQ_OVL_LOWPRIO,lowprio)
@@ -73,34 +72,16 @@ static struct nla_policy cbq_policy[TCA_CBQ_MAX+1] = {
[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)
+static int cbq_msg_parser(struct rtnl_tc *tc, void *data)
{
struct nlattr *tb[TCA_CBQ_MAX + 1];
- struct rtnl_cbq *cbq;
+ struct rtnl_cbq *cbq = data;
int err;
- err = tca_parse(tb, TCA_CBQ_MAX, tca, cbq_policy);
+ err = tca_parse(tb, TCA_CBQ_MAX, tc, cbq_policy);
if (err < 0)
return err;
- cbq = cbq_alloc(tca);
- if (!cbq)
- return -NLE_NOMEM;
-
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));
@@ -113,53 +94,13 @@ static int cbq_msg_parser(struct rtnl_tca *tca)
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 -NLE_NOMEM;
- 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)
+static void cbq_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- 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 void cbq_dump_line(struct rtnl_tca *tca, struct nl_dump_params *p)
-{
- struct rtnl_cbq *cbq;
+ struct rtnl_cbq *cbq = data;
double r, rbit;
char *ru, *rubit;
- cbq = cbq_qdisc(tca);
if (!cbq)
return;
@@ -170,26 +111,14 @@ static void cbq_dump_line(struct rtnl_tca *tca, struct nl_dump_params *p)
r, ru, rbit, rubit, cbq->cbq_wrr.priority);
}
-static void cbq_qdisc_dump_line(struct rtnl_qdisc *qdisc,
- struct nl_dump_params *p)
+static void cbq_dump_details(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- cbq_dump_line((struct rtnl_tca *) qdisc, p);
-}
-
-static void cbq_class_dump_line(struct rtnl_class *class,
- struct nl_dump_params *p)
-{
- cbq_dump_line((struct rtnl_tca *) class, p);
-}
-
-static void cbq_dump_details(struct rtnl_tca *tca, struct nl_dump_params *p)
-{
- struct rtnl_cbq *cbq;
+ struct rtnl_cbq *cbq = data;
char *unit, buf[32];
double w;
uint32_t el;
- cbq = cbq_qdisc(tca);
if (!cbq)
return;
@@ -222,23 +151,12 @@ static void cbq_dump_details(struct rtnl_tca *tca, struct nl_dump_params *p)
nl_police2str(cbq->cbq_police.police, buf, sizeof(buf)));
}
-static void cbq_qdisc_dump_details(struct rtnl_qdisc *qdisc,
- struct nl_dump_params *p)
-{
- cbq_dump_details((struct rtnl_tca *) qdisc, p);
-}
-
-static void cbq_class_dump_details(struct rtnl_class *class,
- struct nl_dump_params *p)
+static void cbq_dump_stats(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- cbq_dump_details((struct rtnl_tca *) class, p);
-}
-
-static void cbq_dump_stats(struct rtnl_tca *tca, struct nl_dump_params *p)
-{
- struct tc_cbq_xstats *x = tca_xstats(tca);
-
- if (!x)
+ struct tc_cbq_xstats *x;
+
+ if (!(x = tca_xstats(tc)))
return;
nl_dump_line(p, " borrows overact "
@@ -247,52 +165,40 @@ static void cbq_dump_stats(struct rtnl_tca *tca, struct nl_dump_params *p)
x->borrows, x->overactions, x->avgidle, x->undertime);
}
-static void cbq_qdisc_dump_stats(struct rtnl_qdisc *qdisc,
- struct nl_dump_params *p)
-{
- cbq_dump_stats((struct rtnl_tca *) qdisc, p);
-}
-
-static void cbq_class_dump_stats(struct rtnl_class *class,
- struct nl_dump_params *p)
-{
- cbq_dump_stats((struct rtnl_tca *) class, p);
-}
-
-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_LINE] = cbq_qdisc_dump_line,
- [NL_DUMP_DETAILS] = cbq_qdisc_dump_details,
- [NL_DUMP_STATS] = cbq_qdisc_dump_stats,
+static struct rtnl_tc_ops cbq_qdisc_ops = {
+ .to_kind = "cbq",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_cbq),
+ .to_msg_parser = cbq_msg_parser,
+ .to_dump = {
+ [NL_DUMP_LINE] = cbq_dump_line,
+ [NL_DUMP_DETAILS] = cbq_dump_details,
+ [NL_DUMP_STATS] = cbq_dump_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_LINE] = cbq_class_dump_line,
- [NL_DUMP_DETAILS] = cbq_class_dump_details,
- [NL_DUMP_STATS] = cbq_class_dump_stats,
+static struct rtnl_tc_ops cbq_class_ops = {
+ .to_kind = "cbq",
+ .to_type = RTNL_TC_TYPE_CLASS,
+ .to_size = sizeof(struct rtnl_cbq),
+ .to_msg_parser = cbq_msg_parser,
+ .to_dump = {
+ [NL_DUMP_LINE] = cbq_dump_line,
+ [NL_DUMP_DETAILS] = cbq_dump_details,
+ [NL_DUMP_STATS] = cbq_dump_stats,
},
};
static void __init cbq_init(void)
{
- rtnl_qdisc_register(&cbq_qdisc_ops);
- rtnl_class_register(&cbq_class_ops);
+ rtnl_tc_register(&cbq_qdisc_ops);
+ rtnl_tc_register(&cbq_class_ops);
}
static void __exit cbq_exit(void)
{
- rtnl_qdisc_unregister(&cbq_qdisc_ops);
- rtnl_class_unregister(&cbq_class_ops);
+ rtnl_tc_unregister(&cbq_qdisc_ops);
+ rtnl_tc_unregister(&cbq_class_ops);
}
/** @} */
diff --git a/lib/route/sch/dsmark.c b/lib/route/qdisc/dsmark.c
index 61b0feaa..fd9553dc 100644
--- a/lib/route/sch/dsmark.c
+++ b/lib/route/qdisc/dsmark.c
@@ -1,30 +1,29 @@
/*
- * lib/route/sch/dsmark.c DSMARK
+ * lib/route/qdisc/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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup qdisc_api
- * @ingroup class_api
- * @defgroup dsmark Differentiated Services Marker (DSMARK)
+ * @ingroup qdisc
+ * @ingroup class
+ * @defgroup qdisc_dsmark Differentiated Services Marker (DSMARK)
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/qdisc.h>
-#include <netlink/route/qdisc-modules.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/class.h>
-#include <netlink/route/class-modules.h>
-#include <netlink/route/sch/dsmark.h>
+#include <netlink/route/qdisc/dsmark.h>
/** @cond SKIP */
#define SCH_DSMARK_ATTR_INDICES 0x1
@@ -35,20 +34,6 @@
#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 },
@@ -57,21 +42,16 @@ static struct nla_policy dsmark_policy[TCA_DSMARK_MAX+1] = {
[TCA_DSMARK_MASK] = { .type = NLA_U8 },
};
-static int dsmark_qdisc_msg_parser(struct rtnl_qdisc *qdisc)
+static int dsmark_qdisc_msg_parser(struct rtnl_tc *tc, void *data)
{
- int err;
+ struct rtnl_dsmark_qdisc *dsmark = data;
struct nlattr *tb[TCA_DSMARK_MAX + 1];
- struct rtnl_dsmark_qdisc *dsmark;
+ int err;
- err = tca_parse(tb, TCA_DSMARK_MAX, (struct rtnl_tca *) qdisc,
- dsmark_policy);
+ err = tca_parse(tb, TCA_DSMARK_MAX, tc, dsmark_policy);
if (err < 0)
return err;
- dsmark = dsmark_qdisc_alloc(qdisc);
- if (!dsmark)
- return -NLE_NOMEM;
-
if (tb[TCA_DSMARK_INDICES]) {
dsmark->qdm_indices = nla_get_u16(tb[TCA_DSMARK_INDICES]);
dsmark->qdm_mask |= SCH_DSMARK_ATTR_INDICES;
@@ -91,35 +71,16 @@ static int dsmark_qdisc_msg_parser(struct rtnl_qdisc *qdisc)
return 0;
}
-static inline struct rtnl_dsmark_class *dsmark_class(struct rtnl_class *class)
+static int dsmark_class_msg_parser(struct rtnl_tc *tc, void *data)
{
- 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 rtnl_dsmark_class *dsmark = data;
struct nlattr *tb[TCA_DSMARK_MAX + 1];
- struct rtnl_dsmark_class *dsmark;
+ int err;
- err = tca_parse(tb, TCA_DSMARK_MAX, (struct rtnl_tca *) class,
- dsmark_policy);
+ err = tca_parse(tb, TCA_DSMARK_MAX, tc, dsmark_policy);
if (err < 0)
return err;
- dsmark = dsmark_class_alloc(class);
- if (!dsmark)
- return -NLE_NOMEM;
-
if (tb[TCA_DSMARK_MASK]) {
dsmark->cdm_bmask = nla_get_u8(tb[TCA_DSMARK_MASK]);
dsmark->cdm_mask |= SCH_DSMARK_ATTR_MASK;
@@ -133,19 +94,19 @@ static int dsmark_class_msg_parser(struct rtnl_class *class)
return 0;
}
-static void dsmark_qdisc_dump_line(struct rtnl_qdisc *qdisc,
+static void dsmark_qdisc_dump_line(struct rtnl_tc *tc, void *data,
struct nl_dump_params *p)
{
- struct rtnl_dsmark_qdisc *dsmark = dsmark_qdisc(qdisc);
+ struct rtnl_dsmark_qdisc *dsmark = data;
if (dsmark && (dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES))
nl_dump(p, " indices 0x%04x", dsmark->qdm_indices);
}
-static void dsmark_qdisc_dump_details(struct rtnl_qdisc *qdisc,
+static void dsmark_qdisc_dump_details(struct rtnl_tc *tc, void *data,
struct nl_dump_params *p)
{
- struct rtnl_dsmark_qdisc *dsmark = dsmark_qdisc(qdisc);
+ struct rtnl_dsmark_qdisc *dsmark = data;
if (!dsmark)
return;
@@ -157,10 +118,10 @@ static void dsmark_qdisc_dump_details(struct rtnl_qdisc *qdisc,
nl_dump(p, " set-tc-index");
}
-static void dsmark_class_dump_line(struct rtnl_class *class,
+static void dsmark_class_dump_line(struct rtnl_tc *tc, void *data,
struct nl_dump_params *p)
{
- struct rtnl_dsmark_class *dsmark = dsmark_class(class);
+ struct rtnl_dsmark_class *dsmark = data;
if (!dsmark)
return;
@@ -172,17 +133,13 @@ static void dsmark_class_dump_line(struct rtnl_class *class,
nl_dump(p, " mask 0x%02x", dsmark->cdm_bmask);
}
-static struct nl_msg *dsmark_qdisc_get_opts(struct rtnl_qdisc *qdisc)
+static int dsmark_qdisc_msg_fill(struct rtnl_tc *tc, void *data,
+ struct nl_msg *msg)
{
- struct rtnl_dsmark_qdisc *dsmark = dsmark_qdisc(qdisc);
- struct nl_msg *msg;
+ struct rtnl_dsmark_qdisc *dsmark = data;
if (!dsmark)
- return NULL;
-
- msg = nlmsg_alloc();
- if (!msg)
- goto nla_put_failure;
+ return 0;
if (dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES)
NLA_PUT_U16(msg, TCA_DSMARK_INDICES, dsmark->qdm_indices);
@@ -194,24 +151,19 @@ static struct nl_msg *dsmark_qdisc_get_opts(struct rtnl_qdisc *qdisc)
if (dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX)
NLA_PUT_FLAG(msg, TCA_DSMARK_SET_TC_INDEX);
- return msg;
+ return 0;
nla_put_failure:
- nlmsg_free(msg);
- return NULL;
+ return -NLE_MSGSIZE;
}
-static struct nl_msg *dsmark_class_get_opts(struct rtnl_class *class)
+static int dsmark_class_msg_fill(struct rtnl_tc *tc, void *data,
+ struct nl_msg *msg)
{
- struct rtnl_dsmark_class *dsmark = dsmark_class(class);
- struct nl_msg *msg;
+ struct rtnl_dsmark_class *dsmark = data;
if (!dsmark)
- return NULL;
-
- msg = nlmsg_alloc();
- if (!msg)
- goto nla_put_failure;
+ return 0;
if (dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK)
NLA_PUT_U8(msg, TCA_DSMARK_MASK, dsmark->cdm_bmask);
@@ -219,11 +171,10 @@ static struct nl_msg *dsmark_class_get_opts(struct rtnl_class *class)
if (dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE)
NLA_PUT_U8(msg, TCA_DSMARK_VALUE, dsmark->cdm_value);
- return msg;
+ return 0;
nla_put_failure:
- nlmsg_free(msg);
- return NULL;
+ return -NLE_MSGSIZE;
}
/**
@@ -241,8 +192,7 @@ int rtnl_class_dsmark_set_bitmask(struct rtnl_class *class, uint8_t mask)
{
struct rtnl_dsmark_class *dsmark;
- dsmark = dsmark_class(class);
- if (!dsmark)
+ if (!(dsmark = rtnl_tc_data(TC_CAST(class))))
return -NLE_NOMEM;
dsmark->cdm_bmask = mask;
@@ -259,9 +209,11 @@ int rtnl_class_dsmark_set_bitmask(struct rtnl_class *class, uint8_t mask)
int rtnl_class_dsmark_get_bitmask(struct rtnl_class *class)
{
struct rtnl_dsmark_class *dsmark;
+
+ if (!(dsmark = rtnl_tc_data(TC_CAST(class))))
+ return -NLE_NOMEM;
- dsmark = dsmark_class(class);
- if (dsmark && dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK)
+ if (dsmark->cdm_mask & SCH_DSMARK_ATTR_MASK)
return dsmark->cdm_bmask;
else
return -NLE_NOATTR;
@@ -276,9 +228,8 @@ int rtnl_class_dsmark_get_bitmask(struct rtnl_class *class)
int rtnl_class_dsmark_set_value(struct rtnl_class *class, uint8_t value)
{
struct rtnl_dsmark_class *dsmark;
-
- dsmark = dsmark_class(class);
- if (!dsmark)
+
+ if (!(dsmark = rtnl_tc_data(TC_CAST(class))))
return -NLE_NOMEM;
dsmark->cdm_value = value;
@@ -295,9 +246,11 @@ int rtnl_class_dsmark_set_value(struct rtnl_class *class, uint8_t value)
int rtnl_class_dsmark_get_value(struct rtnl_class *class)
{
struct rtnl_dsmark_class *dsmark;
+
+ if (!(dsmark = rtnl_tc_data(TC_CAST(class))))
+ return -NLE_NOMEM;
- dsmark = dsmark_class(class);
- if (dsmark && dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE)
+ if (dsmark->cdm_mask & SCH_DSMARK_ATTR_VALUE)
return dsmark->cdm_value;
else
return -NLE_NOATTR;
@@ -319,8 +272,7 @@ int rtnl_qdisc_dsmark_set_indices(struct rtnl_qdisc *qdisc, uint16_t indices)
{
struct rtnl_dsmark_qdisc *dsmark;
- dsmark = dsmark_qdisc(qdisc);
- if (!dsmark)
+ if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc))))
return -NLE_NOMEM;
dsmark->qdm_indices = indices;
@@ -338,8 +290,10 @@ 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)
+ if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ if (dsmark->qdm_mask & SCH_DSMARK_ATTR_INDICES)
return dsmark->qdm_indices;
else
return -NLE_NOATTR;
@@ -356,8 +310,7 @@ int rtnl_qdisc_dsmark_set_default_index(struct rtnl_qdisc *qdisc,
{
struct rtnl_dsmark_qdisc *dsmark;
- dsmark = dsmark_qdisc(qdisc);
- if (!dsmark)
+ if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc))))
return -NLE_NOMEM;
dsmark->qdm_default_index = default_index;
@@ -375,8 +328,10 @@ 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)
+ if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ if (dsmark->qdm_mask & SCH_DSMARK_ATTR_DEFAULT_INDEX)
return dsmark->qdm_default_index;
else
return -NLE_NOATTR;
@@ -392,8 +347,7 @@ 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)
+ if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc))))
return -NLE_NOMEM;
dsmark->qdm_set_tc_index = !!flag;
@@ -412,8 +366,10 @@ 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)
+ if (!(dsmark = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ if (dsmark->qdm_mask & SCH_DSMARK_ATTR_SET_TC_INDEX)
return dsmark->qdm_set_tc_index;
else
return -NLE_NOATTR;
@@ -421,33 +377,37 @@ int rtnl_qdisc_dsmark_get_set_tc_index(struct rtnl_qdisc *qdisc)
/** @} */
-static struct rtnl_qdisc_ops dsmark_qdisc_ops = {
- .qo_kind = "dsmark",
- .qo_msg_parser = dsmark_qdisc_msg_parser,
- .qo_dump = {
+static struct rtnl_tc_ops dsmark_qdisc_ops = {
+ .to_kind = "dsmark",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_dsmark_qdisc),
+ .to_msg_parser = dsmark_qdisc_msg_parser,
+ .to_dump = {
[NL_DUMP_LINE] = dsmark_qdisc_dump_line,
[NL_DUMP_DETAILS] = dsmark_qdisc_dump_details,
},
- .qo_get_opts = dsmark_qdisc_get_opts,
+ .to_msg_fill = dsmark_qdisc_msg_fill,
};
-static struct rtnl_class_ops dsmark_class_ops = {
- .co_kind = "dsmark",
- .co_msg_parser = dsmark_class_msg_parser,
- .co_dump[NL_DUMP_LINE] = dsmark_class_dump_line,
- .co_get_opts = dsmark_class_get_opts,
+static struct rtnl_tc_ops dsmark_class_ops = {
+ .to_kind = "dsmark",
+ .to_type = RTNL_TC_TYPE_CLASS,
+ .to_size = sizeof(struct rtnl_dsmark_class),
+ .to_msg_parser = dsmark_class_msg_parser,
+ .to_dump[NL_DUMP_LINE] = dsmark_class_dump_line,
+ .to_msg_fill = dsmark_class_msg_fill,
};
static void __init dsmark_init(void)
{
- rtnl_qdisc_register(&dsmark_qdisc_ops);
- rtnl_class_register(&dsmark_class_ops);
+ rtnl_tc_register(&dsmark_qdisc_ops);
+ rtnl_tc_register(&dsmark_class_ops);
}
static void __exit dsmark_exit(void)
{
- rtnl_qdisc_unregister(&dsmark_qdisc_ops);
- rtnl_class_unregister(&dsmark_class_ops);
+ rtnl_tc_unregister(&dsmark_qdisc_ops);
+ rtnl_tc_unregister(&dsmark_class_ops);
}
/** @} */
diff --git a/lib/route/qdisc/fifo.c b/lib/route/qdisc/fifo.c
new file mode 100644
index 00000000..d94c0079
--- /dev/null
+++ b/lib/route/qdisc/fifo.c
@@ -0,0 +1,169 @@
+/*
+ * lib/route/qdisc/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-2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup qdisc
+ * @defgroup qdisc_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_tc_set_kind()).
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/qdisc/fifo.h>
+#include <netlink/utils.h>
+
+/** @cond SKIP */
+#define SCH_FIFO_ATTR_LIMIT 1
+/** @endcond */
+
+static int fifo_msg_parser(struct rtnl_tc *tc, void *data)
+{
+ struct rtnl_fifo *fifo = data;
+ struct tc_fifo_qopt *opt;
+
+ if (tc->tc_opts->d_size < sizeof(struct tc_fifo_qopt))
+ return -NLE_INVAL;
+
+ opt = (struct tc_fifo_qopt *) tc->tc_opts->d_data;
+ fifo->qf_limit = opt->limit;
+ fifo->qf_mask = SCH_FIFO_ATTR_LIMIT;
+
+ return 0;
+}
+
+static void pfifo_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
+{
+ struct rtnl_fifo *fifo = data;
+
+ if (fifo)
+ nl_dump(p, " limit %u packets", fifo->qf_limit);
+}
+
+static void bfifo_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
+{
+ struct rtnl_fifo *fifo = data;
+ char *unit;
+ double r;
+
+ if (!fifo)
+ return;
+
+ r = nl_cancel_down_bytes(fifo->qf_limit, &unit);
+ nl_dump(p, " limit %.1f%s", r, unit);
+}
+
+static int fifo_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+ struct rtnl_fifo *fifo = data;
+ struct tc_fifo_qopt opts = {0};
+
+ if (!fifo || !(fifo->qf_mask & SCH_FIFO_ATTR_LIMIT))
+ return -NLE_INVAL;
+
+ opts.limit = fifo->qf_limit;
+
+ return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD);
+}
+
+/**
+ * @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;
+
+ if (!(fifo = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ 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;
+
+ if (!(fifo = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ if (fifo->qf_mask & SCH_FIFO_ATTR_LIMIT)
+ return fifo->qf_limit;
+ else
+ return -NLE_NOATTR;
+}
+
+/** @} */
+
+static struct rtnl_tc_ops pfifo_ops = {
+ .to_kind = "pfifo",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_fifo),
+ .to_msg_parser = fifo_msg_parser,
+ .to_dump[NL_DUMP_LINE] = pfifo_dump_line,
+ .to_msg_fill = fifo_msg_fill,
+};
+
+static struct rtnl_tc_ops bfifo_ops = {
+ .to_kind = "bfifo",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_fifo),
+ .to_msg_parser = fifo_msg_parser,
+ .to_dump[NL_DUMP_LINE] = bfifo_dump_line,
+ .to_msg_fill = fifo_msg_fill,
+};
+
+static void __init fifo_init(void)
+{
+ rtnl_tc_register(&pfifo_ops);
+ rtnl_tc_register(&bfifo_ops);
+}
+
+static void __exit fifo_exit(void)
+{
+ rtnl_tc_unregister(&pfifo_ops);
+ rtnl_tc_unregister(&bfifo_ops);
+}
+
+/** @} */
diff --git a/lib/route/qdisc/fq_codel.c b/lib/route/qdisc/fq_codel.c
new file mode 100644
index 00000000..ade20e50
--- /dev/null
+++ b/lib/route/qdisc/fq_codel.c
@@ -0,0 +1,377 @@
+/*
+ * lib/route/qdisc/fq_codel.c fq_codel
+ *
+ * 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) 2013 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+/**
+ * @ingroup qdisc
+ * @defgroup qdisc_fq_codel Fair Queue CoDel
+ * @brief
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/qdisc/fq_codel.h>
+#include <netlink/utils.h>
+
+/** @cond SKIP */
+#define SCH_FQ_CODEL_ATTR_TARGET 0x1
+#define SCH_FQ_CODEL_ATTR_LIMIT 0x2
+#define SCH_FQ_CODEL_ATTR_INTERVAL 0x4
+#define SCH_FQ_CODEL_ATTR_FLOWS 0x8
+#define SCH_FQ_CODEL_ATTR_QUANTUM 0x10
+#define SCH_FQ_CODEL_ATTR_ECN 0x20
+/** @endcond */
+
+static struct nla_policy fq_codel_policy[TCA_FQ_CODEL_MAX + 1] = {
+ [TCA_FQ_CODEL_TARGET] = { .type = NLA_U32 },
+ [TCA_FQ_CODEL_LIMIT] = { .type = NLA_U32 },
+ [TCA_FQ_CODEL_INTERVAL] = { .type = NLA_U32 },
+ [TCA_FQ_CODEL_ECN] = { .type = NLA_U32 },
+ [TCA_FQ_CODEL_FLOWS] = { .type = NLA_U32 },
+ [TCA_FQ_CODEL_QUANTUM] = { .type = NLA_U32 },
+};
+
+static int fq_codel_msg_parser(struct rtnl_tc *tc, void *data)
+{
+ struct rtnl_fq_codel *fq_codel = data;
+ struct nlattr *tb[TCA_FQ_CODEL_MAX + 1];
+ int err;
+
+ err = tca_parse(tb, TCA_FQ_CODEL_MAX, tc, fq_codel_policy);
+ if (err < 0)
+ return err;
+
+ if (tb[TCA_FQ_CODEL_TARGET]) {
+ fq_codel->fq_target = nla_get_u32(tb[TCA_FQ_CODEL_TARGET]);
+ fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_TARGET;
+ }
+
+ if (tb[TCA_FQ_CODEL_INTERVAL]) {
+ fq_codel->fq_interval = nla_get_u32(tb[TCA_FQ_CODEL_INTERVAL]);
+ fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_INTERVAL;
+ }
+
+ if (tb[TCA_FQ_CODEL_LIMIT]) {
+ fq_codel->fq_limit = nla_get_u32(tb[TCA_FQ_CODEL_LIMIT]);
+ fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_LIMIT;
+ }
+
+ if (tb[TCA_FQ_CODEL_QUANTUM]) {
+ fq_codel->fq_quantum = nla_get_u32(tb[TCA_FQ_CODEL_QUANTUM]);
+ fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_QUANTUM;
+ }
+
+ if (tb[TCA_FQ_CODEL_FLOWS]) {
+ fq_codel->fq_flows = nla_get_u32(tb[TCA_FQ_CODEL_FLOWS]);
+ fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_FLOWS;
+ }
+
+ if (tb[TCA_FQ_CODEL_ECN]) {
+ fq_codel->fq_ecn = nla_get_u32(tb[TCA_FQ_CODEL_ECN]);
+ fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_ECN;
+ }
+
+ return 0;
+}
+
+static void fq_codel_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
+{
+ struct rtnl_fq_codel *fq_codel = data;
+
+ if (!fq_codel)
+ return;
+
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_LIMIT)
+ nl_dump(p, " limit %u packets", fq_codel->fq_limit);
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_TARGET)
+ nl_dump(p, " target %u", fq_codel->fq_target);
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_INTERVAL)
+ nl_dump(p, " interval %u", fq_codel->fq_interval);
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_ECN)
+ nl_dump(p, " ecn %u", fq_codel->fq_ecn);
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_FLOWS)
+ nl_dump(p, " flows %u", fq_codel->fq_flows);
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_QUANTUM)
+ nl_dump(p, " quantum %u", fq_codel->fq_quantum);
+}
+
+static int fq_codel_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+ struct rtnl_fq_codel *fq_codel = data;
+
+ if (!fq_codel)
+ return -NLE_INVAL;
+
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_LIMIT)
+ NLA_PUT_U32(msg, TCA_FQ_CODEL_LIMIT, fq_codel->fq_limit);
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_INTERVAL)
+ NLA_PUT_U32(msg, TCA_FQ_CODEL_INTERVAL, fq_codel->fq_interval);
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_TARGET)
+ NLA_PUT_U32(msg, TCA_FQ_CODEL_TARGET, fq_codel->fq_target);
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_QUANTUM)
+ NLA_PUT_U32(msg, TCA_FQ_CODEL_QUANTUM, fq_codel->fq_quantum);
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_FLOWS)
+ NLA_PUT_U32(msg, TCA_FQ_CODEL_FLOWS, fq_codel->fq_flows);
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_ECN)
+ NLA_PUT_U32(msg, TCA_FQ_CODEL_ECN, fq_codel->fq_ecn);
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
+
+}
+
+/**
+ * @name Attribute Modification
+ * @{
+ */
+
+/**
+ * Set limit of fq_codel qdisc.
+ * @arg qdisc fq_codel qdisc to be modified.
+ * @arg limit New limit.
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_fq_codel_set_limit(struct rtnl_qdisc *qdisc, int limit)
+{
+ struct rtnl_fq_codel *fq_codel;
+
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ fq_codel->fq_limit = limit;
+ fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_LIMIT;
+
+ return 0;
+}
+
+/**
+ * Get limit of a fq_codel qdisc.
+ * @arg qdisc fq_codel qdisc.
+ * @return Numeric limit or a negative error code.
+ */
+int rtnl_qdisc_fq_codel_get_limit(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_fq_codel *fq_codel;
+
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_LIMIT)
+ return fq_codel->fq_limit;
+ else
+ return -NLE_NOATTR;
+}
+
+/**
+ * Set target of fq_codel qdisc.
+ * @arg qdisc fq_codel qdisc to be modified.
+ * @arg target New target.
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_fq_codel_set_target(struct rtnl_qdisc *qdisc, uint32_t target)
+{
+ struct rtnl_fq_codel *fq_codel;
+
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ fq_codel->fq_target = target;
+ fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_TARGET;
+
+ return 0;
+}
+
+/**
+ * Get target of a fq_codel qdisc.
+ * @arg qdisc fq_codel qdisc.
+ * @return Numeric target or zero.
+ */
+uint32_t rtnl_qdisc_fq_codel_get_target(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_fq_codel *fq_codel;
+
+ if ((fq_codel = rtnl_tc_data(TC_CAST(qdisc))) &&
+ fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_TARGET)
+ return fq_codel->fq_target;
+ else
+ return 0;
+}
+
+/**
+ * Set interval of fq_codel qdisc.
+ * @arg qdisc fq_codel qdisc to be modified.
+ * @arg interval New interval.
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_fq_codel_set_interval(struct rtnl_qdisc *qdisc, uint32_t interval)
+{
+ struct rtnl_fq_codel *fq_codel;
+
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ fq_codel->fq_interval = interval;
+ fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_INTERVAL;
+
+ return 0;
+}
+
+/**
+ * Get target of a fq_codel qdisc.
+ * @arg qdisc fq_codel qdisc.
+ * @return Numeric interval or zero.
+ */
+uint32_t rtnl_qdisc_fq_codel_get_interval(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_fq_codel *fq_codel;
+
+ if ((fq_codel = rtnl_tc_data(TC_CAST(qdisc))) &&
+ fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_INTERVAL)
+ return fq_codel->fq_interval;
+ else
+ return 0;
+}
+
+/**
+ * Set quantum of fq_codel qdisc.
+ * @arg qdisc fq_codel qdisc to be modified.
+ * @arg quantum New quantum.
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_fq_codel_set_quantum(struct rtnl_qdisc *qdisc, uint32_t quantum)
+{
+ struct rtnl_fq_codel *fq_codel;
+
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ fq_codel->fq_quantum = quantum;
+ fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_QUANTUM;
+
+ return 0;
+}
+
+/**
+ * Get quantum of a fq_codel qdisc.
+ * @arg qdisc fq_codel qdisc.
+ * @return Numeric quantum or zero.
+ */
+uint32_t rtnl_qdisc_fq_codel_get_quantum(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_fq_codel *fq_codel;
+
+ if ((fq_codel = rtnl_tc_data(TC_CAST(qdisc))) &&
+ (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_QUANTUM))
+ return fq_codel->fq_quantum;
+ else
+ return 0;
+}
+
+/**
+ * Set flows of fq_codel qdisc.
+ * @arg qdisc fq_codel qdisc to be modified.
+ * @arg flows New flows value.
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_fq_codel_set_flows(struct rtnl_qdisc *qdisc, int flows)
+{
+ struct rtnl_fq_codel *fq_codel;
+
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ fq_codel->fq_flows = flows;
+ fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_FLOWS;
+
+ return 0;
+}
+
+/**
+ * Get flows of a fq_codel qdisc.
+ * @arg qdisc fq_codel qdisc.
+ * @return Numeric flows or a negative error code.
+ */
+int rtnl_qdisc_fq_codel_get_flows(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_fq_codel *fq_codel;
+
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_FLOWS)
+ return fq_codel->fq_flows;
+ else
+ return -NLE_NOATTR;
+}
+/**
+ * Set ecn of fq_codel qdisc.
+ * @arg qdisc fq_codel qdisc to be modified.
+ * @arg ecn New ecn value.
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_fq_codel_set_ecn(struct rtnl_qdisc *qdisc, int ecn)
+{
+ struct rtnl_fq_codel *fq_codel;
+
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ fq_codel->fq_ecn = ecn;
+ fq_codel->fq_mask |= SCH_FQ_CODEL_ATTR_ECN;
+
+ return 0;
+}
+
+/**
+ * Get ecn of a fq_codel qdisc.
+ * @arg qdisc fq_codel qdisc.
+ * @return Numeric ecn or a negative error code.
+ */
+int rtnl_qdisc_fq_codel_get_ecn(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_fq_codel *fq_codel;
+
+ if (!(fq_codel = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ if (fq_codel->fq_mask & SCH_FQ_CODEL_ATTR_ECN)
+ return fq_codel->fq_ecn;
+ else
+ return -NLE_NOATTR;
+}
+/** @} */
+
+static struct rtnl_tc_ops fq_codel_ops = {
+ .to_kind = "fq_codel",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_fq_codel),
+ .to_msg_parser = fq_codel_msg_parser,
+ .to_dump[NL_DUMP_LINE] = fq_codel_dump_line,
+ .to_msg_fill = fq_codel_msg_fill,
+};
+
+static void __init fq_codel_init(void)
+{
+ rtnl_tc_register(&fq_codel_ops);
+}
+
+static void __exit fq_codel_exit(void)
+{
+ rtnl_tc_unregister(&fq_codel_ops);
+}
+
+/** @} */
diff --git a/lib/route/qdisc/htb.c b/lib/route/qdisc/htb.c
new file mode 100644
index 00000000..5a61a4ea
--- /dev/null
+++ b/lib/route/qdisc/htb.c
@@ -0,0 +1,643 @@
+/*
+ * lib/route/qdisc/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-2011 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com>
+ * Copyright (c) 2005-2006 Siemens AG Oesterreich
+ */
+
+/**
+ * @ingroup qdisc
+ * @ingroup class
+ * @defgroup qdisc_htb Hierachical Token Bucket (HTB)
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/class.h>
+#include <netlink/route/link.h>
+#include <netlink/route/qdisc/htb.h>
+
+/** @cond SKIP */
+#define SCH_HTB_HAS_RATE2QUANTUM 0x01
+#define SCH_HTB_HAS_DEFCLS 0x02
+
+#define SCH_HTB_HAS_PRIO 0x001
+#define SCH_HTB_HAS_RATE 0x002
+#define SCH_HTB_HAS_CEIL 0x004
+#define SCH_HTB_HAS_RBUFFER 0x008
+#define SCH_HTB_HAS_CBUFFER 0x010
+#define SCH_HTB_HAS_QUANTUM 0x020
+#define SCH_HTB_HAS_LEVEL 0x040
+/** @endcond */
+
+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_tc *tc, void *data)
+{
+ struct nlattr *tb[TCA_HTB_MAX + 1];
+ struct rtnl_htb_qdisc *htb = data;
+ int err;
+
+ if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0)
+ return err;
+
+ if (tb[TCA_HTB_INIT]) {
+ struct tc_htb_glob opts;
+
+ nla_memcpy(&opts, tb[TCA_HTB_INIT], sizeof(opts));
+ htb->qh_rate2quantum = opts.rate2quantum;
+ htb->qh_defcls = opts.defcls;
+ htb->qh_direct_pkts = opts.direct_pkts;
+
+ htb->qh_mask = (SCH_HTB_HAS_RATE2QUANTUM | SCH_HTB_HAS_DEFCLS);
+ }
+
+ return 0;
+}
+
+static int htb_class_msg_parser(struct rtnl_tc *tc, void *data)
+{
+ struct nlattr *tb[TCA_HTB_MAX + 1];
+ struct rtnl_htb_class *htb = data;
+ int err;
+
+ if ((err = tca_parse(tb, TCA_HTB_MAX, tc, htb_policy)) < 0)
+ return err;
+
+ if (tb[TCA_HTB_PARMS]) {
+ struct tc_htb_opt opts;
+
+ nla_memcpy(&opts, tb[TCA_HTB_PARMS], sizeof(opts));
+ htb->ch_prio = opts.prio;
+ rtnl_copy_ratespec(&htb->ch_rate, &opts.rate);
+ rtnl_copy_ratespec(&htb->ch_ceil, &opts.ceil);
+ htb->ch_rbuffer = rtnl_tc_calc_bufsize(nl_ticks2us(opts.buffer),
+ opts.rate.rate);
+ htb->ch_cbuffer = rtnl_tc_calc_bufsize(nl_ticks2us(opts.cbuffer),
+ opts.ceil.rate);
+ htb->ch_quantum = opts.quantum;
+ htb->ch_level = opts.level;
+
+ rtnl_tc_set_mpu(tc, htb->ch_rate.rs_mpu);
+ rtnl_tc_set_overhead(tc, htb->ch_rate.rs_overhead);
+
+ htb->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_LEVEL);
+ }
+
+ return 0;
+}
+
+static void htb_qdisc_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
+{
+ struct rtnl_htb_qdisc *htb = data;
+
+ if (!htb)
+ return;
+
+ if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
+ nl_dump(p, " r2q %u", htb->qh_rate2quantum);
+
+ if (htb->qh_mask & SCH_HTB_HAS_DEFCLS) {
+ char buf[64];
+ nl_dump(p, " default-class %s",
+ rtnl_tc_handle2str(htb->qh_defcls, buf, sizeof(buf)));
+ }
+}
+
+static void htb_class_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
+{
+ struct rtnl_htb_class *htb = data;
+
+ if (!htb)
+ return;
+
+ if (htb->ch_mask & SCH_HTB_HAS_RATE) {
+ double r, rbit;
+ char *ru, *rubit;
+
+ r = nl_cancel_down_bytes(htb->ch_rate.rs_rate, &ru);
+ rbit = nl_cancel_down_bits(htb->ch_rate.rs_rate*8, &rubit);
+
+ nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u",
+ r, ru, rbit, rubit, 1<<htb->ch_rate.rs_cell_log);
+ }
+}
+
+static void htb_class_dump_details(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
+{
+ struct rtnl_htb_class *htb = data;
+
+ if (!htb)
+ return;
+
+ /* line 1 */
+ if (htb->ch_mask & SCH_HTB_HAS_CEIL) {
+ double r, rbit;
+ char *ru, *rubit;
+
+ r = nl_cancel_down_bytes(htb->ch_ceil.rs_rate, &ru);
+ rbit = nl_cancel_down_bits(htb->ch_ceil.rs_rate*8, &rubit);
+
+ nl_dump(p, " ceil %.2f%s/s (%.0f%s) log %u",
+ r, ru, rbit, rubit, 1<<htb->ch_ceil.rs_cell_log);
+ }
+
+ if (htb->ch_mask & SCH_HTB_HAS_PRIO)
+ nl_dump(p, " prio %u", htb->ch_prio);
+
+ if (htb->ch_mask & SCH_HTB_HAS_RBUFFER) {
+ double b;
+ char *bu;
+
+ b = nl_cancel_down_bytes(htb->ch_rbuffer, &bu);
+ nl_dump(p, " rbuffer %.2f%s", b, bu);
+ }
+
+ if (htb->ch_mask & SCH_HTB_HAS_CBUFFER) {
+ double b;
+ char *bu;
+
+ b = nl_cancel_down_bytes(htb->ch_cbuffer, &bu);
+ nl_dump(p, " cbuffer %.2f%s", b, bu);
+ }
+
+ if (htb->ch_mask & SCH_HTB_HAS_QUANTUM)
+ nl_dump(p, " quantum %u", htb->ch_quantum);
+}
+
+static int htb_qdisc_msg_fill(struct rtnl_tc *tc, void *data,
+ struct nl_msg *msg)
+{
+ struct rtnl_htb_qdisc *htb = data;
+ struct tc_htb_glob opts = {
+ .version = TC_HTB_PROTOVER,
+ .rate2quantum = 10,
+ };
+
+ if (htb) {
+ if (htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
+ opts.rate2quantum = htb->qh_rate2quantum;
+
+ if (htb->qh_mask & SCH_HTB_HAS_DEFCLS)
+ opts.defcls = htb->qh_defcls;
+ }
+
+ return nla_put(msg, TCA_HTB_INIT, sizeof(opts), &opts);
+}
+
+static int htb_class_msg_fill(struct rtnl_tc *tc, void *data,
+ struct nl_msg *msg)
+{
+ struct rtnl_htb_class *htb = data;
+ uint32_t mtu, rtable[RTNL_TC_RTABLE_SIZE], ctable[RTNL_TC_RTABLE_SIZE];
+ struct tc_htb_opt opts;
+ int buffer, cbuffer;
+
+ if (!htb || !(htb->ch_mask & SCH_HTB_HAS_RATE))
+ BUG();
+
+ memset(&opts, 0, sizeof(opts));
+
+ /* if not set, zero (0) is used as priority */
+ if (htb->ch_mask & SCH_HTB_HAS_PRIO)
+ opts.prio = htb->ch_prio;
+
+ mtu = rtnl_tc_get_mtu(tc);
+
+ rtnl_tc_build_rate_table(tc, &htb->ch_rate, rtable);
+ rtnl_rcopy_ratespec(&opts.rate, &htb->ch_rate);
+
+ if (htb->ch_mask & SCH_HTB_HAS_CEIL) {
+ rtnl_tc_build_rate_table(tc, &htb->ch_ceil, ctable);
+ rtnl_rcopy_ratespec(&opts.ceil, &htb->ch_ceil);
+ } else {
+ /*
+ * If not set, configured rate is used as ceil, which implies
+ * no borrowing.
+ */
+ memcpy(&opts.ceil, &opts.rate, sizeof(struct tc_ratespec));
+ }
+
+ if (htb->ch_mask & SCH_HTB_HAS_RBUFFER)
+ buffer = htb->ch_rbuffer;
+ else
+ buffer = opts.rate.rate / nl_get_psched_hz() + mtu; /* XXX */
+
+ opts.buffer = nl_us2ticks(rtnl_tc_calc_txtime(buffer, opts.rate.rate));
+
+ if (htb->ch_mask & SCH_HTB_HAS_CBUFFER)
+ cbuffer = htb->ch_cbuffer;
+ else
+ cbuffer = opts.ceil.rate / nl_get_psched_hz() + mtu; /* XXX */
+
+ opts.cbuffer = nl_us2ticks(rtnl_tc_calc_txtime(cbuffer, opts.ceil.rate));
+
+ if (htb->ch_mask & SCH_HTB_HAS_QUANTUM)
+ opts.quantum = htb->ch_quantum;
+
+ NLA_PUT(msg, TCA_HTB_PARMS, sizeof(opts), &opts);
+ NLA_PUT(msg, TCA_HTB_RTAB, sizeof(rtable), &rtable);
+ NLA_PUT(msg, TCA_HTB_CTAB, sizeof(ctable), &ctable);
+
+ return 0;
+
+nla_put_failure:
+ return -NLE_MSGSIZE;
+}
+
+static struct rtnl_tc_ops htb_qdisc_ops;
+static struct rtnl_tc_ops htb_class_ops;
+
+static struct rtnl_htb_qdisc *htb_qdisc_data(struct rtnl_qdisc *qdisc)
+{
+ return rtnl_tc_data_check(TC_CAST(qdisc), &htb_qdisc_ops);
+}
+
+static struct rtnl_htb_class *htb_class_data(struct rtnl_class *class)
+{
+ return rtnl_tc_data_check(TC_CAST(class), &htb_class_ops);
+}
+
+/**
+ * @name Attribute Modifications
+ * @{
+ */
+
+/**
+ * Return rate/quantum ratio of HTB qdisc
+ * @arg qdisc htb qdisc object
+ *
+ * @return rate/quantum ratio or 0 if unspecified
+ */
+uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_htb_qdisc *htb;
+
+ if ((htb = htb_qdisc_data(qdisc)) &&
+ htb->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
+ return htb->qh_rate2quantum;
+
+ return 0;
+}
+
+int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *qdisc, uint32_t rate2quantum)
+{
+ struct rtnl_htb_qdisc *htb;
+
+ if (!(htb = htb_qdisc_data(qdisc)))
+ return -NLE_OPNOTSUPP;
+
+ htb->qh_rate2quantum = rate2quantum;
+ htb->qh_mask |= SCH_HTB_HAS_RATE2QUANTUM;
+
+ return 0;
+}
+
+/**
+ * Return default class of HTB qdisc
+ * @arg qdisc htb qdisc object
+ *
+ * Returns the classid of the class where all unclassified traffic
+ * goes to.
+ *
+ * @return classid or TC_H_UNSPEC if unspecified.
+ */
+uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_htb_qdisc *htb;
+
+ if ((htb = htb_qdisc_data(qdisc)) &&
+ htb->qh_mask & SCH_HTB_HAS_DEFCLS)
+ return htb->qh_defcls;
+
+ return TC_H_UNSPEC;
+}
+
+/**
+ * Set default class of the htb qdisc to the specified value
+ * @arg qdisc qdisc to change
+ * @arg defcls new default class
+ */
+int rtnl_htb_set_defcls(struct rtnl_qdisc *qdisc, uint32_t defcls)
+{
+ struct rtnl_htb_qdisc *htb;
+
+ if (!(htb = htb_qdisc_data(qdisc)))
+ return -NLE_OPNOTSUPP;
+
+ htb->qh_defcls = defcls;
+ htb->qh_mask |= SCH_HTB_HAS_DEFCLS;
+
+ return 0;
+}
+
+uint32_t rtnl_htb_get_prio(struct rtnl_class *class)
+{
+ struct rtnl_htb_class *htb;
+
+ if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_PRIO)
+ return htb->ch_prio;
+
+ return 0;
+}
+
+int rtnl_htb_set_prio(struct rtnl_class *class, uint32_t prio)
+{
+ struct rtnl_htb_class *htb;
+
+ if (!(htb = htb_class_data(class)))
+ return -NLE_OPNOTSUPP;
+
+ htb->ch_prio = prio;
+ htb->ch_mask |= SCH_HTB_HAS_PRIO;
+
+ return 0;
+}
+
+/**
+ * Return rate of HTB class
+ * @arg class htb class object
+ *
+ * @return Rate in bytes/s or 0 if unspecified.
+ */
+uint32_t rtnl_htb_get_rate(struct rtnl_class *class)
+{
+ struct rtnl_htb_class *htb;
+
+ if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_RATE)
+ return htb->ch_rate.rs_rate;
+
+ return 0;
+}
+
+/**
+ * Set rate of HTB class
+ * @arg class htb class object
+ * @arg rate new rate in bytes per second
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_htb_set_rate(struct rtnl_class *class, uint32_t rate)
+{
+ struct rtnl_htb_class *htb;
+
+ if (!(htb = htb_class_data(class)))
+ return -NLE_OPNOTSUPP;
+
+ htb->ch_rate.rs_cell_log = UINT8_MAX; /* use default value */
+ htb->ch_rate.rs_rate = rate;
+ htb->ch_mask |= SCH_HTB_HAS_RATE;
+
+ return 0;
+}
+
+/**
+ * Return ceil rate of HTB class
+ * @arg class htb class object
+ *
+ * @return Ceil rate in bytes/s or 0 if unspecified
+ */
+uint32_t rtnl_htb_get_ceil(struct rtnl_class *class)
+{
+ struct rtnl_htb_class *htb;
+
+ if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_CEIL)
+ return htb->ch_ceil.rs_rate;
+
+ return 0;
+}
+
+/**
+ * Set ceil rate of HTB class
+ * @arg class htb class object
+ * @arg ceil new ceil rate number of bytes per second
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_htb_set_ceil(struct rtnl_class *class, uint32_t ceil)
+{
+ struct rtnl_htb_class *htb;
+
+ if (!(htb = htb_class_data(class)))
+ return -NLE_OPNOTSUPP;
+
+ htb->ch_ceil.rs_cell_log = UINT8_MAX; /* use default value */
+ htb->ch_ceil.rs_rate = ceil;
+ htb->ch_mask |= SCH_HTB_HAS_CEIL;
+
+ return 0;
+}
+
+/**
+ * Return burst buffer size of HTB class
+ * @arg class htb class object
+ *
+ * @return Burst buffer size or 0 if unspecified
+ */
+uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *class)
+{
+ struct rtnl_htb_class *htb;
+
+ if ((htb = htb_class_data(class)) &&
+ htb->ch_mask & SCH_HTB_HAS_RBUFFER)
+ return htb->ch_rbuffer;
+
+ return 0;
+}
+
+/**
+ * Set size of the rate bucket of HTB class.
+ * @arg class HTB class to be modified.
+ * @arg rbuffer New size in bytes.
+ */
+int rtnl_htb_set_rbuffer(struct rtnl_class *class, uint32_t rbuffer)
+{
+ struct rtnl_htb_class *htb;
+
+ if (!(htb = htb_class_data(class)))
+ return -NLE_OPNOTSUPP;
+
+ htb->ch_rbuffer = rbuffer;
+ htb->ch_mask |= SCH_HTB_HAS_RBUFFER;
+
+ return 0;
+}
+
+/**
+ * Return ceil burst buffer size of HTB class
+ * @arg class htb class object
+ *
+ * @return Ceil burst buffer size or 0 if unspecified
+ */
+uint32_t rtnl_htb_get_cbuffer(struct rtnl_class *class)
+{
+ struct rtnl_htb_class *htb;
+
+ if ((htb = htb_class_data(class)) &&
+ htb->ch_mask & SCH_HTB_HAS_CBUFFER)
+ return htb->ch_cbuffer;
+
+ return 0;
+}
+
+/**
+ * Set size of the ceil bucket of HTB class.
+ * @arg class HTB class to be modified.
+ * @arg cbuffer New size in bytes.
+ */
+int rtnl_htb_set_cbuffer(struct rtnl_class *class, uint32_t cbuffer)
+{
+ struct rtnl_htb_class *htb;
+
+ if (!(htb = htb_class_data(class)))
+ return -NLE_OPNOTSUPP;
+
+ htb->ch_cbuffer = cbuffer;
+ htb->ch_mask |= SCH_HTB_HAS_CBUFFER;
+
+ return 0;
+}
+
+/**
+ * Return quantum of HTB class
+ * @arg class htb class object
+ *
+ * See XXX[quantum def]
+ *
+ * @return Quantum or 0 if unspecified.
+ */
+uint32_t rtnl_htb_get_quantum(struct rtnl_class *class)
+{
+ struct rtnl_htb_class *htb;
+
+ if ((htb = htb_class_data(class)) &&
+ htb->ch_mask & SCH_HTB_HAS_QUANTUM)
+ return htb->ch_quantum;
+
+ return 0;
+}
+
+/**
+ * Set quantum of HTB class (overwrites value calculated based on r2q)
+ * @arg class htb class object
+ * @arg quantum new quantum in number of bytes
+ *
+ * See XXX[quantum def]
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_htb_set_quantum(struct rtnl_class *class, uint32_t quantum)
+{
+ struct rtnl_htb_class *htb;
+
+ if (!(htb = htb_class_data(class)))
+ return -NLE_OPNOTSUPP;
+
+ htb->ch_quantum = quantum;
+ htb->ch_mask |= SCH_HTB_HAS_QUANTUM;
+
+ return 0;
+}
+
+/**
+ * Return level of HTB class
+ * @arg class htb class object
+ *
+ * Returns the level of the HTB class. Leaf classes are assigned level
+ * 0, root classes have level (TC_HTB_MAXDEPTH - 1). Interior classes
+ * have a level of one less than their parent.
+ *
+ * @return Level or -NLE_OPNOTSUPP
+ */
+int rtnl_htb_get_level(struct rtnl_class *class)
+{
+ struct rtnl_htb_class *htb;
+
+ if ((htb = htb_class_data(class)) && htb->ch_mask & SCH_HTB_HAS_LEVEL)
+ return htb->ch_level;
+
+ return -NLE_OPNOTSUPP;
+}
+
+/**
+ * Set level of HTB class
+ * @arg class htb class object
+ * @arg level new level of HTB class
+ *
+ * Sets the level of a HTB class. Note that changing the level of a HTB
+ * class does not change the level of its in kernel counterpart. This
+ * function is provided only to create HTB objects which can be compared
+ * against or filtered upon.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_htb_set_level(struct rtnl_class *class, int level)
+{
+ struct rtnl_htb_class *htb;
+
+ if (!(htb = htb_class_data(class)))
+ return -NLE_OPNOTSUPP;
+
+ htb->ch_level = level;
+ htb->ch_mask |= SCH_HTB_HAS_LEVEL;
+
+ return 0;
+}
+
+/** @} */
+
+static struct rtnl_tc_ops htb_qdisc_ops = {
+ .to_kind = "htb",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_htb_qdisc),
+ .to_msg_parser = htb_qdisc_msg_parser,
+ .to_dump[NL_DUMP_LINE] = htb_qdisc_dump_line,
+ .to_msg_fill = htb_qdisc_msg_fill,
+};
+
+static struct rtnl_tc_ops htb_class_ops = {
+ .to_kind = "htb",
+ .to_type = RTNL_TC_TYPE_CLASS,
+ .to_size = sizeof(struct rtnl_htb_class),
+ .to_msg_parser = htb_class_msg_parser,
+ .to_dump = {
+ [NL_DUMP_LINE] = htb_class_dump_line,
+ [NL_DUMP_DETAILS] = htb_class_dump_details,
+ },
+ .to_msg_fill = htb_class_msg_fill,
+};
+
+static void __init htb_init(void)
+{
+ rtnl_tc_register(&htb_qdisc_ops);
+ rtnl_tc_register(&htb_class_ops);
+}
+
+static void __exit htb_exit(void)
+{
+ rtnl_tc_unregister(&htb_qdisc_ops);
+ rtnl_tc_unregister(&htb_class_ops);
+}
+
+/** @} */
diff --git a/lib/route/qdisc/ingress.c b/lib/route/qdisc/ingress.c
new file mode 100644
index 00000000..1a63f364
--- /dev/null
+++ b/lib/route/qdisc/ingress.c
@@ -0,0 +1,64 @@
+/*
+ * lib/route/qdisc/ingress.c ingress
+ *
+ * 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) 2013 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+/**
+ * @ingroup qdisc
+ * @defgroup qdisc_ingress Ingress qdisc
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/utils.h>
+
+struct dumb {
+ uint32_t foo;
+};
+
+static int dumb_msg_parser(struct rtnl_tc *tc, void *data)
+{
+ return 0;
+}
+
+static void dumb_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
+{
+}
+
+static int dumb_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+ return 0;
+}
+
+static struct rtnl_tc_ops ingress_ops = {
+ .to_kind = "ingress",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct dumb),
+ .to_msg_parser = dumb_msg_parser,
+ .to_dump[NL_DUMP_LINE] = dumb_dump_line,
+ .to_msg_fill = dumb_msg_fill,
+};
+
+static void __init ingress_init(void)
+{
+ rtnl_tc_register(&ingress_ops);
+}
+
+static void __exit ingress_exit(void)
+{
+ rtnl_tc_unregister(&ingress_ops);
+}
+
+/** @} */
diff --git a/lib/route/sch/netem.c b/lib/route/qdisc/netem.c
index 18878a7a..06d9fe8c 100644
--- a/lib/route/sch/netem.c
+++ b/lib/route/qdisc/netem.c
@@ -1,30 +1,30 @@
/*
- * lib/route/sch/netem.c Network Emulator Qdisc
+ * lib/route/qdisc/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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup qdisc_api
- * @defgroup netem Network Emulator
+ * @ingroup qdisc
+ * @defgroup qdisc_netem Network Emulator
* @brief
*
* For further documentation see http://linux-net.osdl.org/index.php/Netem
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/qdisc.h>
-#include <netlink/route/qdisc-modules.h>
-#include <netlink/route/sch/netem.h>
+#include <netlink/route/qdisc/netem.h>
/** @cond SKIP */
#define SCH_NETEM_ATTR_LATENCY 0x0001
@@ -43,39 +43,22 @@
#define SCH_NETEM_ATTR_DIST 0x2000
/** @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) },
[TCA_NETEM_CORRUPT] = { .minlen = sizeof(struct tc_netem_corrupt) },
};
-static int netem_msg_parser(struct rtnl_qdisc *qdisc)
+static int netem_msg_parser(struct rtnl_tc *tc, void *data)
{
- int len, err = 0;
- struct rtnl_netem *netem;
+ struct rtnl_netem *netem = data;
struct tc_netem_qopt *opts;
+ int len, err = 0;
- if (qdisc->q_opts->d_size < sizeof(*opts))
+ if (tc->tc_opts->d_size < sizeof(*opts))
return -NLE_INVAL;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
-
- opts = (struct tc_netem_qopt *) qdisc->q_opts->d_data;
+ opts = (struct tc_netem_qopt *) tc->tc_opts->d_data;
netem->qnm_latency = opts->latency;
netem->qnm_limit = opts->limit;
netem->qnm_loss = opts->loss;
@@ -87,13 +70,13 @@ static int netem_msg_parser(struct rtnl_qdisc *qdisc)
SCH_NETEM_ATTR_LOSS | SCH_NETEM_ATTR_GAP |
SCH_NETEM_ATTR_DUPLICATE | SCH_NETEM_ATTR_JITTER);
- len = qdisc->q_opts->d_size - sizeof(*opts);
+ len = tc->tc_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)),
+ (tc->tc_opts->d_data + sizeof(*opts)),
len, netem_policy);
if (err < 0) {
free(netem);
@@ -143,52 +126,46 @@ static int netem_msg_parser(struct rtnl_qdisc *qdisc)
return 0;
}
-static void netem_free_data(struct rtnl_qdisc *qdisc)
+static void netem_free_data(struct rtnl_tc *tc, void *data)
{
- struct rtnl_netem *netem;
-
- if ( ! qdisc ) return;
-
- netem = netem_qdisc(qdisc);
- if ( ! netem ) return;
+ struct rtnl_netem *netem = data;
- if ( netem->qnm_dist.dist_data )
- free(netem->qnm_dist.dist_data);
-
- netem = NULL;
+ if (!netem)
+ return;
- free (qdisc->q_subdata);
+ free(netem->qnm_dist.dist_data);
}
-static void netem_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
+static void netem_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_netem *netem = netem_qdisc(qdisc);
+ struct rtnl_netem *netem = data;
if (netem)
nl_dump(p, "limit %d", netem->qnm_limit);
}
-int netem_build_msg(struct rtnl_qdisc *qdisc, struct nl_msg *msg)
+static int netem_msg_fill_raw(struct rtnl_tc *tc, void *data,
+ struct nl_msg *msg)
{
int err = 0;
struct tc_netem_qopt opts;
struct tc_netem_corr cor;
struct tc_netem_reorder reorder;
struct tc_netem_corrupt corrupt;
- struct rtnl_netem *netem;
+ struct rtnl_netem *netem = data;
unsigned char set_correlation = 0, set_reorder = 0,
set_corrupt = 0, set_dist = 0;
+ if (!netem)
+ BUG();
+
memset(&opts, 0, sizeof(opts));
memset(&cor, 0, sizeof(cor));
memset(&reorder, 0, sizeof(reorder));
memset(&corrupt, 0, sizeof(corrupt));
- netem = netem_qdisc(qdisc);
- if (!netem || !msg)
- return EFAULT;
-
msg->nm_nlh->nlmsg_flags |= NLM_F_REQUEST;
if ( netem->qnm_ro.nmro_probability != 0 ) {
@@ -316,18 +293,15 @@ nla_put_failure:
* @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)
+void rtnl_netem_set_limit(struct rtnl_qdisc *qdisc, int limit)
{
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
netem->qnm_limit = limit;
netem->qnm_mask |= SCH_NETEM_ATTR_LIMIT;
-
- return 0;
}
/**
@@ -339,8 +313,10 @@ 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))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_LIMIT)
return netem->qnm_limit;
else
return -NLE_NOATTR;
@@ -359,18 +335,15 @@ int rtnl_netem_get_limit(struct rtnl_qdisc *qdisc)
* @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)
+void rtnl_netem_set_gap(struct rtnl_qdisc *qdisc, int gap)
{
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
netem->qnm_gap = gap;
netem->qnm_mask |= SCH_NETEM_ATTR_GAP;
-
- return 0;
}
/**
@@ -382,8 +355,10 @@ 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))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_GAP)
return netem->qnm_gap;
else
return -NLE_NOATTR;
@@ -395,18 +370,15 @@ int rtnl_netem_get_gap(struct rtnl_qdisc *qdisc)
* @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)
+void rtnl_netem_set_reorder_probability(struct rtnl_qdisc *qdisc, int prob)
{
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
netem->qnm_ro.nmro_probability = prob;
netem->qnm_mask |= SCH_NETEM_ATTR_RO_PROB;
-
- return 0;
}
/**
@@ -418,8 +390,10 @@ 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))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_RO_PROB)
return netem->qnm_ro.nmro_probability;
else
return -NLE_NOATTR;
@@ -431,18 +405,15 @@ int rtnl_netem_get_reorder_probability(struct rtnl_qdisc *qdisc)
* @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)
+void rtnl_netem_set_reorder_correlation(struct rtnl_qdisc *qdisc, int prob)
{
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
netem->qnm_ro.nmro_correlation = prob;
netem->qnm_mask |= SCH_NETEM_ATTR_RO_CORR;
-
- return 0;
}
/**
@@ -454,8 +425,10 @@ 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))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_RO_CORR)
return netem->qnm_ro.nmro_correlation;
else
return -NLE_NOATTR;
@@ -474,18 +447,15 @@ int rtnl_netem_get_reorder_correlation(struct rtnl_qdisc *qdisc)
* @arg prob New corruption probability.
* @return 0 on success or a negative error code.
*/
-int rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob)
+void rtnl_netem_set_corruption_probability(struct rtnl_qdisc *qdisc, int prob)
{
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
netem->qnm_crpt.nmcr_probability = prob;
netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_PROB;
-
- return 0;
}
/**
@@ -497,8 +467,10 @@ int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc)
{
struct rtnl_netem *netem;
- netem = netem_qdisc(qdisc);
- if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_PROB)
return netem->qnm_crpt.nmcr_probability;
else
return -NLE_NOATTR;
@@ -510,18 +482,15 @@ int rtnl_netem_get_corruption_probability(struct rtnl_qdisc *qdisc)
* @arg prob New corruption correlation probability.
* @return 0 on success or a negative error code.
*/
-int rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob)
+void rtnl_netem_set_corruption_correlation(struct rtnl_qdisc *qdisc, int prob)
{
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
netem->qnm_crpt.nmcr_correlation = prob;
netem->qnm_mask |= SCH_NETEM_ATTR_CORRUPT_CORR;
-
- return 0;
}
/**
@@ -533,8 +502,10 @@ int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc)
{
struct rtnl_netem *netem;
- netem = netem_qdisc(qdisc);
- if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_CORRUPT_CORR)
return netem->qnm_crpt.nmcr_correlation;
else
return -NLE_NOATTR;
@@ -553,18 +524,15 @@ int rtnl_netem_get_corruption_correlation(struct rtnl_qdisc *qdisc)
* @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)
+void rtnl_netem_set_loss(struct rtnl_qdisc *qdisc, int prob)
{
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
netem->qnm_loss = prob;
netem->qnm_mask |= SCH_NETEM_ATTR_LOSS;
-
- return 0;
}
/**
@@ -576,8 +544,10 @@ 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))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS)
return netem->qnm_loss;
else
return -NLE_NOATTR;
@@ -589,18 +559,15 @@ int rtnl_netem_get_loss(struct rtnl_qdisc *qdisc)
* @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)
+void rtnl_netem_set_loss_correlation(struct rtnl_qdisc *qdisc, int prob)
{
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
netem->qnm_corr.nmc_loss = prob;
netem->qnm_mask |= SCH_NETEM_ATTR_LOSS_CORR;
-
- return 0;
}
/**
@@ -612,8 +579,10 @@ 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))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_LOSS_CORR)
return netem->qnm_corr.nmc_loss;
else
return -NLE_NOATTR;
@@ -632,18 +601,15 @@ int rtnl_netem_get_loss_correlation(struct rtnl_qdisc *qdisc)
* @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)
+void rtnl_netem_set_duplicate(struct rtnl_qdisc *qdisc, int prob)
{
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
netem->qnm_duplicate = prob;
netem->qnm_mask |= SCH_NETEM_ATTR_DUPLICATE;
-
- return 0;
}
/**
@@ -655,8 +621,10 @@ 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))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_DUPLICATE)
return netem->qnm_duplicate;
else
return -NLE_NOATTR;
@@ -668,18 +636,15 @@ int rtnl_netem_get_duplicate(struct rtnl_qdisc *qdisc)
* @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)
+void rtnl_netem_set_duplicate_correlation(struct rtnl_qdisc *qdisc, int prob)
{
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
netem->qnm_corr.nmc_duplicate = prob;
netem->qnm_mask |= SCH_NETEM_ATTR_DUP_CORR;
-
- return 0;
}
/**
@@ -691,8 +656,10 @@ 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))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_DUP_CORR)
return netem->qnm_corr.nmc_duplicate;
else
return -NLE_NOATTR;
@@ -711,18 +678,15 @@ int rtnl_netem_get_duplicate_correlation(struct rtnl_qdisc *qdisc)
* @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)
+void rtnl_netem_set_delay(struct rtnl_qdisc *qdisc, int delay)
{
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
netem->qnm_latency = nl_us2ticks(delay);
netem->qnm_mask |= SCH_NETEM_ATTR_LATENCY;
-
- return 0;
}
/**
@@ -734,8 +698,10 @@ 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))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_LATENCY)
return nl_ticks2us(netem->qnm_latency);
else
return -NLE_NOATTR;
@@ -747,18 +713,15 @@ int rtnl_netem_get_delay(struct rtnl_qdisc *qdisc)
* @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)
+void rtnl_netem_set_jitter(struct rtnl_qdisc *qdisc, int jitter)
{
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
netem->qnm_jitter = nl_us2ticks(jitter);
netem->qnm_mask |= SCH_NETEM_ATTR_JITTER;
-
- return 0;
}
/**
@@ -770,8 +733,10 @@ 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))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_JITTER)
return nl_ticks2us(netem->qnm_jitter);
else
return -NLE_NOATTR;
@@ -782,18 +747,15 @@ int rtnl_netem_get_jitter(struct rtnl_qdisc *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)
+void rtnl_netem_set_delay_correlation(struct rtnl_qdisc *qdisc, int prob)
{
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
netem->qnm_corr.nmc_delay = prob;
netem->qnm_mask |= SCH_NETEM_ATTR_DELAY_CORR;
-
- return 0;
}
/**
@@ -805,8 +767,10 @@ 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))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_DELAY_CORR)
return netem->qnm_corr.nmc_delay;
else
return -NLE_NOATTR;
@@ -821,8 +785,10 @@ int rtnl_netem_get_delay_distribution_size(struct rtnl_qdisc *qdisc)
{
struct rtnl_netem *netem;
- netem = netem_qdisc(qdisc);
- if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST))
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_DIST)
return netem->qnm_dist.dist_size;
else
return -NLE_NOATTR;
@@ -838,12 +804,13 @@ int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_p
{
struct rtnl_netem *netem;
- netem = netem_qdisc(qdisc);
- if (netem && (netem->qnm_mask & SCH_NETEM_ATTR_DIST)) {
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (netem->qnm_mask & SCH_NETEM_ATTR_DIST) {
*dist_ptr = netem->qnm_dist.dist_data;
return 0;
- }
- else
+ } else
return -NLE_NOATTR;
}
@@ -856,12 +823,12 @@ int rtnl_netem_get_delay_distribution(struct rtnl_qdisc *qdisc, int16_t **dist_p
int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist_type) {
struct rtnl_netem *netem;
- netem = netem_alloc(qdisc);
- if (!netem)
- return -NLE_NOMEM;
+ if (!(netem = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
- FILE *f = NULL;
- int i, n = 0;
+ FILE *f;
+ int n = 0;
+ size_t i;
size_t len = 2048;
char *line;
char name[NAME_MAX];
@@ -875,9 +842,10 @@ int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist
/* Check several locations for the dist file */
char *test_path[] = { "", "./", "/usr/lib/tc/", "/usr/local/lib/tc/" };
- for (i = 0; i < sizeof(test_path) && f == NULL; i++) {
+ for (i = 0; i < ARRAY_SIZE(test_path); i++) {
snprintf(name, NAME_MAX, "%s%s%s", test_path[i], dist_type, dist_suffix);
- f = fopen(name, "r");
+ if ((f = fopen(name, "r")))
+ break;
}
if ( f == NULL )
@@ -917,23 +885,24 @@ int rtnl_netem_set_delay_distribution(struct rtnl_qdisc *qdisc, const char *dist
/** @} */
-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_LINE] = netem_dump_line,
- .qo_get_opts = 0,
- .qo_build_msg = netem_build_msg
+static struct rtnl_tc_ops netem_ops = {
+ .to_kind = "netem",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_netem),
+ .to_msg_parser = netem_msg_parser,
+ .to_free_data = netem_free_data,
+ .to_dump[NL_DUMP_LINE] = netem_dump_line,
+ .to_msg_fill_raw = netem_msg_fill_raw,
};
static void __init netem_init(void)
{
- rtnl_qdisc_register(&netem_ops);
+ rtnl_tc_register(&netem_ops);
}
static void __exit netem_exit(void)
{
- rtnl_qdisc_unregister(&netem_ops);
+ rtnl_tc_unregister(&netem_ops);
}
/** @} */
diff --git a/lib/route/qdisc/plug.c b/lib/route/qdisc/plug.c
new file mode 100644
index 00000000..9f536375
--- /dev/null
+++ b/lib/route/qdisc/plug.c
@@ -0,0 +1,177 @@
+/*
+ * lib/route/qdisc/plug.c PLUG 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) 2012 Shriram Rajagopalan <rshriram@cs.ubc.ca>
+ */
+
+/**
+ * @ingroup qdisc
+ * @defgroup qdisc_plug Plug/Unplug Traffic (PLUG)
+ * @brief
+ *
+ * Queue traffic until an explicit release command.
+ *
+ * There are two ways to use this qdisc:
+ * 1. A simple "instantaneous" plug/unplug operation, by issuing an alternating
+ * sequence of TCQ_PLUG_BUFFER & TCQ_PLUG_RELEASE_INDEFINITE commands.
+ *
+ * 2. For network output buffering (a.k.a output commit) functionality.
+ * Output commit property is commonly used by applications using checkpoint
+ * based fault-tolerance to ensure that the checkpoint from which a system
+ * is being restored is consistent w.r.t outside world.
+ *
+ * Consider for e.g. Remus - a Virtual Machine checkpointing system,
+ * wherein a VM is checkpointed, say every 50ms. The checkpoint is replicated
+ * asynchronously to the backup host, while the VM continues executing the
+ * next epoch speculatively.
+ *
+ * The following is a typical sequence of output buffer operations:
+ * 1.At epoch i, start_buffer(i)
+ * 2. At end of epoch i (i.e. after 50ms):
+ * 2.1 Stop VM and take checkpoint(i).
+ * 2.2 start_buffer(i+1) and Resume VM
+ * 3. While speculatively executing epoch(i+1), asynchronously replicate
+ * checkpoint(i) to backup host.
+ * 4. When checkpoint_ack(i) is received from backup, release_buffer(i)
+ * Thus, this Qdisc would receive the following sequence of commands:
+ * TCQ_PLUG_BUFFER (epoch i)
+ * .. TCQ_PLUG_BUFFER (epoch i+1)
+ * ....TCQ_PLUG_RELEASE_ONE (epoch i)
+ * ......TCQ_PLUG_BUFFER (epoch i+2)
+ * ........
+ *
+ *
+ * State of the queue, when used for network output buffering:
+ *
+ * plug(i+1) plug(i) head
+ * ------------------+--------------------+---------------->
+ * | |
+ * | |
+ * pkts_current_epoch| pkts_last_epoch |pkts_to_release
+ * ----------------->|<--------+--------->|+--------------->
+ * v v
+ *
+ *
+ * @{
+ */
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
+#include <netlink/route/qdisc/plug.h>
+
+static int plug_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
+{
+ struct rtnl_plug *plug = data;
+ struct tc_plug_qopt opts;
+
+ if (!plug)
+ return -NLE_INVAL;
+
+ opts.action = plug->action;
+ opts.limit = plug->limit;
+
+ return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD);
+}
+
+/**
+ * @name Attribute Modification
+ * @{
+ */
+
+/**
+ * Insert a plug into the qdisc and buffer any incoming
+ * network traffic.
+ * @arg qdisc PLUG qdisc to be modified.
+ */
+int rtnl_qdisc_plug_buffer(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_plug *plug;
+
+ if (!(plug = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ plug->action = TCQ_PLUG_BUFFER;
+ return 0;
+}
+
+/**
+ * Unplug the qdisc, releasing packets from queue head
+ * to the last complete buffer, while new traffic
+ * continues to be buffered.
+ * @arg qdisc PLUG qdisc to be modified.
+ */
+int rtnl_qdisc_plug_release_one(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_plug *plug;
+
+ if (!(plug = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ plug->action = TCQ_PLUG_RELEASE_ONE;
+ return 0;
+}
+
+/**
+ * Indefinitely unplug the qdisc, releasing all packets.
+ * Network traffic will not be buffered until the next
+ * buffer command is issued.
+ * @arg qdisc PLUG qdisc to be modified.
+ */
+int rtnl_qdisc_plug_release_indefinite(struct rtnl_qdisc *qdisc)
+{
+ struct rtnl_plug *plug;
+
+ if (!(plug = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ plug->action = TCQ_PLUG_RELEASE_INDEFINITE;
+ return 0;
+}
+
+/**
+ * Set limit of PLUG qdisc.
+ * @arg qdisc PLUG qdisc to be modified.
+ * @arg limit New limit.
+ * @return 0 on success or a negative error code.
+ */
+int rtnl_qdisc_plug_set_limit(struct rtnl_qdisc *qdisc, int limit)
+{
+ struct rtnl_plug *plug;
+
+ if (!(plug = rtnl_tc_data(TC_CAST(qdisc))))
+ return -NLE_NOMEM;
+
+ plug->action = TCQ_PLUG_LIMIT;
+ plug->limit = limit;
+
+ return 0;
+}
+
+/** @} */
+
+static struct rtnl_tc_ops plug_ops = {
+ .to_kind = "plug",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_plug),
+ .to_msg_fill = plug_msg_fill,
+};
+
+static void __init plug_init(void)
+{
+ rtnl_tc_register(&plug_ops);
+}
+
+static void __exit plug_exit(void)
+{
+ rtnl_tc_unregister(&plug_ops);
+}
+
+/** @} */
diff --git a/lib/route/sch/prio.c b/lib/route/qdisc/prio.c
index 4c9ebcff..54a46f01 100644
--- a/lib/route/sch/prio.c
+++ b/lib/route/qdisc/prio.c
@@ -1,17 +1,17 @@
/*
- * lib/route/sch/prio.c PRIO Qdisc/Class
+ * lib/route/qdisc/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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup qdisc_api
- * @defgroup prio (Fast) Prio
+ * @ingroup qdisc
+ * @defgroup qdisc_prio (Fast) Prio
* @brief
*
* @par 1) Typical PRIO configuration
@@ -26,45 +26,28 @@
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/qdisc.h>
-#include <netlink/route/qdisc-modules.h>
-#include <netlink/route/sch/prio.h>
+#include <netlink/route/qdisc/prio.h>
/** @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)
+static int prio_msg_parser(struct rtnl_tc *tc, void *data)
{
- 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 rtnl_prio *prio = data;
struct tc_prio_qopt *opt;
- if (qdisc->q_opts->d_size < sizeof(*opt))
+ if (tc->tc_opts->d_size < sizeof(*opt))
return -NLE_INVAL;
- prio = prio_alloc(qdisc);
- if (!prio)
- return -NLE_NOMEM;
-
- opt = (struct tc_prio_qopt *) qdisc->q_opts->d_data;
+ opt = (struct tc_prio_qopt *) tc->tc_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);
@@ -72,22 +55,19 @@ static int prio_msg_parser(struct rtnl_qdisc *qdisc)
return 0;
}
-static void prio_free_data(struct rtnl_qdisc *qdisc)
-{
- free(qdisc->q_subdata);
-}
-
-static void prio_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
+static void prio_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_prio *prio = prio_qdisc(qdisc);
+ struct rtnl_prio *prio = data;
if (prio)
nl_dump(p, " bands %u", prio->qp_bands);
}
-static void prio_dump_details(struct rtnl_qdisc *qdisc,struct nl_dump_params *p)
+static void prio_dump_details(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_prio *prio = prio_qdisc(qdisc);
+ struct rtnl_prio *prio = data;
int i, hp;
if (!prio)
@@ -121,32 +101,18 @@ static void prio_dump_details(struct rtnl_qdisc *qdisc,struct nl_dump_params *p)
}
}
-static struct nl_msg *prio_get_opts(struct rtnl_qdisc *qdisc)
+static int prio_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
{
- struct rtnl_prio *prio;
+ struct rtnl_prio *prio = data;
struct tc_prio_qopt opts;
- struct nl_msg *msg;
- prio = prio_qdisc(qdisc);
- if (!prio ||
- !(prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP))
- goto errout;
+ if (!prio || !(prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP))
+ BUG();
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;
+ return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD);
}
/**
@@ -160,18 +126,15 @@ errout:
* @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)
+void rtnl_qdisc_prio_set_bands(struct rtnl_qdisc *qdisc, int bands)
{
struct rtnl_prio *prio;
-
- prio = prio_alloc(qdisc);
- if (!prio)
- return -NLE_NOMEM;
+
+ if (!(prio = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
prio->qp_bands = bands;
prio->qp_mask |= SCH_PRIO_ATTR_BANDS;
-
- return 0;
}
/**
@@ -183,8 +146,10 @@ 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)
+ if (!(prio = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (prio->qp_mask & SCH_PRIO_ATTR_BANDS)
return prio->qp_bands;
else
return -NLE_NOMEM;
@@ -203,9 +168,8 @@ int rtnl_qdisc_prio_set_priomap(struct rtnl_qdisc *qdisc, uint8_t priomap[],
struct rtnl_prio *prio;
int i;
- prio = prio_alloc(qdisc);
- if (!prio)
- return -NLE_NOMEM;
+ if (!(prio = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
if (!(prio->qp_mask & SCH_PRIO_ATTR_BANDS))
return -NLE_MISSING_ATTR;
@@ -234,8 +198,10 @@ 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)
+ if (!(prio = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (prio->qp_mask & SCH_PRIO_ATTR_PRIOMAP)
return prio->qp_priomap;
else
return NULL;
@@ -248,7 +214,7 @@ uint8_t *rtnl_qdisc_prio_get_priomap(struct rtnl_qdisc *qdisc)
* @{
*/
-static struct trans_tbl prios[] = {
+static const struct trans_tbl prios[] = {
__ADD(TC_PRIO_BESTEFFORT,besteffort)
__ADD(TC_PRIO_FILLER,filler)
__ADD(TC_PRIO_BULK,bulk)
@@ -289,38 +255,40 @@ int rtnl_str2prio(const char *name)
/** @} */
-static struct rtnl_qdisc_ops prio_ops = {
- .qo_kind = "prio",
- .qo_msg_parser = prio_msg_parser,
- .qo_free_data = prio_free_data,
- .qo_dump = {
+static struct rtnl_tc_ops prio_ops = {
+ .to_kind = "prio",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_prio),
+ .to_msg_parser = prio_msg_parser,
+ .to_dump = {
[NL_DUMP_LINE] = prio_dump_line,
[NL_DUMP_DETAILS] = prio_dump_details,
},
- .qo_get_opts = prio_get_opts,
+ .to_msg_fill = prio_msg_fill,
};
-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 = {
+static struct rtnl_tc_ops pfifo_fast_ops = {
+ .to_kind = "pfifo_fast",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_prio),
+ .to_msg_parser = prio_msg_parser,
+ .to_dump = {
[NL_DUMP_LINE] = prio_dump_line,
[NL_DUMP_DETAILS] = prio_dump_details,
},
- .qo_get_opts = prio_get_opts,
+ .to_msg_fill = prio_msg_fill,
};
static void __init prio_init(void)
{
- rtnl_qdisc_register(&prio_ops);
- rtnl_qdisc_register(&pfifo_fast_ops);
+ rtnl_tc_register(&prio_ops);
+ rtnl_tc_register(&pfifo_fast_ops);
}
static void __exit prio_exit(void)
{
- rtnl_qdisc_unregister(&prio_ops);
- rtnl_qdisc_unregister(&pfifo_fast_ops);
+ rtnl_tc_unregister(&prio_ops);
+ rtnl_tc_unregister(&pfifo_fast_ops);
}
/** @} */
diff --git a/lib/route/sch/red.c b/lib/route/qdisc/red.c
index e4cac796..f05626eb 100644
--- a/lib/route/sch/red.c
+++ b/lib/route/qdisc/red.c
@@ -1,28 +1,28 @@
/*
- * lib/route/sch/red.c RED Qdisc
+ * lib/route/qdisc/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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup qdisc_api
- * @defgroup red Random Early Detection (RED)
+ * @ingroup qdisc
+ * @defgroup qdisc_red Random Early Detection (RED)
* @brief
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/qdisc.h>
-#include <netlink/route/qdisc-modules.h>
-#include <netlink/route/sch/red.h>
+#include <netlink/route/qdisc/red.h>
/** @cond SKIP */
#define RED_ATTR_LIMIT 0x01
@@ -34,44 +34,27 @@
#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)
+static int red_msg_parser(struct rtnl_tc *tc, void *data)
{
struct nlattr *tb[TCA_RED_MAX+1];
- struct rtnl_red *red;
+ struct rtnl_red *red = data;
struct tc_red_qopt *opts;
int err;
- if (!(qdisc->ce_mask & TCA_ATTR_OPTS))
+ if (!(tc->ce_mask & TCA_ATTR_OPTS))
return 0;
- err = tca_parse(tb, TCA_RED_MAX, (struct rtnl_tca *) qdisc, red_policy);
+ err = tca_parse(tb, TCA_RED_MAX, tc, red_policy);
if (err < 0)
return err;
if (!tb[TCA_RED_PARMS])
return -NLE_MISSING_ATTR;
- red = red_alloc(qdisc);
- if (!red)
- return -NLE_NOMEM;
-
opts = nla_data(tb[TCA_RED_PARMS]);
red->qr_limit = opts->limit;
@@ -89,45 +72,42 @@ static int red_msg_parser(struct rtnl_qdisc *qdisc)
return 0;
}
-static void red_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
+static void red_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_red *red = red_qdisc(qdisc);
+ struct rtnl_red *red = data;
if (red) {
/* XXX: limit, min, max, flags */
}
}
-static void red_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
+static void red_dump_details(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_red *red = red_qdisc(qdisc);
+ struct rtnl_red *red = data;
if (red) {
/* XXX: wlog, plog, scell_log */
}
}
-static void red_dump_stats(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
+static void red_dump_stats(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_red *red = red_qdisc(qdisc);
+ struct rtnl_red *red = data;
if (red) {
/* XXX: xstats */
}
}
-static struct nl_msg *red_get_opts(struct rtnl_qdisc *qdisc)
+static int red_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
{
- struct rtnl_red *red;
- struct nl_msg *msg;
+ struct rtnl_red *red = data;
- red = red_qdisc(qdisc);
if (!red)
- return NULL;
-
- msg = nlmsg_alloc();
- if (!msg)
- goto errout;
+ BUG();
#if 0
memset(&opts, 0, sizeof(opts));
@@ -139,10 +119,7 @@ static struct nl_msg *red_get_opts(struct rtnl_qdisc *qdisc)
goto errout;
#endif
- return msg;
-errout:
- nlmsg_free(msg);
- return NULL;
+ return -NLE_OPNOTSUPP;
}
/**
@@ -156,18 +133,15 @@ errout:
* @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)
+void rtnl_red_set_limit(struct rtnl_qdisc *qdisc, int limit)
{
struct rtnl_red *red;
- red = red_alloc(qdisc);
- if (!red)
- return -NLE_NOMEM;
+ if (!(red = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
red->qr_limit = limit;
red->qr_mask |= RED_ATTR_LIMIT;
-
- return 0;
}
/**
@@ -179,8 +153,10 @@ 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))
+ if (!(red = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (red->qr_mask & RED_ATTR_LIMIT)
return red->qr_limit;
else
return -NLE_NOATTR;
@@ -188,25 +164,27 @@ int rtnl_red_get_limit(struct rtnl_qdisc *qdisc)
/** @} */
-static struct rtnl_qdisc_ops red_ops = {
- .qo_kind = "red",
- .qo_msg_parser = red_msg_parser,
- .qo_dump = {
+static struct rtnl_tc_ops red_ops = {
+ .to_kind = "red",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_red),
+ .to_msg_parser = red_msg_parser,
+ .to_dump = {
[NL_DUMP_LINE] = red_dump_line,
[NL_DUMP_DETAILS] = red_dump_details,
[NL_DUMP_STATS] = red_dump_stats,
},
- .qo_get_opts = red_get_opts,
+ .to_msg_fill = red_msg_fill,
};
static void __init red_init(void)
{
- rtnl_qdisc_register(&red_ops);
+ rtnl_tc_register(&red_ops);
}
static void __exit red_exit(void)
{
- rtnl_qdisc_unregister(&red_ops);
+ rtnl_tc_unregister(&red_ops);
}
/** @} */
diff --git a/lib/route/sch/sfq.c b/lib/route/qdisc/sfq.c
index 4b47356f..acbb4ef8 100644
--- a/lib/route/sch/sfq.c
+++ b/lib/route/qdisc/sfq.c
@@ -1,17 +1,17 @@
/*
- * lib/route/sch/sfq.c SFQ Qdisc
+ * lib/route/qdisc/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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup qdisc_api
- * @defgroup sfq Stochastic Fairness Queueing (SFQ)
+ * @ingroup qdisc
+ * @defgroup qdisc_sfq Stochastic Fairness Queueing (SFQ)
* @brief
*
* @par Parameter Description
@@ -23,13 +23,13 @@
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/qdisc.h>
-#include <netlink/route/qdisc-modules.h>
-#include <netlink/route/sch/sfq.h>
+#include <netlink/route/qdisc/sfq.h>
/** @cond SKIP */
#define SCH_SFQ_ATTR_QUANTUM 0x01
@@ -39,35 +39,18 @@
#define SCH_SFQ_ATTR_FLOWS 0x10
/** @endcond */
-static inline struct rtnl_sfq *sfq_qdisc(struct rtnl_qdisc *qdisc)
+static int sfq_msg_parser(struct rtnl_tc *tc, void *data)
{
- 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 rtnl_sfq *sfq = data;
struct tc_sfq_qopt *opts;
- if (!(qdisc->ce_mask & TCA_ATTR_OPTS))
+ if (!(tc->ce_mask & TCA_ATTR_OPTS))
return 0;
- if (qdisc->q_opts->d_size < sizeof(*opts))
+ if (tc->tc_opts->d_size < sizeof(*opts))
return -NLE_INVAL;
- sfq = sfq_alloc(qdisc);
- if (!sfq)
- return -NLE_NOMEM;
-
- opts = (struct tc_sfq_qopt *) qdisc->q_opts->d_data;
+ opts = (struct tc_sfq_qopt *) tc->tc_opts->d_data;
sfq->qs_quantum = opts->quantum;
sfq->qs_perturb = opts->perturb_period;
@@ -82,55 +65,39 @@ static int sfq_msg_parser(struct rtnl_qdisc *qdisc)
return 0;
}
-static void sfq_free_data(struct rtnl_qdisc *qdisc)
-{
- free(qdisc->q_subdata);
-}
-
-static void sfq_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
+static void sfq_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_sfq *sfq = sfq_qdisc(qdisc);
+ struct rtnl_sfq *sfq = data;
if (sfq)
nl_dump(p, " quantum %u perturb %us", sfq->qs_quantum,
- nl_ticks2us(sfq->qs_perturb * nl_get_hz()));
+ sfq->qs_perturb);
}
-static void sfq_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
+static void sfq_dump_details(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_sfq *sfq = sfq_qdisc(qdisc);
+ struct rtnl_sfq *sfq = data;
if (sfq)
nl_dump(p, "limit %u divisor %u",
sfq->qs_limit, sfq->qs_divisor);
}
-static struct nl_msg *sfq_get_opts(struct rtnl_qdisc *qdisc)
+static int sfq_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
{
- struct rtnl_sfq *sfq;
- struct tc_sfq_qopt opts;
- struct nl_msg *msg;
+ struct rtnl_sfq *sfq = data;
+ struct tc_sfq_qopt opts = {0};
- sfq = sfq_qdisc(qdisc);
if (!sfq)
- return NULL;
-
- msg = nlmsg_alloc();
- if (!msg)
- goto errout;
+ BUG();
- 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;
+ return nlmsg_append(msg, &opts, sizeof(opts), NL_DONTPAD);
}
/**
@@ -144,18 +111,15 @@ errout:
* @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)
+void rtnl_sfq_set_quantum(struct rtnl_qdisc *qdisc, int quantum)
{
struct rtnl_sfq *sfq;
- sfq = sfq_alloc(qdisc);
- if (!sfq)
- return -NLE_NOMEM;
+ if (!(sfq = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
sfq->qs_quantum = quantum;
sfq->qs_mask |= SCH_SFQ_ATTR_QUANTUM;
-
- return 0;
}
/**
@@ -166,9 +130,11 @@ int rtnl_sfq_set_quantum(struct rtnl_qdisc *qdisc, int quantum)
int rtnl_sfq_get_quantum(struct rtnl_qdisc *qdisc)
{
struct rtnl_sfq *sfq;
+
+ if (!(sfq = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
- sfq = sfq_qdisc(qdisc);
- if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_QUANTUM)
+ if (sfq->qs_mask & SCH_SFQ_ATTR_QUANTUM)
return sfq->qs_quantum;
else
return -NLE_NOATTR;
@@ -180,18 +146,15 @@ int rtnl_sfq_get_quantum(struct rtnl_qdisc *qdisc)
* @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)
+void rtnl_sfq_set_limit(struct rtnl_qdisc *qdisc, int limit)
{
struct rtnl_sfq *sfq;
-
- sfq = sfq_alloc(qdisc);
- if (!sfq)
- return -NLE_NOMEM;
+
+ if (!(sfq = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
sfq->qs_limit = limit;
sfq->qs_mask |= SCH_SFQ_ATTR_LIMIT;
-
- return 0;
}
/**
@@ -202,9 +165,11 @@ int rtnl_sfq_set_limit(struct rtnl_qdisc *qdisc, int limit)
int rtnl_sfq_get_limit(struct rtnl_qdisc *qdisc)
{
struct rtnl_sfq *sfq;
+
+ if (!(sfq = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
- sfq = sfq_qdisc(qdisc);
- if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_LIMIT)
+ if (sfq->qs_mask & SCH_SFQ_ATTR_LIMIT)
return sfq->qs_limit;
else
return -NLE_NOATTR;
@@ -217,18 +182,15 @@ int rtnl_sfq_get_limit(struct rtnl_qdisc *qdisc)
* @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)
+void rtnl_sfq_set_perturb(struct rtnl_qdisc *qdisc, int perturb)
{
struct rtnl_sfq *sfq;
-
- sfq = sfq_alloc(qdisc);
- if (!sfq)
- return -NLE_NOMEM;
+
+ if (!(sfq = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
sfq->qs_perturb = perturb;
sfq->qs_mask |= SCH_SFQ_ATTR_PERTURB;
-
- return 0;
}
/**
@@ -239,9 +201,11 @@ int rtnl_sfq_set_perturb(struct rtnl_qdisc *qdisc, int perturb)
int rtnl_sfq_get_perturb(struct rtnl_qdisc *qdisc)
{
struct rtnl_sfq *sfq;
+
+ if (!(sfq = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
- sfq = sfq_qdisc(qdisc);
- if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_PERTURB)
+ if (sfq->qs_mask & SCH_SFQ_ATTR_PERTURB)
return sfq->qs_perturb;
else
return -NLE_NOATTR;
@@ -255,9 +219,11 @@ int rtnl_sfq_get_perturb(struct rtnl_qdisc *qdisc)
int rtnl_sfq_get_divisor(struct rtnl_qdisc *qdisc)
{
struct rtnl_sfq *sfq;
+
+ if (!(sfq = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
- sfq = sfq_qdisc(qdisc);
- if (sfq && sfq->qs_mask & SCH_SFQ_ATTR_DIVISOR)
+ if (sfq->qs_mask & SCH_SFQ_ATTR_DIVISOR)
return sfq->qs_divisor;
else
return -NLE_NOATTR;
@@ -265,25 +231,26 @@ int rtnl_sfq_get_divisor(struct rtnl_qdisc *qdisc)
/** @} */
-static struct rtnl_qdisc_ops sfq_ops = {
- .qo_kind = "sfq",
- .qo_msg_parser = sfq_msg_parser,
- .qo_free_data = sfq_free_data,
- .qo_dump = {
+static struct rtnl_tc_ops sfq_ops = {
+ .to_kind = "sfq",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_sfq),
+ .to_msg_parser = sfq_msg_parser,
+ .to_dump = {
[NL_DUMP_LINE] = sfq_dump_line,
[NL_DUMP_DETAILS] = sfq_dump_details,
},
- .qo_get_opts = sfq_get_opts,
+ .to_msg_fill = sfq_msg_fill,
};
static void __init sfq_init(void)
{
- rtnl_qdisc_register(&sfq_ops);
+ rtnl_tc_register(&sfq_ops);
}
static void __exit sfq_exit(void)
{
- rtnl_qdisc_unregister(&sfq_ops);
+ rtnl_tc_unregister(&sfq_ops);
}
/** @} */
diff --git a/lib/route/sch/tbf.c b/lib/route/qdisc/tbf.c
index eccaf702..eb574d95 100644
--- a/lib/route/sch/tbf.c
+++ b/lib/route/qdisc/tbf.c
@@ -1,78 +1,56 @@
/*
- * lib/route/sch/tbf.c TBF Qdisc
+ * lib/route/qdisc/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-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup qdisc_api
- * @defgroup tbf Token Bucket Filter (TBF)
+ * @ingroup qdisc
+ * @defgroup qdisc_tbf Token Bucket Filter (TBF)
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/utils.h>
-#include <netlink/route/tc.h>
+#include <netlink-private/route/tc-api.h>
#include <netlink/route/qdisc.h>
-#include <netlink/route/qdisc-modules.h>
#include <netlink/route/class.h>
-#include <netlink/route/class-modules.h>
#include <netlink/route/link.h>
-#include <netlink/route/sch/tbf.h>
+#include <netlink/route/qdisc/tbf.h>
/** @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)
+static int tbf_msg_parser(struct rtnl_tc *tc, void *data)
{
- int err;
struct nlattr *tb[TCA_TBF_MAX + 1];
- struct rtnl_tbf *tbf;
+ struct rtnl_tbf *tbf = data;
+ int err;
- err = tca_parse(tb, TCA_TBF_MAX, (struct rtnl_tca *) q, tbf_policy);
- if (err < 0)
+ if ((err = tca_parse(tb, TCA_TBF_MAX, tc, tbf_policy)) < 0)
return err;
- tbf = tbf_alloc(q);
- if (!tbf)
- return -NLE_NOMEM;
-
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;
@@ -86,23 +64,21 @@ static int tbf_msg_parser(struct rtnl_qdisc *q)
opts.peakrate.rate);
tbf->qt_peakrate_bucket = bufsize;
- tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_MPU | TBF_ATTR_RATE |
- TBF_ATTR_PEAKRATE);
+ rtnl_tc_set_mpu(tc, tbf->qt_rate.rs_mpu);
+ rtnl_tc_set_overhead(tc, tbf->qt_rate.rs_overhead);
+
+ tbf->qt_mask = (TBF_ATTR_LIMIT | TBF_ATTR_RATE | TBF_ATTR_PEAKRATE);
}
return 0;
}
-static void tbf_free_data(struct rtnl_qdisc *qdisc)
-{
- free(qdisc->q_subdata);
-}
-
-static void tbf_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
+static void tbf_dump_line(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
double r, rbit, lim;
char *ru, *rubit, *limu;
- struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
+ struct rtnl_tbf *tbf = data;
if (!tbf)
return;
@@ -115,9 +91,10 @@ static void tbf_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
r, ru, rbit, rubit, lim, limu);
}
-static void tbf_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
+static void tbf_dump_details(struct rtnl_tc *tc, void *data,
+ struct nl_dump_params *p)
{
- struct rtnl_tbf *tbf = tbf_qdisc(qdisc);
+ struct rtnl_tbf *tbf = data;
if (!tbf)
return;
@@ -128,9 +105,9 @@ static void tbf_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
double cl = nl_cancel_down_bytes(1 << tbf->qt_rate.rs_cell_log,
&cu);
- nl_dump(p, "mpu %u rate-bucket-size %1.f%s "
+ nl_dump(p, "rate-bucket-size %1.f%s "
"rate-cell-size %.1f%s\n",
- tbf->qt_mpu, bs, bu, cl, cu);
+ bs, bu, cl, cu);
}
@@ -151,59 +128,40 @@ static void tbf_dump_details(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
}
}
-static struct nl_msg *tbf_get_opts(struct rtnl_qdisc *qdisc)
+static int tbf_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg)
{
+ uint32_t rtab[RTNL_TC_RTABLE_SIZE], ptab[RTNL_TC_RTABLE_SIZE];
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];
+ struct rtnl_tbf *tbf = data;
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;
+ if ((tbf->qt_mask & required) != required)
+ return -NLE_MISSING_ATTR;
+ memset(&opts, 0, sizeof(opts));
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);
+ rtnl_tc_build_rate_table(tc, &tbf->qt_rate, rtab);
+ rtnl_rcopy_ratespec(&opts.rate, &tbf->qt_rate);
if (tbf->qt_mask & TBF_ATTR_PEAKRATE) {
opts.mtu = tbf->qt_peakrate_txtime;
- tbf->qt_peakrate.rs_mpu = tbf->qt_mpu;
+ rtnl_tc_build_rate_table(tc, &tbf->qt_peakrate, ptab);
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;
+ return 0;
nla_put_failure:
- nlmsg_free(msg);
- return NULL;
+ return -NLE_MSGSIZE;
}
/**
@@ -217,18 +175,15 @@ nla_put_failure:
* @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)
+void rtnl_qdisc_tbf_set_limit(struct rtnl_qdisc *qdisc, int limit)
{
struct rtnl_tbf *tbf;
- tbf = tbf_alloc(qdisc);
- if (!tbf)
- return -NLE_NOMEM;
+ if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
tbf->qt_limit = limit;
tbf->qt_mask |= TBF_ATTR_LIMIT;
-
- return 0;
}
static inline double calc_limit(struct rtnl_ratespec *spec, int latency,
@@ -265,9 +220,8 @@ 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 -NLE_NOMEM;
+ if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
if (!(tbf->qt_mask & TBF_ATTR_RATE))
return -NLE_MISSING_ATTR;
@@ -282,7 +236,9 @@ int rtnl_qdisc_tbf_set_limit_by_latency(struct rtnl_qdisc *qdisc, int latency)
limit = limit2;
}
- return rtnl_qdisc_tbf_set_limit(qdisc, (int) limit);
+ rtnl_qdisc_tbf_set_limit(qdisc, (int) limit);
+
+ return 0;
}
/**
@@ -294,63 +250,18 @@ 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;
- else
- return -NLE_NOATTR;
-}
-
-/**
- * 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 -NLE_NOMEM;
-
- tbf->qt_mpu = mpu;
- tbf->qt_mask |= TBF_ATTR_MPU;
-
- return 0;
-}
+ if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
-/**
- * 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;
+ if (tbf->qt_mask & TBF_ATTR_LIMIT)
+ return tbf->qt_limit;
else
return -NLE_NOATTR;
}
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;
}
@@ -362,27 +273,25 @@ static inline int calc_cell_log(int cell, int bucket)
* @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,
+void 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 -NLE_NOMEM;
+ if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
- cell_log = calc_cell_log(cell, bucket);
- if (cell_log < 0)
- return cell_log;
+ if (!cell)
+ cell_log = UINT8_MAX;
+ else
+ cell_log = rtnl_tc_calc_cell_log(cell);
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_rate_txtime = nl_us2ticks(rtnl_tc_calc_txtime(bucket, rate));
tbf->qt_mask |= TBF_ATTR_RATE;
-
- return 0;
}
/**
@@ -394,8 +303,10 @@ 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))
+ if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (tbf->qt_mask & TBF_ATTR_RATE)
return tbf->qt_rate.rs_rate;
else
return -1;
@@ -410,8 +321,10 @@ 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))
+ if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (tbf->qt_mask & TBF_ATTR_RATE)
return tbf->qt_rate_bucket;
else
return -1;
@@ -426,8 +339,10 @@ 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))
+ if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (tbf->qt_mask & TBF_ATTR_RATE)
return (1 << tbf->qt_rate.rs_cell_log);
else
return -1;
@@ -447,9 +362,8 @@ int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket,
struct rtnl_tbf *tbf;
int cell_log;
- tbf = tbf_alloc(qdisc);
- if (!tbf)
- return -NLE_NOMEM;
+ if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
cell_log = calc_cell_log(cell, bucket);
if (cell_log < 0)
@@ -458,7 +372,7 @@ int rtnl_qdisc_tbf_set_peakrate(struct rtnl_qdisc *qdisc, int rate, int bucket,
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_peakrate_txtime = nl_us2ticks(rtnl_tc_calc_txtime(bucket, rate));
tbf->qt_mask |= TBF_ATTR_PEAKRATE;
@@ -474,8 +388,10 @@ 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))
+ if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
return tbf->qt_peakrate.rs_rate;
else
return -1;
@@ -490,8 +406,10 @@ 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))
+ if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
return tbf->qt_peakrate_bucket;
else
return -1;
@@ -506,8 +424,10 @@ 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))
+ if (!(tbf = rtnl_tc_data(TC_CAST(qdisc))))
+ BUG();
+
+ if (tbf->qt_mask & TBF_ATTR_PEAKRATE)
return (1 << tbf->qt_peakrate.rs_cell_log);
else
return -1;
@@ -515,25 +435,26 @@ int rtnl_qdisc_tbf_get_peakrate_cell(struct rtnl_qdisc *qdisc)
/** @} */
-static struct rtnl_qdisc_ops tbf_qdisc_ops = {
- .qo_kind = "tbf",
- .qo_msg_parser = tbf_msg_parser,
- .qo_dump = {
+static struct rtnl_tc_ops tbf_tc_ops = {
+ .to_kind = "tbf",
+ .to_type = RTNL_TC_TYPE_QDISC,
+ .to_size = sizeof(struct rtnl_tbf),
+ .to_msg_parser = tbf_msg_parser,
+ .to_dump = {
[NL_DUMP_LINE] = tbf_dump_line,
[NL_DUMP_DETAILS] = tbf_dump_details,
},
- .qo_free_data = tbf_free_data,
- .qo_get_opts = tbf_get_opts,
+ .to_msg_fill = tbf_msg_fill,
};
static void __init tbf_init(void)
{
- rtnl_qdisc_register(&tbf_qdisc_ops);
+ rtnl_tc_register(&tbf_tc_ops);
}
static void __exit tbf_exit(void)
{
- rtnl_qdisc_unregister(&tbf_qdisc_ops);
+ rtnl_tc_unregister(&tbf_tc_ops);
}
/** @} */
diff --git a/lib/route/qdisc_api.c b/lib/route/qdisc_api.c
deleted file mode 100644
index 089f2123..00000000
--- a/lib/route/qdisc_api.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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 <tgraf@suug.ch>
- */
-
-/**
- * @ingroup qdisc
- * @defgroup qdisc_api Queueing Discipline Modules
- * @{
- */
-
-#include <netlink-local.h>
-#include <netlink-tc.h>
-#include <netlink/netlink.h>
-#include <netlink/utils.h>
-#include <netlink/route/link.h>
-#include <netlink/route/tc.h>
-#include <netlink/route/qdisc.h>
-#include <netlink/route/class.h>
-#include <netlink/route/classifier.h>
-#include <netlink/route/qdisc-modules.h>
-
-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 -NLE_EXIST;
-
- 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 -NLE_OBJ_NOTFOUND;
-
- *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
deleted file mode 100644
index dc52ae80..00000000
--- a/lib/route/qdisc_obj.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * 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 <tgraf@suug.ch>
- */
-
-/**
- * @ingroup qdisc
- * @defgroup qdisc_obj Queueing Discipline Object
- * @{
- */
-
-#include <netlink-local.h>
-#include <netlink-tc.h>
-#include <netlink/netlink.h>
-#include <netlink/utils.h>
-#include <netlink/route/link.h>
-#include <netlink/route/tc.h>
-#include <netlink/route/qdisc.h>
-#include <netlink/route/class.h>
-#include <netlink/route/classifier.h>
-#include <netlink/route/qdisc-modules.h>
-
-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 void qdisc_dump_line(struct nl_object *obj, struct nl_dump_params *p)
-{
- struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) obj;
- struct rtnl_qdisc_ops *qops;
-
- tca_dump_line((struct rtnl_tca *) qdisc, "qdisc", p);
-
- qops = rtnl_qdisc_lookup_ops(qdisc);
- if (qops && qops->qo_dump[NL_DUMP_LINE])
- qops->qo_dump[NL_DUMP_LINE](qdisc, p);
-
- nl_dump(p, "\n");
-}
-
-static void qdisc_dump_details(struct nl_object *arg, struct nl_dump_params *p)
-{
- struct rtnl_qdisc *qdisc = (struct rtnl_qdisc *) arg;
- struct rtnl_qdisc_ops *qops;
-
- qdisc_dump_line(arg, p);
-
- tca_dump_details((struct rtnl_tca *) qdisc, p);
- nl_dump(p, "refcnt %u ", qdisc->q_info);
-
- qops = rtnl_qdisc_lookup_ops(qdisc);
- if (qops && qops->qo_dump[NL_DUMP_DETAILS])
- qops->qo_dump[NL_DUMP_DETAILS](qdisc, p);
-
- nl_dump(p, "\n");
-}
-
-static void 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;
-
- qdisc_dump_details(arg, p);
- tca_dump_stats((struct rtnl_tca *) qdisc, p);
- nl_dump(p, "\n");
-
- qops = rtnl_qdisc_lookup_ops(qdisc);
- if (qops && qops->qo_dump[NL_DUMP_STATS])
- qops->qo_dump[NL_DUMP_STATS](qdisc, p);
-}
-
-/**
- * @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_LINE] = qdisc_dump_line,
- [NL_DUMP_DETAILS] = qdisc_dump_details,
- [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
index c85c2258..29851873 100644
--- a/lib/route/route.c
+++ b/lib/route/route.c
@@ -16,7 +16,7 @@
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/utils.h>
@@ -64,13 +64,18 @@ static int route_request_update(struct nl_cache *c, struct nl_sock *h)
* @arg sk Netlink socket.
* @arg family Address family of routes to cover or AF_UNSPEC
* @arg flags Flags
+ * @arg result Result pointer
*
* Allocates a new cache, initializes it properly and updates it to
* contain all routes currently configured in the kernel.
*
+ * Valid flags:
+ * * ROUTE_CACHE_CONTENT - Cache will contain contents of routing cache
+ * instead of actual routes.
+ *
* @note The caller is responsible for destroying and freeing the
* cache after using it.
- * @return The cache or NULL if an error has occured.
+ * @return 0 on success or a negative error code.
*/
int rtnl_route_alloc_cache(struct nl_sock *sk, int family, int flags,
struct nl_cache **result)
diff --git a/lib/route/route_obj.c b/lib/route/route_obj.c
index 7f26bfdd..dd4bf49e 100644
--- a/lib/route/route_obj.c
+++ b/lib/route/route_obj.c
@@ -30,11 +30,12 @@
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/utils.h>
#include <netlink/data.h>
+#include <netlink/hashtable.h>
#include <netlink/route/rtnl.h>
#include <netlink/route/route.h>
#include <netlink/route/link.h>
@@ -70,6 +71,7 @@ static void route_constructor(struct nl_object *c)
r->rt_table = RT_TABLE_MAIN;
r->rt_protocol = RTPROT_STATIC;
r->rt_type = RTN_UNICAST;
+ r->rt_prio = 0;
nl_init_list_head(&r->rt_nexthops);
}
@@ -110,6 +112,9 @@ static int route_clone(struct nl_object *_dst, struct nl_object *_src)
if (!(dst->rt_pref_src = nl_addr_clone(src->rt_pref_src)))
return -NLE_NOMEM;
+ /* Will be inc'ed again while adding the nexthops of the source */
+ dst->rt_nr_nh = 0;
+
nl_init_list_head(&dst->rt_nexthops);
nl_list_for_each_entry(nh, &src->rt_nexthops, rtnh_list) {
new = rtnl_route_nh_clone(nh);
@@ -125,12 +130,9 @@ static int route_clone(struct nl_object *_dst, struct nl_object *_src)
static void route_dump_line(struct nl_object *a, struct nl_dump_params *p)
{
struct rtnl_route *r = (struct rtnl_route *) a;
- struct nl_cache *link_cache;
int cache = 0, flags;
char buf[64];
- link_cache = nl_cache_mngt_require("route/link");
-
if (r->rt_flags & RTM_F_CLONED)
cache = 1;
@@ -206,10 +208,10 @@ static void route_dump_details(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];
+ char buf[256];
int i;
- link_cache = nl_cache_mngt_require("route/link");
+ link_cache = nl_cache_mngt_require_safe("route/link");
route_dump_line(a, p);
nl_dump_line(p, " ");
@@ -257,7 +259,7 @@ static void route_dump_details(struct nl_object *a, struct nl_dump_params *p)
if ((r->ce_mask & ROUTE_ATTR_CACHEINFO) && r->rt_cacheinfo.rtci_error) {
nl_dump_line(p, " cacheinfo error %d (%s)\n",
r->rt_cacheinfo.rtci_error,
- strerror(-r->rt_cacheinfo.rtci_error));
+ strerror_r(-r->rt_cacheinfo.rtci_error, buf, sizeof(buf)));
}
if (r->ce_mask & ROUTE_ATTR_METRICS) {
@@ -270,6 +272,9 @@ static void route_dump_details(struct nl_object *a, struct nl_dump_params *p)
r->rt_metrics[i]);
nl_dump(p, "]\n");
}
+
+ if (link_cache)
+ nl_cache_put(link_cache);
}
static void route_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
@@ -284,74 +289,58 @@ static void route_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
nl_dump_line(p, " used %u refcnt %u last-use %us "
"expires %us\n",
ci->rtci_used, ci->rtci_clntref,
- ci->rtci_last_use / nl_get_hz(),
- ci->rtci_expires / nl_get_hz());
+ ci->rtci_last_use / nl_get_user_hz(),
+ ci->rtci_expires / nl_get_user_hz());
}
}
-static void route_dump_env(struct nl_object *obj, struct nl_dump_params *p)
+static void route_keygen(struct nl_object *obj, uint32_t *hashkey,
+ uint32_t table_sz)
{
struct rtnl_route *route = (struct rtnl_route *) obj;
- struct nl_cache *link_cache;
- char buf[128];
+ unsigned int rkey_sz;
+ struct nl_addr *addr = NULL;
+ struct route_hash_key {
+ uint8_t rt_family;
+ uint8_t rt_tos;
+ uint32_t rt_table;
+ uint32_t rt_prio;
+ char rt_addr[0];
+ } __attribute__((packed)) *rkey;
+#ifdef NL_DEBUG
+ char buf[INET6_ADDRSTRLEN+5];
+#endif
- link_cache = nl_cache_mngt_require("route/link");
-
- nl_dump_line(p, "ROUTE_FAMILY=%s\n",
- nl_af2str(route->rt_family, buf, sizeof(buf)));
-
- if (route->ce_mask & ROUTE_ATTR_DST)
- nl_dump_line(p, "ROUTE_DST=%s\n",
- nl_addr2str(route->rt_dst, buf, sizeof(buf)));
-
- if (route->ce_mask & ROUTE_ATTR_SRC)
- nl_dump_line(p, "ROUTE_SRC=%s\n",
- nl_addr2str(route->rt_src, buf, sizeof(buf)));
-
- if (route->ce_mask & ROUTE_ATTR_PREF_SRC)
- nl_dump_line(p, "ROUTE_PREFSRC=%s\n",
- nl_addr2str(route->rt_pref_src, buf, sizeof(buf)));
-
- if (route->ce_mask & ROUTE_ATTR_IIF) {
- if (link_cache) {
- nl_dump_line(p, "ROUTE_IIF=%s",
- rtnl_link_i2name(link_cache, route->rt_iif,
- buf, sizeof(buf)));
- } else
- nl_dump_line(p, "ROUTE_IIF=%d", route->rt_iif);
+ if (route->rt_dst)
+ addr = route->rt_dst;
+
+ rkey_sz = sizeof(*rkey);
+ if (addr)
+ rkey_sz += nl_addr_get_len(addr);
+ rkey = calloc(1, rkey_sz);
+ if (!rkey) {
+ NL_DBG(2, "Warning: calloc failed for %d bytes...\n", rkey_sz);
+ *hashkey = 0;
+ return;
}
+ rkey->rt_family = route->rt_family;
+ rkey->rt_tos = route->rt_tos;
+ rkey->rt_table = route->rt_table;
+ rkey->rt_prio = route->rt_prio;
+ if (addr)
+ memcpy(rkey->rt_addr, nl_addr_get_binary_addr(addr),
+ nl_addr_get_len(addr));
- if (route->ce_mask & ROUTE_ATTR_TOS)
- nl_dump_line(p, "ROUTE_TOS=%u\n", route->rt_tos);
+ *hashkey = nl_hash(rkey, rkey_sz, 0) % table_sz;
- if (route->ce_mask & ROUTE_ATTR_TABLE)
- nl_dump_line(p, "ROUTE_TABLE=%u\n",
- route->rt_table);
+ NL_DBG(5, "route %p key (fam %d tos %d table %d addr %s) keysz %d "
+ "hash 0x%x\n", route, rkey->rt_family, rkey->rt_tos,
+ rkey->rt_table, nl_addr2str(addr, buf, sizeof(buf)),
+ rkey_sz, *hashkey);
- if (route->ce_mask & ROUTE_ATTR_SCOPE)
- nl_dump_line(p, "ROUTE_SCOPE=%s\n",
- rtnl_scope2str(route->rt_scope, buf, sizeof(buf)));
+ free(rkey);
- if (route->ce_mask & ROUTE_ATTR_PRIO)
- nl_dump_line(p, "ROUTE_PRIORITY=%u\n",
- route->rt_prio);
-
- if (route->ce_mask & ROUTE_ATTR_TYPE)
- nl_dump_line(p, "ROUTE_TYPE=%s\n",
- nl_rtntype2str(route->rt_type, buf, sizeof(buf)));
-
- if (route->ce_mask & ROUTE_ATTR_MULTIPATH) {
- struct rtnl_nexthop *nh;
- int index = 1;
-
- if (route->rt_nr_nh > 0)
- nl_dump_line(p, "ROUTE_NR_NH=%u\n", route->rt_nr_nh);
-
- nl_list_for_each_entry(nh, &route->rt_nexthops, rtnh_list) {
- p->dp_ivar = index++;
- rtnl_route_nh_dump(nh, p);
- }
- }
+ return;
}
static int route_compare(struct nl_object *_a, struct nl_object *_b,
@@ -397,13 +386,13 @@ static int route_compare(struct nl_object *_a, struct nl_object *_b,
if (a->rt_metrics_mask & (1 << i) &&
(!(b->rt_metrics_mask & (1 << i)) ||
a->rt_metrics[i] != b->rt_metrics[i]))
- ROUTE_DIFF(METRICS, 1);
+ diff |= ROUTE_DIFF(METRICS, 1);
}
diff |= ROUTE_DIFF(FLAGS,
(a->rt_flags ^ b->rt_flags) & b->rt_flag_mask);
} else {
- if (a->rt_nr_nh != a->rt_nr_nh)
+ if (a->rt_nr_nh != b->rt_nr_nh)
goto nh_mismatch;
/* search for a dup in each nh of a */
@@ -411,9 +400,10 @@ static int route_compare(struct nl_object *_a, struct nl_object *_b,
found = 0;
nl_list_for_each_entry(nh_b, &b->rt_nexthops,
rtnh_list) {
- if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0))
+ if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) {
found = 1;
break;
+ }
}
if (!found)
goto nh_mismatch;
@@ -425,9 +415,10 @@ static int route_compare(struct nl_object *_a, struct nl_object *_b,
found = 0;
nl_list_for_each_entry(nh_a, &a->rt_nexthops,
rtnh_list) {
- if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0))
+ if (!rtnl_route_nh_compare(nh_a, nh_b, ~0, 0)) {
found = 1;
break;
+ }
}
if (!found)
goto nh_mismatch;
@@ -455,7 +446,104 @@ nh_mismatch:
#undef ROUTE_DIFF
}
-static struct trans_tbl route_attrs[] = {
+static int route_update(struct nl_object *old_obj, struct nl_object *new_obj)
+{
+ struct rtnl_route *new_route = (struct rtnl_route *) new_obj;
+ struct rtnl_route *old_route = (struct rtnl_route *) old_obj;
+ struct rtnl_nexthop *new_nh;
+ int action = new_obj->ce_msgtype;
+#ifdef NL_DEBUG
+ char buf[INET6_ADDRSTRLEN+5];
+#endif
+
+ /*
+ * ipv6 ECMP route notifications from the kernel come as
+ * separate notifications, one for every nexthop. This update
+ * function collapses such route msgs into a single
+ * route with multiple nexthops. The resulting object looks
+ * similar to a ipv4 ECMP route
+ */
+ if (new_route->rt_family != AF_INET6 ||
+ new_route->rt_table == RT_TABLE_LOCAL)
+ return -NLE_OPNOTSUPP;
+
+ /*
+ * For routes that are already multipath,
+ * or dont have a nexthop dont do anything
+ */
+ if (rtnl_route_get_nnexthops(new_route) != 1)
+ return -NLE_OPNOTSUPP;
+
+ /*
+ * Get the only nexthop entry from the new route. For
+ * IPv6 we always get a route with a 0th NH
+ * filled or nothing at all
+ */
+ new_nh = rtnl_route_nexthop_n(new_route, 0);
+ if (!new_nh || !rtnl_route_nh_get_gateway(new_nh))
+ return -NLE_OPNOTSUPP;
+
+ switch(action) {
+ case RTM_NEWROUTE : {
+ struct rtnl_nexthop *cloned_nh;
+
+ /*
+ * Add the nexthop to old route
+ */
+ cloned_nh = rtnl_route_nh_clone(new_nh);
+ if (!cloned_nh)
+ return -NLE_NOMEM;
+ rtnl_route_add_nexthop(old_route, cloned_nh);
+
+ NL_DBG(2, "Route obj %p updated. Added "
+ "nexthop %p via %s\n", old_route, cloned_nh,
+ nl_addr2str(cloned_nh->rtnh_gateway, buf,
+ sizeof(buf)));
+ }
+ break;
+ case RTM_DELROUTE : {
+ struct rtnl_nexthop *old_nh;
+
+ /*
+ * Only take care of nexthop deletes and not
+ * route deletes. So, if there is only one nexthop
+ * quite likely we did not update it. So dont do
+ * anything and return
+ */
+ if (rtnl_route_get_nnexthops(old_route) <= 1)
+ return -NLE_OPNOTSUPP;
+
+ /*
+ * Find the next hop in old route and delete it
+ */
+ nl_list_for_each_entry(old_nh, &old_route->rt_nexthops,
+ rtnh_list) {
+ if (!rtnl_route_nh_compare(old_nh, new_nh, ~0, 0)) {
+
+ rtnl_route_remove_nexthop(old_route, old_nh);
+
+ NL_DBG(2, "Route obj %p updated. Removed "
+ "nexthop %p via %s\n", old_route,
+ old_nh,
+ nl_addr2str(old_nh->rtnh_gateway, buf,
+ sizeof(buf)));
+
+ rtnl_route_nh_free(old_nh);
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ NL_DBG(2, "Unknown action associated "
+ "to object %p during route update\n", new_obj);
+ return -NLE_OPNOTSUPP;
+ }
+
+ return NLE_SUCCESS;
+}
+
+static const struct trans_tbl route_attrs[] = {
__ADD(ROUTE_ATTR_FAMILY, family)
__ADD(ROUTE_ATTR_TOS, tos)
__ADD(ROUTE_ATTR_TABLE, table)
@@ -752,18 +840,26 @@ void rtnl_route_add_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh)
void rtnl_route_remove_nexthop(struct rtnl_route *route, struct rtnl_nexthop *nh)
{
- route->rt_nr_nh--;
- nl_list_del(&nh->rtnh_list);
+ if (route->ce_mask & ROUTE_ATTR_MULTIPATH) {
+ route->rt_nr_nh--;
+ nl_list_del(&nh->rtnh_list);
+ }
}
struct nl_list_head *rtnl_route_get_nexthops(struct rtnl_route *route)
{
- return &route->rt_nexthops;
+ if (route->ce_mask & ROUTE_ATTR_MULTIPATH)
+ return &route->rt_nexthops;
+
+ return NULL;
}
int rtnl_route_get_nnexthops(struct rtnl_route *route)
{
- return route->rt_nr_nh;
+ if (route->ce_mask & ROUTE_ATTR_MULTIPATH)
+ return route->rt_nr_nh;
+
+ return 0;
}
void rtnl_route_foreach_nexthop(struct rtnl_route *r,
@@ -782,7 +878,7 @@ void rtnl_route_foreach_nexthop(struct rtnl_route *r,
struct rtnl_nexthop *rtnl_route_nexthop_n(struct rtnl_route *r, int n)
{
struct rtnl_nexthop *nh;
- int i;
+ uint32_t i;
if (r->ce_mask & ROUTE_ATTR_MULTIPATH && r->rt_nr_nh > n) {
i = 0;
@@ -937,11 +1033,12 @@ int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result)
route->rt_scope = rtm->rtm_scope;
route->rt_protocol = rtm->rtm_protocol;
route->rt_flags = rtm->rtm_flags;
+ route->rt_prio = 0;
route->ce_mask |= ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
ROUTE_ATTR_TABLE | ROUTE_ATTR_TYPE |
ROUTE_ATTR_SCOPE | ROUTE_ATTR_PROTOCOL |
- ROUTE_ATTR_FLAGS;
+ ROUTE_ATTR_FLAGS | ROUTE_ATTR_PRIO;
if (tb[RTA_DST]) {
if (!(dst = nl_addr_alloc_attr(tb[RTA_DST], family)))
@@ -972,6 +1069,9 @@ int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result)
nl_addr_put(src);
}
+ if (tb[RTA_TABLE])
+ rtnl_route_set_table(route, nla_get_u32(tb[RTA_TABLE]));
+
if (tb[RTA_IIF])
rtnl_route_set_iif(route, nla_get_u32(tb[RTA_IIF]));
@@ -1038,6 +1138,7 @@ int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result)
}
if (old_nh) {
+ rtnl_route_nh_set_flags(old_nh, rtm->rtm_flags & 0xff);
if (route->rt_nr_nh == 0) {
/* If no nexthops have been provided via RTA_MULTIPATH
* we add it as regular nexthop to maintain backwards
@@ -1097,10 +1198,15 @@ int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route)
if (route->rt_src)
rtmsg.rtm_src_len = nl_addr_get_prefixlen(route->rt_src);
-
- if (rtmsg.rtm_scope == RT_SCOPE_NOWHERE)
+ if (!(route->ce_mask & ROUTE_ATTR_SCOPE))
rtmsg.rtm_scope = rtnl_route_guess_scope(route);
+ if (rtnl_route_get_nnexthops(route) == 1) {
+ struct rtnl_nexthop *nh;
+ nh = rtnl_route_nexthop_n(route, 0);
+ rtmsg.rtm_flags |= nh->rtnh_flags;
+ }
+
if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0)
goto nla_put_failure;
@@ -1136,7 +1242,17 @@ int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route)
nla_nest_end(msg, metrics);
}
- if (rtnl_route_get_nnexthops(route) > 0) {
+ if (rtnl_route_get_nnexthops(route) == 1) {
+ struct rtnl_nexthop *nh;
+
+ nh = rtnl_route_nexthop_n(route, 0);
+ if (nh->rtnh_gateway)
+ NLA_PUT_ADDR(msg, RTA_GATEWAY, nh->rtnh_gateway);
+ if (nh->rtnh_ifindex)
+ NLA_PUT_U32(msg, RTA_OIF, nh->rtnh_ifindex);
+ if (nh->rtnh_realms)
+ NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms);
+ } else if (rtnl_route_get_nnexthops(route) > 1) {
struct nlattr *multipath;
struct rtnl_nexthop *nh;
@@ -1185,12 +1301,14 @@ struct nl_object_ops route_obj_ops = {
[NL_DUMP_LINE] = route_dump_line,
[NL_DUMP_DETAILS] = route_dump_details,
[NL_DUMP_STATS] = route_dump_stats,
- [NL_DUMP_ENV] = route_dump_env,
},
.oo_compare = route_compare,
+ .oo_keygen = route_keygen,
+ .oo_update = route_update,
.oo_attrs2str = route_attrs2str,
.oo_id_attrs = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS |
- ROUTE_ATTR_TABLE | ROUTE_ATTR_DST),
+ ROUTE_ATTR_TABLE | ROUTE_ATTR_DST |
+ ROUTE_ATTR_PRIO),
};
/** @endcond */
diff --git a/lib/route/route_utils.c b/lib/route/route_utils.c
index 41ae65c4..a5b39661 100644
--- a/lib/route/route_utils.c
+++ b/lib/route/route_utils.c
@@ -37,7 +37,7 @@
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/rtnl.h>
@@ -58,6 +58,7 @@ static int add_routing_table_name(long id, const char *name)
static void __init init_routing_table_names(void)
{
add_routing_table_name(RT_TABLE_UNSPEC, "unspec");
+ add_routing_table_name(RT_TABLE_COMPAT, "compat");
add_routing_table_name(RT_TABLE_DEFAULT, "default");
add_routing_table_name(RT_TABLE_MAIN, "main");
add_routing_table_name(RT_TABLE_LOCAL, "local");
@@ -138,7 +139,7 @@ int rtnl_route_str2proto(const char *name)
* @{
*/
-static struct trans_tbl route_metrices[] = {
+static const struct trans_tbl route_metrices[] = {
__ADD(RTAX_UNSPEC, unspec)
__ADD(RTAX_LOCK, lock)
__ADD(RTAX_MTU, mtu)
diff --git a/lib/route/rtnl.c b/lib/route/rtnl.c
index 25336745..6a55ca1a 100644
--- a/lib/route/rtnl.c
+++ b/lib/route/rtnl.c
@@ -6,15 +6,15 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @defgroup rtnl Routing Family
+ * @defgroup rtnl Routing Library (libnl-route)
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/rtnl.h>
@@ -34,15 +34,20 @@
* Fills out a routing netlink request message and sends it out
* using nl_send_simple().
*
- * @return 0 on success or a negative error code.
+ * @return 0 on success or a negative error code. Due to a bug in
+ * older versions, this returned the number of bytes sent. So for
+ * compatibility, treat positive return values as success too.
*/
int nl_rtgen_request(struct nl_sock *sk, int type, int family, int flags)
{
+ int err;
struct rtgenmsg gmsg = {
.rtgen_family = family,
};
- return nl_send_simple(sk, type, flags, &gmsg, sizeof(gmsg));
+ err = nl_send_simple(sk, type, flags, &gmsg, sizeof(gmsg));
+
+ return err >= 0 ? 0 : err;
}
/** @} */
@@ -52,7 +57,7 @@ int nl_rtgen_request(struct nl_sock *sk, int type, int family, int flags)
* @{
*/
-static struct trans_tbl rtntypes[] = {
+static const struct trans_tbl rtntypes[] = {
__ADD(RTN_UNSPEC,unspec)
__ADD(RTN_UNICAST,unicast)
__ADD(RTN_LOCAL,local)
@@ -84,12 +89,12 @@ int nl_str2rtntype(const char *name)
* @{
*/
-static struct trans_tbl scopes[] = {
+static const struct trans_tbl scopes[] = {
__ADD(255,nowhere)
__ADD(254,host)
__ADD(253,link)
__ADD(200,site)
- __ADD(0,universe)
+ __ADD(0,global)
};
char *rtnl_scope2str(int scope, char *buf, size_t size)
diff --git a/lib/route/rule.c b/lib/route/rule.c
index 126e96d2..b2161a2e 100644
--- a/lib/route/rule.c
+++ b/lib/route/rule.c
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2010 Thomas Graf <tgraf@suug.ch>
*/
/**
@@ -16,7 +16,7 @@
* @{
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/rtnl.h>
@@ -25,18 +25,19 @@
/** @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
+#define RULE_ATTR_TABLE 0x0002
+#define RULE_ATTR_ACTION 0x0004
+#define RULE_ATTR_FLAGS 0x0008
+#define RULE_ATTR_IIFNAME 0x0010
+#define RULE_ATTR_OIFNAME 0x0020
+#define RULE_ATTR_PRIO 0x0040
+#define RULE_ATTR_MARK 0x0080
+#define RULE_ATTR_MASK 0x0100
+#define RULE_ATTR_GOTO 0x0200
+#define RULE_ATTR_SRC 0x0400
+#define RULE_ATTR_DST 0x0800
+#define RULE_ATTR_DSFIELD 0x1000
+#define RULE_ATTR_FLOW 0x2000
static struct nl_cache_ops rtnl_rule_ops;
static struct nl_object_ops rule_obj_ops;
@@ -69,20 +70,23 @@ static int rule_clone(struct nl_object *_dst, struct nl_object *_src)
return 0;
}
-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 struct nla_policy rule_policy[FRA_MAX+1] = {
+ [FRA_TABLE] = { .type = NLA_U32 },
+ [FRA_IIFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ },
+ [FRA_OIFNAME] = { .type = NLA_STRING, .maxlen = IFNAMSIZ },
+ [FRA_PRIORITY] = { .type = NLA_U32 },
+ [FRA_FWMARK] = { .type = NLA_U32 },
+ [FRA_FWMASK] = { .type = NLA_U32 },
+ [FRA_GOTO] = { .type = NLA_U32 },
+ [FRA_FLOW] = { .type = NLA_U32 },
};
static int rule_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
struct nlmsghdr *n, struct nl_parser_param *pp)
{
struct rtnl_rule *rule;
- struct rtmsg *r;
- struct nlattr *tb[RTA_MAX+1];
+ struct fib_rule_hdr *frh;
+ struct nlattr *tb[FRA_MAX+1];
int err = 1, family;
rule = rtnl_rule_alloc();
@@ -92,67 +96,81 @@ static int rule_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
}
rule->ce_msgtype = n->nlmsg_type;
- r = nlmsg_data(n);
+ frh = nlmsg_data(n);
- err = nlmsg_parse(n, sizeof(*r), tb, RTA_MAX, rule_policy);
+ err = nlmsg_parse(n, sizeof(*frh), tb, FRA_MAX, rule_policy);
if (err < 0)
goto errout;
- rule->r_family = 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 |
- RULE_ATTR_TABLE);
-
- if (tb[RTA_PRIORITY]) {
- rule->r_prio = nla_get_u32(tb[RTA_PRIORITY]);
- rule->ce_mask |= RULE_ATTR_PRIO;
+ rule->r_family = family = frh->family;
+ rule->r_table = frh->table;
+ rule->r_action = frh->action;
+ rule->r_flags = frh->flags;
+
+ rule->ce_mask = (RULE_ATTR_FAMILY | RULE_ATTR_TABLE | RULE_ATTR_ACTION |
+ RULE_ATTR_FLAGS);
+
+ /* ipv4 only */
+ if (frh->tos) {
+ rule->r_dsfield = frh->tos;
+ rule->ce_mask |= RULE_ATTR_DSFIELD;
}
- if (tb[RTA_SRC]) {
- if (!(rule->r_src = nl_addr_alloc_attr(tb[RTA_SRC], family)))
- goto errout_enomem;
- nl_addr_set_prefixlen(rule->r_src, r->rtm_src_len);
- rule->ce_mask |= RULE_ATTR_SRC;
+ if (tb[FRA_TABLE]) {
+ rule->r_table = nla_get_u32(tb[FRA_TABLE]);
+ rule->ce_mask |= RULE_ATTR_TABLE;
}
- if (tb[RTA_DST]) {
- if (!(rule->r_dst = nl_addr_alloc_attr(tb[RTA_DST], family)))
- goto errout_enomem;
- nl_addr_set_prefixlen(rule->r_dst, r->rtm_dst_len);
- rule->ce_mask |= RULE_ATTR_DST;
+ if (tb[FRA_IIFNAME]) {
+ nla_strlcpy(rule->r_iifname, tb[FRA_IIFNAME], IFNAMSIZ);
+ rule->ce_mask |= RULE_ATTR_IIFNAME;
}
- if (tb[RTA_PROTOINFO]) {
- rule->r_mark = nla_get_u32(tb[RTA_PROTOINFO]);
+ if (tb[FRA_OIFNAME]) {
+ nla_strlcpy(rule->r_oifname, tb[FRA_OIFNAME], IFNAMSIZ);
+ rule->ce_mask |= RULE_ATTR_OIFNAME;
+ }
+
+ if (tb[FRA_PRIORITY]) {
+ rule->r_prio = nla_get_u32(tb[FRA_PRIORITY]);
+ rule->ce_mask |= RULE_ATTR_PRIO;
+ }
+
+ if (tb[FRA_FWMARK]) {
+ rule->r_mark = nla_get_u32(tb[FRA_FWMARK]);
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[FRA_FWMASK]) {
+ rule->r_mask = nla_get_u32(tb[FRA_FWMASK]);
+ rule->ce_mask |= RULE_ATTR_MASK;
+ }
+
+ if (tb[FRA_GOTO]) {
+ rule->r_goto = nla_get_u32(tb[FRA_GOTO]);
+ rule->ce_mask |= RULE_ATTR_GOTO;
}
- if (tb[RTA_FLOW]) {
- rule->r_realms = nla_get_u32(tb[RTA_FLOW]);
- rule->ce_mask |= RULE_ATTR_REALMS;
+ if (tb[FRA_SRC]) {
+ if (!(rule->r_src = nl_addr_alloc_attr(tb[FRA_SRC], family)))
+ goto errout_enomem;
+
+ nl_addr_set_prefixlen(rule->r_src, frh->src_len);
+ rule->ce_mask |= RULE_ATTR_SRC;
}
- if (tb[RTA_GATEWAY]) {
- rule->r_srcmap = nl_addr_alloc_attr(tb[RTA_GATEWAY], family);
- if (!rule->r_srcmap)
+ if (tb[FRA_DST]) {
+ if (!(rule->r_dst = nl_addr_alloc_attr(tb[FRA_DST], family)))
goto errout_enomem;
- rule->ce_mask |= RULE_ATTR_SRCMAP;
+ nl_addr_set_prefixlen(rule->r_dst, frh->dst_len);
+ rule->ce_mask |= RULE_ATTR_DST;
}
- if (tb[RTA_TABLE]) {
- rule->r_table = nla_get_u32(tb[RTA_TABLE]);
- rule->ce_mask |= RULE_ATTR_TABLE;
- }
+ /* ipv4 only */
+ if (tb[FRA_FLOW]) {
+ rule->r_flow = nla_get_u32(tb[FRA_FLOW]);
+ rule->ce_mask |= RULE_ATTR_FLOW;
+ }
err = pp->pp_cb((struct nl_object *) rule, pp);
errout:
@@ -180,46 +198,44 @@ static void rule_dump_line(struct nl_object *o, struct nl_dump_params *p)
if (r->ce_mask & RULE_ATTR_SRC)
nl_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)
- nl_dump(p, "from 0/%d ", r->r_src_len);
if (r->ce_mask & RULE_ATTR_DST)
nl_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)
- nl_dump(p, "to 0/%d ", r->r_dst_len);
- if (r->ce_mask & RULE_ATTR_DSFIELD && r->r_dsfield)
- nl_dump(p, "tos %d ", r->r_dsfield);
+ if (r->ce_mask & RULE_ATTR_DSFIELD)
+ nl_dump(p, "tos %u ", r->r_dsfield);
+
+ if (r->ce_mask & (RULE_ATTR_MARK | RULE_ATTR_MASK))
+ nl_dump(p, "mark %#x/%#x", r->r_mark, r->r_mask);
- if (r->ce_mask & RULE_ATTR_MARK)
- nl_dump(p, "mark %" PRIx64 , r->r_mark);
+ if (r->ce_mask & RULE_ATTR_IIFNAME)
+ nl_dump(p, "iif %s ", r->r_iifname);
- if (r->ce_mask & RULE_ATTR_IIF)
- nl_dump(p, "iif %s ", r->r_iif);
+ if (r->ce_mask & RULE_ATTR_OIFNAME)
+ nl_dump(p, "oif %s ", r->r_oifname);
if (r->ce_mask & RULE_ATTR_TABLE)
nl_dump(p, "lookup %s ",
rtnl_route_table2str(r->r_table, buf, sizeof(buf)));
- if (r->ce_mask & RULE_ATTR_REALMS)
- nl_dump(p, "realms %s ",
- rtnl_realms2str(r->r_realms, buf, sizeof(buf)));
+ if (r->ce_mask & RULE_ATTR_FLOW)
+ nl_dump(p, "flow %s ",
+ rtnl_realms2str(r->r_flow, buf, sizeof(buf)));
- nl_dump(p, "action %s\n",
- nl_rtntype2str(r->r_type, buf, sizeof(buf)));
+ if (r->ce_mask & RULE_ATTR_GOTO)
+ nl_dump(p, "goto %u ", r->r_goto);
+
+ if (r->ce_mask & RULE_ATTR_ACTION)
+ nl_dump(p, "action %s",
+ nl_rtntype2str(r->r_action, buf, sizeof(buf)));
+
+ nl_dump(p, "\n");
}
static void rule_dump_details(struct nl_object *obj, struct nl_dump_params *p)
{
- struct rtnl_rule *rule = (struct rtnl_rule *) obj;
- char buf[128];
-
rule_dump_line(obj, p);
-
- if (rule->ce_mask & RULE_ATTR_SRCMAP)
- nl_dump_line(p, " srcmap %s\n",
- nl_addr2str(rule->r_srcmap, buf, sizeof(buf)));
}
static void rule_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
@@ -227,52 +243,7 @@ static void rule_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
rule_dump_details(obj, p);
}
-static void rule_dump_env(struct nl_object *obj, struct nl_dump_params *p)
-{
- struct rtnl_rule *rule = (struct rtnl_rule *) obj;
- char buf[128];
-
- nl_dump_line(p, "RULE_PRIORITY=%u\n", rule->r_prio);
- nl_dump_line(p, "RULE_FAMILY=%s\n",
- nl_af2str(rule->r_family, buf, sizeof(buf)));
-
- if (rule->ce_mask & RULE_ATTR_DST)
- nl_dump_line(p, "RULE_DST=%s\n",
- nl_addr2str(rule->r_dst, buf, sizeof(buf)));
-
- if (rule->ce_mask & RULE_ATTR_DST_LEN)
- nl_dump_line(p, "RULE_DSTLEN=%u\n", rule->r_dst_len);
-
- if (rule->ce_mask & RULE_ATTR_SRC)
- nl_dump_line(p, "RULE_SRC=%s\n",
- nl_addr2str(rule->r_src, buf, sizeof(buf)));
-
- if (rule->ce_mask & RULE_ATTR_SRC_LEN)
- nl_dump_line(p, "RULE_SRCLEN=%u\n", rule->r_src_len);
-
- if (rule->ce_mask & RULE_ATTR_IIF)
- nl_dump_line(p, "RULE_IIF=%s\n", rule->r_iif);
-
- if (rule->ce_mask & RULE_ATTR_TABLE)
- nl_dump_line(p, "RULE_TABLE=%u\n", rule->r_table);
-
- if (rule->ce_mask & RULE_ATTR_REALMS)
- nl_dump_line(p, "RULE_REALM=%u\n", rule->r_realms);
-
- if (rule->ce_mask & RULE_ATTR_MARK)
- nl_dump_line(p, "RULE_MARK=0x%" PRIx64 "\n", rule->r_mark);
-
- if (rule->ce_mask & RULE_ATTR_DSFIELD)
- nl_dump_line(p, "RULE_DSFIELD=%u\n", rule->r_dsfield);
-
- if (rule->ce_mask & RULE_ATTR_TYPE)
- nl_dump_line(p, "RULE_TYPE=%s\n",
- nl_rtntype2str(rule->r_type, buf, sizeof(buf)));
-
- if (rule->ce_mask & RULE_ATTR_SRCMAP)
- nl_dump_line(p, "RULE_SRCMAP=%s\n",
- nl_addr2str(rule->r_srcmap, buf, sizeof(buf)));
-}
+#define RULE_ATTR_FLAGS 0x0008
static int rule_compare(struct nl_object *_a, struct nl_object *_b,
uint32_t attrs, int flags)
@@ -285,36 +256,37 @@ static int rule_compare(struct nl_object *_a, struct nl_object *_b,
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(ACTION, a->r_action != b->r_action);
+ diff |= RULE_DIFF(IIFNAME, strcmp(a->r_iifname, b->r_iifname));
+ diff |= RULE_DIFF(OIFNAME, strcmp(a->r_oifname, b->r_oifname));
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(MASK, a->r_mask != b->r_mask);
+ diff |= RULE_DIFF(GOTO, a->r_goto != b->r_goto);
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));
+ diff |= RULE_DIFF(DSFIELD, a->r_dsfield != b->r_dsfield);
+ diff |= RULE_DIFF(FLOW, a->r_flow != b->r_flow);
#undef RULE_DIFF
return diff;
}
-static struct trans_tbl rule_attrs[] = {
+static const struct trans_tbl rule_attrs[] = {
__ADD(RULE_ATTR_FAMILY, family)
+ __ADD(RULE_ATTR_TABLE, table)
+ __ADD(RULE_ATTR_ACTION, action)
+ __ADD(RULE_ATTR_IIFNAME, iifname)
+ __ADD(RULE_ATTR_OIFNAME, oifname)
__ADD(RULE_ATTR_PRIO, prio)
__ADD(RULE_ATTR_MARK, mark)
- __ADD(RULE_ATTR_IIF, iif)
- __ADD(RULE_ATTR_REALMS, realms)
+ __ADD(RULE_ATTR_MASK, mask)
+ __ADD(RULE_ATTR_GOTO, goto)
__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)
+ __ADD(RULE_ATTR_FLOW, flow)
};
static char *rule_attrs2str(int attrs, char *buf, size_t len)
@@ -347,7 +319,7 @@ void rtnl_rule_put(struct rtnl_rule *rule)
/**
* Build a rule cache including all rules currently configured in the kernel.
- * @arg sk Netlink socket.
+ * @arg sock Netlink socket.
* @arg family Address family or AF_UNSPEC.
* @arg result Pointer to store resulting cache.
*
@@ -387,55 +359,61 @@ static int build_rule_msg(struct rtnl_rule *tmpl, int cmd, int flags,
struct nl_msg **result)
{
struct nl_msg *msg;
- struct rtmsg rtm = {
- .rtm_type = RTN_UNSPEC
+ struct fib_rule_hdr frh = {
+ .family = tmpl->r_family,
+ .table = tmpl->r_table,
+ .action = tmpl->r_action,
+ .flags = tmpl->r_flags,
+ .tos = tmpl->r_dsfield,
};
- 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;
+ if (!(tmpl->ce_mask & RULE_ATTR_FAMILY))
+ return -NLE_MISSING_ATTR;
msg = nlmsg_alloc_simple(cmd, flags);
if (!msg)
return -NLE_NOMEM;
- if (nlmsg_append(msg, &rtm, sizeof(rtm), NLMSG_ALIGNTO) < 0)
+ if (tmpl->ce_mask & RULE_ATTR_SRC)
+ frh.src_len = nl_addr_get_prefixlen(tmpl->r_src);
+
+ if (tmpl->ce_mask & RULE_ATTR_DST)
+ frh.dst_len = nl_addr_get_prefixlen(tmpl->r_dst);
+
+ if (nlmsg_append(msg, &frh, sizeof(frh), NLMSG_ALIGNTO) < 0)
goto nla_put_failure;
+ /* Additional table attribute replacing the 8bit in the header, was
+ * required to allow more than 256 tables. */
+ NLA_PUT_U32(msg, FRA_TABLE, tmpl->r_table);
+
if (tmpl->ce_mask & RULE_ATTR_SRC)
- NLA_PUT_ADDR(msg, RTA_SRC, tmpl->r_src);
+ NLA_PUT_ADDR(msg, FRA_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_DST)
+ NLA_PUT_ADDR(msg, FRA_DST, tmpl->r_dst);
+
+ if (tmpl->ce_mask & RULE_ATTR_IIFNAME)
+ NLA_PUT_STRING(msg, FRA_IIFNAME, tmpl->r_iifname);
+
+ if (tmpl->ce_mask & RULE_ATTR_OIFNAME)
+ NLA_PUT_STRING(msg, FRA_OIFNAME, tmpl->r_oifname);
if (tmpl->ce_mask & RULE_ATTR_PRIO)
- NLA_PUT_U32(msg, RTA_PRIORITY, tmpl->r_prio);
+ NLA_PUT_U32(msg, FRA_PRIORITY, tmpl->r_prio);
if (tmpl->ce_mask & RULE_ATTR_MARK)
- NLA_PUT_U32(msg, RTA_PROTOINFO, tmpl->r_mark);
+ NLA_PUT_U32(msg, FRA_FWMARK, tmpl->r_mark);
+
+ if (tmpl->ce_mask & RULE_ATTR_MASK)
+ NLA_PUT_U32(msg, FRA_FWMASK, tmpl->r_mask);
+
+ if (tmpl->ce_mask & RULE_ATTR_GOTO)
+ NLA_PUT_U32(msg, FRA_GOTO, tmpl->r_goto);
- if (tmpl->ce_mask & RULE_ATTR_REALMS)
- NLA_PUT_U32(msg, RTA_FLOW, tmpl->r_realms);
+ if (tmpl->ce_mask & RULE_ATTR_FLOW)
+ NLA_PUT_U32(msg, FRA_FLOW, tmpl->r_flow);
- if (tmpl->ce_mask & RULE_ATTR_IIF)
- NLA_PUT_STRING(msg, RTA_IIF, tmpl->r_iif);
*result = msg;
return 0;
@@ -449,6 +427,7 @@ nla_put_failure:
* Build netlink request message to add a new rule
* @arg tmpl template with data of new rule
* @arg flags additional netlink message flags
+ * @arg result Result pointer
*
* Builds a new netlink message requesting a addition of a new
* rule. The netlink message header isn't fully equipped with
@@ -456,7 +435,7 @@ nla_put_failure:
* 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
+ * @return 0 on success or a negative error code.
*/
int rtnl_rule_build_add_request(struct rtnl_rule *tmpl, int flags,
struct nl_msg **result)
@@ -504,6 +483,7 @@ int rtnl_rule_add(struct nl_sock *sk, struct rtnl_rule *tmpl, int flags)
* Build a netlink request message to delete a rule
* @arg rule rule to delete
* @arg flags additional netlink message flags
+ * @arg result Result pointer
*
* Builds a new netlink message requesting a deletion of a rule.
* The netlink message header isn't fully equipped with all relevant
@@ -511,7 +491,7 @@ int rtnl_rule_add(struct nl_sock *sk, struct rtnl_rule *tmpl, int flags)
* or supplemented as needed. \a rule must point to an existing
* address.
*
- * @return The netlink message
+ * @return 0 on success or a negative error code.
*/
int rtnl_rule_build_delete_request(struct rtnl_rule *rule, int flags,
struct nl_msg **result)
@@ -568,96 +548,63 @@ int rtnl_rule_get_family(struct rtnl_rule *rule)
return AF_UNSPEC;
}
-void rtnl_rule_set_prio(struct rtnl_rule *rule, int prio)
+void rtnl_rule_set_prio(struct rtnl_rule *rule, uint32_t prio)
{
rule->r_prio = prio;
rule->ce_mask |= RULE_ATTR_PRIO;
}
-int rtnl_rule_get_prio(struct rtnl_rule *rule)
+uint32_t rtnl_rule_get_prio(struct rtnl_rule *rule)
{
- if (rule->ce_mask & RULE_ATTR_PRIO)
- return rule->r_prio;
- else
- return -1;
+ return rule->r_prio;
}
-void rtnl_rule_set_mark(struct rtnl_rule *rule, uint64_t mark)
+void rtnl_rule_set_mark(struct rtnl_rule *rule, uint32_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)
+uint32_t rtnl_rule_get_mark(struct rtnl_rule *rule)
{
- rule->r_table = table;
- rule->ce_mask |= RULE_ATTR_TABLE;
+ return rule->r_mark;
}
-int rtnl_rule_get_table(struct rtnl_rule *rule)
+void rtnl_rule_set_mask(struct rtnl_rule *rule, uint32_t mask)
{
- if (rule->ce_mask & RULE_ATTR_TABLE)
- return rule->r_table;
- else
- return -1;
+ rule->r_mask = mask;
+ rule->ce_mask |= RULE_ATTR_MASK;
}
-void rtnl_rule_set_dsfield(struct rtnl_rule *rule, int dsfield)
+uint32_t rtnl_rule_get_mask(struct rtnl_rule *rule)
{
- rule->r_dsfield = dsfield;
- rule->ce_mask |= RULE_ATTR_DSFIELD;
+ return rule->r_mask;
}
-int rtnl_rule_get_dsfield(struct rtnl_rule *rule)
+void rtnl_rule_set_table(struct rtnl_rule *rule, uint32_t table)
{
- 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;
+ rule->r_table = table;
+ rule->ce_mask |= RULE_ATTR_TABLE;
}
-int rtnl_rule_get_src_len(struct rtnl_rule *rule)
+uint32_t rtnl_rule_get_table(struct rtnl_rule *rule)
{
- if (rule->ce_mask & RULE_ATTR_SRC_LEN)
- return rule->r_src_len;
- else
- return -1;
+ return rule->r_table;
}
-void rtnl_rule_set_dst_len(struct rtnl_rule *rule, int len)
+void rtnl_rule_set_dsfield(struct rtnl_rule *rule, uint8_t dsfield)
{
- 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;
+ rule->r_dsfield = dsfield;
+ rule->ce_mask |= RULE_ATTR_DSFIELD;
}
-int rtnl_rule_get_dst_len(struct rtnl_rule *rule)
+uint8_t rtnl_rule_get_dsfield(struct rtnl_rule *rule)
{
- if (rule->ce_mask & RULE_ATTR_DST_LEN)
- return rule->r_dst_len;
- else
- return -1;
+ return rule->r_dsfield;
}
static inline int __assign_addr(struct rtnl_rule *rule, struct nl_addr **pos,
- struct nl_addr *new, uint8_t *len, int flag)
+ struct nl_addr *new, int flag)
{
if (rule->ce_mask & RULE_ATTR_FAMILY) {
if (new->a_family != rule->r_family)
@@ -670,7 +617,6 @@ static inline int __assign_addr(struct rtnl_rule *rule, struct nl_addr **pos,
nl_addr_get(new);
*pos = new;
- *len = nl_addr_get_prefixlen(new);
rule->ce_mask |= (flag | RULE_ATTR_FAMILY);
@@ -679,30 +625,22 @@ static inline int __assign_addr(struct rtnl_rule *rule, struct nl_addr **pos,
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);
+ return __assign_addr(rule, &rule->r_src, src, RULE_ATTR_SRC);
}
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;
+ return rule->r_src;
}
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);
+ return __assign_addr(rule, &rule->r_dst, dst, RULE_ATTR_DST);
}
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;
+ return rule->r_dst;
}
int rtnl_rule_set_iif(struct rtnl_rule *rule, const char *dev)
@@ -710,45 +648,68 @@ int rtnl_rule_set_iif(struct rtnl_rule *rule, const char *dev)
if (strlen(dev) > IFNAMSIZ-1)
return -NLE_RANGE;
- strcpy(rule->r_iif, dev);
- rule->ce_mask |= RULE_ATTR_IIF;
+ strcpy(rule->r_iifname, dev);
+ rule->ce_mask |= RULE_ATTR_IIFNAME;
return 0;
}
char *rtnl_rule_get_iif(struct rtnl_rule *rule)
{
- if (rule->ce_mask & RULE_ATTR_IIF)
- return rule->r_iif;
+ if (rule->ce_mask & RULE_ATTR_IIFNAME)
+ return rule->r_iifname;
else
return NULL;
}
-void rtnl_rule_set_action(struct rtnl_rule *rule, int type)
+int rtnl_rule_set_oif(struct rtnl_rule *rule, const char *dev)
{
- rule->r_type = type;
- rule->ce_mask |= RULE_ATTR_TYPE;
+ if (strlen(dev) > IFNAMSIZ-1)
+ return -NLE_RANGE;
+
+ strcpy(rule->r_oifname, dev);
+ rule->ce_mask |= RULE_ATTR_OIFNAME;
+ return 0;
}
-int rtnl_rule_get_action(struct rtnl_rule *rule)
+char *rtnl_rule_get_oif(struct rtnl_rule *rule)
{
- if (rule->ce_mask & RULE_ATTR_TYPE)
- return rule->r_type;
+ if (rule->ce_mask & RULE_ATTR_OIFNAME)
+ return rule->r_oifname;
else
- return -NLE_NOATTR;
+ return NULL;
+}
+
+void rtnl_rule_set_action(struct rtnl_rule *rule, uint8_t action)
+{
+ rule->r_action = action;
+ rule->ce_mask |= RULE_ATTR_ACTION;
+}
+
+uint8_t rtnl_rule_get_action(struct rtnl_rule *rule)
+{
+ return rule->r_action;
}
void rtnl_rule_set_realms(struct rtnl_rule *rule, uint32_t realms)
{
- rule->r_realms = realms;
- rule->ce_mask |= RULE_ATTR_REALMS;
+ rule->r_flow = realms;
+ rule->ce_mask |= RULE_ATTR_FLOW;
}
uint32_t rtnl_rule_get_realms(struct rtnl_rule *rule)
{
- if (rule->ce_mask & RULE_ATTR_REALMS)
- return rule->r_realms;
- else
- return 0;
+ return rule->r_flow;
+}
+
+void rtnl_rule_set_goto(struct rtnl_rule *rule, uint32_t ref)
+{
+ rule->r_goto = ref;
+ rule->ce_mask |= RULE_ATTR_GOTO;
+}
+
+uint32_t rtnl_rule_get_goto(struct rtnl_rule *rule)
+{
+ return rule->r_goto;
}
/** @} */
@@ -762,7 +723,6 @@ static struct nl_object_ops rule_obj_ops = {
[NL_DUMP_LINE] = rule_dump_line,
[NL_DUMP_DETAILS] = rule_dump_details,
[NL_DUMP_STATS] = rule_dump_stats,
- [NL_DUMP_ENV] = rule_dump_env,
},
.oo_compare = rule_compare,
.oo_attrs2str = rule_attrs2str,
@@ -771,7 +731,7 @@ static struct nl_object_ops rule_obj_ops = {
static struct nl_cache_ops rtnl_rule_ops = {
.co_name = "route/rule",
- .co_hdrsize = sizeof(struct rtmsg),
+ .co_hdrsize = sizeof(struct fib_rule_hdr),
.co_msgtypes = {
{ RTM_NEWRULE, NL_ACT_NEW, "new" },
{ RTM_DELRULE, NL_ACT_DEL, "del" },
diff --git a/lib/route/sch/blackhole.c b/lib/route/sch/blackhole.c
deleted file mode 100644
index a30b6933..00000000
--- a/lib/route/sch/blackhole.c
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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 <tgraf@suug.ch>
- */
-
-/**
- * @ingroup qdisc_api
- * @defgroup blackhole Blackhole
- * @{
- */
-
-#include <netlink-local.h>
-#include <netlink-tc.h>
-#include <netlink/netlink.h>
-#include <netlink/route/qdisc.h>
-#include <netlink/route/qdisc-modules.h>
-
-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/fifo.c b/lib/route/sch/fifo.c
deleted file mode 100644
index 464af307..00000000
--- a/lib/route/sch/fifo.c
+++ /dev/null
@@ -1,199 +0,0 @@
-/*
- * 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-2008 Thomas Graf <tgraf@suug.ch>
- */
-
-/**
- * @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 <netlink-local.h>
-#include <netlink-tc.h>
-#include <netlink/netlink.h>
-#include <netlink/route/qdisc.h>
-#include <netlink/route/qdisc-modules.h>
-#include <netlink/route/sch/fifo.h>
-#include <netlink/utils.h>
-
-/** @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 -NLE_INVAL;
-
- fifo = fifo_alloc(qdisc);
- if (!fifo)
- return -NLE_NOMEM;
-
- 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 void pfifo_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
-{
- struct rtnl_fifo *fifo = fifo_qdisc(qdisc);
-
- if (fifo)
- nl_dump(p, " limit %u packets", fifo->qf_limit);
-}
-
-static void bfifo_dump_line(struct rtnl_qdisc *qdisc, struct nl_dump_params *p)
-{
- struct rtnl_fifo *fifo = fifo_qdisc(qdisc);
-
- if (fifo) {
- char *unit;
- double r;
-
- r = nl_cancel_down_bytes(fifo->qf_limit, &unit);
- nl_dump(p, " limit %.1f%s", r, unit);
- }
-}
-
-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 -NLE_NOMEM;
-
- 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 -NLE_NOATTR;
-}
-
-/** @} */
-
-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_LINE] = pfifo_dump_line,
- .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_LINE] = bfifo_dump_line,
- .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
deleted file mode 100644
index a1671360..00000000
--- a/lib/route/sch/htb.c
+++ /dev/null
@@ -1,546 +0,0 @@
-/*
- * 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-2008 Thomas Graf <tgraf@suug.ch>
- * Copyright (c) 2005-2006 Petr Gotthard <petr.gotthard@siemens.com>
- * Copyright (c) 2005-2006 Siemens AG Oesterreich
- */
-
-/**
- * @ingroup qdisc_api
- * @ingroup class_api
- * @defgroup htb Hierachical Token Bucket (HTB)
- * @{
- */
-
-#include <netlink-local.h>
-#include <netlink-tc.h>
-#include <netlink/netlink.h>
-#include <netlink/cache.h>
-#include <netlink/utils.h>
-#include <netlink/route/tc.h>
-#include <netlink/route/qdisc.h>
-#include <netlink/route/qdisc-modules.h>
-#include <netlink/route/class.h>
-#include <netlink/route/class-modules.h>
-#include <netlink/route/link.h>
-#include <netlink/route/sch/htb.h>
-
-/** @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 void htb_qdisc_dump_line(struct rtnl_qdisc *qdisc,
- struct nl_dump_params *p)
-{
- struct rtnl_htb_qdisc *d = (struct rtnl_htb_qdisc *) qdisc->q_subdata;
-
- if (d == NULL)
- return;
-
- if (d->qh_mask & SCH_HTB_HAS_RATE2QUANTUM)
- nl_dump(p, " r2q %u", d->qh_rate2quantum);
-
- if (d->qh_mask & SCH_HTB_HAS_DEFCLS) {
- char buf[32];
- nl_dump(p, " default %s",
- rtnl_tc_handle2str(d->qh_defcls, buf, sizeof(buf)));
- }
-}
-
-static void htb_class_dump_line(struct rtnl_class *class,
- struct nl_dump_params *p)
-{
- struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
-
- if (d == NULL)
- return;
-
- 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);
-
- nl_dump(p, " rate %.2f%s/s (%.0f%s) log %u",
- r, ru, rbit, rubit, 1<<d->ch_rate.rs_cell_log);
- }
-}
-
-static void htb_class_dump_details(struct rtnl_class *class,
- struct nl_dump_params *p)
-{
- struct rtnl_htb_class *d = (struct rtnl_htb_class *) class->c_subdata;
-
- if (d == NULL)
- return;
-
- /* 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);
-
- nl_dump(p, " ceil %.2f%s/s (%.0f%s) log %u",
- r, ru, rbit, rubit, 1<<d->ch_ceil.rs_cell_log);
- }
-
- if (d->ch_mask & SCH_HTB_HAS_PRIO)
- nl_dump(p, " prio %u", d->ch_prio);
-
- if (d->ch_mask & SCH_HTB_HAS_MTU)
- nl_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);
- nl_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);
- nl_dump(p, " cbuffer %.2f%s", b, bu);
- }
-
- if (d->ch_mask & SCH_HTB_HAS_QUANTUM)
- nl_dump(p, " quantum %u", d->ch_quantum);
-
- if (d->ch_mask & SCH_HTB_HAS_OVERHEAD)
- nl_dump(p, " overhead %u", d->ch_overhead);
-
- if (d->ch_mask & SCH_HTB_HAS_MPU)
- nl_dump(p, " mpu %u", d->ch_mpu);
-}
-
-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_LINE] = htb_qdisc_dump_line,
- .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_LINE] = htb_class_dump_line,
- [NL_DUMP_DETAILS] = htb_class_dump_details,
- },
- .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/tc.c b/lib/route/tc.c
index 97faef4e..0f150cca 100644
--- a/lib/route/tc.c
+++ b/lib/route/tc.c
@@ -6,26 +6,29 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
/**
* @ingroup rtnl
* @defgroup tc Traffic Control
- * @brief
* @{
*/
-#include <netlink-local.h>
-#include <netlink-tc.h>
+#include <netlink-private/netlink.h>
+#include <netlink-private/tc.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/route/rtnl.h>
#include <netlink/route/link.h>
#include <netlink/route/tc.h>
+#include <netlink-private/route/tc-api.h>
/** @cond SKIP */
+static struct nl_list_head tc_ops_list[__RTNL_TC_TYPE_MAX];
+static struct rtnl_tc_type_ops *tc_type_ops[__RTNL_TC_TYPE_MAX];
+
static struct nla_policy tc_policy[TCA_MAX+1] = {
[TCA_KIND] = { .type = NLA_STRING,
.maxlen = TCKINDSIZ },
@@ -33,7 +36,7 @@ static struct nla_policy tc_policy[TCA_MAX+1] = {
[TCA_STATS2] = { .type = NLA_NESTED },
};
-int tca_parse(struct nlattr **tb, int maxattr, struct rtnl_tca *g,
+int tca_parse(struct nlattr **tb, int maxattr, struct rtnl_tc *g,
struct nla_policy *policy)
{
@@ -55,12 +58,17 @@ static struct nla_policy tc_stats2_policy[TCA_STATS_MAX+1] = {
[TCA_STATS_QUEUE] = { .minlen = sizeof(struct gnet_stats_queue) },
};
-int tca_msg_parser(struct nlmsghdr *n, struct rtnl_tca *g)
+int rtnl_tc_msg_parse(struct nlmsghdr *n, struct rtnl_tc *tc)
{
+ struct nl_cache *link_cache;
+ struct rtnl_tc_ops *ops;
struct nlattr *tb[TCA_MAX + 1];
+ char kind[TCKINDSIZ];
struct tcmsg *tm;
int err;
+ tc->ce_msgtype = n->nlmsg_type;
+
err = nlmsg_parse(n, sizeof(*tm), tb, TCA_MAX, tc_policy);
if (err < 0)
return err;
@@ -68,25 +76,25 @@ int tca_msg_parser(struct nlmsghdr *n, struct rtnl_tca *g)
if (tb[TCA_KIND] == NULL)
return -NLE_MISSING_ATTR;
- nla_strlcpy(g->tc_kind, tb[TCA_KIND], TCKINDSIZ);
+ nla_strlcpy(kind, tb[TCA_KIND], sizeof(kind));
+ rtnl_tc_set_kind(tc, kind);
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;
+ tc->tc_family = tm->tcm_family;
+ tc->tc_ifindex = tm->tcm_ifindex;
+ tc->tc_handle = tm->tcm_handle;
+ tc->tc_parent = tm->tcm_parent;
+ tc->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);
+ tc->ce_mask |= (TCA_ATTR_FAMILY | TCA_ATTR_IFINDEX | TCA_ATTR_HANDLE|
+ TCA_ATTR_PARENT | TCA_ATTR_INFO);
if (tb[TCA_OPTIONS]) {
- g->tc_opts = nl_data_alloc_attr(tb[TCA_OPTIONS]);
- if (!g->tc_opts)
+ tc->tc_opts = nl_data_alloc_attr(tb[TCA_OPTIONS]);
+ if (!tc->tc_opts)
return -NLE_NOMEM;
- g->ce_mask |= TCA_ATTR_OPTS;
+ tc->ce_mask |= TCA_ATTR_OPTS;
}
-
if (tb[TCA_STATS2]) {
struct nlattr *tbs[TCA_STATS_MAX + 1];
@@ -100,34 +108,34 @@ int tca_msg_parser(struct nlmsghdr *n, struct rtnl_tca *g)
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;
+ tc->tc_stats[RTNL_TC_BYTES] = bs->bytes;
+ tc->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;
+ tc->tc_stats[RTNL_TC_RATE_BPS] = re->bps;
+ tc->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;
+ tc->tc_stats[RTNL_TC_QLEN] = q->qlen;
+ tc->tc_stats[RTNL_TC_BACKLOG] = q->backlog;
+ tc->tc_stats[RTNL_TC_DROPS] = q->drops;
+ tc->tc_stats[RTNL_TC_REQUEUES] = q->requeues;
+ tc->tc_stats[RTNL_TC_OVERLIMITS] = q->overlimits;
}
- g->ce_mask |= TCA_ATTR_STATS;
+ tc->ce_mask |= TCA_ATTR_STATS;
if (tbs[TCA_STATS_APP]) {
- g->tc_xstats = nl_data_alloc_attr(tbs[TCA_STATS_APP]);
- if (g->tc_xstats == NULL)
+ tc->tc_xstats = nl_data_alloc_attr(tbs[TCA_STATS_APP]);
+ if (tc->tc_xstats == NULL)
return -NLE_NOMEM;
} else
goto compat_xstats;
@@ -135,223 +143,420 @@ int tca_msg_parser(struct nlmsghdr *n, struct rtnl_tca *g)
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;
+ tc->tc_stats[RTNL_TC_BYTES] = st->bytes;
+ tc->tc_stats[RTNL_TC_PACKETS] = st->packets;
+ tc->tc_stats[RTNL_TC_RATE_BPS] = st->bps;
+ tc->tc_stats[RTNL_TC_RATE_PPS] = st->pps;
+ tc->tc_stats[RTNL_TC_QLEN] = st->qlen;
+ tc->tc_stats[RTNL_TC_BACKLOG] = st->backlog;
+ tc->tc_stats[RTNL_TC_DROPS] = st->drops;
+ tc->tc_stats[RTNL_TC_OVERLIMITS]= st->overlimits;
- g->ce_mask |= TCA_ATTR_STATS;
+ tc->ce_mask |= TCA_ATTR_STATS;
}
compat_xstats:
if (tb[TCA_XSTATS]) {
- g->tc_xstats = nl_data_alloc_attr(tb[TCA_XSTATS]);
- if (g->tc_xstats == NULL)
+ tc->tc_xstats = nl_data_alloc_attr(tb[TCA_XSTATS]);
+ if (tc->tc_xstats == NULL)
return -NLE_NOMEM;
- g->ce_mask |= TCA_ATTR_XSTATS;
+ tc->ce_mask |= TCA_ATTR_XSTATS;
}
}
+ ops = rtnl_tc_get_ops(tc);
+ if (ops && ops->to_msg_parser) {
+ void *data = rtnl_tc_data(tc);
- 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)
+ if (!data)
return -NLE_NOMEM;
+
+ err = ops->to_msg_parser(tc, data);
+ if (err < 0)
+ return err;
}
-
- if (src->tc_xstats) {
- dst->tc_xstats = nl_data_clone(src->tc_xstats);
- if (!dst->tc_xstats)
- return -NLE_NOMEM;
+
+ if ((link_cache = __nl_cache_mngt_require("route/link"))) {
+ struct rtnl_link *link;
+
+ if ((link = rtnl_link_get(link_cache, tc->tc_ifindex))) {
+ rtnl_tc_set_link(tc, link);
+
+ /* rtnl_tc_set_link incs refcnt */
+ rtnl_link_put(link);
+ }
}
return 0;
}
-void tca_dump_line(struct rtnl_tca *g, const char *type,
- struct nl_dump_params *p)
+int rtnl_tc_msg_build(struct rtnl_tc *tc, int type, int flags,
+ struct nl_msg **result)
{
- char handle[32], parent[32];
- struct nl_cache *link_cache;
-
- link_cache = nl_cache_mngt_require("route/link");
+ struct nl_msg *msg;
+ struct rtnl_tc_ops *ops;
+ struct tcmsg tchdr = {
+ .tcm_family = AF_UNSPEC,
+ .tcm_ifindex = tc->tc_ifindex,
+ .tcm_handle = tc->tc_handle,
+ .tcm_parent = tc->tc_parent,
+ };
+ int err = -NLE_MSGSIZE;
- nl_dump_line(p, "%s %s ", g->tc_kind, type);
+ msg = nlmsg_alloc_simple(type, flags);
+ if (!msg)
+ return -NLE_NOMEM;
- if (link_cache) {
- char buf[32];
- nl_dump(p, "dev %s ",
- rtnl_link_i2name(link_cache, g->tc_ifindex,
- buf, sizeof(buf)));
- } else
- nl_dump(p, "dev %u ", g->tc_ifindex);
-
- nl_dump(p, "handle %s parent %s",
- rtnl_tc_handle2str(g->tc_handle, handle, sizeof(handle)),
- rtnl_tc_handle2str(g->tc_parent, parent, sizeof(parent)));
-}
+ if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0)
+ goto nla_put_failure;
-void tca_dump_details(struct rtnl_tca *g, struct nl_dump_params *p)
-{
- nl_dump_line(p, " ");
+ if (tc->ce_mask & TCA_ATTR_KIND)
+ NLA_PUT_STRING(msg, TCA_KIND, tc->tc_kind);
+
+ ops = rtnl_tc_get_ops(tc);
+ if (ops && (ops->to_msg_fill || ops->to_msg_fill_raw)) {
+ struct nlattr *opts;
+ void *data = rtnl_tc_data(tc);
+
+ if (ops->to_msg_fill) {
+ if (!(opts = nla_nest_start(msg, TCA_OPTIONS)))
+ goto nla_put_failure;
+
+ if ((err = ops->to_msg_fill(tc, data, msg)) < 0)
+ goto nla_put_failure;
+
+ nla_nest_end(msg, opts);
+ } else if ((err = ops->to_msg_fill_raw(tc, data, msg)) < 0)
+ goto nla_put_failure;
+ }
+
+ *result = msg;
+ return 0;
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return err;
}
-void tca_dump_stats(struct rtnl_tca *g, struct nl_dump_params *p)
+void tca_set_kind(struct rtnl_tc *t, const char *kind)
{
- char *unit, fmt[64];
- float res;
- strcpy(fmt, " %7.2f %s %10u %10u %10u %10u %10u\n");
-
- nl_dump_line(p,
- " Stats: bytes packets drops overlimits" \
- " qlen backlog\n");
+ strncpy(t->tc_kind, kind, sizeof(t->tc_kind) - 1);
+ t->ce_mask |= TCA_ATTR_KIND;
+}
- res = nl_cancel_down_bytes(g->tc_stats[RTNL_TC_BYTES], &unit);
- if (*unit == 'B')
- fmt[11] = '9';
- nl_dump_line(p, 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]);
+/** @endcond */
- res = nl_cancel_down_bytes(g->tc_stats[RTNL_TC_RATE_BPS], &unit);
+/**
+ * @name Attributes
+ * @{
+ */
- strcpy(fmt, " %7.2f %s/s%9u pps");
+/**
+ * Set interface index of traffic control object
+ * @arg tc traffic control object
+ * @arg ifindex interface index.
+ *
+ * Sets the interface index of a traffic control object. The interface
+ * index defines the network device which this tc object is attached to.
+ * This function will overwrite any network device assigned with previous
+ * calls to rtnl_tc_set_ifindex() or rtnl_tc_set_link().
+ */
+void rtnl_tc_set_ifindex(struct rtnl_tc *tc, int ifindex)
+{
+ /* Obsolete possible old link reference */
+ rtnl_link_put(tc->tc_link);
+ tc->tc_link = NULL;
+ tc->ce_mask &= ~TCA_ATTR_LINK;
- if (*unit == 'B')
- fmt[11] = '9';
+ tc->tc_ifindex = ifindex;
+ tc->ce_mask |= TCA_ATTR_IFINDEX;
+}
- nl_dump_line(p, fmt, res, unit, g->tc_stats[RTNL_TC_RATE_PPS]);
+/**
+ * Return interface index of traffic control object
+ * @arg tc traffic control object
+ */
+int rtnl_tc_get_ifindex(struct rtnl_tc *tc)
+{
+ return tc->tc_ifindex;
}
-int tca_compare(struct nl_object *_a, struct nl_object *_b,
- uint32_t attrs, int flags)
+/**
+ * Set link of traffic control object
+ * @arg tc traffic control object
+ * @arg link link object
+ *
+ * Sets the link of a traffic control object. This function serves
+ * the same purpose as rtnl_tc_set_ifindex() but due to the continued
+ * allowed access to the link object it gives it the possibility to
+ * retrieve sane default values for the the MTU and the linktype.
+ * Always prefer this function over rtnl_tc_set_ifindex() if you can
+ * spare to have an additional link object around.
+ */
+void rtnl_tc_set_link(struct rtnl_tc *tc, struct rtnl_link *link)
{
- struct rtnl_tca *a = (struct rtnl_tca *) _a;
- struct rtnl_tca *b = (struct rtnl_tca *) _b;
- int diff = 0;
+ rtnl_link_put(tc->tc_link);
-#define TC_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, TCA_ATTR_##ATTR, a, b, EXPR)
+ if (!link)
+ return;
+ if (!link->l_index)
+ BUG();
- 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));
+ nl_object_get(OBJ_CAST(link));
+ tc->tc_link = link;
+ tc->tc_ifindex = link->l_index;
+ tc->ce_mask |= TCA_ATTR_LINK | TCA_ATTR_IFINDEX;
+}
-#undef TC_DIFF
+/**
+ * Get link of traffic control object
+ * @arg tc traffic control object
+ *
+ * Returns the link of a traffic control object. The link is only
+ * returned if it has been set before via rtnl_tc_set_link() or
+ * if a link cache was available while parsing the tc object. This
+ * function may still return NULL even if an ifindex is assigned to
+ * the tc object. It will _not_ look up the link by itself.
+ *
+ * @note The returned link will have its reference counter incremented.
+ * It is in the responsibility of the caller to return the
+ * reference.
+ *
+ * @return link object or NULL if not set.
+ */
+struct rtnl_link *rtnl_tc_get_link(struct rtnl_tc *tc)
+{
+ if (tc->tc_link) {
+ nl_object_get(OBJ_CAST(tc->tc_link));
+ return tc->tc_link;
+ }
- return diff;
+ return NULL;
}
-void tca_set_ifindex(struct rtnl_tca *t, int ifindex)
+/**
+ * Set the Maximum Transmission Unit (MTU) of traffic control object
+ * @arg tc traffic control object
+ * @arg mtu largest packet size expected
+ *
+ * Sets the MTU of a traffic control object. Not all traffic control
+ * objects will make use of this but it helps while calculating rate
+ * tables. This value is typically derived directly from the link
+ * the tc object is attached to if the link has been assigned via
+ * rtnl_tc_set_link(). It is usually not necessary to set the MTU
+ * manually, this function is provided to allow overwriting the derived
+ * value.
+ */
+void rtnl_tc_set_mtu(struct rtnl_tc *tc, uint32_t mtu)
{
- t->tc_ifindex = ifindex;
- t->ce_mask |= TCA_ATTR_IFINDEX;
+ tc->tc_mtu = mtu;
+ tc->ce_mask |= TCA_ATTR_MTU;
}
-int tca_get_ifindex(struct rtnl_tca *t)
+/**
+ * Return the MTU of traffic control object
+ * @arg tc traffic control object
+ *
+ * Returns the MTU of a traffic control object which has been set via:
+ * -# User specified value set via rtnl_tc_set_mtu()
+ * -# Dervied from link set via rtnl_tc_set_link()
+ * -# Fall back to default: ethernet = 1500
+ */
+uint32_t rtnl_tc_get_mtu(struct rtnl_tc *tc)
{
- return t->tc_ifindex;
+ if (tc->ce_mask & TCA_ATTR_MTU)
+ return tc->tc_mtu;
+ else if (tc->ce_mask & TCA_ATTR_LINK)
+ return tc->tc_link->l_mtu;
+ else
+ return 1500; /* default to ethernet */
}
-void tca_set_handle(struct rtnl_tca *t, uint32_t handle)
+/**
+ * Set the Minimum Packet Unit (MPU) of a traffic control object
+ * @arg tc traffic control object
+ * @arg mpu minimum packet size expected
+ *
+ * Sets the MPU of a traffic contorl object. It specifies the minimum
+ * packet size to ever hit this traffic control object. Not all traffic
+ * control objects will make use of this but it helps while calculating
+ * rate tables.
+ */
+void rtnl_tc_set_mpu(struct rtnl_tc *tc, uint32_t mpu)
{
- t->tc_handle = handle;
- t->ce_mask |= TCA_ATTR_HANDLE;
+ tc->tc_mpu = mpu;
+ tc->ce_mask |= TCA_ATTR_MPU;
}
-uint32_t tca_get_handle(struct rtnl_tca *t)
+/**
+ * Return the Minimum Packet Unit (MPU) of a traffic control object
+ * @arg tc traffic control object
+ *
+ * @return The MPU previously set via rtnl_tc_set_mpu() or 0.
+ */
+uint32_t rtnl_tc_get_mpu(struct rtnl_tc *tc)
{
- if (t->ce_mask & TCA_ATTR_HANDLE)
- return t->tc_handle;
- else
- return 0;
+ return tc->tc_mpu;
}
-void tca_set_parent(struct rtnl_tca *t, uint32_t parent)
+/**
+ * Set per packet overhead of a traffic control object
+ * @arg tc traffic control object
+ * @arg overhead overhead per packet in bytes
+ *
+ * Sets the per packet overhead in bytes occuring on the link not seen
+ * by the kernel. This value can be used to correct size calculations
+ * if the packet size on the wire does not match the packet sizes seen
+ * in the network stack. Not all traffic control objects will make use
+ * this but it helps while calculating accurate packet sizes in the
+ * kernel.
+ */
+void rtnl_tc_set_overhead(struct rtnl_tc *tc, uint32_t overhead)
{
- t->tc_parent = parent;
- t->ce_mask |= TCA_ATTR_PARENT;
+ tc->tc_overhead = overhead;
+ tc->ce_mask |= TCA_ATTR_OVERHEAD;
}
-uint32_t tca_get_parent(struct rtnl_tca *t)
+/**
+ * Return per packet overhead of a traffic control object
+ * @arg tc traffic control object
+ *
+ * @return The overhead previously set by rtnl_tc_set_overhead() or 0.
+ */
+uint32_t rtnl_tc_get_overhead(struct rtnl_tc *tc)
{
- if (t->ce_mask & TCA_ATTR_PARENT)
- return t->tc_parent;
- else
- return 0;
+ return tc->tc_overhead;
}
-void tca_set_kind(struct rtnl_tca *t, const char *kind)
+/**
+ * Set the linktype of a traffic control object
+ * @arg tc traffic control object
+ * @arg type type of link (e.g. ARPHRD_ATM, ARPHRD_ETHER)
+ *
+ * Overwrites the type of link this traffic control object is attached to.
+ * This value is typically derived from the link this tc object is attached
+ * if the link has been assigned via rtnl_tc_set_link(). It is usually not
+ * necessary to set the linktype manually. This function is provided to
+ * allow overwriting the linktype.
+ */
+void rtnl_tc_set_linktype(struct rtnl_tc *tc, uint32_t type)
{
- strncpy(t->tc_kind, kind, sizeof(t->tc_kind) - 1);
- t->ce_mask |= TCA_ATTR_KIND;
+ tc->tc_linktype = type;
+ tc->ce_mask |= TCA_ATTR_LINKTYPE;
}
-char *tca_get_kind(struct rtnl_tca *t)
+/**
+ * Return the linktype of a traffic control object
+ * @arg tc traffic control object
+ *
+ * Returns the linktype of the link the traffic control object is attached to:
+ * -# User specified value via rtnl_tc_set_linktype()
+ * -# Value derived from link set via rtnl_tc_set_link()
+ * -# Default fall-back: ARPHRD_ETHER
+ */
+uint32_t rtnl_tc_get_linktype(struct rtnl_tc *tc)
{
- if (t->ce_mask & TCA_ATTR_KIND)
- return t->tc_kind;
+ if (tc->ce_mask & TCA_ATTR_LINKTYPE)
+ return tc->tc_linktype;
+ else if (tc->ce_mask & TCA_ATTR_LINK)
+ return tc->tc_link->l_arptype;
else
- return NULL;
+ return ARPHRD_ETHER; /* default to ethernet */
}
-uint64_t tca_get_stat(struct rtnl_tca *t, int id)
+/**
+ * Set identifier of traffic control object
+ * @arg tc traffic control object
+ * @arg id unique identifier
+ */
+void rtnl_tc_set_handle(struct rtnl_tc *tc, uint32_t id)
{
- if (id < 0 || id > RTNL_TC_STATS_MAX)
- return 0;
+ tc->tc_handle = id;
+ tc->ce_mask |= TCA_ATTR_HANDLE;
+}
- return t->tc_stats[id];
+/**
+ * Return identifier of a traffic control object
+ * @arg tc traffic control object
+ */
+uint32_t rtnl_tc_get_handle(struct rtnl_tc *tc)
+{
+ return tc->tc_handle;
}
-int tca_build_msg(struct rtnl_tca *tca, int type, int flags,
- struct nl_msg **result)
+/**
+ * Set the parent identifier of a traffic control object
+ * @arg tc traffic control object
+ * @arg parent identifier of parent traffif control object
+ *
+ */
+void rtnl_tc_set_parent(struct rtnl_tc *tc, uint32_t parent)
{
- 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,
- };
+ tc->tc_parent = parent;
+ tc->ce_mask |= TCA_ATTR_PARENT;
+}
- msg = nlmsg_alloc_simple(type, flags);
- if (!msg)
- return -NLE_NOMEM;
+/**
+ * Return parent identifier of a traffic control object
+ * @arg tc traffic control object
+ */
+uint32_t rtnl_tc_get_parent(struct rtnl_tc *tc)
+{
+ return tc->tc_parent;
+}
- if (nlmsg_append(msg, &tchdr, sizeof(tchdr), NLMSG_ALIGNTO) < 0)
- goto nla_put_failure;
+/**
+ * Define the type of traffic control object
+ * @arg tc traffic control object
+ * @arg kind name of the tc object type
+ *
+ * @return 0 on success or a negative error code
+ */
+int rtnl_tc_set_kind(struct rtnl_tc *tc, const char *kind)
+{
+ if (tc->ce_mask & TCA_ATTR_KIND)
+ return -NLE_EXIST;
- if (tca->ce_mask & TCA_ATTR_KIND)
- NLA_PUT_STRING(msg, TCA_KIND, tca->tc_kind);
+ strncpy(tc->tc_kind, kind, sizeof(tc->tc_kind) - 1);
+ tc->ce_mask |= TCA_ATTR_KIND;
+
+ /* Force allocation of data */
+ rtnl_tc_data(tc);
- *result = msg;
return 0;
+}
-nla_put_failure:
- nlmsg_free(msg);
- return -NLE_MSGSIZE;
+/**
+ * Return kind of traffic control object
+ * @arg tc traffic control object
+ *
+ * @return Kind of traffic control object or NULL if not set.
+ */
+char *rtnl_tc_get_kind(struct rtnl_tc *tc)
+{
+ if (tc->ce_mask & TCA_ATTR_KIND)
+ return tc->tc_kind;
+ else
+ return NULL;
}
-/** @endcond */
+/**
+ * Return value of a statistical counter of a traffic control object
+ * @arg tc traffic control object
+ * @arg id identifier of statistical counter
+ *
+ * @return Value of requested statistic counter or 0.
+ */
+uint64_t rtnl_tc_get_stat(struct rtnl_tc *tc, enum rtnl_tc_stat id)
+{
+ if (id < 0 || id > RTNL_TC_STATS_MAX)
+ return 0;
+
+ return tc->tc_stats[id];
+}
+
+/** @} */
/**
* @name Utilities
@@ -428,148 +633,449 @@ int rtnl_tc_calc_cell_log(int cell_size)
* @{
*/
+/*
+ * COPYRIGHT NOTE:
+ * align_to_atm() and adjust_size() derived/coped from iproute2 source.
+ */
+
+/*
+ * The align to ATM cells is used for determining the (ATM) SAR
+ * alignment overhead at the ATM layer. (SAR = Segmentation And
+ * Reassembly). This is for example needed when scheduling packet on
+ * an ADSL connection. Note that the extra ATM-AAL overhead is _not_
+ * included in this calculation. This overhead is added in the kernel
+ * before doing the rate table lookup, as this gives better precision
+ * (as the table will always be aligned for 48 bytes).
+ * --Hawk, d.7/11-2004. <hawk@diku.dk>
+ */
+static unsigned int align_to_atm(unsigned int size)
+{
+ int linksize, cells;
+ cells = size / ATM_CELL_PAYLOAD;
+ if ((size % ATM_CELL_PAYLOAD) > 0)
+ cells++;
+
+ linksize = cells * ATM_CELL_SIZE; /* Use full cell size to add ATM tax */
+ return linksize;
+}
+
+static unsigned int adjust_size(unsigned int size, unsigned int mpu,
+ uint32_t linktype)
+{
+ if (size < mpu)
+ size = mpu;
+
+ switch (linktype) {
+ case ARPHRD_ATM:
+ return align_to_atm(size);
+
+ case ARPHRD_ETHER:
+ default:
+ return size;
+ }
+}
+
/**
* 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.
+ * @arg tc traffic control object
+ * @arg spec Rate specification
+ * @arg dst Destination buffer of RTNL_TC_RTABLE_SIZE uint32_t[].
*
* 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)];
+ * txtime = table[pktsize >> log2(mtu)];
* @endcode
*/
-int rtnl_tc_build_rate_table(uint32_t *dst, uint8_t mpu, uint8_t overhead,
- int cell, int rate)
+int rtnl_tc_build_rate_table(struct rtnl_tc *tc, struct rtnl_ratespec *spec,
+ uint32_t *dst)
{
- int i, size, cell_log;
-
- cell_log = rtnl_tc_calc_cell_log(cell);
- if (cell_log < 0)
- return cell_log;
+ uint32_t mtu = rtnl_tc_get_mtu(tc);
+ uint32_t linktype = rtnl_tc_get_linktype(tc);
+ uint8_t cell_log = spec->rs_cell_log;
+ unsigned int size, i;
+
+ spec->rs_mpu = rtnl_tc_get_mpu(tc);
+ spec->rs_overhead = rtnl_tc_get_overhead(tc);
+
+ if (mtu == 0)
+ mtu = 2047;
+
+ if (cell_log == UINT8_MAX) {
+ /*
+ * cell_log not specified, calculate it. It has to specify the
+ * minimum number of rshifts required to break the MTU to below
+ * RTNL_TC_RTABLE_SIZE.
+ */
+ cell_log = 0;
+ while ((mtu >> cell_log) >= RTNL_TC_RTABLE_SIZE)
+ cell_log++;
+ }
for (i = 0; i < RTNL_TC_RTABLE_SIZE; i++) {
- size = (i << cell_log) + overhead;
- if (size < mpu)
- size = mpu;
+ size = adjust_size((i + 1) << cell_log, spec->rs_mpu, linktype);
+ dst[i] = nl_us2ticks(rtnl_tc_calc_txtime(size, spec->rs_rate));
+ }
+
+ spec->rs_cell_align = -1;
+ spec->rs_cell_log = cell_log;
+
+ return 0;
+}
+
+/** @} */
+
+/**
+ * @name TC implementation of cache functions
+ */
+
+void rtnl_tc_free_data(struct nl_object *obj)
+{
+ struct rtnl_tc *tc = TC_CAST(obj);
+ struct rtnl_tc_ops *ops;
+
+ rtnl_link_put(tc->tc_link);
+ nl_data_free(tc->tc_opts);
+ nl_data_free(tc->tc_xstats);
+
+ if (tc->tc_subdata) {
+ ops = rtnl_tc_get_ops(tc);
+ if (ops && ops->to_free_data)
+ ops->to_free_data(tc, nl_data_get(tc->tc_subdata));
+
+ nl_data_free(tc->tc_subdata);
+ }
+}
+
+int rtnl_tc_clone(struct nl_object *dstobj, struct nl_object *srcobj)
+{
+ struct rtnl_tc *dst = TC_CAST(dstobj);
+ struct rtnl_tc *src = TC_CAST(srcobj);
+ struct rtnl_tc_ops *ops;
+
+ if (src->tc_link) {
+ nl_object_get(OBJ_CAST(src->tc_link));
+ dst->tc_link = src->tc_link;
+ }
+
+ if (src->tc_opts) {
+ dst->tc_opts = nl_data_clone(src->tc_opts);
+ if (!dst->tc_opts)
+ return -NLE_NOMEM;
+ }
+
+ if (src->tc_xstats) {
+ dst->tc_xstats = nl_data_clone(src->tc_xstats);
+ if (!dst->tc_xstats)
+ return -NLE_NOMEM;
+ }
- dst[i] = rtnl_tc_calc_txtime(size, rate);
+ if (src->tc_subdata) {
+ if (!(dst->tc_subdata = nl_data_clone(src->tc_subdata))) {
+ return -NLE_NOMEM;
+ }
+ }
+
+ ops = rtnl_tc_get_ops(src);
+ if (ops && ops->to_clone) {
+ void *a = rtnl_tc_data(dst), *b = rtnl_tc_data(src);
+
+ if (!a)
+ return 0;
+ else if (!b)
+ return -NLE_NOMEM;
+
+ return ops->to_clone(a, b);
}
return 0;
}
+static int tc_dump(struct rtnl_tc *tc, enum nl_dump_type type,
+ struct nl_dump_params *p)
+{
+ struct rtnl_tc_type_ops *type_ops;
+ struct rtnl_tc_ops *ops;
+ void *data = rtnl_tc_data(tc);
+
+ type_ops = tc_type_ops[tc->tc_type];
+ if (type_ops && type_ops->tt_dump[type])
+ type_ops->tt_dump[type](tc, p);
+
+ ops = rtnl_tc_get_ops(tc);
+ if (ops && ops->to_dump[type]) {
+ ops->to_dump[type](tc, data, p);
+ return 1;
+ }
+
+ return 0;
+}
+
+void rtnl_tc_dump_line(struct nl_object *obj, struct nl_dump_params *p)
+{
+ struct rtnl_tc_type_ops *type_ops;
+ struct rtnl_tc *tc = TC_CAST(obj);
+ struct nl_cache *link_cache;
+ char buf[32];
+
+ nl_new_line(p);
+
+ type_ops = tc_type_ops[tc->tc_type];
+ if (type_ops && type_ops->tt_dump_prefix)
+ nl_dump(p, "%s ", type_ops->tt_dump_prefix);
+
+ nl_dump(p, "%s ", tc->tc_kind);
+
+ if ((link_cache = nl_cache_mngt_require_safe("route/link"))) {
+ nl_dump(p, "dev %s ",
+ rtnl_link_i2name(link_cache, tc->tc_ifindex,
+ buf, sizeof(buf)));
+ } else
+ nl_dump(p, "dev %u ", tc->tc_ifindex);
+
+ nl_dump(p, "id %s ",
+ rtnl_tc_handle2str(tc->tc_handle, buf, sizeof(buf)));
+
+ nl_dump(p, "parent %s",
+ rtnl_tc_handle2str(tc->tc_parent, buf, sizeof(buf)));
+
+ tc_dump(tc, NL_DUMP_LINE, p);
+ nl_dump(p, "\n");
+
+ if (link_cache)
+ nl_cache_put(link_cache);
+}
+
+void rtnl_tc_dump_details(struct nl_object *obj, struct nl_dump_params *p)
+{
+ struct rtnl_tc *tc = TC_CAST(obj);
+
+ rtnl_tc_dump_line(OBJ_CAST(tc), p);
+
+ nl_dump_line(p, " ");
+
+ if (tc->ce_mask & TCA_ATTR_MTU)
+ nl_dump(p, " mtu %u", tc->tc_mtu);
+
+ if (tc->ce_mask & TCA_ATTR_MPU)
+ nl_dump(p, " mpu %u", tc->tc_mpu);
+
+ if (tc->ce_mask & TCA_ATTR_OVERHEAD)
+ nl_dump(p, " overhead %u", tc->tc_overhead);
+
+ if (!tc_dump(tc, NL_DUMP_DETAILS, p))
+ nl_dump(p, "no options");
+ nl_dump(p, "\n");
+}
+
+void rtnl_tc_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
+{
+ struct rtnl_tc *tc = TC_CAST(obj);
+ char *unit, fmt[64];
+ float res;
+
+ rtnl_tc_dump_details(OBJ_CAST(tc), p);
+
+ strcpy(fmt, " %7.2f %s %10u %10u %10u %10u %10u\n");
+
+ nl_dump_line(p,
+ " Stats: bytes packets drops overlimits" \
+ " qlen backlog\n");
+
+ res = nl_cancel_down_bytes(tc->tc_stats[RTNL_TC_BYTES], &unit);
+ if (*unit == 'B')
+ fmt[11] = '9';
+
+ nl_dump_line(p, fmt, res, unit,
+ tc->tc_stats[RTNL_TC_PACKETS],
+ tc->tc_stats[RTNL_TC_DROPS],
+ tc->tc_stats[RTNL_TC_OVERLIMITS],
+ tc->tc_stats[RTNL_TC_QLEN],
+ tc->tc_stats[RTNL_TC_BACKLOG]);
+
+ res = nl_cancel_down_bytes(tc->tc_stats[RTNL_TC_RATE_BPS], &unit);
+
+ strcpy(fmt, " %7.2f %s/s%9u pps");
+
+ if (*unit == 'B')
+ fmt[11] = '9';
+
+ nl_dump_line(p, fmt, res, unit, tc->tc_stats[RTNL_TC_RATE_PPS]);
+
+ tc_dump(tc, NL_DUMP_LINE, p);
+ nl_dump(p, "\n");
+}
+
+int rtnl_tc_compare(struct nl_object *aobj, struct nl_object *bobj,
+ uint32_t attrs, int flags)
+{
+ struct rtnl_tc *a = TC_CAST(aobj);
+ struct rtnl_tc *b = TC_CAST(bobj);
+ 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;
+}
+
/** @} */
/**
- * @name Traffic Control Handle Translations
- * @{
+ * @name Modules API
+ */
+
+struct rtnl_tc_ops *rtnl_tc_lookup_ops(enum rtnl_tc_type type, const char *kind)
+{
+ struct rtnl_tc_ops *ops;
+
+ nl_list_for_each_entry(ops, &tc_ops_list[type], to_list)
+ if (!strcmp(kind, ops->to_kind))
+ return ops;
+
+ return NULL;
+}
+
+struct rtnl_tc_ops *rtnl_tc_get_ops(struct rtnl_tc *tc)
+{
+ if (!tc->tc_ops)
+ tc->tc_ops = rtnl_tc_lookup_ops(tc->tc_type, tc->tc_kind);
+
+ return tc->tc_ops;
+}
+
+/**
+ * Register a traffic control module
+ * @arg ops traffic control module operations
*/
+int rtnl_tc_register(struct rtnl_tc_ops *ops)
+{
+ static int init = 0;
+
+ /*
+ * Initialiation hack, make sure list is initialized when
+ * the first tc module registers. Putting this in a
+ * separate __init would required correct ordering of init
+ * functions
+ */
+ if (!init) {
+ int i;
+
+ for (i = 0; i < __RTNL_TC_TYPE_MAX; i++)
+ nl_init_list_head(&tc_ops_list[i]);
+
+ init = 1;
+ }
+
+ if (!ops->to_kind || ops->to_type > RTNL_TC_TYPE_MAX)
+ BUG();
+
+ if (rtnl_tc_lookup_ops(ops->to_type, ops->to_kind))
+ return -NLE_EXIST;
+
+ nl_list_add_tail(&ops->to_list, &tc_ops_list[ops->to_type]);
+
+ return 0;
+}
/**
- * Convert a traffic control handle to a character string (Reentrant).
- * @arg handle traffic control handle
- * @arg buf destination buffer
- * @arg len buffer length
+ * Unregister a traffic control module
+ * @arg ops traffic control module operations
+ */
+void rtnl_tc_unregister(struct rtnl_tc_ops *ops)
+{
+ nl_list_del(&ops->to_list);
+}
+
+/**
+ * Return pointer to private data of traffic control object
+ * @arg tc traffic control object
*
- * Converts a tarffic control handle to a character string in the
- * form of \c MAJ:MIN and stores it in the specified destination buffer.
+ * Allocates the private traffic control object data section
+ * as necessary and returns it.
*
- * @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 Pointer to private tc data or NULL if allocation failed.
+ */
+void *rtnl_tc_data(struct rtnl_tc *tc)
+{
+ if (!tc->tc_subdata) {
+ size_t size;
+
+ if (!tc->tc_ops) {
+ if (!tc->tc_kind)
+ BUG();
+
+ if (!rtnl_tc_get_ops(tc))
+ return NULL;
+ }
+
+ if (!(size = tc->tc_ops->to_size))
+ BUG();
- return buf;
+ if (!(tc->tc_subdata = nl_data_alloc(NULL, size)))
+ return NULL;
+ }
+
+ return nl_data_get(tc->tc_subdata);
}
/**
- * Convert a charactering strint to a traffic control handle
- * @arg name traffic control handle as character string
- * @arg res destination buffer
+ * Check traffic control object type and return private data section
+ * @arg tc traffic control object
+ * @arg ops expected traffic control object operations
*
- * Converts the provided character string specifying a traffic
- * control handle to the corresponding numeric value.
+ * Checks whether the traffic control object matches the type
+ * specified with the traffic control object operations. If the
+ * type matches, the private tc object data is returned. If type
+ * mismatches, APPBUG() will print a application bug warning.
*
- * The handle must be provided in one of the following formats:
- * - root
- * - none
- * - XXXX:
- * - :YYYY
- * - XXXX:YYYY
- * - XXXXYYYY
+ * @see rtnl_tc_data()
*
- * @return 0 on success or a negative error code
+ * @return Pointer to private tc data or NULL if type mismatches.
*/
-int rtnl_tc_str2handle(const char *name, uint32_t *res)
+void *rtnl_tc_data_check(struct rtnl_tc *tc, struct rtnl_tc_ops *ops)
{
- char *colon, *end;
- uint32_t h;
+ if (tc->tc_ops != ops) {
+ char buf[64];
- if (!strcasecmp(name, "root")) {
- *res = TC_H_ROOT;
- return 0;
- }
+ snprintf(buf, sizeof(buf),
+ "tc object %p used in %s context but is of type %s",
+ tc, ops->to_kind, tc->tc_ops->to_kind);
+ APPBUG(buf);
- if (!strcasecmp(name, "none")) {
- *res = TC_H_UNSPEC;
- return 0;
+ return NULL;
}
- h = strtoul(name, &colon, 16);
-
- if (colon == name) {
- /* :YYYY */
- h = 0;
- if (':' != *colon)
- return -NLE_INVAL;
- }
+ return rtnl_tc_data(tc);
+}
- if (':' == *colon) {
- /* check if we would lose bits */
- if (TC_H_MAJ(h))
- return -NLE_RANGE;
- h <<= 16;
+struct nl_af_group tc_groups[] = {
+ { AF_UNSPEC, RTNLGRP_TC },
+ { END_OF_GROUP_LIST },
+};
- 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 -NLE_RANGE;
+void rtnl_tc_type_register(struct rtnl_tc_type_ops *ops)
+{
+ if (ops->tt_type > RTNL_TC_TYPE_MAX)
+ BUG();
- if ('\0' != *end)
- return -NLE_INVAL;
+ tc_type_ops[ops->tt_type] = ops;
+}
- *res = (h | l);
- }
- } else if ('\0' == *colon) {
- /* XXXXYYYY */
- *res = h;
- } else
- return -NLE_INVAL;
+void rtnl_tc_type_unregister(struct rtnl_tc_type_ops *ops)
+{
+ if (ops->tt_type > RTNL_TC_TYPE_MAX)
+ BUG();
- return 0;
+ tc_type_ops[ops->tt_type] = NULL;
}
/** @} */
diff --git a/lib/socket.c b/lib/socket.c
index 8083bbb6..5f61b382 100644
--- a/lib/socket.c
+++ b/lib/socket.c
@@ -6,16 +6,31 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
- * @ingroup core
+ * @ingroup core_types
* @defgroup socket Socket
+ *
+ * Representation of a netlink socket
+ *
+ * Related sections in the development guide:
+ * - @core_doc{core_sockets, Netlink Sockets}
+ *
* @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/socket.h>
+ * ~~~~
*/
-#include <netlink-local.h>
+#include "defs.h"
+
+#include <netlink-private/netlink.h>
+#include <netlink-private/socket.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/handlers.h>
@@ -43,17 +58,43 @@ static void __init init_default_cb(void)
}
static uint32_t used_ports_map[32];
+static NL_RW_LOCK(port_map_lock);
static uint32_t generate_local_port(void)
{
- int i, n;
+ int i, j, n, m;
+ static uint16_t idx_state = 0;
uint32_t pid = getpid() & 0x3FFFFF;
- for (i = 0; i < 32; i++) {
+ nl_write_lock(&port_map_lock);
+
+ if (idx_state == 0) {
+ uint32_t t = time(NULL);
+
+ /* from time to time (on average each 2^15 calls), the idx_state will
+ * be zero again. No problem, just "seed" anew with time(). */
+ idx_state = t ^ (t >> 16) ^ 0x3047;
+ } else
+ idx_state = idx_state + 20011; /* add prime number */
+
+ i = idx_state >> 5;
+ n = idx_state;
+ for (j = 0; j < 32; j++) {
+ /* walk the index somewhat randomized, with always leaving the block
+ * #0 as last. The reason is that libnl-1 will start at block #0,
+ * so just leave the first 32 ports preferably for libnl-1 owned sockets
+ * (this is relevant only if the applications ends up using both versions
+ * of the library and doesn't hurt otherwise). */
+ if (j == 31)
+ i = 0;
+ else
+ i = (((i-1) + 7) % 31) + 1;
+
if (used_ports_map[i] == 0xFFFFFFFF)
continue;
- for (n = 0; n < 32; n++) {
+ for (m = 0; m < 32; m++) {
+ n = (n + 13) % 32;
if (1UL & (used_ports_map[i] >> n))
continue;
@@ -62,26 +103,76 @@ static uint32_t generate_local_port(void)
/* PID_MAX_LIMIT is currently at 2^22, leaving 10 bit
* to, i.e. 1024 unique ports per application. */
- return pid + (n << 22);
+ nl_write_unlock(&port_map_lock);
+
+ return pid + (((uint32_t)n) << 22);
}
}
+ nl_write_unlock(&port_map_lock);
+
/* Out of sockets in our own PID namespace, what to do? FIXME */
- return UINT_MAX;
+ NL_DBG(1, "Warning: Ran out of unique local port namespace\n");
+ return UINT32_MAX;
}
static void release_local_port(uint32_t port)
{
int nr;
+ uint32_t mask;
- if (port == UINT_MAX)
+ if (port == UINT32_MAX)
return;
-
+
+ BUG_ON(port == 0);
+
nr = port >> 22;
- used_ports_map[nr / 32] &= ~(1 << nr % 32);
+ mask = 1UL << (nr % 32);
+ nr /= 32;
+
+ nl_write_lock(&port_map_lock);
+ BUG_ON((used_ports_map[nr] & mask) != mask);
+ used_ports_map[nr] &= ~mask;
+ nl_write_unlock(&port_map_lock);
}
+/** \cond skip */
+void _nl_socket_used_ports_release_all(const uint32_t *used_ports)
+{
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ if (used_ports[i] != 0) {
+ nl_write_lock(&port_map_lock);
+ for (; i < 32; i++) {
+ BUG_ON((used_ports_map[i] & used_ports[i]) != used_ports[i]);
+ used_ports_map[i] &= ~(used_ports[i]);
+ }
+ nl_write_unlock(&port_map_lock);
+ return;
+ }
+ }
+}
+
+void _nl_socket_used_ports_set(uint32_t *used_ports, uint32_t port)
+{
+ int nr;
+ int32_t mask;
+
+ nr = port >> 22;
+ mask = 1UL << (nr % 32);
+ nr /= 32;
+
+ /*
+ BUG_ON(port == UINT32_MAX || port == 0 || (getpid() & 0x3FFFFF) != (port & 0x3FFFFF));
+ BUG_ON(used_ports[nr] & mask);
+ */
+
+ used_ports[nr] |= mask;
+}
+/** \endcond */
+
/**
* @name Allocation
* @{
@@ -96,15 +187,13 @@ static struct nl_sock *__alloc_socket(struct nl_cb *cb)
return NULL;
sk->s_fd = -1;
- sk->s_cb = cb;
+ sk->s_cb = nl_cb_get(cb);
sk->s_local.nl_family = AF_NETLINK;
sk->s_peer.nl_family = AF_NETLINK;
sk->s_seq_expect = sk->s_seq_next = time(0);
- sk->s_local.nl_pid = generate_local_port();
- if (sk->s_local.nl_pid == UINT_MAX) {
- nl_socket_free(sk);
- return NULL;
- }
+
+ /* the port is 0 (unspecified), meaning NL_OWN_PORT */
+ sk->s_flags = NL_OWN_PORT;
return sk;
}
@@ -117,12 +206,18 @@ static struct nl_sock *__alloc_socket(struct nl_cb *cb)
struct nl_sock *nl_socket_alloc(void)
{
struct nl_cb *cb;
-
+ struct nl_sock *sk;
+
cb = nl_cb_alloc(default_cb);
if (!cb)
return NULL;
- return __alloc_socket(cb);
+ /* will increment cb reference count on success */
+ sk = __alloc_socket(cb);
+
+ nl_cb_put(cb);
+
+ return sk;
}
/**
@@ -139,7 +234,7 @@ struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb)
if (cb == NULL)
BUG();
- return __alloc_socket(nl_cb_get(cb));
+ return __alloc_socket(cb);
}
/**
@@ -234,13 +329,45 @@ void nl_socket_enable_auto_ack(struct nl_sock *sk)
/** @} */
+/** \cond skip */
+int _nl_socket_is_local_port_unspecified(struct nl_sock *sk)
+{
+ return (sk->s_local.nl_pid == 0);
+}
+
+uint32_t _nl_socket_generate_local_port_no_release(struct nl_sock *sk)
+{
+ uint32_t port;
+
+ /* reset the port to generate_local_port(), but do not release
+ * the previously generated port. */
+
+ port = generate_local_port();
+ sk->s_flags &= ~NL_OWN_PORT;
+ sk->s_local.nl_pid = port;
+ return port;
+}
+/** \endcond */
+
/**
* @name Source Idenficiation
* @{
*/
-uint32_t nl_socket_get_local_port(struct nl_sock *sk)
+uint32_t nl_socket_get_local_port(const struct nl_sock *sk)
{
+ if (sk->s_local.nl_pid == 0) {
+ /* modify the const argument sk. This is justified, because
+ * nobody ever saw the local_port from externally. So, we
+ * initilize it on first use.
+ *
+ * Note that this also means that you cannot call this function
+ * from multiple threads without synchronization. But nl_sock
+ * is not automatically threadsafe anyway, so the user is not
+ * allowed to do that.
+ */
+ return _nl_socket_generate_local_port_no_release((struct nl_sock *) sk);
+ }
return sk->s_local.nl_pid;
}
@@ -249,20 +376,18 @@ uint32_t nl_socket_get_local_port(struct nl_sock *sk)
* @arg sk Netlink socket.
* @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.
+ * Assigns a local port identifier to the socket.
+ *
+ * If port is 0, the port is reset to 'unspecified' as it is after newly
+ * calling nl_socket_alloc().
+ * Unspecified means, that the port will be generated automatically later
+ * on first use (either on nl_socket_get_local_port() or nl_connect()).
*/
void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port)
{
- if (port == 0) {
- port = generate_local_port();
- sk->s_flags &= ~NL_OWN_PORT;
- } else {
- if (!(sk->s_flags & NL_OWN_PORT))
- release_local_port(sk->s_local.nl_pid);
- sk->s_flags |= NL_OWN_PORT;
- }
-
+ if (!(sk->s_flags & NL_OWN_PORT))
+ release_local_port(sk->s_local.nl_pid);
+ sk->s_flags |= NL_OWN_PORT;
sk->s_local.nl_pid = port;
}
@@ -300,13 +425,17 @@ int nl_socket_add_memberships(struct nl_sock *sk, int group, ...)
va_start(ap, group);
while (group != 0) {
- if (group < 0)
+ if (group < 0) {
+ va_end(ap);
return -NLE_INVAL;
+ }
err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
&group, sizeof(group));
- if (err < 0)
+ if (err < 0) {
+ va_end(ap);
return -nl_syserr2nlerr(errno);
+ }
group = va_arg(ap, int);
}
@@ -344,13 +473,17 @@ int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...)
va_start(ap, group);
while (group != 0) {
- if (group < 0)
+ if (group < 0) {
+ va_end(ap);
return -NLE_INVAL;
+ }
err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
&group, sizeof(group));
- if (err < 0)
+ if (err < 0) {
+ va_end(ap);
return -nl_syserr2nlerr(errno);
+ }
group = va_arg(ap, int);
}
@@ -388,7 +521,7 @@ void nl_join_groups(struct nl_sock *sk, int groups)
* @{
*/
-uint32_t nl_socket_get_peer_port(struct nl_sock *sk)
+uint32_t nl_socket_get_peer_port(const struct nl_sock *sk)
{
return sk->s_peer.nl_pid;
}
@@ -398,6 +531,18 @@ void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port)
sk->s_peer.nl_pid = port;
}
+uint32_t nl_socket_get_peer_groups(const struct nl_sock *sk)
+{
+ return sk->s_peer.nl_groups;
+}
+
+void nl_socket_set_peer_groups(struct nl_sock *sk, uint32_t groups)
+{
+ sk->s_peer.nl_groups = groups;
+}
+
+
+
/** @} */
/**
@@ -405,7 +550,16 @@ void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port)
* @{
*/
-int nl_socket_get_fd(struct nl_sock *sk)
+/**
+ * Return the file descriptor of the backing socket
+ * @arg sk Netlink socket
+ *
+ * Only valid after calling nl_connect() to create and bind the respective
+ * socket.
+ *
+ * @return File descriptor or -1 if not available.
+ */
+int nl_socket_get_fd(const struct nl_sock *sk)
{
return sk->s_fd;
}
@@ -416,7 +570,7 @@ int nl_socket_get_fd(struct nl_sock *sk)
*
* @return 0 on success or a negative error code.
*/
-int nl_socket_set_nonblocking(struct nl_sock *sk)
+int nl_socket_set_nonblocking(const struct nl_sock *sk)
{
if (sk->s_fd == -1)
return -NLE_BAD_SOCK;
@@ -452,24 +606,27 @@ void nl_socket_disable_msg_peek(struct nl_sock *sk)
* @{
*/
-struct nl_cb *nl_socket_get_cb(struct nl_sock *sk)
+struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk)
{
return nl_cb_get(sk->s_cb);
}
void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb)
{
+ if (cb == NULL)
+ BUG();
+
nl_cb_put(sk->s_cb);
sk->s_cb = nl_cb_get(cb);
}
/**
- * Modify the callback handler associated to the socket
+ * Modify the callback handler associated with the socket
* @arg sk Netlink socket.
* @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
+ * @arg arg argument to be passed to callback function
*
* @see nl_cb_set
*/
@@ -480,6 +637,21 @@ int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type,
return nl_cb_set(sk->s_cb, type, kind, func, arg);
}
+/**
+ * Modify the error callback handler associated with the socket
+ * @arg sk Netlink socket.
+ * @arg kind kind of callback
+ * @arg func callback function
+ * @arg arg argument to be passed to callback function
+ *
+ * @see nl_cb_err
+ */
+int nl_socket_modify_err_cb(struct nl_sock *sk, enum nl_cb_kind kind,
+ nl_recvmsg_err_cb_t func, void *arg)
+{
+ return nl_cb_err(sk->s_cb, kind, func, arg);
+}
+
/** @} */
/**
@@ -529,6 +701,36 @@ int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf)
}
/**
+ * Set default message buffer size of netlink socket.
+ * @arg sk Netlink socket.
+ * @arg bufsize Default message buffer size in bytes.
+ *
+ * Sets the default message buffer size to the specified length in bytes.
+ * The default message buffer size limits the maximum message size the
+ * socket will be able to receive. It is generally recommneded to specify
+ * a buffer size no less than the size of a memory page.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nl_socket_set_msg_buf_size(struct nl_sock *sk, size_t bufsize)
+{
+ sk->s_bufsize = bufsize;
+
+ return 0;
+}
+
+/**
+ * Get default message buffer size of netlink socket.
+ * @arg sk Netlink socket.
+ *
+ * @return Size of default message buffer.
+ */
+size_t nl_socket_get_msg_buf_size(struct nl_sock *sk)
+{
+ return sk->s_bufsize;
+}
+
+/**
* Enable/disable credential passing on netlink socket.
* @arg sk Netlink socket.
* @arg state New state (0 - disabled, 1 - enabled)
diff --git a/lib/utils.c b/lib/utils.c
index e1fdae13..5cc9e94f 100644
--- a/lib/utils.c
+++ b/lib/utils.c
@@ -6,25 +6,50 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
/**
* @ingroup core
* @defgroup utils Utilities
+ *
+ * Collection of helper functions
+ *
* @{
+ *
+ * Header
+ * ------
+ * ~~~~{.c}
+ * #include <netlink/utils.h>
+ * ~~~~
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <linux/socket.h>
+#include <stdlib.h> /* exit() */
/**
- * Debug level
+ * Global variable indicating the desired level of debugging output.
+ *
+ * Level | Messages Printed
+ * ----- | ---------------------------------------------------------
+ * 0 | Debugging output disabled
+ * 1 | Warnings, important events and notifications
+ * 2 | More or less important debugging messages
+ * 3 | Repetitive events causing a flood of debugging messages
+ * 4 | Even less important messages
+ *
+ * If available, the variable will be initialized to the value of the
+ * environment variable `NLDBG`. The default value is 0 (disabled).
+ *
+ * For more information, see section @core_doc{_debugging, Debugging}.
*/
int nl_debug = 0;
+/** @cond SKIP */
+#ifdef NL_DEBUG
struct nl_dump_params nl_debug_dp = {
.dp_type = NL_DUMP_DETAILS,
};
@@ -41,6 +66,7 @@ static void __init nl_debug_init(void)
nl_debug_dp.dp_fd = stderr;
}
+#endif
int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *))
{
@@ -60,33 +86,42 @@ int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *))
continue;
num = strtol(buf, &end, 0);
- if (end == buf)
+ if (end == buf) {
+ fclose(fd);
return -NLE_INVAL;
+ }
- if (num == LONG_MIN || num == LONG_MAX)
+ if (num == LONG_MIN || num == LONG_MAX) {
+ fclose(fd);
return -NLE_RANGE;
+ }
while (*end == ' ' || *end == '\t')
end++;
goodlen = strcspn(end, "#\r\n\t ");
- if (goodlen == 0)
+ if (goodlen == 0) {
+ fclose(fd);
return -NLE_INVAL;
+ }
end[goodlen] = '\0';
err = cb(num, end);
- if (err < 0)
+ if (err < 0) {
+ fclose(fd);
return err;
+ }
}
fclose(fd);
return 0;
}
+/** @endcond */
/**
- * @name Unit Pretty-Printing
+ * @name Pretty Printing of Numbers
* @{
*/
@@ -97,6 +132,7 @@ int __nl_read_num_str_file(const char *path, int (*cb)(long, const char *))
*
* Cancels down a byte counter until it reaches a reasonable
* unit. The chosen unit is assigned to \a unit.
+ * This function assume 1024 bytes in one kilobyte
*
* @return The cancelled down byte counter in the new unit.
*/
@@ -125,30 +161,57 @@ double nl_cancel_down_bytes(unsigned long long l, char **unit)
* @arg l bit counter
* @arg unit destination unit pointer
*
- * Cancels downa bit counter until it reaches a reasonable
+ * Cancels down bit counter until it reaches a reasonable
* unit. The chosen unit is assigned to \a unit.
+ * This function assume 1000 bits in one kilobit
*
* @return The cancelled down bit counter in the new unit.
*/
double nl_cancel_down_bits(unsigned long long l, char **unit)
{
- if (l >= 1099511627776ULL) {
+ if (l >= 1000000000000ULL) {
*unit = "Tbit";
- return ((double) l) / 1099511627776ULL;
- } else if (l >= 1073741824) {
+ return ((double) l) / 1000000000000ULL;
+ }
+
+ if (l >= 1000000000) {
*unit = "Gbit";
- return ((double) l) / 1073741824;
- } else if (l >= 1048576) {
+ return ((double) l) / 1000000000;
+ }
+
+ if (l >= 1000000) {
*unit = "Mbit";
- return ((double) l) / 1048576;
- } else if (l >= 1024) {
+ return ((double) l) / 1000000;
+ }
+
+ if (l >= 1000) {
*unit = "Kbit";
- return ((double) l) / 1024;
- } else {
- *unit = "bit";
- return (double) l;
+ return ((double) l) / 1000;
}
-
+
+ *unit = "bit";
+ return (double) l;
+}
+
+int nl_rate2str(unsigned long long rate, int type, char *buf, size_t len)
+{
+ char *unit;
+ double frac;
+
+ switch (type) {
+ case NL_BYTE_RATE:
+ frac = nl_cancel_down_bytes(rate, &unit);
+ break;
+
+ case NL_BIT_RATE:
+ frac = nl_cancel_down_bits(rate, &unit);
+ break;
+
+ default:
+ BUG();
+ }
+
+ return snprintf(buf, len, "%.2f%s/s", frac, unit);
}
/**
@@ -193,6 +256,9 @@ double nl_cancel_down_us(uint32_t l, char **unit)
* - b,kb/k,m/mb,gb/g for bytes
* - bit,kbit/mbit/gbit
*
+ * This function assume 1000 bits in one kilobit and
+ * 1024 bytes in one kilobyte
+ *
* @return The number of bytes or -1 if the string is unparseable
*/
long nl_size2int(const char *str)
@@ -208,13 +274,13 @@ long nl_size2int(const char *str)
else if (!strcasecmp(p, "gb") || !strcasecmp(p, "g"))
l *= 1024*1024*1024;
else if (!strcasecmp(p, "gbit"))
- l *= 1024*1024*1024/8;
+ l *= 1000000000L/8;
else if (!strcasecmp(p, "mb") || !strcasecmp(p, "m"))
l *= 1024*1024;
else if (!strcasecmp(p, "mbit"))
- l *= 1024*1024/8;
+ l *= 1000000/8;
else if (!strcasecmp(p, "kbit"))
- l *= 1024/8;
+ l *= 1000/8;
else if (!strcasecmp(p, "bit"))
l /= 8;
else if (strcasecmp(p, "b") != 0)
@@ -224,6 +290,61 @@ long nl_size2int(const char *str)
return l;
}
+static const struct {
+ double limit;
+ const char *unit;
+} size_units[] = {
+ { 1024. * 1024. * 1024. * 1024. * 1024., "EiB" },
+ { 1024. * 1024. * 1024. * 1024., "TiB" },
+ { 1024. * 1024. * 1024., "GiB" },
+ { 1024. * 1024., "MiB" },
+ { 1024., "KiB" },
+ { 0., "B" },
+};
+
+/**
+ * Convert a size toa character string
+ * @arg size Size in number of bytes
+ * @arg buf Buffer to write character string to
+ * @arg len Size of buf
+ *
+ * This function converts a value in bytes to a human readable representation
+ * of it. The function uses IEC prefixes:
+ *
+ * @code
+ * 1024 bytes => 1 KiB
+ * 1048576 bytes => 1 MiB
+ * @endcode
+ *
+ * The highest prefix is used which ensures a result of >= 1.0, the result
+ * is provided as floating point number with a maximum precision of 2 digits:
+ * @code
+ * 965176 bytes => 942.55 KiB
+ * @endcode
+ *
+ * @return pointer to buf
+ */
+char *nl_size2str(const size_t size, char *buf, const size_t len)
+{
+ size_t i;
+
+ if (size == 0) {
+ snprintf(buf, len, "0B");
+ return buf;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(size_units); i++) {
+ if (size >= size_units[i].limit) {
+ snprintf(buf, len, "%.2g%s",
+ (double) size / size_units[i].limit,
+ size_units[i].unit);
+ return buf;
+ }
+ }
+
+ BUG();
+}
+
/**
* Convert a character string to a probability
* @arg str probability encoded as character string
@@ -264,12 +385,13 @@ long nl_prob2int(const char *str)
* @{
*/
-#ifdef USER_HZ
-static uint32_t user_hz = USER_HZ;
-#else
-static uint32_t user_hz = 100;
+#ifndef USER_HZ
+#define USER_HZ 100
#endif
+static uint32_t user_hz = USER_HZ;
+static uint32_t psched_hz = USER_HZ;
+
static double ticks_per_usec = 1.0f;
/* Retrieves the configured HZ and ticks/us value in the kernel.
@@ -299,6 +421,8 @@ static void __init get_psched_settings(void)
if (!got_hz)
user_hz = sysconf(_SC_CLK_TCK);
+ psched_hz = user_hz;
+
if (getenv("TICKS_PER_USEC")) {
double t = strtod(getenv("TICKS_PER_USEC"), NULL);
ticks_per_usec = t;
@@ -313,11 +437,22 @@ static void __init get_psched_settings(void)
strncpy(name, "/proc/net/psched", sizeof(name) - 1);
if ((fd = fopen(name, "r"))) {
- uint32_t tick, us;
- /* the file contains 4 hexadecimals, but we just use
- the first two of them */
- fscanf(fd, "%08x %08x", &tick, &us);
- ticks_per_usec = (double)tick/(double)us;
+ unsigned int ns_per_usec, ns_per_tick, nom, denom;
+
+ if (fscanf(fd, "%08x %08x %08x %08x",
+ &ns_per_usec, &ns_per_tick, &nom, &denom) != 4) {
+ NL_DBG(1, "Fatal error: can not read psched settings from \"%s\". " \
+ "Try to set TICKS_PER_USEC, PROC_NET_PSCHED or PROC_ROOT " \
+ "environment variables\n", name);
+ exit(1);
+ }
+
+ ticks_per_usec = (double) ns_per_usec /
+ (double) ns_per_tick;
+
+ if (nom == 1000000)
+ psched_hz = denom;
+
fclose(fd);
}
}
@@ -327,11 +462,18 @@ static void __init get_psched_settings(void)
/**
* Return the value of HZ
*/
-int nl_get_hz(void)
+int nl_get_user_hz(void)
{
return user_hz;
}
+/**
+ * Return the value of packet scheduler HZ
+ */
+int nl_get_psched_hz(void)
+{
+ return psched_hz;
+}
/**
* Convert micro seconds to ticks
@@ -404,10 +546,17 @@ int nl_str2msec(const char *str, uint64_t *result)
*/
char * nl_msec2str(uint64_t msec, char *buf, size_t len)
{
- int i, split[5];
- char *units[] = {"d", "h", "m", "s", "msec"};
+ uint64_t split[5];
+ size_t i;
+ static const char *units[5] = {"d", "h", "m", "s", "msec"};
+ char * const buf_orig = buf;
+
+ if (msec == 0) {
+ snprintf(buf, len, "0msec");
+ return buf_orig;
+ }
-#define _SPLIT(idx, unit) if ((split[idx] = msec / unit) > 0) msec %= unit
+#define _SPLIT(idx, unit) if ((split[idx] = msec / unit)) msec %= unit
_SPLIT(0, 86400000); /* days */
_SPLIT(1, 3600000); /* hours */
_SPLIT(2, 60000); /* minutes */
@@ -415,18 +564,17 @@ char * nl_msec2str(uint64_t msec, char *buf, size_t len)
#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);
- }
+ for (i = 0; i < ARRAY_SIZE(split) && len; i++) {
+ int l;
+ if (split[i] == 0)
+ continue;
+ l = snprintf(buf, len, "%s%" PRIu64 "%s",
+ (buf==buf_orig) ? "" : " ", split[i], units[i]);
+ buf += l;
+ len -= l;
}
- return buf;
+ return buf_orig;
}
/** @} */
@@ -436,7 +584,7 @@ char * nl_msec2str(uint64_t msec, char *buf, size_t len)
* @{
*/
-static struct trans_tbl nlfamilies[] = {
+static const struct trans_tbl nlfamilies[] = {
__ADD(NETLINK_ROUTE,route)
__ADD(NETLINK_USERSOCK,usersock)
__ADD(NETLINK_FIREWALL,firewall)
@@ -477,7 +625,7 @@ int nl_str2nlfamily(const char *name)
* @{
*/
-static struct trans_tbl llprotos[] = {
+static const struct trans_tbl llprotos[] = {
{0, "generic"},
__ADD(ARPHRD_ETHER,ether)
__ADD(ARPHRD_EETHER,eether)
@@ -506,6 +654,7 @@ static struct trans_tbl llprotos[] = {
#ifdef ARPHRD_HWX25
__ADD(ARPHRD_HWX25,hwx25)
#endif
+ __ADD(ARPHRD_CAN,can)
__ADD(ARPHRD_PPP,ppp)
__ADD(ARPHRD_HDLC,hdlc)
__ADD(ARPHRD_LAPB,lapb)
@@ -545,12 +694,19 @@ static struct trans_tbl llprotos[] = {
__ADD(ARPHRD_FCFABRIC+12,fcfb_12)
__ADD(ARPHRD_IEEE802_TR,tr)
__ADD(ARPHRD_IEEE80211,ieee802.11)
+ __ADD(ARPHRD_PHONET,phonet)
+#ifdef ARPHRD_CAIF
+ __ADD(ARPHRD_CAIF, caif)
+#endif
#ifdef ARPHRD_IEEE80211_PRISM
__ADD(ARPHRD_IEEE80211_PRISM, ieee802.11_prism)
#endif
#ifdef ARPHRD_VOID
__ADD(ARPHRD_VOID,void)
#endif
+#ifdef ARPHRD_NONE
+ __ADD(ARPHRD_NONE,nohdr)
+#endif
};
char * nl_llproto2str(int llproto, char *buf, size_t len)
@@ -571,7 +727,7 @@ int nl_str2llproto(const char *name)
* @{
*/
-static struct trans_tbl ether_protos[] = {
+static const struct trans_tbl ether_protos[] = {
__ADD(ETH_P_LOOP,loop)
__ADD(ETH_P_PUP,pup)
__ADD(ETH_P_PUPAT,pupat)
@@ -589,6 +745,7 @@ static struct trans_tbl ether_protos[] = {
__ADD(ETH_P_DIAG,diag)
__ADD(ETH_P_CUST,cust)
__ADD(ETH_P_SCA,sca)
+ __ADD(ETH_P_TEB,teb)
__ADD(ETH_P_RARP,rarp)
__ADD(ETH_P_ATALK,atalk)
__ADD(ETH_P_AARP,aarp)
@@ -597,6 +754,8 @@ static struct trans_tbl ether_protos[] = {
#endif
__ADD(ETH_P_IPX,ipx)
__ADD(ETH_P_IPV6,ipv6)
+ __ADD(ETH_P_PAUSE,pause)
+ __ADD(ETH_P_SLOW,slow)
#ifdef ETH_P_WCCP
__ADD(ETH_P_WCCP,wccp)
#endif
@@ -605,7 +764,15 @@ static struct trans_tbl ether_protos[] = {
__ADD(ETH_P_MPLS_UC,mpls_uc)
__ADD(ETH_P_MPLS_MC,mpls_mc)
__ADD(ETH_P_ATMMPOA,atmmpoa)
+ __ADD(ETH_P_LINK_CTL,link_ctl)
__ADD(ETH_P_ATMFATE,atmfate)
+ __ADD(ETH_P_PAE,pae)
+ __ADD(ETH_P_AOE,aoe)
+ __ADD(ETH_P_TIPC,tipc)
+ __ADD(ETH_P_1588,ieee1588)
+ __ADD(ETH_P_FCOE,fcoe)
+ __ADD(ETH_P_FIP,fip)
+ __ADD(ETH_P_EDSA,edsa)
__ADD(ETH_P_EDP2,edp2)
__ADD(ETH_P_802_3,802.3)
__ADD(ETH_P_AX25,ax25)
@@ -616,6 +783,7 @@ static struct trans_tbl ether_protos[] = {
__ADD(ETH_P_WAN_PPP,wan_ppp)
__ADD(ETH_P_PPP_MP,ppp_mp)
__ADD(ETH_P_LOCALTALK,localtalk)
+ __ADD(ETH_P_CAN,can)
__ADD(ETH_P_PPPTALK,ppptalk)
__ADD(ETH_P_TR_802_2,tr_802.2)
__ADD(ETH_P_MOBITEX,mobitex)
@@ -623,6 +791,12 @@ static struct trans_tbl ether_protos[] = {
__ADD(ETH_P_IRDA,irda)
__ADD(ETH_P_ECONET,econet)
__ADD(ETH_P_HDLC,hdlc)
+ __ADD(ETH_P_ARCNET,arcnet)
+ __ADD(ETH_P_DSA,dsa)
+ __ADD(ETH_P_TRAILER,trailer)
+ __ADD(ETH_P_PHONET,phonet)
+ __ADD(ETH_P_IEEE802154,ieee802154)
+ __ADD(ETH_P_CAIF,caif)
};
char *nl_ether_proto2str(int eproto, char *buf, size_t len)
@@ -701,7 +875,7 @@ void nl_new_line(struct nl_dump_params *params)
else if (params->dp_buf)
strncat(params->dp_buf, " ",
params->dp_buflen -
- sizeof(params->dp_buf) - 1);
+ strlen(params->dp_buf) - 1);
}
}
@@ -716,13 +890,15 @@ static void dump_one(struct nl_dump_params *parms, const char *fmt,
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);
+ if (vasprintf(&buf, fmt, args) >= 0) {
+ 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);
+ }
}
}
@@ -785,12 +961,14 @@ void __trans_list_clear(struct nl_list_head *head)
free(tl->a);
free(tl);
}
+
+ nl_init_list_head(head);
}
-char *__type2str(int type, char *buf, size_t len, struct trans_tbl *tbl,
- size_t tbl_len)
+char *__type2str(int type, char *buf, size_t len,
+ const struct trans_tbl *tbl, size_t tbl_len)
{
- int i;
+ size_t i;
for (i = 0; i < tbl_len; i++) {
if (tbl[i].i == type) {
snprintf(buf, len, "%s", tbl[i].a);
@@ -819,13 +997,13 @@ char *__list_type2str(int type, char *buf, size_t len,
}
char *__flags2str(int flags, char *buf, size_t len,
- struct trans_tbl *tbl, size_t tbl_len)
+ const struct trans_tbl *tbl, size_t tbl_len)
{
- int i;
+ size_t i;
int tmp = flags;
memset(buf, 0, len);
-
+
for (i = 0; i < tbl_len; i++) {
if (tbl[i].i & tmp) {
tmp &= ~tbl[i].i;
@@ -838,11 +1016,11 @@ char *__flags2str(int flags, char *buf, size_t len,
return buf;
}
-int __str2type(const char *buf, struct trans_tbl *tbl, size_t tbl_len)
+int __str2type(const char *buf, const struct trans_tbl *tbl, size_t tbl_len)
{
unsigned long l;
char *end;
- int i;
+ size_t i;
if (*buf == '\0')
return -NLE_INVAL;
@@ -879,9 +1057,11 @@ int __list_str2type(const char *buf, struct nl_list_head *head)
return (int) l;
}
-int __str2flags(const char *buf, struct trans_tbl *tbl, size_t tbl_len)
+int __str2flags(const char *buf, const struct trans_tbl *tbl, size_t tbl_len)
{
- int i, flags = 0, len;
+ int flags = 0;
+ size_t i;
+ size_t len; /* ptrdiff_t ? */
char *p = (char *) buf, *t;
for (;;) {
@@ -891,7 +1071,8 @@ int __str2flags(const char *buf, struct trans_tbl *tbl, size_t tbl_len)
t = strchr(p, ',');
len = t ? t - p : strlen(p);
for (i = 0; i < tbl_len; i++)
- if (!strncasecmp(tbl[i].a, p, len))
+ if (len == strlen(tbl[i].a) &&
+ !strncasecmp(tbl[i].a, p, len))
flags |= tbl[i].i;
if (!t)
@@ -930,6 +1111,58 @@ void dump_from_ops(struct nl_object *obj, struct nl_dump_params *params)
obj->ce_ops->oo_dump[type](obj, params);
}
+/**
+ * Check for library capabilities
+ *
+ * @arg capability capability identifier
+ *
+ * Check whether the loaded libnl library supports a certain capability.
+ * This is useful so that applications can workaround known issues of
+ * libnl that are fixed in newer library versions, without
+ * having a hard dependency on the new version. It is also useful, for
+ * capabilities that cannot easily be detected using autoconf tests.
+ * The capabilities are integer constants with name NL_CAPABILITY_*.
+ *
+ * As this function is intended to detect capabilities at runtime,
+ * you might not want to depend during compile time on the NL_CAPABILITY_*
+ * names. Instead you can use their numeric values which are guaranteed not to
+ * change meaning.
+ *
+ * @return non zero if libnl supports a certain capability, 0 otherwise.
+ **/
+int nl_has_capability (int capability)
+{
+ static const uint8_t caps[ ( NL_CAPABILITY_MAX + 7 ) / 8 ] = {
+#define _NL_ASSERT(expr) ( 0 * sizeof(struct { unsigned int x: ( (!!(expr)) ? 1 : -1 ); }) )
+#define _NL_SETV(i, r, v) \
+ ( _NL_ASSERT( (v) == 0 || (i) * 8 + (r) == (v) - 1 ) + \
+ ( (v) == 0 ? 0 : (1 << (r)) ) )
+#define _NL_SET(i, v0, v1, v2, v3, v4, v5, v6, v7) \
+ [(i)] = ( \
+ _NL_SETV((i), 0, (v0)) | _NL_SETV((i), 4, (v4)) | \
+ _NL_SETV((i), 1, (v1)) | _NL_SETV((i), 5, (v5)) | \
+ _NL_SETV((i), 2, (v2)) | _NL_SETV((i), 6, (v6)) | \
+ _NL_SETV((i), 3, (v3)) | _NL_SETV((i), 7, (v7)) )
+ _NL_SET(0,
+ NL_CAPABILITY_ROUTE_BUILD_MSG_SET_SCOPE,
+ NL_CAPABILITY_ROUTE_LINK_VETH_GET_PEER_OWN_REFERENCE,
+ NL_CAPABILITY_ROUTE_LINK_CLS_ADD_ACT_OWN_REFERENCE,
+ NL_CAPABILITY_NL_CONNECT_RETRY_GENERATE_PORT_ON_ADDRINUSE,
+ 0,
+ 0,
+ 0,
+ 0),
+#undef _NL_SET
+#undef _NL_SETV
+#undef _NL_ASSERT
+ };
+
+ if (capability <= 0 || capability > NL_CAPABILITY_MAX)
+ return 0;
+ capability--;
+ return (caps[capability / 8] & (1 << (capability % 8))) != 0;
+}
+
/** @endcond */
/** @} */
diff --git a/lib/version.c b/lib/version.c
new file mode 100644
index 00000000..0dcafa0f
--- /dev/null
+++ b/lib/version.c
@@ -0,0 +1,36 @@
+/*
+ * lib/version.c Run-time version information
+ *
+ * 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-2012 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup core
+ * @defgroup utils Utilities
+ *
+ * Run-time version information
+ *
+ * @{
+ */
+
+
+/**
+ * @name Run-time version information
+ * @{
+ */
+
+#include <netlink/version.h>
+
+const int nl_ver_num = LIBNL_VER_NUM;
+const int nl_ver_maj = LIBNL_VER_MAJ;
+const int nl_ver_min = LIBNL_VER_MIN;
+const int nl_ver_mic = LIBNL_VER_MIC;
+
+/** @} */
+
+/** @} */
diff --git a/libnl-2.0.pc.in b/libnl-3.0.pc.in
index e44f0fbe..b87e3dcd 100644
--- a/libnl-2.0.pc.in
+++ b/libnl-3.0.pc.in
@@ -6,5 +6,5 @@ includedir=@includedir@
Name: libnl
Description: Convenience library for netlink sockets
Version: @PACKAGE_VERSION@
-Libs: -L${libdir} -lnl
-Cflags: -I${includedir}
+Libs: -L${libdir} -lnl-@MAJ_VERSION@
+Cflags: -I${includedir}/libnl@MAJ_VERSION@
diff --git a/libnl-cli-3.0.pc.in b/libnl-cli-3.0.pc.in
new file mode 100644
index 00000000..d3638ba7
--- /dev/null
+++ b/libnl-cli-3.0.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libnl-cli
+Description: Command Line Interface library for netlink sockets
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lnl-cli-@MAJ_VERSION@
+Cflags: -I${includedir}
+Requires: libnl-3.0 libnl-genl-3.0 libnl-nf-3.0 libnl-route-3.0
diff --git a/libnl-genl-3.0.pc.in b/libnl-genl-3.0.pc.in
new file mode 100644
index 00000000..d6b69b8e
--- /dev/null
+++ b/libnl-genl-3.0.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libnl-genl
+Description: Generic Netlink Library
+Version: @PACKAGE_VERSION@
+Requires: libnl-3.0
+Libs: -L${libdir} -lnl-genl-@MAJ_VERSION@
+Cflags: -I${includedir}/libnl@MAJ_VERSION@
diff --git a/libnl-idiag-3.0.pc.in b/libnl-idiag-3.0.pc.in
new file mode 100644
index 00000000..9ce51005
--- /dev/null
+++ b/libnl-idiag-3.0.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libnl-idiag
+Description: Netlink Inet Diag Family Library
+Version: @PACKAGE_VERSION@
+Requires: libnl-3.0
+Libs: -L${libdir} -lnl-idiag-@MAJ_VERSION@
+Cflags: -I${includedir}/libnl@MAJ_VERSION@
+
diff --git a/libnl-nf-3.0.pc.in b/libnl-nf-3.0.pc.in
new file mode 100644
index 00000000..d82e1a69
--- /dev/null
+++ b/libnl-nf-3.0.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libnl-nf
+Description: Netfilter Netlink Library
+Version: @PACKAGE_VERSION@
+Requires: libnl-route-3.0
+Libs: -L${libdir} -lnl-nf-@MAJ_VERSION@
+Cflags: -I${includedir}/libnl@MAJ_VERSION@
diff --git a/libnl-route-3.0.pc.in b/libnl-route-3.0.pc.in
new file mode 100644
index 00000000..372a4f4d
--- /dev/null
+++ b/libnl-route-3.0.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libnl-route
+Description: Netlink Routing Family Library
+Version: @PACKAGE_VERSION@
+Requires: libnl-3.0
+Libs: -L${libdir} -lnl-route-@MAJ_VERSION@
+Cflags: -I${includedir}/libnl@MAJ_VERSION@
diff --git a/libnl.sym.in b/libnl.sym.in
new file mode 100644
index 00000000..df8888c6
--- /dev/null
+++ b/libnl.sym.in
@@ -0,0 +1,9 @@
+libnl_@MAJ_VERSION@ {
+global:
+ *;
+local:
+ _nl_socket_generate_local_port_no_release;
+ _nl_socket_is_local_port_unspecified;
+ _nl_socket_used_ports_release_all;
+ _nl_socket_used_ports_set;
+};
diff --git a/m4/ax_pkg_swig.m4 b/m4/ax_pkg_swig.m4
new file mode 100644
index 00000000..e112f3d3
--- /dev/null
+++ b/m4/ax_pkg_swig.m4
@@ -0,0 +1,135 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_pkg_swig.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_PKG_SWIG([major.minor.micro], [action-if-found], [action-if-not-found])
+#
+# DESCRIPTION
+#
+# This macro searches for a SWIG installation on your system. If found,
+# then SWIG is AC_SUBST'd; if not found, then $SWIG is empty. If SWIG is
+# found, then SWIG_LIB is set to the SWIG library path, and AC_SUBST'd.
+#
+# You can use the optional first argument to check if the version of the
+# available SWIG is greater than or equal to the value of the argument. It
+# should have the format: N[.N[.N]] (N is a number between 0 and 999. Only
+# the first N is mandatory.) If the version argument is given (e.g.
+# 1.3.17), AX_PKG_SWIG checks that the swig package is this version number
+# or higher.
+#
+# As usual, action-if-found is executed if SWIG is found, otherwise
+# action-if-not-found is executed.
+#
+# In configure.in, use as:
+#
+# AX_PKG_SWIG(1.3.17, [], [ AC_MSG_ERROR([SWIG is required to build..]) ])
+# AX_SWIG_ENABLE_CXX
+# AX_SWIG_MULTI_MODULE_SUPPORT
+# AX_SWIG_PYTHON
+#
+# LICENSE
+#
+# Copyright (c) 2008 Sebastian Huber <sebastian-huber@web.de>
+# Copyright (c) 2008 Alan W. Irwin <irwin@beluga.phys.uvic.ca>
+# Copyright (c) 2008 Rafael Laboissiere <rafael@laboissiere.net>
+# Copyright (c) 2008 Andrew Collier <colliera@ukzn.ac.za>
+# Copyright (c) 2011 Murray Cumming <murrayc@openismus.com>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 8
+
+AC_DEFUN([AX_PKG_SWIG],[
+ # Ubuntu has swig 2.0 as /usr/bin/swig2.0
+ AC_PATH_PROGS([SWIG],[swig swig2.0])
+ if test -z "$SWIG" ; then
+ m4_ifval([$3],[$3],[:])
+ elif test -n "$1" ; then
+ AC_MSG_CHECKING([SWIG version])
+ [swig_version=`$SWIG -version 2>&1 | grep 'SWIG Version' | sed 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/g'`]
+ AC_MSG_RESULT([$swig_version])
+ if test -n "$swig_version" ; then
+ # Calculate the required version number components
+ [required=$1]
+ [required_major=`echo $required | sed 's/[^0-9].*//'`]
+ if test -z "$required_major" ; then
+ [required_major=0]
+ fi
+ [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
+ [required_minor=`echo $required | sed 's/[^0-9].*//'`]
+ if test -z "$required_minor" ; then
+ [required_minor=0]
+ fi
+ [required=`echo $required | sed 's/[0-9]*[^0-9]//'`]
+ [required_patch=`echo $required | sed 's/[^0-9].*//'`]
+ if test -z "$required_patch" ; then
+ [required_patch=0]
+ fi
+ # Calculate the available version number components
+ [available=$swig_version]
+ [available_major=`echo $available | sed 's/[^0-9].*//'`]
+ if test -z "$available_major" ; then
+ [available_major=0]
+ fi
+ [available=`echo $available | sed 's/[0-9]*[^0-9]//'`]
+ [available_minor=`echo $available | sed 's/[^0-9].*//'`]
+ if test -z "$available_minor" ; then
+ [available_minor=0]
+ fi
+ [available=`echo $available | sed 's/[0-9]*[^0-9]//'`]
+ [available_patch=`echo $available | sed 's/[^0-9].*//'`]
+ if test -z "$available_patch" ; then
+ [available_patch=0]
+ fi
+ # Convert the version tuple into a single number for easier comparison.
+ # Using base 100 should be safe since SWIG internally uses BCD values
+ # to encode its version number.
+ required_swig_vernum=`expr $required_major \* 10000 \
+ \+ $required_minor \* 100 \+ $required_patch`
+ available_swig_vernum=`expr $available_major \* 10000 \
+ \+ $available_minor \* 100 \+ $available_patch`
+
+ if test $available_swig_vernum -lt $required_swig_vernum; then
+ AC_MSG_WARN([SWIG version >= $1 is required. You have $swig_version.])
+ SWIG=''
+ m4_ifval([$3],[$3],[])
+ else
+ AC_MSG_CHECKING([for SWIG library])
+ SWIG_LIB=`$SWIG -swiglib`
+ AC_MSG_RESULT([$SWIG_LIB])
+ m4_ifval([$2],[$2],[])
+ fi
+ else
+ AC_MSG_WARN([cannot determine SWIG version])
+ SWIG=''
+ m4_ifval([$3],[$3],[])
+ fi
+ fi
+ AC_SUBST([SWIG_LIB])
+])
diff --git a/m4/ax_python.m4 b/m4/ax_python.m4
new file mode 100644
index 00000000..1bc9d8a2
--- /dev/null
+++ b/m4/ax_python.m4
@@ -0,0 +1,97 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_python.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_PYTHON
+#
+# DESCRIPTION
+#
+# This macro does a complete Python development environment check.
+#
+# It recurses through several python versions (from 2.1 to 2.6 in this
+# version), looking for an executable. When it finds an executable, it
+# looks to find the header files and library.
+#
+# It sets PYTHON_BIN to the name of the python executable,
+# PYTHON_INCLUDE_DIR to the directory holding the header files, and
+# PYTHON_LIB to the name of the Python library.
+#
+# This macro calls AC_SUBST on PYTHON_BIN (via AC_CHECK_PROG),
+# PYTHON_INCLUDE_DIR and PYTHON_LIB.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Michael Tindal
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 9
+
+AC_DEFUN([AX_PYTHON],
+[AC_MSG_CHECKING(for python build information)
+AC_MSG_RESULT([])
+for python in python2.6 python2.5 python2.4 python2.3 python2.2 python2.1 python; do
+AC_CHECK_PROGS(PYTHON_BIN, [$python])
+ax_python_bin=$PYTHON_BIN
+if test x$ax_python_bin != x; then
+ AC_CHECK_LIB($ax_python_bin, main, ax_python_lib=$ax_python_bin, ax_python_lib=no)
+ AC_CHECK_HEADER([$ax_python_bin/Python.h],
+ [[ax_python_header=`locate $ax_python_bin/Python.h | sed -e s,/Python.h,,`]],
+ ax_python_header=no)
+ if test $ax_python_lib != no; then
+ if test $ax_python_header != no; then
+ break;
+ fi
+ fi
+fi
+done
+if test x$ax_python_bin = x; then
+ ax_python_bin=no
+fi
+if test x$ax_python_header = x; then
+ ax_python_header=no
+fi
+if test x$ax_python_lib = x; then
+ ax_python_lib=no
+fi
+
+AC_MSG_RESULT([ results of the Python check:])
+AC_MSG_RESULT([ Binary: $ax_python_bin])
+AC_MSG_RESULT([ Library: $ax_python_lib])
+AC_MSG_RESULT([ Include Dir: $ax_python_header])
+
+if test x$ax_python_header != xno; then
+ PYTHON_INCLUDE_DIR=$ax_python_header
+ AC_SUBST(PYTHON_INCLUDE_DIR)
+fi
+if test x$ax_python_lib != xno; then
+ PYTHON_LIB=$ax_python_lib
+ AC_SUBST(PYTHON_LIB)
+fi
+])dnl
diff --git a/m4/ax_python_devel.m4 b/m4/ax_python_devel.m4
new file mode 100644
index 00000000..a62b860d
--- /dev/null
+++ b/m4/ax_python_devel.m4
@@ -0,0 +1,325 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_python_devel.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_PYTHON_DEVEL([version])
+#
+# DESCRIPTION
+#
+# Note: Defines as a precious variable "PYTHON_VERSION". Don't override it
+# in your configure.ac.
+#
+# This macro checks for Python and tries to get the include path to
+# 'Python.h'. It provides the $(PYTHON_CPPFLAGS) and $(PYTHON_LDFLAGS)
+# output variables. It also exports $(PYTHON_EXTRA_LIBS) and
+# $(PYTHON_EXTRA_LDFLAGS) for embedding Python in your code.
+#
+# You can search for some particular version of Python by passing a
+# parameter to this macro, for example ">= '2.3.1'", or "== '2.4'". Please
+# note that you *have* to pass also an operator along with the version to
+# match, and pay special attention to the single quotes surrounding the
+# version number. Don't use "PYTHON_VERSION" for this: that environment
+# variable is declared as precious and thus reserved for the end-user.
+#
+# This macro should work for all versions of Python >= 2.1.0. As an end
+# user, you can disable the check for the python version by setting the
+# PYTHON_NOVERSIONCHECK environment variable to something else than the
+# empty string.
+#
+# If you need to use this macro for an older Python version, please
+# contact the authors. We're always open for feedback.
+#
+# LICENSE
+#
+# Copyright (c) 2009 Sebastian Huber <sebastian-huber@web.de>
+# Copyright (c) 2009 Alan W. Irwin <irwin@beluga.phys.uvic.ca>
+# Copyright (c) 2009 Rafael Laboissiere <rafael@laboissiere.net>
+# Copyright (c) 2009 Andrew Collier <colliera@ukzn.ac.za>
+# Copyright (c) 2009 Matteo Settenvini <matteo@member.fsf.org>
+# Copyright (c) 2009 Horst Knorr <hk_classes@knoda.org>
+#
+# 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 3 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 8
+
+AU_ALIAS([AC_PYTHON_DEVEL], [AX_PYTHON_DEVEL])
+AC_DEFUN([AX_PYTHON_DEVEL],[
+ #
+ # Allow the use of a (user set) custom python version
+ #
+ AC_ARG_VAR([PYTHON_VERSION],[The installed Python
+ version to use, for example '2.3'. This string
+ will be appended to the Python interpreter
+ canonical name.])
+
+ AC_PATH_PROG([PYTHON],[python[$PYTHON_VERSION]])
+ if test -z "$PYTHON"; then
+ AC_MSG_ERROR([Cannot find python$PYTHON_VERSION in your system path])
+ PYTHON_VERSION=""
+ fi
+
+ #
+ # Check for a version of Python >= 2.1.0
+ #
+ AC_MSG_CHECKING([for a version of Python >= '2.1.0'])
+ ac_supports_python_ver=`$PYTHON -c "import sys; \
+ ver = sys.version.split ()[[0]]; \
+ print (ver >= '2.1.0')"`
+ if test "$ac_supports_python_ver" != "True"; then
+ if test -z "$PYTHON_NOVERSIONCHECK"; then
+ AC_MSG_RESULT([no])
+ AC_MSG_FAILURE([
+This version of the AC@&t@_PYTHON_DEVEL macro
+doesn't work properly with versions of Python before
+2.1.0. You may need to re-run configure, setting the
+variables PYTHON_CPPFLAGS, PYTHON_LDFLAGS, PYTHON_SITE_PKG,
+PYTHON_EXTRA_LIBS and PYTHON_EXTRA_LDFLAGS by hand.
+Moreover, to disable this check, set PYTHON_NOVERSIONCHECK
+to something else than an empty string.
+])
+ else
+ AC_MSG_RESULT([skip at user request])
+ fi
+ else
+ AC_MSG_RESULT([yes])
+ fi
+
+ #
+ # if the macro parameter ``version'' is set, honour it
+ #
+ if test -n "$1"; then
+ AC_MSG_CHECKING([for a version of Python $1])
+ ac_supports_python_ver=`$PYTHON -c "import sys; \
+ ver = sys.version.split ()[[0]]; \
+ print (ver $1)"`
+ if test "$ac_supports_python_ver" = "True"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([this package requires Python $1.
+If you have it installed, but it isn't the default Python
+interpreter in your system path, please pass the PYTHON_VERSION
+variable to configure. See ``configure --help'' for reference.
+])
+ PYTHON_VERSION=""
+ fi
+ fi
+
+ #
+ # Check if you have distutils, else fail
+ #
+ AC_MSG_CHECKING([for the distutils Python package])
+ ac_distutils_result=`$PYTHON -c "import distutils" 2>&1`
+ if test -z "$ac_distutils_result"; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([cannot import Python module "distutils".
+Please check your Python installation. The error was:
+$ac_distutils_result])
+ PYTHON_VERSION=""
+ fi
+
+ #
+ # Check for Python include path
+ #
+ AC_MSG_CHECKING([for Python include path])
+ if test -z "$PYTHON_CPPFLAGS"; then
+ python_path=`$PYTHON -c "import distutils.sysconfig; \
+ print (distutils.sysconfig.get_python_inc ());"`
+ if test -n "${python_path}"; then
+ python_path="-I$python_path"
+ fi
+ PYTHON_CPPFLAGS=$python_path
+ fi
+ AC_MSG_RESULT([$PYTHON_CPPFLAGS])
+ AC_SUBST([PYTHON_CPPFLAGS])
+
+ #
+ # Check for Python library path
+ #
+ AC_MSG_CHECKING([for Python library path])
+ if test -z "$PYTHON_LDFLAGS"; then
+ # (makes two attempts to ensure we've got a version number
+ # from the interpreter)
+ ac_python_version=`cat<<EOD | $PYTHON -
+
+# join all versioning strings, on some systems
+# major/minor numbers could be in different list elements
+from distutils.sysconfig import *
+ret = ''
+for e in get_config_vars ('VERSION'):
+ if (e != None):
+ ret += e
+print (ret)
+EOD`
+
+ if test -z "$ac_python_version"; then
+ if test -n "$PYTHON_VERSION"; then
+ ac_python_version=$PYTHON_VERSION
+ else
+ ac_python_version=`$PYTHON -c "import sys; \
+ print (sys.version[[:3]])"`
+ fi
+ fi
+
+ # Make the versioning information available to the compiler
+ AC_DEFINE_UNQUOTED([HAVE_PYTHON], ["$ac_python_version"],
+ [If available, contains the Python version number currently in use.])
+
+ # First, the library directory:
+ ac_python_libdir=`cat<<EOD | $PYTHON -
+
+# There should be only one
+import distutils.sysconfig
+for e in distutils.sysconfig.get_config_vars ('LIBDIR'):
+ if e != None:
+ print (e)
+ break
+EOD`
+
+ # Before checking for libpythonX.Y, we need to know
+ # the extension the OS we're on uses for libraries
+ # (we take the first one, if there's more than one fix me!):
+ ac_python_soext=`$PYTHON -c \
+ "import distutils.sysconfig; \
+ print (distutils.sysconfig.get_config_vars('SO')[[0]])"`
+
+ # Now, for the library:
+ ac_python_soname=`$PYTHON -c \
+ "import distutils.sysconfig; \
+ print (distutils.sysconfig.get_config_vars('LDLIBRARY')[[0]])"`
+
+ # Strip away extension from the end to canonicalize its name:
+ ac_python_library=`echo "$ac_python_soname" | sed "s/${ac_python_soext}$//"`
+
+ # This small piece shamelessly adapted from PostgreSQL python macro;
+ # credits goes to momjian, I think. I'd like to put the right name
+ # in the credits, if someone can point me in the right direction... ?
+ #
+ if test -n "$ac_python_libdir" -a -n "$ac_python_library" \
+ -a x"$ac_python_library" != x"$ac_python_soname"
+ then
+ # use the official shared library
+ ac_python_library=`echo "$ac_python_library" | sed "s/^lib//"`
+ PYTHON_LDFLAGS="-L$ac_python_libdir -l$ac_python_library"
+ else
+ # old way: use libpython from python_configdir
+ ac_python_libdir=`$PYTHON -c \
+ "from distutils.sysconfig import get_python_lib as f; \
+ import os; \
+ print (os.path.join(f(plat_specific=1, standard_lib=1), 'config'));"`
+ PYTHON_LDFLAGS="-L$ac_python_libdir -lpython$ac_python_version"
+ fi
+
+ if test -z "PYTHON_LDFLAGS"; then
+ AC_MSG_ERROR([
+ Cannot determine location of your Python DSO. Please check it was installed with
+ dynamic libraries enabled, or try setting PYTHON_LDFLAGS by hand.
+ ])
+ fi
+ fi
+ AC_MSG_RESULT([$PYTHON_LDFLAGS])
+ AC_SUBST([PYTHON_LDFLAGS])
+
+ #
+ # Check for site packages
+ #
+ AC_MSG_CHECKING([for Python site-packages path])
+ if test -z "$PYTHON_SITE_PKG"; then
+ PYTHON_SITE_PKG=`$PYTHON -c "import distutils.sysconfig; \
+ print (distutils.sysconfig.get_python_lib(0,0));"`
+ fi
+ AC_MSG_RESULT([$PYTHON_SITE_PKG])
+ AC_SUBST([PYTHON_SITE_PKG])
+
+ #
+ # libraries which must be linked in when embedding
+ #
+ AC_MSG_CHECKING(python extra libraries)
+ if test -z "$PYTHON_EXTRA_LIBS"; then
+ PYTHON_EXTRA_LIBS=`$PYTHON -c "import distutils.sysconfig; \
+ conf = distutils.sysconfig.get_config_var; \
+ print (conf('LOCALMODLIBS') + ' ' + conf('LIBS'))"`
+ fi
+ AC_MSG_RESULT([$PYTHON_EXTRA_LIBS])
+ AC_SUBST(PYTHON_EXTRA_LIBS)
+
+ #
+ # linking flags needed when embedding
+ #
+ AC_MSG_CHECKING(python extra linking flags)
+ if test -z "$PYTHON_EXTRA_LDFLAGS"; then
+ PYTHON_EXTRA_LDFLAGS=`$PYTHON -c "import distutils.sysconfig; \
+ conf = distutils.sysconfig.get_config_var; \
+ print (conf('LINKFORSHARED'))"`
+ fi
+ AC_MSG_RESULT([$PYTHON_EXTRA_LDFLAGS])
+ AC_SUBST(PYTHON_EXTRA_LDFLAGS)
+
+ #
+ # final check to see if everything compiles alright
+ #
+ AC_MSG_CHECKING([consistency of all components of python development environment])
+ # save current global flags
+ ac_save_LIBS="$LIBS"
+ ac_save_CPPFLAGS="$CPPFLAGS"
+ LIBS="$ac_save_LIBS $PYTHON_LDFLAGS $PYTHON_EXTRA_LDFLAGS $PYTHON_EXTRA_LIBS"
+ CPPFLAGS="$ac_save_CPPFLAGS $PYTHON_CPPFLAGS"
+ AC_LANG_PUSH([C])
+ AC_LINK_IFELSE([
+ AC_LANG_PROGRAM([[#include <Python.h>]],
+ [[Py_Initialize();]])
+ ],[pythonexists=yes],[pythonexists=no])
+ AC_LANG_POP([C])
+ # turn back to default flags
+ CPPFLAGS="$ac_save_CPPFLAGS"
+ LIBS="$ac_save_LIBS"
+
+ AC_MSG_RESULT([$pythonexists])
+
+ if test ! "x$pythonexists" = "xyes"; then
+ AC_MSG_FAILURE([
+ Could not link test program to Python. Maybe the main Python library has been
+ installed in some non-standard library path. If so, pass it to configure,
+ via the LDFLAGS environment variable.
+ Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib"
+ ============================================================================
+ ERROR!
+ You probably have to install the development version of the Python package
+ for your distribution. The exact name of this package varies among them.
+ ============================================================================
+ ])
+ PYTHON_VERSION=""
+ fi
+
+ #
+ # all done!
+ #
+])
diff --git a/m4/ax_swig_python.m4 b/m4/ax_swig_python.m4
new file mode 100644
index 00000000..8fd3df5a
--- /dev/null
+++ b/m4/ax_swig_python.m4
@@ -0,0 +1,64 @@
+# ===========================================================================
+# http://www.gnu.org/software/autoconf-archive/ax_swig_python.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_SWIG_PYTHON([use-shadow-classes = {no, yes}])
+#
+# DESCRIPTION
+#
+# Checks for Python and provides the $(AX_SWIG_PYTHON_CPPFLAGS), and
+# $(AX_SWIG_PYTHON_OPT) output variables.
+#
+# $(AX_SWIG_PYTHON_OPT) contains all necessary SWIG options to generate
+# code for Python. Shadow classes are enabled unless the value of the
+# optional first argument is exactly 'no'. If you need multi module
+# support (provided by the AX_SWIG_MULTI_MODULE_SUPPORT macro) use
+# $(AX_SWIG_PYTHON_LIBS) to link against the appropriate library. It
+# contains the SWIG Python runtime library that is needed by the type
+# check system for example.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Sebastian Huber <sebastian-huber@web.de>
+# Copyright (c) 2008 Alan W. Irwin <irwin@beluga.phys.uvic.ca>
+# Copyright (c) 2008 Rafael Laboissiere <rafael@laboissiere.net>
+# Copyright (c) 2008 Andrew Collier <colliera@ukzn.ac.za>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+# Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+# As a special exception, the respective Autoconf Macro's copyright owner
+# gives unlimited permission to copy, distribute and modify the configure
+# scripts that are the output of Autoconf when processing the Macro. You
+# need not follow the terms of the GNU General Public License when using
+# or distributing such scripts, even though portions of the text of the
+# Macro appear in them. The GNU General Public License (GPL) does govern
+# all other use of the material that constitutes the Autoconf Macro.
+#
+# This special exception to the GPL applies to versions of the Autoconf
+# Macro released by the Autoconf Archive. When you make and distribute a
+# modified version of the Autoconf Macro, you may extend this special
+# exception to the GPL to apply to your modified version as well.
+
+#serial 7
+
+AU_ALIAS([SWIG_PYTHON], [AX_SWIG_PYTHON])
+AC_DEFUN([AX_SWIG_PYTHON],[
+ AC_REQUIRE([AX_PKG_SWIG])
+ AC_REQUIRE([AX_PYTHON_DEVEL])
+ test "x$1" != "xno" || swig_shadow=" -noproxy"
+ AC_SUBST([AX_SWIG_PYTHON_OPT],[-python$swig_shadow])
+ AC_SUBST([AX_SWIG_PYTHON_CPPFLAGS],[$PYTHON_CPPFLAGS])
+])
diff --git a/make.log b/make.log
new file mode 100644
index 00000000..d4379b50
--- /dev/null
+++ b/make.log
@@ -0,0 +1,355 @@
+find: `vendor': No such file or directory
+============================================
+PLATFORM_VERSION_CODENAME=REL
+PLATFORM_VERSION=7.0
+TARGET_PRODUCT=aosp_bullhead
+TARGET_BUILD_VARIANT=eng
+TARGET_BUILD_TYPE=release
+TARGET_BUILD_APPS=
+TARGET_ARCH=arm64
+TARGET_ARCH_VARIANT=armv8-a
+TARGET_CPU_VARIANT=cortex-a53
+TARGET_2ND_ARCH=arm
+TARGET_2ND_ARCH_VARIANT=armv7-a-neon
+TARGET_2ND_CPU_VARIANT=cortex-a53.a57
+HOST_ARCH=x86_64
+HOST_2ND_ARCH=x86
+HOST_OS=linux
+HOST_OS_EXTRA=Linux-3.13.0-95-generic-x86_64-with-Ubuntu-14.04-trusty
+HOST_CROSS_OS=windows
+HOST_CROSS_ARCH=x86
+HOST_CROSS_2ND_ARCH=x86_64
+HOST_BUILD_TYPE=release
+BUILD_ID=NYC
+OUT_DIR=out
+AUX_OS_VARIANT_LIST=
+============================================
+make: Entering directory `/usr/local/google/home/pstew/build/aosp'
+ninja: no work to do.
+ninja: no work to do.
+Running kati to generate build-aosp_bullhead-mmm-external_libnl_Android.mk.ninja...
+No need to regenerate ninja file
+Starting build with ninja
+ninja: Entering directory `.'
+[ 1% 1/68] target thumb C: libnl_32 <= external/libnl/lib/cache.c
+[ 2% 2/68] target thumb C: libnl_32 <= external/libnl/lib/data.c
+external/libnl/lib/data.c:119:24: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ memcpy(data->d_data + data->d_size, buf, size);
+ ~~~~~~~~~~~~ ^
+external/libnl/lib/data.c:121:24: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ memset(data->d_data + data->d_size, 0, size);
+ ~~~~~~~~~~~~ ^
+2 warnings generated.
+[ 4% 3/68] target thumb C: libnl_32 <= external/libnl/lib/nl.c
+external/libnl/lib/nl.c:694:18: warning: comparison of integers of different signs: '__kernel_size_t' (aka 'unsigned int') and 'ssize_t' (aka 'int') [-Wsign-compare]
+ if (iov.iov_len < n || (msg.msg_flags & MSG_TRUNC)) {
+ ~~~~~~~~~~~ ^ ~
+external/libnl/lib/nl.c:786:29: warning: missing field 'nl_pad' initializer [-Wmissing-field-initializers]
+ struct sockaddr_nl nla = {0};
+ ^
+external/libnl/lib/nl.c:917:23: warning: comparison of integers of different signs: '__u32' (aka 'unsigned int') and 'int' [-Wsign-compare]
+ if (hdr->nlmsg_len < nlmsg_size(sizeof(*e))) {
+ ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~
+3 warnings generated.
+[ 5% 4/68] target thumb C: libnl_32 <= external/libnl/lib/cache_mngr.c
+[ 7% 5/68] target thumb C: libnl_32 <= external/libnl/lib/socket.c
+[ 8% 6/68] target thumb C: libnl_32 <= external/libnl/lib/addr.c
+external/libnl/lib/addr.c:707:14: warning: comparison of integers of different signs: 'socklen_t' (aka 'int') and 'unsigned int' [-Wsign-compare]
+ if (*salen < sizeof(*sai))
+ ~~~~~~ ^ ~~~~~~~~~~~~
+external/libnl/lib/addr.c:719:14: warning: comparison of integers of different signs: 'socklen_t' (aka 'int') and 'unsigned int' [-Wsign-compare]
+ if (*salen < sizeof(*sa6))
+ ~~~~~~ ^ ~~~~~~~~~~~~
+external/libnl/lib/addr.c:990:24: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
+ if (addr->a_prefixlen != (8 * addr->a_len)) {
+ ~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~
+3 warnings generated.
+[ 10% 7/68] target thumb C: libnl_32 <= external/libnl/lib/fib_lookup/lookup.c
+external/libnl/lib/fib_lookup/lookup.c:215:30: warning: missing field 'fl_fwmark' initializer [-Wmissing-field-initializers]
+ struct fib_result_nl fr = {0};
+ ^
+1 warning generated.
+[ 11% 8/68] target thumb C: libnl_32 <= external/libnl/lib/fib_lookup/request.c
+[ 13% 9/68] target thumb C: libnl_32 <= external/libnl/lib/object.c
+external/libnl/lib/object.c:134:22: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ memcpy((void *)new + doff, (void *)obj + doff, size);
+ ~~~~~~~~~~~ ^
+external/libnl/lib/object.c:134:42: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ memcpy((void *)new + doff, (void *)obj + doff, size);
+ ~~~~~~~~~~~ ^
+2 warnings generated.
+[ 14% 10/68] target thumb C: libnl_32 <= external/libnl/lib/msg.c
+external/libnl/lib/msg.c:168:21: warning: comparison of integers of different signs: 'const __u32' (aka 'const unsigned int') and 'int' [-Wsign-compare]
+ if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
+ ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~
+external/libnl/lib/msg.c:183:18: warning: comparison of integers of different signs: 'const __u32' (aka 'const unsigned int') and 'int' [-Wsign-compare]
+ nlh->nlmsg_len <= remaining);
+ ~~~~~~~~~~~~~~ ^ ~~~~~~~~~
+external/libnl/lib/msg.c:367:10: warning: comparison of integers of different signs: 'size_t' (aka 'unsigned int') and 'int' [-Wsign-compare]
+ if (max < nlmsg_total_size(0))
+ ~~~ ^ ~~~~~~~~~~~~~~~~~~~
+external/libnl/lib/msg.c:418:6: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ buf += nlmsg_len;
+ ~~~ ^
+external/libnl/lib/msg.c:422:14: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ memset(buf + len, 0, tlen - len);
+ ~~~ ^
+external/libnl/lib/msg.c:842:7: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ data += GENL_HDRLEN;
+ ~~~~ ^
+external/libnl/lib/msg.c:855:9: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ data += hdrsize;
+ ~~~~ ^
+external/libnl/lib/msg.c:836:18: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
+ if (*payloadlen < GENL_HDRLEN)
+ ~~~~~~~~~~~ ^ ~~~~~~~~~~~
+external/libnl/lib/msg.c:897:32: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ dump_hex(ofd, nla_data(nla) + alen,
+ ~~~~~~~~~~~~~ ^
+external/libnl/lib/msg.c:920:4: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
+ strerror_r(-err->error, buf, sizeof(buf)));
+ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+external/libnl/lib/msg.c:915:21: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
+ if (nlmsg_len(hdr) >= sizeof(*err)) {
+ ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~
+11 warnings generated.
+[ 16% 11/68] target thumb C: libnl_32 <= external/libnl/lib/attr.c
+external/libnl/lib/attr.c:150:19: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
+ return remaining >= sizeof(*nla) &&
+ ~~~~~~~~~ ^ ~~~~~~~~~~~~
+external/libnl/lib/attr.c:208:19: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
+ if (nla_len(nla) < minlen)
+ ~~~~~~~~~~~~ ^ ~~~~~~
+external/libnl/lib/attr.c:463:11: warning: comparison of integers of different signs: 'int' and 'size_t' (aka 'unsigned int') [-Wsign-compare]
+ if (tlen > msg->nm_size)
+ ~~~~ ^ ~~~~~~~~~~~~
+external/libnl/lib/attr.c:653:26: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
+ if (nla && nla_len(nla) >= sizeof(tmp))
+ ~~~~~~~~~~~~ ^ ~~~~~~~~~~~
+external/libnl/lib/attr.c:815:41: warning: arithmetic on pointers to void is a GNU extension [-Wpointer-arith]
+ len = (void *) nlmsg_tail(msg->nm_nlh) - (void *) start;
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~
+external/libnl/lib/attr.c:863:41: warning: arithmetic on pointers to void is a GNU extension [-Wpointer-arith]
+ len = (void *) nlmsg_tail(msg->nm_nlh) - (void *) attr;
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~
+6 warnings generated.
+[ 17% 12/68] target thumb C: libnl_32 <= external/libnl/lib/utils.c
+[ 19% 13/68] target thumb C: libnl_32 <= external/libnl/lib/handlers.c
+external/libnl/lib/handlers.c:85:3: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
+ strerror_r(-e->error, buf, sizeof(buf)));
+ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+external/libnl/lib/handlers.c:206:11: warning: comparison of unsigned enum expression < 0 is always false [-Wtautological-compare]
+ if (kind < 0 || kind > NL_CB_KIND_MAX)
+ ~~~~ ^ ~
+external/libnl/lib/handlers.c:296:11: warning: comparison of unsigned enum expression < 0 is always false [-Wtautological-compare]
+ if (type < 0 || type > NL_CB_TYPE_MAX)
+ ~~~~ ^ ~
+external/libnl/lib/handlers.c:299:11: warning: comparison of unsigned enum expression < 0 is always false [-Wtautological-compare]
+ if (kind < 0 || kind > NL_CB_KIND_MAX)
+ ~~~~ ^ ~
+external/libnl/lib/handlers.c:346:11: warning: comparison of unsigned enum expression < 0 is always false [-Wtautological-compare]
+ if (kind < 0 || kind > NL_CB_KIND_MAX)
+ ~~~~ ^ ~
+5 warnings generated.
+[ 20% 14/68] target thumb C: libnl_32 <= external/libnl/lib/cache_mngt.c
+[ 22% 15/68] target thumb C: libnl_32 <= external/libnl/lib/genl/mngt.c
+external/libnl/lib/genl/mngt.c:250:22: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
+ if (ops->co_hdrsize < GENL_HDRSIZE(0)) {
+ ~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~
+1 warning generated.
+[ 23% 16/68] target thumb C: libnl_32 <= external/libnl/lib/genl/ctrl.c
+[ 25% 17/68] target thumb C: libnl_32 <= external/libnl/lib/genl/genl.c
+external/libnl/lib/genl/genl.c:125:24: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
+ if (genlmsg_len(ghdr) < NLMSG_ALIGN(hdrlen))
+ ~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~
+external/libnl/lib/genl/genl.c:261:32: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ return genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen);
+ ~~~~~~~~~~~~~~~~~~~~~~ ^
+external/libnl/lib/genl/genl.c:365:25: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ return nlmsg_data(nlh) + GENL_HDRLEN;
+ ~~~~~~~~~~~~~~~ ^
+3 warnings generated.
+[ 26% 18/68] target thumb C: libnl_32 <= external/libnl/lib/genl/family.c
+[ 27% 19/68] target thumb C: libnl_32 <= external/libnl/lib/route/rtnl.c
+[ 29% 20/68] target thumb C: libnl_32 <= external/libnl/lib/route/route_utils.c
+[ 30% 21/68] target thumb C: libnl_32 <= external/libnl/lib/error.c
+[ 32% 22/68] target thumb C: libnl_32 <= external/libnl/lib/netfilter/nfnl.c
+[ 33% 23/68] target thumb C: libnl_32 <= external/libnl/lib/version.c
+[ 35% 24/68] target thumb C: libnl_32 <= external/libnl/lib/hash.c
+[ 36% 25/68] target thumb C: libnl_32 <= external/libnl/lib/hashtable.c
+external/libnl/lib/hashtable.c:194:9: warning: invalid application of 'sizeof' to a void type [-Wpointer-arith]
+ return(__nl_hash(k, length, initval));
+ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+external/libnl/include/netlink/hash.h:64:62: note: expanded from macro '__nl_hash'
+#define __nl_hash(p, num, base) nl_hash_any((p), (num)*sizeof(*(p)), (base))
+ ^~~~~~
+1 warning generated.
+[ 38% 26/68] target StaticLib: libnl_32 (out/target/product/bullhead/obj_arm/STATIC_LIBRARIES/libnl_intermediates/libnl.a)
+[ 39% 27/68] target SharedLib: libnl_32 (out/target/product/bullhead/obj_arm/SHARED_LIBRARIES/libnl_intermediates/LINKED/libnl.so)
+[ 41% 28/68] target Pack Relocations: libnl_32 (out/target/product/bullhead/obj_arm/SHARED_LIBRARIES/libnl_intermediates/PACKED/libnl.so)
+[ 42% 29/68] target Symbolic: libnl_32 (out/target/product/bullhead/symbols/system/lib/libnl.so)
+[ 44% 30/68] target Strip (mini debug info): libnl_32 (out/target/product/bullhead/obj_arm/lib/libnl.so)
+[ 45% 31/68] Notice file: external/libnl/NOTICE -- out/target/product/bullhead/obj/NOTICE_FILES/src//system/lib/libnl.so.txt
+[ 47% 32/68] Notice file: external/libnl/NOTICE -- out/target/product/bullhead/obj/NOTICE_FILES/src//system/lib64/libnl.a.txt
+[ 48% 33/68] Notice file: external/libnl/NOTICE -- out/target/product/bullhead/obj/NOTICE_FILES/src//system/lib/libnl.a.txt
+[ 50% 34/68] Install: out/target/product/bullhead/system/lib/libnl.so
+[ 51% 35/68] target C: libnl <= external/libnl/lib/cache.c
+[ 52% 36/68] target C: libnl <= external/libnl/lib/data.c
+external/libnl/lib/data.c:119:24: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ memcpy(data->d_data + data->d_size, buf, size);
+ ~~~~~~~~~~~~ ^
+external/libnl/lib/data.c:121:24: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ memset(data->d_data + data->d_size, 0, size);
+ ~~~~~~~~~~~~ ^
+2 warnings generated.
+[ 54% 37/68] target C: libnl <= external/libnl/lib/nl.c
+external/libnl/lib/nl.c:694:18: warning: comparison of integers of different signs: '__kernel_size_t' (aka 'unsigned long') and 'ssize_t' (aka 'long') [-Wsign-compare]
+ if (iov.iov_len < n || (msg.msg_flags & MSG_TRUNC)) {
+ ~~~~~~~~~~~ ^ ~
+external/libnl/lib/nl.c:786:29: warning: missing field 'nl_pad' initializer [-Wmissing-field-initializers]
+ struct sockaddr_nl nla = {0};
+ ^
+external/libnl/lib/nl.c:917:23: warning: comparison of integers of different signs: '__u32' (aka 'unsigned int') and 'int' [-Wsign-compare]
+ if (hdr->nlmsg_len < nlmsg_size(sizeof(*e))) {
+ ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~
+3 warnings generated.
+[ 55% 38/68] target C: libnl <= external/libnl/lib/addr.c
+external/libnl/lib/addr.c:990:24: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
+ if (addr->a_prefixlen != (8 * addr->a_len)) {
+ ~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~
+1 warning generated.
+[ 57% 39/68] target C: libnl <= external/libnl/lib/cache_mngr.c
+[ 58% 40/68] target C: libnl <= external/libnl/lib/socket.c
+[ 60% 41/68] target C: libnl <= external/libnl/lib/fib_lookup/lookup.c
+external/libnl/lib/fib_lookup/lookup.c:215:30: warning: missing field 'fl_fwmark' initializer [-Wmissing-field-initializers]
+ struct fib_result_nl fr = {0};
+ ^
+1 warning generated.
+[ 61% 42/68] target C: libnl <= external/libnl/lib/msg.c
+external/libnl/lib/msg.c:168:21: warning: comparison of integers of different signs: 'const __u32' (aka 'const unsigned int') and 'int' [-Wsign-compare]
+ if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
+ ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~~~~
+external/libnl/lib/msg.c:183:18: warning: comparison of integers of different signs: 'const __u32' (aka 'const unsigned int') and 'int' [-Wsign-compare]
+ nlh->nlmsg_len <= remaining);
+ ~~~~~~~~~~~~~~ ^ ~~~~~~~~~
+external/libnl/lib/msg.c:367:10: warning: comparison of integers of different signs: 'size_t' (aka 'unsigned long') and 'int' [-Wsign-compare]
+ if (max < nlmsg_total_size(0))
+ ~~~ ^ ~~~~~~~~~~~~~~~~~~~
+external/libnl/lib/msg.c:418:6: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ buf += nlmsg_len;
+ ~~~ ^
+external/libnl/lib/msg.c:422:14: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ memset(buf + len, 0, tlen - len);
+ ~~~ ^
+external/libnl/lib/msg.c:842:7: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ data += GENL_HDRLEN;
+ ~~~~ ^
+external/libnl/lib/msg.c:855:9: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ data += hdrsize;
+ ~~~~ ^
+external/libnl/lib/msg.c:836:18: warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
+ if (*payloadlen < GENL_HDRLEN)
+ ~~~~~~~~~~~ ^ ~~~~~~~~~~~
+external/libnl/lib/msg.c:897:32: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ dump_hex(ofd, nla_data(nla) + alen,
+ ~~~~~~~~~~~~~ ^
+external/libnl/lib/msg.c:920:4: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
+ strerror_r(-err->error, buf, sizeof(buf)));
+ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+external/libnl/lib/msg.c:915:21: warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
+ if (nlmsg_len(hdr) >= sizeof(*err)) {
+ ~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~
+11 warnings generated.
+[ 63% 43/68] target C: libnl <= external/libnl/lib/fib_lookup/request.c
+[ 64% 44/68] target C: libnl <= external/libnl/lib/attr.c
+external/libnl/lib/attr.c:150:19: warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
+ return remaining >= sizeof(*nla) &&
+ ~~~~~~~~~ ^ ~~~~~~~~~~~~
+external/libnl/lib/attr.c:208:19: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
+ if (nla_len(nla) < minlen)
+ ~~~~~~~~~~~~ ^ ~~~~~~
+external/libnl/lib/attr.c:463:11: warning: comparison of integers of different signs: 'int' and 'size_t' (aka 'unsigned long') [-Wsign-compare]
+ if (tlen > msg->nm_size)
+ ~~~~ ^ ~~~~~~~~~~~~
+external/libnl/lib/attr.c:653:26: warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
+ if (nla && nla_len(nla) >= sizeof(tmp))
+ ~~~~~~~~~~~~ ^ ~~~~~~~~~~~
+external/libnl/lib/attr.c:815:41: warning: arithmetic on pointers to void is a GNU extension [-Wpointer-arith]
+ len = (void *) nlmsg_tail(msg->nm_nlh) - (void *) start;
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~
+external/libnl/lib/attr.c:863:41: warning: arithmetic on pointers to void is a GNU extension [-Wpointer-arith]
+ len = (void *) nlmsg_tail(msg->nm_nlh) - (void *) attr;
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~
+6 warnings generated.
+[ 66% 45/68] target C: libnl <= external/libnl/lib/object.c
+external/libnl/lib/object.c:134:22: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ memcpy((void *)new + doff, (void *)obj + doff, size);
+ ~~~~~~~~~~~ ^
+external/libnl/lib/object.c:134:42: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ memcpy((void *)new + doff, (void *)obj + doff, size);
+ ~~~~~~~~~~~ ^
+2 warnings generated.
+[ 67% 46/68] target C: libnl <= external/libnl/lib/utils.c
+[ 69% 47/68] target C: libnl <= external/libnl/lib/cache_mngt.c
+[ 70% 48/68] target C: libnl <= external/libnl/lib/handlers.c
+external/libnl/lib/handlers.c:85:3: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
+ strerror_r(-e->error, buf, sizeof(buf)));
+ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+external/libnl/lib/handlers.c:206:11: warning: comparison of unsigned enum expression < 0 is always false [-Wtautological-compare]
+ if (kind < 0 || kind > NL_CB_KIND_MAX)
+ ~~~~ ^ ~
+external/libnl/lib/handlers.c:296:11: warning: comparison of unsigned enum expression < 0 is always false [-Wtautological-compare]
+ if (type < 0 || type > NL_CB_TYPE_MAX)
+ ~~~~ ^ ~
+external/libnl/lib/handlers.c:299:11: warning: comparison of unsigned enum expression < 0 is always false [-Wtautological-compare]
+ if (kind < 0 || kind > NL_CB_KIND_MAX)
+ ~~~~ ^ ~
+external/libnl/lib/handlers.c:346:11: warning: comparison of unsigned enum expression < 0 is always false [-Wtautological-compare]
+ if (kind < 0 || kind > NL_CB_KIND_MAX)
+ ~~~~ ^ ~
+5 warnings generated.
+[ 72% 49/68] target C: libnl <= external/libnl/lib/genl/ctrl.c
+[ 73% 50/68] target C: libnl <= external/libnl/lib/genl/mngt.c
+external/libnl/lib/genl/mngt.c:250:22: warning: comparison of integers of different signs: 'int' and 'unsigned long' [-Wsign-compare]
+ if (ops->co_hdrsize < GENL_HDRSIZE(0)) {
+ ~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~
+1 warning generated.
+[ 75% 51/68] target C: libnl <= external/libnl/lib/genl/family.c
+[ 76% 52/68] target C: libnl <= external/libnl/lib/genl/genl.c
+external/libnl/lib/genl/genl.c:125:24: warning: comparison of integers of different signs: 'int' and 'unsigned int' [-Wsign-compare]
+ if (genlmsg_len(ghdr) < NLMSG_ALIGN(hdrlen))
+ ~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~~~~~~~~~~~
+external/libnl/lib/genl/genl.c:261:32: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ return genlmsg_user_hdr(gnlh) + NLMSG_ALIGN(hdrlen);
+ ~~~~~~~~~~~~~~~~~~~~~~ ^
+external/libnl/lib/genl/genl.c:365:25: warning: arithmetic on a pointer to void is a GNU extension [-Wpointer-arith]
+ return nlmsg_data(nlh) + GENL_HDRLEN;
+ ~~~~~~~~~~~~~~~ ^
+3 warnings generated.
+[ 77% 53/68] target C: libnl <= external/libnl/lib/route/rtnl.c
+[ 79% 54/68] target C: libnl <= external/libnl/lib/netfilter/nfnl.c
+[ 80% 55/68] target C: libnl <= external/libnl/lib/route/route_utils.c
+[ 82% 56/68] target C: libnl <= external/libnl/lib/error.c
+[ 83% 57/68] target C: libnl <= external/libnl/lib/hash.c
+[ 85% 58/68] target C: libnl <= external/libnl/lib/version.c
+[ 86% 59/68] target C: libnl <= external/libnl/lib/hashtable.c
+external/libnl/lib/hashtable.c:194:9: warning: invalid application of 'sizeof' to a void type [-Wpointer-arith]
+ return(__nl_hash(k, length, initval));
+ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+external/libnl/include/netlink/hash.h:64:62: note: expanded from macro '__nl_hash'
+#define __nl_hash(p, num, base) nl_hash_any((p), (num)*sizeof(*(p)), (base))
+ ^~~~~~
+1 warning generated.
+[ 88% 60/68] target StaticLib: libnl (out/target/product/bullhead/obj/STATIC_LIBRARIES/libnl_intermediates/libnl.a)
+[ 89% 61/68] target SharedLib: libnl (out/target/product/bullhead/obj/SHARED_LIBRARIES/libnl_intermediates/LINKED/libnl.so)
+[ 91% 62/68] target Pack Relocations: libnl (out/target/product/bullhead/obj/SHARED_LIBRARIES/libnl_intermediates/PACKED/libnl.so)
+[ 92% 63/68] target Symbolic: libnl (out/target/product/bullhead/symbols/system/lib64/libnl.so)
+[ 94% 64/68] target Strip (mini debug info): libnl (out/target/product/bullhead/obj/lib/libnl.so)
+[ 95% 65/68] Notice file: external/libnl/NOTICE -- out/target/product/bullhead/obj/NOTICE_FILES/src//system/lib64/libnl.so.txt
+[ 97% 66/68] Install: out/target/product/bullhead/system/lib64/libnl.so
+[ 98% 67/68] build out/target/product/bullhead/obj_arm/lib/libnl.so.toc
+[100% 68/68] build out/target/product/bullhead/obj/lib/libnl.so.toc
+make: Leaving directory `/usr/local/google/home/pstew/build/aosp'
+
+#### make completed successfully (13 seconds) ####
+
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644
index 00000000..af1277b0
--- /dev/null
+++ b/man/Makefile.am
@@ -0,0 +1,7 @@
+# -*- Makefile -*-
+
+dist_man8_MANS = \
+ nl-classid-lookup.8 \
+ nl-pktloc-lookup.8 \
+ nl-qdisc-add.8 nl-qdisc-delete.8 nl-qdisc-list.8 \
+ genl-ctrl-list.8
diff --git a/man/genl-ctrl-list.8 b/man/genl-ctrl-list.8
new file mode 100644
index 00000000..61324754
--- /dev/null
+++ b/man/genl-ctrl-list.8
@@ -0,0 +1,104 @@
+.TH genl\-ctrl-list 8 "20 April 2012" "libnl"
+.SH NAME
+genl\-ctrl\-list \- List available kernel-side Generic Netlink families
+.SH SYNOPSIS
+.B genl\-ctrl\-list [-d]
+
+.SH DESCRIPTION
+.PP
+Queries the Generic Netlink controller in kernel and prints a list of all
+registered Generic Netlink families including the version of the interface
+that has been registered.
+
+.SH OPTIONS
+.TP
+.BR \-\^h " or " \-\-help
+Print help text to console and exit.
+.TP
+.BR \-\^v " or " \-\-version
+Print versioning information to console and exit.
+.TP
+.BR \-\^d " or " \-\-details
+Include additional detailed information for each Generic Netlink
+family that is printed.
+
+The information includes:
+
+.RS
+.TP
+.B hdrsize N
+The size of the user specific header.
+
+.TP
+.B maxattr N
+The maximum Netlink attribute identifier expected by the interface.
+
+.TP
+.B op NAME (ID) <FLAGS>
+A list of available operations including their name, numeric identifier
+and the flags indicating the capabilities of the opertion.
+
+Available flags:
+.RS
+.TP
+.I admin-perm
+Requires administrative privileges
+
+.TP
+.I has-doit
+Command can handle request
+
+.TP
+.I has-dump
+Command can handle a dump request
+
+.TP
+.I has-policy
+Command enforces attribute validation policy
+.RE
+
+.TP
+.B grp NAME (ID)
+A list of registered multicast groups including name (if available)
+and identifier.
+.RE
+
+.RS
+.B Example:
+.RS
+0x0010 nlctrl version 2
+.RS 0
+ hdrsize 0 maxattr 7
+.RS 0
+ op GETFAMILY (0x03) <has-doit,has-dump,has-policy>
+.RS 0
+ grp notify (0x10)
+.RE
+
+
+.SH EXAMPLE
+.RS 0
+$ genl-ctrl-list
+.RS 0
+0x0010 nlctrl version 2
+.RS 0
+0x0011 NLBL_MGMT version 3
+.RS 0
+0x0012 NLBL_CIPSOv4 version 3
+.RS 0
+0x0013 NLBL_UNLBL version 3
+.RS 0
+0x0014 acpi_event version 1
+.RS 0
+0x0015 thermal_event version 1
+.RS 0
+0x0016 VFS_DQUOT version 1
+.RS 0
+0x0017 TASKSTATS version 1
+.RS 0
+0x0018 NET_DM version 2
+
+.SH AUTHOR
+.PP
+Thomas Graf is the original author and current maintainer of libnl and
+libnl tools. Many people have contributed to it since.
diff --git a/man/nl-classid-lookup.8 b/man/nl-classid-lookup.8
new file mode 100644
index 00000000..3cf13de9
--- /dev/null
+++ b/man/nl-classid-lookup.8
@@ -0,0 +1,51 @@
+.TH nl\-classid\-lookup 8 "19 October 2010" "libnl"
+.SH NAME
+nl\-classid\-lookup - Lookup classid definitions
+.SH SYNOPSIS
+.B nl\-classid\-lookup
+.RB [ \-hv ]
+.RB [ \-r ]
+.RB [ \-\-raw ]
+.I name
+
+.SH DESCRIPTION
+.PP
+nl\-classid\-lookup searches the classid database for a matching entry. It is used
+to resolve qdisc/class names to classid values and vice versa.
+
+.SH OPTIONS
+.TP
+.BR \-\^h " or " \-\-help
+Print help text to console and exit.
+.TP
+.BR \-\^v " or " \-\-version
+Print versioning information to console and exit.
+.TP
+.BR \-\^r " or " \-\-reverse
+Do a reverse lookup. Lookup a classid and print its name.
+.TP
+.B \-\-raw
+Print the raw classid in hexadecimal format, do not pretty print it.
+
+.SH USAGE
+.PP
+Resolve the qdisc/class name "interactive":
+.PP
+.RS
+# nl\-classid\-lookup interactive
+.RE
+.PP
+Lookup the name of classid 1:2:
+.PP
+.RS
+# nl\-classid\-lookup -r 1:2
+.RE
+
+.SH FILES
+.PP
+/etc/libnl/classid
+
+.SH AUTHOR
+.PP
+Thomas Graf is the original author and current maintainer of libnl and
+libnl tools. Many people have contributed to it since.
diff --git a/man/nl-pktloc-lookup.8 b/man/nl-pktloc-lookup.8
new file mode 100644
index 00000000..7a1daa4c
--- /dev/null
+++ b/man/nl-pktloc-lookup.8
@@ -0,0 +1,48 @@
+.TH nl\-pktloc-lookup 8 "27 October 2010" "libnl"
+.SH NAME
+nl\-pktloc\-lookup - Lookup packet location definitions
+.SH SYNOPSIS
+.B nl\-pktloc\-lookup
+.I name
+.br
+.B nl\-pktloc\-lookup \-\-list
+
+.SH DESCRIPTION
+.PP
+nl\-pktloc\-lookup searches the packet location database for a matching
+entry. It is used to resolve packet location aliases to their definition,
+i.e. alignment, layer, offset, and mask.
+
+.SH OPTIONS
+.TP
+.BR \-\^h " or " \-\-help
+Print help text to console and exit.
+.TP
+.BR \-\^v " or " \-\-version
+Print versioning information to console and exit.
+.TP
+.BR \-\^l " or " \-\-list
+List all packet location definitions.
+.TP
+.BR \-\-u32=VALUE
+Prints the packet location definition in a special format that is
+understood by iproute2's u32 selector parser. It will output a
+u32 selector which will compare the provided value with the value
+specified by the packet location.
+
+Please note that due to the limitation of u32, it is not possible
+to use packet locations based on the link layer. nl-pktloc-lookup
+will print an error message in this case.
+
+Example:
+ selector=$(nl-pktloc-lookup --u32 22 tcp.sport)
+ tc filter add [...] u32 match $(selector) flowid 1:2
+
+.SH FILES
+.PP
+/etc/libnl/pktloc
+
+.SH AUTHOR
+.PP
+Thomas Graf is the original author and current maintainer of libnl and
+libnl tools. Many people have contributed to it since.
diff --git a/man/nl-qdisc-add.8 b/man/nl-qdisc-add.8
new file mode 100644
index 00000000..bc3fb175
--- /dev/null
+++ b/man/nl-qdisc-add.8
@@ -0,0 +1,118 @@
+.TH nl\-qdisc 8 "21 October 2010" "libnl"
+.SH NAME
+nl\-qdisc\-{add|list|delete} - Manage queueing disciplines
+.SH SYNOPSIS
+.B nl\-qdisc\-add \-\-dev
+.I dev
+.B \-\-parent
+.I id
+.B [OPTIONS]
+.I qdisc-type
+.B [QDISC]
+.sp
+.B nl\-qdisc\-delete [ \-\-interactive ] [OPTIONS]
+.sp
+.B nl\-qdisc\-list [OPTIONS]
+
+.SH DESCRIPTION
+.PP
+The nl\-qdisc tools allow to manage and configure queueing disciplines
+(qdiscs) in the kernel.
+
+.SH OPTIONS
+.TP
+.BR \-\^h " or " \-\-help
+Print help text to console and exit.
+.TP
+.BR \-\^v " or " \-\-version
+Print versioning information to console and exit.
+.TP
+.BR \-\^q " or " \-\-quiet
+Do not print informal notifications about actions taken to the console.
+By default a short description of each qdisc added/update/deleted will
+be printed to the console. This option disables this behaviour.
+.TP
+.BR \-\^d " or " \-\-dev "=DEV"
+Network device the qdisc is attached to.
+.TP
+.BR \-\^p " or " \-\-parent "=ID"
+Identifier of the parent qdisc/class this qdisc is attached to. The
+identifier can be specified as classid, name or one of the special
+values "root" or "ingress".
+.TP
+.BR \-\^i " or " \-\-id "=ID"
+Identifier of qdisc. It can be specified as classid or name.
+
+.SS nl\-qdisc\-add Options
+.TP
+.B \-\-update
+Update qdisc if it already exists, otherwise attempting to add a qdisc which already
+exists will result in an error. This does not include changing the type of the qdisc,
+use \-\-replace if you wish to do so.
+.TP
+.B \-\-replace
+Replace or update qdisc if it already exists. Same behaviour as \-\-update but will
+completely replace the qdisc if it exists already.
+.TP
+.B \-\-update\-only
+Update an existing qdisc but do not create it if it does not exist.
+.TP
+.B \-\-replace\-only
+Update or replace an existing qdisc but do not create it if it does exist.
+
+.SS nl\-qdisc\-delete Options
+.TP
+.B \-\-interactive
+The interactive mode requires confirmation by the user for each qdisc deleted. It
+will print a prompt for each qdisc matching the provided filter and requires the
+user to answer 'y'es or 'n'o.
+.TP
+.B \-\-yes
+Make the default answer for interactive prompts be 'y'es. This option is also
+required to delete all qdiscs on all network devices.
+.TP
+.BR \-\^k " or " \-\-kind "=TYPE"
+Only delete qdiscs of this type.
+
+.SS nl\-qdisc\-list Options
+.TP
+.B \-\-details
+Show detailed information for each qdisc listed.
+.TP
+.B \-\-stats
+Show statistics information for each qdisc listed. This option will also turn
+on detailed information automatically.
+.TP
+.BR \-\^r " or " \-\-recursive
+List all TC objects recurisvely attached to all qdiscs matching the filter.
+.TP
+.BR \-\^k " or " \-\-kind "=TYPE"
+Only list qdiscs of this type.
+
+.SH USAGE
+.PP
+Add a HTB root qdisc with id "5:":
+.PP
+.RS
+nl\-qdisc\-add \-\-dev eth0 \-\-parent root \-\-id 5: htb
+.RE
+.PP
+List all qdiscs on eth0 and print statistical data:
+.PP
+.RS
+nl\-qdisc\-list \-\-stats \-\-dev eth0
+.RE
+.PP
+Delete the qdisc "5:":
+.RS
+nl\-qdisc\-delete \-\-id 5:
+.RE
+
+.SH "SEE ALSO"
+.PP
+.B nl\-classid\-lookup(8)
+
+.SH AUTHOR
+.PP
+Thomas Graf is the original author and current maintainer of libnl and
+libnl tools. Many people have contributed to it since.
diff --git a/man/nl-qdisc-delete.8 b/man/nl-qdisc-delete.8
new file mode 100644
index 00000000..864a4e05
--- /dev/null
+++ b/man/nl-qdisc-delete.8
@@ -0,0 +1 @@
+.so man8/nl-qdisc-add.8
diff --git a/man/nl-qdisc-list.8 b/man/nl-qdisc-list.8
new file mode 100644
index 00000000..864a4e05
--- /dev/null
+++ b/man/nl-qdisc-list.8
@@ -0,0 +1 @@
+.so man8/nl-qdisc-add.8
diff --git a/python/.gitignore b/python/.gitignore
new file mode 100644
index 00000000..a83b9429
--- /dev/null
+++ b/python/.gitignore
@@ -0,0 +1,4 @@
+build
+capi_wrap.c
+capi.py
+setup.py
diff --git a/python/Makefile.am b/python/Makefile.am
new file mode 100644
index 00000000..a1a3426e
--- /dev/null
+++ b/python/Makefile.am
@@ -0,0 +1,3 @@
+# -*- Makefile -*-
+
+SUBDIRS = doc examples netlink tests
diff --git a/python/README b/python/README
new file mode 100644
index 00000000..4ccc337a
--- /dev/null
+++ b/python/README
@@ -0,0 +1,12 @@
+
+***************************************************************************
+
+NOTE: The python wrapper is experimental and may or may not work.
+
+***************************************************************************
+
+For the brave:
+
+ (requires an installed libnl)
+ - $ python ./setup.py build
+ - $ sudo python ./setup.py install
diff --git a/python/doc/Makefile.am b/python/doc/Makefile.am
new file mode 100644
index 00000000..a2fe6f2b
--- /dev/null
+++ b/python/doc/Makefile.am
@@ -0,0 +1,8 @@
+# -*- Makefile -*-
+
+EXTRA_DIST = \
+ conf.py \
+ core.rst \
+ index.rst \
+ route_addr.rst \
+ route.rst
diff --git a/python/doc/conf.py b/python/doc/conf.py
new file mode 100644
index 00000000..74041f04
--- /dev/null
+++ b/python/doc/conf.py
@@ -0,0 +1,216 @@
+# -*- coding: utf-8 -*-
+#
+# libnl-python documentation build configuration file, created by
+# sphinx-quickstart on Mon May 9 10:58:58 2011.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.todo', 'sphinx.ext.ifconfig', 'sphinx.ext.viewcode']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'libnl-python'
+copyright = u'2011, Thomas Graf <tgraf@suug.ch>'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '1.0'
+# The full version, including alpha/beta/rc tags.
+release = '1.0'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'libnl-pythondoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'libnl-python.tex', u'libnl-python Documentation',
+ u'Thomas Graf \\textless{}tgraf@suug.ch\\textgreater{}', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'libnl-python', u'libnl-python Documentation',
+ [u'Thomas Graf <tgraf@suug.ch>'], 1)
+]
diff --git a/python/doc/core.rst b/python/doc/core.rst
new file mode 100644
index 00000000..012e14cf
--- /dev/null
+++ b/python/doc/core.rst
@@ -0,0 +1,215 @@
+*******************
+Netlink Core Module
+*******************
+
+.. py:module:: netlink.core
+
+Examples::
+
+ import netlink.core as netlink
+
+===============
+Object
+===============
+
+.. py:class:: Object
+
+ Base class for all classes representing a cacheable object
+
+ Example::
+ obj = netlink.Object("route/link", "link")
+
+ .. py:method:: clone
+
+ Clone the object and return a duplicate (used for COW)
+
+ .. py:method:: dump([params=None])
+
+ Call the libnl internal dump mechanism to dump the object
+ according to the parameters specified.
+
+ .. py:method:: apply(attr, val)
+
+ Applies a attribute=value pair and modifies the object accordingly.
+ Example::
+ obj.apply("mtu", 1200) # Sets attribute mtu to 1200 (link obj)
+
+ :raises: KeyError if attribute is unknown
+ :raises: ImmutableError if attribute is not mutable
+
+ .. py:attribute:: mark
+
+ True if the object is marked, otherwise False.
+
+ .. py:attribute:: shared
+
+ True if the object is used by multiple parties, otherwise False.
+
+ .. py:attribute:: refcnt
+
+ Number of users sharing a reference to the object
+ :rtype: int
+
+ .. py:attribute:: attrs
+
+ List of attributes
+
+ :rtype: list of strings
+
+===============
+Cache
+===============
+
+.. py:class:: Cache
+
+ Base class for all cache implementations.
+
+ A cache is a collection of cacheable objects which is typically used
+ by netlink protocols which handle any kind of object, e.g. network
+ links, network addresses, neighbours, ...
+
+ .. py:method:: subset(filter)
+
+ Returns a new cache containing the subset which matches the
+ provided filter.
+
+ :raises: ValueError if no filter is specified
+ :rtype: :py:class:`Cache`
+
+ .. py:method:: dump([params=None, filter=None])
+
+ Calls the libnl internal dump mechanism to dump the cache according
+ to the parameters and filter specified.
+
+ .. py:method:: clear()
+
+ Remove and possibly destroy all objects in the cache
+
+ .. py:method:: refill([socket=None]) -> :py:class:`Cache`
+
+ Clears and refills the cache with the content which is provided by
+ the kernel, e.g. for a link cache this would mean refilling the
+ cache with all configured network links.
+
+ .. py:method:: provide()
+
+ Caches which have been "provided" are made available to other users
+ (of the same application context) which "require" it. F.e. a link
+ cache is generally provided to allow others to translate interface
+ indexes to link names
+
+
+ .. py:method:: unprovide()
+
+ No longer make the cache available to others. If the cache has been
+ handed out already, that reference will still be valid.
+
+===============
+AbstractAddress
+===============
+
+.. py:class:: AbstractAddress
+
+ Abstract representation of an address. This class is not to be mistaken
+ with :py:class:`route.Address` which represents a configured network
+ address. This class represents the actual address in a family independent
+ way::
+
+ addr = netlink.AbstractAddress('127.0.0.1/8')
+ print addr # => '127.0.0.1/8'
+ print addr.prefixlen # => '8'
+ print addr.family # => 'inet'
+ print len(addr) # => '4' (32bit ipv4 address)
+
+ a = netlink.AbstractAddress('10.0.0.1/24')
+ b = netlink.AbstractAddress('10.0.0.2/24')
+ print a == b # => False
+
+ .. py:attribute:: prefixlen
+
+ Length of prefix in number of bits.
+
+ :rtype: int
+
+ .. py:attribute:: family
+
+ The family type of the address. Setting the address family can be
+ done with a string or a :py:class:`AddressFamily` object.
+
+ :rtype: :py:class:`AddressFamily`
+
+ .. py:attribute:: shared
+
+ True if address is in use by multiple callers, otherwise False
+
+ :rtype: bool
+
+===============
+AddressFamily
+===============
+
+.. py:class:: AddressFamily
+
+ Address family representation::
+
+ af = netlink.AddressFamily('inet6')
+ # raises:
+ # - ValueError if family name is not known
+ # - TypeError if invalid type is specified for family
+
+ print af # => 'inet6' (string representation)
+ print int(af) # => 10 (numeric representation)
+ print repr(af) # => AddressFamily('inet6')
+
+===============
+Exceptions
+===============
+
+.. py:exception:: NetlinkError
+
+ Generic exception raised by netlink modules.
+
+.. py:exception:: KernelError
+
+ Raised if an error occured while communicating with the kernel. Contains
+ the error code returning which is automatically included in the error
+ message.
+
+.. py:exception:: ImmutableError
+
+ Raised if an attribute is modified which is marked immutable.
+
+===============
+Socket
+===============
+
+.. py:class:: Socket
+
+ Netlink socket.
+
+ Note: It is not required to manually create and connect netlink sockets
+ when using caches. The caches will automatically lookup or create a
+ socket as needed.
+
+ .. py:attribute:: local_port
+
+ Local port (address) of netlink socket
+
+ .. py:attribute:: peer_port
+
+ Peer port (remote address) of netlink socket. If set, all messages
+ will be sent to that peer.
+
+ .. py:method:: connect(proto)
+
+ Connect the netlink socket using the specified netlink protocol::
+ sock.connect(netlink.NETLINK_ROUTE)
+
+ .. py:method:: disconnect()
+
+ Disconnect the socket
+
+ .. py:method:: set_bufsize(rx, tx)
+
+ Sets the size of the socket buffer
+
diff --git a/python/doc/index.rst b/python/doc/index.rst
new file mode 100644
index 00000000..8de8ae31
--- /dev/null
+++ b/python/doc/index.rst
@@ -0,0 +1,24 @@
+.. libnl-python documentation master file, created by
+ sphinx-quickstart on Mon May 9 10:58:58 2011.
+ You can adapt this file completely to your liking, but it should at least
+ contain the root `toctree` directive.
+
+Welcome to libnl-python's documentation!
+========================================
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ core
+ route
+ route_addr
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
diff --git a/python/doc/route.rst b/python/doc/route.rst
new file mode 100644
index 00000000..0b8f3f95
--- /dev/null
+++ b/python/doc/route.rst
@@ -0,0 +1,3 @@
+**********************
+Routing
+**********************
diff --git a/python/doc/route_addr.rst b/python/doc/route_addr.rst
new file mode 100644
index 00000000..2cfe139d
--- /dev/null
+++ b/python/doc/route_addr.rst
@@ -0,0 +1,47 @@
+=================
+Network Addresses
+=================
+
+The **Address** module provides access to the network address configuration
+of the kernel. It provides an interface to fetch all configured addresses,
+add new addresses and to delete existing addresses.
+
+Fetching the list of network addresses is achieved by creating a new
+address cache::
+
+ import netlink.route.address as Address
+
+ addr_cache = Address.AddressCache()
+ addr_cache.refill()
+
+ for addr in addr_cache:
+ print addr
+
+.. py:module:: netlink.route.addr
+
+
+AddressCache
+------------
+
+.. py:class:: AddressCache
+
+ Represents a cache containing all or a subset of network addresses.
+
+ .. py:method:: lookup(ifindex, local)
+
+ Lookup the address which matches ifindex and local address
+
+ :raises: KeyError if address is not found.
+
+Address
+-------
+
+.. py:class:: Address
+
+ Representation of a configured network address.
+
+ .. py:attribute:: ifindex
+
+ Interface index
+
+ :rtype: int
diff --git a/python/examples/Makefile.am b/python/examples/Makefile.am
new file mode 100644
index 00000000..1cea5e2d
--- /dev/null
+++ b/python/examples/Makefile.am
@@ -0,0 +1,6 @@
+# -*- Makefile -*-
+
+EXTRA_DIST = \
+ iface.py \
+ nl80211.py \
+ wiphy.py
diff --git a/python/examples/iface.py b/python/examples/iface.py
new file mode 100644
index 00000000..7021882a
--- /dev/null
+++ b/python/examples/iface.py
@@ -0,0 +1,93 @@
+import netlink.capi as nl
+import netlink.genl.capi as genl
+import nl80211
+import sys
+import traceback
+
+class test_class:
+ def __init__(self):
+ self.done = 1;
+
+def msg_handler(m, a):
+ try:
+ e, attr = genl.py_genlmsg_parse(nl.nlmsg_hdr(m), 0,
+ nl80211.NL80211_ATTR_MAX, None)
+ if nl80211.NL80211_ATTR_WIPHY in attr:
+ thiswiphy = nl.nla_get_u32(attr[nl80211.NL80211_ATTR_WIPHY])
+ print("phy#%d" % thiswiphy)
+ if nl80211.NL80211_ATTR_IFNAME in attr:
+ print("\tinterface %s" % nl.nla_get_string(attr[nl80211.NL80211_ATTR_IFNAME]));
+ if nl80211.NL80211_ATTR_IFINDEX in attr:
+ print("\tifindex %d" % nl.nla_get_u32(attr[nl80211.NL80211_ATTR_IFINDEX]))
+ if nl80211.NL80211_ATTR_WDEV in attr:
+ print("\twdev 0x%lx" % nl.nla_get_u64(attr[nl80211.NL80211_ATTR_WDEV]))
+ if nl80211.NL80211_ATTR_MAC in attr:
+ print("\tmac %02x:%02x:%02x:%02x:%02x:%02x" % tuple(nl.nla_data(attr[nl80211.NL80211_ATTR_MAC])))
+ if nl80211.NL80211_ATTR_SSID in attr:
+ print("\tssid ", nl.nla_data(attr[nl80211.NL80211_ATTR_SSID]))
+ if nl80211.NL80211_ATTR_IFTYPE in attr:
+ iftype = nl.nla_get_u32(attr[nl80211.NL80211_ATTR_IFTYPE])
+ print("\ttype %s" % nl80211.nl80211_iftype2str[iftype])
+ if nl80211.NL80211_ATTR_WIPHY_FREQ in attr:
+ freq = nl.nla_get_u32(attr[nl80211.NL80211_ATTR_WIPHY_FREQ])
+
+ sys.stdout.write("\tfreq %d MHz" % freq);
+
+ if nl80211.NL80211_ATTR_CHANNEL_WIDTH in attr:
+ chanw = nl.nla_get_u32(attr[nl80211.NL80211_ATTR_CHANNEL_WIDTH])
+ sys.stdout.write(", width: %s" % nl80211.nl80211_chan_width2str[chanw])
+ if nl80211.NL80211_ATTR_CENTER_FREQ1 in attr:
+ sys.stdout.write(", center1: %d MHz" %
+ nl.nla_get_u32(attr[nl80211.NL80211_ATTR_CENTER_FREQ1]))
+ if nl80211.NL80211_ATTR_CENTER_FREQ2 in attr:
+ sys.stdout.write(", center2: %d MHz" %
+ nl.nla_get_u32(attr[nl80211.NL80211_ATTR_CENTER_FREQ2]))
+ elif nl80211.NL80211_ATTR_WIPHY_CHANNEL_TYPE in attr:
+ channel_type = nl.nla_get_u32(attr[nl80211.NL80211_ATTR_WIPHY_CHANNEL_TYPE])
+ sys.stdout.write(" %s" % nl80211.nl80211_channel_type2str(channel_type));
+
+ sys.stdout.write("\n");
+ return nl.NL_SKIP;
+ except Exception as e:
+ (t,v,tb) = sys.exc_info()
+ print v.message
+ traceback.print_tb(tb)
+
+def error_handler(err, a):
+ a.done = err.error
+ return nl.NL_STOP
+
+def finish_handler(m, a):
+ return nl.NL_SKIP
+
+def ack_handler(m, a):
+ a.done = 0
+ return nl.NL_STOP
+
+try:
+ cbd = test_class()
+ tx_cb = nl.nl_cb_alloc(nl.NL_CB_DEFAULT)
+ rx_cb = nl.nl_cb_clone(tx_cb)
+ s = nl.nl_socket_alloc_cb(tx_cb)
+ nl.py_nl_cb_err(rx_cb, nl.NL_CB_CUSTOM, error_handler, cbd);
+ nl.py_nl_cb_set(rx_cb, nl.NL_CB_FINISH, nl.NL_CB_CUSTOM, finish_handler, cbd);
+ nl.py_nl_cb_set(rx_cb, nl.NL_CB_ACK, nl.NL_CB_CUSTOM, ack_handler, cbd);
+ nl.py_nl_cb_set(rx_cb, nl.NL_CB_VALID, nl.NL_CB_CUSTOM, msg_handler, cbd);
+
+ genl.genl_connect(s)
+ family = genl.genl_ctrl_resolve(s, 'nl80211')
+ m = nl.nlmsg_alloc()
+ genl.genlmsg_put(m, 0, 0, family, 0, 0, nl80211.NL80211_CMD_GET_INTERFACE, 0)
+ nl.nla_put_u32(m, nl80211.NL80211_ATTR_IFINDEX, nl.if_nametoindex('wlan0'))
+
+ err = nl.nl_send_auto_complete(s, m);
+ if err < 0:
+ nl.nlmsg_free(msg)
+
+ while cbd.done > 0 and not err < 0:
+ err = nl.nl_recvmsgs(s, rx_cb)
+
+except Exception as e:
+ (t, v, tb) = sys.exc_info()
+ print v.message
+ traceback.print_tb(tb)
diff --git a/python/examples/nl80211.py b/python/examples/nl80211.py
new file mode 100644
index 00000000..7434bef2
--- /dev/null
+++ b/python/examples/nl80211.py
@@ -0,0 +1,1577 @@
+###########################################################
+# file: nl80211.py
+# ---------------------------------------------------------
+# This file is generated using extract.py using pycparser
+###########################################################
+NL80211_GENL_FAMILY = 'nl80211'
+
+NL80211_CMD_UNSPEC = 0
+NL80211_CMD_GET_WIPHY = 1
+NL80211_CMD_SET_WIPHY = 2
+NL80211_CMD_NEW_WIPHY = 3
+NL80211_CMD_DEL_WIPHY = 4
+NL80211_CMD_GET_INTERFACE = 5
+NL80211_CMD_SET_INTERFACE = 6
+NL80211_CMD_NEW_INTERFACE = 7
+NL80211_CMD_DEL_INTERFACE = 8
+NL80211_CMD_GET_KEY = 9
+NL80211_CMD_SET_KEY = 10
+NL80211_CMD_NEW_KEY = 11
+NL80211_CMD_DEL_KEY = 12
+NL80211_CMD_GET_BEACON = 13
+NL80211_CMD_SET_BEACON = 14
+NL80211_CMD_START_AP = 15
+NL80211_CMD_NEW_BEACON = NL80211_CMD_START_AP
+NL80211_CMD_STOP_AP = 16
+NL80211_CMD_DEL_BEACON = NL80211_CMD_STOP_AP
+NL80211_CMD_GET_STATION = 17
+NL80211_CMD_SET_STATION = 18
+NL80211_CMD_NEW_STATION = 19
+NL80211_CMD_DEL_STATION = 20
+NL80211_CMD_GET_MPATH = 21
+NL80211_CMD_SET_MPATH = 22
+NL80211_CMD_NEW_MPATH = 23
+NL80211_CMD_DEL_MPATH = 24
+NL80211_CMD_SET_BSS = 25
+NL80211_CMD_SET_REG = 26
+NL80211_CMD_REQ_SET_REG = 27
+NL80211_CMD_GET_MESH_CONFIG = 28
+NL80211_CMD_SET_MESH_CONFIG = 29
+NL80211_CMD_SET_MGMT_EXTRA_IE = 30
+NL80211_CMD_GET_REG = 31
+NL80211_CMD_GET_SCAN = 32
+NL80211_CMD_TRIGGER_SCAN = 33
+NL80211_CMD_NEW_SCAN_RESULTS = 34
+NL80211_CMD_SCAN_ABORTED = 35
+NL80211_CMD_REG_CHANGE = 36
+NL80211_CMD_AUTHENTICATE = 37
+NL80211_CMD_ASSOCIATE = 38
+NL80211_CMD_DEAUTHENTICATE = 39
+NL80211_CMD_DISASSOCIATE = 40
+NL80211_CMD_MICHAEL_MIC_FAILURE = 41
+NL80211_CMD_REG_BEACON_HINT = 42
+NL80211_CMD_JOIN_IBSS = 43
+NL80211_CMD_LEAVE_IBSS = 44
+NL80211_CMD_TESTMODE = 45
+NL80211_CMD_CONNECT = 46
+NL80211_CMD_ROAM = 47
+NL80211_CMD_DISCONNECT = 48
+NL80211_CMD_SET_WIPHY_NETNS = 49
+NL80211_CMD_GET_SURVEY = 50
+NL80211_CMD_NEW_SURVEY_RESULTS = 51
+NL80211_CMD_SET_PMKSA = 52
+NL80211_CMD_DEL_PMKSA = 53
+NL80211_CMD_FLUSH_PMKSA = 54
+NL80211_CMD_REMAIN_ON_CHANNEL = 55
+NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL = 56
+NL80211_CMD_SET_TX_BITRATE_MASK = 57
+NL80211_CMD_REGISTER_FRAME = 58
+NL80211_CMD_REGISTER_ACTION = NL80211_CMD_REGISTER_FRAME
+NL80211_CMD_FRAME = 59
+NL80211_CMD_ACTION = NL80211_CMD_FRAME
+NL80211_CMD_FRAME_TX_STATUS = 60
+NL80211_CMD_ACTION_TX_STATUS = NL80211_CMD_FRAME_TX_STATUS
+NL80211_CMD_SET_POWER_SAVE = 61
+NL80211_CMD_GET_POWER_SAVE = 62
+NL80211_CMD_SET_CQM = 63
+NL80211_CMD_NOTIFY_CQM = 64
+NL80211_CMD_SET_CHANNEL = 65
+NL80211_CMD_SET_WDS_PEER = 66
+NL80211_CMD_FRAME_WAIT_CANCEL = 67
+NL80211_CMD_JOIN_MESH = 68
+NL80211_CMD_LEAVE_MESH = 69
+NL80211_CMD_UNPROT_DEAUTHENTICATE = 70
+NL80211_CMD_UNPROT_DISASSOCIATE = 71
+NL80211_CMD_NEW_PEER_CANDIDATE = 72
+NL80211_CMD_GET_WOWLAN = 73
+NL80211_CMD_SET_WOWLAN = 74
+NL80211_CMD_START_SCHED_SCAN = 75
+NL80211_CMD_STOP_SCHED_SCAN = 76
+NL80211_CMD_SCHED_SCAN_RESULTS = 77
+NL80211_CMD_SCHED_SCAN_STOPPED = 78
+NL80211_CMD_SET_REKEY_OFFLOAD = 79
+NL80211_CMD_PMKSA_CANDIDATE = 80
+NL80211_CMD_TDLS_OPER = 81
+NL80211_CMD_TDLS_MGMT = 82
+NL80211_CMD_UNEXPECTED_FRAME = 83
+NL80211_CMD_PROBE_CLIENT = 84
+NL80211_CMD_REGISTER_BEACONS = 85
+NL80211_CMD_UNEXPECTED_4ADDR_FRAME = 86
+NL80211_CMD_SET_NOACK_MAP = 87
+NL80211_CMD_CH_SWITCH_NOTIFY = 88
+NL80211_CMD_START_P2P_DEVICE = 89
+NL80211_CMD_STOP_P2P_DEVICE = 90
+NL80211_CMD_CONN_FAILED = 91
+NL80211_CMD_SET_MCAST_RATE = 92
+NL80211_CMD_SET_MAC_ACL = 93
+NL80211_CMD_RADAR_DETECT = 94
+NL80211_CMD_GET_PROTOCOL_FEATURES = 95
+NL80211_CMD_UPDATE_FT_IES = 96
+NL80211_CMD_FT_EVENT = 97
+NL80211_CMD_CRIT_PROTOCOL_START = 98
+NL80211_CMD_CRIT_PROTOCOL_STOP = 99
+__NL80211_CMD_AFTER_LAST = 100
+NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
+NL80211_ATTR_UNSPEC = 0
+NL80211_ATTR_WIPHY = 1
+NL80211_ATTR_WIPHY_NAME = 2
+NL80211_ATTR_IFINDEX = 3
+NL80211_ATTR_IFNAME = 4
+NL80211_ATTR_IFTYPE = 5
+NL80211_ATTR_MAC = 6
+NL80211_ATTR_KEY_DATA = 7
+NL80211_ATTR_KEY_IDX = 8
+NL80211_ATTR_KEY_CIPHER = 9
+NL80211_ATTR_KEY_SEQ = 10
+NL80211_ATTR_KEY_DEFAULT = 11
+NL80211_ATTR_BEACON_INTERVAL = 12
+NL80211_ATTR_DTIM_PERIOD = 13
+NL80211_ATTR_BEACON_HEAD = 14
+NL80211_ATTR_BEACON_TAIL = 15
+NL80211_ATTR_STA_AID = 16
+NL80211_ATTR_STA_FLAGS = 17
+NL80211_ATTR_STA_LISTEN_INTERVAL = 18
+NL80211_ATTR_STA_SUPPORTED_RATES = 19
+NL80211_ATTR_STA_VLAN = 20
+NL80211_ATTR_STA_INFO = 21
+NL80211_ATTR_WIPHY_BANDS = 22
+NL80211_ATTR_MNTR_FLAGS = 23
+NL80211_ATTR_MESH_ID = 24
+NL80211_ATTR_STA_PLINK_ACTION = 25
+NL80211_ATTR_MPATH_NEXT_HOP = 26
+NL80211_ATTR_MPATH_INFO = 27
+NL80211_ATTR_BSS_CTS_PROT = 28
+NL80211_ATTR_BSS_SHORT_PREAMBLE = 29
+NL80211_ATTR_BSS_SHORT_SLOT_TIME = 30
+NL80211_ATTR_HT_CAPABILITY = 31
+NL80211_ATTR_SUPPORTED_IFTYPES = 32
+NL80211_ATTR_REG_ALPHA2 = 33
+NL80211_ATTR_REG_RULES = 34
+NL80211_ATTR_MESH_CONFIG = 35
+NL80211_ATTR_BSS_BASIC_RATES = 36
+NL80211_ATTR_WIPHY_TXQ_PARAMS = 37
+NL80211_ATTR_WIPHY_FREQ = 38
+NL80211_ATTR_WIPHY_CHANNEL_TYPE = 39
+NL80211_ATTR_KEY_DEFAULT_MGMT = 40
+NL80211_ATTR_MGMT_SUBTYPE = 41
+NL80211_ATTR_IE = 42
+NL80211_ATTR_MAX_NUM_SCAN_SSIDS = 43
+NL80211_ATTR_SCAN_FREQUENCIES = 44
+NL80211_ATTR_SCAN_SSIDS = 45
+NL80211_ATTR_GENERATION = 46
+NL80211_ATTR_BSS = 47
+NL80211_ATTR_REG_INITIATOR = 48
+NL80211_ATTR_REG_TYPE = 49
+NL80211_ATTR_SUPPORTED_COMMANDS = 50
+NL80211_ATTR_FRAME = 51
+NL80211_ATTR_SSID = 52
+NL80211_ATTR_AUTH_TYPE = 53
+NL80211_ATTR_REASON_CODE = 54
+NL80211_ATTR_KEY_TYPE = 55
+NL80211_ATTR_MAX_SCAN_IE_LEN = 56
+NL80211_ATTR_CIPHER_SUITES = 57
+NL80211_ATTR_FREQ_BEFORE = 58
+NL80211_ATTR_FREQ_AFTER = 59
+NL80211_ATTR_FREQ_FIXED = 60
+NL80211_ATTR_WIPHY_RETRY_SHORT = 61
+NL80211_ATTR_WIPHY_RETRY_LONG = 62
+NL80211_ATTR_WIPHY_FRAG_THRESHOLD = 63
+NL80211_ATTR_WIPHY_RTS_THRESHOLD = 64
+NL80211_ATTR_TIMED_OUT = 65
+NL80211_ATTR_USE_MFP = 66
+NL80211_ATTR_STA_FLAGS2 = 67
+NL80211_ATTR_CONTROL_PORT = 68
+NL80211_ATTR_TESTDATA = 69
+NL80211_ATTR_PRIVACY = 70
+NL80211_ATTR_DISCONNECTED_BY_AP = 71
+NL80211_ATTR_STATUS_CODE = 72
+NL80211_ATTR_CIPHER_SUITES_PAIRWISE = 73
+NL80211_ATTR_CIPHER_SUITE_GROUP = 74
+NL80211_ATTR_WPA_VERSIONS = 75
+NL80211_ATTR_AKM_SUITES = 76
+NL80211_ATTR_REQ_IE = 77
+NL80211_ATTR_RESP_IE = 78
+NL80211_ATTR_PREV_BSSID = 79
+NL80211_ATTR_KEY = 80
+NL80211_ATTR_KEYS = 81
+NL80211_ATTR_PID = 82
+NL80211_ATTR_4ADDR = 83
+NL80211_ATTR_SURVEY_INFO = 84
+NL80211_ATTR_PMKID = 85
+NL80211_ATTR_MAX_NUM_PMKIDS = 86
+NL80211_ATTR_DURATION = 87
+NL80211_ATTR_COOKIE = 88
+NL80211_ATTR_WIPHY_COVERAGE_CLASS = 89
+NL80211_ATTR_TX_RATES = 90
+NL80211_ATTR_FRAME_MATCH = 91
+NL80211_ATTR_ACK = 92
+NL80211_ATTR_PS_STATE = 93
+NL80211_ATTR_CQM = 94
+NL80211_ATTR_LOCAL_STATE_CHANGE = 95
+NL80211_ATTR_AP_ISOLATE = 96
+NL80211_ATTR_WIPHY_TX_POWER_SETTING = 97
+NL80211_ATTR_WIPHY_TX_POWER_LEVEL = 98
+NL80211_ATTR_TX_FRAME_TYPES = 99
+NL80211_ATTR_RX_FRAME_TYPES = 100
+NL80211_ATTR_FRAME_TYPE = 101
+NL80211_ATTR_CONTROL_PORT_ETHERTYPE = 102
+NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT = 103
+NL80211_ATTR_SUPPORT_IBSS_RSN = 104
+NL80211_ATTR_WIPHY_ANTENNA_TX = 105
+NL80211_ATTR_WIPHY_ANTENNA_RX = 106
+NL80211_ATTR_MCAST_RATE = 107
+NL80211_ATTR_OFFCHANNEL_TX_OK = 108
+NL80211_ATTR_BSS_HT_OPMODE = 109
+NL80211_ATTR_KEY_DEFAULT_TYPES = 110
+NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION = 111
+NL80211_ATTR_MESH_SETUP = 112
+NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX = 113
+NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX = 114
+NL80211_ATTR_SUPPORT_MESH_AUTH = 115
+NL80211_ATTR_STA_PLINK_STATE = 116
+NL80211_ATTR_WOWLAN_TRIGGERS = 117
+NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED = 118
+NL80211_ATTR_SCHED_SCAN_INTERVAL = 119
+NL80211_ATTR_INTERFACE_COMBINATIONS = 120
+NL80211_ATTR_SOFTWARE_IFTYPES = 121
+NL80211_ATTR_REKEY_DATA = 122
+NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS = 123
+NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN = 124
+NL80211_ATTR_SCAN_SUPP_RATES = 125
+NL80211_ATTR_HIDDEN_SSID = 126
+NL80211_ATTR_IE_PROBE_RESP = 127
+NL80211_ATTR_IE_ASSOC_RESP = 128
+NL80211_ATTR_STA_WME = 129
+NL80211_ATTR_SUPPORT_AP_UAPSD = 130
+NL80211_ATTR_ROAM_SUPPORT = 131
+NL80211_ATTR_SCHED_SCAN_MATCH = 132
+NL80211_ATTR_MAX_MATCH_SETS = 133
+NL80211_ATTR_PMKSA_CANDIDATE = 134
+NL80211_ATTR_TX_NO_CCK_RATE = 135
+NL80211_ATTR_TDLS_ACTION = 136
+NL80211_ATTR_TDLS_DIALOG_TOKEN = 137
+NL80211_ATTR_TDLS_OPERATION = 138
+NL80211_ATTR_TDLS_SUPPORT = 139
+NL80211_ATTR_TDLS_EXTERNAL_SETUP = 140
+NL80211_ATTR_DEVICE_AP_SME = 141
+NL80211_ATTR_DONT_WAIT_FOR_ACK = 142
+NL80211_ATTR_FEATURE_FLAGS = 143
+NL80211_ATTR_PROBE_RESP_OFFLOAD = 144
+NL80211_ATTR_PROBE_RESP = 145
+NL80211_ATTR_DFS_REGION = 146
+NL80211_ATTR_DISABLE_HT = 147
+NL80211_ATTR_HT_CAPABILITY_MASK = 148
+NL80211_ATTR_NOACK_MAP = 149
+NL80211_ATTR_INACTIVITY_TIMEOUT = 150
+NL80211_ATTR_RX_SIGNAL_DBM = 151
+NL80211_ATTR_BG_SCAN_PERIOD = 152
+NL80211_ATTR_WDEV = 153
+NL80211_ATTR_USER_REG_HINT_TYPE = 154
+NL80211_ATTR_CONN_FAILED_REASON = 155
+NL80211_ATTR_SAE_DATA = 156
+NL80211_ATTR_VHT_CAPABILITY = 157
+NL80211_ATTR_SCAN_FLAGS = 158
+NL80211_ATTR_CHANNEL_WIDTH = 159
+NL80211_ATTR_CENTER_FREQ1 = 160
+NL80211_ATTR_CENTER_FREQ2 = 161
+NL80211_ATTR_P2P_CTWINDOW = 162
+NL80211_ATTR_P2P_OPPPS = 163
+NL80211_ATTR_LOCAL_MESH_POWER_MODE = 164
+NL80211_ATTR_ACL_POLICY = 165
+NL80211_ATTR_MAC_ADDRS = 166
+NL80211_ATTR_MAC_ACL_MAX = 167
+NL80211_ATTR_RADAR_EVENT = 168
+NL80211_ATTR_EXT_CAPA = 169
+NL80211_ATTR_EXT_CAPA_MASK = 170
+NL80211_ATTR_STA_CAPABILITY = 171
+NL80211_ATTR_STA_EXT_CAPABILITY = 172
+NL80211_ATTR_PROTOCOL_FEATURES = 173
+NL80211_ATTR_SPLIT_WIPHY_DUMP = 174
+NL80211_ATTR_DISABLE_VHT = 175
+NL80211_ATTR_VHT_CAPABILITY_MASK = 176
+NL80211_ATTR_MDID = 177
+NL80211_ATTR_IE_RIC = 178
+NL80211_ATTR_CRIT_PROT_ID = 179
+NL80211_ATTR_MAX_CRIT_PROT_DURATION = 180
+NL80211_ATTR_PEER_AID = 181
+__NL80211_ATTR_AFTER_LAST = 182
+NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
+NL80211_IFTYPE_UNSPECIFIED = 0
+NL80211_IFTYPE_ADHOC = 1
+NL80211_IFTYPE_STATION = 2
+NL80211_IFTYPE_AP = 3
+NL80211_IFTYPE_AP_VLAN = 4
+NL80211_IFTYPE_WDS = 5
+NL80211_IFTYPE_MONITOR = 6
+NL80211_IFTYPE_MESH_POINT = 7
+NL80211_IFTYPE_P2P_CLIENT = 8
+NL80211_IFTYPE_P2P_GO = 9
+NL80211_IFTYPE_P2P_DEVICE = 10
+NUM_NL80211_IFTYPES = 11
+NL80211_IFTYPE_MAX = NUM_NL80211_IFTYPES - 1
+__NL80211_STA_FLAG_INVALID = 0
+NL80211_STA_FLAG_AUTHORIZED = 1
+NL80211_STA_FLAG_SHORT_PREAMBLE = 2
+NL80211_STA_FLAG_WME = 3
+NL80211_STA_FLAG_MFP = 4
+NL80211_STA_FLAG_AUTHENTICATED = 5
+NL80211_STA_FLAG_TDLS_PEER = 6
+NL80211_STA_FLAG_ASSOCIATED = 7
+__NL80211_STA_FLAG_AFTER_LAST = 8
+NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
+__NL80211_RATE_INFO_INVALID = 0
+NL80211_RATE_INFO_BITRATE = 1
+NL80211_RATE_INFO_MCS = 2
+NL80211_RATE_INFO_40_MHZ_WIDTH = 3
+NL80211_RATE_INFO_SHORT_GI = 4
+NL80211_RATE_INFO_BITRATE32 = 5
+NL80211_RATE_INFO_VHT_MCS = 6
+NL80211_RATE_INFO_VHT_NSS = 7
+NL80211_RATE_INFO_80_MHZ_WIDTH = 8
+NL80211_RATE_INFO_80P80_MHZ_WIDTH = 9
+NL80211_RATE_INFO_160_MHZ_WIDTH = 10
+__NL80211_RATE_INFO_AFTER_LAST = 11
+NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1
+__NL80211_STA_BSS_PARAM_INVALID = 0
+NL80211_STA_BSS_PARAM_CTS_PROT = 1
+NL80211_STA_BSS_PARAM_SHORT_PREAMBLE = 2
+NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME = 3
+NL80211_STA_BSS_PARAM_DTIM_PERIOD = 4
+NL80211_STA_BSS_PARAM_BEACON_INTERVAL = 5
+__NL80211_STA_BSS_PARAM_AFTER_LAST = 6
+NL80211_STA_BSS_PARAM_MAX = __NL80211_STA_BSS_PARAM_AFTER_LAST - 1
+__NL80211_STA_INFO_INVALID = 0
+NL80211_STA_INFO_INACTIVE_TIME = 1
+NL80211_STA_INFO_RX_BYTES = 2
+NL80211_STA_INFO_TX_BYTES = 3
+NL80211_STA_INFO_LLID = 4
+NL80211_STA_INFO_PLID = 5
+NL80211_STA_INFO_PLINK_STATE = 6
+NL80211_STA_INFO_SIGNAL = 7
+NL80211_STA_INFO_TX_BITRATE = 8
+NL80211_STA_INFO_RX_PACKETS = 9
+NL80211_STA_INFO_TX_PACKETS = 10
+NL80211_STA_INFO_TX_RETRIES = 11
+NL80211_STA_INFO_TX_FAILED = 12
+NL80211_STA_INFO_SIGNAL_AVG = 13
+NL80211_STA_INFO_RX_BITRATE = 14
+NL80211_STA_INFO_BSS_PARAM = 15
+NL80211_STA_INFO_CONNECTED_TIME = 16
+NL80211_STA_INFO_STA_FLAGS = 17
+NL80211_STA_INFO_BEACON_LOSS = 18
+NL80211_STA_INFO_T_OFFSET = 19
+NL80211_STA_INFO_LOCAL_PM = 20
+NL80211_STA_INFO_PEER_PM = 21
+NL80211_STA_INFO_NONPEER_PM = 22
+NL80211_STA_INFO_RX_BYTES64 = 23
+NL80211_STA_INFO_TX_BYTES64 = 24
+NL80211_STA_INFO_CHAIN_SIGNAL = 25
+NL80211_STA_INFO_CHAIN_SIGNAL_AVG = 26
+__NL80211_STA_INFO_AFTER_LAST = 27
+NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1
+NL80211_MPATH_FLAG_ACTIVE = 1 << 0
+NL80211_MPATH_FLAG_RESOLVING = 1 << 1
+NL80211_MPATH_FLAG_SN_VALID = 1 << 2
+NL80211_MPATH_FLAG_FIXED = 1 << 3
+NL80211_MPATH_FLAG_RESOLVED = 1 << 4
+__NL80211_MPATH_INFO_INVALID = 0
+NL80211_MPATH_INFO_FRAME_QLEN = 1
+NL80211_MPATH_INFO_SN = 2
+NL80211_MPATH_INFO_METRIC = 3
+NL80211_MPATH_INFO_EXPTIME = 4
+NL80211_MPATH_INFO_FLAGS = 5
+NL80211_MPATH_INFO_DISCOVERY_TIMEOUT = 6
+NL80211_MPATH_INFO_DISCOVERY_RETRIES = 7
+__NL80211_MPATH_INFO_AFTER_LAST = 8
+NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1
+__NL80211_BAND_ATTR_INVALID = 0
+NL80211_BAND_ATTR_FREQS = 1
+NL80211_BAND_ATTR_RATES = 2
+NL80211_BAND_ATTR_HT_MCS_SET = 3
+NL80211_BAND_ATTR_HT_CAPA = 4
+NL80211_BAND_ATTR_HT_AMPDU_FACTOR = 5
+NL80211_BAND_ATTR_HT_AMPDU_DENSITY = 6
+NL80211_BAND_ATTR_VHT_MCS_SET = 7
+NL80211_BAND_ATTR_VHT_CAPA = 8
+__NL80211_BAND_ATTR_AFTER_LAST = 9
+NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
+__NL80211_FREQUENCY_ATTR_INVALID = 0
+NL80211_FREQUENCY_ATTR_FREQ = 1
+NL80211_FREQUENCY_ATTR_DISABLED = 2
+NL80211_FREQUENCY_ATTR_PASSIVE_SCAN = 3
+NL80211_FREQUENCY_ATTR_NO_IBSS = 4
+NL80211_FREQUENCY_ATTR_RADAR = 5
+NL80211_FREQUENCY_ATTR_MAX_TX_POWER = 6
+NL80211_FREQUENCY_ATTR_DFS_STATE = 7
+NL80211_FREQUENCY_ATTR_DFS_TIME = 8
+NL80211_FREQUENCY_ATTR_NO_HT40_MINUS = 9
+NL80211_FREQUENCY_ATTR_NO_HT40_PLUS = 10
+NL80211_FREQUENCY_ATTR_NO_80MHZ = 11
+NL80211_FREQUENCY_ATTR_NO_160MHZ = 12
+__NL80211_FREQUENCY_ATTR_AFTER_LAST = 13
+NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1
+__NL80211_BITRATE_ATTR_INVALID = 0
+NL80211_BITRATE_ATTR_RATE = 1
+NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE = 2
+__NL80211_BITRATE_ATTR_AFTER_LAST = 3
+NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1
+NL80211_REGDOM_SET_BY_CORE = 0
+NL80211_REGDOM_SET_BY_USER = 1
+NL80211_REGDOM_SET_BY_DRIVER = 2
+NL80211_REGDOM_SET_BY_COUNTRY_IE = 3
+NL80211_REGDOM_TYPE_COUNTRY = 0
+NL80211_REGDOM_TYPE_WORLD = 1
+NL80211_REGDOM_TYPE_CUSTOM_WORLD = 2
+NL80211_REGDOM_TYPE_INTERSECTION = 3
+__NL80211_REG_RULE_ATTR_INVALID = 0
+NL80211_ATTR_REG_RULE_FLAGS = 1
+NL80211_ATTR_FREQ_RANGE_START = 2
+NL80211_ATTR_FREQ_RANGE_END = 3
+NL80211_ATTR_FREQ_RANGE_MAX_BW = 4
+NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN = 5
+NL80211_ATTR_POWER_RULE_MAX_EIRP = 6
+__NL80211_REG_RULE_ATTR_AFTER_LAST = 7
+NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
+__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID = 0
+NL80211_SCHED_SCAN_MATCH_ATTR_SSID = 1
+NL80211_SCHED_SCAN_MATCH_ATTR_RSSI = 2
+__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST = 3
+NL80211_SCHED_SCAN_MATCH_ATTR_MAX = __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST - 1
+NL80211_RRF_NO_OFDM = 1 << 0
+NL80211_RRF_NO_CCK = 1 << 1
+NL80211_RRF_NO_INDOOR = 1 << 2
+NL80211_RRF_NO_OUTDOOR = 1 << 3
+NL80211_RRF_DFS = 1 << 4
+NL80211_RRF_PTP_ONLY = 1 << 5
+NL80211_RRF_PTMP_ONLY = 1 << 6
+NL80211_RRF_PASSIVE_SCAN = 1 << 7
+NL80211_RRF_NO_IBSS = 1 << 8
+NL80211_DFS_UNSET = 0
+NL80211_DFS_FCC = 1
+NL80211_DFS_ETSI = 2
+NL80211_DFS_JP = 3
+NL80211_USER_REG_HINT_USER = 0
+NL80211_USER_REG_HINT_CELL_BASE = 1
+__NL80211_SURVEY_INFO_INVALID = 0
+NL80211_SURVEY_INFO_FREQUENCY = 1
+NL80211_SURVEY_INFO_NOISE = 2
+NL80211_SURVEY_INFO_IN_USE = 3
+NL80211_SURVEY_INFO_CHANNEL_TIME = 4
+NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY = 5
+NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY = 6
+NL80211_SURVEY_INFO_CHANNEL_TIME_RX = 7
+NL80211_SURVEY_INFO_CHANNEL_TIME_TX = 8
+__NL80211_SURVEY_INFO_AFTER_LAST = 9
+NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1
+__NL80211_MNTR_FLAG_INVALID = 0
+NL80211_MNTR_FLAG_FCSFAIL = 1
+NL80211_MNTR_FLAG_PLCPFAIL = 2
+NL80211_MNTR_FLAG_CONTROL = 3
+NL80211_MNTR_FLAG_OTHER_BSS = 4
+NL80211_MNTR_FLAG_COOK_FRAMES = 5
+NL80211_MNTR_FLAG_ACTIVE = 6
+__NL80211_MNTR_FLAG_AFTER_LAST = 7
+NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1
+NL80211_MESH_POWER_UNKNOWN = 0
+NL80211_MESH_POWER_ACTIVE = 1
+NL80211_MESH_POWER_LIGHT_SLEEP = 2
+NL80211_MESH_POWER_DEEP_SLEEP = 3
+__NL80211_MESH_POWER_AFTER_LAST = 4
+NL80211_MESH_POWER_MAX = __NL80211_MESH_POWER_AFTER_LAST - 1
+__NL80211_MESHCONF_INVALID = 0
+NL80211_MESHCONF_RETRY_TIMEOUT = 1
+NL80211_MESHCONF_CONFIRM_TIMEOUT = 2
+NL80211_MESHCONF_HOLDING_TIMEOUT = 3
+NL80211_MESHCONF_MAX_PEER_LINKS = 4
+NL80211_MESHCONF_MAX_RETRIES = 5
+NL80211_MESHCONF_TTL = 6
+NL80211_MESHCONF_AUTO_OPEN_PLINKS = 7
+NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES = 8
+NL80211_MESHCONF_PATH_REFRESH_TIME = 9
+NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT = 10
+NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT = 11
+NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL = 12
+NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME = 13
+NL80211_MESHCONF_HWMP_ROOTMODE = 14
+NL80211_MESHCONF_ELEMENT_TTL = 15
+NL80211_MESHCONF_HWMP_RANN_INTERVAL = 16
+NL80211_MESHCONF_GATE_ANNOUNCEMENTS = 17
+NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL = 18
+NL80211_MESHCONF_FORWARDING = 19
+NL80211_MESHCONF_RSSI_THRESHOLD = 20
+NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR = 21
+NL80211_MESHCONF_HT_OPMODE = 22
+NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT = 23
+NL80211_MESHCONF_HWMP_ROOT_INTERVAL = 24
+NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL = 25
+NL80211_MESHCONF_POWER_MODE = 26
+NL80211_MESHCONF_AWAKE_WINDOW = 27
+NL80211_MESHCONF_PLINK_TIMEOUT = 28
+__NL80211_MESHCONF_ATTR_AFTER_LAST = 29
+NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1
+__NL80211_MESH_SETUP_INVALID = 0
+NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL = 1
+NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC = 2
+NL80211_MESH_SETUP_IE = 3
+NL80211_MESH_SETUP_USERSPACE_AUTH = 4
+NL80211_MESH_SETUP_USERSPACE_AMPE = 5
+NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC = 6
+NL80211_MESH_SETUP_USERSPACE_MPM = 7
+NL80211_MESH_SETUP_AUTH_PROTOCOL = 8
+__NL80211_MESH_SETUP_ATTR_AFTER_LAST = 9
+NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1
+__NL80211_TXQ_ATTR_INVALID = 0
+NL80211_TXQ_ATTR_AC = 1
+NL80211_TXQ_ATTR_TXOP = 2
+NL80211_TXQ_ATTR_CWMIN = 3
+NL80211_TXQ_ATTR_CWMAX = 4
+NL80211_TXQ_ATTR_AIFS = 5
+__NL80211_TXQ_ATTR_AFTER_LAST = 6
+NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1
+NL80211_AC_VO = 0
+NL80211_AC_VI = 1
+NL80211_AC_BE = 2
+NL80211_AC_BK = 3
+NL80211_NUM_ACS = 4
+NL80211_CHAN_NO_HT = 0
+NL80211_CHAN_HT20 = 1
+NL80211_CHAN_HT40MINUS = 2
+NL80211_CHAN_HT40PLUS = 3
+NL80211_CHAN_WIDTH_20_NOHT = 0
+NL80211_CHAN_WIDTH_20 = 1
+NL80211_CHAN_WIDTH_40 = 2
+NL80211_CHAN_WIDTH_80 = 3
+NL80211_CHAN_WIDTH_80P80 = 4
+NL80211_CHAN_WIDTH_160 = 5
+__NL80211_BSS_INVALID = 0
+NL80211_BSS_BSSID = 1
+NL80211_BSS_FREQUENCY = 2
+NL80211_BSS_TSF = 3
+NL80211_BSS_BEACON_INTERVAL = 4
+NL80211_BSS_CAPABILITY = 5
+NL80211_BSS_INFORMATION_ELEMENTS = 6
+NL80211_BSS_SIGNAL_MBM = 7
+NL80211_BSS_SIGNAL_UNSPEC = 8
+NL80211_BSS_STATUS = 9
+NL80211_BSS_SEEN_MS_AGO = 10
+NL80211_BSS_BEACON_IES = 11
+__NL80211_BSS_AFTER_LAST = 12
+NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1
+NL80211_BSS_STATUS_AUTHENTICATED = 0
+NL80211_BSS_STATUS_ASSOCIATED = 1
+NL80211_BSS_STATUS_IBSS_JOINED = 2
+NL80211_AUTHTYPE_OPEN_SYSTEM = 0
+NL80211_AUTHTYPE_SHARED_KEY = 1
+NL80211_AUTHTYPE_FT = 2
+NL80211_AUTHTYPE_NETWORK_EAP = 3
+NL80211_AUTHTYPE_SAE = 4
+__NL80211_AUTHTYPE_NUM = 5
+NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1
+NL80211_AUTHTYPE_AUTOMATIC = 6
+NL80211_KEYTYPE_GROUP = 0
+NL80211_KEYTYPE_PAIRWISE = 1
+NL80211_KEYTYPE_PEERKEY = 2
+NUM_NL80211_KEYTYPES = 3
+NL80211_MFP_NO = 0
+NL80211_MFP_REQUIRED = 1
+NL80211_WPA_VERSION_1 = 1 << 0
+NL80211_WPA_VERSION_2 = 1 << 1
+__NL80211_KEY_DEFAULT_TYPE_INVALID = 0
+NL80211_KEY_DEFAULT_TYPE_UNICAST = 1
+NL80211_KEY_DEFAULT_TYPE_MULTICAST = 2
+NUM_NL80211_KEY_DEFAULT_TYPES = 3
+__NL80211_KEY_INVALID = 0
+NL80211_KEY_DATA = 1
+NL80211_KEY_IDX = 2
+NL80211_KEY_CIPHER = 3
+NL80211_KEY_SEQ = 4
+NL80211_KEY_DEFAULT = 5
+NL80211_KEY_DEFAULT_MGMT = 6
+NL80211_KEY_TYPE = 7
+NL80211_KEY_DEFAULT_TYPES = 8
+__NL80211_KEY_AFTER_LAST = 9
+NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1
+__NL80211_TXRATE_INVALID = 0
+NL80211_TXRATE_LEGACY = 1
+NL80211_TXRATE_MCS = 2
+__NL80211_TXRATE_AFTER_LAST = 3
+NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1
+NL80211_BAND_2GHZ = 0
+NL80211_BAND_5GHZ = 1
+NL80211_BAND_60GHZ = 2
+NL80211_PS_DISABLED = 0
+NL80211_PS_ENABLED = 1
+__NL80211_ATTR_CQM_INVALID = 0
+NL80211_ATTR_CQM_RSSI_THOLD = 1
+NL80211_ATTR_CQM_RSSI_HYST = 2
+NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT = 3
+NL80211_ATTR_CQM_PKT_LOSS_EVENT = 4
+NL80211_ATTR_CQM_TXE_RATE = 5
+NL80211_ATTR_CQM_TXE_PKTS = 6
+NL80211_ATTR_CQM_TXE_INTVL = 7
+__NL80211_ATTR_CQM_AFTER_LAST = 8
+NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1
+NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW = 0
+NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH = 1
+NL80211_CQM_RSSI_BEACON_LOSS_EVENT = 2
+NL80211_TX_POWER_AUTOMATIC = 0
+NL80211_TX_POWER_LIMITED = 1
+NL80211_TX_POWER_FIXED = 2
+__NL80211_WOWLAN_PKTPAT_INVALID = 0
+NL80211_WOWLAN_PKTPAT_MASK = 1
+NL80211_WOWLAN_PKTPAT_PATTERN = 2
+NL80211_WOWLAN_PKTPAT_OFFSET = 3
+NUM_NL80211_WOWLAN_PKTPAT = 4
+MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1
+__NL80211_WOWLAN_TRIG_INVALID = 0
+NL80211_WOWLAN_TRIG_ANY = 1
+NL80211_WOWLAN_TRIG_DISCONNECT = 2
+NL80211_WOWLAN_TRIG_MAGIC_PKT = 3
+NL80211_WOWLAN_TRIG_PKT_PATTERN = 4
+NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED = 5
+NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE = 6
+NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST = 7
+NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE = 8
+NL80211_WOWLAN_TRIG_RFKILL_RELEASE = 9
+NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211 = 10
+NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN = 11
+NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023 = 12
+NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN = 13
+NL80211_WOWLAN_TRIG_TCP_CONNECTION = 14
+NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH = 15
+NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST = 16
+NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS = 17
+NUM_NL80211_WOWLAN_TRIG = 18
+MAX_NL80211_WOWLAN_TRIG = NUM_NL80211_WOWLAN_TRIG - 1
+__NL80211_WOWLAN_TCP_INVALID = 0
+NL80211_WOWLAN_TCP_SRC_IPV4 = 1
+NL80211_WOWLAN_TCP_DST_IPV4 = 2
+NL80211_WOWLAN_TCP_DST_MAC = 3
+NL80211_WOWLAN_TCP_SRC_PORT = 4
+NL80211_WOWLAN_TCP_DST_PORT = 5
+NL80211_WOWLAN_TCP_DATA_PAYLOAD = 6
+NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ = 7
+NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN = 8
+NL80211_WOWLAN_TCP_DATA_INTERVAL = 9
+NL80211_WOWLAN_TCP_WAKE_PAYLOAD = 10
+NL80211_WOWLAN_TCP_WAKE_MASK = 11
+NUM_NL80211_WOWLAN_TCP = 12
+MAX_NL80211_WOWLAN_TCP = NUM_NL80211_WOWLAN_TCP - 1
+NL80211_IFACE_LIMIT_UNSPEC = 0
+NL80211_IFACE_LIMIT_MAX = 1
+NL80211_IFACE_LIMIT_TYPES = 2
+NUM_NL80211_IFACE_LIMIT = 3
+MAX_NL80211_IFACE_LIMIT = NUM_NL80211_IFACE_LIMIT - 1
+NL80211_IFACE_COMB_UNSPEC = 0
+NL80211_IFACE_COMB_LIMITS = 1
+NL80211_IFACE_COMB_MAXNUM = 2
+NL80211_IFACE_COMB_STA_AP_BI_MATCH = 3
+NL80211_IFACE_COMB_NUM_CHANNELS = 4
+NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS = 5
+NUM_NL80211_IFACE_COMB = 6
+MAX_NL80211_IFACE_COMB = NUM_NL80211_IFACE_COMB - 1
+NL80211_PLINK_LISTEN = 0
+NL80211_PLINK_OPN_SNT = 1
+NL80211_PLINK_OPN_RCVD = 2
+NL80211_PLINK_CNF_RCVD = 3
+NL80211_PLINK_ESTAB = 4
+NL80211_PLINK_HOLDING = 5
+NL80211_PLINK_BLOCKED = 6
+NUM_NL80211_PLINK_STATES = 7
+MAX_NL80211_PLINK_STATES = NUM_NL80211_PLINK_STATES - 1
+NL80211_PLINK_ACTION_NO_ACTION = 0
+NL80211_PLINK_ACTION_OPEN = 1
+NL80211_PLINK_ACTION_BLOCK = 2
+NUM_NL80211_PLINK_ACTIONS = 3
+__NL80211_REKEY_DATA_INVALID = 0
+NL80211_REKEY_DATA_KEK = 1
+NL80211_REKEY_DATA_KCK = 2
+NL80211_REKEY_DATA_REPLAY_CTR = 3
+NUM_NL80211_REKEY_DATA = 4
+MAX_NL80211_REKEY_DATA = NUM_NL80211_REKEY_DATA - 1
+NL80211_HIDDEN_SSID_NOT_IN_USE = 0
+NL80211_HIDDEN_SSID_ZERO_LEN = 1
+NL80211_HIDDEN_SSID_ZERO_CONTENTS = 2
+__NL80211_STA_WME_INVALID = 0
+NL80211_STA_WME_UAPSD_QUEUES = 1
+NL80211_STA_WME_MAX_SP = 2
+__NL80211_STA_WME_AFTER_LAST = 3
+NL80211_STA_WME_MAX = __NL80211_STA_WME_AFTER_LAST - 1
+__NL80211_PMKSA_CANDIDATE_INVALID = 0
+NL80211_PMKSA_CANDIDATE_INDEX = 1
+NL80211_PMKSA_CANDIDATE_BSSID = 2
+NL80211_PMKSA_CANDIDATE_PREAUTH = 3
+NUM_NL80211_PMKSA_CANDIDATE = 4
+MAX_NL80211_PMKSA_CANDIDATE = NUM_NL80211_PMKSA_CANDIDATE - 1
+NL80211_TDLS_DISCOVERY_REQ = 0
+NL80211_TDLS_SETUP = 1
+NL80211_TDLS_TEARDOWN = 2
+NL80211_TDLS_ENABLE_LINK = 3
+NL80211_TDLS_DISABLE_LINK = 4
+NL80211_FEATURE_SK_TX_STATUS = 1 << 0
+NL80211_FEATURE_HT_IBSS = 1 << 1
+NL80211_FEATURE_INACTIVITY_TIMER = 1 << 2
+NL80211_FEATURE_CELL_BASE_REG_HINTS = 1 << 3
+NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL = 1 << 4
+NL80211_FEATURE_SAE = 1 << 5
+NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6
+NL80211_FEATURE_SCAN_FLUSH = 1 << 7
+NL80211_FEATURE_AP_SCAN = 1 << 8
+NL80211_FEATURE_VIF_TXPOWER = 1 << 9
+NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10
+NL80211_FEATURE_P2P_GO_CTWIN = 1 << 11
+NL80211_FEATURE_P2P_GO_OPPPS = 1 << 12
+NL80211_FEATURE_ADVERTISE_CHAN_LIMITS = 1 << 14
+NL80211_FEATURE_FULL_AP_CLIENT_STATE = 1 << 15
+NL80211_FEATURE_USERSPACE_MPM = 1 << 16
+NL80211_FEATURE_ACTIVE_MONITOR = 1 << 17
+NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS = 1 << 0
+NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 = 1 << 1
+NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P = 1 << 2
+NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U = 1 << 3
+NL80211_CONN_FAIL_MAX_CLIENTS = 0
+NL80211_CONN_FAIL_BLOCKED_CLIENT = 1
+NL80211_SCAN_FLAG_LOW_PRIORITY = 1 << 0
+NL80211_SCAN_FLAG_FLUSH = 1 << 1
+NL80211_SCAN_FLAG_AP = 1 << 2
+NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED = 0
+NL80211_ACL_POLICY_DENY_UNLESS_LISTED = 1
+NL80211_RADAR_DETECTED = 0
+NL80211_RADAR_CAC_FINISHED = 1
+NL80211_RADAR_CAC_ABORTED = 2
+NL80211_RADAR_NOP_FINISHED = 3
+NL80211_DFS_USABLE = 0
+NL80211_DFS_UNAVAILABLE = 1
+NL80211_DFS_AVAILABLE = 2
+NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP = 1 << 0
+NL80211_CRIT_PROTO_UNSPEC = 0
+NL80211_CRIT_PROTO_DHCP = 1
+NL80211_CRIT_PROTO_EAPOL = 2
+NL80211_CRIT_PROTO_APIPA = 3
+NUM_NL80211_CRIT_PROTO = 4
+nl80211_commands2str = {
+ NL80211_CMD_UNSPEC: "NL80211_CMD_UNSPEC",
+ NL80211_CMD_GET_WIPHY: "NL80211_CMD_GET_WIPHY",
+ NL80211_CMD_SET_WIPHY: "NL80211_CMD_SET_WIPHY",
+ NL80211_CMD_NEW_WIPHY: "NL80211_CMD_NEW_WIPHY",
+ NL80211_CMD_DEL_WIPHY: "NL80211_CMD_DEL_WIPHY",
+ NL80211_CMD_GET_INTERFACE: "NL80211_CMD_GET_INTERFACE",
+ NL80211_CMD_SET_INTERFACE: "NL80211_CMD_SET_INTERFACE",
+ NL80211_CMD_NEW_INTERFACE: "NL80211_CMD_NEW_INTERFACE",
+ NL80211_CMD_DEL_INTERFACE: "NL80211_CMD_DEL_INTERFACE",
+ NL80211_CMD_GET_KEY: "NL80211_CMD_GET_KEY",
+ NL80211_CMD_SET_KEY: "NL80211_CMD_SET_KEY",
+ NL80211_CMD_NEW_KEY: "NL80211_CMD_NEW_KEY",
+ NL80211_CMD_DEL_KEY: "NL80211_CMD_DEL_KEY",
+ NL80211_CMD_GET_BEACON: "NL80211_CMD_GET_BEACON",
+ NL80211_CMD_SET_BEACON: "NL80211_CMD_SET_BEACON",
+ NL80211_CMD_START_AP: "NL80211_CMD_START_AP",
+ NL80211_CMD_STOP_AP: "NL80211_CMD_STOP_AP",
+ NL80211_CMD_GET_STATION: "NL80211_CMD_GET_STATION",
+ NL80211_CMD_SET_STATION: "NL80211_CMD_SET_STATION",
+ NL80211_CMD_NEW_STATION: "NL80211_CMD_NEW_STATION",
+ NL80211_CMD_DEL_STATION: "NL80211_CMD_DEL_STATION",
+ NL80211_CMD_GET_MPATH: "NL80211_CMD_GET_MPATH",
+ NL80211_CMD_SET_MPATH: "NL80211_CMD_SET_MPATH",
+ NL80211_CMD_NEW_MPATH: "NL80211_CMD_NEW_MPATH",
+ NL80211_CMD_DEL_MPATH: "NL80211_CMD_DEL_MPATH",
+ NL80211_CMD_SET_BSS: "NL80211_CMD_SET_BSS",
+ NL80211_CMD_SET_REG: "NL80211_CMD_SET_REG",
+ NL80211_CMD_REQ_SET_REG: "NL80211_CMD_REQ_SET_REG",
+ NL80211_CMD_GET_MESH_CONFIG: "NL80211_CMD_GET_MESH_CONFIG",
+ NL80211_CMD_SET_MESH_CONFIG: "NL80211_CMD_SET_MESH_CONFIG",
+ NL80211_CMD_SET_MGMT_EXTRA_IE: "NL80211_CMD_SET_MGMT_EXTRA_IE",
+ NL80211_CMD_GET_REG: "NL80211_CMD_GET_REG",
+ NL80211_CMD_GET_SCAN: "NL80211_CMD_GET_SCAN",
+ NL80211_CMD_TRIGGER_SCAN: "NL80211_CMD_TRIGGER_SCAN",
+ NL80211_CMD_NEW_SCAN_RESULTS: "NL80211_CMD_NEW_SCAN_RESULTS",
+ NL80211_CMD_SCAN_ABORTED: "NL80211_CMD_SCAN_ABORTED",
+ NL80211_CMD_REG_CHANGE: "NL80211_CMD_REG_CHANGE",
+ NL80211_CMD_AUTHENTICATE: "NL80211_CMD_AUTHENTICATE",
+ NL80211_CMD_ASSOCIATE: "NL80211_CMD_ASSOCIATE",
+ NL80211_CMD_DEAUTHENTICATE: "NL80211_CMD_DEAUTHENTICATE",
+ NL80211_CMD_DISASSOCIATE: "NL80211_CMD_DISASSOCIATE",
+ NL80211_CMD_MICHAEL_MIC_FAILURE: "NL80211_CMD_MICHAEL_MIC_FAILURE",
+ NL80211_CMD_REG_BEACON_HINT: "NL80211_CMD_REG_BEACON_HINT",
+ NL80211_CMD_JOIN_IBSS: "NL80211_CMD_JOIN_IBSS",
+ NL80211_CMD_LEAVE_IBSS: "NL80211_CMD_LEAVE_IBSS",
+ NL80211_CMD_TESTMODE: "NL80211_CMD_TESTMODE",
+ NL80211_CMD_CONNECT: "NL80211_CMD_CONNECT",
+ NL80211_CMD_ROAM: "NL80211_CMD_ROAM",
+ NL80211_CMD_DISCONNECT: "NL80211_CMD_DISCONNECT",
+ NL80211_CMD_SET_WIPHY_NETNS: "NL80211_CMD_SET_WIPHY_NETNS",
+ NL80211_CMD_GET_SURVEY: "NL80211_CMD_GET_SURVEY",
+ NL80211_CMD_NEW_SURVEY_RESULTS: "NL80211_CMD_NEW_SURVEY_RESULTS",
+ NL80211_CMD_SET_PMKSA: "NL80211_CMD_SET_PMKSA",
+ NL80211_CMD_DEL_PMKSA: "NL80211_CMD_DEL_PMKSA",
+ NL80211_CMD_FLUSH_PMKSA: "NL80211_CMD_FLUSH_PMKSA",
+ NL80211_CMD_REMAIN_ON_CHANNEL: "NL80211_CMD_REMAIN_ON_CHANNEL",
+ NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: "NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL",
+ NL80211_CMD_SET_TX_BITRATE_MASK: "NL80211_CMD_SET_TX_BITRATE_MASK",
+ NL80211_CMD_REGISTER_FRAME: "NL80211_CMD_REGISTER_FRAME",
+ NL80211_CMD_FRAME: "NL80211_CMD_FRAME",
+ NL80211_CMD_FRAME_TX_STATUS: "NL80211_CMD_FRAME_TX_STATUS",
+ NL80211_CMD_SET_POWER_SAVE: "NL80211_CMD_SET_POWER_SAVE",
+ NL80211_CMD_GET_POWER_SAVE: "NL80211_CMD_GET_POWER_SAVE",
+ NL80211_CMD_SET_CQM: "NL80211_CMD_SET_CQM",
+ NL80211_CMD_NOTIFY_CQM: "NL80211_CMD_NOTIFY_CQM",
+ NL80211_CMD_SET_CHANNEL: "NL80211_CMD_SET_CHANNEL",
+ NL80211_CMD_SET_WDS_PEER: "NL80211_CMD_SET_WDS_PEER",
+ NL80211_CMD_FRAME_WAIT_CANCEL: "NL80211_CMD_FRAME_WAIT_CANCEL",
+ NL80211_CMD_JOIN_MESH: "NL80211_CMD_JOIN_MESH",
+ NL80211_CMD_LEAVE_MESH: "NL80211_CMD_LEAVE_MESH",
+ NL80211_CMD_UNPROT_DEAUTHENTICATE: "NL80211_CMD_UNPROT_DEAUTHENTICATE",
+ NL80211_CMD_UNPROT_DISASSOCIATE: "NL80211_CMD_UNPROT_DISASSOCIATE",
+ NL80211_CMD_NEW_PEER_CANDIDATE: "NL80211_CMD_NEW_PEER_CANDIDATE",
+ NL80211_CMD_GET_WOWLAN: "NL80211_CMD_GET_WOWLAN",
+ NL80211_CMD_SET_WOWLAN: "NL80211_CMD_SET_WOWLAN",
+ NL80211_CMD_START_SCHED_SCAN: "NL80211_CMD_START_SCHED_SCAN",
+ NL80211_CMD_STOP_SCHED_SCAN: "NL80211_CMD_STOP_SCHED_SCAN",
+ NL80211_CMD_SCHED_SCAN_RESULTS: "NL80211_CMD_SCHED_SCAN_RESULTS",
+ NL80211_CMD_SCHED_SCAN_STOPPED: "NL80211_CMD_SCHED_SCAN_STOPPED",
+ NL80211_CMD_SET_REKEY_OFFLOAD: "NL80211_CMD_SET_REKEY_OFFLOAD",
+ NL80211_CMD_PMKSA_CANDIDATE: "NL80211_CMD_PMKSA_CANDIDATE",
+ NL80211_CMD_TDLS_OPER: "NL80211_CMD_TDLS_OPER",
+ NL80211_CMD_TDLS_MGMT: "NL80211_CMD_TDLS_MGMT",
+ NL80211_CMD_UNEXPECTED_FRAME: "NL80211_CMD_UNEXPECTED_FRAME",
+ NL80211_CMD_PROBE_CLIENT: "NL80211_CMD_PROBE_CLIENT",
+ NL80211_CMD_REGISTER_BEACONS: "NL80211_CMD_REGISTER_BEACONS",
+ NL80211_CMD_UNEXPECTED_4ADDR_FRAME: "NL80211_CMD_UNEXPECTED_4ADDR_FRAME",
+ NL80211_CMD_SET_NOACK_MAP: "NL80211_CMD_SET_NOACK_MAP",
+ NL80211_CMD_CH_SWITCH_NOTIFY: "NL80211_CMD_CH_SWITCH_NOTIFY",
+ NL80211_CMD_START_P2P_DEVICE: "NL80211_CMD_START_P2P_DEVICE",
+ NL80211_CMD_STOP_P2P_DEVICE: "NL80211_CMD_STOP_P2P_DEVICE",
+ NL80211_CMD_CONN_FAILED: "NL80211_CMD_CONN_FAILED",
+ NL80211_CMD_SET_MCAST_RATE: "NL80211_CMD_SET_MCAST_RATE",
+ NL80211_CMD_SET_MAC_ACL: "NL80211_CMD_SET_MAC_ACL",
+ NL80211_CMD_RADAR_DETECT: "NL80211_CMD_RADAR_DETECT",
+ NL80211_CMD_GET_PROTOCOL_FEATURES: "NL80211_CMD_GET_PROTOCOL_FEATURES",
+ NL80211_CMD_UPDATE_FT_IES: "NL80211_CMD_UPDATE_FT_IES",
+ NL80211_CMD_FT_EVENT: "NL80211_CMD_FT_EVENT",
+ NL80211_CMD_CRIT_PROTOCOL_START: "NL80211_CMD_CRIT_PROTOCOL_START",
+ NL80211_CMD_CRIT_PROTOCOL_STOP: "NL80211_CMD_CRIT_PROTOCOL_STOP",
+ __NL80211_CMD_AFTER_LAST: "__NL80211_CMD_AFTER_LAST",
+}
+nl80211_attrs2str = {
+ NL80211_ATTR_UNSPEC: "NL80211_ATTR_UNSPEC",
+ NL80211_ATTR_WIPHY: "NL80211_ATTR_WIPHY",
+ NL80211_ATTR_WIPHY_NAME: "NL80211_ATTR_WIPHY_NAME",
+ NL80211_ATTR_IFINDEX: "NL80211_ATTR_IFINDEX",
+ NL80211_ATTR_IFNAME: "NL80211_ATTR_IFNAME",
+ NL80211_ATTR_IFTYPE: "NL80211_ATTR_IFTYPE",
+ NL80211_ATTR_MAC: "NL80211_ATTR_MAC",
+ NL80211_ATTR_KEY_DATA: "NL80211_ATTR_KEY_DATA",
+ NL80211_ATTR_KEY_IDX: "NL80211_ATTR_KEY_IDX",
+ NL80211_ATTR_KEY_CIPHER: "NL80211_ATTR_KEY_CIPHER",
+ NL80211_ATTR_KEY_SEQ: "NL80211_ATTR_KEY_SEQ",
+ NL80211_ATTR_KEY_DEFAULT: "NL80211_ATTR_KEY_DEFAULT",
+ NL80211_ATTR_BEACON_INTERVAL: "NL80211_ATTR_BEACON_INTERVAL",
+ NL80211_ATTR_DTIM_PERIOD: "NL80211_ATTR_DTIM_PERIOD",
+ NL80211_ATTR_BEACON_HEAD: "NL80211_ATTR_BEACON_HEAD",
+ NL80211_ATTR_BEACON_TAIL: "NL80211_ATTR_BEACON_TAIL",
+ NL80211_ATTR_STA_AID: "NL80211_ATTR_STA_AID",
+ NL80211_ATTR_STA_FLAGS: "NL80211_ATTR_STA_FLAGS",
+ NL80211_ATTR_STA_LISTEN_INTERVAL: "NL80211_ATTR_STA_LISTEN_INTERVAL",
+ NL80211_ATTR_STA_SUPPORTED_RATES: "NL80211_ATTR_STA_SUPPORTED_RATES",
+ NL80211_ATTR_STA_VLAN: "NL80211_ATTR_STA_VLAN",
+ NL80211_ATTR_STA_INFO: "NL80211_ATTR_STA_INFO",
+ NL80211_ATTR_WIPHY_BANDS: "NL80211_ATTR_WIPHY_BANDS",
+ NL80211_ATTR_MNTR_FLAGS: "NL80211_ATTR_MNTR_FLAGS",
+ NL80211_ATTR_MESH_ID: "NL80211_ATTR_MESH_ID",
+ NL80211_ATTR_STA_PLINK_ACTION: "NL80211_ATTR_STA_PLINK_ACTION",
+ NL80211_ATTR_MPATH_NEXT_HOP: "NL80211_ATTR_MPATH_NEXT_HOP",
+ NL80211_ATTR_MPATH_INFO: "NL80211_ATTR_MPATH_INFO",
+ NL80211_ATTR_BSS_CTS_PROT: "NL80211_ATTR_BSS_CTS_PROT",
+ NL80211_ATTR_BSS_SHORT_PREAMBLE: "NL80211_ATTR_BSS_SHORT_PREAMBLE",
+ NL80211_ATTR_BSS_SHORT_SLOT_TIME: "NL80211_ATTR_BSS_SHORT_SLOT_TIME",
+ NL80211_ATTR_HT_CAPABILITY: "NL80211_ATTR_HT_CAPABILITY",
+ NL80211_ATTR_SUPPORTED_IFTYPES: "NL80211_ATTR_SUPPORTED_IFTYPES",
+ NL80211_ATTR_REG_ALPHA2: "NL80211_ATTR_REG_ALPHA2",
+ NL80211_ATTR_REG_RULES: "NL80211_ATTR_REG_RULES",
+ NL80211_ATTR_MESH_CONFIG: "NL80211_ATTR_MESH_CONFIG",
+ NL80211_ATTR_BSS_BASIC_RATES: "NL80211_ATTR_BSS_BASIC_RATES",
+ NL80211_ATTR_WIPHY_TXQ_PARAMS: "NL80211_ATTR_WIPHY_TXQ_PARAMS",
+ NL80211_ATTR_WIPHY_FREQ: "NL80211_ATTR_WIPHY_FREQ",
+ NL80211_ATTR_WIPHY_CHANNEL_TYPE: "NL80211_ATTR_WIPHY_CHANNEL_TYPE",
+ NL80211_ATTR_KEY_DEFAULT_MGMT: "NL80211_ATTR_KEY_DEFAULT_MGMT",
+ NL80211_ATTR_MGMT_SUBTYPE: "NL80211_ATTR_MGMT_SUBTYPE",
+ NL80211_ATTR_IE: "NL80211_ATTR_IE",
+ NL80211_ATTR_MAX_NUM_SCAN_SSIDS: "NL80211_ATTR_MAX_NUM_SCAN_SSIDS",
+ NL80211_ATTR_SCAN_FREQUENCIES: "NL80211_ATTR_SCAN_FREQUENCIES",
+ NL80211_ATTR_SCAN_SSIDS: "NL80211_ATTR_SCAN_SSIDS",
+ NL80211_ATTR_GENERATION: "NL80211_ATTR_GENERATION",
+ NL80211_ATTR_BSS: "NL80211_ATTR_BSS",
+ NL80211_ATTR_REG_INITIATOR: "NL80211_ATTR_REG_INITIATOR",
+ NL80211_ATTR_REG_TYPE: "NL80211_ATTR_REG_TYPE",
+ NL80211_ATTR_SUPPORTED_COMMANDS: "NL80211_ATTR_SUPPORTED_COMMANDS",
+ NL80211_ATTR_FRAME: "NL80211_ATTR_FRAME",
+ NL80211_ATTR_SSID: "NL80211_ATTR_SSID",
+ NL80211_ATTR_AUTH_TYPE: "NL80211_ATTR_AUTH_TYPE",
+ NL80211_ATTR_REASON_CODE: "NL80211_ATTR_REASON_CODE",
+ NL80211_ATTR_KEY_TYPE: "NL80211_ATTR_KEY_TYPE",
+ NL80211_ATTR_MAX_SCAN_IE_LEN: "NL80211_ATTR_MAX_SCAN_IE_LEN",
+ NL80211_ATTR_CIPHER_SUITES: "NL80211_ATTR_CIPHER_SUITES",
+ NL80211_ATTR_FREQ_BEFORE: "NL80211_ATTR_FREQ_BEFORE",
+ NL80211_ATTR_FREQ_AFTER: "NL80211_ATTR_FREQ_AFTER",
+ NL80211_ATTR_FREQ_FIXED: "NL80211_ATTR_FREQ_FIXED",
+ NL80211_ATTR_WIPHY_RETRY_SHORT: "NL80211_ATTR_WIPHY_RETRY_SHORT",
+ NL80211_ATTR_WIPHY_RETRY_LONG: "NL80211_ATTR_WIPHY_RETRY_LONG",
+ NL80211_ATTR_WIPHY_FRAG_THRESHOLD: "NL80211_ATTR_WIPHY_FRAG_THRESHOLD",
+ NL80211_ATTR_WIPHY_RTS_THRESHOLD: "NL80211_ATTR_WIPHY_RTS_THRESHOLD",
+ NL80211_ATTR_TIMED_OUT: "NL80211_ATTR_TIMED_OUT",
+ NL80211_ATTR_USE_MFP: "NL80211_ATTR_USE_MFP",
+ NL80211_ATTR_STA_FLAGS2: "NL80211_ATTR_STA_FLAGS2",
+ NL80211_ATTR_CONTROL_PORT: "NL80211_ATTR_CONTROL_PORT",
+ NL80211_ATTR_TESTDATA: "NL80211_ATTR_TESTDATA",
+ NL80211_ATTR_PRIVACY: "NL80211_ATTR_PRIVACY",
+ NL80211_ATTR_DISCONNECTED_BY_AP: "NL80211_ATTR_DISCONNECTED_BY_AP",
+ NL80211_ATTR_STATUS_CODE: "NL80211_ATTR_STATUS_CODE",
+ NL80211_ATTR_CIPHER_SUITES_PAIRWISE: "NL80211_ATTR_CIPHER_SUITES_PAIRWISE",
+ NL80211_ATTR_CIPHER_SUITE_GROUP: "NL80211_ATTR_CIPHER_SUITE_GROUP",
+ NL80211_ATTR_WPA_VERSIONS: "NL80211_ATTR_WPA_VERSIONS",
+ NL80211_ATTR_AKM_SUITES: "NL80211_ATTR_AKM_SUITES",
+ NL80211_ATTR_REQ_IE: "NL80211_ATTR_REQ_IE",
+ NL80211_ATTR_RESP_IE: "NL80211_ATTR_RESP_IE",
+ NL80211_ATTR_PREV_BSSID: "NL80211_ATTR_PREV_BSSID",
+ NL80211_ATTR_KEY: "NL80211_ATTR_KEY",
+ NL80211_ATTR_KEYS: "NL80211_ATTR_KEYS",
+ NL80211_ATTR_PID: "NL80211_ATTR_PID",
+ NL80211_ATTR_4ADDR: "NL80211_ATTR_4ADDR",
+ NL80211_ATTR_SURVEY_INFO: "NL80211_ATTR_SURVEY_INFO",
+ NL80211_ATTR_PMKID: "NL80211_ATTR_PMKID",
+ NL80211_ATTR_MAX_NUM_PMKIDS: "NL80211_ATTR_MAX_NUM_PMKIDS",
+ NL80211_ATTR_DURATION: "NL80211_ATTR_DURATION",
+ NL80211_ATTR_COOKIE: "NL80211_ATTR_COOKIE",
+ NL80211_ATTR_WIPHY_COVERAGE_CLASS: "NL80211_ATTR_WIPHY_COVERAGE_CLASS",
+ NL80211_ATTR_TX_RATES: "NL80211_ATTR_TX_RATES",
+ NL80211_ATTR_FRAME_MATCH: "NL80211_ATTR_FRAME_MATCH",
+ NL80211_ATTR_ACK: "NL80211_ATTR_ACK",
+ NL80211_ATTR_PS_STATE: "NL80211_ATTR_PS_STATE",
+ NL80211_ATTR_CQM: "NL80211_ATTR_CQM",
+ NL80211_ATTR_LOCAL_STATE_CHANGE: "NL80211_ATTR_LOCAL_STATE_CHANGE",
+ NL80211_ATTR_AP_ISOLATE: "NL80211_ATTR_AP_ISOLATE",
+ NL80211_ATTR_WIPHY_TX_POWER_SETTING: "NL80211_ATTR_WIPHY_TX_POWER_SETTING",
+ NL80211_ATTR_WIPHY_TX_POWER_LEVEL: "NL80211_ATTR_WIPHY_TX_POWER_LEVEL",
+ NL80211_ATTR_TX_FRAME_TYPES: "NL80211_ATTR_TX_FRAME_TYPES",
+ NL80211_ATTR_RX_FRAME_TYPES: "NL80211_ATTR_RX_FRAME_TYPES",
+ NL80211_ATTR_FRAME_TYPE: "NL80211_ATTR_FRAME_TYPE",
+ NL80211_ATTR_CONTROL_PORT_ETHERTYPE: "NL80211_ATTR_CONTROL_PORT_ETHERTYPE",
+ NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT: "NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT",
+ NL80211_ATTR_SUPPORT_IBSS_RSN: "NL80211_ATTR_SUPPORT_IBSS_RSN",
+ NL80211_ATTR_WIPHY_ANTENNA_TX: "NL80211_ATTR_WIPHY_ANTENNA_TX",
+ NL80211_ATTR_WIPHY_ANTENNA_RX: "NL80211_ATTR_WIPHY_ANTENNA_RX",
+ NL80211_ATTR_MCAST_RATE: "NL80211_ATTR_MCAST_RATE",
+ NL80211_ATTR_OFFCHANNEL_TX_OK: "NL80211_ATTR_OFFCHANNEL_TX_OK",
+ NL80211_ATTR_BSS_HT_OPMODE: "NL80211_ATTR_BSS_HT_OPMODE",
+ NL80211_ATTR_KEY_DEFAULT_TYPES: "NL80211_ATTR_KEY_DEFAULT_TYPES",
+ NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION: "NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION",
+ NL80211_ATTR_MESH_SETUP: "NL80211_ATTR_MESH_SETUP",
+ NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX: "NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX",
+ NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX: "NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX",
+ NL80211_ATTR_SUPPORT_MESH_AUTH: "NL80211_ATTR_SUPPORT_MESH_AUTH",
+ NL80211_ATTR_STA_PLINK_STATE: "NL80211_ATTR_STA_PLINK_STATE",
+ NL80211_ATTR_WOWLAN_TRIGGERS: "NL80211_ATTR_WOWLAN_TRIGGERS",
+ NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED: "NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED",
+ NL80211_ATTR_SCHED_SCAN_INTERVAL: "NL80211_ATTR_SCHED_SCAN_INTERVAL",
+ NL80211_ATTR_INTERFACE_COMBINATIONS: "NL80211_ATTR_INTERFACE_COMBINATIONS",
+ NL80211_ATTR_SOFTWARE_IFTYPES: "NL80211_ATTR_SOFTWARE_IFTYPES",
+ NL80211_ATTR_REKEY_DATA: "NL80211_ATTR_REKEY_DATA",
+ NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS: "NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS",
+ NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN: "NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN",
+ NL80211_ATTR_SCAN_SUPP_RATES: "NL80211_ATTR_SCAN_SUPP_RATES",
+ NL80211_ATTR_HIDDEN_SSID: "NL80211_ATTR_HIDDEN_SSID",
+ NL80211_ATTR_IE_PROBE_RESP: "NL80211_ATTR_IE_PROBE_RESP",
+ NL80211_ATTR_IE_ASSOC_RESP: "NL80211_ATTR_IE_ASSOC_RESP",
+ NL80211_ATTR_STA_WME: "NL80211_ATTR_STA_WME",
+ NL80211_ATTR_SUPPORT_AP_UAPSD: "NL80211_ATTR_SUPPORT_AP_UAPSD",
+ NL80211_ATTR_ROAM_SUPPORT: "NL80211_ATTR_ROAM_SUPPORT",
+ NL80211_ATTR_SCHED_SCAN_MATCH: "NL80211_ATTR_SCHED_SCAN_MATCH",
+ NL80211_ATTR_MAX_MATCH_SETS: "NL80211_ATTR_MAX_MATCH_SETS",
+ NL80211_ATTR_PMKSA_CANDIDATE: "NL80211_ATTR_PMKSA_CANDIDATE",
+ NL80211_ATTR_TX_NO_CCK_RATE: "NL80211_ATTR_TX_NO_CCK_RATE",
+ NL80211_ATTR_TDLS_ACTION: "NL80211_ATTR_TDLS_ACTION",
+ NL80211_ATTR_TDLS_DIALOG_TOKEN: "NL80211_ATTR_TDLS_DIALOG_TOKEN",
+ NL80211_ATTR_TDLS_OPERATION: "NL80211_ATTR_TDLS_OPERATION",
+ NL80211_ATTR_TDLS_SUPPORT: "NL80211_ATTR_TDLS_SUPPORT",
+ NL80211_ATTR_TDLS_EXTERNAL_SETUP: "NL80211_ATTR_TDLS_EXTERNAL_SETUP",
+ NL80211_ATTR_DEVICE_AP_SME: "NL80211_ATTR_DEVICE_AP_SME",
+ NL80211_ATTR_DONT_WAIT_FOR_ACK: "NL80211_ATTR_DONT_WAIT_FOR_ACK",
+ NL80211_ATTR_FEATURE_FLAGS: "NL80211_ATTR_FEATURE_FLAGS",
+ NL80211_ATTR_PROBE_RESP_OFFLOAD: "NL80211_ATTR_PROBE_RESP_OFFLOAD",
+ NL80211_ATTR_PROBE_RESP: "NL80211_ATTR_PROBE_RESP",
+ NL80211_ATTR_DFS_REGION: "NL80211_ATTR_DFS_REGION",
+ NL80211_ATTR_DISABLE_HT: "NL80211_ATTR_DISABLE_HT",
+ NL80211_ATTR_HT_CAPABILITY_MASK: "NL80211_ATTR_HT_CAPABILITY_MASK",
+ NL80211_ATTR_NOACK_MAP: "NL80211_ATTR_NOACK_MAP",
+ NL80211_ATTR_INACTIVITY_TIMEOUT: "NL80211_ATTR_INACTIVITY_TIMEOUT",
+ NL80211_ATTR_RX_SIGNAL_DBM: "NL80211_ATTR_RX_SIGNAL_DBM",
+ NL80211_ATTR_BG_SCAN_PERIOD: "NL80211_ATTR_BG_SCAN_PERIOD",
+ NL80211_ATTR_WDEV: "NL80211_ATTR_WDEV",
+ NL80211_ATTR_USER_REG_HINT_TYPE: "NL80211_ATTR_USER_REG_HINT_TYPE",
+ NL80211_ATTR_CONN_FAILED_REASON: "NL80211_ATTR_CONN_FAILED_REASON",
+ NL80211_ATTR_SAE_DATA: "NL80211_ATTR_SAE_DATA",
+ NL80211_ATTR_VHT_CAPABILITY: "NL80211_ATTR_VHT_CAPABILITY",
+ NL80211_ATTR_SCAN_FLAGS: "NL80211_ATTR_SCAN_FLAGS",
+ NL80211_ATTR_CHANNEL_WIDTH: "NL80211_ATTR_CHANNEL_WIDTH",
+ NL80211_ATTR_CENTER_FREQ1: "NL80211_ATTR_CENTER_FREQ1",
+ NL80211_ATTR_CENTER_FREQ2: "NL80211_ATTR_CENTER_FREQ2",
+ NL80211_ATTR_P2P_CTWINDOW: "NL80211_ATTR_P2P_CTWINDOW",
+ NL80211_ATTR_P2P_OPPPS: "NL80211_ATTR_P2P_OPPPS",
+ NL80211_ATTR_LOCAL_MESH_POWER_MODE: "NL80211_ATTR_LOCAL_MESH_POWER_MODE",
+ NL80211_ATTR_ACL_POLICY: "NL80211_ATTR_ACL_POLICY",
+ NL80211_ATTR_MAC_ADDRS: "NL80211_ATTR_MAC_ADDRS",
+ NL80211_ATTR_MAC_ACL_MAX: "NL80211_ATTR_MAC_ACL_MAX",
+ NL80211_ATTR_RADAR_EVENT: "NL80211_ATTR_RADAR_EVENT",
+ NL80211_ATTR_EXT_CAPA: "NL80211_ATTR_EXT_CAPA",
+ NL80211_ATTR_EXT_CAPA_MASK: "NL80211_ATTR_EXT_CAPA_MASK",
+ NL80211_ATTR_STA_CAPABILITY: "NL80211_ATTR_STA_CAPABILITY",
+ NL80211_ATTR_STA_EXT_CAPABILITY: "NL80211_ATTR_STA_EXT_CAPABILITY",
+ NL80211_ATTR_PROTOCOL_FEATURES: "NL80211_ATTR_PROTOCOL_FEATURES",
+ NL80211_ATTR_SPLIT_WIPHY_DUMP: "NL80211_ATTR_SPLIT_WIPHY_DUMP",
+ NL80211_ATTR_DISABLE_VHT: "NL80211_ATTR_DISABLE_VHT",
+ NL80211_ATTR_VHT_CAPABILITY_MASK: "NL80211_ATTR_VHT_CAPABILITY_MASK",
+ NL80211_ATTR_MDID: "NL80211_ATTR_MDID",
+ NL80211_ATTR_IE_RIC: "NL80211_ATTR_IE_RIC",
+ NL80211_ATTR_CRIT_PROT_ID: "NL80211_ATTR_CRIT_PROT_ID",
+ NL80211_ATTR_MAX_CRIT_PROT_DURATION: "NL80211_ATTR_MAX_CRIT_PROT_DURATION",
+ NL80211_ATTR_PEER_AID: "NL80211_ATTR_PEER_AID",
+ __NL80211_ATTR_AFTER_LAST: "__NL80211_ATTR_AFTER_LAST",
+}
+nl80211_iftype2str = {
+ NL80211_IFTYPE_UNSPECIFIED: "NL80211_IFTYPE_UNSPECIFIED",
+ NL80211_IFTYPE_ADHOC: "NL80211_IFTYPE_ADHOC",
+ NL80211_IFTYPE_STATION: "NL80211_IFTYPE_STATION",
+ NL80211_IFTYPE_AP: "NL80211_IFTYPE_AP",
+ NL80211_IFTYPE_AP_VLAN: "NL80211_IFTYPE_AP_VLAN",
+ NL80211_IFTYPE_WDS: "NL80211_IFTYPE_WDS",
+ NL80211_IFTYPE_MONITOR: "NL80211_IFTYPE_MONITOR",
+ NL80211_IFTYPE_MESH_POINT: "NL80211_IFTYPE_MESH_POINT",
+ NL80211_IFTYPE_P2P_CLIENT: "NL80211_IFTYPE_P2P_CLIENT",
+ NL80211_IFTYPE_P2P_GO: "NL80211_IFTYPE_P2P_GO",
+ NL80211_IFTYPE_P2P_DEVICE: "NL80211_IFTYPE_P2P_DEVICE",
+ NUM_NL80211_IFTYPES: "NUM_NL80211_IFTYPES",
+}
+nl80211_sta_flags2str = {
+ __NL80211_STA_FLAG_INVALID: "__NL80211_STA_FLAG_INVALID",
+ NL80211_STA_FLAG_AUTHORIZED: "NL80211_STA_FLAG_AUTHORIZED",
+ NL80211_STA_FLAG_SHORT_PREAMBLE: "NL80211_STA_FLAG_SHORT_PREAMBLE",
+ NL80211_STA_FLAG_WME: "NL80211_STA_FLAG_WME",
+ NL80211_STA_FLAG_MFP: "NL80211_STA_FLAG_MFP",
+ NL80211_STA_FLAG_AUTHENTICATED: "NL80211_STA_FLAG_AUTHENTICATED",
+ NL80211_STA_FLAG_TDLS_PEER: "NL80211_STA_FLAG_TDLS_PEER",
+ NL80211_STA_FLAG_ASSOCIATED: "NL80211_STA_FLAG_ASSOCIATED",
+ __NL80211_STA_FLAG_AFTER_LAST: "__NL80211_STA_FLAG_AFTER_LAST",
+}
+nl80211_rate_info2str = {
+ __NL80211_RATE_INFO_INVALID: "__NL80211_RATE_INFO_INVALID",
+ NL80211_RATE_INFO_BITRATE: "NL80211_RATE_INFO_BITRATE",
+ NL80211_RATE_INFO_MCS: "NL80211_RATE_INFO_MCS",
+ NL80211_RATE_INFO_40_MHZ_WIDTH: "NL80211_RATE_INFO_40_MHZ_WIDTH",
+ NL80211_RATE_INFO_SHORT_GI: "NL80211_RATE_INFO_SHORT_GI",
+ NL80211_RATE_INFO_BITRATE32: "NL80211_RATE_INFO_BITRATE32",
+ NL80211_RATE_INFO_VHT_MCS: "NL80211_RATE_INFO_VHT_MCS",
+ NL80211_RATE_INFO_VHT_NSS: "NL80211_RATE_INFO_VHT_NSS",
+ NL80211_RATE_INFO_80_MHZ_WIDTH: "NL80211_RATE_INFO_80_MHZ_WIDTH",
+ NL80211_RATE_INFO_80P80_MHZ_WIDTH: "NL80211_RATE_INFO_80P80_MHZ_WIDTH",
+ NL80211_RATE_INFO_160_MHZ_WIDTH: "NL80211_RATE_INFO_160_MHZ_WIDTH",
+ __NL80211_RATE_INFO_AFTER_LAST: "__NL80211_RATE_INFO_AFTER_LAST",
+}
+nl80211_sta_bss_param2str = {
+ __NL80211_STA_BSS_PARAM_INVALID: "__NL80211_STA_BSS_PARAM_INVALID",
+ NL80211_STA_BSS_PARAM_CTS_PROT: "NL80211_STA_BSS_PARAM_CTS_PROT",
+ NL80211_STA_BSS_PARAM_SHORT_PREAMBLE: "NL80211_STA_BSS_PARAM_SHORT_PREAMBLE",
+ NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME: "NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME",
+ NL80211_STA_BSS_PARAM_DTIM_PERIOD: "NL80211_STA_BSS_PARAM_DTIM_PERIOD",
+ NL80211_STA_BSS_PARAM_BEACON_INTERVAL: "NL80211_STA_BSS_PARAM_BEACON_INTERVAL",
+ __NL80211_STA_BSS_PARAM_AFTER_LAST: "__NL80211_STA_BSS_PARAM_AFTER_LAST",
+}
+nl80211_sta_info2str = {
+ __NL80211_STA_INFO_INVALID: "__NL80211_STA_INFO_INVALID",
+ NL80211_STA_INFO_INACTIVE_TIME: "NL80211_STA_INFO_INACTIVE_TIME",
+ NL80211_STA_INFO_RX_BYTES: "NL80211_STA_INFO_RX_BYTES",
+ NL80211_STA_INFO_TX_BYTES: "NL80211_STA_INFO_TX_BYTES",
+ NL80211_STA_INFO_LLID: "NL80211_STA_INFO_LLID",
+ NL80211_STA_INFO_PLID: "NL80211_STA_INFO_PLID",
+ NL80211_STA_INFO_PLINK_STATE: "NL80211_STA_INFO_PLINK_STATE",
+ NL80211_STA_INFO_SIGNAL: "NL80211_STA_INFO_SIGNAL",
+ NL80211_STA_INFO_TX_BITRATE: "NL80211_STA_INFO_TX_BITRATE",
+ NL80211_STA_INFO_RX_PACKETS: "NL80211_STA_INFO_RX_PACKETS",
+ NL80211_STA_INFO_TX_PACKETS: "NL80211_STA_INFO_TX_PACKETS",
+ NL80211_STA_INFO_TX_RETRIES: "NL80211_STA_INFO_TX_RETRIES",
+ NL80211_STA_INFO_TX_FAILED: "NL80211_STA_INFO_TX_FAILED",
+ NL80211_STA_INFO_SIGNAL_AVG: "NL80211_STA_INFO_SIGNAL_AVG",
+ NL80211_STA_INFO_RX_BITRATE: "NL80211_STA_INFO_RX_BITRATE",
+ NL80211_STA_INFO_BSS_PARAM: "NL80211_STA_INFO_BSS_PARAM",
+ NL80211_STA_INFO_CONNECTED_TIME: "NL80211_STA_INFO_CONNECTED_TIME",
+ NL80211_STA_INFO_STA_FLAGS: "NL80211_STA_INFO_STA_FLAGS",
+ NL80211_STA_INFO_BEACON_LOSS: "NL80211_STA_INFO_BEACON_LOSS",
+ NL80211_STA_INFO_T_OFFSET: "NL80211_STA_INFO_T_OFFSET",
+ NL80211_STA_INFO_LOCAL_PM: "NL80211_STA_INFO_LOCAL_PM",
+ NL80211_STA_INFO_PEER_PM: "NL80211_STA_INFO_PEER_PM",
+ NL80211_STA_INFO_NONPEER_PM: "NL80211_STA_INFO_NONPEER_PM",
+ NL80211_STA_INFO_RX_BYTES64: "NL80211_STA_INFO_RX_BYTES64",
+ NL80211_STA_INFO_TX_BYTES64: "NL80211_STA_INFO_TX_BYTES64",
+ NL80211_STA_INFO_CHAIN_SIGNAL: "NL80211_STA_INFO_CHAIN_SIGNAL",
+ NL80211_STA_INFO_CHAIN_SIGNAL_AVG: "NL80211_STA_INFO_CHAIN_SIGNAL_AVG",
+ __NL80211_STA_INFO_AFTER_LAST: "__NL80211_STA_INFO_AFTER_LAST",
+}
+nl80211_mpath_flags2str = {
+ NL80211_MPATH_FLAG_ACTIVE: "NL80211_MPATH_FLAG_ACTIVE",
+ NL80211_MPATH_FLAG_RESOLVING: "NL80211_MPATH_FLAG_RESOLVING",
+ NL80211_MPATH_FLAG_SN_VALID: "NL80211_MPATH_FLAG_SN_VALID",
+ NL80211_MPATH_FLAG_FIXED: "NL80211_MPATH_FLAG_FIXED",
+ NL80211_MPATH_FLAG_RESOLVED: "NL80211_MPATH_FLAG_RESOLVED",
+}
+nl80211_mpath_info2str = {
+ __NL80211_MPATH_INFO_INVALID: "__NL80211_MPATH_INFO_INVALID",
+ NL80211_MPATH_INFO_FRAME_QLEN: "NL80211_MPATH_INFO_FRAME_QLEN",
+ NL80211_MPATH_INFO_SN: "NL80211_MPATH_INFO_SN",
+ NL80211_MPATH_INFO_METRIC: "NL80211_MPATH_INFO_METRIC",
+ NL80211_MPATH_INFO_EXPTIME: "NL80211_MPATH_INFO_EXPTIME",
+ NL80211_MPATH_INFO_FLAGS: "NL80211_MPATH_INFO_FLAGS",
+ NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: "NL80211_MPATH_INFO_DISCOVERY_TIMEOUT",
+ NL80211_MPATH_INFO_DISCOVERY_RETRIES: "NL80211_MPATH_INFO_DISCOVERY_RETRIES",
+ __NL80211_MPATH_INFO_AFTER_LAST: "__NL80211_MPATH_INFO_AFTER_LAST",
+}
+nl80211_band_attr2str = {
+ __NL80211_BAND_ATTR_INVALID: "__NL80211_BAND_ATTR_INVALID",
+ NL80211_BAND_ATTR_FREQS: "NL80211_BAND_ATTR_FREQS",
+ NL80211_BAND_ATTR_RATES: "NL80211_BAND_ATTR_RATES",
+ NL80211_BAND_ATTR_HT_MCS_SET: "NL80211_BAND_ATTR_HT_MCS_SET",
+ NL80211_BAND_ATTR_HT_CAPA: "NL80211_BAND_ATTR_HT_CAPA",
+ NL80211_BAND_ATTR_HT_AMPDU_FACTOR: "NL80211_BAND_ATTR_HT_AMPDU_FACTOR",
+ NL80211_BAND_ATTR_HT_AMPDU_DENSITY: "NL80211_BAND_ATTR_HT_AMPDU_DENSITY",
+ NL80211_BAND_ATTR_VHT_MCS_SET: "NL80211_BAND_ATTR_VHT_MCS_SET",
+ NL80211_BAND_ATTR_VHT_CAPA: "NL80211_BAND_ATTR_VHT_CAPA",
+ __NL80211_BAND_ATTR_AFTER_LAST: "__NL80211_BAND_ATTR_AFTER_LAST",
+}
+nl80211_frequency_attr2str = {
+ __NL80211_FREQUENCY_ATTR_INVALID: "__NL80211_FREQUENCY_ATTR_INVALID",
+ NL80211_FREQUENCY_ATTR_FREQ: "NL80211_FREQUENCY_ATTR_FREQ",
+ NL80211_FREQUENCY_ATTR_DISABLED: "NL80211_FREQUENCY_ATTR_DISABLED",
+ NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: "NL80211_FREQUENCY_ATTR_PASSIVE_SCAN",
+ NL80211_FREQUENCY_ATTR_NO_IBSS: "NL80211_FREQUENCY_ATTR_NO_IBSS",
+ NL80211_FREQUENCY_ATTR_RADAR: "NL80211_FREQUENCY_ATTR_RADAR",
+ NL80211_FREQUENCY_ATTR_MAX_TX_POWER: "NL80211_FREQUENCY_ATTR_MAX_TX_POWER",
+ NL80211_FREQUENCY_ATTR_DFS_STATE: "NL80211_FREQUENCY_ATTR_DFS_STATE",
+ NL80211_FREQUENCY_ATTR_DFS_TIME: "NL80211_FREQUENCY_ATTR_DFS_TIME",
+ NL80211_FREQUENCY_ATTR_NO_HT40_MINUS: "NL80211_FREQUENCY_ATTR_NO_HT40_MINUS",
+ NL80211_FREQUENCY_ATTR_NO_HT40_PLUS: "NL80211_FREQUENCY_ATTR_NO_HT40_PLUS",
+ NL80211_FREQUENCY_ATTR_NO_80MHZ: "NL80211_FREQUENCY_ATTR_NO_80MHZ",
+ NL80211_FREQUENCY_ATTR_NO_160MHZ: "NL80211_FREQUENCY_ATTR_NO_160MHZ",
+ __NL80211_FREQUENCY_ATTR_AFTER_LAST: "__NL80211_FREQUENCY_ATTR_AFTER_LAST",
+}
+nl80211_bitrate_attr2str = {
+ __NL80211_BITRATE_ATTR_INVALID: "__NL80211_BITRATE_ATTR_INVALID",
+ NL80211_BITRATE_ATTR_RATE: "NL80211_BITRATE_ATTR_RATE",
+ NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: "NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE",
+ __NL80211_BITRATE_ATTR_AFTER_LAST: "__NL80211_BITRATE_ATTR_AFTER_LAST",
+}
+nl80211_reg_initiator2str = {
+ NL80211_REGDOM_SET_BY_CORE: "NL80211_REGDOM_SET_BY_CORE",
+ NL80211_REGDOM_SET_BY_USER: "NL80211_REGDOM_SET_BY_USER",
+ NL80211_REGDOM_SET_BY_DRIVER: "NL80211_REGDOM_SET_BY_DRIVER",
+ NL80211_REGDOM_SET_BY_COUNTRY_IE: "NL80211_REGDOM_SET_BY_COUNTRY_IE",
+}
+nl80211_reg_type2str = {
+ NL80211_REGDOM_TYPE_COUNTRY: "NL80211_REGDOM_TYPE_COUNTRY",
+ NL80211_REGDOM_TYPE_WORLD: "NL80211_REGDOM_TYPE_WORLD",
+ NL80211_REGDOM_TYPE_CUSTOM_WORLD: "NL80211_REGDOM_TYPE_CUSTOM_WORLD",
+ NL80211_REGDOM_TYPE_INTERSECTION: "NL80211_REGDOM_TYPE_INTERSECTION",
+}
+nl80211_reg_rule_attr2str = {
+ __NL80211_REG_RULE_ATTR_INVALID: "__NL80211_REG_RULE_ATTR_INVALID",
+ NL80211_ATTR_REG_RULE_FLAGS: "NL80211_ATTR_REG_RULE_FLAGS",
+ NL80211_ATTR_FREQ_RANGE_START: "NL80211_ATTR_FREQ_RANGE_START",
+ NL80211_ATTR_FREQ_RANGE_END: "NL80211_ATTR_FREQ_RANGE_END",
+ NL80211_ATTR_FREQ_RANGE_MAX_BW: "NL80211_ATTR_FREQ_RANGE_MAX_BW",
+ NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: "NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN",
+ NL80211_ATTR_POWER_RULE_MAX_EIRP: "NL80211_ATTR_POWER_RULE_MAX_EIRP",
+ __NL80211_REG_RULE_ATTR_AFTER_LAST: "__NL80211_REG_RULE_ATTR_AFTER_LAST",
+}
+nl80211_sched_scan_match_attr2str = {
+ __NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: "__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID",
+ NL80211_SCHED_SCAN_MATCH_ATTR_SSID: "NL80211_SCHED_SCAN_MATCH_ATTR_SSID",
+ NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: "NL80211_SCHED_SCAN_MATCH_ATTR_RSSI",
+ __NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: "__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST",
+}
+nl80211_reg_rule_flags2str = {
+ NL80211_RRF_NO_OFDM: "NL80211_RRF_NO_OFDM",
+ NL80211_RRF_NO_CCK: "NL80211_RRF_NO_CCK",
+ NL80211_RRF_NO_INDOOR: "NL80211_RRF_NO_INDOOR",
+ NL80211_RRF_NO_OUTDOOR: "NL80211_RRF_NO_OUTDOOR",
+ NL80211_RRF_DFS: "NL80211_RRF_DFS",
+ NL80211_RRF_PTP_ONLY: "NL80211_RRF_PTP_ONLY",
+ NL80211_RRF_PTMP_ONLY: "NL80211_RRF_PTMP_ONLY",
+ NL80211_RRF_PASSIVE_SCAN: "NL80211_RRF_PASSIVE_SCAN",
+ NL80211_RRF_NO_IBSS: "NL80211_RRF_NO_IBSS",
+}
+nl80211_dfs_regions2str = {
+}
+nl80211_user_reg_hint_type2str = {
+}
+nl80211_survey_info2str = {
+ __NL80211_SURVEY_INFO_INVALID: "__NL80211_SURVEY_INFO_INVALID",
+ NL80211_SURVEY_INFO_FREQUENCY: "NL80211_SURVEY_INFO_FREQUENCY",
+ NL80211_SURVEY_INFO_NOISE: "NL80211_SURVEY_INFO_NOISE",
+ NL80211_SURVEY_INFO_IN_USE: "NL80211_SURVEY_INFO_IN_USE",
+ NL80211_SURVEY_INFO_CHANNEL_TIME: "NL80211_SURVEY_INFO_CHANNEL_TIME",
+ NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: "NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY",
+ NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: "NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY",
+ NL80211_SURVEY_INFO_CHANNEL_TIME_RX: "NL80211_SURVEY_INFO_CHANNEL_TIME_RX",
+ NL80211_SURVEY_INFO_CHANNEL_TIME_TX: "NL80211_SURVEY_INFO_CHANNEL_TIME_TX",
+ __NL80211_SURVEY_INFO_AFTER_LAST: "__NL80211_SURVEY_INFO_AFTER_LAST",
+}
+nl80211_mntr_flags2str = {
+ __NL80211_MNTR_FLAG_INVALID: "__NL80211_MNTR_FLAG_INVALID",
+ NL80211_MNTR_FLAG_FCSFAIL: "NL80211_MNTR_FLAG_FCSFAIL",
+ NL80211_MNTR_FLAG_PLCPFAIL: "NL80211_MNTR_FLAG_PLCPFAIL",
+ NL80211_MNTR_FLAG_CONTROL: "NL80211_MNTR_FLAG_CONTROL",
+ NL80211_MNTR_FLAG_OTHER_BSS: "NL80211_MNTR_FLAG_OTHER_BSS",
+ NL80211_MNTR_FLAG_COOK_FRAMES: "NL80211_MNTR_FLAG_COOK_FRAMES",
+ NL80211_MNTR_FLAG_ACTIVE: "NL80211_MNTR_FLAG_ACTIVE",
+ __NL80211_MNTR_FLAG_AFTER_LAST: "__NL80211_MNTR_FLAG_AFTER_LAST",
+}
+nl80211_mesh_power_mode2str = {
+ NL80211_MESH_POWER_UNKNOWN: "NL80211_MESH_POWER_UNKNOWN",
+ NL80211_MESH_POWER_ACTIVE: "NL80211_MESH_POWER_ACTIVE",
+ NL80211_MESH_POWER_LIGHT_SLEEP: "NL80211_MESH_POWER_LIGHT_SLEEP",
+ NL80211_MESH_POWER_DEEP_SLEEP: "NL80211_MESH_POWER_DEEP_SLEEP",
+ __NL80211_MESH_POWER_AFTER_LAST: "__NL80211_MESH_POWER_AFTER_LAST",
+}
+nl80211_meshconf_params2str = {
+ __NL80211_MESHCONF_INVALID: "__NL80211_MESHCONF_INVALID",
+ NL80211_MESHCONF_RETRY_TIMEOUT: "NL80211_MESHCONF_RETRY_TIMEOUT",
+ NL80211_MESHCONF_CONFIRM_TIMEOUT: "NL80211_MESHCONF_CONFIRM_TIMEOUT",
+ NL80211_MESHCONF_HOLDING_TIMEOUT: "NL80211_MESHCONF_HOLDING_TIMEOUT",
+ NL80211_MESHCONF_MAX_PEER_LINKS: "NL80211_MESHCONF_MAX_PEER_LINKS",
+ NL80211_MESHCONF_MAX_RETRIES: "NL80211_MESHCONF_MAX_RETRIES",
+ NL80211_MESHCONF_TTL: "NL80211_MESHCONF_TTL",
+ NL80211_MESHCONF_AUTO_OPEN_PLINKS: "NL80211_MESHCONF_AUTO_OPEN_PLINKS",
+ NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: "NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES",
+ NL80211_MESHCONF_PATH_REFRESH_TIME: "NL80211_MESHCONF_PATH_REFRESH_TIME",
+ NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: "NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT",
+ NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: "NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT",
+ NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: "NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL",
+ NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: "NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME",
+ NL80211_MESHCONF_HWMP_ROOTMODE: "NL80211_MESHCONF_HWMP_ROOTMODE",
+ NL80211_MESHCONF_ELEMENT_TTL: "NL80211_MESHCONF_ELEMENT_TTL",
+ NL80211_MESHCONF_HWMP_RANN_INTERVAL: "NL80211_MESHCONF_HWMP_RANN_INTERVAL",
+ NL80211_MESHCONF_GATE_ANNOUNCEMENTS: "NL80211_MESHCONF_GATE_ANNOUNCEMENTS",
+ NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL: "NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL",
+ NL80211_MESHCONF_FORWARDING: "NL80211_MESHCONF_FORWARDING",
+ NL80211_MESHCONF_RSSI_THRESHOLD: "NL80211_MESHCONF_RSSI_THRESHOLD",
+ NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR: "NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR",
+ NL80211_MESHCONF_HT_OPMODE: "NL80211_MESHCONF_HT_OPMODE",
+ NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT: "NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT",
+ NL80211_MESHCONF_HWMP_ROOT_INTERVAL: "NL80211_MESHCONF_HWMP_ROOT_INTERVAL",
+ NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL: "NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL",
+ NL80211_MESHCONF_POWER_MODE: "NL80211_MESHCONF_POWER_MODE",
+ NL80211_MESHCONF_AWAKE_WINDOW: "NL80211_MESHCONF_AWAKE_WINDOW",
+ NL80211_MESHCONF_PLINK_TIMEOUT: "NL80211_MESHCONF_PLINK_TIMEOUT",
+ __NL80211_MESHCONF_ATTR_AFTER_LAST: "__NL80211_MESHCONF_ATTR_AFTER_LAST",
+}
+nl80211_mesh_setup_params2str = {
+ __NL80211_MESH_SETUP_INVALID: "__NL80211_MESH_SETUP_INVALID",
+ NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: "NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL",
+ NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: "NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC",
+ NL80211_MESH_SETUP_IE: "NL80211_MESH_SETUP_IE",
+ NL80211_MESH_SETUP_USERSPACE_AUTH: "NL80211_MESH_SETUP_USERSPACE_AUTH",
+ NL80211_MESH_SETUP_USERSPACE_AMPE: "NL80211_MESH_SETUP_USERSPACE_AMPE",
+ NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC: "NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC",
+ NL80211_MESH_SETUP_USERSPACE_MPM: "NL80211_MESH_SETUP_USERSPACE_MPM",
+ NL80211_MESH_SETUP_AUTH_PROTOCOL: "NL80211_MESH_SETUP_AUTH_PROTOCOL",
+ __NL80211_MESH_SETUP_ATTR_AFTER_LAST: "__NL80211_MESH_SETUP_ATTR_AFTER_LAST",
+}
+nl80211_txq_attr2str = {
+ __NL80211_TXQ_ATTR_INVALID: "__NL80211_TXQ_ATTR_INVALID",
+ NL80211_TXQ_ATTR_AC: "NL80211_TXQ_ATTR_AC",
+ NL80211_TXQ_ATTR_TXOP: "NL80211_TXQ_ATTR_TXOP",
+ NL80211_TXQ_ATTR_CWMIN: "NL80211_TXQ_ATTR_CWMIN",
+ NL80211_TXQ_ATTR_CWMAX: "NL80211_TXQ_ATTR_CWMAX",
+ NL80211_TXQ_ATTR_AIFS: "NL80211_TXQ_ATTR_AIFS",
+ __NL80211_TXQ_ATTR_AFTER_LAST: "__NL80211_TXQ_ATTR_AFTER_LAST",
+}
+nl80211_ac2str = {
+ NL80211_AC_VO: "NL80211_AC_VO",
+ NL80211_AC_VI: "NL80211_AC_VI",
+ NL80211_AC_BE: "NL80211_AC_BE",
+ NL80211_AC_BK: "NL80211_AC_BK",
+ NL80211_NUM_ACS: "NL80211_NUM_ACS",
+}
+nl80211_channel_type2str = {
+ NL80211_CHAN_NO_HT: "NL80211_CHAN_NO_HT",
+ NL80211_CHAN_HT20: "NL80211_CHAN_HT20",
+ NL80211_CHAN_HT40MINUS: "NL80211_CHAN_HT40MINUS",
+ NL80211_CHAN_HT40PLUS: "NL80211_CHAN_HT40PLUS",
+}
+nl80211_chan_width2str = {
+ NL80211_CHAN_WIDTH_20_NOHT: "NL80211_CHAN_WIDTH_20_NOHT",
+ NL80211_CHAN_WIDTH_20: "NL80211_CHAN_WIDTH_20",
+ NL80211_CHAN_WIDTH_40: "NL80211_CHAN_WIDTH_40",
+ NL80211_CHAN_WIDTH_80: "NL80211_CHAN_WIDTH_80",
+ NL80211_CHAN_WIDTH_80P80: "NL80211_CHAN_WIDTH_80P80",
+ NL80211_CHAN_WIDTH_160: "NL80211_CHAN_WIDTH_160",
+}
+nl80211_bss2str = {
+ __NL80211_BSS_INVALID: "__NL80211_BSS_INVALID",
+ NL80211_BSS_BSSID: "NL80211_BSS_BSSID",
+ NL80211_BSS_FREQUENCY: "NL80211_BSS_FREQUENCY",
+ NL80211_BSS_TSF: "NL80211_BSS_TSF",
+ NL80211_BSS_BEACON_INTERVAL: "NL80211_BSS_BEACON_INTERVAL",
+ NL80211_BSS_CAPABILITY: "NL80211_BSS_CAPABILITY",
+ NL80211_BSS_INFORMATION_ELEMENTS: "NL80211_BSS_INFORMATION_ELEMENTS",
+ NL80211_BSS_SIGNAL_MBM: "NL80211_BSS_SIGNAL_MBM",
+ NL80211_BSS_SIGNAL_UNSPEC: "NL80211_BSS_SIGNAL_UNSPEC",
+ NL80211_BSS_STATUS: "NL80211_BSS_STATUS",
+ NL80211_BSS_SEEN_MS_AGO: "NL80211_BSS_SEEN_MS_AGO",
+ NL80211_BSS_BEACON_IES: "NL80211_BSS_BEACON_IES",
+ __NL80211_BSS_AFTER_LAST: "__NL80211_BSS_AFTER_LAST",
+}
+nl80211_bss_status2str = {
+ NL80211_BSS_STATUS_AUTHENTICATED: "NL80211_BSS_STATUS_AUTHENTICATED",
+ NL80211_BSS_STATUS_ASSOCIATED: "NL80211_BSS_STATUS_ASSOCIATED",
+ NL80211_BSS_STATUS_IBSS_JOINED: "NL80211_BSS_STATUS_IBSS_JOINED",
+}
+nl80211_auth_type2str = {
+ NL80211_AUTHTYPE_OPEN_SYSTEM: "NL80211_AUTHTYPE_OPEN_SYSTEM",
+ NL80211_AUTHTYPE_SHARED_KEY: "NL80211_AUTHTYPE_SHARED_KEY",
+ NL80211_AUTHTYPE_FT: "NL80211_AUTHTYPE_FT",
+ NL80211_AUTHTYPE_NETWORK_EAP: "NL80211_AUTHTYPE_NETWORK_EAP",
+ NL80211_AUTHTYPE_SAE: "NL80211_AUTHTYPE_SAE",
+ __NL80211_AUTHTYPE_NUM: "__NL80211_AUTHTYPE_NUM",
+ NL80211_AUTHTYPE_AUTOMATIC: "NL80211_AUTHTYPE_AUTOMATIC",
+}
+nl80211_key_type2str = {
+ NL80211_KEYTYPE_GROUP: "NL80211_KEYTYPE_GROUP",
+ NL80211_KEYTYPE_PAIRWISE: "NL80211_KEYTYPE_PAIRWISE",
+ NL80211_KEYTYPE_PEERKEY: "NL80211_KEYTYPE_PEERKEY",
+ NUM_NL80211_KEYTYPES: "NUM_NL80211_KEYTYPES",
+}
+nl80211_mfp2str = {
+ NL80211_MFP_NO: "NL80211_MFP_NO",
+ NL80211_MFP_REQUIRED: "NL80211_MFP_REQUIRED",
+}
+nl80211_wpa_versions2str = {
+ NL80211_WPA_VERSION_1: "NL80211_WPA_VERSION_1",
+ NL80211_WPA_VERSION_2: "NL80211_WPA_VERSION_2",
+}
+nl80211_key_default_types2str = {
+ __NL80211_KEY_DEFAULT_TYPE_INVALID: "__NL80211_KEY_DEFAULT_TYPE_INVALID",
+ NL80211_KEY_DEFAULT_TYPE_UNICAST: "NL80211_KEY_DEFAULT_TYPE_UNICAST",
+ NL80211_KEY_DEFAULT_TYPE_MULTICAST: "NL80211_KEY_DEFAULT_TYPE_MULTICAST",
+ NUM_NL80211_KEY_DEFAULT_TYPES: "NUM_NL80211_KEY_DEFAULT_TYPES",
+}
+nl80211_key_attributes2str = {
+ __NL80211_KEY_INVALID: "__NL80211_KEY_INVALID",
+ NL80211_KEY_DATA: "NL80211_KEY_DATA",
+ NL80211_KEY_IDX: "NL80211_KEY_IDX",
+ NL80211_KEY_CIPHER: "NL80211_KEY_CIPHER",
+ NL80211_KEY_SEQ: "NL80211_KEY_SEQ",
+ NL80211_KEY_DEFAULT: "NL80211_KEY_DEFAULT",
+ NL80211_KEY_DEFAULT_MGMT: "NL80211_KEY_DEFAULT_MGMT",
+ NL80211_KEY_TYPE: "NL80211_KEY_TYPE",
+ NL80211_KEY_DEFAULT_TYPES: "NL80211_KEY_DEFAULT_TYPES",
+ __NL80211_KEY_AFTER_LAST: "__NL80211_KEY_AFTER_LAST",
+}
+nl80211_tx_rate_attributes2str = {
+ __NL80211_TXRATE_INVALID: "__NL80211_TXRATE_INVALID",
+ NL80211_TXRATE_LEGACY: "NL80211_TXRATE_LEGACY",
+ NL80211_TXRATE_MCS: "NL80211_TXRATE_MCS",
+ __NL80211_TXRATE_AFTER_LAST: "__NL80211_TXRATE_AFTER_LAST",
+}
+nl80211_band2str = {
+ NL80211_BAND_2GHZ: "NL80211_BAND_2GHZ",
+ NL80211_BAND_5GHZ: "NL80211_BAND_5GHZ",
+ NL80211_BAND_60GHZ: "NL80211_BAND_60GHZ",
+}
+nl80211_ps_state2str = {
+ NL80211_PS_DISABLED: "NL80211_PS_DISABLED",
+ NL80211_PS_ENABLED: "NL80211_PS_ENABLED",
+}
+nl80211_attr_cqm2str = {
+ __NL80211_ATTR_CQM_INVALID: "__NL80211_ATTR_CQM_INVALID",
+ NL80211_ATTR_CQM_RSSI_THOLD: "NL80211_ATTR_CQM_RSSI_THOLD",
+ NL80211_ATTR_CQM_RSSI_HYST: "NL80211_ATTR_CQM_RSSI_HYST",
+ NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: "NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT",
+ NL80211_ATTR_CQM_PKT_LOSS_EVENT: "NL80211_ATTR_CQM_PKT_LOSS_EVENT",
+ NL80211_ATTR_CQM_TXE_RATE: "NL80211_ATTR_CQM_TXE_RATE",
+ NL80211_ATTR_CQM_TXE_PKTS: "NL80211_ATTR_CQM_TXE_PKTS",
+ NL80211_ATTR_CQM_TXE_INTVL: "NL80211_ATTR_CQM_TXE_INTVL",
+ __NL80211_ATTR_CQM_AFTER_LAST: "__NL80211_ATTR_CQM_AFTER_LAST",
+}
+nl80211_cqm_rssi_threshold_event2str = {
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW: "NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW",
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: "NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH",
+ NL80211_CQM_RSSI_BEACON_LOSS_EVENT: "NL80211_CQM_RSSI_BEACON_LOSS_EVENT",
+}
+nl80211_tx_power_setting2str = {
+ NL80211_TX_POWER_AUTOMATIC: "NL80211_TX_POWER_AUTOMATIC",
+ NL80211_TX_POWER_LIMITED: "NL80211_TX_POWER_LIMITED",
+ NL80211_TX_POWER_FIXED: "NL80211_TX_POWER_FIXED",
+}
+nl80211_wowlan_packet_pattern_attr2str = {
+ __NL80211_WOWLAN_PKTPAT_INVALID: "__NL80211_WOWLAN_PKTPAT_INVALID",
+ NL80211_WOWLAN_PKTPAT_MASK: "NL80211_WOWLAN_PKTPAT_MASK",
+ NL80211_WOWLAN_PKTPAT_PATTERN: "NL80211_WOWLAN_PKTPAT_PATTERN",
+ NL80211_WOWLAN_PKTPAT_OFFSET: "NL80211_WOWLAN_PKTPAT_OFFSET",
+ NUM_NL80211_WOWLAN_PKTPAT: "NUM_NL80211_WOWLAN_PKTPAT",
+}
+nl80211_wowlan_triggers2str = {
+ __NL80211_WOWLAN_TRIG_INVALID: "__NL80211_WOWLAN_TRIG_INVALID",
+ NL80211_WOWLAN_TRIG_ANY: "NL80211_WOWLAN_TRIG_ANY",
+ NL80211_WOWLAN_TRIG_DISCONNECT: "NL80211_WOWLAN_TRIG_DISCONNECT",
+ NL80211_WOWLAN_TRIG_MAGIC_PKT: "NL80211_WOWLAN_TRIG_MAGIC_PKT",
+ NL80211_WOWLAN_TRIG_PKT_PATTERN: "NL80211_WOWLAN_TRIG_PKT_PATTERN",
+ NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED: "NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED",
+ NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE: "NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE",
+ NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST: "NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST",
+ NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE: "NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE",
+ NL80211_WOWLAN_TRIG_RFKILL_RELEASE: "NL80211_WOWLAN_TRIG_RFKILL_RELEASE",
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211: "NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211",
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN: "NL80211_WOWLAN_TRIG_WAKEUP_PKT_80211_LEN",
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023: "NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023",
+ NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN: "NL80211_WOWLAN_TRIG_WAKEUP_PKT_8023_LEN",
+ NL80211_WOWLAN_TRIG_TCP_CONNECTION: "NL80211_WOWLAN_TRIG_TCP_CONNECTION",
+ NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH: "NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH",
+ NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST: "NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST",
+ NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: "NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS",
+ NUM_NL80211_WOWLAN_TRIG: "NUM_NL80211_WOWLAN_TRIG",
+}
+nl80211_wowlan_tcp_attrs2str = {
+ __NL80211_WOWLAN_TCP_INVALID: "__NL80211_WOWLAN_TCP_INVALID",
+ NL80211_WOWLAN_TCP_SRC_IPV4: "NL80211_WOWLAN_TCP_SRC_IPV4",
+ NL80211_WOWLAN_TCP_DST_IPV4: "NL80211_WOWLAN_TCP_DST_IPV4",
+ NL80211_WOWLAN_TCP_DST_MAC: "NL80211_WOWLAN_TCP_DST_MAC",
+ NL80211_WOWLAN_TCP_SRC_PORT: "NL80211_WOWLAN_TCP_SRC_PORT",
+ NL80211_WOWLAN_TCP_DST_PORT: "NL80211_WOWLAN_TCP_DST_PORT",
+ NL80211_WOWLAN_TCP_DATA_PAYLOAD: "NL80211_WOWLAN_TCP_DATA_PAYLOAD",
+ NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ: "NL80211_WOWLAN_TCP_DATA_PAYLOAD_SEQ",
+ NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN: "NL80211_WOWLAN_TCP_DATA_PAYLOAD_TOKEN",
+ NL80211_WOWLAN_TCP_DATA_INTERVAL: "NL80211_WOWLAN_TCP_DATA_INTERVAL",
+ NL80211_WOWLAN_TCP_WAKE_PAYLOAD: "NL80211_WOWLAN_TCP_WAKE_PAYLOAD",
+ NL80211_WOWLAN_TCP_WAKE_MASK: "NL80211_WOWLAN_TCP_WAKE_MASK",
+ NUM_NL80211_WOWLAN_TCP: "NUM_NL80211_WOWLAN_TCP",
+}
+nl80211_iface_limit_attrs2str = {
+ NL80211_IFACE_LIMIT_UNSPEC: "NL80211_IFACE_LIMIT_UNSPEC",
+ NL80211_IFACE_LIMIT_MAX: "NL80211_IFACE_LIMIT_MAX",
+ NL80211_IFACE_LIMIT_TYPES: "NL80211_IFACE_LIMIT_TYPES",
+ NUM_NL80211_IFACE_LIMIT: "NUM_NL80211_IFACE_LIMIT",
+}
+nl80211_if_combination_attrs2str = {
+ NL80211_IFACE_COMB_UNSPEC: "NL80211_IFACE_COMB_UNSPEC",
+ NL80211_IFACE_COMB_LIMITS: "NL80211_IFACE_COMB_LIMITS",
+ NL80211_IFACE_COMB_MAXNUM: "NL80211_IFACE_COMB_MAXNUM",
+ NL80211_IFACE_COMB_STA_AP_BI_MATCH: "NL80211_IFACE_COMB_STA_AP_BI_MATCH",
+ NL80211_IFACE_COMB_NUM_CHANNELS: "NL80211_IFACE_COMB_NUM_CHANNELS",
+ NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: "NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS",
+ NUM_NL80211_IFACE_COMB: "NUM_NL80211_IFACE_COMB",
+}
+nl80211_plink_state2str = {
+ NL80211_PLINK_LISTEN: "NL80211_PLINK_LISTEN",
+ NL80211_PLINK_OPN_SNT: "NL80211_PLINK_OPN_SNT",
+ NL80211_PLINK_OPN_RCVD: "NL80211_PLINK_OPN_RCVD",
+ NL80211_PLINK_CNF_RCVD: "NL80211_PLINK_CNF_RCVD",
+ NL80211_PLINK_ESTAB: "NL80211_PLINK_ESTAB",
+ NL80211_PLINK_HOLDING: "NL80211_PLINK_HOLDING",
+ NL80211_PLINK_BLOCKED: "NL80211_PLINK_BLOCKED",
+ NUM_NL80211_PLINK_STATES: "NUM_NL80211_PLINK_STATES",
+}
+plink_actions2str = {
+ NL80211_PLINK_ACTION_NO_ACTION: "NL80211_PLINK_ACTION_NO_ACTION",
+ NL80211_PLINK_ACTION_OPEN: "NL80211_PLINK_ACTION_OPEN",
+ NL80211_PLINK_ACTION_BLOCK: "NL80211_PLINK_ACTION_BLOCK",
+ NUM_NL80211_PLINK_ACTIONS: "NUM_NL80211_PLINK_ACTIONS",
+}
+nl80211_rekey_data2str = {
+ __NL80211_REKEY_DATA_INVALID: "__NL80211_REKEY_DATA_INVALID",
+ NL80211_REKEY_DATA_KEK: "NL80211_REKEY_DATA_KEK",
+ NL80211_REKEY_DATA_KCK: "NL80211_REKEY_DATA_KCK",
+ NL80211_REKEY_DATA_REPLAY_CTR: "NL80211_REKEY_DATA_REPLAY_CTR",
+ NUM_NL80211_REKEY_DATA: "NUM_NL80211_REKEY_DATA",
+}
+nl80211_hidden_ssid2str = {
+ NL80211_HIDDEN_SSID_NOT_IN_USE: "NL80211_HIDDEN_SSID_NOT_IN_USE",
+ NL80211_HIDDEN_SSID_ZERO_LEN: "NL80211_HIDDEN_SSID_ZERO_LEN",
+ NL80211_HIDDEN_SSID_ZERO_CONTENTS: "NL80211_HIDDEN_SSID_ZERO_CONTENTS",
+}
+nl80211_sta_wme_attr2str = {
+ __NL80211_STA_WME_INVALID: "__NL80211_STA_WME_INVALID",
+ NL80211_STA_WME_UAPSD_QUEUES: "NL80211_STA_WME_UAPSD_QUEUES",
+ NL80211_STA_WME_MAX_SP: "NL80211_STA_WME_MAX_SP",
+ __NL80211_STA_WME_AFTER_LAST: "__NL80211_STA_WME_AFTER_LAST",
+}
+nl80211_pmksa_candidate_attr2str = {
+ __NL80211_PMKSA_CANDIDATE_INVALID: "__NL80211_PMKSA_CANDIDATE_INVALID",
+ NL80211_PMKSA_CANDIDATE_INDEX: "NL80211_PMKSA_CANDIDATE_INDEX",
+ NL80211_PMKSA_CANDIDATE_BSSID: "NL80211_PMKSA_CANDIDATE_BSSID",
+ NL80211_PMKSA_CANDIDATE_PREAUTH: "NL80211_PMKSA_CANDIDATE_PREAUTH",
+ NUM_NL80211_PMKSA_CANDIDATE: "NUM_NL80211_PMKSA_CANDIDATE",
+}
+nl80211_tdls_operation2str = {
+ NL80211_TDLS_DISCOVERY_REQ: "NL80211_TDLS_DISCOVERY_REQ",
+ NL80211_TDLS_SETUP: "NL80211_TDLS_SETUP",
+ NL80211_TDLS_TEARDOWN: "NL80211_TDLS_TEARDOWN",
+ NL80211_TDLS_ENABLE_LINK: "NL80211_TDLS_ENABLE_LINK",
+ NL80211_TDLS_DISABLE_LINK: "NL80211_TDLS_DISABLE_LINK",
+}
+nl80211_feature_flags2str = {
+ NL80211_FEATURE_SK_TX_STATUS: "NL80211_FEATURE_SK_TX_STATUS",
+ NL80211_FEATURE_HT_IBSS: "NL80211_FEATURE_HT_IBSS",
+ NL80211_FEATURE_INACTIVITY_TIMER: "NL80211_FEATURE_INACTIVITY_TIMER",
+ NL80211_FEATURE_CELL_BASE_REG_HINTS: "NL80211_FEATURE_CELL_BASE_REG_HINTS",
+ NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: "NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL",
+ NL80211_FEATURE_SAE: "NL80211_FEATURE_SAE",
+ NL80211_FEATURE_LOW_PRIORITY_SCAN: "NL80211_FEATURE_LOW_PRIORITY_SCAN",
+ NL80211_FEATURE_SCAN_FLUSH: "NL80211_FEATURE_SCAN_FLUSH",
+ NL80211_FEATURE_AP_SCAN: "NL80211_FEATURE_AP_SCAN",
+ NL80211_FEATURE_VIF_TXPOWER: "NL80211_FEATURE_VIF_TXPOWER",
+ NL80211_FEATURE_NEED_OBSS_SCAN: "NL80211_FEATURE_NEED_OBSS_SCAN",
+ NL80211_FEATURE_P2P_GO_CTWIN: "NL80211_FEATURE_P2P_GO_CTWIN",
+ NL80211_FEATURE_P2P_GO_OPPPS: "NL80211_FEATURE_P2P_GO_OPPPS",
+ NL80211_FEATURE_ADVERTISE_CHAN_LIMITS: "NL80211_FEATURE_ADVERTISE_CHAN_LIMITS",
+ NL80211_FEATURE_FULL_AP_CLIENT_STATE: "NL80211_FEATURE_FULL_AP_CLIENT_STATE",
+ NL80211_FEATURE_USERSPACE_MPM: "NL80211_FEATURE_USERSPACE_MPM",
+ NL80211_FEATURE_ACTIVE_MONITOR: "NL80211_FEATURE_ACTIVE_MONITOR",
+}
+nl80211_probe_resp_offload_support_attr2str = {
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS: "NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS",
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2: "NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2",
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P: "NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P",
+ NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U: "NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U",
+}
+nl80211_connect_failed_reason2str = {
+ NL80211_CONN_FAIL_MAX_CLIENTS: "NL80211_CONN_FAIL_MAX_CLIENTS",
+ NL80211_CONN_FAIL_BLOCKED_CLIENT: "NL80211_CONN_FAIL_BLOCKED_CLIENT",
+}
+nl80211_scan_flags2str = {
+ NL80211_SCAN_FLAG_LOW_PRIORITY: "NL80211_SCAN_FLAG_LOW_PRIORITY",
+ NL80211_SCAN_FLAG_FLUSH: "NL80211_SCAN_FLAG_FLUSH",
+ NL80211_SCAN_FLAG_AP: "NL80211_SCAN_FLAG_AP",
+}
+nl80211_acl_policy2str = {
+ NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED: "NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED",
+ NL80211_ACL_POLICY_DENY_UNLESS_LISTED: "NL80211_ACL_POLICY_DENY_UNLESS_LISTED",
+}
+nl80211_radar_event2str = {
+ NL80211_RADAR_DETECTED: "NL80211_RADAR_DETECTED",
+ NL80211_RADAR_CAC_FINISHED: "NL80211_RADAR_CAC_FINISHED",
+ NL80211_RADAR_CAC_ABORTED: "NL80211_RADAR_CAC_ABORTED",
+ NL80211_RADAR_NOP_FINISHED: "NL80211_RADAR_NOP_FINISHED",
+}
+nl80211_dfs_state2str = {
+ NL80211_DFS_USABLE: "NL80211_DFS_USABLE",
+ NL80211_DFS_UNAVAILABLE: "NL80211_DFS_UNAVAILABLE",
+ NL80211_DFS_AVAILABLE: "NL80211_DFS_AVAILABLE",
+}
+nl80211_protocol_features2str = {
+ NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: "NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP",
+}
+nl80211_crit_proto_id2str = {
+ NL80211_CRIT_PROTO_UNSPEC: "NL80211_CRIT_PROTO_UNSPEC",
+ NL80211_CRIT_PROTO_DHCP: "NL80211_CRIT_PROTO_DHCP",
+ NL80211_CRIT_PROTO_EAPOL: "NL80211_CRIT_PROTO_EAPOL",
+ NL80211_CRIT_PROTO_APIPA: "NL80211_CRIT_PROTO_APIPA",
+ NUM_NL80211_CRIT_PROTO: "NUM_NL80211_CRIT_PROTO",
+}
diff --git a/python/examples/wiphy.py b/python/examples/wiphy.py
new file mode 100644
index 00000000..73e2d4dc
--- /dev/null
+++ b/python/examples/wiphy.py
@@ -0,0 +1,141 @@
+import netlink.capi as nl
+import netlink.genl.capi as genl
+import nl80211
+import sys
+import traceback
+
+class test_class:
+ def __init__(self):
+ self.done = 1;
+
+def freq_to_ch(freq):
+ if freq == 2484:
+ return 14;
+
+ if freq < 2484:
+ return (freq - 2407) / 5;
+
+ # FIXME: dot11ChannelStartingFactor (802.11-2007 17.3.8.3.2)
+ if freq < 45000:
+ return freq/5 - 1000;
+
+ if freq >= 58320 and freq <= 64800:
+ return (freq - 56160) / 2160;
+
+ return 0;
+
+def handle_freq(attr, pol):
+ e, fattr = nl.py_nla_parse_nested(nl80211.NL80211_FREQUENCY_ATTR_MAX, attr, pol)
+ if nl80211.NL80211_FREQUENCY_ATTR_FREQ in fattr:
+ freq = nl.nla_get_u32(fattr[nl80211.NL80211_FREQUENCY_ATTR_FREQ])
+ sys.stdout.write("\t\tfreq %d MHz [%d]" % (freq, freq_to_ch(freq)))
+ if nl80211.NL80211_FREQUENCY_ATTR_MAX_TX_POWER in fattr and not (nl80211.NL80211_FREQUENCY_ATTR_DISABLED in fattr):
+ sys.stdout.write(" (%.1f dBm)" % (0.01 * nl.nla_get_u32(fattr[nl80211.NL80211_FREQUENCY_ATTR_MAX_TX_POWER])))
+ if nl80211.NL80211_FREQUENCY_ATTR_DISABLED in fattr:
+ sys.stdout.write(" (disabled)")
+ sys.stdout.write("\n")
+
+def handle_band(attr, fpol):
+ e, battr = nl.py_nla_parse_nested(nl80211.NL80211_BAND_ATTR_MAX, attr, None)
+ print("\tband %d:" % nl.nla_type(attr))
+ if nl80211.NL80211_BAND_ATTR_FREQS in battr:
+ for fattr in nl.nla_get_nested(battr[nl80211.NL80211_BAND_ATTR_FREQS]):
+ handle_freq(fattr, fpol)
+
+def cipher_name(suite):
+ suite_val = '%02x%02x%02x%02x' % tuple(reversed(suite))
+ if suite_val == '000fac01':
+ return "WEP40 (00-0f-ac:1)"
+ elif suite_val == '000fac05':
+ return "WEP104 (00-0f-ac:5)"
+ elif suite_val == '000fac02':
+ return "TKIP (00-0f-ac:2)"
+ elif suite_val == '000fac04':
+ return "CCMP (00-0f-ac:4)"
+ elif suite_val == '000fac06':
+ return "CMAC (00-0f-ac:6)"
+ elif suite_val == '000fac08':
+ return "GCMP (00-0f-ac:8)"
+ elif suite_val == '00147201':
+ return "WPI-SMS4 (00-14-72:1)"
+ else:
+ return suite_val
+
+def msg_handler(m, a):
+ try:
+ e, attr = genl.py_genlmsg_parse(nl.nlmsg_hdr(m), 0,
+ nl80211.NL80211_ATTR_MAX, None)
+ if nl80211.NL80211_ATTR_WIPHY_NAME in attr:
+ print('wiphy %s' % nl.nla_get_string(attr[nl80211.NL80211_ATTR_WIPHY_NAME]))
+ if nl80211.NL80211_ATTR_WIPHY_BANDS in attr:
+ fpol = nl.nla_policy_array(nl80211.NL80211_FREQUENCY_ATTR_MAX + 1)
+ fpol[nl80211.NL80211_FREQUENCY_ATTR_FREQ].type = nl.NLA_U32
+ fpol[nl80211.NL80211_FREQUENCY_ATTR_DISABLED].type = nl.NLA_FLAG
+ fpol[nl80211.NL80211_FREQUENCY_ATTR_PASSIVE_SCAN].type = nl.NLA_FLAG
+ fpol[nl80211.NL80211_FREQUENCY_ATTR_NO_IBSS].type = nl.NLA_FLAG
+ fpol[nl80211.NL80211_FREQUENCY_ATTR_RADAR].type = nl.NLA_FLAG
+ fpol[nl80211.NL80211_FREQUENCY_ATTR_MAX_TX_POWER].type = nl.NLA_U32
+
+ nattrs = nl.nla_get_nested(attr[nl80211.NL80211_ATTR_WIPHY_BANDS])
+ for nattr in nattrs:
+ handle_band(nattr, fpol)
+ if nl80211.NL80211_ATTR_CIPHER_SUITES in attr:
+ ciphers = nl.nla_data(attr[nl80211.NL80211_ATTR_CIPHER_SUITES])
+ num = len(ciphers) / 4
+ if num > 0:
+ print("\tSupported Ciphers:");
+ for i in range(0, num, 4):
+ print("\t\t* %s" % cipher_name(ciphers[i:i+4]))
+ if nl80211.NL80211_ATTR_SUPPORTED_IFTYPES in attr:
+ print("\tSupported interface modes:")
+ ifattr = nl.nla_get_nested(attr[nl80211.NL80211_ATTR_SUPPORTED_IFTYPES])
+ for nl_mode in ifattr:
+ print("\t\t* %s" % nl80211.nl80211_iftype2str[nl.nla_type(nl_mode)])
+ if nl80211.NL80211_ATTR_SOFTWARE_IFTYPES in attr:
+ print("\tsoftware interface modes (can always be added):")
+ ifattr = nl.nla_get_nested(attr[nl80211.NL80211_ATTR_SOFTWARE_IFTYPES])
+ for nl_mode in ifattr:
+ print("\t\t* %s" % nl80211.nl80211_iftype2str[nl.nla_type(nl_mode)])
+ return nl.NL_SKIP
+ except Exception as e:
+ (t,v,tb) = sys.exc_info()
+ print v.message
+ traceback.print_tb(tb)
+
+def error_handler(err, a):
+ a.done = err.error
+ return nl.NL_STOP
+
+def finish_handler(m, a):
+ return nl.NL_SKIP
+
+def ack_handler(m, a):
+ a.done = 0
+ return nl.NL_STOP
+
+try:
+ cbd = test_class()
+ tx_cb = nl.nl_cb_alloc(nl.NL_CB_DEFAULT)
+ rx_cb = nl.nl_cb_clone(tx_cb)
+ s = nl.nl_socket_alloc_cb(tx_cb)
+ nl.py_nl_cb_err(rx_cb, nl.NL_CB_CUSTOM, error_handler, cbd);
+ nl.py_nl_cb_set(rx_cb, nl.NL_CB_FINISH, nl.NL_CB_CUSTOM, finish_handler, cbd);
+ nl.py_nl_cb_set(rx_cb, nl.NL_CB_ACK, nl.NL_CB_CUSTOM, ack_handler, cbd);
+ nl.py_nl_cb_set(rx_cb, nl.NL_CB_VALID, nl.NL_CB_CUSTOM, msg_handler, cbd);
+
+ genl.genl_connect(s)
+ family = genl.genl_ctrl_resolve(s, 'nl80211')
+ m = nl.nlmsg_alloc()
+ genl.genlmsg_put(m, 0, 0, family, 0, 0, nl80211.NL80211_CMD_GET_WIPHY, 0)
+ nl.nla_put_u32(m, nl80211.NL80211_ATTR_WIPHY, 7)
+
+ err = nl.nl_send_auto_complete(s, m);
+ if err < 0:
+ nl.nlmsg_free(msg)
+
+ while cbd.done > 0 and not err < 0:
+ err = nl.nl_recvmsgs(s, rx_cb)
+except Exception as e:
+ (t, v, tb) = sys.exc_info()
+ print v.message
+ traceback.print_tb(tb)
diff --git a/python/netlink/Makefile.am b/python/netlink/Makefile.am
new file mode 100644
index 00000000..1f6eaf8f
--- /dev/null
+++ b/python/netlink/Makefile.am
@@ -0,0 +1,11 @@
+# -*- Makefile -*-
+
+SUBDIRS = route genl
+
+EXTRA_DIST = \
+ capi.i \
+ fixes.h \
+ __init__.py \
+ core.py \
+ util.py \
+ utils.h
diff --git a/python/netlink/__init__.py b/python/netlink/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/python/netlink/__init__.py
diff --git a/python/netlink/capi.i b/python/netlink/capi.i
new file mode 100644
index 00000000..e5d8a536
--- /dev/null
+++ b/python/netlink/capi.i
@@ -0,0 +1,964 @@
+%module capi
+%{
+#include <netlink/netlink.h>
+#include <netlink/types.h>
+#include <netlink/socket.h>
+#include <netlink/msg.h>
+#include <netlink/object.h>
+#include <netlink/cache.h>
+#include <netlink/attr.h>
+#include <net/if.h>
+
+#define DEBUG
+#include "utils.h"
+%}
+
+%include <stdint.i>
+%include <cstring.i>
+%include <cpointer.i>
+
+%inline %{
+ struct nl_dump_params *alloc_dump_params(void)
+ {
+ struct nl_dump_params *dp;
+ if (!(dp = calloc(1, sizeof(*dp))))
+ return NULL;
+ dp->dp_fd = stdout;
+ return dp;
+ }
+
+ void free_dump_params(struct nl_dump_params *dp)
+ {
+ free(dp);
+ }
+%};
+
+/* <netlink/types.h> */
+
+enum nl_dump_type {
+ NL_DUMP_LINE, /**< Dump object briefly on one line */
+ NL_DUMP_DETAILS, /**< Dump all attributes but no statistics */
+ NL_DUMP_STATS, /**< Dump all attributes including statistics */
+ __NL_DUMP_MAX,
+};
+
+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;
+
+ /**
+ * PRIVATE
+ * Owned by the current caller
+ */
+ int dp_ivar;
+
+ unsigned int dp_line;
+};
+
+/* <net/if.h> */
+extern unsigned int if_nametoindex(const char *ifname);
+
+/* <netlink/errno.h> */
+extern const char *nl_geterror(int);
+
+/* <netlink/utils.h> */
+
+extern double nl_cancel_down_bytes(unsigned long long, char **);
+extern double nl_cancel_down_bits(unsigned long long, char **);
+%cstring_output_maxsize(char *buf, size_t len)
+extern int nl_rate2str(unsigned long long rate, int type, char *buf, size_t len);
+extern double nl_cancel_down_us(uint32_t, char **);
+
+extern long nl_size2int(const char *);
+%cstring_output_maxsize(char *buf, const size_t len)
+extern char *nl_size2str(const size_t, char *buf, const size_t len);
+extern long nl_prob2int(const char *);
+
+extern int nl_get_user_hz(void);
+extern uint32_t nl_us2ticks(uint32_t);
+extern uint32_t nl_ticks2us(uint32_t);
+extern int nl_str2msec(const char *, uint64_t *);
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char *nl_msec2str(uint64_t, char *buf, size_t len);
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char *nl_llproto2str(int, char *buf, size_t len);
+extern int nl_str2llproto(const char *);
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char *nl_ether_proto2str(int, char *buf, size_t len);
+extern int nl_str2ether_proto(const char *);
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char *nl_ip_proto2str(int, char *buf, size_t len);
+extern int nl_str2ip_proto(const char *);
+
+extern void nl_new_line(struct nl_dump_params *);
+extern void nl_dump(struct nl_dump_params *, const char *, ...);
+extern void nl_dump_line(struct nl_dump_params *, const char *, ...);
+
+/* <netlink/netlink.h> */
+extern struct nl_dump_params *alloc_dump_params(void);
+extern void free_dump_params(struct nl_dump_params *);
+
+extern int nl_connect(struct nl_sock *, int);
+extern void nl_close(struct nl_sock *);
+
+/* <netlink/socket.h> */
+extern struct nl_sock *nl_socket_alloc(void);
+extern struct nl_sock *nl_socket_alloc_cb(struct nl_cb *);
+extern void nl_socket_free(struct nl_sock *);
+
+extern uint32_t nl_socket_get_local_port(const struct nl_sock *);
+extern void nl_socket_set_local_port(struct nl_sock *, uint32_t);
+
+extern uint32_t nl_socket_get_peer_port(const struct nl_sock *);
+extern void nl_socket_set_peer_port(struct nl_sock *, uint32_t);
+
+extern uint32_t nl_socket_get_peer_groups(const struct nl_sock *sk);
+extern void nl_socket_set_peer_groups(struct nl_sock *sk, uint32_t groups);
+
+extern int nl_socket_set_buffer_size(struct nl_sock *, int, int);
+extern void nl_socket_set_cb(struct nl_sock *, struct nl_cb *);
+
+extern int nl_send_auto_complete(struct nl_sock *, struct nl_msg *);
+extern int nl_recvmsgs(struct nl_sock *, struct nl_cb *);
+
+/* <netlink/msg.h> */
+extern int nlmsg_size(int);
+extern int nlmsg_total_size(int);
+extern int nlmsg_padlen(int);
+
+extern void * nlmsg_data(const struct nlmsghdr *);
+extern int nlmsg_datalen(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_valid_hdr(const struct nlmsghdr *, int);
+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 *);
+
+extern struct nl_msg * nlmsg_alloc(void);
+extern struct nl_msg * nlmsg_alloc_size(size_t);
+extern struct nl_msg * nlmsg_alloc_simple(int, int);
+extern void nlmsg_set_default_size(size_t);
+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 int nlmsg_expand(struct nl_msg *, size_t);
+
+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_get(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 size_t nlmsg_get_max_size(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 *);
+
+%inline %{
+ struct nl_object *cast_obj(void *obj)
+ {
+ return (struct nl_object *) obj;
+ }
+
+ struct nl_object *object_alloc_name(const char *name)
+ {
+ struct nl_object *obj;
+
+ if (nl_object_alloc_name(name, &obj) < 0)
+ return NULL;
+
+ return obj;
+ }
+%};
+
+extern struct nl_object *nl_object_alloc(struct nl_object_ops *);
+extern void nl_object_free(struct nl_object *);
+extern struct nl_object *nl_object_clone(struct nl_object *);
+extern void nl_object_get(struct nl_object *);
+extern void nl_object_put(struct nl_object *);
+extern int nl_object_shared(struct nl_object *);
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern void nl_object_dump_buf(struct nl_object *, char *buf, size_t len);
+
+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 *);
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char *nl_object_attrs2str(struct nl_object *, uint32_t, char *buf, size_t len);
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char *nl_object_attr_list(struct nl_object *, char *buf, size_t len);
+
+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 *);
+
+extern int nl_object_get_refcnt(struct nl_object *);
+
+/* <netlink/cache.h> */
+
+typedef void (*change_func_t)(struct nl_cache *, struct nl_object *, int, void *);
+
+%inline %{
+ struct nl_cache *alloc_cache_name(const char *name)
+ {
+ struct nl_cache *c;
+ if (nl_cache_alloc_name(name, &c) < 0)
+ return NULL;
+ return c;
+ }
+
+ struct nl_cache_mngr *alloc_cache_mngr(struct nl_sock *sock,
+ int protocol, int flags)
+ {
+ struct nl_cache_mngr *mngr;
+
+ if (nl_cache_mngr_alloc(sock, protocol, flags, &mngr) < 0)
+ return NULL;
+
+ return mngr;
+ }
+
+ struct nl_cache *cache_mngr_add(struct nl_cache_mngr *mngr,
+ const char *name, change_func_t func,
+ void *arg)
+ {
+ struct nl_cache *cache;
+
+ if (nl_cache_mngr_add(mngr, name, func, arg, &cache) < 0)
+ return NULL;
+
+ return cache;
+ }
+%}
+
+/* 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 *);
+
+extern struct nl_cache * nl_cache_alloc(struct nl_cache_ops *);
+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 *);
+extern void nl_cache_remove(struct nl_object *);
+extern int nl_cache_refill(struct nl_sock *,
+ struct nl_cache *);
+extern int nl_cache_pickup(struct nl_sock *,
+ struct nl_cache *);
+extern int nl_cache_resync(struct nl_sock *,
+ struct nl_cache *,
+ change_func_t,
+ void *);
+extern int nl_cache_include(struct nl_cache *,
+ struct nl_object *,
+ change_func_t,
+ void *);
+extern void nl_cache_set_arg1(struct nl_cache *, int);
+extern void nl_cache_set_arg2(struct nl_cache *, int);
+
+/* General */
+extern int nl_cache_is_empty(struct nl_cache *);
+extern struct nl_object * nl_cache_search(struct nl_cache *,
+ struct nl_object *);
+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_associate(int, int);
+extern struct nl_msgtype * nl_msgtype_lookup(struct nl_cache_ops *, int);
+extern void nl_cache_ops_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 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 *);
+
+/* <netlink/addr.h> */
+%inline %{
+ struct nl_addr *addr_parse(const char *addr, int guess)
+ {
+ struct nl_addr *result;
+
+ if (nl_addr_parse(addr, guess, &result) < 0)
+ return NULL;
+
+ return result;
+ }
+%};
+
+extern struct nl_addr *nl_addr_alloc(size_t);
+extern struct nl_addr *nl_addr_alloc_attr(struct nlattr *, int);
+extern struct nl_addr *nl_addr_build(int, void *, size_t);
+extern struct nl_addr *nl_addr_clone(struct nl_addr *);
+
+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_iszero(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 int nl_addr_info(struct nl_addr *, struct addrinfo **);
+extern int nl_addr_resolve(struct nl_addr *addr, char *host, size_t hostlen);
+
+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 *);
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char *nl_af2str(int, char *buf, size_t len);
+extern int nl_str2af(const char *);
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char *nl_addr2str(struct nl_addr *, char *buf, size_t len);
+
+/* Message Handlers <netlink/handlers.h> */
+/**
+ * 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,
+};
+
+/**
+ * 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,
+ /** Flag NLM_F_DUMP_INTR is set in message */
+ NL_CB_DUMP_INTR,
+ __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 *);
+
+struct nlmsgerr {
+ int error;
+};
+
+%{
+
+struct pynl_callback {
+ PyObject *cbf;
+ PyObject *cba;
+};
+
+struct pynl_cbinfo {
+ struct nl_cb *cb;
+ struct pynl_callback cbtype[NL_CB_TYPE_MAX+1];
+ struct pynl_callback cberr;
+ struct list_head list;
+};
+
+LIST_HEAD(callback_list);
+
+static struct pynl_cbinfo *pynl_find_cbinfo(struct nl_cb *cb, int unlink)
+{
+ struct list_head *pos, *prev;
+ struct pynl_cbinfo *info;
+
+ list_for_each_safe(pos, prev, &callback_list) {
+ info = container_of(pos, struct pynl_cbinfo, list);
+ if (info->cb == cb) {
+ if (unlink)
+ list_del(pos, prev);
+ pynl_dbg("cb=%p: found=%p\n", cb, info);
+ return info;
+ }
+ }
+ pynl_dbg("cb=%p: not found\n", cb);
+ return NULL;
+}
+
+static struct pynl_cbinfo *pynl_get_cbinfo(struct nl_cb *cb, int unlink)
+{
+ struct pynl_cbinfo *info;
+
+ info = pynl_find_cbinfo(cb, unlink);
+
+ if (info || unlink) {
+ /* found or no need to allocate a new one */
+ pynl_dbg("cb=%p: done\n", cb);
+ return info;
+ }
+
+ info = calloc(1, sizeof(*info));
+ info->cb = cb;
+ list_add(&info->list, &callback_list);
+ pynl_dbg("cb=%p: added %p\n", cb, info);
+ return info;
+}
+
+static int nl_recv_msg_handler(struct nl_msg *msg, void *arg)
+{
+ struct pynl_callback *cbd = arg;
+ PyObject *msgobj;
+ PyObject *cbparobj;
+ PyObject *resobj;
+ PyObject *funcobj;
+ int result;
+
+ if (!cbd) {
+ result = NL_STOP;
+ goto done;
+ }
+ msgobj = SWIG_NewPointerObj(SWIG_as_voidptr(msg),
+ SWIGTYPE_p_nl_msg, 0 | 0 );
+ /* add selfobj if callback is a method */
+ if (cbd->cbf && PyMethod_Check(cbd->cbf)) {
+ PyObject *selfobj = PyMethod_Self(cbd->cbf);
+ cbparobj = Py_BuildValue("(OOO)", selfobj ? selfobj : cbd->cba,
+ msgobj, cbd->cba);
+ funcobj = PyMethod_Function(cbd->cbf);
+ pynl_dbg("callback %sbounded instance method %p\n",
+ selfobj ? "" : "un", funcobj);
+ } else {
+ cbparobj = Py_BuildValue("(OO)", msgobj, cbd->cba);
+ funcobj = cbd->cbf;
+ pynl_dbg("callback function %p\n", funcobj);
+ }
+ resobj = PyObject_CallObject(funcobj, cbparobj);
+ Py_DECREF(cbparobj);
+ if (resobj && PyInt_Check(resobj))
+ result = (int)PyInt_AsLong(resobj);
+ else
+ result = NL_STOP;
+ Py_XDECREF(resobj);
+done:
+ pynl_dbg("result=%d\n", result);
+ return result;
+}
+
+static int nl_recv_err_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
+ void *arg)
+{
+ struct pynl_callback *cbd = arg;
+ PyObject *errobj;
+ PyObject *cbparobj;
+ PyObject *resobj;
+ PyObject *funcobj;
+ int result;
+
+ if (!cbd)
+ return NL_STOP;
+ errobj = SWIG_NewPointerObj(SWIG_as_voidptr(err),
+ SWIGTYPE_p_nlmsgerr, 0 | 0 );
+ /* add selfobj if callback is a method */
+ if (cbd->cbf && PyMethod_Check(cbd->cbf)) {
+ PyObject *selfobj = PyMethod_Self(cbd->cbf);
+ cbparobj = Py_BuildValue("(OOO)", selfobj ? selfobj : cbd->cba,
+ errobj, cbd->cba);
+ funcobj = PyMethod_Function(cbd->cbf);
+ } else {
+ cbparobj = Py_BuildValue("(OO)", errobj, cbd->cba);
+ funcobj = cbd->cbf;
+ }
+ resobj = PyObject_CallObject(funcobj, cbparobj);
+ Py_DECREF(cbparobj);
+ if (resobj && PyInt_Check(resobj))
+ result = (int)PyInt_AsLong(resobj);
+ else
+ result = NL_STOP;
+ Py_XDECREF(resobj);
+ pynl_dbg("error: err=%d ret=%d\n", err->error, result);
+ return result;
+}
+
+%}
+%inline %{
+struct nl_cb *py_nl_cb_clone(struct nl_cb *cb)
+{
+ struct pynl_cbinfo *info, *clone_info;
+ struct nl_cb *clone;
+ int i;
+
+ clone = nl_cb_clone(cb);
+ info = pynl_find_cbinfo(cb, 0);
+ if (info) {
+ clone_info = pynl_get_cbinfo(clone, 0);
+ /* increase refcnt to callback parameters and copy them */
+ for (i = 0; info && i <= NL_CB_TYPE_MAX; i++) {
+ Py_XINCREF(info->cbtype[i].cbf);
+ Py_XINCREF(info->cbtype[i].cba);
+ clone_info->cbtype[i].cbf = info->cbtype[i].cbf;
+ clone_info->cbtype[i].cba = info->cbtype[i].cba;
+ }
+ Py_XINCREF(info->cberr.cbf);
+ Py_XINCREF(info->cberr.cba);
+ clone_info->cberr.cbf = info->cberr.cbf;
+ clone_info->cberr.cba = info->cberr.cba;
+ }
+ return clone;
+}
+
+void py_nl_cb_put(struct nl_cb *cb)
+{
+ struct pynl_cbinfo *info;
+ int i;
+
+ /* obtain callback info (and unlink) */
+ info = pynl_get_cbinfo(cb, 1);
+ pynl_dbg("cb=%p, info=%p\n", cb, info);
+ /* decrease refcnt for callback type handlers */
+ for (i = 0; info && i <= NL_CB_TYPE_MAX; i++) {
+ Py_XDECREF(info->cbtype[i].cbf);
+ Py_XDECREF(info->cbtype[i].cba);
+ }
+ /* decrease refcnt for error handler and free callback info */
+ if (info) {
+ Py_XDECREF(info->cberr.cbf);
+ Py_XDECREF(info->cberr.cba);
+ free(info);
+ }
+ nl_cb_put(cb);
+}
+
+int py_nl_cb_set(struct nl_cb *cb, enum nl_cb_type t, enum nl_cb_kind k,
+ PyObject *func, PyObject *a)
+{
+ struct pynl_cbinfo *info;
+
+ /* obtain callback info */
+ info = pynl_get_cbinfo(cb, 0);
+
+ /* clear existing handlers (if any) */
+ Py_XDECREF(info->cbtype[t].cbf);
+ Py_XDECREF(info->cbtype[t].cba);
+ info->cbtype[t].cbf = NULL;
+ info->cbtype[t].cba = NULL;
+ pynl_dbg("cb=%p, info=%p, type=%d, kind=%d\n", cb, info, t, k);
+ /* handle custom callback */
+ if (k == NL_CB_CUSTOM) {
+ Py_XINCREF(func);
+ Py_XINCREF(a);
+ info->cbtype[t].cbf = func;
+ info->cbtype[t].cba = a;
+ return nl_cb_set(cb, t, k,
+ nl_recv_msg_handler, &info->cbtype[t]);
+ }
+ return nl_cb_set(cb, t, k, NULL, NULL);
+}
+
+int py_nl_cb_set_all(struct nl_cb *cb, enum nl_cb_kind k,
+ PyObject *func , PyObject *a)
+{
+ struct pynl_cbinfo *info;
+ int t;
+
+ info = pynl_get_cbinfo(cb, 0);
+ pynl_dbg("cb=%p, info=%p, kind=%d\n", cb, info, k);
+ for (t = 0; t <= NL_CB_TYPE_MAX; t++) {
+ /* (possibly) free existing handler */
+ Py_XDECREF(info->cbtype[t].cbf);
+ Py_XDECREF(info->cbtype[t].cba);
+ info->cbtype[t].cbf = NULL;
+ info->cbtype[t].cba = NULL;
+ if (k == NL_CB_CUSTOM) {
+ Py_XINCREF(func);
+ Py_XINCREF(a);
+ info->cbtype[t].cbf = func;
+ info->cbtype[t].cba = a;
+ }
+ }
+ if (k == NL_CB_CUSTOM)
+ /* callback argument is same for all so using idx 0 here */
+ return nl_cb_set_all(cb, k, nl_recv_msg_handler,
+ &info->cbtype[0]);
+ else
+ return nl_cb_set_all(cb, k, NULL, NULL);
+}
+
+int py_nl_cb_err(struct nl_cb *cb, enum nl_cb_kind k,
+ PyObject *func, PyObject *a)
+{
+ struct pynl_cbinfo *info;
+
+ /* obtain callback info */
+ info = pynl_get_cbinfo(cb, 0);
+ pynl_dbg("cb=%p, info=%p, kind=%d\n", cb, info, k);
+ /* clear existing handlers (if any) */
+ Py_XDECREF(info->cberr.cbf);
+ Py_XDECREF(info->cberr.cba);
+ info->cberr.cbf = NULL;
+ info->cberr.cba = NULL;
+
+ /* handle custom callback */
+ if (k == NL_CB_CUSTOM) {
+ Py_XINCREF(func);
+ Py_XINCREF(a);
+ info->cberr.cbf = func;
+ info->cberr.cba = a;
+ return nl_cb_err(cb, k,
+ nl_recv_err_handler, &info->cberr);
+ }
+ return nl_cb_err(cb, k, NULL, NULL);
+}
+%}
+
+/* Attributes <netlink/attr.h> */
+/*
+ * This typemap is a bit tricky as it uses arg1, which is knowledge about
+ * the SWIGged wrapper output.
+ */
+%typemap(out) void * {
+ $result = PyByteArray_FromStringAndSize($1, nla_len(arg1));
+}
+extern void *nla_data(struct nlattr *);
+%typemap(out) void *;
+extern int nla_type(const struct nlattr *);
+
+/* Integer attribute */
+extern uint8_t nla_get_u8(struct nlattr *);
+extern int nla_put_u8(struct nl_msg *, int, uint8_t);
+extern uint16_t nla_get_u16(struct nlattr *);
+extern int nla_put_u16(struct nl_msg *, int, uint16_t);
+extern uint32_t nla_get_u32(struct nlattr *);
+extern int nla_put_u32(struct nl_msg *, int, uint32_t);
+extern uint64_t nla_get_u64(struct nlattr *);
+extern int nla_put_u64(struct nl_msg *, int, uint64_t);
+
+/* String attribute */
+extern char * nla_get_string(struct nlattr *);
+extern char * nla_strdup(struct nlattr *);
+extern int nla_put_string(struct nl_msg *, int, const char *);
+
+/* Flag attribute */
+extern int nla_get_flag(struct nlattr *);
+extern int nla_put_flag(struct nl_msg *, int);
+
+/* Msec attribute */
+extern unsigned long nla_get_msecs(struct nlattr *);
+extern int nla_put_msecs(struct nl_msg *, int, unsigned long);
+
+/* Attribute nesting */
+extern int nla_put_nested(struct nl_msg *, int, struct nl_msg *);
+extern struct nlattr * nla_nest_start(struct nl_msg *, int);
+extern int nla_nest_end(struct nl_msg *, struct nlattr *);
+%inline %{
+PyObject *py_nla_parse_nested(int max, struct nlattr *nest_attr, PyObject *p)
+{
+ struct nlattr *tb_msg[max + 1];
+ struct nla_policy *policy = NULL;
+ void *pol;
+ PyObject *attrs = Py_None;
+ PyObject *k;
+ PyObject *v;
+ PyObject *resobj;
+ int err;
+ int i;
+
+ if (p != Py_None) {
+ PyObject *pobj;
+
+ if (!PyList_Check(p)) {
+ fprintf(stderr, "expected list object\n");
+ err = -1;
+ goto fail;
+ }
+ pobj = PyList_GetItem(p, 0);
+ err = SWIG_ConvertPtr(pobj, &pol, SWIGTYPE_p_nla_policy, 0 | 0 );
+ if (!SWIG_IsOK(err))
+ goto fail;
+ policy = pol;
+ }
+ err = nla_parse_nested(tb_msg, max, nest_attr, policy);
+ if (err < 0) {
+ fprintf(stderr, "Failed to parse response message\n");
+ } else {
+ attrs = PyDict_New();
+ for (i = 0; i <= max; i++)
+ if (tb_msg[i]) {
+ k = PyInt_FromLong((long)i);
+ v = SWIG_NewPointerObj(SWIG_as_voidptr(tb_msg[i]), SWIGTYPE_p_nlattr, 0 | 0 );
+ PyDict_SetItem(attrs, k, v);
+ }
+ }
+fail:
+ if (attrs == Py_None)
+ Py_INCREF(attrs);
+ resobj = Py_BuildValue("(iO)", err, attrs);
+ return resobj;
+}
+
+/*
+ * nla_get_nested() - get list of nested attributes.
+ *
+ * nla_for_each_<nested|attr>() is a macro construct that needs another approach
+ * for Python. Create and return list of nested attributes.
+ */
+PyObject *nla_get_nested(struct nlattr *nest_attr)
+{
+ PyObject *listobj;
+ PyObject *nestattrobj;
+ struct nlattr *pos;
+ int rem;
+
+ listobj = PyList_New(0);
+ nla_for_each_nested(pos, nest_attr, rem) {
+ nestattrobj = SWIG_NewPointerObj(SWIG_as_voidptr(pos),
+ SWIGTYPE_p_nlattr, 0 | 0 );
+ PyList_Append(listobj, nestattrobj);
+ }
+ return listobj;
+}
+%}
+
+ /**
+ * @ingroup attr
+ * Basic attribute data types
+ *
+ * See \ref attr_datatypes for more details.
+ */
+enum {
+ NLA_UNSPEC, /**< Unspecified type, binary data chunk */
+ NLA_U8, /**< 8 bit integer */
+ NLA_U16, /**< 16 bit integer */
+ NLA_U32, /**< 32 bit integer */
+ NLA_U64, /**< 64 bit integer */
+ NLA_STRING, /**< NUL terminated character string */
+ NLA_FLAG, /**< Flag */
+ NLA_MSECS, /**< Micro seconds (64bit) */
+ NLA_NESTED, /**< Nested attributes */
+ __NLA_TYPE_MAX,
+};
+
+#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)
+
+/** @} */
+
+/**
+ * @ingroup attr
+ * Attribute validation policy.
+ *
+ * See \ref attr_datatypes for more details.
+ */
+struct nla_policy {
+ /** Type of attribute or NLA_UNSPEC */
+ uint16_t type;
+
+ /** Minimal length of payload required */
+ uint16_t minlen;
+
+ /** Maximal length of payload allowed */
+ uint16_t maxlen;
+};
+
+%inline %{
+PyObject *nla_policy_array(int n_items)
+{
+ struct nla_policy *policies;
+ PyObject *listobj;
+ PyObject *polobj;
+ int i;
+
+ policies = calloc(n_items, sizeof(*policies));
+ listobj = PyList_New(n_items);
+ for (i = 0; i < n_items; i++) {
+ polobj = SWIG_NewPointerObj(SWIG_as_voidptr(&policies[i]),
+ SWIGTYPE_p_nla_policy, 0 | 0 );
+ PyList_SetItem(listobj, i, polobj);
+ }
+ return listobj;
+}
+%}
diff --git a/python/netlink/core.py b/python/netlink/core.py
new file mode 100644
index 00000000..e5864cf5
--- /dev/null
+++ b/python/netlink/core.py
@@ -0,0 +1,793 @@
+#
+# Netlink interface based on libnl
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""netlink library based on libnl
+
+This module provides an interface to netlink sockets
+
+The module contains the following public classes:
+ - Socket -- The netlink socket
+ - Message -- The netlink message
+ - Callback -- The netlink callback handler
+ - Object -- Abstract object (based on struct nl_obect in libnl) used as
+ base class for all object types which can be put into a Cache
+ - Cache -- A collection of objects which are derived from the base
+ class Object. Used for netlink protocols which maintain a list
+ or tree of objects.
+ - DumpParams --
+
+The following exceptions are defined:
+ - NetlinkError -- Base exception for all general purpose exceptions raised.
+ - KernelError -- Raised when the kernel returns an error as response to a
+ request.
+
+All other classes or functions in this module are considered implementation
+details.
+"""
+from __future__ import absolute_import
+
+
+
+from . import capi
+import sys
+import socket
+
+__all__ = [
+ 'Socket',
+ 'Message',
+ 'Callback',
+ 'DumpParams',
+ 'Object',
+ 'Cache',
+ 'KernelError',
+ 'NetlinkError',
+]
+
+__version__ = '0.1'
+
+# netlink protocols
+NETLINK_ROUTE = 0
+# NETLINK_UNUSED = 1
+NETLINK_USERSOCK = 2
+NETLINK_FIREWALL = 3
+NETLINK_INET_DIAG = 4
+NETLINK_NFLOG = 5
+NETLINK_XFRM = 6
+NETLINK_SELINUX = 7
+NETLINK_ISCSI = 8
+NETLINK_AUDIT = 9
+NETLINK_FIB_LOOKUP = 10
+NETLINK_CONNECTOR = 11
+NETLINK_NETFILTER = 12
+NETLINK_IP6_FW = 13
+NETLINK_DNRTMSG = 14
+NETLINK_KOBJECT_UEVENT = 15
+NETLINK_GENERIC = 16
+NETLINK_SCSITRANSPORT = 18
+NETLINK_ECRYPTFS = 19
+
+NL_DONTPAD = 0
+NL_AUTO_PORT = 0
+NL_AUTO_SEQ = 0
+
+NL_DUMP_LINE = 0
+NL_DUMP_DETAILS = 1
+NL_DUMP_STATS = 2
+
+NLM_F_REQUEST = 1
+NLM_F_MULTI = 2
+NLM_F_ACK = 4
+NLM_F_ECHO = 8
+
+NLM_F_ROOT = 0x100
+NLM_F_MATCH = 0x200
+NLM_F_ATOMIC = 0x400
+NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH
+
+NLM_F_REPLACE = 0x100
+NLM_F_EXCL = 0x200
+NLM_F_CREATE = 0x400
+NLM_F_APPEND = 0x800
+
+class NetlinkError(Exception):
+ def __init__(self, error):
+ self._error = error
+ self._msg = capi.nl_geterror(error)
+
+ def __str__(self):
+ return self._msg
+
+class KernelError(NetlinkError):
+ def __str__(self):
+ return 'Kernel returned: {0}'.format(self._msg)
+
+class ImmutableError(NetlinkError):
+ def __init__(self, msg):
+ self._msg = msg
+
+ def __str__(self):
+ return 'Immutable attribute: {0}'.format(self._msg)
+
+class Message(object):
+ """Netlink message"""
+
+ def __init__(self, size=0):
+ if size == 0:
+ self._msg = capi.nlmsg_alloc()
+ else:
+ self._msg = capi.nlmsg_alloc_size(size)
+
+ if self._msg is None:
+ raise Exception('Message allocation returned NULL')
+
+ def __del__(self):
+ capi.nlmsg_free(self._msg)
+
+ def __len__(self):
+ return capi.nlmsg_len(nlmsg_hdr(self._msg))
+
+ @property
+ def protocol(self):
+ return capi.nlmsg_get_proto(self._msg)
+
+ @protocol.setter
+ def protocol(self, value):
+ capi.nlmsg_set_proto(self._msg, value)
+
+ @property
+ def maxSize(self):
+ return capi.nlmsg_get_max_size(self._msg)
+
+ @property
+ def hdr(self):
+ return capi.nlmsg_hdr(self._msg)
+
+ @property
+ def data(self):
+ return capi.nlmsg_data(self._msg)
+
+ @property
+ def attrs(self):
+ return capi.nlmsg_attrdata(self._msg)
+
+ def send(self, sock):
+ sock.send(self)
+
+class Callback(object):
+ """Netlink callback"""
+
+ def __init__(self, kind=capi.NL_CB_DEFAULT):
+ if isinstance(kind, Callback):
+ self._cb = capi.py_nl_cb_clone(kind._cb)
+ else:
+ self._cb = capi.nl_cb_alloc(kind)
+
+ def __del__(self):
+ capi.py_nl_cb_put(self._cb)
+
+ def set_type(self, t, k, handler, obj):
+ return capi.py_nl_cb_set(self._cb, t, k, handler, obj)
+
+ def set_all(self, k, handler, obj):
+ return capi.py_nl_cb_set_all(self._cb, k, handler, obj)
+
+ def set_err(self, k, handler, obj):
+ return capi.py_nl_cb_err(self._cb, k, handler, obj)
+
+ def clone(self):
+ return Callback(self)
+
+class Socket(object):
+ """Netlink socket"""
+
+ def __init__(self, cb=None):
+ if isinstance(cb, Callback):
+ self._sock = capi.nl_socket_alloc_cb(cb._cb)
+ elif cb == None:
+ self._sock = capi.nl_socket_alloc()
+ else:
+ raise Exception('\'cb\' parameter has wrong type')
+
+ if self._sock is None:
+ raise Exception('NULL pointer returned while allocating socket')
+
+ def __del__(self):
+ capi.nl_socket_free(self._sock)
+
+ def __str__(self):
+ return 'nlsock<{0}>'.format(self.local_port)
+
+ @property
+ def local_port(self):
+ return capi.nl_socket_get_local_port(self._sock)
+
+ @local_port.setter
+ def local_port(self, value):
+ capi.nl_socket_set_local_port(self._sock, int(value))
+
+ @property
+ def peer_port(self):
+ return capi.nl_socket_get_peer_port(self._sock)
+
+ @peer_port.setter
+ def peer_port(self, value):
+ capi.nl_socket_set_peer_port(self._sock, int(value))
+
+ @property
+ def peer_groups(self):
+ return capi.nl_socket_get_peer_groups(self._sock)
+
+ @peer_groups.setter
+ def peer_groups(self, value):
+ capi.nl_socket_set_peer_groups(self._sock, value)
+
+ def set_bufsize(self, rx, tx):
+ capi.nl_socket_set_buffer_size(self._sock, rx, tx)
+
+ def connect(self, proto):
+ capi.nl_connect(self._sock, proto)
+ return self
+
+ def disconnect(self):
+ capi.nl_close(self._sock)
+
+ def sendto(self, buf):
+ ret = capi.nl_sendto(self._sock, buf, len(buf))
+ if ret < 0:
+ raise Exception('Failed to send')
+ else:
+ return ret
+
+ def send_auto_complete(self, msg):
+ if not isinstance(msg, Message):
+ raise Exception('must provide Message instance')
+ ret = capi.nl_send_auto_complete(self._sock, msg._msg)
+ if ret < 0:
+ raise Exception('send_auto_complete failed: ret=%d' % ret)
+ return ret
+
+ def recvmsgs(self, recv_cb):
+ if not isinstance(recv_cb, Callback):
+ raise Exception('must provide Callback instance')
+ ret = capi.nl_recvmsgs(self._sock, recv_cb._cb)
+ if ret < 0:
+ raise Exception('recvmsg failed: ret=%d' % ret)
+
+_sockets = {}
+
+def lookup_socket(protocol):
+ try:
+ sock = _sockets[protocol]
+ except KeyError:
+ sock = Socket()
+ sock.connect(protocol)
+ _sockets[protocol] = sock
+
+ return sock
+
+class DumpParams(object):
+ """Dumping parameters"""
+
+ def __init__(self, type_=NL_DUMP_LINE):
+ self._dp = capi.alloc_dump_params()
+ if not self._dp:
+ raise Exception('Unable to allocate struct nl_dump_params')
+
+ self._dp.dp_type = type_
+
+ def __del__(self):
+ capi.free_dump_params(self._dp)
+
+ @property
+ def type(self):
+ return self._dp.dp_type
+
+ @type.setter
+ def type(self, value):
+ self._dp.dp_type = value
+
+ @property
+ def prefix(self):
+ return self._dp.dp_prefix
+
+ @prefix.setter
+ def prefix(self, value):
+ self._dp.dp_prefix = value
+
+# underscore this to make sure it is deleted first upon module deletion
+_defaultDumpParams = DumpParams(NL_DUMP_LINE)
+
+class Object(object):
+ """Cacheable object (base class)"""
+
+ def __init__(self, obj_name, name, obj=None):
+ self._obj_name = obj_name
+ self._name = name
+ self._modules = []
+
+ if not obj:
+ obj = capi.object_alloc_name(self._obj_name)
+
+ self._nl_object = obj
+
+ # Create a clone which stores the original state to notice
+ # modifications
+ clone_obj = capi.nl_object_clone(self._nl_object)
+ self._orig = self._obj2type(clone_obj)
+
+ def __del__(self):
+ if not self._nl_object:
+ raise ValueError()
+
+ capi.nl_object_put(self._nl_object)
+
+ def __str__(self):
+ if hasattr(self, 'format'):
+ return self.format()
+ else:
+ return capi.nl_object_dump_buf(self._nl_object, 4096).rstrip()
+
+ def _new_instance(self):
+ raise NotImplementedError()
+
+ def clone(self):
+ """Clone object"""
+ return self._new_instance(capi.nl_object_clone(self._nl_object))
+
+ def _module_lookup(self, path, constructor=None):
+ """Lookup object specific module and load it
+
+ Object implementations consisting of multiple types may
+ offload some type specific code to separate modules which
+ are loadable on demand, e.g. a VLAN link or a specific
+ queueing discipline implementation.
+
+ Loads the module `path` and calls the constructor if
+ supplied or `module`.init()
+
+ The constructor/init function typically assigns a new
+ object covering the type specific implementation aspects
+ to the new object, e.g. link.vlan = VLANLink()
+ """
+ try:
+ __import__(path)
+ except ImportError:
+ return
+
+ module = sys.modules[path]
+
+ if constructor:
+ ret = getattr(module, constructor)(self)
+ else:
+ ret = module.init(self)
+
+ if ret:
+ self._modules.append(ret)
+
+ def _module_brief(self):
+ ret = ''
+
+ for module in self._modules:
+ if hasattr(module, 'brief'):
+ ret += module.brief()
+
+ return ret
+
+ def dump(self, params=None):
+ """Dump object as human readable text"""
+ if params is None:
+ params = _defaultDumpParams
+
+ capi.nl_object_dump(self._nl_object, params._dp)
+
+
+ @property
+ def mark(self):
+ return bool(capi.nl_object_is_marked(self._nl_object))
+
+ @mark.setter
+ def mark(self, value):
+ if value:
+ capi.nl_object_mark(self._nl_object)
+ else:
+ capi.nl_object_unmark(self._nl_object)
+
+ @property
+ def shared(self):
+ return capi.nl_object_shared(self._nl_object) != 0
+
+ @property
+ def attrs(self):
+ attr_list = capi.nl_object_attr_list(self._nl_object, 1024)
+ return attr_list[0].split()
+
+ @property
+ def refcnt(self):
+ return capi.nl_object_get_refcnt(self._nl_object)
+
+ # this method resolves multiple levels of sub types to allow
+ # accessing properties of subclass/subtypes (e.g. link.vlan.id)
+ def _resolve(self, attr):
+ obj = self
+ l = attr.split('.')
+ while len(l) > 1:
+ obj = getattr(obj, l.pop(0))
+ return (obj, l.pop(0))
+
+ def _setattr(self, attr, val):
+ obj, attr = self._resolve(attr)
+ return setattr(obj, attr, val)
+
+ def _hasattr(self, attr):
+ obj, attr = self._resolve(attr)
+ return hasattr(obj, attr)
+
+class ObjIterator(object):
+ def __init__(self, cache, obj):
+ self._cache = cache
+ self._nl_object = None
+
+ if not obj:
+ self._end = 1
+ else:
+ capi.nl_object_get(obj)
+ self._nl_object = obj
+ self._first = 1
+ self._end = 0
+
+ def __del__(self):
+ if self._nl_object:
+ capi.nl_object_put(self._nl_object)
+
+ def __iter__(self):
+ return self
+
+ def get_next(self):
+ return capi.nl_cache_get_next(self._nl_object)
+
+ def next(self):
+ return self.__next__()
+
+ def __next__(self):
+ if self._end:
+ raise StopIteration()
+
+ if self._first:
+ ret = self._nl_object
+ self._first = 0
+ else:
+ ret = self.get_next()
+ if not ret:
+ self._end = 1
+ raise StopIteration()
+
+ # return ref of previous element and acquire ref of current
+ # element to have object stay around until we fetched the
+ # next ptr
+ capi.nl_object_put(self._nl_object)
+ capi.nl_object_get(ret)
+ self._nl_object = ret
+
+ # reference used inside object
+ capi.nl_object_get(ret)
+ return self._cache._new_object(ret)
+
+
+class ReverseObjIterator(ObjIterator):
+ def get_next(self):
+ return capi.nl_cache_get_prev(self._nl_object)
+
+class Cache(object):
+ """Collection of netlink objects"""
+ def __init__(self):
+ if self.__class__ is Cache:
+ raise NotImplementedError()
+ self.arg1 = None
+ self.arg2 = None
+
+ def __del__(self):
+ capi.nl_cache_free(self._nl_cache)
+
+ def __len__(self):
+ return capi.nl_cache_nitems(self._nl_cache)
+
+ def __iter__(self):
+ obj = capi.nl_cache_get_first(self._nl_cache)
+ return ObjIterator(self, obj)
+
+ def __reversed__(self):
+ obj = capi.nl_cache_get_last(self._nl_cache)
+ return ReverseObjIterator(self, obj)
+
+ def __contains__(self, item):
+ obj = capi.nl_cache_search(self._nl_cache, item._nl_object)
+ if obj is None:
+ return False
+ else:
+ capi.nl_object_put(obj)
+ return True
+
+ # called by sub classes to allocate type specific caches by name
+ @staticmethod
+ def _alloc_cache_name(name):
+ return capi.alloc_cache_name(name)
+
+ # implemented by sub classes, must return new instasnce of cacheable
+ # object
+ @staticmethod
+ def _new_object(obj):
+ raise NotImplementedError()
+
+ # implemented by sub classes, must return instance of sub class
+ def _new_cache(self, cache):
+ raise NotImplementedError()
+
+ def subset(self, filter_):
+ """Return new cache containing subset of cache
+
+ Cretes a new cache containing all objects which match the
+ specified filter.
+ """
+ if not filter_:
+ raise ValueError()
+
+ c = capi.nl_cache_subset(self._nl_cache, filter_._nl_object)
+ return self._new_cache(cache=c)
+
+ def dump(self, params=None, filter_=None):
+ """Dump (print) cache as human readable text"""
+ if not params:
+ params = _defaultDumpParams
+
+ if filter_:
+ filter_ = filter_._nl_object
+
+ capi.nl_cache_dump_filter(self._nl_cache, params._dp, filter_)
+
+ def clear(self):
+ """Remove all cache entries"""
+ capi.nl_cache_clear(self._nl_cache)
+
+ # Called by sub classes to set first cache argument
+ def _set_arg1(self, arg):
+ self.arg1 = arg
+ capi.nl_cache_set_arg1(self._nl_cache, arg)
+
+ # Called by sub classes to set second cache argument
+ def _set_arg2(self, arg):
+ self.arg2 = arg
+ capi.nl_cache_set_arg2(self._nl_cache, arg)
+
+ def refill(self, socket=None):
+ """Clear cache and refill it"""
+ if socket is None:
+ socket = lookup_socket(self._protocol)
+
+ capi.nl_cache_refill(socket._sock, self._nl_cache)
+ return self
+
+ def resync(self, socket=None, cb=None, args=None):
+ """Synchronize cache with content in kernel"""
+ if socket is None:
+ socket = lookup_socket(self._protocol)
+
+ capi.nl_cache_resync(socket._sock, self._nl_cache, cb, args)
+
+ def provide(self):
+ """Provide this cache to others
+
+ Caches which have been "provided" are made available
+ to other users (of the same application context) which
+ "require" it. F.e. a link cache is generally provided
+ to allow others to translate interface indexes to
+ link names
+ """
+
+ capi.nl_cache_mngt_provide(self._nl_cache)
+
+ def unprovide(self):
+ """Unprovide this cache
+
+ No longer make the cache available to others. If the cache
+ has been handed out already, that reference will still
+ be valid.
+ """
+ capi.nl_cache_mngt_unprovide(self._nl_cache)
+
+# Cache Manager (Work in Progress)
+NL_AUTO_PROVIDE = 1
+class CacheManager(object):
+ def __init__(self, protocol, flags=None):
+
+ self._sock = Socket()
+ self._sock.connect(protocol)
+
+ if not flags:
+ flags = NL_AUTO_PROVIDE
+
+ self._mngr = capi.cache_mngr_alloc(self._sock._sock, protocol, flags)
+
+ def __del__(self):
+ if self._sock:
+ self._sock.disconnect()
+
+ if self._mngr:
+ capi.nl_cache_mngr_free(self._mngr)
+
+ def add(self, name):
+ capi.cache_mngr_add(self._mngr, name, None, None)
+
+class AddressFamily(object):
+ """Address family representation
+
+ af = AddressFamily('inet6')
+ # raises:
+ # - ValueError if family name is not known
+ # - TypeError if invalid type is specified for family
+
+ print af # => 'inet6' (string representation)
+ print int(af) # => 10 (numeric representation)
+ print repr(af) # => AddressFamily('inet6')
+ """
+ def __init__(self, family=socket.AF_UNSPEC):
+ if isinstance(family, str):
+ family = capi.nl_str2af(family)
+ if family < 0:
+ raise ValueError('Unknown family name')
+ elif not isinstance(family, int):
+ raise TypeError()
+
+ self._family = family
+
+ def __str__(self):
+ return capi.nl_af2str(self._family, 32)[0]
+
+ def __int__(self):
+ return self._family
+
+ def __repr__(self):
+ return 'AddressFamily({0!r})'.format(str(self))
+
+
+class AbstractAddress(object):
+ """Abstract address object
+
+ addr = AbstractAddress('127.0.0.1/8')
+ print addr # => '127.0.0.1/8'
+ print addr.prefixlen # => '8'
+ print addr.family # => 'inet'
+ print len(addr) # => '4' (32bit ipv4 address)
+
+ a = AbstractAddress('10.0.0.1/24')
+ b = AbstractAddress('10.0.0.2/24')
+ print a == b # => False
+
+
+ """
+ def __init__(self, addr):
+ self._nl_addr = None
+
+ if isinstance(addr, str):
+ # returns None on success I guess
+ # TO CORRECT
+ addr = capi.addr_parse(addr, socket.AF_UNSPEC)
+ if addr is None:
+ raise ValueError('Invalid address format')
+ elif addr:
+ capi.nl_addr_get(addr)
+
+ self._nl_addr = addr
+
+ def __del__(self):
+ if self._nl_addr:
+ capi.nl_addr_put(self._nl_addr)
+
+ def __cmp__(self, other):
+ if isinstance(other, str):
+ other = AbstractAddress(other)
+
+ diff = self.prefixlen - other.prefixlen
+ if diff == 0:
+ diff = capi.nl_addr_cmp(self._nl_addr, other._nl_addr)
+
+ return diff
+
+ def contains(self, item):
+ diff = int(self.family) - int(item.family)
+ if diff:
+ return False
+
+ if item.prefixlen < self.prefixlen:
+ return False
+
+ diff = capi.nl_addr_cmp_prefix(self._nl_addr, item._nl_addr)
+ return diff == 0
+
+ def __nonzero__(self):
+ if self._nl_addr:
+ return not capi.nl_addr_iszero(self._nl_addr)
+ else:
+ return False
+
+ def __len__(self):
+ if self._nl_addr:
+ return capi.nl_addr_get_len(self._nl_addr)
+ else:
+ return 0
+
+ def __str__(self):
+ if self._nl_addr:
+ return capi.nl_addr2str(self._nl_addr, 64)[0]
+ else:
+ return 'none'
+
+ @property
+ def shared(self):
+ """True if address is shared (multiple users)"""
+ if self._nl_addr:
+ return capi.nl_addr_shared(self._nl_addr) != 0
+ else:
+ return False
+
+ @property
+ def prefixlen(self):
+ """Length of prefix (number of bits)"""
+ if self._nl_addr:
+ return capi.nl_addr_get_prefixlen(self._nl_addr)
+ else:
+ return 0
+
+ @prefixlen.setter
+ def prefixlen(self, value):
+ if not self._nl_addr:
+ raise TypeError()
+
+ capi.nl_addr_set_prefixlen(self._nl_addr, int(value))
+
+ @property
+ def family(self):
+ """Address family"""
+ f = 0
+ if self._nl_addr:
+ f = capi.nl_addr_get_family(self._nl_addr)
+
+ return AddressFamily(f)
+
+ @family.setter
+ def family(self, value):
+ if not self._nl_addr:
+ raise TypeError()
+
+ if not isinstance(value, AddressFamily):
+ value = AddressFamily(value)
+
+ capi.nl_addr_set_family(self._nl_addr, int(value))
+
+
+# keyword:
+# type = { int | str }
+# immutable = { True | False }
+# fmt = func (formatting function)
+# title = string
+
+def nlattr(**kwds):
+ """netlink object attribute decorator
+
+ decorator used to mark mutable and immutable properties
+ of netlink objects. All properties marked as such are
+ regarded to be accessable.
+
+ @property
+ @netlink.nlattr(type=int)
+ def my_attr(self):
+ return self._my_attr
+
+ """
+
+ def wrap_fn(func):
+ func.formatinfo = kwds
+ return func
+ return wrap_fn
diff --git a/python/netlink/fixes.h b/python/netlink/fixes.h
new file mode 100644
index 00000000..9a6118bd
--- /dev/null
+++ b/python/netlink/fixes.h
@@ -0,0 +1 @@
+#include <stdint.h>
diff --git a/python/netlink/genl/Makefile.am b/python/netlink/genl/Makefile.am
new file mode 100644
index 00000000..9e309047
--- /dev/null
+++ b/python/netlink/genl/Makefile.am
@@ -0,0 +1,5 @@
+# -*- Makefile -*-
+
+EXTRA_DIST = \
+ capi.i \
+ __init__.py
diff --git a/python/netlink/genl/__init__.py b/python/netlink/genl/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/python/netlink/genl/__init__.py
diff --git a/python/netlink/genl/capi.i b/python/netlink/genl/capi.i
new file mode 100644
index 00000000..069e617e
--- /dev/null
+++ b/python/netlink/genl/capi.i
@@ -0,0 +1,108 @@
+%module capi
+%{
+#include <netlink/genl/ctrl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/mngt.h>
+%}
+
+%include <stdint.i>
+%include <cstring.i>
+
+/* #include <netlink/genl/ctrl.h> */
+extern int genl_ctrl_alloc_cache(struct nl_sock *, struct nl_cache **o_cache);
+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_sock *, const char *);
+extern int genl_ctrl_resolve_grp(struct nl_sock *sk, const char *family,
+ const char *grp);
+
+/* #include <netlink/genl/family.h> */
+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);
+extern int genl_family_add_grp(struct genl_family *, uint32_t , const char *);
+
+/* #include <netlink/genl/genl.h> */
+extern int genl_connect(struct nl_sock *);
+
+extern void *genlmsg_put(struct nl_msg *, uint32_t, uint32_t,
+ int, int, int, uint8_t, uint8_t);
+
+struct nlattr {
+};
+
+struct nla_policy {
+ /** Type of attribute or NLA_UNSPEC */
+ uint16_t type;
+
+ /** Minimal length of payload required */
+ uint16_t minlen;
+
+ /** Maximal length of payload allowed */
+ uint16_t maxlen;
+};
+
+%inline %{
+PyObject *py_genlmsg_parse(struct nlmsghdr *nlh, int uhl, int max,
+ PyObject *p)
+{
+ struct nlattr *tb_msg[max + 1];
+ struct nla_policy *policy = NULL;
+ void *pol;
+ PyObject *attrs = Py_None;
+ PyObject *k;
+ PyObject *v;
+ PyObject *resobj;
+ int err;
+ int i;
+
+ if (p != Py_None) {
+ PyObject *pobj;
+
+ if (!PyList_Check(p)) {
+ fprintf(stderr, "expected list object\n");
+ err = -1;
+ goto fail;
+ }
+ pobj = PyList_GetItem(p, 0);
+ err = SWIG_ConvertPtr(pobj, &pol, SWIGTYPE_p_nla_policy, 0 | 0 );
+ if (!SWIG_IsOK(err))
+ goto fail;
+ policy = pol;
+ }
+ err = genlmsg_parse(nlh, uhl, tb_msg, max, policy);
+ if (err < 0) {
+ fprintf(stderr, "Failed to parse response message\n");
+ } else {
+ attrs = PyDict_New();
+ for (i = 0; i <= max; i++)
+ if (tb_msg[i]) {
+ k = PyInt_FromLong((long)i);
+ v = SWIG_NewPointerObj(SWIG_as_voidptr(tb_msg[i]), SWIGTYPE_p_nlattr, 0 | 0 );
+ PyDict_SetItem(attrs, k, v);
+ }
+ }
+fail:
+ if (attrs == Py_None)
+ Py_INCREF(Py_None);
+ resobj = Py_BuildValue("(iO)", err, attrs);
+ return resobj;
+}
+
+%}
+/* #include <netlink/genl/mngt.h> */
+/* nothing yet */
diff --git a/python/netlink/route/Makefile.am b/python/netlink/route/Makefile.am
new file mode 100644
index 00000000..ef714f45
--- /dev/null
+++ b/python/netlink/route/Makefile.am
@@ -0,0 +1,14 @@
+# -*- Makefile -*-
+
+EXTRA_DIST = \
+ capi.i \
+ __init__.py \
+ address.py \
+ link.py \
+ tc.py \
+ links/__init__.py \
+ links/dummy.py \
+ links/inet.py \
+ links/vlan.py \
+ qdisc/__init__.py \
+ qdisc/htb.py
diff --git a/python/netlink/route/__init__.py b/python/netlink/route/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/python/netlink/route/__init__.py
diff --git a/python/netlink/route/address.py b/python/netlink/route/address.py
new file mode 100644
index 00000000..cab2a97b
--- /dev/null
+++ b/python/netlink/route/address.py
@@ -0,0 +1,367 @@
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""Module providing access to network addresses
+"""
+
+from __future__ import absolute_import
+
+
+__version__ = '1.0'
+__all__ = [
+ 'AddressCache',
+ 'Address']
+
+import datetime
+from .. import core as netlink
+from . import capi as capi
+from . import link as Link
+from .. import util as util
+
+class AddressCache(netlink.Cache):
+ """Cache containing network addresses"""
+
+ def __init__(self, cache=None):
+ if not cache:
+ cache = self._alloc_cache_name('route/addr')
+
+ self._protocol = netlink.NETLINK_ROUTE
+ self._nl_cache = cache
+
+ def __getitem__(self, key):
+ # Using ifindex=0 here implies that the local address itself
+ # is unique, otherwise the first occurence is returned.
+ return self.lookup(0, key)
+
+ def lookup(self, ifindex, local):
+ if type(local) is str:
+ local = netlink.AbstractAddress(local)
+
+ addr = capi.rtnl_addr_get(self._nl_cache, ifindex,
+ local._nl_addr)
+ if addr is None:
+ raise KeyError()
+
+ return Address._from_capi(addr)
+
+ @staticmethod
+ def _new_object(obj):
+ return Address(obj)
+
+ @staticmethod
+ def _new_cache(cache):
+ return AddressCache(cache=cache)
+
+class Address(netlink.Object):
+ """Network address"""
+
+ def __init__(self, obj=None):
+ netlink.Object.__init__(self, 'route/addr', 'address', obj)
+ self._rtnl_addr = self._obj2type(self._nl_object)
+
+ @classmethod
+ def _from_capi(cls, obj):
+ return cls(capi.addr2obj(obj))
+
+ @staticmethod
+ def _obj2type(obj):
+ return capi.obj2addr(obj)
+
+ def __cmp__(self, other):
+ # sort by:
+ # 1. network link
+ # 2. address family
+ # 3. local address (including prefixlen)
+ diff = self.ifindex - other.ifindex
+
+ if diff == 0:
+ diff = self.family - other.family
+ if diff == 0:
+ diff = capi.nl_addr_cmp(self.local, other.local)
+
+ return diff
+
+ @staticmethod
+ def _new_instance(obj):
+ return Address(obj)
+
+ @property
+ @netlink.nlattr(type=int, immutable=True, fmt=util.num)
+ def ifindex(self):
+ """interface index"""
+ return capi.rtnl_addr_get_ifindex(self._rtnl_addr)
+
+ @ifindex.setter
+ def ifindex(self, value):
+ link = Link.resolve(value)
+ if not link:
+ raise ValueError()
+
+ self.link = link
+
+ @property
+ @netlink.nlattr(type=str, fmt=util.string)
+ def link(self):
+ link = capi.rtnl_addr_get_link(self._rtnl_addr)
+ if not link:
+ return None
+
+ return Link.Link.from_capi(link)
+
+ @link.setter
+ def link(self, value):
+ if type(value) is str:
+ try:
+ value = Link.resolve(value)
+ except KeyError:
+ raise ValueError()
+
+ capi.rtnl_addr_set_link(self._rtnl_addr, value._rtnl_link)
+
+ # ifindex is immutable but we assume that if _orig does not
+ # have an ifindex specified, it was meant to be given here
+ if capi.rtnl_addr_get_ifindex(self._orig) == 0:
+ capi.rtnl_addr_set_ifindex(self._orig, value.ifindex)
+
+ @property
+ @netlink.nlattr(type=str, fmt=util.string)
+ def label(self):
+ """address label"""
+ return capi.rtnl_addr_get_label(self._rtnl_addr)
+
+ @label.setter
+ def label(self, value):
+ capi.rtnl_addr_set_label(self._rtnl_addr, value)
+
+ @property
+ @netlink.nlattr(type=str, fmt=util.string)
+ def flags(self):
+ """Flags
+
+ Setting this property will *Not* reset flags to value you supply in
+
+ Examples:
+ addr.flags = '+xxx' # add xxx flag
+ addr.flags = 'xxx' # exactly the same
+ addr.flags = '-xxx' # remove xxx flag
+ addr.flags = [ '+xxx', '-yyy' ] # list operation
+ """
+ flags = capi.rtnl_addr_get_flags(self._rtnl_addr)
+ return capi.rtnl_addr_flags2str(flags, 256)[0].split(',')
+
+ def _set_flag(self, flag):
+ if flag.startswith('-'):
+ i = capi.rtnl_addr_str2flags(flag[1:])
+ capi.rtnl_addr_unset_flags(self._rtnl_addr, i)
+ elif flag.startswith('+'):
+ i = capi.rtnl_addr_str2flags(flag[1:])
+ capi.rtnl_addr_set_flags(self._rtnl_addr, i)
+ else:
+ i = capi.rtnl_addr_str2flags(flag)
+ capi.rtnl_addr_set_flags(self._rtnl_addr, i)
+
+ @flags.setter
+ def flags(self, value):
+ if type(value) is list:
+ for flag in value:
+ self._set_flag(flag)
+ else:
+ self._set_flag(value)
+
+ @property
+ @netlink.nlattr(type=int, immutable=True, fmt=util.num)
+ def family(self):
+ """Address family"""
+ fam = capi.rtnl_addr_get_family(self._rtnl_addr)
+ return netlink.AddressFamily(fam)
+
+ @family.setter
+ def family(self, value):
+ if not isinstance(value, netlink.AddressFamily):
+ value = netlink.AddressFamily(value)
+
+ capi.rtnl_addr_set_family(self._rtnl_addr, int(value))
+
+ @property
+ @netlink.nlattr(type=int, fmt=util.num)
+ def scope(self):
+ """Address scope"""
+ scope = capi.rtnl_addr_get_scope(self._rtnl_addr)
+ return capi.rtnl_scope2str(scope, 32)[0]
+
+ @scope.setter
+ def scope(self, value):
+ if type(value) is str:
+ value = capi.rtnl_str2scope(value)
+ capi.rtnl_addr_set_scope(self._rtnl_addr, value)
+
+ @property
+ @netlink.nlattr(type=str, immutable=True, fmt=util.addr)
+ def local(self):
+ """Local address"""
+ a = capi.rtnl_addr_get_local(self._rtnl_addr)
+ return netlink.AbstractAddress(a)
+
+ @local.setter
+ def local(self, value):
+ a = netlink.AbstractAddress(value)
+ capi.rtnl_addr_set_local(self._rtnl_addr, a._nl_addr)
+
+ # local is immutable but we assume that if _orig does not
+ # have a local address specified, it was meant to be given here
+ if capi.rtnl_addr_get_local(self._orig) is None:
+ capi.rtnl_addr_set_local(self._orig, a._nl_addr)
+
+ @property
+ @netlink.nlattr(type=str, fmt=util.addr)
+ def peer(self):
+ """Peer address"""
+ a = capi.rtnl_addr_get_peer(self._rtnl_addr)
+ return netlink.AbstractAddress(a)
+
+ @peer.setter
+ def peer(self, value):
+ a = netlink.AbstractAddress(value)
+ capi.rtnl_addr_set_peer(self._rtnl_addr, a._nl_addr)
+
+ @property
+ @netlink.nlattr(type=str, fmt=util.addr)
+ def broadcast(self):
+ """Broadcast address"""
+ a = capi.rtnl_addr_get_broadcast(self._rtnl_addr)
+ return netlink.AbstractAddress(a)
+
+ @broadcast.setter
+ def broadcast(self, value):
+ a = netlink.AbstractAddress(value)
+ capi.rtnl_addr_set_broadcast(self._rtnl_addr, a._nl_addr)
+
+ @property
+ @netlink.nlattr(type=str, fmt=util.addr)
+ def multicast(self):
+ """multicast address"""
+ a = capi.rtnl_addr_get_multicast(self._rtnl_addr)
+ return netlink.AbstractAddress(a)
+
+ @multicast.setter
+ def multicast(self, value):
+ try:
+ a = netlink.AbstractAddress(value)
+ except ValueError as err:
+ raise AttributeError('multicast', err)
+
+ capi.rtnl_addr_set_multicast(self._rtnl_addr, a._nl_addr)
+
+ @property
+ @netlink.nlattr(type=str, fmt=util.addr)
+ def anycast(self):
+ """anycast address"""
+ a = capi.rtnl_addr_get_anycast(self._rtnl_addr)
+ return netlink.AbstractAddress(a)
+
+ @anycast.setter
+ def anycast(self, value):
+ a = netlink.AbstractAddress(value)
+ capi.rtnl_addr_set_anycast(self._rtnl_addr, a._nl_addr)
+
+ @property
+ @netlink.nlattr(type=int, immutable=True, fmt=util.num)
+ def valid_lifetime(self):
+ """Valid lifetime"""
+ msecs = capi.rtnl_addr_get_valid_lifetime(self._rtnl_addr)
+ if msecs == 0xFFFFFFFF:
+ return None
+ else:
+ return datetime.timedelta(seconds=msecs)
+
+ @valid_lifetime.setter
+ def valid_lifetime(self, value):
+ capi.rtnl_addr_set_valid_lifetime(self._rtnl_addr, int(value))
+
+ @property
+ @netlink.nlattr(type=int, immutable=True, fmt=util.num)
+ def preferred_lifetime(self):
+ """Preferred lifetime"""
+ msecs = capi.rtnl_addr_get_preferred_lifetime(self._rtnl_addr)
+ if msecs == 0xFFFFFFFF:
+ return None
+ else:
+ return datetime.timedelta(seconds=msecs)
+
+ @preferred_lifetime.setter
+ def preferred_lifetime(self, value):
+ capi.rtnl_addr_set_preferred_lifetime(self._rtnl_addr, int(value))
+
+ @property
+ @netlink.nlattr(type=int, immutable=True, fmt=util.num)
+ def create_time(self):
+ """Creation time"""
+ hsec = capi.rtnl_addr_get_create_time(self._rtnl_addr)
+ return datetime.timedelta(milliseconds=10*hsec)
+
+ @property
+ @netlink.nlattr(type=int, immutable=True, fmt=util.num)
+ def last_update(self):
+ """Last update"""
+ hsec = capi.rtnl_addr_get_last_update_time(self._rtnl_addr)
+ return datetime.timedelta(milliseconds=10*hsec)
+
+ def add(self, socket=None, flags=None):
+ if not socket:
+ socket = netlink.lookup_socket(netlink.NETLINK_ROUTE)
+
+ if not flags:
+ flags = netlink.NLM_F_CREATE
+
+ ret = capi.rtnl_addr_add(socket._sock, self._rtnl_addr, flags)
+ if ret < 0:
+ raise netlink.KernelError(ret)
+
+ def delete(self, socket, flags=0):
+ """Attempt to delete this address in the kernel"""
+ ret = capi.rtnl_addr_delete(socket._sock, self._rtnl_addr, flags)
+ if ret < 0:
+ raise netlink.KernelError(ret)
+
+ ###################################################################
+ # private properties
+ #
+ # Used for formatting output. USE AT OWN RISK
+ @property
+ def _flags(self):
+ return ','.join(self.flags)
+
+ def format(self, details=False, stats=False, nodev=False, indent=''):
+ """Return address as formatted text"""
+ fmt = util.MyFormatter(self, indent)
+
+ buf = fmt.format('{a|local!b}')
+
+ if not nodev:
+ buf += fmt.format(' {a|ifindex}')
+
+ buf += fmt.format(' {a|scope}')
+
+ if self.label:
+ buf += fmt.format(' "{a|label}"')
+
+ buf += fmt.format(' <{a|_flags}>')
+
+ if details:
+ buf += fmt.nl('\t{t|broadcast} {t|multicast}') \
+ + fmt.nl('\t{t|peer} {t|anycast}')
+
+ if self.valid_lifetime:
+ buf += fmt.nl('\t{s|valid-lifetime!k} '\
+ '{a|valid_lifetime}')
+
+ if self.preferred_lifetime:
+ buf += fmt.nl('\t{s|preferred-lifetime!k} '\
+ '{a|preferred_lifetime}')
+
+ if stats and (self.create_time or self.last_update):
+ buf += self.nl('\t{s|created!k} {a|create_time}'\
+ ' {s|last-updated!k} {a|last_update}')
+
+ return buf
diff --git a/python/netlink/route/capi.i b/python/netlink/route/capi.i
new file mode 100644
index 00000000..2d72bd72
--- /dev/null
+++ b/python/netlink/route/capi.i
@@ -0,0 +1,507 @@
+%module capi
+%{
+#include <netlink/route/rtnl.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/vlan.h>
+#include <netlink/route/link/macvlan.h>
+#include <netlink/route/link/vxlan.h>
+#include <netlink/route/link/bridge.h>
+#include <netlink/route/link/inet.h>
+
+#include <netlink/route/tc.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/class.h>
+#include <netlink/route/classifier.h>
+
+#include <netlink/route/qdisc/htb.h>
+
+#include <netlink/route/addr.h>
+%}
+
+%include <stdint.i>
+%include <cstring.i>
+
+%inline %{
+ struct nl_object *link2obj(struct rtnl_link *link)
+ {
+ return OBJ_CAST(link);
+ }
+
+ struct rtnl_link *obj2link(struct nl_object *obj)
+ {
+ return (struct rtnl_link *) obj;
+ }
+
+ struct rtnl_link *get_from_kernel(struct nl_sock *sk, int ifindex, const char *name)
+ {
+ struct rtnl_link *link;
+ if (rtnl_link_get_kernel(sk, ifindex, name, &link) < 0)
+ return NULL;
+ return link;
+ }
+
+ uint32_t inet_get_conf(struct rtnl_link *link, const unsigned int id)
+ {
+ uint32_t result;
+
+ if (rtnl_link_inet_get_conf(link, id, &result) < 0)
+ return 0;
+
+ return result;
+ }
+%};
+
+extern struct nl_object *link2obj(struct rtnl_link *);
+extern struct rtnl_link *obj2link(struct nl_object *);
+
+/* <netlink/route/rtnl.h> */
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char * rtnl_scope2str(int, char *buf, size_t len);
+extern int rtnl_str2scope(const char *);
+
+/* <netlink/route/link.h> */
+
+extern struct rtnl_link *rtnl_link_alloc(void);
+
+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 *);
+
+extern int rtnl_link_build_add_request(struct rtnl_link *, int, struct nl_msg **);
+extern int rtnl_link_add(struct nl_sock *, struct rtnl_link *, int);
+extern int rtnl_link_build_change_request(struct rtnl_link *, struct rtnl_link *, int, struct nl_msg **);
+extern int rtnl_link_change(struct nl_sock *, struct rtnl_link *, struct rtnl_link *, int);
+
+extern int rtnl_link_build_delete_request(const struct rtnl_link *, struct nl_msg **);
+extern int rtnl_link_delete(struct nl_sock *, const struct rtnl_link *);
+extern int rtnl_link_build_get_request(int, const char *, struct nl_msg **);
+
+extern char *rtnl_link_stat2str(int, char *, size_t);
+extern int rtnl_link_str2stat(const char *);
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char *rtnl_link_flags2str(int, char *buf, size_t len);
+extern int rtnl_link_str2flags(const char *);
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char *rtnl_link_operstate2str(uint8_t, char *buf, size_t len);
+extern int rtnl_link_str2operstate(const char *);
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char *rtnl_link_mode2str(uint8_t, char *buf, size_t len);
+extern int rtnl_link_str2mode(const char *);
+
+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_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 void rtnl_link_set_operstate(struct rtnl_link *, uint8_t);
+extern uint8_t rtnl_link_get_operstate(struct rtnl_link *);
+
+extern void rtnl_link_set_linkmode(struct rtnl_link *, uint8_t);
+extern uint8_t rtnl_link_get_linkmode(struct rtnl_link *);
+
+extern const char *rtnl_link_get_ifalias(struct rtnl_link *);
+extern void rtnl_link_set_ifalias(struct rtnl_link *, const char *);
+
+extern int rtnl_link_get_num_vf(struct rtnl_link *, uint32_t *);
+
+extern uint64_t rtnl_link_get_stat(struct rtnl_link *, int);
+extern int rtnl_link_set_stat(struct rtnl_link *, const unsigned int, const uint64_t);
+
+extern int rtnl_link_set_type(struct rtnl_link *, const char *);
+extern char *rtnl_link_get_type(struct rtnl_link *);
+
+extern int rtnl_link_enslave(struct nl_sock * sock, struct rtnl_link * master, struct rtnl_link * slave);
+extern int rtnl_link_release(struct nl_sock * sock, struct rtnl_link * slave);
+
+/* <netlink/route/link/vlan.h> */
+
+struct vlan_map
+{
+ uint32_t vm_from;
+ uint32_t vm_to;
+};
+
+#define VLAN_PRIO_MAX 7
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char *rtnl_link_vlan_flags2str(int, char *buf, size_t len);
+extern int rtnl_link_vlan_str2flags(const char *);
+
+extern int rtnl_link_vlan_set_id(struct rtnl_link *, int);
+extern int rtnl_link_vlan_get_id(struct rtnl_link *);
+
+extern int rtnl_link_vlan_set_flags(struct rtnl_link *, unsigned int);
+extern int rtnl_link_vlan_unset_flags(struct rtnl_link *, unsigned int);
+extern unsigned int rtnl_link_vlan_get_flags(struct rtnl_link *);
+
+extern int rtnl_link_vlan_set_ingress_map(struct rtnl_link *, int, uint32_t);
+extern uint32_t *rtnl_link_vlan_get_ingress_map(struct rtnl_link *);
+
+extern int rtnl_link_vlan_set_egress_map(struct rtnl_link *, uint32_t, int);
+extern struct vlan_map *rtnl_link_vlan_get_egress_map(struct rtnl_link *, int *);
+
+/* <netlink/route/link/macvlan.h> */
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern struct rtnl_link *rtnl_link_macvlan_alloc(void);
+extern int rtnl_link_is_macvlan(struct rtnl_link *);
+extern char * rtnl_link_macvlan_mode2str(int, char *, size_t);
+extern int rtnl_link_macvlan_str2mode(const char *);
+extern char * rtnl_link_macvlan_flags2str(int, char *, size_t);
+extern int rtnl_link_macvlan_str2flags(const char *);
+extern int rtnl_link_macvlan_set_mode(struct rtnl_link *, uint32_t);
+extern uint32_t rtnl_link_macvlan_get_mode(struct rtnl_link *);
+extern int rtnl_link_macvlan_set_flags(struct rtnl_link *, uint16_t);
+extern int rtnl_link_macvlan_unset_flags(struct rtnl_link *, uint16_t);
+extern uint16_t rtnl_link_macvlan_get_flags(struct rtnl_link *);
+
+/* <netlink/route/link/vxlan.h> */
+
+#define VXLAN_ID_MAX 16777215
+
+extern struct rtnl_link *rtnl_link_vxlan_alloc(void);
+
+extern int rtnl_link_is_vxlan(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_id(struct rtnl_link *, uint32_t);
+extern int rtnl_link_vxlan_get_id(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_vxlan_set_group(struct rtnl_link *, struct nl_addr *);
+extern int rtnl_link_vxlan_get_group(struct rtnl_link *, struct nl_addr **);
+
+extern int rtnl_link_vxlan_set_link(struct rtnl_link *, uint32_t);
+extern int rtnl_link_vxlan_get_link(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_vxlan_set_local(struct rtnl_link *, struct nl_addr *);
+extern int rtnl_link_vxlan_get_local(struct rtnl_link *, struct nl_addr **);
+
+extern int rtnl_link_vxlan_set_ttl(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_ttl(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_tos(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_tos(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_learning(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_learning(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_learning(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_learning(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_ageing(struct rtnl_link *, uint32_t);
+extern int rtnl_link_vxlan_get_ageing(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_vxlan_set_limit(struct rtnl_link *, uint32_t);
+extern int rtnl_link_vxlan_get_limit(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_vxlan_set_port_range(struct rtnl_link *,
+ struct ifla_vxlan_port_range *);
+extern int rtnl_link_vxlan_get_port_range(struct rtnl_link *,
+ struct ifla_vxlan_port_range *);
+
+extern int rtnl_link_vxlan_set_proxy(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_proxy(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_proxy(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_proxy(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_rsc(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_rsc(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_rsc(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_rsc(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_l2miss(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_l2miss(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_l2miss(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_l2miss(struct rtnl_link *);
+
+extern int rtnl_link_vxlan_set_l3miss(struct rtnl_link *, uint8_t);
+extern int rtnl_link_vxlan_get_l3miss(struct rtnl_link *);
+extern int rtnl_link_vxlan_enable_l3miss(struct rtnl_link *);
+extern int rtnl_link_vxlan_disable_l3miss(struct rtnl_link *);
+
+/* <netlink/route/link/bridge.h> */
+
+enum rtnl_link_bridge_flags {
+ RTNL_BRIDGE_HAIRPIN_MODE = 0x0001,
+ RTNL_BRIDGE_BPDU_GUARD = 0x0002,
+ RTNL_BRIDGE_ROOT_BLOCK = 0x0004,
+ RTNL_BRIDGE_FAST_LEAVE = 0x0008,
+};
+
+extern int rtnl_link_is_bridge(struct rtnl_link *);
+extern int rtnl_link_bridge_has_ext_info(struct rtnl_link *);
+
+extern int rtnl_link_bridge_set_port_state(struct rtnl_link *, uint8_t );
+extern int rtnl_link_bridge_get_port_state(struct rtnl_link *);
+
+extern int rtnl_link_bridge_set_priority(struct rtnl_link *, uint16_t);
+extern int rtnl_link_bridge_get_priority(struct rtnl_link *);
+
+extern int rtnl_link_bridge_set_cost(struct rtnl_link *, uint32_t);
+extern int rtnl_link_bridge_get_cost(struct rtnl_link *, uint32_t *);
+
+extern int rtnl_link_bridge_unset_flags(struct rtnl_link *, unsigned int);
+extern int rtnl_link_bridge_set_flags(struct rtnl_link *, unsigned int);
+extern int rtnl_link_bridge_get_flags(struct rtnl_link *);
+
+extern char * rtnl_link_bridge_flags2str(int, char *, size_t);
+extern int rtnl_link_bridge_str2flags(const char *);
+
+/* <netlink/route/link/inet.h> */
+%cstring_output_maxsize(char *buf, size_t len)
+extern const char *rtnl_link_inet_devconf2str(int, char *buf, size_t len);
+extern unsigned int rtnl_link_inet_str2devconf(const char *);
+
+extern int rtnl_link_inet_set_conf(struct rtnl_link *, const unsigned int, uint32_t);
+
+/* <netlink/route/tc.h> */
+
+%inline %{
+ uint32_t tc_str2handle(const char *name)
+ {
+ uint32_t result;
+
+ if (rtnl_tc_str2handle(name, &result) < 0)
+ return 0;
+
+ return result;
+ }
+%};
+
+extern void rtnl_tc_set_ifindex(struct rtnl_tc *, int);
+extern int rtnl_tc_get_ifindex(struct rtnl_tc *);
+extern void rtnl_tc_set_link(struct rtnl_tc *, struct rtnl_link *);
+extern struct rtnl_link *rtnl_tc_get_link(struct rtnl_tc *);
+extern void rtnl_tc_set_mtu(struct rtnl_tc *, uint32_t);
+extern uint32_t rtnl_tc_get_mtu(struct rtnl_tc *);
+extern void rtnl_tc_set_mpu(struct rtnl_tc *, uint32_t);
+extern uint32_t rtnl_tc_get_mpu(struct rtnl_tc *);
+extern void rtnl_tc_set_overhead(struct rtnl_tc *, uint32_t);
+extern uint32_t rtnl_tc_get_overhead(struct rtnl_tc *);
+extern void rtnl_tc_set_linktype(struct rtnl_tc *, uint32_t);
+extern uint32_t rtnl_tc_get_linktype(struct rtnl_tc *);
+extern void rtnl_tc_set_handle(struct rtnl_tc *, uint32_t);
+extern uint32_t rtnl_tc_get_handle(struct rtnl_tc *);
+extern void rtnl_tc_set_parent(struct rtnl_tc *, uint32_t);
+extern uint32_t rtnl_tc_get_parent(struct rtnl_tc *);
+extern int rtnl_tc_set_kind(struct rtnl_tc *, const char *);
+extern char * rtnl_tc_get_kind(struct rtnl_tc *);
+extern uint64_t rtnl_tc_get_stat(struct rtnl_tc *, enum rtnl_tc_stat);
+
+extern int rtnl_tc_calc_txtime(int, int);
+extern int rtnl_tc_calc_bufsize(int, int);
+extern int rtnl_tc_calc_cell_log(int);
+
+extern int rtnl_tc_read_classid_file(void);
+%cstring_output_maxsize(char *buf, size_t len)
+extern char * rtnl_tc_handle2str(uint32_t, char *buf, size_t len);
+extern int rtnl_classid_generate(const char *, uint32_t *, uint32_t);
+
+/* <netlink/route/qdisc.h> */
+
+%inline %{
+ struct nl_object *qdisc2obj(struct rtnl_qdisc *qdisc)
+ {
+ return OBJ_CAST(qdisc);
+ }
+
+ struct rtnl_qdisc *obj2qdisc(struct nl_object *obj)
+ {
+ return (struct rtnl_qdisc *) obj;
+ }
+
+ struct nl_object *class2obj(struct rtnl_class *cl)
+ {
+ return OBJ_CAST(cl);
+ }
+
+ struct rtnl_class *obj2class(struct nl_object *obj)
+ {
+ return (struct rtnl_class *) obj;
+ }
+
+ struct nl_object *cls2obj(struct rtnl_cls *cls)
+ {
+ return OBJ_CAST(cls);
+ }
+
+ struct rtnl_cls *obj2cls(struct nl_object *obj)
+ {
+ return (struct rtnl_cls *) obj;
+ }
+
+ struct rtnl_tc *obj2tc(struct nl_object *obj)
+ {
+ return TC_CAST(obj);
+ }
+%};
+extern struct rtnl_qdisc *
+ rtnl_qdisc_alloc(void);
+
+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);
+
+extern int rtnl_qdisc_build_add_request(struct rtnl_qdisc *, int,
+ struct nl_msg **);
+extern int rtnl_qdisc_add(struct nl_sock *, struct rtnl_qdisc *, int);
+
+extern int rtnl_qdisc_build_update_request(struct rtnl_qdisc *,
+ struct rtnl_qdisc *,
+ int, struct nl_msg **);
+
+extern int rtnl_qdisc_update(struct nl_sock *, struct rtnl_qdisc *,
+ struct rtnl_qdisc *, int);
+
+extern int rtnl_qdisc_build_delete_request(struct rtnl_qdisc *,
+ struct nl_msg **);
+extern int rtnl_qdisc_delete(struct nl_sock *, struct rtnl_qdisc *);
+
+/* <netlink/route/classifier.h> */
+
+extern struct rtnl_cls *rtnl_cls_alloc(void);
+extern void rtnl_cls_put(struct rtnl_cls *);
+
+extern int rtnl_cls_add(struct nl_sock *, struct rtnl_cls *, int);
+
+extern int rtnl_cls_delete(struct nl_sock *, struct rtnl_cls *,
+ int);
+
+extern void rtnl_cls_set_prio(struct rtnl_cls *, uint16_t);
+extern uint16_t rtnl_cls_get_prio(struct rtnl_cls *);
+
+extern void rtnl_cls_set_protocol(struct rtnl_cls *, uint16_t);
+extern uint16_t rtnl_cls_get_protocol(struct rtnl_cls *);
+
+/* <netlink/route/qdisc/htb.h> */
+
+extern uint32_t rtnl_htb_get_rate2quantum(struct rtnl_qdisc *);
+extern int rtnl_htb_set_rate2quantum(struct rtnl_qdisc *, uint32_t);
+extern uint32_t rtnl_htb_get_defcls(struct rtnl_qdisc *);
+extern int rtnl_htb_set_defcls(struct rtnl_qdisc *, uint32_t);
+
+extern uint32_t rtnl_htb_get_prio(struct rtnl_class *);
+extern int rtnl_htb_set_prio(struct rtnl_class *, uint32_t);
+extern uint32_t rtnl_htb_get_rate(struct rtnl_class *);
+extern int rtnl_htb_set_rate(struct rtnl_class *, uint32_t);
+extern uint32_t rtnl_htb_get_ceil(struct rtnl_class *);
+extern int rtnl_htb_set_ceil(struct rtnl_class *, uint32_t);
+extern uint32_t rtnl_htb_get_rbuffer(struct rtnl_class *);
+extern int rtnl_htb_set_rbuffer(struct rtnl_class *, uint32_t);
+extern uint32_t rtnl_htb_get_cbuffer(struct rtnl_class *);
+extern int rtnl_htb_set_cbuffer(struct rtnl_class *, uint32_t);
+extern uint32_t rtnl_htb_get_quantum(struct rtnl_class *);
+extern int rtnl_htb_set_quantum(struct rtnl_class *, uint32_t);
+extern int rtnl_htb_get_level(struct rtnl_class *);
+
+/* <netlink/route/addr.h> */
+
+%inline %{
+ struct nl_object *addr2obj(struct rtnl_addr *addr)
+ {
+ return OBJ_CAST(addr);
+ }
+
+ struct rtnl_addr *obj2addr(struct nl_object *obj)
+ {
+ return (struct rtnl_addr *) obj;
+ }
+%};
+
+extern struct rtnl_addr *rtnl_addr_alloc(void);
+
+extern struct rtnl_addr *
+ rtnl_addr_get(struct nl_cache *, int, struct nl_addr *);
+
+extern int rtnl_addr_build_add_request(struct rtnl_addr *, int,
+ struct nl_msg **);
+extern int rtnl_addr_add(struct nl_sock *, struct rtnl_addr *, int);
+
+extern int rtnl_addr_build_delete_request(struct rtnl_addr *, int,
+ struct nl_msg **);
+extern int rtnl_addr_delete(struct nl_sock *,
+ struct rtnl_addr *, int);
+
+%cstring_output_maxsize(char *buf, size_t len)
+extern char * rtnl_addr_flags2str(int, char *buf, size_t len);
+extern int rtnl_addr_str2flags(const char *);
+
+extern int 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_link(struct rtnl_addr *, struct rtnl_link *);
+extern struct rtnl_link *
+ rtnl_addr_get_link(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_multicast(struct rtnl_addr *, struct nl_addr *);
+extern struct nl_addr *rtnl_addr_get_multicast(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 uint32_t rtnl_addr_get_valid_lifetime(struct rtnl_addr *);
+extern void rtnl_addr_set_valid_lifetime(struct rtnl_addr *, uint32_t);
+extern uint32_t rtnl_addr_get_preferred_lifetime(struct rtnl_addr *);
+extern void rtnl_addr_set_preferred_lifetime(struct rtnl_addr *, uint32_t);
+extern uint32_t rtnl_addr_get_create_time(struct rtnl_addr *);
+extern uint32_t rtnl_addr_get_last_update_time(struct rtnl_addr *);
diff --git a/python/netlink/route/link.py b/python/netlink/route/link.py
new file mode 100644
index 00000000..5ec14b26
--- /dev/null
+++ b/python/netlink/route/link.py
@@ -0,0 +1,551 @@
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""Module providing access to network links
+
+This module provides an interface to view configured network links,
+modify them and to add and delete virtual network links.
+
+The following is a basic example:
+ import netlink.core as netlink
+ import netlink.route.link as link
+
+ sock = netlink.Socket()
+ sock.connect(netlink.NETLINK_ROUTE)
+
+ cache = link.LinkCache() # create new empty link cache
+ cache.refill(sock) # fill cache with all configured links
+ eth0 = cache['eth0'] # lookup link "eth0"
+ print eth0 # print basic configuration
+
+The module contains the following public classes:
+
+ - Link -- Represents a network link. Instances can be created directly
+ via the constructor (empty link objects) or via the refill()
+ method of a LinkCache.
+ - LinkCache -- Derived from netlink.Cache, holds any number of
+ network links (Link instances). Main purpose is to keep
+ a local list of all network links configured in the
+ kernel.
+
+The following public functions exist:
+ - get_from_kernel(socket, name)
+
+"""
+
+from __future__ import absolute_import
+
+__version__ = '0.1'
+__all__ = [
+ 'LinkCache',
+ 'Link',
+ 'get_from_kernel',
+]
+
+import socket
+from .. import core as netlink
+from .. import capi as core_capi
+from . import capi as capi
+from .links import inet as inet
+from .. import util as util
+
+# Link statistics definitions
+RX_PACKETS = 0
+TX_PACKETS = 1
+RX_BYTES = 2
+TX_BYTES = 3
+RX_ERRORS = 4
+TX_ERRORS = 5
+RX_DROPPED = 6
+TX_DROPPED = 7
+RX_COMPRESSED = 8
+TX_COMPRESSED = 9
+RX_FIFO_ERR = 10
+TX_FIFO_ERR = 11
+RX_LEN_ERR = 12
+RX_OVER_ERR = 13
+RX_CRC_ERR = 14
+RX_FRAME_ERR = 15
+RX_MISSED_ERR = 16
+TX_ABORT_ERR = 17
+TX_CARRIER_ERR = 18
+TX_HBEAT_ERR = 19
+TX_WIN_ERR = 20
+COLLISIONS = 21
+MULTICAST = 22
+IP6_INPKTS = 23
+IP6_INHDRERRORS = 24
+IP6_INTOOBIGERRORS = 25
+IP6_INNOROUTES = 26
+IP6_INADDRERRORS = 27
+IP6_INUNKNOWNPROTOS = 28
+IP6_INTRUNCATEDPKTS = 29
+IP6_INDISCARDS = 30
+IP6_INDELIVERS = 31
+IP6_OUTFORWDATAGRAMS = 32
+IP6_OUTPKTS = 33
+IP6_OUTDISCARDS = 34
+IP6_OUTNOROUTES = 35
+IP6_REASMTIMEOUT = 36
+IP6_REASMREQDS = 37
+IP6_REASMOKS = 38
+IP6_REASMFAILS = 39
+IP6_FRAGOKS = 40
+IP6_FRAGFAILS = 41
+IP6_FRAGCREATES = 42
+IP6_INMCASTPKTS = 43
+IP6_OUTMCASTPKTS = 44
+IP6_INBCASTPKTS = 45
+IP6_OUTBCASTPKTS = 46
+IP6_INOCTETS = 47
+IP6_OUTOCTETS = 48
+IP6_INMCASTOCTETS = 49
+IP6_OUTMCASTOCTETS = 50
+IP6_INBCASTOCTETS = 51
+IP6_OUTBCASTOCTETS = 52
+ICMP6_INMSGS = 53
+ICMP6_INERRORS = 54
+ICMP6_OUTMSGS = 55
+ICMP6_OUTERRORS = 56
+
+class LinkCache(netlink.Cache):
+ """Cache of network links"""
+
+ def __init__(self, family=socket.AF_UNSPEC, cache=None):
+ if not cache:
+ cache = self._alloc_cache_name('route/link')
+
+ self._info_module = None
+ self._protocol = netlink.NETLINK_ROUTE
+ self._nl_cache = cache
+ self._set_arg1(family)
+
+ def __getitem__(self, key):
+ if type(key) is int:
+ link = capi.rtnl_link_get(self._nl_cache, key)
+ else:
+ link = capi.rtnl_link_get_by_name(self._nl_cache, key)
+
+ if link is None:
+ raise KeyError()
+ else:
+ return Link.from_capi(link)
+
+ @staticmethod
+ def _new_object(obj):
+ return Link(obj)
+
+ def _new_cache(self, cache):
+ return LinkCache(family=self.arg1, cache=cache)
+
+class Link(netlink.Object):
+ """Network link"""
+
+ def __init__(self, obj=None):
+ netlink.Object.__init__(self, 'route/link', 'link', obj)
+ self._rtnl_link = self._obj2type(self._nl_object)
+
+ if self.type:
+ self._module_lookup('netlink.route.links.' + self.type)
+
+ self.inet = inet.InetLink(self)
+ self.af = {'inet' : self.inet }
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, tb):
+ if exc_type is None:
+ self.change()
+ else:
+ return false
+
+ @classmethod
+ def from_capi(cls, obj):
+ return cls(capi.link2obj(obj))
+
+ @staticmethod
+ def _obj2type(obj):
+ return capi.obj2link(obj)
+
+ def __cmp__(self, other):
+ return self.ifindex - other.ifindex
+
+ @staticmethod
+ def _new_instance(obj):
+ if not obj:
+ raise ValueError()
+
+ return Link(obj)
+
+ @property
+ @netlink.nlattr(type=int, immutable=True, fmt=util.num)
+ def ifindex(self):
+ """interface index"""
+ return capi.rtnl_link_get_ifindex(self._rtnl_link)
+
+ @ifindex.setter
+ def ifindex(self, value):
+ capi.rtnl_link_set_ifindex(self._rtnl_link, int(value))
+
+ # ifindex is immutable but we assume that if _orig does not
+ # have an ifindex specified, it was meant to be given here
+ if capi.rtnl_link_get_ifindex(self._orig) == 0:
+ capi.rtnl_link_set_ifindex(self._orig, int(value))
+
+ @property
+ @netlink.nlattr(type=str, fmt=util.bold)
+ def name(self):
+ """Name of link"""
+ return capi.rtnl_link_get_name(self._rtnl_link)
+
+ @name.setter
+ def name(self, value):
+ capi.rtnl_link_set_name(self._rtnl_link, value)
+
+ # name is the secondary identifier, if _orig does not have
+ # the name specified yet, assume it was meant to be specified
+ # here. ifindex will always take priority, therefore if ifindex
+ # is specified as well, this will be ignored automatically.
+ if capi.rtnl_link_get_name(self._orig) is None:
+ capi.rtnl_link_set_name(self._orig, value)
+
+ @property
+ @netlink.nlattr(type=str, fmt=util.string)
+ def flags(self):
+ """Flags
+ Setting this property will *Not* reset flags to value you supply in
+ Examples:
+ link.flags = '+xxx' # add xxx flag
+ link.flags = 'xxx' # exactly the same
+ link.flags = '-xxx' # remove xxx flag
+ link.flags = [ '+xxx', '-yyy' ] # list operation
+ """
+ flags = capi.rtnl_link_get_flags(self._rtnl_link)
+ return capi.rtnl_link_flags2str(flags, 256)[0].split(',')
+
+ def _set_flag(self, flag):
+ if flag.startswith('-'):
+ i = capi.rtnl_link_str2flags(flag[1:])
+ capi.rtnl_link_unset_flags(self._rtnl_link, i)
+ elif flag.startswith('+'):
+ i = capi.rtnl_link_str2flags(flag[1:])
+ capi.rtnl_link_set_flags(self._rtnl_link, i)
+ else:
+ i = capi.rtnl_link_str2flags(flag)
+ capi.rtnl_link_set_flags(self._rtnl_link, i)
+
+ @flags.setter
+ def flags(self, value):
+ if not (type(value) is str):
+ for flag in value:
+ self._set_flag(flag)
+ else:
+ self._set_flag(value)
+
+ @property
+ @netlink.nlattr(type=int, fmt=util.num)
+ def mtu(self):
+ """Maximum Transmission Unit"""
+ return capi.rtnl_link_get_mtu(self._rtnl_link)
+
+ @mtu.setter
+ def mtu(self, value):
+ capi.rtnl_link_set_mtu(self._rtnl_link, int(value))
+
+ @property
+ @netlink.nlattr(type=int, immutable=True, fmt=util.num)
+ def family(self):
+ """Address family"""
+ return capi.rtnl_link_get_family(self._rtnl_link)
+
+ @family.setter
+ def family(self, value):
+ capi.rtnl_link_set_family(self._rtnl_link, value)
+
+ @property
+ @netlink.nlattr(type=str, fmt=util.addr)
+ def address(self):
+ """Hardware address (MAC address)"""
+ a = capi.rtnl_link_get_addr(self._rtnl_link)
+ return netlink.AbstractAddress(a)
+
+ @address.setter
+ def address(self, value):
+ capi.rtnl_link_set_addr(self._rtnl_link, value._addr)
+
+ @property
+ @netlink.nlattr(type=str, fmt=util.addr)
+ def broadcast(self):
+ """Hardware broadcast address"""
+ a = capi.rtnl_link_get_broadcast(self._rtnl_link)
+ return netlink.AbstractAddress(a)
+
+ @broadcast.setter
+ def broadcast(self, value):
+ capi.rtnl_link_set_broadcast(self._rtnl_link, value._addr)
+
+ @property
+ @netlink.nlattr(type=str, immutable=True, fmt=util.string)
+ def qdisc(self):
+ """Name of qdisc (cannot be changed)"""
+ return capi.rtnl_link_get_qdisc(self._rtnl_link)
+
+ @qdisc.setter
+ def qdisc(self, value):
+ capi.rtnl_link_set_qdisc(self._rtnl_link, value)
+
+ @property
+ @netlink.nlattr(type=int, fmt=util.num)
+ def txqlen(self):
+ """Length of transmit queue"""
+ return capi.rtnl_link_get_txqlen(self._rtnl_link)
+
+ @txqlen.setter
+ def txqlen(self, value):
+ capi.rtnl_link_set_txqlen(self._rtnl_link, int(value))
+
+ @property
+ @netlink.nlattr(type=str, immutable=True, fmt=util.string)
+ def arptype(self):
+ """Type of link (cannot be changed)"""
+ type_ = capi.rtnl_link_get_arptype(self._rtnl_link)
+ return core_capi.nl_llproto2str(type_, 64)[0]
+
+ @arptype.setter
+ def arptype(self, value):
+ i = core_capi.nl_str2llproto(value)
+ capi.rtnl_link_set_arptype(self._rtnl_link, i)
+
+ @property
+ @netlink.nlattr(type=str, immutable=True, fmt=util.string, title='state')
+ def operstate(self):
+ """Operational status"""
+ operstate = capi.rtnl_link_get_operstate(self._rtnl_link)
+ return capi.rtnl_link_operstate2str(operstate, 32)[0]
+
+ @operstate.setter
+ def operstate(self, value):
+ i = capi.rtnl_link_str2operstate(value)
+ capi.rtnl_link_set_operstate(self._rtnl_link, i)
+
+ @property
+ @netlink.nlattr(type=str, immutable=True, fmt=util.string)
+ def mode(self):
+ """Link mode"""
+ mode = capi.rtnl_link_get_linkmode(self._rtnl_link)
+ return capi.rtnl_link_mode2str(mode, 32)[0]
+
+ @mode.setter
+ def mode(self, value):
+ i = capi.rtnl_link_str2mode(value)
+ capi.rtnl_link_set_linkmode(self._rtnl_link, i)
+
+ @property
+ @netlink.nlattr(type=str, fmt=util.string)
+ def alias(self):
+ """Interface alias (SNMP)"""
+ return capi.rtnl_link_get_ifalias(self._rtnl_link)
+
+ @alias.setter
+ def alias(self, value):
+ capi.rtnl_link_set_ifalias(self._rtnl_link, value)
+
+ @property
+ @netlink.nlattr(type=str, fmt=util.string)
+ def type(self):
+ """Link type"""
+ return capi.rtnl_link_get_type(self._rtnl_link)
+
+ @type.setter
+ def type(self, value):
+ if capi.rtnl_link_set_type(self._rtnl_link, value) < 0:
+ raise NameError('unknown info type')
+
+ self._module_lookup('netlink.route.links.' + value)
+
+ def get_stat(self, stat):
+ """Retrieve statistical information"""
+ if type(stat) is str:
+ stat = capi.rtnl_link_str2stat(stat)
+ if stat < 0:
+ raise NameError('unknown name of statistic')
+
+ return capi.rtnl_link_get_stat(self._rtnl_link, stat)
+
+ def enslave(self, slave, sock=None):
+ if not sock:
+ sock = netlink.lookup_socket(netlink.NETLINK_ROUTE)
+
+ return capi.rtnl_link_enslave(sock._sock, self._rtnl_link, slave._rtnl_link)
+
+ def release(self, slave, sock=None):
+ if not sock:
+ sock = netlink.lookup_socket(netlink.NETLINK_ROUTE)
+
+ return capi.rtnl_link_release(sock._sock, self._rtnl_link, slave._rtnl_link)
+
+ def add(self, sock=None, flags=None):
+ if not sock:
+ sock = netlink.lookup_socket(netlink.NETLINK_ROUTE)
+
+ if not flags:
+ flags = netlink.NLM_F_CREATE
+
+ ret = capi.rtnl_link_add(sock._sock, self._rtnl_link, flags)
+ if ret < 0:
+ raise netlink.KernelError(ret)
+
+ def change(self, sock=None, flags=0):
+ """Commit changes made to the link object"""
+ if sock is None:
+ sock = netlink.lookup_socket(netlink.NETLINK_ROUTE)
+
+ if not self._orig:
+ raise netlink.NetlinkError('Original link not available')
+ ret = capi.rtnl_link_change(sock._sock, self._orig, self._rtnl_link, flags)
+ if ret < 0:
+ raise netlink.KernelError(ret)
+
+ def delete(self, sock=None):
+ """Attempt to delete this link in the kernel"""
+ if sock is None:
+ sock = netlink.lookup_socket(netlink.NETLINK_ROUTE)
+
+ ret = capi.rtnl_link_delete(sock._sock, self._rtnl_link)
+ if ret < 0:
+ raise netlink.KernelError(ret)
+
+ ###################################################################
+ # private properties
+ #
+ # Used for formatting output. USE AT OWN RISK
+ @property
+ def _state(self):
+ if 'up' in self.flags:
+ buf = util.good('up')
+ if 'lowerup' not in self.flags:
+ buf += ' ' + util.bad('no-carrier')
+ else:
+ buf = util.bad('down')
+ return buf
+
+ @property
+ def _brief(self):
+ return self._module_brief() + self._foreach_af('brief')
+
+ @property
+ def _flags(self):
+ ignore = [
+ 'up',
+ 'running',
+ 'lowerup',
+ ]
+ return ','.join([flag for flag in self.flags if flag not in ignore])
+
+ def _foreach_af(self, name, args=None):
+ buf = ''
+ for af in self.af:
+ try:
+ func = getattr(self.af[af], name)
+ s = str(func(args))
+ if len(s) > 0:
+ buf += ' ' + s
+ except AttributeError:
+ pass
+ return buf
+
+ def format(self, details=False, stats=False, indent=''):
+ """Return link as formatted text"""
+ fmt = util.MyFormatter(self, indent)
+
+ buf = fmt.format('{a|ifindex} {a|name} {a|arptype} {a|address} '\
+ '{a|_state} <{a|_flags}> {a|_brief}')
+
+ if details:
+ buf += fmt.nl('\t{t|mtu} {t|txqlen} {t|weight} '\
+ '{t|qdisc} {t|operstate}')
+ buf += fmt.nl('\t{t|broadcast} {t|alias}')
+
+ buf += self._foreach_af('details', fmt)
+
+ if stats:
+ l = [['Packets', RX_PACKETS, TX_PACKETS],
+ ['Bytes', RX_BYTES, TX_BYTES],
+ ['Errors', RX_ERRORS, TX_ERRORS],
+ ['Dropped', RX_DROPPED, TX_DROPPED],
+ ['Compressed', RX_COMPRESSED, TX_COMPRESSED],
+ ['FIFO Errors', RX_FIFO_ERR, TX_FIFO_ERR],
+ ['Length Errors', RX_LEN_ERR, None],
+ ['Over Errors', RX_OVER_ERR, None],
+ ['CRC Errors', RX_CRC_ERR, None],
+ ['Frame Errors', RX_FRAME_ERR, None],
+ ['Missed Errors', RX_MISSED_ERR, None],
+ ['Abort Errors', None, TX_ABORT_ERR],
+ ['Carrier Errors', None, TX_CARRIER_ERR],
+ ['Heartbeat Errors', None, TX_HBEAT_ERR],
+ ['Window Errors', None, TX_WIN_ERR],
+ ['Collisions', None, COLLISIONS],
+ ['Multicast', None, MULTICAST],
+ ['', None, None],
+ ['Ipv6:', None, None],
+ ['Packets', IP6_INPKTS, IP6_OUTPKTS],
+ ['Bytes', IP6_INOCTETS, IP6_OUTOCTETS],
+ ['Discards', IP6_INDISCARDS, IP6_OUTDISCARDS],
+ ['Multicast Packets', IP6_INMCASTPKTS, IP6_OUTMCASTPKTS],
+ ['Multicast Bytes', IP6_INMCASTOCTETS, IP6_OUTMCASTOCTETS],
+ ['Broadcast Packets', IP6_INBCASTPKTS, IP6_OUTBCASTPKTS],
+ ['Broadcast Bytes', IP6_INBCASTOCTETS, IP6_OUTBCASTOCTETS],
+ ['Delivers', IP6_INDELIVERS, None],
+ ['Forwarded', None, IP6_OUTFORWDATAGRAMS],
+ ['No Routes', IP6_INNOROUTES, IP6_OUTNOROUTES],
+ ['Header Errors', IP6_INHDRERRORS, None],
+ ['Too Big Errors', IP6_INTOOBIGERRORS, None],
+ ['Address Errors', IP6_INADDRERRORS, None],
+ ['Unknown Protocol', IP6_INUNKNOWNPROTOS, None],
+ ['Truncated Packets', IP6_INTRUNCATEDPKTS, None],
+ ['Reasm Timeouts', IP6_REASMTIMEOUT, None],
+ ['Reasm Requests', IP6_REASMREQDS, None],
+ ['Reasm Failures', IP6_REASMFAILS, None],
+ ['Reasm OK', IP6_REASMOKS, None],
+ ['Frag Created', None, IP6_FRAGCREATES],
+ ['Frag Failures', None, IP6_FRAGFAILS],
+ ['Frag OK', None, IP6_FRAGOKS],
+ ['', None, None],
+ ['ICMPv6:', None, None],
+ ['Messages', ICMP6_INMSGS, ICMP6_OUTMSGS],
+ ['Errors', ICMP6_INERRORS, ICMP6_OUTERRORS]]
+
+ buf += '\n\t%s%s%s%s\n' % (33 * ' ', util.title('RX'),
+ 15 * ' ', util.title('TX'))
+
+ for row in l:
+ row[0] = util.kw(row[0])
+ row[1] = self.get_stat(row[1]) if row[1] else ''
+ row[2] = self.get_stat(row[2]) if row[2] else ''
+ buf += '\t{0[0]:27} {0[1]:>16} {0[2]:>16}\n'.format(row)
+
+ buf += self._foreach_af('stats')
+
+ return buf
+
+def get(name, sock=None):
+ """Lookup Link object directly from kernel"""
+ if not name:
+ raise ValueError()
+
+ if not sock:
+ sock = netlink.lookup_socket(netlink.NETLINK_ROUTE)
+
+ link = capi.get_from_kernel(sock._sock, 0, name)
+ if not link:
+ return None
+
+ return Link.from_capi(link)
+
+_link_cache = LinkCache()
+
+def resolve(name):
+ _link_cache.refill()
+ return _link_cache[name]
diff --git a/python/netlink/route/links/__init__.py b/python/netlink/route/links/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/python/netlink/route/links/__init__.py
diff --git a/python/netlink/route/links/bridge.py b/python/netlink/route/links/bridge.py
new file mode 100644
index 00000000..549b0925
--- /dev/null
+++ b/python/netlink/route/links/bridge.py
@@ -0,0 +1,119 @@
+#
+# Copyright (c) 2013 Nicolas PLANEL <nicolas.planel@enovance.com>
+#
+
+"""BRIDGE network link
+
+"""
+
+from __future__ import absolute_import
+
+from ... import core as netlink
+from .. import capi as capi
+
+class BRIDGELink(object):
+ def __init__(self, link):
+ self._link = link
+ self._has_ext_info = capi.rtnl_link_bridge_has_ext_info(self._link)
+ self._port_state_values = ['disabled','listening','learning','forwarding','blocking']
+
+ def bridge_assert_ext_info(self):
+ if self._has_ext_info == False:
+ print """
+ Please update your kernel to be able to call this method.
+ Your current kernel bridge version is too old to support this extention.
+ """
+ raise RuntimeWarning()
+
+ def port_state2str(self, state):
+ return self._port_state_values[state]
+
+ def str2port_state(self, str):
+ for value, port in enumerate(self._port_state_values):
+ if str.lower() == port:
+ return value
+ raise ValueError()
+
+ @property
+ @netlink.nlattr(type=int)
+ def port_state(self):
+ """bridge state :
+ %s
+ """ % (self.port_state)
+ return capi.rtnl_link_bridge_get_state(self._link)
+
+ @port_state.setter
+ def port_state(self, state):
+ capi.rtnl_link_bridge_set_state(self._link, int(state))
+
+ @property
+ @netlink.nlattr(type=int)
+ def priority(self):
+ """bridge prio
+ """
+ bridge_assert_ext_info()
+ return capi.rtnl_link_bridge_get_prio(self._link)
+
+ @priority.setter
+ def priority(self, prio):
+ bridge_assert_ext_info()
+ if prio < 0 or prio >= 2**16:
+ raise ValueError()
+ capi.rtnl_link_bridge_set_prio(self._link, int(prio))
+
+ @property
+ @netlink.nlattr(type=int)
+ def cost(self):
+ """bridge prio
+ """
+ bridge_assert_ext_info()
+ return capi.rtnl_link_bridge_get_cost(self._link)
+
+ @cost.setter
+ def cost(self, cost):
+ bridge_assert_ext_info()
+ if cost < 0 or cost >= 2**32:
+ raise ValueError()
+ capi.rtnl_link_bridge_set_cost(self._link, int(cost))
+
+ @property
+ @netlink.nlattr(type=str)
+ def flags(self):
+ """ BRIDGE flags
+ Setting this property will *Not* reset flags to value you supply in
+ Examples:
+ link.flags = '+xxx' # add xxx flag
+ link.flags = 'xxx' # exactly the same
+ link.flags = '-xxx' # remove xxx flag
+ link.flags = [ '+xxx', '-yyy' ] # list operation
+ """
+ self.bridge_assert_ext_info()
+ flags = capi.rtnl_link_bridge_get_flags(self._link)
+ return capi.rtnl_link_bridge_flags2str(flags, 256)[0].split(',')
+
+ def _set_flag(self, flag):
+ if flag.startswith('-'):
+ i = capi.rtnl_link_bridge_str2flags(flag[1:])
+ capi.rtnl_link_bridge_unset_flags(self._link, i)
+ elif flag.startswith('+'):
+ i = capi.rtnl_link_bridge_str2flags(flag[1:])
+ capi.rtnl_link_bridge_set_flags(self._link, i)
+ else:
+ i = capi.rtnl_link_bridge_str2flags(flag)
+ capi.rtnl_link_bridge_set_flags(self._link, i)
+
+ @flags.setter
+ def flags(self, value):
+ self.bridge_assert_ext_info()
+ if type(value) is list:
+ for flag in value:
+ self._set_flag(flag)
+ else:
+ self._set_flag(value)
+
+ def brief(self):
+ return 'bridge-has-ext-info {0}'.format(self._has_ext_info)
+
+def init(link):
+ link.bridge = BRIDGELink(link._rtnl_link)
+ return link.bridge
diff --git a/python/netlink/route/links/dummy.py b/python/netlink/route/links/dummy.py
new file mode 100644
index 00000000..e9491cc6
--- /dev/null
+++ b/python/netlink/route/links/dummy.py
@@ -0,0 +1,26 @@
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""Dummy
+
+"""
+from __future__ import absolute_import
+
+__version__ = '1.0'
+__all__ = [
+ 'init',
+]
+
+
+class DummyLink(object):
+ def __init__(self, link):
+ self._rtnl_link = link
+
+ @staticmethod
+ def brief():
+ return 'dummy'
+
+def init(link):
+ link.dummy = DummyLink(link._rtnl_link)
+ return link.dummy
diff --git a/python/netlink/route/links/inet.py b/python/netlink/route/links/inet.py
new file mode 100644
index 00000000..f5f45cb3
--- /dev/null
+++ b/python/netlink/route/links/inet.py
@@ -0,0 +1,158 @@
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""IPv4
+
+"""
+
+from __future__ import absolute_import
+
+__all__ = [
+ '',
+]
+
+from ... import core as netlink
+from .. import capi as capi
+from ... import util as util
+DEVCONF_FORWARDING = 1
+DEVCONF_MC_FORWARDING = 2
+DEVCONF_PROXY_ARP = 3
+DEVCONF_ACCEPT_REDIRECTS = 4
+DEVCONF_SECURE_REDIRECTS = 5
+DEVCONF_SEND_REDIRECTS = 6
+DEVCONF_SHARED_MEDIA = 7
+DEVCONF_RP_FILTER = 8
+DEVCONF_ACCEPT_SOURCE_ROUTE = 9
+DEVCONF_BOOTP_RELAY = 10
+DEVCONF_LOG_MARTIANS = 11
+DEVCONF_TAG = 12
+DEVCONF_ARPFILTER = 13
+DEVCONF_MEDIUM_ID = 14
+DEVCONF_NOXFRM = 15
+DEVCONF_NOPOLICY = 16
+DEVCONF_FORCE_IGMP_VERSION = 17
+DEVCONF_ARP_ANNOUNCE = 18
+DEVCONF_ARP_IGNORE = 19
+DEVCONF_PROMOTE_SECONDARIES = 20
+DEVCONF_ARP_ACCEPT = 21
+DEVCONF_ARP_NOTIFY = 22
+DEVCONF_ACCEPT_LOCAL = 23
+DEVCONF_SRC_VMARK = 24
+DEVCONF_PROXY_ARP_PVLAN = 25
+DEVCONF_MAX = DEVCONF_PROXY_ARP_PVLAN
+
+def _resolve(id):
+ if type(id) is str:
+ id = capi.rtnl_link_inet_str2devconf(id)[0]
+ if id < 0:
+ raise NameError('unknown configuration id')
+ return id
+
+class InetLink(object):
+ def __init__(self, link):
+ self._link = link
+
+ def details(self, fmt):
+ buf = fmt.nl('\n\t{0}\n\t'.format(util.title('Configuration:')))
+
+ for i in range(DEVCONF_FORWARDING, DEVCONF_MAX+1):
+ if i & 1 and i > 1:
+ buf += fmt.nl('\t')
+ txt = util.kw(capi.rtnl_link_inet_devconf2str(i, 32)[0])
+ buf += fmt.format('{0:28s} {1:12} ', txt,
+ self.get_conf(i))
+
+
+ return buf
+
+ def get_conf(self, id):
+ return capi.inet_get_conf(self._link._rtnl_link, _resolve(id))
+
+ def set_conf(self, id, value):
+ return capi.rtnl_link_inet_set_conf(self._link._rtnl_link,
+ _resolve(id), int(value))
+
+ @property
+ @netlink.nlattr(type=bool, fmt=util.boolean)
+ def forwarding(self):
+ return bool(self.get_conf(DEVCONF_FORWARDING))
+
+ @forwarding.setter
+ def forwarding(self, value):
+ self.set_conf(DEVCONF_FORWARDING, int(value))
+
+
+ @property
+ @netlink.nlattr(type=bool, fmt=util.boolean)
+ def mc_forwarding(self):
+ return bool(self.get_conf(DEVCONF_MC_FORWARDING))
+
+ @mc_forwarding.setter
+ def mc_forwarding(self, value):
+ self.set_conf(DEVCONF_MC_FORWARDING, int(value))
+
+
+ @property
+ @netlink.nlattr(type=bool, fmt=util.boolean)
+ def proxy_arp(self):
+ return bool(self.get_conf(DEVCONF_PROXY_ARP))
+
+ @proxy_arp.setter
+ def proxy_arp(self, value):
+ self.set_conf(DEVCONF_PROXY_ARP, int(value))
+
+ @property
+ @netlink.nlattr(type=bool, fmt=util.boolean)
+ def accept_redirects(self):
+ return bool(self.get_conf(DEVCONF_ACCEPT_REDIRECTS))
+
+ @accept_redirects.setter
+ def accept_redirects(self, value):
+ self.set_conf(DEVCONF_ACCEPT_REDIRECTS, int(value))
+
+ @property
+ @netlink.nlattr(type=bool, fmt=util.boolean)
+ def secure_redirects(self):
+ return bool(self.get_conf(DEVCONF_SECURE_REDIRECTS))
+
+ @secure_redirects.setter
+ def secure_redirects(self, value):
+ self.set_conf(DEVCONF_SECURE_REDIRECTS, int(value))
+
+ @property
+ @netlink.nlattr(type=bool, fmt=util.boolean)
+ def send_redirects(self):
+ return bool(self.get_conf(DEVCONF_SEND_REDIRECTS))
+
+ @send_redirects.setter
+ def send_redirects(self, value):
+ self.set_conf(DEVCONF_SEND_REDIRECTS, int(value))
+
+ @property
+ @netlink.nlattr(type=bool, fmt=util.boolean)
+ def shared_media(self):
+ return bool(self.get_conf(DEVCONF_SHARED_MEDIA))
+
+ @shared_media.setter
+ def shared_media(self, value):
+ self.set_conf(DEVCONF_SHARED_MEDIA, int(value))
+
+# IPV4_DEVCONF_RP_FILTER,
+# IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE,
+# IPV4_DEVCONF_BOOTP_RELAY,
+# IPV4_DEVCONF_LOG_MARTIANS,
+# IPV4_DEVCONF_TAG,
+# IPV4_DEVCONF_ARPFILTER,
+# IPV4_DEVCONF_MEDIUM_ID,
+# IPV4_DEVCONF_NOXFRM,
+# IPV4_DEVCONF_NOPOLICY,
+# IPV4_DEVCONF_FORCE_IGMP_VERSION,
+# IPV4_DEVCONF_ARP_ANNOUNCE,
+# IPV4_DEVCONF_ARP_IGNORE,
+# IPV4_DEVCONF_PROMOTE_SECONDARIES,
+# IPV4_DEVCONF_ARP_ACCEPT,
+# IPV4_DEVCONF_ARP_NOTIFY,
+# IPV4_DEVCONF_ACCEPT_LOCAL,
+# IPV4_DEVCONF_SRC_VMARK,
+# IPV4_DEVCONF_PROXY_ARP_PVLAN,
diff --git a/python/netlink/route/links/vlan.py b/python/netlink/route/links/vlan.py
new file mode 100644
index 00000000..0ba37810
--- /dev/null
+++ b/python/netlink/route/links/vlan.py
@@ -0,0 +1,71 @@
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""VLAN network link
+
+"""
+
+from __future__ import absolute_import
+
+
+from ... import core as netlink
+from .. import capi as capi
+class VLANLink(object):
+ def __init__(self, link):
+ self._link = link
+
+ @property
+ @netlink.nlattr(type=int)
+ def id(self):
+ """vlan identifier"""
+ return capi.rtnl_link_vlan_get_id(self._link)
+
+ @id.setter
+ def id(self, value):
+ capi.rtnl_link_vlan_set_id(self._link, int(value))
+
+ @property
+ @netlink.nlattr(type=str)
+ def flags(self):
+ """ VLAN flags
+ Setting this property will *Not* reset flags to value you supply in
+ Examples:
+ link.flags = '+xxx' # add xxx flag
+ link.flags = 'xxx' # exactly the same
+ link.flags = '-xxx' # remove xxx flag
+ link.flags = [ '+xxx', '-yyy' ] # list operation
+ """
+ flags = capi.rtnl_link_vlan_get_flags(self._link)
+ return capi.rtnl_link_vlan_flags2str(flags, 256)[0].split(',')
+
+ def _set_flag(self, flag):
+ if flag.startswith('-'):
+ i = capi.rtnl_link_vlan_str2flags(flag[1:])
+ capi.rtnl_link_vlan_unset_flags(self._link, i)
+ elif flag.startswith('+'):
+ i = capi.rtnl_link_vlan_str2flags(flag[1:])
+ capi.rtnl_link_vlan_set_flags(self._link, i)
+ else:
+ i = capi.rtnl_link_vlan_str2flags(flag)
+ capi.rtnl_link_vlan_set_flags(self._link, i)
+
+ @flags.setter
+ def flags(self, value):
+ if type(value) is list:
+ for flag in value:
+ self._set_flag(flag)
+ else:
+ self._set_flag(value)
+
+ ###################################################################
+ # TODO:
+ # - ingress map
+ # - egress map
+
+ def brief(self):
+ return 'vlan-id {0}'.format(self.id)
+
+def init(link):
+ link.vlan = VLANLink(link._rtnl_link)
+ return link.vlan
diff --git a/python/netlink/route/qdisc/__init__.py b/python/netlink/route/qdisc/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/python/netlink/route/qdisc/__init__.py
diff --git a/python/netlink/route/qdisc/htb.py b/python/netlink/route/qdisc/htb.py
new file mode 100644
index 00000000..d051c8db
--- /dev/null
+++ b/python/netlink/route/qdisc/htb.py
@@ -0,0 +1,145 @@
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""HTB qdisc
+
+"""
+
+from __future__ import absolute_import
+
+from ... import core as netlink
+from ... import util as util
+from .. import capi as capi
+from .. import tc as tc
+
+class HTBQdisc(object):
+ def __init__(self, qdisc):
+ self._qdisc = qdisc
+
+ @property
+ @netlink.nlattr(type=int)
+ def default_class(self):
+ return tc.Handle(capi.rtnl_htb_get_defcls(self._qdisc._rtnl_qdisc))
+
+ @default_class.setter
+ def default_class(self, value):
+ capi.rtnl_htb_set_defcls(self._qdisc._rtnl_qdisc, int(value))
+
+ @property
+ @netlink.nlattr('r2q', type=int)
+ def r2q(self):
+ return capi.rtnl_htb_get_rate2quantum(self._qdisc._rtnl_qdisc)
+
+ @r2q.setter
+ def r2q(self, value):
+ capi.rtnl_htb_get_rate2quantum(self._qdisc._rtnl_qdisc,
+ int(value))
+
+ def brief(self):
+ fmt = util.MyFormatter(self)
+
+ ret = ' {s|default-class!k} {a|default_class}'
+
+ if self.r2q:
+ ret += ' {s|r2q!k} {a|r2q}'
+
+ return fmt.format(ret)
+
+class HTBClass(object):
+ def __init__(self, cl):
+ self._class = cl
+
+ @property
+ @netlink.nlattr(type=str)
+ def rate(self):
+ rate = capi.rtnl_htb_get_rate(self._class._rtnl_class)
+ return util.Rate(rate)
+
+ @rate.setter
+ def rate(self, value):
+ capi.rtnl_htb_set_rate(self._class._rtnl_class, int(value))
+
+ @property
+ @netlink.nlattr(type=str)
+ def ceil(self):
+ ceil = capi.rtnl_htb_get_ceil(self._class._rtnl_class)
+ return util.Rate(ceil)
+
+ @ceil.setter
+ def ceil(self, value):
+ capi.rtnl_htb_set_ceil(self._class._rtnl_class, int(value))
+
+ @property
+ @netlink.nlattr(type=str)
+ def burst(self):
+ burst = capi.rtnl_htb_get_rbuffer(self._class._rtnl_class)
+ return util.Size(burst)
+
+ @burst.setter
+ def burst(self, value):
+ capi.rtnl_htb_set_rbuffer(self._class._rtnl_class, int(value))
+
+ @property
+ @netlink.nlattr(type=str)
+ def ceil_burst(self):
+ burst = capi.rtnl_htb_get_cbuffer(self._class._rtnl_class)
+ return util.Size(burst)
+
+ @ceil_burst.setter
+ def ceil_burst(self, value):
+ capi.rtnl_htb_set_cbuffer(self._class._rtnl_class, int(value))
+
+ @property
+ @netlink.nlattr(type=int)
+ def prio(self):
+ return capi.rtnl_htb_get_prio(self._class._rtnl_class)
+
+ @prio.setter
+ def prio(self, value):
+ capi.rtnl_htb_set_prio(self._class._rtnl_class, int(value))
+
+ @property
+ @netlink.nlattr(type=int)
+ def quantum(self):
+ return capi.rtnl_htb_get_quantum(self._class._rtnl_class)
+
+ @quantum.setter
+ def quantum(self, value):
+ capi.rtnl_htb_set_quantum(self._class._rtnl_class, int(value))
+
+ @property
+ @netlink.nlattr(type=int)
+ def level(self):
+ return capi.rtnl_htb_get_level(self._class._rtnl_class)
+
+ @level.setter
+ def level(self, value):
+ capi.rtnl_htb_set_level(self._class._rtnl_class, int(value))
+
+ def brief(self):
+ fmt = util.MyFormatter(self)
+
+ ret = ' {t|prio} {t|rate}'
+
+ if self.rate != self.ceil:
+ ret += ' {s|borrow-up-to!k} {a|ceil}'
+
+ ret += ' {t|burst}'
+
+ return fmt.format(ret)
+
+ def details(self):
+ fmt = util.MyFormatter(self)
+
+ return fmt.nl('\t{t|level} {t|quantum}')
+
+def init_qdisc(qdisc):
+ qdisc.htb = HTBQdisc(qdisc)
+ return qdisc.htb
+
+def init_class(cl):
+ cl.htb = HTBClass(cl)
+ return cl.htb
+
+#extern void rtnl_htb_set_quantum(struct rtnl_class *, uint32_t quantum);
diff --git a/python/netlink/route/tc.py b/python/netlink/route/tc.py
new file mode 100644
index 00000000..0689b71d
--- /dev/null
+++ b/python/netlink/route/tc.py
@@ -0,0 +1,595 @@
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+from __future__ import absolute_import
+
+__all__ = [
+ 'TcCache',
+ 'Tc',
+ 'QdiscCache',
+ 'Qdisc',
+ 'TcClassCache',
+ 'TcClass',
+]
+
+from .. import core as netlink
+from . import capi as capi
+from .. import util as util
+from . import link as Link
+
+TC_PACKETS = 0
+TC_BYTES = 1
+TC_RATE_BPS = 2
+TC_RATE_PPS = 3
+TC_QLEN = 4
+TC_BACKLOG = 5
+TC_DROPS = 6
+TC_REQUEUES = 7
+TC_OVERLIMITS = 9
+
+TC_H_ROOT = 0xFFFFFFFF
+TC_H_INGRESS = 0xFFFFFFF1
+
+STAT_PACKETS = 0
+STAT_BYTES = 1
+STAT_RATE_BPS = 2
+STAT_RATE_PPS = 3
+STAT_QLEN = 4
+STAT_BACKLOG = 5
+STAT_DROPS = 6
+STAT_REQUEUES = 7
+STAT_OVERLIMITS = 8
+STAT_MAX = STAT_OVERLIMITS
+
+
+class Handle(object):
+ """ Traffic control handle
+
+ Representation of a traffic control handle which uniquely identifies
+ each traffic control object in its link namespace.
+
+ handle = tc.Handle('10:20')
+ handle = tc.handle('root')
+ print int(handle)
+ print str(handle)
+ """
+ def __init__(self, val=None):
+ if type(val) is str:
+ val = capi.tc_str2handle(val)
+ elif not val:
+ val = 0
+
+ self._val = int(val)
+
+ def __cmp__(self, other):
+ if other is None:
+ other = 0
+
+ if isinstance(other, Handle):
+ return int(self) - int(other)
+ elif isinstance(other, int):
+ return int(self) - other
+ else:
+ raise TypeError()
+
+ def __int__(self):
+ return self._val
+
+ def __str__(self):
+ return capi.rtnl_tc_handle2str(self._val, 64)[0]
+
+ def isroot(self):
+ return self._val == TC_H_ROOT or self._val == TC_H_INGRESS
+
+class TcCache(netlink.Cache):
+ """Cache of traffic control object"""
+
+ def __getitem__(self, key):
+ raise NotImplementedError()
+
+class Tc(netlink.Object):
+ def __cmp__(self, other):
+ diff = self.ifindex - other.ifindex
+ if diff == 0:
+ diff = int(self.handle) - int(other.handle)
+ return diff
+
+ def _tc_module_lookup(self):
+ self._module_lookup(self._module_path + self.kind,
+ 'init_' + self._name)
+
+ @property
+ def root(self):
+ """True if tc object is a root object"""
+ return self.parent.isroot()
+
+ @property
+ def ifindex(self):
+ """interface index"""
+ return capi.rtnl_tc_get_ifindex(self._rtnl_tc)
+
+ @ifindex.setter
+ def ifindex(self, value):
+ capi.rtnl_tc_set_ifindex(self._rtnl_tc, int(value))
+
+ @property
+ def link(self):
+ link = capi.rtnl_tc_get_link(self._rtnl_tc)
+ if not link:
+ return None
+
+ return Link.Link.from_capi(link)
+
+ @link.setter
+ def link(self, value):
+ capi.rtnl_tc_set_link(self._rtnl_tc, value._link)
+
+ @property
+ def mtu(self):
+ return capi.rtnl_tc_get_mtu(self._rtnl_tc)
+
+ @mtu.setter
+ def mtu(self, value):
+ capi.rtnl_tc_set_mtu(self._rtnl_tc, int(value))
+
+ @property
+ def mpu(self):
+ return capi.rtnl_tc_get_mpu(self._rtnl_tc)
+
+ @mpu.setter
+ def mpu(self, value):
+ capi.rtnl_tc_set_mpu(self._rtnl_tc, int(value))
+
+ @property
+ def overhead(self):
+ return capi.rtnl_tc_get_overhead(self._rtnl_tc)
+
+ @overhead.setter
+ def overhead(self, value):
+ capi.rtnl_tc_set_overhead(self._rtnl_tc, int(value))
+
+ @property
+ def linktype(self):
+ return capi.rtnl_tc_get_linktype(self._rtnl_tc)
+
+ @linktype.setter
+ def linktype(self, value):
+ capi.rtnl_tc_set_linktype(self._rtnl_tc, int(value))
+
+ @property
+ @netlink.nlattr(fmt=util.handle)
+ def handle(self):
+ return Handle(capi.rtnl_tc_get_handle(self._rtnl_tc))
+
+ @handle.setter
+ def handle(self, value):
+ capi.rtnl_tc_set_handle(self._rtnl_tc, int(value))
+
+ @property
+ @netlink.nlattr(fmt=util.handle)
+ def parent(self):
+ return Handle(capi.rtnl_tc_get_parent(self._rtnl_tc))
+
+ @parent.setter
+ def parent(self, value):
+ capi.rtnl_tc_set_parent(self._rtnl_tc, int(value))
+
+ @property
+ @netlink.nlattr(fmt=util.bold)
+ def kind(self):
+ return capi.rtnl_tc_get_kind(self._rtnl_tc)
+
+ @kind.setter
+ def kind(self, value):
+ capi.rtnl_tc_set_kind(self._rtnl_tc, value)
+ self._tc_module_lookup()
+
+ def get_stat(self, id):
+ return capi.rtnl_tc_get_stat(self._rtnl_tc, id)
+
+ @property
+ def _dev(self):
+ buf = util.kw('dev') + ' '
+
+ if self.link:
+ return buf + util.string(self.link.name)
+ else:
+ return buf + util.num(self.ifindex)
+
+ def brief(self, title, nodev=False, noparent=False):
+ ret = title + ' {a|kind} {a|handle}'
+
+ if not nodev:
+ ret += ' {a|_dev}'
+
+ if not noparent:
+ ret += ' {t|parent}'
+
+ return ret + self._module_brief()
+
+ @staticmethod
+ def details():
+ return '{t|mtu} {t|mpu} {t|overhead} {t|linktype}'
+
+ @property
+ def packets(self):
+ return self.get_stat(STAT_PACKETS)
+
+ @property
+ def bytes(self):
+ return self.get_stat(STAT_BYTES)
+
+ @property
+ def qlen(self):
+ return self.get_stat(STAT_QLEN)
+
+ @staticmethod
+ def stats(fmt):
+ return fmt.nl('{t|packets} {t|bytes} {t|qlen}')
+
+class QdiscCache(netlink.Cache):
+ """Cache of qdiscs"""
+
+ def __init__(self, cache=None):
+ if not cache:
+ cache = self._alloc_cache_name('route/qdisc')
+
+ self._protocol = netlink.NETLINK_ROUTE
+ self._nl_cache = cache
+
+# def __getitem__(self, key):
+# if type(key) is int:
+# link = capi.rtnl_link_get(self._this, key)
+# elif type(key) is str:
+# link = capi.rtnl_link_get_by_name(self._this, key)
+#
+# if qdisc is None:
+# raise KeyError()
+# else:
+# return Qdisc._from_capi(capi.qdisc2obj(qdisc))
+
+ @staticmethod
+ def _new_object(obj):
+ return Qdisc(obj)
+
+ @staticmethod
+ def _new_cache(cache):
+ return QdiscCache(cache=cache)
+
+class Qdisc(Tc):
+ """Queueing discipline"""
+
+ def __init__(self, obj=None):
+ netlink.Object.__init__(self, 'route/qdisc', 'qdisc', obj)
+ self._module_path = 'netlink.route.qdisc.'
+ self._rtnl_qdisc = self._obj2type(self._nl_object)
+ self._rtnl_tc = capi.obj2tc(self._nl_object)
+
+ if self.kind:
+ self._tc_module_lookup()
+
+ @classmethod
+ def from_capi(cls, obj):
+ return cls(capi.qdisc2obj(obj))
+
+ @staticmethod
+ def _obj2type(obj):
+ return capi.obj2qdisc(obj)
+
+ @staticmethod
+ def _new_instance(obj):
+ if not obj:
+ raise ValueError()
+
+ return Qdisc(obj)
+
+ @property
+ def childs(self):
+ ret = []
+
+ if int(self.handle):
+ ret += get_cls(self.ifindex, parent=self.handle)
+
+ if self.root:
+ ret += get_class(self.ifindex, parent=TC_H_ROOT)
+
+ ret += get_class(self.ifindex, parent=self.handle)
+
+ return ret
+
+# def add(self, socket, flags=None):
+# if not flags:
+# flags = netlink.NLM_F_CREATE
+#
+# ret = capi.rtnl_link_add(socket._sock, self._link, flags)
+# if ret < 0:
+# raise netlink.KernelError(ret)
+#
+# def change(self, socket, flags=0):
+# """Commit changes made to the link object"""
+# if not self._orig:
+# raise NetlinkError('Original link not available')
+# ret = capi.rtnl_link_change(socket._sock, self._orig, self._link, flags)
+# if ret < 0:
+# raise netlink.KernelError(ret)
+#
+# def delete(self, socket):
+# """Attempt to delete this link in the kernel"""
+# ret = capi.rtnl_link_delete(socket._sock, self._link)
+# if ret < 0:
+# raise netlink.KernelError(ret)
+
+ def format(self, details=False, stats=False, nodev=False,
+ noparent=False, indent=''):
+ """Return qdisc as formatted text"""
+ fmt = util.MyFormatter(self, indent)
+
+ buf = fmt.format(self.brief('qdisc', nodev, noparent))
+
+ if details:
+ buf += fmt.nl('\t' + self.details())
+
+ if stats:
+ buf += self.stats(fmt)
+
+# if stats:
+# l = [['Packets', RX_PACKETS, TX_PACKETS],
+# ['Bytes', RX_BYTES, TX_BYTES],
+# ['Errors', RX_ERRORS, TX_ERRORS],
+# ['Dropped', RX_DROPPED, TX_DROPPED],
+# ['Compressed', RX_COMPRESSED, TX_COMPRESSED],
+# ['FIFO Errors', RX_FIFO_ERR, TX_FIFO_ERR],
+# ['Length Errors', RX_LEN_ERR, None],
+# ['Over Errors', RX_OVER_ERR, None],
+# ['CRC Errors', RX_CRC_ERR, None],
+# ['Frame Errors', RX_FRAME_ERR, None],
+# ['Missed Errors', RX_MISSED_ERR, None],
+# ['Abort Errors', None, TX_ABORT_ERR],
+# ['Carrier Errors', None, TX_CARRIER_ERR],
+# ['Heartbeat Errors', None, TX_HBEAT_ERR],
+# ['Window Errors', None, TX_WIN_ERR],
+# ['Collisions', None, COLLISIONS],
+# ['Multicast', None, MULTICAST],
+# ['', None, None],
+# ['Ipv6:', None, None],
+# ['Packets', IP6_INPKTS, IP6_OUTPKTS],
+# ['Bytes', IP6_INOCTETS, IP6_OUTOCTETS],
+# ['Discards', IP6_INDISCARDS, IP6_OUTDISCARDS],
+# ['Multicast Packets', IP6_INMCASTPKTS, IP6_OUTMCASTPKTS],
+# ['Multicast Bytes', IP6_INMCASTOCTETS, IP6_OUTMCASTOCTETS],
+# ['Broadcast Packets', IP6_INBCASTPKTS, IP6_OUTBCASTPKTS],
+# ['Broadcast Bytes', IP6_INBCASTOCTETS, IP6_OUTBCASTOCTETS],
+# ['Delivers', IP6_INDELIVERS, None],
+# ['Forwarded', None, IP6_OUTFORWDATAGRAMS],
+# ['No Routes', IP6_INNOROUTES, IP6_OUTNOROUTES],
+# ['Header Errors', IP6_INHDRERRORS, None],
+# ['Too Big Errors', IP6_INTOOBIGERRORS, None],
+# ['Address Errors', IP6_INADDRERRORS, None],
+# ['Unknown Protocol', IP6_INUNKNOWNPROTOS, None],
+# ['Truncated Packets', IP6_INTRUNCATEDPKTS, None],
+# ['Reasm Timeouts', IP6_REASMTIMEOUT, None],
+# ['Reasm Requests', IP6_REASMREQDS, None],
+# ['Reasm Failures', IP6_REASMFAILS, None],
+# ['Reasm OK', IP6_REASMOKS, None],
+# ['Frag Created', None, IP6_FRAGCREATES],
+# ['Frag Failures', None, IP6_FRAGFAILS],
+# ['Frag OK', None, IP6_FRAGOKS],
+# ['', None, None],
+# ['ICMPv6:', None, None],
+# ['Messages', ICMP6_INMSGS, ICMP6_OUTMSGS],
+# ['Errors', ICMP6_INERRORS, ICMP6_OUTERRORS]]
+#
+# buf += '\n\t%s%s%s%s\n' % (33 * ' ', util.title('RX'),
+# 15 * ' ', util.title('TX'))
+#
+# for row in l:
+# row[0] = util.kw(row[0])
+# row[1] = self.get_stat(row[1]) if row[1] else ''
+# row[2] = self.get_stat(row[2]) if row[2] else ''
+# buf += '\t{0:27} {1:>16} {2:>16}\n'.format(*row)
+
+ return buf
+
+class TcClassCache(netlink.Cache):
+ """Cache of traffic classes"""
+
+ def __init__(self, ifindex, cache=None):
+ if not cache:
+ cache = self._alloc_cache_name('route/class')
+
+ self._protocol = netlink.NETLINK_ROUTE
+ self._nl_cache = cache
+ self._set_arg1(ifindex)
+
+ @staticmethod
+ def _new_object(obj):
+ return TcClass(obj)
+
+ def _new_cache(self, cache):
+ return TcClassCache(self.arg1, cache=cache)
+
+class TcClass(Tc):
+ """Traffic Class"""
+
+ def __init__(self, obj=None):
+ netlink.Object.__init__(self, 'route/class', 'class', obj)
+ self._module_path = 'netlink.route.qdisc.'
+ self._rtnl_class = self._obj2type(self._nl_object)
+ self._rtnl_tc = capi.obj2tc(self._nl_object)
+
+ if self.kind:
+ self._tc_module_lookup()
+
+ @classmethod
+ def from_capi(cls, obj):
+ return cls(capi.class2obj(obj))
+
+ @staticmethod
+ def _obj2type(obj):
+ return capi.obj2class(obj)
+
+ @staticmethod
+ def _new_instance(obj):
+ if not obj:
+ raise ValueError()
+
+ return TcClass(obj)
+
+ @property
+ def childs(self):
+ ret = []
+
+ # classes can have classifiers, child classes and leaf
+ # qdiscs
+ ret += get_cls(self.ifindex, parent=self.handle)
+ ret += get_class(self.ifindex, parent=self.handle)
+ ret += get_qdisc(self.ifindex, parent=self.handle)
+
+ return ret
+
+ def format(self, details=False, _stats=False, nodev=False,
+ noparent=False, indent=''):
+ """Return class as formatted text"""
+ fmt = util.MyFormatter(self, indent)
+
+ buf = fmt.format(self.brief('class', nodev, noparent))
+
+ if details:
+ buf += fmt.nl('\t' + self.details())
+
+ return buf
+
+class ClassifierCache(netlink.Cache):
+ """Cache of traffic classifiers objects"""
+
+ def __init__(self, ifindex, parent, cache=None):
+ if not cache:
+ cache = self._alloc_cache_name('route/cls')
+
+ self._protocol = netlink.NETLINK_ROUTE
+ self._nl_cache = cache
+ self._set_arg1(ifindex)
+ self._set_arg2(int(parent))
+
+ @staticmethod
+ def _new_object(obj):
+ return Classifier(obj)
+
+ def _new_cache(self, cache):
+ return ClassifierCache(self.arg1, self.arg2, cache=cache)
+
+class Classifier(Tc):
+ """Classifier"""
+
+ def __init__(self, obj=None):
+ netlink.Object.__init__(self, 'route/cls', 'cls', obj)
+ self._module_path = 'netlink.route.cls.'
+ self._rtnl_cls = self._obj2type(self._nl_object)
+ self._rtnl_tc = capi.obj2tc(self._nl_object)
+
+ @classmethod
+ def from_capi(cls, obj):
+ return cls(capi.cls2obj(obj))
+
+ @staticmethod
+ def _obj2type(obj):
+ return capi.obj2cls(obj)
+
+ @staticmethod
+ def _new_instance(obj):
+ if not obj:
+ raise ValueError()
+
+ return Classifier(obj)
+
+ @property
+ def priority(self):
+ return capi.rtnl_cls_get_prio(self._rtnl_cls)
+
+ @priority.setter
+ def priority(self, value):
+ capi.rtnl_cls_set_prio(self._rtnl_cls, int(value))
+
+ @property
+ def protocol(self):
+ return capi.rtnl_cls_get_protocol(self._rtnl_cls)
+
+ @protocol.setter
+ def protocol(self, value):
+ capi.rtnl_cls_set_protocol(self._rtnl_cls, int(value))
+
+ @property
+ def childs(self):
+ return []
+
+ def format(self, details=False, _stats=False, nodev=False,
+ noparent=False, indent=''):
+ """Return class as formatted text"""
+ fmt = util.MyFormatter(self, indent)
+
+ buf = fmt.format(self.brief('classifier', nodev, noparent))
+ buf += fmt.format(' {t|priority} {t|protocol}')
+
+ if details:
+ buf += fmt.nl('\t' + self.details())
+
+ return buf
+
+_qdisc_cache = QdiscCache()
+
+def get_qdisc(ifindex, handle=None, parent=None):
+ l = []
+
+ _qdisc_cache.refill()
+
+ for qdisc in _qdisc_cache:
+ if qdisc.ifindex != ifindex:
+ continue
+ if (handle is not None) and (qdisc.handle != handle):
+ continue
+ if (parent is not None) and (qdisc.parent != parent):
+ continue
+ l.append(qdisc)
+
+ return l
+
+_class_cache = {}
+
+def get_class(ifindex, parent, handle=None):
+ l = []
+
+ try:
+ cache = _class_cache[ifindex]
+ except KeyError:
+ cache = TcClassCache(ifindex)
+ _class_cache[ifindex] = cache
+
+ cache.refill()
+
+ for cl in cache:
+ if (parent is not None) and (cl.parent != parent):
+ continue
+ if (handle is not None) and (cl.handle != handle):
+ continue
+ l.append(cl)
+
+ return l
+
+_cls_cache = {}
+
+def get_cls(ifindex, parent, handle=None):
+
+ chain = _cls_cache.get(ifindex, dict())
+
+ try:
+ cache = chain[parent]
+ except KeyError:
+ cache = ClassifierCache(ifindex, parent)
+ chain[parent] = cache
+
+ cache.refill()
+
+ if handle is None:
+ return [ cls for cls in cache ]
+
+ return [ cls for cls in cache if cls.handle == handle ]
diff --git a/python/netlink/util.py b/python/netlink/util.py
new file mode 100644
index 00000000..22ed5cfc
--- /dev/null
+++ b/python/netlink/util.py
@@ -0,0 +1,175 @@
+#
+# Utilities
+#
+# Copyright (c) 2011 Thomas Graf <tgraf@suug.ch>
+#
+
+"""utility module for netlink
+
+"""
+
+from __future__ import absolute_import
+
+from . import core as netlink
+from . import capi as capi
+from string import Formatter
+import types
+
+__version__ = '1.0'
+
+#rename into colored_output
+def _color(t, c):
+ return '{esc}[{color}m{text}{esc}[0m'.format(esc=b'\x1b'.decode(), color=c, text=t)
+
+def black(t):
+ return _color(t, 30)
+
+def red(t):
+ return _color(t, 31)
+
+def green(t):
+ return _color(t, 32)
+
+def yellow(t):
+ return _color(t, 33)
+
+def blue(t):
+ return _color(t, 34)
+
+def magenta(t):
+ return _color(t, 35)
+
+def cyan(t):
+ return _color(t, 36)
+
+def white(t):
+ return _color(t, 37)
+
+def bold(t):
+ return _color(t, 1)
+
+def kw(t):
+ return yellow(t)
+
+def num(t):
+ return str(t)
+
+def string(t):
+ return t
+
+def addr(t):
+ return str(t)
+
+def bad(t):
+ return red(t)
+
+def good(t):
+ return green(t)
+
+def title(t):
+ return t
+
+def boolean(t):
+ return str(t)
+
+def handle(t):
+ return str(t)
+
+class MyFormatter(Formatter):
+ def __init__(self, obj, indent=''):
+ self._obj = obj
+ self._indent = indent
+
+ def _nlattr(self, key):
+ value = getattr(self._obj.__class__, key)
+ if not isinstance(value, property):
+ raise ValueError('Invalid formatting string {0}'.format(key))
+
+ d = getattr(value.fget, 'formatinfo', dict())
+
+ # value = value.fget() is exactly the same
+ value = getattr(self._obj, key)
+
+ if 'fmt' in d:
+ value = d['fmt'](value)
+
+ title_ = d.get('title', None)
+
+ return title_, str(value)
+
+ def get_value(self, key, args, kwds):
+ # Let default get_value() handle ints
+ if not isinstance(key, str):
+ return Formatter.get_value(self, key, args, kwds)
+
+ # HACK, we allow defining strings via fields to allow
+ # conversions
+ if key[:2] == 's|':
+ return key[2:]
+
+ if key[:2] == 't|':
+ # title mode ("TITLE ATTR")
+ include_title = True
+ elif key[:2] == 'a|':
+ # plain attribute mode ("ATTR")
+ include_title = False
+ else:
+ # No special field, have default get_value() get it
+ return Formatter.get_value(self, key, args, kwds)
+
+ key = key[2:]
+ (title_, value) = self._nlattr(key)
+
+ if include_title:
+ if not title_:
+ title_ = key # fall back to key as title
+ value = '{0} {1}'.format(kw(title_), value)
+
+ return value
+
+ def convert_field(self, value, conversion):
+ if conversion == 'r':
+ return repr(value)
+ elif conversion == 's':
+ return str(value)
+ elif conversion == 'k':
+ return kw(value)
+ elif conversion == 'b':
+ return bold(value)
+ elif conversion is None:
+ return value
+
+ raise ValueError('Unknown converion specifier {0!s}'.format(conversion))
+
+ def nl(self, format_string=''):
+ return '\n' + self._indent + self.format(format_string)
+
+NL_BYTE_RATE = 0
+NL_BIT_RATE = 1
+
+class Rate(object):
+ def __init__(self, rate, mode=NL_BYTE_RATE):
+ self._rate = rate
+ self._mode = mode
+
+ def __str__(self):
+ return capi.nl_rate2str(self._rate, self._mode, 32)[1]
+
+ def __int__(self):
+ return self._rate
+
+ def __cmp__(self, other):
+ return int(self) - int(other)
+
+class Size(object):
+ def __init__(self, size):
+ self._size = size
+
+ def __str__(self):
+ return capi.nl_size2str(self._size, 32)[0]
+
+ def __int__(self):
+ return self._size
+
+ def __cmp__(self, other):
+ return int(self) - int(other)
diff --git a/python/netlink/utils.h b/python/netlink/utils.h
new file mode 100644
index 00000000..7836c307
--- /dev/null
+++ b/python/netlink/utils.h
@@ -0,0 +1,41 @@
+struct list_head {
+ struct list_head *next;
+};
+
+#define LIST_HEAD(name) \
+ struct list_head name = { &(name) }
+
+static inline int list_empty(const struct list_head *head)
+{
+ return head->next == head;
+}
+
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+ new->next = head->next;
+ head->next = new;
+}
+
+static inline void list_del(struct list_head *entry, struct list_head *prev)
+{
+ prev->next = entry->next;
+ entry->next = entry;
+}
+
+#define list_for_each_safe(pos, n, head) \
+ for (n = (head), pos = (head)->next; pos != (head); \
+ n = pos, pos = n->next)
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+#define container_of(ptr, type, member) ({ \
+ const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+ (type *)( (char *)__mptr - offsetof(type,member) );})
+
+#ifdef DEBUG
+#define pynl_dbg(fmt, ...) \
+ fprintf(stderr, "%s: " fmt, __func__, __VA_ARGS__)
+#else
+#define pynl_dbg(fmt, ...)
+#endif
diff --git a/python/setup.py.in b/python/setup.py.in
new file mode 100644
index 00000000..346c770f
--- /dev/null
+++ b/python/setup.py.in
@@ -0,0 +1,42 @@
+#!/usr/bin/env python
+
+from distutils.core import setup, Extension
+
+opts = ['-O', '-nodefaultctor']
+include = ['@top_builddir@/include', '@top_srcdir@/include']
+library_dirs = ['@top_builddir@/lib/.libs']
+
+netlink_capi = Extension('netlink/_capi',
+ sources = ['@srcdir@/netlink/capi.i'],
+ include_dirs = include,
+ swig_opts = opts,
+ library_dirs = library_dirs,
+ libraries = ['nl-3'],
+ )
+
+route_capi = Extension('netlink/route/_capi',
+ sources = ['@srcdir@/netlink/route/capi.i'],
+ include_dirs = include,
+ swig_opts = opts,
+ library_dirs = library_dirs,
+ libraries = ['nl-3', 'nl-route-3'],
+ )
+
+genl_capi = Extension('netlink/genl/_capi',
+ sources = ['@srcdir@/netlink/genl/capi.i'],
+ include_dirs = include,
+ swig_opts = opts,
+ library_dirs = library_dirs,
+ libraries = ['nl-3', 'nl-genl-3'],
+ )
+
+setup(name = 'netlink',
+ version = '1.0',
+ description = 'Python wrapper for netlink protocols',
+ author = 'Thomas Graf',
+ author_email = 'tgraf@suug.ch',
+ ext_modules = [netlink_capi, route_capi, genl_capi],
+ package_dir = {'': '@srcdir@'},
+ packages = ['netlink', 'netlink.genl', 'netlink.route',
+ 'netlink.route.links', 'netlink.route.qdisc'],
+ )
diff --git a/python/tests/Makefile.am b/python/tests/Makefile.am
new file mode 100644
index 00000000..15f77fa4
--- /dev/null
+++ b/python/tests/Makefile.am
@@ -0,0 +1,5 @@
+
+# -*- Makefile -*-
+
+EXTRA_DIST = \
+ test-create-bridge.py
diff --git a/python/tests/test-create-bridge.py b/python/tests/test-create-bridge.py
new file mode 100644
index 00000000..216b2491
--- /dev/null
+++ b/python/tests/test-create-bridge.py
@@ -0,0 +1,28 @@
+import netlink.core as netlink
+import netlink.route.capi as capi
+import netlink.route.link as link
+
+sock = netlink.lookup_socket(netlink.NETLINK_ROUTE)
+
+cache = link.LinkCache()
+cache.refill(sock)
+
+testtap1 = cache['testtap1']
+print testtap1
+
+lbr = link.Link()
+lbr.type = 'bridge'
+lbr.name = 'testbridge'
+print lbr
+lbr.add()
+
+cache.refill(sock)
+lbr = cache['testbridge']
+print lbr
+
+lbr.enslave(testtap1)
+cache.refill(sock)
+testtap1 = cache['testtap1']
+
+print capi.rtnl_link_is_bridge(lbr._rtnl_link)
+print capi.rtnl_link_get_master(testtap1._rtnl_link)
diff --git a/src/.gitignore b/src/.gitignore
index 60233c8c..5de9f293 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -1,5 +1,9 @@
genl-ctrl-list
+/nf-ct-add
nf-ct-list
+nf-exp-list
+nf-exp-add
+nf-exp-delete
nf-log
nf-monitor
nl-addr-add
@@ -21,6 +25,12 @@ nl-neightbl-list
nl-qdisc-add
nl-qdisc-delete
nl-qdisc-list
+nl-class-add
+nl-class-delete
+nl-class-list
+nl-cls-add
+nl-cls-delete
+nl-cls-list
nl-route-add
nl-route-delete
nl-route-list
@@ -29,3 +39,8 @@ nl-rule-list
nl-tctree-list
nl-util-addr
nf-queue
+nl-classid-lookup
+nl-pktloc-lookup
+nl-link-enslave
+nl-link-release
+idiag-socket-details
diff --git a/src/Makefile.am b/src/Makefile.am
index dda32a78..f8ac4ca0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2,17 +2,33 @@
SUBDIRS = lib
-AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE
-AM_LDFLAGS = -L${top_builddir}/lib -L${top_builddir}/src/lib -lnl-cli
+AM_CPPFLAGS = -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\"
+AM_CFLAGS = -Wall
+
+LDADD = \
+ ${top_builddir}/src/lib/libnl-cli-3.la \
+ ${top_builddir}/lib/libnl-3.la \
+ ${top_builddir}/lib/libnl-nf-3.la \
+ ${top_builddir}/lib/libnl-genl-3.la \
+ ${top_builddir}/lib/libnl-route-3.la \
+ ${top_builddir}/lib/libnl-idiag-3.la
+
+sbin_PROGRAMS = \
+ genl-ctrl-list \
+ nl-qdisc-add nl-qdisc-list nl-qdisc-delete \
+ nl-class-add nl-class-list nl-class-delete \
+ nl-cls-add nl-cls-list nl-cls-delete \
+ nl-classid-lookup \
+ nl-pktloc-lookup \
+ nl-link-list
noinst_PROGRAMS = \
- genl-ctrl-list \
- nf-ct-list nf-log nf-queue nf-monitor \
+ nf-ct-list nf-ct-add nf-log nf-queue nf-monitor \
+ nf-exp-list nf-exp-add nf-exp-delete \
nl-addr-add nl-addr-delete nl-addr-list \
- nl-link-list nl-link-set nl-link-stats \
+ nl-link-set nl-link-stats \
nl-link-ifindex2name nl-link-name2ifindex \
nl-neigh-add nl-neigh-delete nl-neigh-list \
- nl-qdisc-delete nl-qdisc-list \
nl-rule-list \
nl-neightbl-list \
nl-monitor \
@@ -21,81 +37,70 @@ noinst_PROGRAMS = \
nl-fib-lookup \
nl-list-caches nl-list-sockets \
nl-util-addr \
- nl-pktloc-lookup
+ nl-link-enslave \
+ nl-link-release \
+ idiag-socket-details
genl_ctrl_list_SOURCES = genl-ctrl-list.c
-genl_ctrl_list_LDADD = -lnl-genl -lnl-route
nf_ct_list_SOURCES = nf-ct-list.c
-nf_ct_list_LDADD = -lnl-nf
+nf_ct_add_SOURCES = nf-ct-add.c
nf_log_SOURCES = nf-log.c
-nf_log_LDADD = -lnl-nf
nf_queue_SOURCES = nf-queue.c
-nf_queue_LDADD = -lnl-nf
nf_monitor_SOURCES = nf-monitor.c
-nf_monitor_LDADD = -lnl-nf
+
+nf_exp_list_SOURCES = nf-exp-list.c
+nf_exp_add_SOURCES = nf-exp-add.c
+nf_exp_delete_SOURCES = nf-exp-delete.c
nl_addr_add_SOURCES = nl-addr-add.c
-nl_addr_add_LDADD = -lnl-route
nl_addr_delete_SOURCES = nl-addr-delete.c
-nl_addr_delete_LDADD = -lnl-route
nl_addr_list_SOURCES = nl-addr-list.c
-nl_addr_list_LDADD = -lnl-route
nl_link_list_SOURCES = nl-link-list.c
-nl_link_list_LDADD = -lnl-route
nl_link_set_SOURCES = nl-link-set.c
-nl_link_set_LDADD = -lnl-route
nl_link_stats_SOURCES = nl-link-stats.c
-nl_link_stats_LDADD = -lnl-route
nl_link_ifindex2name_SOURCES = nl-link-ifindex2name.c
-nl_link_ifindex2name_LDADD = -lnl-route
nl_link_name2ifindex_SOURCES = nl-link-name2ifindex.c
-nl_link_name2ifindex_LDADD = -lnl-route
nl_monitor_SOURCES = nl-monitor.c
-nl_monitor_LDADD = -lnl-route
nl_neigh_add_SOURCES = nl-neigh-add.c
-nl_neigh_add_LDADD = -lnl-route
nl_neigh_delete_SOURCES = nl-neigh-delete.c
-nl_neigh_delete_LDADD = -lnl-route
nl_neigh_list_SOURCES = nl-neigh-list.c
-nl_neigh_list_LDADD = -lnl-route
nl_neightbl_list_SOURCES = nl-neightbl-list.c
-nl_neightbl_list_LDADD = -lnl-route
+nl_qdisc_add_SOURCES = nl-qdisc-add.c
nl_qdisc_delete_SOURCES = nl-qdisc-delete.c
-nl_qdisc_delete_LDADD = -lnl-route
nl_qdisc_list_SOURCES = nl-qdisc-list.c
-nl_qdisc_list_LDADD = -lnl-route
+
+nl_class_add_SOURCES = nl-class-add.c
+nl_class_delete_SOURCES = nl-class-delete.c
+nl_class_list_SOURCES = nl-class-list.c
+
+nl_cls_add_SOURCES = nl-cls-add.c
+nl_cls_list_SOURCES = nl-cls-list.c
+nl_cls_delete_SOURCES = nl-cls-delete.c
nl_route_add_SOURCES = nl-route-add.c
-nl_route_add_LDADD = -lnl-route
nl_route_delete_SOURCES = nl-route-delete.c
-nl_route_delete_LDADD = -lnl-route
nl_route_get_SOURCES = nl-route-get.c
-nl_route_get_LDADD = -lnl-route
nl_route_list_SOURCES = nl-route-list.c
-nl_route_list_LDADD = -lnl-route
nl_rule_list_SOURCES = nl-rule-list.c
-nl_rule_list_LDADD = -lnl-route
nl_tctree_list_SOURCES = nl-tctree-list.c
-nl_tctree_list_LDADD = -lnl-route
nl_fib_lookup_SOURCES = nl-fib-lookup.c
-nl_fib_lookup_LDADD = -lnl-route
nl_list_caches_SOURCES = nl-list-caches.c
-nl_list_caches_LDADD = -lnl-route
nl_list_sockets_SOURCES = nl-list-sockets.c
-nl_list_sockets_LDADD = -lnl-route
nl_util_addr_SOURCES = nl-util-addr.c
-nl_util_addr_LDADD = -lnl-route
nl_pktloc_lookup_SOURCES = nl-pktloc-lookup.c
-nl_pktloc_lookup_LDADD = -lnl-route
+
+nl_classid_lookup_SOURCES = nl-classid-lookup.c
+
+idiag_socket_details_SOURCES = idiag-socket-details.c
diff --git a/src/cls/basic.c b/src/cls/basic.c
deleted file mode 100644
index df1c1122..00000000
--- a/src/cls/basic.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * src/cls/basic.c Basic Classifier
- *
- * This library 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 version 2 of the License.
- *
- * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-#include <netlink/route/cls/basic.h>
-#include <netlink/route/cls/ematch.h>
-
-static void print_usage(void)
-{
- printf(
-"Usage: ... basic [OPTIONS]...\n"
-"\n"
-"Options\n"
-" -h, --help Show this help.\n"
-" -e, --ematch=MATCH Extended match (See --ematch help).\n"
-" -c, --classid=HANDLE Target class to classify matching packets to.\n"
- );
- exit(0);
-}
-
-static void basic_parse_argv(struct rtnl_cls *cls, int argc, char **argv)
-{
- uint32_t classid;
-
- for (;;) {
- int c, optidx = 0, err;
- static struct option long_opts[] = {
- { "help", 0, 0, 'h' },
- { "ematch", 1, 0, 'e' },
- { "classid", 1, 0, 'c' },
- { 0, 0, 0, 0 }
- };
-
- c = getopt_long(argc, argv, "he:c:", long_opts, &optidx);
- if (c == -1)
- break;
-
- switch (c) {
- case '?':
- exit(NLE_INVAL);
-
- case 'h':
- print_usage();
-
- case 'e':
-#if 0
- if ((err = parse_ematch_syntax(optarg, &tree)) < 0)
- fatal(err, "Error while parsing ematch: %s",
- nl_geterror(err));
-
- if ((err = rtnl_basic_set_ematch(cls, tree)) < 0)
- fatal(err, "Unable to set ematch: %s",
- nl_geterror(err));
-#endif
- break;
-
- case 'c':
- if ((err = rtnl_tc_str2handle(optarg, &classid)) < 0)
- fatal(err, "Invalid classid \"%s\": %s",
- optarg, nl_geterror(err));
-
- if ((err = rtnl_basic_set_classid(cls, classid)) < 0)
- fatal(err, "Unable to set classid: %s",
- nl_geterror(err));
- break;
- }
- }
-}
-
-static struct cls_module basic_module = {
- .name = "basic",
- .parse_argv = basic_parse_argv,
-};
-
-static void __attribute__ ((constructor)) basic_init(void)
-{
- register_cls_module(&basic_module);
-}
-
-static void __attribute__ ((destructor)) basic_exit(void)
-{
- unregister_cls_module(&basic_module);
-}
diff --git a/src/cls/cgroup.c b/src/cls/cgroup.c
deleted file mode 100644
index ad0392f7..00000000
--- a/src/cls/cgroup.c
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * src/cls/cgroup.c Control Groups Classifier
- *
- * This library 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 version 2 of the License.
- *
- * Copyright (c) 2009 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-#include <netlink/route/cls/cgroup.h>
-#include <netlink/route/cls/ematch.h>
-
-static void print_usage(void)
-{
- printf(
-"Usage: ... cgroup [OPTIONS]...\n"
-"\n"
-"Options\n"
-" -h, --help Show this help.\n"
-" -e, --ematch=MATCH Extended match (See --ematch help).\n"
-" -c, --classid=HANDLE Target class to classify matching packets to.\n"
- );
- exit(0);
-}
-
-static void basic_parse_argv(struct rtnl_cls *cls, int argc, char **argv)
-{
- for (;;) {
- int c, optidx = 0;
- static struct option long_opts[] = {
- { "help", 0, 0, 'h' },
- { "ematch", 1, 0, 'e' },
- { "classid", 1, 0, 'c' },
- { 0, 0, 0, 0 }
- };
-
- c = getopt_long(argc, argv, "he:c:", long_opts, &optidx);
- if (c == -1)
- break;
-
- switch (c) {
- case '?':
- exit(NLE_INVAL);
-
- case 'h':
- print_usage();
-
-#if 0
- case 'e':
- if ((err = parse_ematch_syntax(optarg, &tree)) < 0)
- fatal(err, "Error while parsing ematch: %s",
- nl_geterror(err));
-
- if ((err = rtnl_basic_set_ematch(cls, tree)) < 0)
- fatal(err, "Unable to set ematch: %s",
- nl_geterror(err));
- break;
-#endif
- }
- }
-}
-
-static struct cls_module cgroup_module = {
- .name = "cgroup",
- .parse_argv = basic_parse_argv,
-};
-
-static void __init cgroup_init(void)
-{
- register_cls_module(&cgroup_module);
-}
-
-static void __exit cgroup_exit(void)
-{
- unregister_cls_module(&cgroup_module);
-}
diff --git a/src/cls/utils.c b/src/cls/utils.c
deleted file mode 100644
index ef6603b6..00000000
--- a/src/cls/utils.c
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * src/cls-utils.c Classifier Helpers
- *
- * This library 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 version 2 of the License.
- *
- * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
- */
-
-#include "utils.h"
-
-struct rtnl_cls *nlt_alloc_cls(void)
-{
- struct rtnl_cls *cls;
-
- cls = rtnl_cls_alloc();
- if (!cls)
- fatal(ENOMEM, "Unable to allocate classifier object");
-
- return cls;
-}
-
-void parse_dev(struct rtnl_cls *cls, struct nl_cache *link_cache, char *arg)
-{
- int ival;
-
- if (!(ival = rtnl_link_name2i(link_cache, arg)))
- fatal(ENOENT, "Link \"%s\" does not exist", arg);
-
- rtnl_cls_set_ifindex(cls, ival);
-}
-
-void parse_prio(struct rtnl_cls *cls, char *arg)
-{
- uint32_t prio = parse_u32(arg);
- rtnl_cls_set_prio(cls, prio);
-}
-
-void parse_parent(struct rtnl_cls *cls, char *arg)
-{
- uint32_t parent;
- int err;
-
- if ((err = rtnl_tc_str2handle(arg, &parent)) < 0)
- fatal(err, "Unable to parse handle \"%s\": %s",
- arg, nl_geterror(err));
-
- rtnl_cls_set_parent(cls, parent);
-}
-
-void parse_handle(struct rtnl_cls *cls, char *arg)
-{
- uint32_t handle;
- int err;
-
- if ((err = rtnl_tc_str2handle(arg, &handle)) < 0)
- fatal(err, "Unable to parse handle \"%s\": %s",
- arg, nl_geterror(err));
-
- rtnl_cls_set_handle(cls, handle);
-}
-
-void parse_proto(struct rtnl_cls *cls, char *arg)
-{
- int proto = nl_str2ether_proto(arg);
- if (proto < 0)
- fatal(proto, "Unable to parse protocol \"%s\": %s",
- arg, nl_geterror(proto));
- rtnl_cls_set_protocol(cls, proto);
-}
-
-static NL_LIST_HEAD(cls_modules);
-
-struct cls_module *lookup_cls_mod(struct rtnl_cls_ops *ops)
-{
- struct cls_module *mod;
-
- nl_list_for_each_entry(mod, &cls_modules, list) {
- if (mod->ops == ops)
- return mod;
- }
-
- return NULL;
-}
-
-void register_cls_module(struct cls_module *mod)
-{
- struct rtnl_cls_ops *ops;
-
- if (!(ops = __rtnl_cls_lookup_ops(mod->name)))
- fatal(ENOENT, "Could not locate classifier module \"%s\"",
- mod->name);
-
- if (lookup_cls_mod(ops) != NULL)
- fatal(EEXIST, "Duplicate classifier module registration.");
-
- mod->ops = ops;
- nl_list_add_tail(&mod->list, &cls_modules);
-}
-
-void unregister_cls_module(struct cls_module *mod)
-{
- nl_list_del(&mod->list);
-}
diff --git a/src/cls/utils.h b/src/cls/utils.h
deleted file mode 100644
index 1a8ee9bc..00000000
--- a/src/cls/utils.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * src/cls-utils.h Classifier Helpers
- *
- * This library 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 version 2 of the License.
- *
- * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
- */
-
-#ifndef __CLS_UTILS_H_
-#define __CLS_UTILS_H_
-
-#include "../utils.h"
-#include <netlink/route/classifier-modules.h>
-#include <netlink/route/cls/ematch.h>
-
-struct cls_module
-{
- const char * name;
- struct rtnl_cls_ops * ops;
- void (*parse_argv)(struct rtnl_cls *, int, char **);
- struct nl_list_head list;
-};
-
-extern struct cls_module *lookup_cls_mod(struct rtnl_cls_ops *);
-extern void register_cls_module(struct cls_module *);
-extern void unregister_cls_module(struct cls_module *);
-
-struct ematch_module
-{
- int kind;
- struct rtnl_ematch_ops *ops;
- void (*parse_argv)(struct rtnl_ematch *, int, char **);
- struct nl_list_head list;
-};
-
-extern struct ematch_module *lookup_ematch_mod(struct rtnl_ematch_ops *);
-extern void register_ematch_module(struct ematch_module *);
-extern void unregister_ematch_module(struct ematch_module *);
-
-extern struct rtnl_cls *nlt_alloc_cls(void);
-extern void parse_dev(struct rtnl_cls *, struct nl_cache *, char *);
-extern void parse_prio(struct rtnl_cls *, char *);
-extern void parse_parent(struct rtnl_cls *, char *);
-extern void parse_handle(struct rtnl_cls *, char *);
-extern void parse_proto(struct rtnl_cls *, char *);
-
-extern int parse_ematch_syntax(const char *, struct rtnl_ematch_tree **);
-
-#endif
diff --git a/src/disabled-nl-qdisc-add.c b/src/disabled-nl-qdisc-add.c
deleted file mode 100644
index a1fab4e2..00000000
--- a/src/disabled-nl-qdisc-add.c
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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 <tgraf@suug.ch>
- */
-
-#include "utils.h"
-#include <netlink/route/sch/fifo.h>
-#include <netlink/route/sch/prio.h>
-
-static void print_usage(void)
-{
- printf(
-"Usage: nl-qdisc-add <ifindex> <handle> <parent> <kind>\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 <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 <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 <nbands> map MAP\n"
- "MAP := <prio> <band>\n");
- return -1;
-}
-
-int main(int argc, char *argv[])
-{
- struct nl_sock *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/genl-ctrl-list.c b/src/genl-ctrl-list.c
index e25eb2ae..0895bcce 100644
--- a/src/genl-ctrl-list.c
+++ b/src/genl-ctrl-list.c
@@ -1,12 +1,12 @@
/*
- * src/genl-ctrl-list.c List Generic Netlink Controller
+ * src/genl-ctrl-list.c List Generic Netlink Families
*
* 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-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2012 Thomas Graf <tgraf@suug.ch>
*/
#include <netlink/cli/utils.h>
@@ -20,10 +20,10 @@ static struct nl_cache *alloc_genl_family_cache(struct nl_sock *sk)
static void print_usage(void)
{
printf(
- "Usage: genl-ctrl-list [OPTION]...\n"
+ "Usage: genl-ctrl-list [--details]\n"
"\n"
"Options\n"
- " -f, --format=TYPE Output format { brief | details | stats }\n"
+ " -d, --details Include detailed information in the list\n"
" -h, --help Show this help\n"
" -v, --version Show versioning information\n"
);
@@ -46,18 +46,20 @@ int main(int argc, char *argv[])
for (;;) {
int c, optidx = 0;
static struct option long_opts[] = {
+ { "details", 0, 0, 'd' },
{ "format", 1, 0, 'f' },
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'v' },
{ 0, 0, 0, 0 }
};
- c = getopt_long(argc, argv, "f:hv", long_opts, &optidx);
+ c = getopt_long(argc, argv, "df:hv", long_opts, &optidx);
if (c == -1)
break;
switch (c) {
case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
+ case 'd': params.dp_type = NL_DUMP_DETAILS; break;
case 'h': print_usage(); break;
case 'v': nl_cli_print_version(); break;
}
diff --git a/src/idiag-socket-details.c b/src/idiag-socket-details.c
new file mode 100644
index 00000000..95686768
--- /dev/null
+++ b/src/idiag-socket-details.c
@@ -0,0 +1,90 @@
+/*
+ * src/idiag-socket-details.c List socket details
+ *
+ * This library 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 version 2 of the License.
+ *
+ * Copyright (c) 2013 Sassano Systems LLC <joe@sassanosystems.com>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/idiag/idiagnl.h>
+#include <netlink/idiag/msg.h>
+#include <linux/netlink.h>
+
+static void print_usage(void)
+{
+ printf(
+"Usage: idiag-socket-details [OPTION]\n"
+"\n"
+"Options\n"
+" --summary Show socket detail summary.\n"
+" --details Show socket details on multiple lines.\n"
+" --stats Show full socket statistics.\n"
+" -h, --help Show this help.\n"
+" -v, --version Show versioning information.\n"
+ );
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_cache *idiag_cache;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_nl_cb = NULL,
+ .dp_fd = stdout,
+ };
+ int err = 0;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_INET_DIAG);
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_SUMMARY = 257,
+ ARG_DETAILS = 258,
+ ARG_STATS = 259,
+ ARG_FAMILY,
+ };
+ static struct option long_opts[] = {
+ { "details", 0, 0, ARG_DETAILS },
+ { "summary", 0, 0, ARG_SUMMARY },
+ { "stats", 0, 0, ARG_STATS},
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "hv", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '?': exit(NLE_INVAL);
+ case ARG_SUMMARY: params.dp_type = NL_DUMP_LINE; break;
+ case ARG_DETAILS: params.dp_type = NL_DUMP_DETAILS; break;
+ case ARG_STATS: params.dp_type = NL_DUMP_STATS; break;
+ case 'h': print_usage(); break;
+ case 'v': nl_cli_print_version(); break;
+ }
+ }
+
+ if ((err = idiagnl_msg_alloc_cache(sock, AF_INET, IDIAG_SS_ALL,
+ &idiag_cache))) {
+ nl_cli_fatal(err, "Unable to allocate idiag msg cache: %s",
+ nl_geterror(err));
+ }
+
+ nl_cache_mngt_provide(idiag_cache);
+
+ nl_cache_dump_filter(idiag_cache, &params, NULL);
+
+ nl_cache_mngt_unprovide(idiag_cache);
+ nl_cache_free(idiag_cache);
+ nl_socket_free(sock);
+
+ return 0;
+}
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 80c217c9..51611151 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -1,7 +1,12 @@
# -*- Makefile -*-
-AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DPKGLIBDIR=\"$(pkglibdir)\" -DSYSCONFDIR=\"$(sysconfdir)\" -rdynamic
-AM_LDFLAGS = -L${top_builddir}/lib -ldl
+AM_CPPFLAGS = -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DPKGLIBDIR=\"$(pkglibdir)\" -DSYSCONFDIR=\"$(sysconfdir)\" -rdynamic
+AM_CFLAGS = -Wall
+AM_LDFLAGS = \
+ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
+NL_LIBADD = \
+ -L${top_builddir}/lib \
+ -ldl
#nobase_pkglib_LTLIBRARIES = cls/basic.la cls/ematch/cmp.la
#cls_basic_la_LDFLAGS = -module -version-info 2:0:0
@@ -26,16 +31,15 @@ AM_LDFLAGS = -L${top_builddir}/lib -ldl
# cls/pktloc_syntax.c cls/pktloc_syntax.h
lib_LTLIBRARIES = \
- libnl-cli.la
+ libnl-cli-3.la
-libnl_cli_la_LDFLAGS = -version-info 2:0:0
+libnl_cli_3_la_LIBADD = ${top_builddir}/lib/libnl-3.la \
+ ${top_builddir}/lib/libnl-route-3.la \
+ ${top_builddir}/lib/libnl-nf-3.la \
+ ${top_builddir}/lib/libnl-genl-3.la ${NL_LIBADD}
-libnl_cli_la_LIBADD = ${top_builddir}/lib/libnl.la \
- ${top_builddir}/lib/libnl-route.la \
- ${top_builddir}/lib/libnl-nf.la \
- ${top_builddir}/lib/libnl-genl.la
-
-libnl_cli_la_SOURCES = \
- utils.c addr.c ct.c link.c neigh.c qdisc.c rule.c route.c
+libnl_cli_3_la_SOURCES = \
+ utils.c addr.c ct.c link.c neigh.c rule.c route.c \
+ tc.c qdisc.c class.c cls.c exp.c
# cls/ematch_syntax.c cls/ematch_grammar.c cls/ematch.c
# cls/pktloc_syntax.c cls/pktloc_grammar.c cls/utils.c
diff --git a/src/lib/class.c b/src/lib/class.c
new file mode 100644
index 00000000..96f60cd1
--- /dev/null
+++ b/src/lib/class.c
@@ -0,0 +1,45 @@
+/*
+ * src/lib/class.c CLI Class Helpers
+ *
+ * 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) 2010-2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cli
+ * @defgroup cli_class Traffic Classes
+ * @{
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/class.h>
+
+struct rtnl_class *nl_cli_class_alloc(void)
+{
+ struct rtnl_class *class;
+
+ if (!(class = rtnl_class_alloc()))
+ nl_cli_fatal(ENOMEM, "Unable to allocate class object");
+
+ return class;
+}
+
+struct nl_cache *nl_cli_class_alloc_cache(struct nl_sock *sock, int ifindex)
+{
+ struct nl_cache *cache;
+ int err;
+
+ if ((err = rtnl_class_alloc_cache(sock, ifindex, &cache)) < 0)
+ nl_cli_fatal(err, "Unable to allocate class cache: %s",
+ nl_geterror(err));
+
+ nl_cache_mngt_provide(cache);
+
+ return cache;
+}
+
+/** @} */
diff --git a/src/lib/cls.c b/src/lib/cls.c
new file mode 100644
index 00000000..86d775d2
--- /dev/null
+++ b/src/lib/cls.c
@@ -0,0 +1,71 @@
+/*
+ * src/lib/cls.c CLI Classifier Helpers
+ *
+ * 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) 2010-2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cli
+ * @defgroup cli_cls Classifiers
+ * @{
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/cls.h>
+#include <netlink/route/cls/ematch.h>
+
+struct rtnl_cls *nl_cli_cls_alloc(void)
+{
+ struct rtnl_cls *cls;
+
+ if (!(cls = rtnl_cls_alloc()))
+ nl_cli_fatal(ENOMEM, "Unable to allocate classifier object");
+
+ return cls;
+}
+
+struct nl_cache *nl_cli_cls_alloc_cache(struct nl_sock *sock, int ifindex,
+ uint32_t parent)
+{
+ struct nl_cache *cache;
+ int err;
+
+ if ((err = rtnl_cls_alloc_cache(sock, ifindex, parent, &cache)) < 0)
+ nl_cli_fatal(err, "Unable to allocate classifier cache: %s",
+ nl_geterror(err));
+
+ return cache;
+}
+
+void nl_cli_cls_parse_proto(struct rtnl_cls *cls, char *arg)
+{
+ int proto;
+
+ if ((proto = nl_str2ether_proto(arg)) < 0)
+ nl_cli_fatal(proto, "Unknown protocol \"%s\".", arg);
+
+ rtnl_cls_set_protocol(cls, proto);
+}
+
+struct rtnl_ematch_tree *nl_cli_cls_parse_ematch(struct rtnl_cls *cls, char *arg)
+{
+ struct rtnl_ematch_tree *tree;
+ char *errstr = NULL;
+ int err;
+
+ if ((err = rtnl_ematch_parse_expr(arg, &errstr, &tree)) < 0)
+ nl_cli_fatal(err, "Unable to parse ematch expression: %s",
+ errstr);
+
+ if (errstr)
+ free(errstr);
+
+ return tree;
+}
+
+/** @} */
diff --git a/src/lib/ct.c b/src/lib/ct.c
index 5bab08f7..c9038780 100644
--- a/src/lib/ct.c
+++ b/src/lib/ct.c
@@ -137,6 +137,12 @@ void nl_cli_ct_parse_status(struct nfnl_ct *ct, char *arg)
nfnl_ct_set_status(ct, status);
}
+void nl_cli_ct_parse_zone(struct nfnl_ct *ct, char *arg)
+{
+ uint32_t zone = nl_cli_parse_u32(arg);
+ nfnl_ct_set_zone(ct, zone);
+}
+
#if 0
} else if (arg_match("origicmpid")) {
if (argc > ++idx)
diff --git a/src/lib/exp.c b/src/lib/exp.c
new file mode 100644
index 00000000..a7a74f5a
--- /dev/null
+++ b/src/lib/exp.c
@@ -0,0 +1,165 @@
+/*
+ * src/lib/exp.c CLI Expectation Helpers
+ *
+ * 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) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2012 Rich Fought <rich.fought@watchguard.com>
+ */
+
+/**
+ * @ingroup cli
+ * @defgroup cli_exp Expectation Tracking
+ *
+ * @{
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/exp.h>
+
+struct nfnl_exp *nl_cli_exp_alloc(void)
+{
+ struct nfnl_exp *exp;
+
+ exp = nfnl_exp_alloc();
+ if (!exp)
+ nl_cli_fatal(ENOMEM, "Unable to allocate expectation object");
+
+ return exp;
+}
+
+struct nl_cache *nl_cli_exp_alloc_cache(struct nl_sock *sk)
+{
+ return nl_cli_alloc_cache(sk, "expectation", nfnl_exp_alloc_cache);
+}
+
+void nl_cli_exp_parse_family(struct nfnl_exp *exp, char *arg)
+{
+ int family;
+
+ if ((family = nl_str2af(arg)) == AF_UNSPEC)
+ nl_cli_fatal(EINVAL,
+ "Unable to nl_cli_exp_parse family \"%s\": %s",
+ arg, nl_geterror(NLE_INVAL));
+
+ nfnl_exp_set_family(exp, family);
+}
+
+void nl_cli_exp_parse_timeout(struct nfnl_exp *exp, char *arg)
+{
+ uint32_t timeout = nl_cli_parse_u32(arg);
+ nfnl_exp_set_timeout(exp, timeout);
+}
+
+void nl_cli_exp_parse_id(struct nfnl_exp *exp, char *arg)
+{
+ uint32_t id = nl_cli_parse_u32(arg);
+ nfnl_exp_set_id(exp, id);
+}
+
+void nl_cli_exp_parse_helper_name(struct nfnl_exp *exp, char *arg)
+{
+ nfnl_exp_set_helper_name(exp, arg);
+}
+
+void nl_cli_exp_parse_zone(struct nfnl_exp *exp, char *arg)
+{
+ uint32_t zone = nl_cli_parse_u32(arg);
+ nfnl_exp_set_zone(exp, zone);
+}
+
+void nl_cli_exp_parse_flags(struct nfnl_exp *exp, char *arg)
+{
+ uint32_t flags = nl_cli_parse_u32(arg);
+ nfnl_exp_set_flags(exp, flags);
+}
+
+void nl_cli_exp_parse_class(struct nfnl_exp *exp, char *arg)
+{
+ uint32_t class = nl_cli_parse_u32(arg);
+ nfnl_exp_set_class(exp, class);
+}
+
+void nl_cli_exp_parse_nat_dir(struct nfnl_exp *exp, char *arg)
+{
+ uint32_t nat_dir = nl_cli_parse_u32(arg);
+ nfnl_exp_set_nat_dir(exp, nat_dir);
+}
+
+void nl_cli_exp_parse_fn(struct nfnl_exp *exp, char *arg)
+{
+ nfnl_exp_set_fn(exp, arg);
+}
+
+void nl_cli_exp_parse_src(struct nfnl_exp *exp, int tuple, char *arg)
+{
+ int err;
+ struct nl_addr *a = nl_cli_addr_parse(arg, nfnl_exp_get_family(exp));
+ if ((err = nfnl_exp_set_src(exp, tuple, a)) < 0)
+ nl_cli_fatal(err, "Unable to set source address: %s",
+ nl_geterror(err));
+}
+
+void nl_cli_exp_parse_dst(struct nfnl_exp *exp, int tuple, char *arg)
+{
+ int err;
+ struct nl_addr *a = nl_cli_addr_parse(arg, nfnl_exp_get_family(exp));
+ if ((err = nfnl_exp_set_dst(exp, tuple, a)) < 0)
+ nl_cli_fatal(err, "Unable to set destination address: %s",
+ nl_geterror(err));
+}
+
+void nl_cli_exp_parse_l4protonum(struct nfnl_exp *exp, int tuple, char *arg)
+{
+ int l4protonum;
+
+ if ((l4protonum = nl_str2ip_proto(arg)) < 0)
+ nl_cli_fatal(l4protonum,
+ "Unable to nl_cli_exp_parse protocol \"%s\": %s",
+ arg, nl_geterror(l4protonum));
+
+ nfnl_exp_set_l4protonum(exp, tuple, l4protonum);
+}
+
+void nl_cli_exp_parse_src_port(struct nfnl_exp *exp, int tuple, char *arg)
+{
+ uint32_t sport = nl_cli_parse_u32(arg);
+ uint16_t dport = nfnl_exp_get_dst_port(exp, tuple);
+ nfnl_exp_set_ports(exp, tuple, sport, dport);
+}
+
+void nl_cli_exp_parse_dst_port(struct nfnl_exp *exp, int tuple, char *arg)
+{
+ uint32_t dport = nl_cli_parse_u32(arg);
+ uint16_t sport = nfnl_exp_get_src_port(exp, tuple);
+ nfnl_exp_set_ports(exp, tuple, sport, dport);
+}
+
+void nl_cli_exp_parse_icmp_id(struct nfnl_exp *exp, int tuple, char *arg)
+{
+ uint32_t id = nl_cli_parse_u32(arg);
+ uint8_t type = nfnl_exp_get_icmp_type(exp, tuple);
+ uint8_t code = nfnl_exp_get_icmp_code(exp, tuple);
+ nfnl_exp_set_icmp(exp, tuple, id, type, code);
+}
+
+void nl_cli_exp_parse_icmp_type(struct nfnl_exp *exp, int tuple, char *arg)
+{
+ uint32_t type = nl_cli_parse_u32(arg);
+ uint16_t id = nfnl_exp_get_icmp_id(exp, tuple);
+ uint8_t code = nfnl_exp_get_icmp_code(exp, tuple);
+ nfnl_exp_set_icmp(exp, tuple, id, type, code);
+}
+
+void nl_cli_exp_parse_icmp_code(struct nfnl_exp *exp, int tuple, char *arg)
+{
+ uint32_t code = nl_cli_parse_u32(arg);
+ uint16_t id = nfnl_exp_get_icmp_id(exp, tuple);
+ uint8_t type = nfnl_exp_get_icmp_type(exp, tuple);
+ nfnl_exp_set_icmp(exp, tuple, id, type, code);
+}
+
+/** @} */
diff --git a/src/lib/link.c b/src/lib/link.c
index c192569a..5bce8240 100644
--- a/src/lib/link.c
+++ b/src/lib/link.c
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
*/
/**
@@ -18,6 +18,7 @@
#include <netlink/cli/utils.h>
#include <netlink/cli/link.h>
+#include <linux/if.h>
struct rtnl_link *nl_cli_link_alloc(void)
{
@@ -30,11 +31,30 @@ struct rtnl_link *nl_cli_link_alloc(void)
return link;
}
+struct nl_cache *nl_cli_link_alloc_cache_family(struct nl_sock *sock, int family)
+{
+ struct nl_cache *cache;
+ int err;
+
+ if ((err = rtnl_link_alloc_cache(sock, family, &cache)) < 0)
+ nl_cli_fatal(err, "Unable to allocate link cache: %s",
+ nl_geterror(err));
+
+ nl_cache_mngt_provide(cache);
+
+ return cache;
+}
+
+struct nl_cache *nl_cli_link_alloc_cache(struct nl_sock *sock)
+{
+ return nl_cli_link_alloc_cache_family(sock, AF_UNSPEC);
+}
+
void nl_cli_link_parse_family(struct rtnl_link *link, char *arg)
{
int family;
- if ((family = nl_str2af(arg)) == AF_UNSPEC)
+ if ((family = nl_str2af(arg)) < 0)
nl_cli_fatal(EINVAL,
"Unable to translate address family \"%s\"", arg);
@@ -66,8 +86,16 @@ void nl_cli_link_parse_txqlen(struct rtnl_link *link, char *arg)
void nl_cli_link_parse_weight(struct rtnl_link *link, char *arg)
{
- uint32_t weight = nl_cli_parse_u32(arg);
- rtnl_link_set_weight(link, weight);
+}
+
+void nl_cli_link_parse_ifalias(struct rtnl_link *link, char *arg)
+{
+ if (strlen(arg) > IFALIASZ)
+ nl_cli_fatal(ERANGE,
+ "Link ifalias too big, must not exceed %u in length.",
+ IFALIASZ);
+
+ rtnl_link_set_ifalias(link, arg);
}
/** @} */
diff --git a/src/lib/neigh.c b/src/lib/neigh.c
index a814bd8f..4518e460 100644
--- a/src/lib/neigh.c
+++ b/src/lib/neigh.c
@@ -25,7 +25,7 @@ struct rtnl_neigh *nl_cli_neigh_alloc(void)
neigh = rtnl_neigh_alloc();
if (!neigh)
- nl_cli_fatal(ENOMEM, "Unable to allocate neighbout object");
+ nl_cli_fatal(ENOMEM, "Unable to allocate neighbour object");
return neigh;
}
diff --git a/src/lib/qdisc.c b/src/lib/qdisc.c
index bc7ff925..ccf7d269 100644
--- a/src/lib/qdisc.c
+++ b/src/lib/qdisc.c
@@ -6,67 +6,27 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2008-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2011 Thomas Graf <tgraf@suug.ch>
*/
/**
* @ingroup cli
* @defgroup cli_qdisc Queueing Disciplines
- *
* @{
*/
#include <netlink/cli/utils.h>
#include <netlink/cli/qdisc.h>
+#include <netlink/route/class.h>
struct rtnl_qdisc *nl_cli_qdisc_alloc(void)
{
struct rtnl_qdisc *qdisc;
- qdisc = rtnl_qdisc_alloc();
- if (!qdisc)
+ if (!(qdisc = rtnl_qdisc_alloc()))
nl_cli_fatal(ENOMEM, "Unable to allocate qdisc object");
return qdisc;
}
-void nl_cli_qdisc_parse_dev(struct rtnl_qdisc *qdisc, struct nl_cache *link_cache, char *arg)
-{
- int ival;
-
- if (!(ival = rtnl_link_name2i(link_cache, arg)))
- nl_cli_fatal(ENOENT, "Link \"%s\" does not exist", arg);
-
- rtnl_qdisc_set_ifindex(qdisc, ival);
-}
-
-void nl_cli_qdisc_parse_parent(struct rtnl_qdisc *qdisc, char *arg)
-{
- uint32_t parent;
- int err;
-
- if ((err = rtnl_tc_str2handle(arg, &parent)) < 0)
- nl_cli_fatal(err, "Unable to parse handle \"%s\": %s",
- arg, nl_geterror(err));
-
- rtnl_qdisc_set_parent(qdisc, parent);
-}
-
-void nl_cli_qdisc_parse_handle(struct rtnl_qdisc *qdisc, char *arg)
-{
- uint32_t handle;
- int err;
-
- if ((err = rtnl_tc_str2handle(arg, &handle)) < 0)
- nl_cli_fatal(err, "Unable to parse handle \"%s\": %s",
- arg, nl_geterror(err));
-
- rtnl_qdisc_set_handle(qdisc, handle);
-}
-
-void nl_cli_qdisc_parse_kind(struct rtnl_qdisc *qdisc, char *arg)
-{
- rtnl_qdisc_set_kind(qdisc, arg);
-}
-
/** @} */
diff --git a/src/lib/route.c b/src/lib/route.c
index 05cb2ada..cd3e8978 100644
--- a/src/lib/route.c
+++ b/src/lib/route.c
@@ -198,14 +198,18 @@ void nl_cli_route_parse_table(struct rtnl_route *route, char *arg)
{
unsigned long lval;
char *endptr;
+ int table;
lval = strtoul(arg, &endptr, 0);
if (endptr == arg) {
- if ((lval = rtnl_route_str2table(arg)) < 0)
+ if ((table = rtnl_route_str2table(arg)) < 0)
nl_cli_fatal(EINVAL, "Unknown table name \"%s\"", arg);
}
+ else {
+ table = lval;
+ }
- rtnl_route_set_table(route, lval);
+ rtnl_route_set_table(route, table);
}
void nl_cli_route_parse_prio(struct rtnl_route *route, char *arg)
@@ -233,16 +237,20 @@ void nl_cli_route_parse_protocol(struct rtnl_route *route, char *arg)
{
unsigned long lval;
char *endptr;
+ int proto;
lval = strtoul(arg, &endptr, 0);
if (endptr == arg) {
- if ((lval = rtnl_route_str2proto(arg)) < 0)
+ if ((proto = rtnl_route_str2proto(arg)) < 0)
nl_cli_fatal(EINVAL,
"Unknown routing protocol name \"%s\"",
arg);
}
+ else {
+ proto = lval;
+ }
- rtnl_route_set_protocol(route, lval);
+ rtnl_route_set_protocol(route, proto);
}
void nl_cli_route_parse_type(struct rtnl_route *route, char *arg)
diff --git a/src/lib/tc.c b/src/lib/tc.c
new file mode 100644
index 00000000..dde729fa
--- /dev/null
+++ b/src/lib/tc.c
@@ -0,0 +1,165 @@
+/*
+ * src/lib/tc.c CLI Traffic Control Helpers
+ *
+ * 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink-private/route/tc-api.h>
+
+/**
+ * @ingroup cli
+ * @defgroup cli_tc Traffic Control
+ * @{
+ */
+void nl_cli_tc_parse_dev(struct rtnl_tc *tc, struct nl_cache *link_cache, char *name)
+{
+ struct rtnl_link *link;
+
+ link = rtnl_link_get_by_name(link_cache, name);
+ if (!link)
+ nl_cli_fatal(ENOENT, "Link \"%s\" does not exist.", name);
+
+ rtnl_tc_set_link(tc, link);
+ rtnl_link_put(link);
+}
+
+void nl_cli_tc_parse_parent(struct rtnl_tc *tc, char *arg)
+{
+ uint32_t parent;
+ int err;
+
+ if ((err = rtnl_tc_str2handle(arg, &parent)) < 0)
+ nl_cli_fatal(err, "Unable to parse handle \"%s\": %s",
+ arg, nl_geterror(err));
+
+ rtnl_tc_set_parent(tc, parent);
+}
+
+void nl_cli_tc_parse_handle(struct rtnl_tc *tc, char *arg, int create)
+{
+ uint32_t handle, parent;
+ int err;
+
+ parent = rtnl_tc_get_parent(tc);
+
+ if ((err = rtnl_tc_str2handle(arg, &handle)) < 0) {
+ if (err == -NLE_OBJ_NOTFOUND && create)
+ err = rtnl_classid_generate(arg, &handle, parent);
+
+ if (err < 0)
+ nl_cli_fatal(err, "Unable to parse handle \"%s\": %s",
+ arg, nl_geterror(err));
+ }
+
+ rtnl_tc_set_handle(tc, handle);
+}
+
+void nl_cli_tc_parse_mtu(struct rtnl_tc *tc, char *arg)
+{
+ rtnl_tc_set_mtu(tc, nl_cli_parse_u32(arg));
+}
+
+void nl_cli_tc_parse_mpu(struct rtnl_tc *tc, char *arg)
+{
+ rtnl_tc_set_mpu(tc, nl_cli_parse_u32(arg));
+}
+
+void nl_cli_tc_parse_overhead(struct rtnl_tc *tc, char *arg)
+{
+ rtnl_tc_set_overhead(tc, nl_cli_parse_u32(arg));
+}
+
+void nl_cli_tc_parse_kind(struct rtnl_tc *tc, char *arg)
+{
+ rtnl_tc_set_kind(tc, arg);
+}
+
+void nl_cli_tc_parse_linktype(struct rtnl_tc *tc, char *arg)
+{
+ int type;
+
+ if ((type = nl_str2llproto(arg)) < 0)
+ nl_cli_fatal(type, "Unable to parse linktype \"%s\": %s",
+ arg, nl_geterror(type));
+
+ rtnl_tc_set_linktype(tc, type);
+}
+
+static NL_LIST_HEAD(tc_modules);
+
+static struct nl_cli_tc_module *__nl_cli_tc_lookup(struct rtnl_tc_ops *ops)
+{
+ struct nl_cli_tc_module *tm;
+
+ nl_list_for_each_entry(tm, &tc_modules, tm_list)
+ if (tm->tm_ops == ops)
+ return tm;
+
+ return NULL;
+}
+
+struct nl_cli_tc_module *nl_cli_tc_lookup(struct rtnl_tc_ops *ops)
+{
+ struct nl_cli_tc_module *tm;
+
+ if ((tm = __nl_cli_tc_lookup(ops)))
+ return tm;
+
+ switch (ops->to_type) {
+ case RTNL_TC_TYPE_QDISC:
+ case RTNL_TC_TYPE_CLASS:
+ nl_cli_load_module("cli/qdisc", ops->to_kind);
+ break;
+
+ case RTNL_TC_TYPE_CLS:
+ nl_cli_load_module("cli/cls", ops->to_kind);
+ break;
+
+ default:
+ nl_cli_fatal(EINVAL, "BUG: unhandled TC object type %d",
+ ops->to_type);
+ }
+
+ if (!(tm = __nl_cli_tc_lookup(ops))) {
+ nl_cli_fatal(EINVAL, "Application bug: The shared library for "
+ "the tc object \"%s\" was successfully loaded but it "
+ "seems that module did not register itself",
+ ops->to_kind);
+ }
+
+ return tm;
+}
+
+void nl_cli_tc_register(struct nl_cli_tc_module *tm)
+{
+ struct rtnl_tc_ops *ops;
+
+ if (!(ops = rtnl_tc_lookup_ops(tm->tm_type, tm->tm_name))) {
+ nl_cli_fatal(ENOENT, "Unable to register CLI TC module "
+ "\"%s\": No matching libnl TC module found.", tm->tm_name);
+ }
+
+ if (__nl_cli_tc_lookup(ops)) {
+ nl_cli_fatal(EEXIST, "Unable to register CLI TC module "
+ "\"%s\": Module already registered.", tm->tm_name);
+ }
+
+ tm->tm_ops = ops;
+
+ nl_list_add_tail(&tm->tm_list, &tc_modules);
+}
+
+void nl_cli_tc_unregister(struct nl_cli_tc_module *tm)
+{
+ nl_list_del(&tm->tm_list);
+}
+
+
+/** @} */
diff --git a/src/lib/utils.c b/src/lib/utils.c
index 02a7be12..e5eacdec 100644
--- a/src/lib/utils.c
+++ b/src/lib/utils.c
@@ -13,10 +13,25 @@
* @defgroup cli Command Line Interface API
*
* @{
+ *
+ * These modules provide an interface for text based applications. The
+ * functions provided are wrappers for their libnl equivalent with
+ * added error handling. The functions check for allocation failures,
+ * invalid input, and unknown types and will print error messages
+ * accordingly via nl_cli_fatal().
*/
#include <netlink/cli/utils.h>
+/**
+ * Parse a text based 32 bit unsigned integer argument
+ * @arg arg Integer in text form.
+ *
+ * Tries to convert the number provided in arg to a uint32_t. Will call
+ * nl_cli_fatal() if the conversion fails.
+ *
+ * @return 32bit unsigned integer.
+ */
uint32_t nl_cli_parse_u32(const char *arg)
{
unsigned long lval;
@@ -34,7 +49,7 @@ void nl_cli_print_version(void)
{
printf("libnl tools version %s\n", LIBNL_VERSION);
printf(
- "Copyright (C) 2003-2009 Thomas Graf <tgraf@redhat.com>\n"
+ "Copyright (C) 2003-2010 Thomas Graf <tgraf@redhat.com>\n"
"\n"
"This program comes with ABSOLUTELY NO WARRANTY. This is free \n"
"software, and you are welcome to redistribute it under certain\n"
@@ -44,9 +59,18 @@ void nl_cli_print_version(void)
exit(0);
}
+/**
+ * Print error message and quit application
+ * @arg err Error code.
+ * @arg fmt Error message.
+ *
+ * Prints the formatted error message to stderr and quits the application
+ * using the provided error code.
+ */
void nl_cli_fatal(int err, const char *fmt, ...)
{
va_list ap;
+ char buf[256];
fprintf(stderr, "Error: ");
@@ -56,7 +80,7 @@ void nl_cli_fatal(int err, const char *fmt, ...)
va_end(ap);
fprintf(stderr, "\n");
} else
- fprintf(stderr, "%s\n", strerror(err));
+ fprintf(stderr, "%s\n", strerror_r(err, buf, sizeof(buf)));
exit(abs(err));
}
@@ -102,8 +126,6 @@ int nl_cli_parse_dumptype(const char *str)
return NL_DUMP_DETAILS;
else if (!strcasecmp(str, "stats"))
return NL_DUMP_STATS;
- else if (!strcasecmp(str, "env"))
- return NL_DUMP_ENV;
else
nl_cli_fatal(EINVAL, "Invalid dump type \"%s\".\n", str);
@@ -113,20 +135,34 @@ int nl_cli_parse_dumptype(const char *str)
int nl_cli_confirm(struct nl_object *obj, struct nl_dump_params *params,
int default_yes)
{
- int answer;
-
nl_object_dump(obj, params);
- printf("Delete? (%c/%c) ",
- default_yes ? 'Y' : 'y',
- default_yes ? 'n' : 'N');
- do {
- answer = tolower(getchar());
- if (answer == '\n')
+ for (;;) {
+ char buf[32] = { 0 };
+ int answer;
+
+ printf("Delete? (%c/%c) ",
+ default_yes ? 'Y' : 'y',
+ default_yes ? 'n' : 'N');
+
+ if (!fgets(buf, sizeof(buf), stdin)) {
+ fprintf(stderr, "Error while reading\n.");
+ continue;
+ }
+
+ switch ((answer = tolower(buf[0]))) {
+ case '\n':
answer = default_yes ? 'y' : 'n';
- } while (answer != 'y' && answer != 'n');
+ case 'y':
+ case 'n':
+ return answer == 'y';
+ }
+
+ fprintf(stderr, "Invalid input, try again.\n");
+ }
+
+ return 0;
- return answer == 'y';
}
struct nl_cache *nl_cli_alloc_cache(struct nl_sock *sock, const char *name,
@@ -144,4 +180,17 @@ struct nl_cache *nl_cli_alloc_cache(struct nl_sock *sock, const char *name,
return cache;
}
+void nl_cli_load_module(const char *prefix, const char *name)
+{
+ char path[FILENAME_MAX+1];
+ void *handle;
+
+ snprintf(path, sizeof(path), "%s/%s/%s.so",
+ PKGLIBDIR, prefix, name);
+
+ if (!(handle = dlopen(path, RTLD_NOW)))
+ nl_cli_fatal(ENOENT, "Unable to load module \"%s\": %s\n",
+ path, dlerror());
+}
+
/** @} */
diff --git a/src/nf-ct-add.c b/src/nf-ct-add.c
new file mode 100644
index 00000000..8ad4c534
--- /dev/null
+++ b/src/nf-ct-add.c
@@ -0,0 +1,142 @@
+/*
+ * src/nf-ct-list.c List Conntrack Entries
+ *
+ * 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-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/ct.h>
+
+static int quiet = 0;
+
+static void print_usage(void)
+{
+ printf(
+ "Usage: nf-ct-add [OPTION]... [CONNTRACK ENTRY]\n"
+ "\n"
+ "Options\n"
+ " -q, --quiet Do not print informal notifications.\n"
+ " -h, --help Show this help\n"
+ " -v, --version Show versioning information\n"
+ "\n"
+ "Conntrack Selection\n"
+ " -p, --proto=PROTOCOL Protocol\n"
+ " --orig-src=ADDR Original source address\n"
+ " --orig-sport=PORT Original source port\n"
+ " --orig-dst=ADDR Original destination address\n"
+ " --orig-dport=PORT Original destination port\n"
+ " --reply-src=ADDR Reply source address\n"
+ " --reply-sport=PORT Reply source port\n"
+ " --reply-dst=ADDR Reply destination address\n"
+ " --reply-dport=PORT Reply destination port\n"
+ " -F, --family=FAMILY Address family\n"
+ " --mark=NUM Mark value\n"
+ " --timeout=NUM Timeout value\n"
+ " --status Bitset representing status of connection.\n"
+ " --zone=NUM Zone value\n"
+ );
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nfnl_ct *ct;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ int err, nlflags = NLM_F_CREATE;
+
+ ct = nl_cli_ct_alloc();
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_ORIG_SRC = 257,
+ ARG_ORIG_SPORT = 258,
+ ARG_ORIG_DST,
+ ARG_ORIG_DPORT,
+ ARG_REPLY_SRC,
+ ARG_REPLY_SPORT,
+ ARG_REPLY_DST,
+ ARG_REPLY_DPORT,
+ ARG_MARK,
+ ARG_TIMEOUT,
+ ARG_STATUS,
+ ARG_ZONE,
+ };
+ static struct option long_opts[] = {
+ { "quiet", 0, 0, 'q' },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "proto", 1, 0, 'p' },
+ { "orig-src", 1, 0, ARG_ORIG_SRC },
+ { "orig-sport", 1, 0, ARG_ORIG_SPORT },
+ { "orig-dst", 1, 0, ARG_ORIG_DST },
+ { "orig-dport", 1, 0, ARG_ORIG_DPORT },
+ { "reply-src", 1, 0, ARG_REPLY_SRC },
+ { "reply-sport", 1, 0, ARG_REPLY_SPORT },
+ { "reply-dst", 1, 0, ARG_REPLY_DST },
+ { "reply-dport", 1, 0, ARG_REPLY_DPORT },
+ { "family", 1, 0, 'F' },
+ { "mark", 1, 0, ARG_MARK },
+ { "timeout", 1, 0, ARG_TIMEOUT },
+ { "status", 1, 0, ARG_STATUS },
+ { "zone", 1, 0, ARG_ZONE },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "46q:hv:p:F:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '?': exit(NLE_INVAL);
+ case 'q': quiet = 1; break;
+ case '4': nfnl_ct_set_family(ct, AF_INET); break;
+ case '6': nfnl_ct_set_family(ct, AF_INET6); break;
+ case 'h': print_usage(); break;
+ case 'v': nl_cli_print_version(); break;
+ case 'p': nl_cli_ct_parse_protocol(ct, optarg); break;
+ case ARG_ORIG_SRC: nl_cli_ct_parse_src(ct, 0, optarg); break;
+ case ARG_ORIG_SPORT: nl_cli_ct_parse_src_port(ct, 0, optarg); break;
+ case ARG_ORIG_DST: nl_cli_ct_parse_dst(ct, 0, optarg); break;
+ case ARG_ORIG_DPORT: nl_cli_ct_parse_dst_port(ct, 0, optarg); break;
+ case ARG_REPLY_SRC: nl_cli_ct_parse_src(ct, 1, optarg); break;
+ case ARG_REPLY_SPORT: nl_cli_ct_parse_src_port(ct, 1, optarg); break;
+ case ARG_REPLY_DST: nl_cli_ct_parse_dst(ct, 1, optarg); break;
+ case ARG_REPLY_DPORT: nl_cli_ct_parse_dst_port(ct, 1, optarg); break;
+ case 'F': nl_cli_ct_parse_family(ct, optarg); break;
+ case ARG_MARK: nl_cli_ct_parse_mark(ct, optarg); break;
+ case ARG_TIMEOUT: nl_cli_ct_parse_timeout(ct, optarg); break;
+ case ARG_STATUS: nl_cli_ct_parse_status(ct, optarg); break;
+ case ARG_ZONE: nl_cli_ct_parse_zone(ct, optarg); break;
+ }
+ }
+
+ if (!quiet) {
+ printf("Adding ");
+ nl_object_dump(OBJ_CAST(ct), &params);
+ }
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_NETFILTER);
+
+ if ((err = nfnl_ct_add(sock, ct, nlflags)) < 0)
+ nl_cli_fatal(err, "Unable to add conntrack: %s", nl_geterror(err));
+
+ if (!quiet) {
+ printf("Added ");
+ nl_object_dump(OBJ_CAST(ct), &params);
+ }
+
+ return 0;
+}
diff --git a/src/nf-exp-add.c b/src/nf-exp-add.c
new file mode 100644
index 00000000..4b7f9d98
--- /dev/null
+++ b/src/nf-exp-add.c
@@ -0,0 +1,187 @@
+/*
+ * src/nf-exp-add.c Create an expectation
+ *
+ * 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-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ * Copyright (c) 2012 Rich Fought <rich.fought@watchguard.com>
+ *
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/exp.h>
+
+static int quiet = 0;
+
+static void print_usage(void)
+{
+ printf(
+ "Usage: nf-exp-list [OPTION]... [CONNTRACK ENTRY]\n"
+ "\n"
+ "Options\n"
+ " --replace Replace the address if it exists.\n"
+ " -q, --quiet Do not print informal notifications.\n"
+ " -h, --help Show this help\n"
+ " -v, --version Show versioning information\n"
+ "\n"
+ "Expectation Selection\n"
+ " -i, --id=NUM Identifier\n"
+ " --expect-proto=PROTOCOL Expectation protocol\n"
+ " --expect-src=ADDR Expectation source address\n"
+ " --expect-sport=PORT Expectation source port\n"
+ " --expect-dst=ADDR Expectation destination address\n"
+ " --expect-dport=PORT Expectation destination port\n"
+ " --master-proto=PROTOCOL Master conntrack protocol\n"
+ " --master-src=ADDR Master conntrack source address\n"
+ " --master-sport=PORT Master conntrack source port\n"
+ " --master-dst=ADDR Master conntrack destination address\n"
+ " --master-dport=PORT Master conntrack destination port\n"
+ " --mask-proto=PROTOCOL Mask protocol\n"
+ " --mask-src=ADDR Mask source address\n"
+ " --mask-sport=PORT Mask source port\n"
+ " --mask-dst=ADDR Mask destination address\n"
+ " --mask-dport=PORT Mask destination port\n"
+ " -F, --family=FAMILY Address family\n"
+ " --timeout=NUM Timeout value\n"
+ " --helper=STRING Helper Name\n"
+ " --flags Flags (Kernel 2.6.37)\n"
+ );
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nfnl_exp *exp;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ int err, nlflags = NLM_F_CREATE;
+
+ exp = nl_cli_exp_alloc();
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_MARK = 270,
+ ARG_TCP_STATE = 271,
+ ARG_EXPECT_PROTO,
+ ARG_EXPECT_SRC,
+ ARG_EXPECT_SPORT,
+ ARG_EXPECT_DST,
+ ARG_EXPECT_DPORT,
+ ARG_MASTER_PROTO,
+ ARG_MASTER_SRC,
+ ARG_MASTER_SPORT,
+ ARG_MASTER_DST,
+ ARG_MASTER_DPORT,
+ ARG_MASK_PROTO,
+ ARG_MASK_SRC,
+ ARG_MASK_SPORT,
+ ARG_MASK_DST,
+ ARG_MASK_DPORT,
+ ARG_NAT_PROTO,
+ ARG_NAT_SRC,
+ ARG_NAT_SPORT,
+ ARG_NAT_DST,
+ ARG_NAT_DPORT,
+ ARG_NAT_DIR,
+ ARG_TIMEOUT,
+ ARG_HELPER_NAME,
+ ARG_REPLACE,
+ ARG_FLAGS,
+ };
+ static struct option long_opts[] = {
+ { "replace", 1, 0, ARG_REPLACE },
+ { "quiet", 0, 0, 'q' },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "id", 1, 0, 'i' },
+ { "expect-proto", 1, 0, ARG_EXPECT_PROTO },
+ { "expect-src", 1, 0, ARG_EXPECT_SRC },
+ { "expect-sport", 1, 0, ARG_EXPECT_SPORT },
+ { "expect-dst", 1, 0, ARG_EXPECT_DST },
+ { "expect-dport", 1, 0, ARG_EXPECT_DPORT },
+ { "master-proto", 1, 0, ARG_MASTER_PROTO },
+ { "master-src", 1, 0, ARG_MASTER_SRC },
+ { "master-sport", 1, 0, ARG_MASTER_SPORT },
+ { "master-dst", 1, 0, ARG_MASTER_DST },
+ { "master-dport", 1, 0, ARG_MASTER_DPORT },
+ { "mask-proto", 1, 0, ARG_MASK_PROTO },
+ { "mask-src", 1, 0, ARG_MASK_SRC },
+ { "mask-sport", 1, 0, ARG_MASK_SPORT },
+ { "mask-dst", 1, 0, ARG_MASK_DST },
+ { "mask-dport", 1, 0, ARG_MASK_DPORT },
+ { "nat-proto", 1, 0, ARG_NAT_PROTO },
+ { "nat-src", 1, 0, ARG_NAT_SRC },
+ { "nat-sport", 1, 0, ARG_NAT_SPORT },
+ { "nat-dst", 1, 0, ARG_NAT_DST },
+ { "nat-dport", 1, 0, ARG_NAT_DPORT },
+ { "nat-dir", 1, 0, ARG_NAT_DIR },
+ { "family", 1, 0, 'F' },
+ { "timeout", 1, 0, ARG_TIMEOUT },
+ { "helper", 1, 0, ARG_HELPER_NAME },
+ { "flags", 1, 0, ARG_FLAGS},
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '?': exit(NLE_INVAL);
+ case ARG_REPLACE: nlflags |= NLM_F_REPLACE; break;
+ case 'q': quiet = 1; break;
+ case '4': nfnl_exp_set_family(exp, AF_INET); break;
+ case '6': nfnl_exp_set_family(exp, AF_INET6); break;
+ case 'h': print_usage(); break;
+ case 'v': nl_cli_print_version(); break;
+ case 'i': nl_cli_exp_parse_id(exp, optarg); break;
+ case ARG_EXPECT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_EXPECT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_EXPECT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_EXPECT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_EXPECT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_MASTER_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASTER_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASTER_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASTER_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASTER_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASK_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
+ case ARG_MASK_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
+ case ARG_MASK_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
+ case ARG_MASK_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
+ case ARG_MASK_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
+ case ARG_NAT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
+ case ARG_NAT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
+ case ARG_NAT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
+ case ARG_NAT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
+ case ARG_NAT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_NAT, optarg); break;
+ case ARG_NAT_DIR: nl_cli_exp_parse_nat_dir(exp, optarg); break;
+ case 'F': nl_cli_exp_parse_family(exp, optarg); break;
+ case ARG_TIMEOUT: nl_cli_exp_parse_timeout(exp, optarg); break;
+ case ARG_HELPER_NAME: nl_cli_exp_parse_helper_name(exp, optarg); break;
+ case ARG_FLAGS: nl_cli_exp_parse_flags(exp, optarg); break;
+ }
+ }
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_NETFILTER);
+
+ if ((err = nfnl_exp_add(sock, exp, nlflags)) < 0)
+ nl_cli_fatal(err, "Unable to add expectation: %s", nl_geterror(err));
+
+ if (!quiet) {
+ printf("Added ");
+ nl_object_dump(OBJ_CAST(exp), &params);
+ }
+
+ return 0;
+}
diff --git a/src/nf-exp-delete.c b/src/nf-exp-delete.c
new file mode 100644
index 00000000..2ec45aea
--- /dev/null
+++ b/src/nf-exp-delete.c
@@ -0,0 +1,165 @@
+/*
+ * src/nf-exp-delete.c Delete an expectation
+ *
+ * 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-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ * Copyright (c) 2012 Rich Fought <rich.fought@watchguard.com>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/exp.h>
+
+static int quiet = 0;
+
+static void print_usage(void)
+{
+ printf(
+ "Usage: nf-exp-list [OPTION]... [CONNTRACK ENTRY]\n"
+ "\n"
+ "Options\n"
+ " --replace Replace the address if it exists.\n"
+ " -q, --quiet Do not print informal notifications.\n"
+ " -h, --help Show this help\n"
+ " -v, --version Show versioning information\n"
+ "\n"
+ "Expectation Selection\n"
+ " -i, --id=NUM Identifier\n"
+ " --expect-proto=PROTOCOL Expectation protocol\n"
+ " --expect-src=ADDR Expectation source address\n"
+ " --expect-sport=PORT Expectation source port\n"
+ " --expect-dst=ADDR Expectation destination address\n"
+ " --expect-dport=PORT Expectation destination port\n"
+ " --master-proto=PROTOCOL Master conntrack protocol\n"
+ " --master-src=ADDR Master conntrack source address\n"
+ " --master-sport=PORT Master conntrack source port\n"
+ " --master-dst=ADDR Master conntrack destination address\n"
+ " --master-dport=PORT Master conntrack destination port\n"
+ " --mask-proto=PROTOCOL Mask protocol\n"
+ " --mask-src=ADDR Mask source address\n"
+ " --mask-sport=PORT Mask source port\n"
+ " --mask-dst=ADDR Mask destination address\n"
+ " --mask-dport=PORT Mask destination port\n"
+ " -F, --family=FAMILY Address family\n"
+ " --timeout=NUM Timeout value\n"
+ " --helper=STRING Helper Name\n"
+ " --flags Flags\n"
+ );
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nfnl_exp *exp;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ int err, nlflags = 0;
+
+ exp = nl_cli_exp_alloc();
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_MARK = 270,
+ ARG_TCP_STATE = 271,
+ ARG_EXPECT_PROTO,
+ ARG_EXPECT_SRC,
+ ARG_EXPECT_SPORT,
+ ARG_EXPECT_DST,
+ ARG_EXPECT_DPORT,
+ ARG_MASTER_PROTO,
+ ARG_MASTER_SRC,
+ ARG_MASTER_SPORT,
+ ARG_MASTER_DST,
+ ARG_MASTER_DPORT,
+ ARG_MASK_PROTO,
+ ARG_MASK_SRC,
+ ARG_MASK_SPORT,
+ ARG_MASK_DST,
+ ARG_MASK_DPORT,
+ ARG_TIMEOUT,
+ ARG_HELPER_NAME,
+ ARG_FLAGS,
+ };
+ static struct option long_opts[] = {
+ { "quiet", 0, 0, 'q' },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "id", 1, 0, 'i' },
+ { "expect-proto", 1, 0, ARG_EXPECT_PROTO },
+ { "expect-src", 1, 0, ARG_EXPECT_SRC },
+ { "expect-sport", 1, 0, ARG_EXPECT_SPORT },
+ { "expect-dst", 1, 0, ARG_EXPECT_DST },
+ { "expect-dport", 1, 0, ARG_EXPECT_DPORT },
+ { "master-proto", 1, 0, ARG_MASTER_PROTO },
+ { "master-src", 1, 0, ARG_MASTER_SRC },
+ { "master-sport", 1, 0, ARG_MASTER_SPORT },
+ { "master-dst", 1, 0, ARG_MASTER_DST },
+ { "master-dport", 1, 0, ARG_MASTER_DPORT },
+ { "mask-proto", 1, 0, ARG_MASK_PROTO },
+ { "mask-src", 1, 0, ARG_MASK_SRC },
+ { "mask-sport", 1, 0, ARG_MASK_SPORT },
+ { "mask-dst", 1, 0, ARG_MASK_DST },
+ { "mask-dport", 1, 0, ARG_MASK_DPORT },
+ { "family", 1, 0, 'F' },
+ { "timeout", 1, 0, ARG_TIMEOUT },
+ { "helper", 1, 0, ARG_HELPER_NAME },
+ { "flags", 1, 0, ARG_FLAGS},
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '?': exit(NLE_INVAL);
+ case 'q': quiet = 1; break;
+ case '4': nfnl_exp_set_family(exp, AF_INET); break;
+ case '6': nfnl_exp_set_family(exp, AF_INET6); break;
+ case 'h': print_usage(); break;
+ case 'v': nl_cli_print_version(); break;
+ case 'i': nl_cli_exp_parse_id(exp, optarg); break;
+ case ARG_EXPECT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_EXPECT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_EXPECT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_EXPECT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_EXPECT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_MASTER_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASTER_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASTER_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASTER_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASTER_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASK_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
+ case ARG_MASK_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
+ case ARG_MASK_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
+ case ARG_MASK_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
+ case ARG_MASK_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASK, optarg); break;
+ case 'F': nl_cli_exp_parse_family(exp, optarg); break;
+ case ARG_TIMEOUT: nl_cli_exp_parse_timeout(exp, optarg); break;
+ case ARG_HELPER_NAME: nl_cli_exp_parse_helper_name(exp, optarg); break;
+ case ARG_FLAGS: nl_cli_exp_parse_flags(exp, optarg); break;
+ }
+ }
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_NETFILTER);
+
+ if ((err = nfnl_exp_del(sock, exp, nlflags)) < 0)
+ nl_cli_fatal(err, "Unable to delete expectation: %s", nl_geterror(err));
+
+ if (!quiet) {
+ printf("Deleted ");
+ nl_object_dump(OBJ_CAST(exp), &params);
+ }
+
+ return 0;
+}
diff --git a/src/nf-exp-list.c b/src/nf-exp-list.c
new file mode 100644
index 00000000..1c6ec690
--- /dev/null
+++ b/src/nf-exp-list.c
@@ -0,0 +1,137 @@
+/*
+ * src/nf-exp-list.c List Expectation Entries
+ *
+ * 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-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2007 Philip Craig <philipc@snapgear.com>
+ * Copyright (c) 2007 Secure Computing Corporation
+ * Copyright (c) 2012 Rich Fought <rich.fought@watchguard.com>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/exp.h>
+
+static void print_usage(void)
+{
+ printf(
+ "Usage: nf-exp-list [OPTION]... [EXPECTATION ENTRY]\n"
+ "\n"
+ "Options\n"
+ " -f, --format=TYPE Output format { brief | details }\n"
+ " -h, --help Show this help\n"
+ " -v, --version Show versioning information\n"
+ "\n"
+ "Expectation Selection\n"
+ " -i, --id=NUM Identifier\n"
+ " --expect-proto=PROTOCOL Expectation protocol\n"
+ " --expect-src=ADDR Expectation source address\n"
+ " --expect-sport=PORT Expectation source port\n"
+ " --expect-dst=ADDR Expectation destination address\n"
+ " --expect-dport=PORT Expectation destination port\n"
+ " --master-proto=PROTOCOL Master conntrack protocol\n"
+ " --master-src=ADDR Master conntrack source address\n"
+ " --master-sport=PORT Master conntrack source port\n"
+ " --master-dst=ADDR Master conntrack destination address\n"
+ " --master-dport=PORT Master conntrack destination port\n"
+ " -F, --family=FAMILY Address family\n"
+ " --timeout=NUM Timeout value\n"
+ " --helper=STRING Helper Name\n"
+ //" --flags Flags\n"
+ );
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_cache *exp_cache;
+ struct nfnl_exp *exp;
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+
+ exp = nl_cli_exp_alloc();
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_MARK = 270,
+ ARG_TCP_STATE = 271,
+ ARG_EXPECT_PROTO,
+ ARG_EXPECT_SRC,
+ ARG_EXPECT_SPORT,
+ ARG_EXPECT_DST,
+ ARG_EXPECT_DPORT,
+ ARG_MASTER_PROTO,
+ ARG_MASTER_SRC,
+ ARG_MASTER_SPORT,
+ ARG_MASTER_DST,
+ ARG_MASTER_DPORT,
+ ARG_TIMEOUT,
+ ARG_HELPER_NAME,
+ ARG_FLAGS,
+ };
+ static struct option long_opts[] = {
+ { "format", 1, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "id", 1, 0, 'i' },
+ { "expect-proto", 1, 0, ARG_EXPECT_PROTO },
+ { "expect-src", 1, 0, ARG_EXPECT_SRC },
+ { "expect-sport", 1, 0, ARG_EXPECT_SPORT },
+ { "expect-dst", 1, 0, ARG_EXPECT_DST },
+ { "expect-dport", 1, 0, ARG_EXPECT_DPORT },
+ { "master-proto", 1, 0, ARG_MASTER_PROTO },
+ { "master-src", 1, 0, ARG_MASTER_SRC },
+ { "master-sport", 1, 0, ARG_MASTER_SPORT },
+ { "master-dst", 1, 0, ARG_MASTER_DST },
+ { "master-dport", 1, 0, ARG_MASTER_DPORT },
+ { "family", 1, 0, 'F' },
+ { "timeout", 1, 0, ARG_TIMEOUT },
+ { "helper", 1, 0, ARG_HELPER_NAME },
+ { "flags", 1, 0, ARG_FLAGS},
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "46f:hvi:p:F:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '?': exit(NLE_INVAL);
+ case '4': nfnl_exp_set_family(exp, AF_INET); break;
+ case '6': nfnl_exp_set_family(exp, AF_INET6); break;
+ case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
+ case 'h': print_usage(); break;
+ case 'v': nl_cli_print_version(); break;
+ case 'i': nl_cli_exp_parse_id(exp, optarg); break;
+ case ARG_EXPECT_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_EXPECT_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_EXPECT_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_EXPECT_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_EXPECT_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_EXPECT, optarg); break;
+ case ARG_MASTER_PROTO: nl_cli_exp_parse_l4protonum(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASTER_SRC: nl_cli_exp_parse_src(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASTER_SPORT: nl_cli_exp_parse_src_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASTER_DST: nl_cli_exp_parse_dst(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case ARG_MASTER_DPORT: nl_cli_exp_parse_dst_port(exp, NFNL_EXP_TUPLE_MASTER, optarg); break;
+ case 'F': nl_cli_exp_parse_family(exp, optarg); break;
+ case ARG_TIMEOUT: nl_cli_exp_parse_timeout(exp, optarg); break;
+ case ARG_HELPER_NAME: nl_cli_exp_parse_helper_name(exp, optarg); break;
+ case ARG_FLAGS: nl_cli_exp_parse_flags(exp, optarg); break;
+ }
+ }
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_NETFILTER);
+ exp_cache = nl_cli_exp_alloc_cache(sock);
+
+ nl_cache_dump_filter(exp_cache, &params, OBJ_CAST(exp));
+
+ return 0;
+}
diff --git a/src/nf-log.c b/src/nf-log.c
index 26bae6da..913ba163 100644
--- a/src/nf-log.c
+++ b/src/nf-log.c
@@ -96,7 +96,7 @@ int main(int argc, char *argv[])
copy_range = 0xFFFF;
if (argc > 4)
- copy_mode = atoi(argv[4]);
+ copy_range = atoi(argv[4]);
nfnl_log_set_copy_range(log, copy_range);
if ((err = nfnl_log_create(nf_sock, log)) < 0)
diff --git a/src/nf-queue.c b/src/nf-queue.c
index 3fb3c11f..922d9c8e 100644
--- a/src/nf-queue.c
+++ b/src/nf-queue.c
@@ -13,6 +13,7 @@
#include <netlink/cli/utils.h>
#include <netlink/cli/link.h>
+#include <netinet/in.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nfnetlink_queue.h>
#include <netlink/netfilter/nfnl.h>
@@ -41,31 +42,7 @@ static void obj_input(struct nl_object *obj, void *arg)
.dp_fd = stdout,
.dp_dump_msgtype = 1,
};
- uint32_t packet_id = nfnl_queue_msg_get_packetid(msg);
- static uint32_t next_packet_id = 0;
- struct nfnl_queue_msg *lost_msg = NULL;
- uint8_t family;
- uint16_t group;
-
- if (packet_id > next_packet_id) {
- printf("Warning: %d Out of order packets. Queue or socket overload \n", packet_id - next_packet_id);
- group = nfnl_queue_msg_get_group(msg);
- family = nfnl_queue_msg_get_family(msg);
- lost_msg = nfnl_queue_msg_alloc();
-
- do {
- nfnl_queue_msg_set_group(lost_msg, group);
- nfnl_queue_msg_set_family(lost_msg, family);
- nfnl_queue_msg_set_packetid(lost_msg, next_packet_id);
- nfnl_queue_msg_set_verdict(lost_msg, NF_ACCEPT);
- nfnl_queue_msg_send_verdict(nf_sock, lost_msg);
- next_packet_id++;
- } while (packet_id > next_packet_id);
-
- nfnl_queue_msg_put(lost_msg);
- }
- next_packet_id = packet_id + 1;
nfnl_queue_msg_set_verdict(msg, NF_ACCEPT);
nl_object_dump(obj, &dp);
nfnl_queue_msg_send_verdict(nf_sock, msg);
diff --git a/src/nl-addr-list.c b/src/nl-addr-list.c
index 9b045a52..20995a84 100644
--- a/src/nl-addr-list.c
+++ b/src/nl-addr-list.c
@@ -41,7 +41,7 @@ static void print_usage(void)
static char *prefix;
-void print_prefix(struct nl_dump_params *p, int line)
+static void print_prefix(struct nl_dump_params *p, int line)
{
if (prefix)
nl_dump(p, "%s", prefix);
@@ -65,7 +65,7 @@ static void env_dump(struct nl_object *obj, void *arg)
nl_addr2str(rtnl_addr_get_local(addr), buf, sizeof(buf)));
nl_dump_line(p, "%s_IFINDEX=%u\n", pfx, rtnl_addr_get_ifindex(addr));
- link_cache = nl_cache_mngt_require("route/link");
+ link_cache = nl_cache_mngt_require_safe("route/link");
if (link_cache)
nl_dump_line(p, "%s_IFNAME=%s\n", pfx,
rtnl_link_i2name(link_cache,
@@ -94,6 +94,9 @@ static void env_dump(struct nl_object *obj, void *arg)
nl_dump_line(p, "%s_CACHEINFO_VALID=%u\n", pfx,
rtnl_addr_get_valid_lifetime(addr));
+ if (link_cache)
+ nl_cache_put(link_cache);
+
#if 0
if (addr->ce_mask & ADDR_ATTR_CACHEINFO) {
struct rtnl_addr_cacheinfo *ci = &addr->a_cacheinfo;
diff --git a/src/nl-class-add.c b/src/nl-class-add.c
new file mode 100644
index 00000000..b9a17dc6
--- /dev/null
+++ b/src/nl-class-add.c
@@ -0,0 +1,155 @@
+/*
+ * src/nl-class-add.c Add/Update/Replace Traffic 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/cli/qdisc.h>
+#include <netlink/cli/class.h>
+#include <netlink/cli/link.h>
+
+#include <netlink-private/route/tc-api.h>
+
+static int quiet = 0;
+
+static void print_usage(void)
+{
+ printf(
+"Usage: nl-class-add [OPTIONS]... class [CONFIGURATION]...\n"
+"\n"
+"OPTIONS\n"
+" -q, --quiet Do not print informal notifications.\n"
+" -h, --help Show this help text.\n"
+" -v, --version Show versioning information.\n"
+" --update Update class if it exists.\n"
+" --update-only Only update class, never create it.\n"
+" -d, --dev=DEV Network device the class should be attached to.\n"
+" -i, --id=ID ID of new class (default: auto-generated)\n"
+" -p, --parent=ID ID of parent { root | ingress | class-ID }\n"
+" --mtu=SIZE Overwrite MTU (default: MTU of network device)\n"
+" --mpu=SIZE Minimum packet size on the link (default: 0).\n"
+" --overhead=SIZE Overhead in bytes per packet (default: 0).\n"
+" --linktype=TYPE Overwrite linktype (default: type of network device)\n"
+"\n"
+"CONFIGURATION\n"
+" -h, --help Show help text of class specific options.\n"
+"\n"
+"EXAMPLE\n"
+" $ nl-class-add --dev=eth1 --parent=root htb --rate=100mbit\n"
+"\n"
+ );
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct rtnl_class *class;
+ struct rtnl_tc *tc;
+ struct nl_cache *link_cache;
+ struct nl_dump_params dp = {
+ .dp_type = NL_DUMP_DETAILS,
+ .dp_fd = stdout,
+ };
+ struct nl_cli_tc_module *tm;
+ struct rtnl_tc_ops *ops;
+ int err, flags = NLM_F_CREATE | NLM_F_EXCL;
+ char *kind, *id = NULL;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_ROUTE);
+
+ link_cache = nl_cli_link_alloc_cache(sock);
+
+ class = nl_cli_class_alloc();
+ tc = (struct rtnl_tc *) class;
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_UPDATE = 257,
+ ARG_UPDATE_ONLY = 258,
+ ARG_MTU,
+ ARG_MPU,
+ ARG_OVERHEAD,
+ ARG_LINKTYPE,
+ };
+ static struct option long_opts[] = {
+ { "quiet", 0, 0, 'q' },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "dev", 1, 0, 'd' },
+ { "parent", 1, 0, 'p' },
+ { "id", 1, 0, 'i' },
+ { "update", 0, 0, ARG_UPDATE },
+ { "update-only", 0, 0, ARG_UPDATE_ONLY },
+ { "mtu", 1, 0, ARG_MTU },
+ { "mpu", 1, 0, ARG_MPU },
+ { "overhead", 1, 0, ARG_OVERHEAD },
+ { "linktype", 1, 0, ARG_LINKTYPE },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "+qhvd:p:i:",
+ long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'q': quiet = 1; break;
+ case 'h': print_usage(); break;
+ case 'v': nl_cli_print_version(); break;
+ case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break;
+ case 'p': nl_cli_tc_parse_parent(tc, optarg); break;
+ case 'i': id = strdup(optarg); break;
+ case ARG_UPDATE: flags = NLM_F_CREATE; break;
+ case ARG_UPDATE_ONLY: flags = 0; break;
+ case ARG_MTU: nl_cli_tc_parse_mtu(tc, optarg); break;
+ case ARG_MPU: nl_cli_tc_parse_mpu(tc, optarg); break;
+ case ARG_OVERHEAD: nl_cli_tc_parse_overhead(tc, optarg); break;
+ case ARG_LINKTYPE: nl_cli_tc_parse_linktype(tc, optarg); break;
+ }
+ }
+
+ if (optind >= argc)
+ print_usage();
+
+ if (!rtnl_tc_get_ifindex(tc))
+ nl_cli_fatal(EINVAL, "You must specify a network device (--dev=XXX)");
+
+ if (!rtnl_tc_get_parent(tc))
+ nl_cli_fatal(EINVAL, "You must specify a parent (--parent=XXX)");
+
+ if (id) {
+ nl_cli_tc_parse_handle(tc, id, 1);
+ free(id);
+ }
+
+ kind = argv[optind++];
+ rtnl_tc_set_kind(tc, kind);
+
+ if (!(ops = rtnl_tc_get_ops(tc)))
+ nl_cli_fatal(ENOENT, "Unknown class \"%s\"", kind);
+
+ if (!(tm = nl_cli_tc_lookup(ops)))
+ nl_cli_fatal(ENOTSUP, "class type \"%s\" not supported.", kind);
+
+ tm->tm_parse_argv(tc, argc, argv);
+
+ if (!quiet) {
+ printf("Adding ");
+ nl_object_dump(OBJ_CAST(class), &dp);
+ }
+
+ if ((err = rtnl_class_add(sock, class, flags)) < 0)
+ nl_cli_fatal(EINVAL, "Unable to add class: %s", nl_geterror(err));
+
+ return 0;
+}
diff --git a/src/nl-class-delete.c b/src/nl-class-delete.c
new file mode 100644
index 00000000..37657a47
--- /dev/null
+++ b/src/nl-class-delete.c
@@ -0,0 +1,128 @@
+/*
+ * src/nl-class-delete.c Delete Traffic 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/class.h>
+#include <netlink/cli/link.h>
+
+static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0;
+static struct nl_sock *sock;
+
+static void print_usage(void)
+{
+ printf(
+ "Usage: nl-class-delete [OPTION]... [class]\n"
+ "\n"
+ "OPTIONS\n"
+ " --interactive Run interactively.\n"
+ " --yes Set default answer to yes.\n"
+ " -q, --quiet Do not print informal notifications.\n"
+ " -h, --help Show this help text and exit.\n"
+ " -v, --version Show versioning information and exit.\n"
+ "\n"
+ " -d, --dev=DEV Device the class is attached to.\n"
+ " -p, --parent=ID Identifier of parent qdisc/class.\n"
+ " -i, --id=ID Identifier\n"
+ " -k, --kind=NAME Kind of class (e.g. pfifo_fast)\n"
+ "\n"
+ "EXAMPLE\n"
+ " # Delete all classes on eth0 attached to parent 1:\n"
+ " $ nl-class-delete --dev eth0 --parent 1:\n"
+ "\n"
+ );
+
+ exit(0);
+}
+
+static void delete_cb(struct nl_object *obj, void *arg)
+{
+ struct rtnl_class *class = nl_object_priv(obj);
+ struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+ .dp_fd = stdout,
+ };
+ int err;
+
+ if (interactive && !nl_cli_confirm(obj, &params, default_yes))
+ return;
+
+ if ((err = rtnl_class_delete(sock, class)) < 0)
+ nl_cli_fatal(err, "Unable to delete class: %s\n", nl_geterror(err));
+
+ if (!quiet) {
+ printf("Deleted ");
+ nl_object_dump(obj, &params);
+ }
+
+ deleted++;
+}
+
+int main(int argc, char *argv[])
+{
+ struct rtnl_class *class;
+ struct rtnl_tc *tc;
+ struct nl_cache *link_cache, *class_cache;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_ROUTE);
+ link_cache = nl_cli_link_alloc_cache(sock);
+ class = nl_cli_class_alloc();
+ tc = (struct rtnl_tc *) class;
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_YES = 257,
+ ARG_INTERACTIVE = 258,
+ };
+ static struct option long_opts[] = {
+ { "interactive", 0, 0, ARG_INTERACTIVE },
+ { "yes", 0, 0, ARG_YES },
+ { "quiet", 0, 0, 'q' },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "dev", 1, 0, 'd' },
+ { "parent", 1, 0, 'p' },
+ { "id", 1, 0, 'i' },
+ { "kind", 1, 0, 'k' },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "qhvd:p:i:k:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case '?': nl_cli_fatal(EINVAL, "Invalid options");
+ case ARG_INTERACTIVE: interactive = 1; break;
+ case ARG_YES: default_yes = 1; break;
+ case 'q': quiet = 1; break;
+ case 'h': print_usage(); break;
+ case 'v': nl_cli_print_version(); break;
+ case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break;
+ case 'p': nl_cli_tc_parse_parent(tc, optarg); break;
+ case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break;
+ case 'k': nl_cli_tc_parse_kind(tc, optarg); break;
+ }
+ }
+
+ if (!rtnl_tc_get_ifindex(tc))
+ nl_cli_fatal(EINVAL, "You must specify a network device (--dev=XXX)");
+
+ class_cache = nl_cli_class_alloc_cache(sock, rtnl_tc_get_ifindex(tc));
+
+ nl_cache_foreach_filter(class_cache, OBJ_CAST(class), delete_cb, NULL);
+
+ if (!quiet)
+ printf("Deleted %d classs\n", deleted);
+
+ return 0;
+}
diff --git a/src/nl-class-list.c b/src/nl-class-list.c
new file mode 100644
index 00000000..c2423fbf
--- /dev/null
+++ b/src/nl-class-list.c
@@ -0,0 +1,117 @@
+/*
+ * src/nl-class-list.c List Traffic 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/cli/class.h>
+#include <netlink/cli/link.h>
+
+static struct nl_sock *sock;
+
+static struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+};
+
+static void print_usage(void)
+{
+ printf(
+ "Usage: nl-class-list [OPTION]...\n"
+ "\n"
+ "OPTIONS\n"
+ " --details Show details\n"
+ " --stats Show statistics\n"
+ " -h, --help Show this help\n"
+ " -v, --version Show versioning information\n"
+ "\n"
+ " -d, --dev=DEV Device the class is attached to. (default: all)\n"
+ " -p, --parent=ID Identifier of parent class.\n"
+ " -i, --id=ID Identifier.\n"
+ " -k, --kind=NAME Kind of class (e.g. pfifo_fast)\n"
+ "\n"
+ "EXAMPLE\n"
+ " # Display statistics of all classes on eth0\n"
+ " $ nl-class-list --stats --dev=eth0\n"
+ "\n"
+ );
+ exit(0);
+}
+
+static void __dump_class(int ifindex, struct rtnl_class *filter)
+{
+ struct nl_cache *cache;
+
+ cache = nl_cli_class_alloc_cache(sock, ifindex);
+ nl_cache_dump_filter(cache, &params, OBJ_CAST(filter));
+}
+
+static void dump_class(struct nl_object *obj, void *arg)
+{
+ struct rtnl_link *link = nl_object_priv(obj);
+
+ __dump_class(rtnl_link_get_ifindex(link), arg);
+}
+
+int main(int argc, char *argv[])
+{
+ struct rtnl_class *class;
+ struct rtnl_tc *tc;
+ struct nl_cache *link_cache;
+ int ifindex;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_ROUTE);
+ link_cache = nl_cli_link_alloc_cache(sock);
+ class = nl_cli_class_alloc();
+ tc = (struct rtnl_tc *) class;
+
+ params.dp_fd = stdout;
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_DETAILS = 257,
+ ARG_STATS = 258,
+ };
+ static struct option long_opts[] = {
+ { "details", 0, 0, ARG_DETAILS },
+ { "stats", 0, 0, ARG_STATS },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "dev", 1, 0, 'd' },
+ { "parent", 1, 0, 'p' },
+ { "id", 1, 0, 'i' },
+ { "kind", 1, 0, 'k' },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "hvd:p:i:k:", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case ARG_DETAILS: params.dp_type = NL_DUMP_DETAILS; break;
+ case ARG_STATS: params.dp_type = NL_DUMP_STATS; break;
+ case 'h': print_usage(); break;
+ case 'v': nl_cli_print_version(); break;
+ case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break;
+ case 'p': nl_cli_tc_parse_parent(tc, optarg); break;
+ case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break;
+ case 'k': nl_cli_tc_parse_kind(tc, optarg); break;
+ }
+ }
+
+ if ((ifindex = rtnl_tc_get_ifindex(tc)))
+ __dump_class(ifindex, class);
+ else
+ nl_cache_foreach(link_cache, dump_class, class);
+
+ return 0;
+}
diff --git a/src/nl-classid-lookup.c b/src/nl-classid-lookup.c
new file mode 100644
index 00000000..1d45d0b2
--- /dev/null
+++ b/src/nl-classid-lookup.c
@@ -0,0 +1,87 @@
+/*
+ * src/nl-classid-lookup.c Lookup classid
+ *
+ * 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+
+static void print_usage(void)
+{
+ printf(
+"Usage: nl-classid-lookup [OPTIONS]... NAME\n"
+"\n"
+"OPTIONS\n"
+" -h, --help Show this help text.\n"
+" -v, --version Show versioning information.\n"
+" -r, --reverse Do a reverse lookup, i.e. classid to name.\n"
+" --raw Print the raw classid, not pretty printed.\n"
+"\n"
+"EXAMPLE\n"
+" $ nl-classid-lookup low_latency\n"
+" $ nl-classid-lookup -r 1:12\n"
+"\n"
+ );
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ uint32_t classid;
+ char *name;
+ int err, reverse = 0, raw = 0;
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_RAW = 257,
+ };
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "reverse", 0, 0, 'r' },
+ { "raw", 0, 0, ARG_RAW },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "hvr", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h': print_usage(); break;
+ case 'v': nl_cli_print_version(); break;
+ case 'r': reverse = 1; break;
+ case ARG_RAW: raw = 1; break;
+ }
+ }
+
+ if (optind >= argc)
+ print_usage();
+
+ name = argv[optind++];
+
+ /*
+ * We use rtnl_tc_str2handle() even while doing a reverse lookup. This
+ * allows for name -> name lookups. This is intentional, it does not
+ * do any harm and avoids duplicating a lot of code.
+ */
+ if ((err = rtnl_tc_str2handle(name, &classid)) < 0)
+ nl_cli_fatal(err, "Unable to lookup classid \"%s\": %s",
+ name, nl_geterror(err));
+
+ if (reverse) {
+ char buf[64];
+ printf("%s\n", rtnl_tc_handle2str(classid, buf, sizeof(buf)));
+ } else if (raw)
+ printf("%#x\n", classid);
+ else
+ printf("%x:%x\n", TC_H_MAJ(classid) >> 16, TC_H_MIN(classid));
+
+ return 0;
+}
diff --git a/src/nl-cls-add.c b/src/nl-cls-add.c
index 997f02f8..6acb3202 100644
--- a/src/nl-cls-add.c
+++ b/src/nl-cls-add.c
@@ -5,29 +5,45 @@
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2 of the License.
*
- * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2011 Thomas Graf <tgraf@suug.ch>
*/
-#include "cls/utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/cli/cls.h>
+#include <netlink/cli/link.h>
+
+#include <netlink-private/route/tc-api.h>
static int quiet = 0;
static void print_usage(void)
{
printf(
-"Usage: nl-cls-add [OPTION]... [CLASSIFIER] TYPE [TYPE OPTIONS]...\n"
+"Usage: nl-cls-add [OPTIONS]... classifier [CONFIGURATION]...\n"
"\n"
-"Options\n"
+"OPTIONS\n"
" -q, --quiet Do not print informal notifications.\n"
-" -h, --help Show this help.\n"
+" -h, --help Show this help text.\n"
" -v, --version Show versioning information.\n"
+" --update Update classifier if it exists.\n"
+" --update-only Only update classifier, never create it.\n"
+" -d, --dev=DEV Network device the classifier should be attached to.\n"
+" -i, --id=ID ID of new classifier (default: auto-generated)\n"
+" -p, --parent=ID ID of parent { root | ingress | class-ID }\n"
+" --protocol=PROTO Protocol to match (default: all)\n"
+" --prio=PRIO Priority (default: 0)\n"
+" --mtu=SIZE Overwrite MTU (default: MTU of network device)\n"
+" --mpu=SIZE Minimum packet size on the link (default: 0).\n"
+" --overhead=SIZE Overhead in bytes per packet (default: 0).\n"
+" --linktype=TYPE Overwrite linktype (default: type of network device)\n"
+"\n"
+"CONFIGURATION\n"
+" -h, --help Show help text of classifier specific options.\n"
+"\n"
+"EXAMPLE\n"
+" $ nl-cls-add --dev=eth1 --parent=q_root basic --target c_www\n"
"\n"
-"Classifier Options\n"
-" -d, --dev=DEV Device the classifier should be assigned to.\n"
-" -p, --parent=HANDLE Parent QDisc\n"
-" --proto=PROTO Protocol (default=IPv4)\n"
-" --prio=NUM Priority (0..256)\n"
-" --id=HANDLE Unique identifier\n"
);
exit(0);
}
@@ -36,27 +52,36 @@ int main(int argc, char *argv[])
{
struct nl_sock *sock;
struct rtnl_cls *cls;
+ struct rtnl_tc *tc;
struct nl_cache *link_cache;
- struct rtnl_cls_ops *ops;
- struct cls_module *mod;
struct nl_dump_params dp = {
.dp_type = NL_DUMP_DETAILS,
.dp_fd = stdout,
};
- char *kind;
- int err, nlflags = NLM_F_CREATE;
+ struct nl_cli_tc_module *tm;
+ struct rtnl_tc_ops *ops;
+ int err, flags = NLM_F_CREATE | NLM_F_EXCL;
+ char *kind, *id = NULL;
- sock = nlt_alloc_socket();
- nlt_connect(sock, NETLINK_ROUTE);
- link_cache = nlt_alloc_link_cache(sock);
- cls = nlt_alloc_cls();
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_ROUTE);
+ link_cache = nl_cli_link_alloc_cache(sock);
+
+ cls = nl_cli_cls_alloc();
+ tc = (struct rtnl_tc *) cls;
+
for (;;) {
int c, optidx = 0;
enum {
- ARG_PROTO = 257,
- ARG_PRIO = 258,
- ARG_ID,
+ ARG_UPDATE = 257,
+ ARG_UPDATE_ONLY = 258,
+ ARG_MTU,
+ ARG_MPU,
+ ARG_OVERHEAD,
+ ARG_LINKTYPE,
+ ARG_PROTO,
+ ARG_PRIO,
};
static struct option long_opts[] = {
{ "quiet", 0, 0, 'q' },
@@ -64,54 +89,75 @@ int main(int argc, char *argv[])
{ "version", 0, 0, 'v' },
{ "dev", 1, 0, 'd' },
{ "parent", 1, 0, 'p' },
+ { "id", 1, 0, 'i' },
{ "proto", 1, 0, ARG_PROTO },
{ "prio", 1, 0, ARG_PRIO },
- { "id", 1, 0, ARG_ID },
+ { "update", 0, 0, ARG_UPDATE },
+ { "update-only", 0, 0, ARG_UPDATE_ONLY },
+ { "mtu", 1, 0, ARG_MTU },
+ { "mpu", 1, 0, ARG_MPU },
+ { "overhead", 1, 0, ARG_OVERHEAD },
+ { "linktype", 1, 0, ARG_LINKTYPE },
{ 0, 0, 0, 0 }
};
- c = getopt_long(argc, argv, "+qhva:d:", long_opts, &optidx);
+ c = getopt_long(argc, argv, "+qhvd:p:i:",
+ long_opts, &optidx);
if (c == -1)
break;
switch (c) {
- case '?': exit(NLE_INVAL);
case 'q': quiet = 1; break;
case 'h': print_usage(); break;
- case 'v': nlt_print_version(); break;
- case 'd': parse_dev(cls, link_cache, optarg); break;
- case 'p': parse_parent(cls, optarg); break;
- case ARG_PRIO: parse_prio(cls, optarg); break;
- case ARG_ID: parse_handle(cls, optarg); break;
- case ARG_PROTO: parse_proto(cls, optarg); break;
+ case 'v': nl_cli_print_version(); break;
+ case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break;
+ case 'p': nl_cli_tc_parse_parent(tc, optarg); break;
+ case 'i': id = strdup(optarg); break;
+ case ARG_UPDATE: flags = NLM_F_CREATE; break;
+ case ARG_UPDATE_ONLY: flags = 0; break;
+ case ARG_MTU: nl_cli_tc_parse_mtu(tc, optarg); break;
+ case ARG_MPU: nl_cli_tc_parse_mpu(tc, optarg); break;
+ case ARG_OVERHEAD: nl_cli_tc_parse_overhead(tc, optarg); break;
+ case ARG_LINKTYPE: nl_cli_tc_parse_linktype(tc, optarg); break;
+ case ARG_PROTO: nl_cli_cls_parse_proto(cls, optarg); break;
+ case ARG_PRIO:
+ rtnl_cls_set_prio(cls, nl_cli_parse_u32(optarg));
+ break;
}
}
- if (optind >= argc) {
+ if (optind >= argc)
print_usage();
- fatal(EINVAL, "Missing classifier type");
+
+ if (!rtnl_tc_get_ifindex(tc))
+ nl_cli_fatal(EINVAL, "You must specify a network device (--dev=XXX)");
+
+ if (!rtnl_tc_get_parent(tc))
+ nl_cli_fatal(EINVAL, "You must specify a parent (--parent=XXX)");
+
+ if (id) {
+ nl_cli_tc_parse_handle(tc, id, 1);
+ free(id);
}
kind = argv[optind++];
- if ((err = rtnl_cls_set_kind(cls, kind)) < 0)
- fatal(ENOENT, "Unknown classifier type \"%s\".", kind);
-
- ops = rtnl_cls_get_ops(cls);
- if (!(mod = lookup_cls_mod(ops)))
- fatal(ENOTSUP, "Classifier type \"%s\" not supported.", kind);
+ rtnl_tc_set_kind(tc, kind);
- mod->parse_argv(cls, argc, argv);
+ if (!(ops = rtnl_tc_get_ops(tc)))
+ nl_cli_fatal(ENOENT, "Unknown classifier \"%s\".", kind);
- printf("Adding ");
- nl_object_dump(OBJ_CAST(cls), &dp);
+ if (!(tm = nl_cli_tc_lookup(ops)))
+ nl_cli_fatal(ENOTSUP, "Classifier type \"%s\" not supported.", kind);
- if ((err = rtnl_cls_add(sock, cls, nlflags)) < 0)
- fatal(err, "Unable to add classifier: %s", nl_geterror(err));
+ tm->tm_parse_argv(tc, argc, argv);
if (!quiet) {
- printf("Added ");
+ printf("Adding ");
nl_object_dump(OBJ_CAST(cls), &dp);
}
+ if ((err = rtnl_cls_add(sock, cls, flags)) < 0)
+ nl_cli_fatal(EINVAL, "Unable to add classifier: %s", nl_geterror(err));
+
return 0;
}
diff --git a/src/nl-cls-delete.c b/src/nl-cls-delete.c
index cfdc1709..2b3db1f1 100644
--- a/src/nl-cls-delete.c
+++ b/src/nl-cls-delete.c
@@ -6,52 +6,59 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
*/
-#include "cls/utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/cls.h>
+#include <netlink/cli/link.h>
-static int interactive = 0, default_yes = 0, quiet = 0;
-static int deleted = 0;
+static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0;
static struct nl_sock *sock;
static void print_usage(void)
{
printf(
- "Usage: nl-cls-list [OPTION]... [CLASSIFIER]\n"
- "\n"
- "Options\n"
- " -i, --interactive Run interactively\n"
- " --yes Set default answer to yes\n"
- " -q, --quiet Do not print informal notifications\n"
- " -h, --help Show this help\n"
- " -v, --version Show versioning information\n"
- "\n"
- "Classifier Options\n"
- " -d, --dev=DEV Device the classifier should be assigned to.\n"
- " -p, --parent=HANDLE Parent qdisc/class\n"
- " --proto=PROTO Protocol\n"
- " --prio=NUM Priority (0..256)\n"
- " --id=HANDLE Unique identifier\n"
+"Usage: nl-cls-delete [OPTION]... [class]\n"
+"\n"
+"OPTIONS\n"
+" --interactive Run interactively.\n"
+" --yes Set default answer to yes.\n"
+" -q, --quiet Do not print informal notifications.\n"
+" -h, --help Show this help text and exit.\n"
+" -v, --version Show versioning information and exit.\n"
+"\n"
+" -d, --dev=DEV Device the classifer is attached to.\n"
+" -p, --parent=ID Identifier of parent qdisc/class.\n"
+" -i, --id=ID Identifier\n"
+" -k, --kind=NAME Kind of classifier (e.g. basic, u32, fw)\n"
+" --protocol=PROTO Protocol to match (default: all)\n"
+" --prio=PRIO Priority (default: 0)\n"
+"\n"
+"EXAMPLE\n"
+" # Delete all classifiers on eth0 attached to parent q_root:\n"
+" $ nl-cls-delete --dev eth0 --parent q_root:\n"
+"\n"
);
+
exit(0);
}
static void delete_cb(struct nl_object *obj, void *arg)
{
- struct rtnl_cls *cls = (struct rtnl_cls *) obj;
+ struct rtnl_cls *cls = nl_object_priv(obj);
struct nl_dump_params params = {
.dp_type = NL_DUMP_LINE,
.dp_fd = stdout,
};
int err;
- if (interactive && !nlt_confirm(obj, &params, default_yes))
+ if (interactive && !nl_cli_confirm(obj, &params, default_yes))
return;
if ((err = rtnl_cls_delete(sock, cls, 0)) < 0)
- fatal(err, "Unable to delete classifier: %s",
- nl_geterror(err));
+ nl_cli_fatal(err, "Unable to delete classifier: %s\n",
+ nl_geterror(err));
if (!quiet) {
printf("Deleted ");
@@ -61,73 +68,88 @@ static void delete_cb(struct nl_object *obj, void *arg)
deleted++;
}
-int main(int argc, char *argv[])
+static void __delete_link(int ifindex, struct rtnl_cls *filter)
{
- struct nl_cache *link_cache, *cls_cache;
- struct rtnl_cls *cls;
- int nf = 0, err;
+ struct nl_cache *cache;
+ uint32_t parent = rtnl_tc_get_parent((struct rtnl_tc *) filter);
+
+ cache = nl_cli_cls_alloc_cache(sock, ifindex, parent);
+ nl_cache_foreach_filter(cache, OBJ_CAST(filter), delete_cb, NULL);
+ nl_cache_free(cache);
+}
+
+static void delete_link(struct nl_object *obj, void *arg)
+{
+ struct rtnl_link *link = nl_object_priv(obj);
- sock = nlt_alloc_socket();
- nlt_connect(sock, NETLINK_ROUTE);
- link_cache = nlt_alloc_link_cache(sock);
- cls = nlt_alloc_cls();
+ __delete_link(rtnl_link_get_ifindex(link), arg);
+}
+int main(int argc, char *argv[])
+{
+ struct rtnl_cls *cls;
+ struct rtnl_tc *tc;
+ struct nl_cache *link_cache;
+ int ifindex;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_ROUTE);
+ link_cache = nl_cli_link_alloc_cache(sock);
+ cls = nl_cli_cls_alloc();
+ tc = (struct rtnl_tc *) cls;
+
for (;;) {
int c, optidx = 0;
enum {
- ARG_PRIO = 257,
- ARG_PROTO = 258,
- ARG_ID,
- ARG_YES,
+ ARG_YES = 257,
+ ARG_INTERACTIVE = 258,
+ ARG_PROTO,
+ ARG_PRIO,
};
static struct option long_opts[] = {
- { "interactive", 0, 0, 'i' },
+ { "interactive", 0, 0, ARG_INTERACTIVE },
{ "yes", 0, 0, ARG_YES },
{ "quiet", 0, 0, 'q' },
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'v' },
{ "dev", 1, 0, 'd' },
{ "parent", 1, 0, 'p' },
+ { "id", 1, 0, 'i' },
+ { "kind", 1, 0, 'k' },
{ "proto", 1, 0, ARG_PROTO },
{ "prio", 1, 0, ARG_PRIO },
- { "id", 1, 0, ARG_ID },
{ 0, 0, 0, 0 }
};
- c = getopt_long(argc, argv, "iqhvd:p:", long_opts, &optidx);
+ c = getopt_long(argc, argv, "qhvd:p:i:k:", long_opts, &optidx);
if (c == -1)
break;
switch (c) {
- case 'i': interactive = 1; break;
+ case '?': nl_cli_fatal(EINVAL, "Invalid options");
+ case ARG_INTERACTIVE: interactive = 1; break;
case ARG_YES: default_yes = 1; break;
case 'q': quiet = 1; break;
case 'h': print_usage(); break;
- case 'v': nlt_print_version(); break;
- case 'd': nf++; parse_dev(cls, link_cache, optarg); break;
- case 'p': nf++; parse_parent(cls, optarg); break;
- case ARG_PRIO: nf++; parse_prio(cls, optarg); break;
- case ARG_ID: nf++; parse_handle(cls, optarg); break;
- case ARG_PROTO: nf++; parse_proto(cls, optarg); break;
+ case 'v': nl_cli_print_version(); break;
+ case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break;
+ case 'p': nl_cli_tc_parse_parent(tc, optarg); break;
+ case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break;
+ case 'k': nl_cli_tc_parse_kind(tc, optarg); break;
+ case ARG_PROTO: nl_cli_cls_parse_proto(cls, optarg); break;
+ case ARG_PRIO:
+ rtnl_cls_set_prio(cls, nl_cli_parse_u32(optarg));
+ break;
}
- }
-
- if (nf == 0 && !interactive && !default_yes) {
- fprintf(stderr, "You attempted to delete all classifiers in "
- "non-interactive mode, aborting.\n");
- exit(0);
- }
-
- err = rtnl_cls_alloc_cache(sock, rtnl_cls_get_ifindex(cls),
- rtnl_cls_get_parent(cls), &cls_cache);
- if (err < 0)
- fatal(err, "Unable to allocate classifier cache: %s",
- nl_geterror(err));
+ }
- nl_cache_foreach_filter(cls_cache, OBJ_CAST(cls), delete_cb, NULL);
+ if ((ifindex = rtnl_tc_get_ifindex(tc)))
+ __delete_link(ifindex, cls);
+ else
+ nl_cache_foreach(link_cache, delete_link, cls);
if (!quiet)
- printf("Deleted %d classifiers\n", deleted);
+ printf("Deleted %d classs\n", deleted);
return 0;
}
diff --git a/src/nl-cls-list.c b/src/nl-cls-list.c
index 9121d523..50725850 100644
--- a/src/nl-cls-list.c
+++ b/src/nl-cls-list.c
@@ -6,13 +6,16 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2008-2010 Thomas Graf <tgraf@suug.ch>
*/
-#include "cls/utils.h"
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/cli/cls.h>
+#include <netlink/cli/link.h>
static struct nl_sock *sock;
-static struct rtnl_cls *cls;
+
static struct nl_dump_params params = {
.dp_type = NL_DUMP_LINE,
};
@@ -20,94 +23,107 @@ static struct nl_dump_params params = {
static void print_usage(void)
{
printf(
- "Usage: nl-cls-list [OPTION]... [CLASSIFIER]\n"
- "\n"
- "Options\n"
- " -f, --format=TYPE Output format { brief | details | stats }\n"
- " -h, --help Show this help text.\n"
- " -v, --version Show versioning information.\n"
- "\n"
- "Classifier Options\n"
- " -d, --dev=DEV Device the classifier should be assigned to.\n"
- " -p, --parent=HANDLE Parent qdisc/class\n"
- " --proto=PROTO Protocol\n"
- " --prio=NUM Priority\n"
- " --id=NUM Identifier\n"
+"Usage: nl-cls-list [OPTION]...\n"
+"\n"
+"OPTIONS\n"
+" --details Show details\n"
+" --stats Show statistics\n"
+" -h, --help Show this help\n"
+" -v, --version Show versioning information\n"
+"\n"
+" -d, --dev=DEV Device the classifier is attached to. (default: all)\n"
+" -p, --parent=ID Identifier of parent class.\n"
+" -i, --id=ID Identifier.\n"
+" -k, --kind=NAME Kind of classifier (e.g. basic, u32, fw)\n"
+" --protocol=PROTO Protocol to match (default: all)\n"
+" --prio=PRIO Priority (default: 0)\n"
+"\n"
+"EXAMPLE\n"
+" # Display statistics of all classes on eth0\n"
+" $ nl-cls-list --stats --dev=eth0\n"
+"\n"
);
exit(0);
}
-static void print_cls(struct nl_object *obj, void *arg)
+static void __dump_link(int ifindex, struct rtnl_cls *filter)
{
- struct nl_cache *cls_cache;
- int err, ifindex;
+ struct nl_cache *cache;
+ uint32_t parent = rtnl_tc_get_parent((struct rtnl_tc *) filter);
- if (obj)
- ifindex = rtnl_link_get_ifindex((struct rtnl_link *) obj);
- else
- ifindex = rtnl_cls_get_ifindex(cls);
+ cache = nl_cli_cls_alloc_cache(sock, ifindex, parent);
+ nl_cache_dump_filter(cache, &params, OBJ_CAST(filter));
+ nl_cache_free(cache);
+}
- err = rtnl_cls_alloc_cache(sock, ifindex, rtnl_cls_get_parent(cls),
- &cls_cache);
- if (err < 0)
- fatal(err, "Unable to allocate classifier cache: %s",
- nl_geterror(err));
+static void dump_link(struct nl_object *obj, void *arg)
+{
+ struct rtnl_link *link = nl_object_priv(obj);
- nl_cache_dump_filter(cls_cache, &params, OBJ_CAST(cls));
- nl_cache_free(cls_cache);
+ __dump_link(rtnl_link_get_ifindex(link), arg);
}
int main(int argc, char *argv[])
{
+ struct rtnl_cls *cls;
+ struct rtnl_tc *tc;
struct nl_cache *link_cache;
- int dev = 0;
+ int ifindex;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_ROUTE);
+ link_cache = nl_cli_link_alloc_cache(sock);
+ cls = nl_cli_cls_alloc();
+ tc = (struct rtnl_tc *) cls;
params.dp_fd = stdout;
- sock = nlt_alloc_socket();
- nlt_connect(sock, NETLINK_ROUTE);
- link_cache = nlt_alloc_link_cache(sock);
- cls = nlt_alloc_cls();
-
+
for (;;) {
int c, optidx = 0;
enum {
- ARG_PROTO = 257,
- ARG_PRIO = 258,
- ARG_ID,
+ ARG_DETAILS = 257,
+ ARG_STATS = 258,
+ ARG_PROTO,
+ ARG_PRIO,
};
static struct option long_opts[] = {
- { "format", 1, 0, 'f' },
+ { "details", 0, 0, ARG_DETAILS },
+ { "stats", 0, 0, ARG_STATS },
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'v' },
{ "dev", 1, 0, 'd' },
{ "parent", 1, 0, 'p' },
+ { "id", 1, 0, 'i' },
+ { "kind", 1, 0, 'k' },
{ "proto", 1, 0, ARG_PROTO },
{ "prio", 1, 0, ARG_PRIO },
- { "id", 1, 0, ARG_ID },
{ 0, 0, 0, 0 }
};
- c = getopt_long(argc, argv, "+f:qhva:d:", long_opts, &optidx);
+ c = getopt_long(argc, argv, "hvd:p:i:k:", long_opts, &optidx);
if (c == -1)
break;
switch (c) {
- case '?': exit(NLE_INVAL);
- case 'f': params.dp_type = nlt_parse_dumptype(optarg); break;
+ case ARG_DETAILS: params.dp_type = NL_DUMP_DETAILS; break;
+ case ARG_STATS: params.dp_type = NL_DUMP_STATS; break;
case 'h': print_usage(); break;
- case 'v': nlt_print_version(); break;
- case 'd': dev = 1; parse_dev(cls, link_cache, optarg); break;
- case 'p': parse_parent(cls, optarg); break;
- case ARG_PRIO: parse_prio(cls, optarg); break;
- case ARG_ID: parse_handle(cls, optarg); break;
- case ARG_PROTO: parse_proto(cls, optarg); break;
+ case 'v': nl_cli_print_version(); break;
+ case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break;
+ case 'p': nl_cli_tc_parse_parent(tc, optarg); break;
+ case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break;
+ case 'k': nl_cli_tc_parse_kind(tc, optarg); break;
+ case ARG_PROTO: nl_cli_cls_parse_proto(cls, optarg); break;
+ case ARG_PRIO:
+ rtnl_cls_set_prio(cls, nl_cli_parse_u32(optarg));
+ break;
}
}
- if (!dev)
- nl_cache_foreach(link_cache, print_cls, NULL);
- else
- print_cls(NULL, NULL);
+ if ((ifindex = rtnl_tc_get_ifindex(tc)))
+ __dump_link(ifindex, cls);
+ else
+ nl_cache_foreach(link_cache, dump_link, cls);
return 0;
}
diff --git a/src/nl-link-enslave.c b/src/nl-link-enslave.c
new file mode 100644
index 00000000..2b5d47db
--- /dev/null
+++ b/src/nl-link-enslave.c
@@ -0,0 +1,50 @@
+/*
+ * src/nl-link-enslave.c Enslave a link
+ *
+ * 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) 2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/link.h>
+#include <netlink/route/link/bonding.h>
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_cache *link_cache;
+ struct rtnl_link *master, *slave;
+ int err;
+
+ if (argc < 3) {
+ fprintf(stderr, "Usage: nl-link-enslave master slave\n");
+ return 1;
+ }
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_ROUTE);
+ link_cache = nl_cli_link_alloc_cache(sock);
+
+ if (!(master = rtnl_link_get_by_name(link_cache, argv[1]))) {
+ fprintf(stderr, "Unknown link: %s\n", argv[1]);
+ return 1;
+ }
+
+ if (!(slave = rtnl_link_get_by_name(link_cache, argv[2]))) {
+ fprintf(stderr, "Unknown link: %s\n", argv[2]);
+ return 1;
+ }
+
+ if ((err = rtnl_link_bond_enslave(sock, master, slave)) < 0) {
+ fprintf(stderr, "Unable to enslave %s to %s: %s\n",
+ argv[2], argv[1], nl_geterror(err));
+ return 1;
+ }
+
+ return 0;
+}
+
diff --git a/src/nl-link-list.c b/src/nl-link-list.c
index 5e1e3f6a..b5c98b40 100644
--- a/src/nl-link-list.c
+++ b/src/nl-link-list.c
@@ -6,43 +6,29 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2010 Thomas Graf <tgraf@suug.ch>
*/
-#if 0
-static void print_usage(void)
-{
- printf(
- "Usage: nl-link-dump <mode> [<filter>]\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);
-}
-#endif
-
#include <netlink/cli/utils.h>
#include <netlink/cli/link.h>
static void print_usage(void)
{
printf(
- "Usage: nl-link-list [OPTION]... [Link]\n"
- "\n"
- "Options\n"
- " -f, --format=TYPE Output format { brief | details | stats }\n"
- " -h, --help Show this help\n"
- " -v, --version Show versioning information\n"
- "\n"
- "Link Options\n"
- " -n, --name=NAME link name\n"
- " -i, --index interface index\n"
- " --mtu=NUM MTU value\n"
- " --txqlen=NUM TX queue length\n"
- " --weight=NUM weight\n"
+"Usage: nl-link-list [OPTIONS]... \n"
+"\n"
+"OPTIONS\n"
+" --details Show detailed information of each link\n"
+" --stats Show statistics, implies --details\n"
+" -h, --help Show this help text.\n"
+" -v, --version Show versioning information.\n"
+"\n"
+" -n, --name=NAME Name of link\n"
+" -i, --index Interface index (unique identifier)\n"
+" --family=NAME Link address family\n"
+" --mtu=NUM MTU value\n"
+" --txqlen=NUM TX queue length\n"
+" --weight=NUM Weight\n"
);
exit(0);
}
@@ -59,7 +45,6 @@ int main(int argc, char *argv[])
sock = nl_cli_alloc_socket();
nl_cli_connect(sock, NETLINK_ROUTE);
- link_cache = nl_cli_link_alloc_cache(sock);
link = nl_cli_link_alloc();
for (;;) {
@@ -69,9 +54,12 @@ int main(int argc, char *argv[])
ARG_MTU = 258,
ARG_TXQLEN,
ARG_WEIGHT,
+ ARG_DETAILS,
+ ARG_STATS,
};
static struct option long_opts[] = {
- { "format", 1, 0, 'f' },
+ { "details", 0, 0, ARG_DETAILS },
+ { "stats", 0, 0, ARG_STATS },
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'v' },
{ "name", 1, 0, 'n' },
@@ -83,12 +71,13 @@ int main(int argc, char *argv[])
{ 0, 0, 0, 0 }
};
- c = getopt_long(argc, argv, "f:hvn:i:", long_opts, &optidx);
+ c = getopt_long(argc, argv, "hvn:i:", long_opts, &optidx);
if (c == -1)
break;
switch (c) {
- case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
+ case ARG_DETAILS: params.dp_type = NL_DUMP_DETAILS; break;
+ case ARG_STATS: params.dp_type = NL_DUMP_STATS; break;
case 'h': print_usage(); break;
case 'v': nl_cli_print_version(); break;
case 'n': nl_cli_link_parse_name(link, optarg); break;
@@ -100,6 +89,9 @@ int main(int argc, char *argv[])
}
}
+ link_cache = nl_cli_link_alloc_cache_family(sock,
+ rtnl_link_get_family(link));
+
nl_cache_dump_filter(link_cache, &params, OBJ_CAST(link));
return 0;
diff --git a/src/nl-link-name2ifindex.c b/src/nl-link-name2ifindex.c
index b04af04e..b8ae4bcd 100644
--- a/src/nl-link-name2ifindex.c
+++ b/src/nl-link-name2ifindex.c
@@ -14,7 +14,7 @@
static void print_usage(void)
{
- printf("Usage: nl-link-ifindex2name <ifindex>\n");
+ printf("Usage: nl-link-name2ifindex <name>\n");
exit(0);
}
diff --git a/src/nl-link-release.c b/src/nl-link-release.c
new file mode 100644
index 00000000..4c9f15a5
--- /dev/null
+++ b/src/nl-link-release.c
@@ -0,0 +1,45 @@
+/*
+ * src/nl-link-release.c release a link
+ *
+ * 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) 2011 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/link.h>
+#include <netlink/route/link/bonding.h>
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct nl_cache *link_cache;
+ struct rtnl_link *slave;
+ int err;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: nl-link-release slave\n");
+ return 1;
+ }
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_ROUTE);
+ link_cache = nl_cli_link_alloc_cache(sock);
+
+ if (!(slave = rtnl_link_get_by_name(link_cache, argv[1]))) {
+ fprintf(stderr, "Unknown link: %s\n", argv[1]);
+ return 1;
+ }
+
+ if ((err = rtnl_link_bond_release(sock, slave)) < 0) {
+ fprintf(stderr, "Unable to release slave %s: %s\n",
+ argv[1], nl_geterror(err));
+ return 1;
+ }
+
+ return 0;
+}
+
diff --git a/src/nl-link-set.c b/src/nl-link-set.c
index 94c94e78..bbb60f97 100644
--- a/src/nl-link-set.c
+++ b/src/nl-link-set.c
@@ -6,7 +6,7 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2010 Thomas Graf <tgraf@suug.ch>
*/
#include <netlink/cli/utils.h>
@@ -41,6 +41,8 @@ static void print_usage(void)
" --mtu=NUM MTU value\n"
" --txqlen=NUM TX queue length\n"
" --weight=NUM weight\n"
+ " --ifalias=NAME alias name (SNMP IfAlias)\n"
+ " --state=up/down set interface up/down\n"
);
exit(0);
}
@@ -55,7 +57,7 @@ static void set_cb(struct nl_object *obj, void *arg)
};
int err;
- if ((err = rtnl_link_change(sock, link, change, 0) < 0))
+ if ((err = rtnl_link_change(sock, link, change, 0)) < 0)
nl_cli_fatal(err, "Unable to change link: %s",
nl_geterror(err));
@@ -84,6 +86,8 @@ int main(int argc, char *argv[])
ARG_MTU = 258,
ARG_TXQLEN,
ARG_WEIGHT,
+ ARG_IFALIAS,
+ ARG_STATE,
};
static struct option long_opts[] = {
{ "quiet", 0, 0, 'q' },
@@ -95,6 +99,8 @@ int main(int argc, char *argv[])
{ "mtu", 1, 0, ARG_MTU },
{ "txqlen", 1, 0, ARG_TXQLEN },
{ "weight", 1, 0, ARG_WEIGHT },
+ { "ifalias", 1, 0, ARG_IFALIAS },
+ { "state", 1, 0, ARG_STATE },
{ 0, 0, 0, 0 }
};
@@ -109,9 +115,16 @@ int main(int argc, char *argv[])
case 'n': ok++; nl_cli_link_parse_name(link, optarg); break;
case 'i': ok++; nl_cli_link_parse_ifindex(link, optarg); break;
case ARG_RENAME: nl_cli_link_parse_name(change, optarg); break;
- case ARG_MTU: nl_cli_link_parse_mtu(link, optarg); break;
- case ARG_TXQLEN: nl_cli_link_parse_txqlen(link, optarg); break;
- case ARG_WEIGHT: nl_cli_link_parse_weight(link, optarg); break;
+ case ARG_MTU: nl_cli_link_parse_mtu(change, optarg); break;
+ case ARG_TXQLEN: nl_cli_link_parse_txqlen(change, optarg); break;
+ case ARG_WEIGHT: nl_cli_link_parse_weight(change, optarg); break;
+ case ARG_IFALIAS: nl_cli_link_parse_ifalias(change, optarg); break;
+ case ARG_STATE:
+ if(!strcmp(optarg, "up"))
+ rtnl_link_set_flags(change, IFF_UP);
+ else if(!strcmp(optarg, "down"))
+ rtnl_link_unset_flags(change, IFF_UP);
+ break;
}
}
diff --git a/src/nl-list-caches.c b/src/nl-list-caches.c
index 7e4ffc15..853d8a40 100644
--- a/src/nl-list-caches.c
+++ b/src/nl-list-caches.c
@@ -9,7 +9,7 @@
* Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
*/
-#include <netlink-local.h>
+#include <netlink-private/netlink.h>
#include <netlink/cli/utils.h>
static void print_usage(void)
@@ -48,7 +48,6 @@ static void print(struct nl_cache_ops *ops, void *arg)
"brief",
"detailed",
"stats",
- "env",
};
int i;
@@ -82,9 +81,9 @@ static void print(struct nl_cache_ops *ops, void *arg)
printf(" genl:\n" \
" name: %s\n" \
- " family: %d\n" \
+ " user-hdr: %d\n" \
" id: %d\n",
- genl_ops->o_name, genl_ops->o_family, genl_ops->o_id);
+ genl_ops->o_name, genl_ops->o_hdrsize, genl_ops->o_id);
if (genl_ops->o_ncmds) {
int i;
diff --git a/src/nl-list-sockets.c b/src/nl-list-sockets.c
index 868006eb..c2fa1cdb 100644
--- a/src/nl-list-sockets.c
+++ b/src/nl-list-sockets.c
@@ -30,7 +30,7 @@ int main(int argc, char *argv[])
while (fgets(buf, sizeof(buf), fd)) {
unsigned long sk, cb;
int ret, proto, pid, rmem, wmem, refcnt;
- uint32_t groups;
+ unsigned int groups;
ret = sscanf(buf, "%lx %d %d %08x %d %d %lx %d\n",
&sk, &proto, &pid, &groups, &rmem, &wmem,
diff --git a/src/nl-neigh-delete.c b/src/nl-neigh-delete.c
index 887bd846..c4186088 100644
--- a/src/nl-neigh-delete.c
+++ b/src/nl-neigh-delete.c
@@ -14,7 +14,7 @@
#include <netlink/cli/link.h>
static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0;
-struct nl_sock *sock;
+static struct nl_sock *sock;
static void print_usage(void)
{
diff --git a/src/nl-pktloc-lookup.c b/src/nl-pktloc-lookup.c
index 09b04b24..17c867b5 100644
--- a/src/nl-pktloc-lookup.c
+++ b/src/nl-pktloc-lookup.c
@@ -14,24 +14,141 @@
static void print_usage(void)
{
- printf("Usage: nl-pktloc-lookup <name>\n");
+printf(
+"Usage: nl-pktloc-lookup [OPTIONS] <name>\n"
+"\n"
+"OPTIONS\n"
+" -h, --help Show this help text.\n"
+" -v, --version Show versioning information.\n"
+" -l, --list List all packet location definitions.\n"
+" --u32=VALUE Print in iproute2's u32 selector style\n"
+"\n"
+"\n"
+"EXAMPLE\n"
+" $ nl-pktloc-lookup ip.dst\n"
+" $ nl-pktloc-lookup --list\n"
+"\n"
+);
exit(0);
}
+static const char *align_txt[] = {
+ [TCF_EM_ALIGN_U8] = "u8",
+ [TCF_EM_ALIGN_U16] = "u16",
+ [TCF_EM_ALIGN_U32] = "u32"
+};
+
+static uint32_t align_mask[] = {
+ [TCF_EM_ALIGN_U8] = 0xff,
+ [TCF_EM_ALIGN_U16] = 0xffff,
+ [TCF_EM_ALIGN_U32] = 0xffffffff,
+};
+
+static const char *layer_txt[] = {
+ [TCF_LAYER_LINK] = "eth",
+ [TCF_LAYER_NETWORK] = "ip",
+ [TCF_LAYER_TRANSPORT] = "tcp"
+};
+
+static void dump_u32_style(struct rtnl_pktloc *loc, uint32_t value)
+{
+ if (loc->align > 4)
+ nl_cli_fatal(EINVAL, "u32 only supports alignments u8|u16|u32.");
+
+ if (loc->layer == TCF_LAYER_LINK)
+ nl_cli_fatal(EINVAL, "u32 does not support link "
+ "layer locations.");
+
+ if (loc->shift > 0)
+ nl_cli_fatal(EINVAL, "u32 does not support shifting.");
+
+ printf("%s %x %x at %s%u\n",
+ align_txt[loc->align],
+ value, loc->mask ? loc->mask : align_mask[loc->align],
+ loc->layer == TCF_LAYER_TRANSPORT ? "nexthdr+" : "",
+ loc->offset);
+}
+
+static char *get_align_txt(struct rtnl_pktloc *loc)
+{
+ static char buf[16];
+
+ if (loc->align <= 4)
+ strcpy(buf, align_txt[loc->align]);
+ else
+ snprintf(buf, sizeof(buf), "%u", loc->align);
+
+ return buf;
+}
+
+static void dump_loc(struct rtnl_pktloc *loc)
+{
+ printf("%s = %s at %s+%u & %#x >> %u\n",
+ loc->name, get_align_txt(loc), layer_txt[loc->layer],
+ loc->offset, loc->mask, loc->shift);
+}
+
+static void list_cb(struct rtnl_pktloc *loc, void *arg)
+{
+ printf("%-26s %-5s %3s+%-4u %#-10x %-8u %u\n",
+ loc->name, get_align_txt(loc), layer_txt[loc->layer],
+ loc->offset, loc->mask, loc->shift, loc->refcnt);
+}
+
+static void do_list(void)
+{
+ printf(
+"name align offset mask shift refcnt\n");
+ printf("---------------------------------------------------------\n");
+
+ rtnl_pktloc_foreach(&list_cb, NULL);
+}
+
int main(int argc, char *argv[])
{
struct rtnl_pktloc *loc;
- int err;
+ int err, ustyle = 0;
+ uint32_t uvalue = 0;
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_U32 = 257,
+ };
+ static struct option long_opts[] = {
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "list", 0, 0, 'l' },
+ { "u32", 1, 0, ARG_U32 },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "hvl", long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'h': print_usage(); break;
+ case 'v': nl_cli_print_version(); break;
+ case 'l': do_list(); exit(0);
+ case ARG_U32:
+ ustyle = 1;
+ uvalue = nl_cli_parse_u32(optarg);
+ break;
+ }
+ }
- if (argc < 2)
+ if (optind >= argc)
print_usage();
- if ((err = rtnl_pktloc_lookup(argv[1], &loc)) < 0)
+ if ((err = rtnl_pktloc_lookup(argv[optind++], &loc)) < 0)
nl_cli_fatal(err, "Unable to lookup packet location: %s",
nl_geterror(err));
- printf("%s: %u %u+%u 0x%x %u\n", loc->name, loc->align,
- loc->layer, loc->offset, loc->mask, loc->flags);
+ if (ustyle)
+ dump_u32_style(loc, uvalue);
+ else
+ dump_loc(loc);
return 0;
}
diff --git a/src/nl-qdisc-add.c b/src/nl-qdisc-add.c
new file mode 100644
index 00000000..c2a7c9f7
--- /dev/null
+++ b/src/nl-qdisc-add.c
@@ -0,0 +1,146 @@
+/*
+ * src/nl-qdisc-add.c Add Queueing Discipline
+ *
+ * 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) 2010 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
+#include <netlink/cli/qdisc.h>
+#include <netlink/cli/link.h>
+
+#include <netlink-private/route/tc-api.h>
+
+static int quiet = 0;
+
+static void print_usage(void)
+{
+ printf(
+"Usage: nl-qdisc-add [OPTIONS]... QDISC [CONFIGURATION]...\n"
+"\n"
+"OPTIONS\n"
+" -q, --quiet Do not print informal notifications.\n"
+" -h, --help Show this help text.\n"
+" -v, --version Show versioning information.\n"
+" --update Update qdisc if it exists.\n"
+" --replace Replace or update qdisc if it exists.\n"
+" --update-only Only update qdisc, never create it.\n"
+" --replace-only Only replace or update qdisc, never create it.\n"
+" -d, --dev=DEV Network device the qdisc should be attached to.\n"
+" -i, --id=ID ID of new qdisc (default: auto-generated)r\n"
+" -p, --parent=ID ID of parent { root | ingress | QDISC-ID }\n"
+"\n"
+"CONFIGURATION\n"
+" -h, --help Show help text of qdisc specific options.\n"
+"\n"
+"EXAMPLE\n"
+" $ nl-qdisc-add --dev=eth1 --parent=root htb --rate=100mbit\n"
+"\n"
+ );
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct nl_sock *sock;
+ struct rtnl_qdisc *qdisc;
+ struct rtnl_tc *tc;
+ struct nl_cache *link_cache;
+ struct nl_dump_params dp = {
+ .dp_type = NL_DUMP_DETAILS,
+ .dp_fd = stdout,
+ };
+ struct nl_cli_tc_module *tm;
+ struct rtnl_tc_ops *ops;
+ int err, flags = NLM_F_CREATE | NLM_F_EXCL;
+ char *kind, *id = NULL;
+
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_ROUTE);
+
+ link_cache = nl_cli_link_alloc_cache(sock);
+
+ qdisc = nl_cli_qdisc_alloc();
+ tc = (struct rtnl_tc *) qdisc;
+
+ for (;;) {
+ int c, optidx = 0;
+ enum {
+ ARG_REPLACE = 257,
+ ARG_UPDATE = 258,
+ ARG_REPLACE_ONLY,
+ ARG_UPDATE_ONLY,
+ };
+ static struct option long_opts[] = {
+ { "quiet", 0, 0, 'q' },
+ { "help", 0, 0, 'h' },
+ { "version", 0, 0, 'v' },
+ { "dev", 1, 0, 'd' },
+ { "parent", 1, 0, 'p' },
+ { "id", 1, 0, 'i' },
+ { "replace", 0, 0, ARG_REPLACE },
+ { "update", 0, 0, ARG_UPDATE },
+ { "replace-only", 0, 0, ARG_REPLACE_ONLY },
+ { "update-only", 0, 0, ARG_UPDATE_ONLY },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "+qhvd:p:i:",
+ long_opts, &optidx);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'q': quiet = 1; break;
+ case 'h': print_usage(); break;
+ case 'v': nl_cli_print_version(); break;
+ case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break;
+ case 'p': nl_cli_tc_parse_parent(tc, optarg); break;
+ case 'i': id = strdup(optarg); break;
+ case ARG_UPDATE: flags = NLM_F_CREATE; break;
+ case ARG_REPLACE: flags = NLM_F_CREATE | NLM_F_REPLACE; break;
+ case ARG_UPDATE_ONLY: flags = 0; break;
+ case ARG_REPLACE_ONLY: flags = NLM_F_REPLACE; break;
+ }
+ }
+
+ if (optind >= argc)
+ print_usage();
+
+ if (!rtnl_tc_get_ifindex(tc))
+ nl_cli_fatal(EINVAL, "You must specify a network device (--dev=XXX)");
+
+ if (!rtnl_tc_get_parent(tc))
+ nl_cli_fatal(EINVAL, "You must specify a parent");
+
+ if (id) {
+ nl_cli_tc_parse_handle(tc, id, 1);
+ free(id);
+ }
+
+ kind = argv[optind++];
+ rtnl_tc_set_kind(tc, kind);
+
+ if (!(ops = rtnl_tc_get_ops(tc)))
+ nl_cli_fatal(ENOENT, "Unknown qdisc \"%s\"", kind);
+
+ if (!(tm = nl_cli_tc_lookup(ops)))
+ nl_cli_fatal(ENOTSUP, "Qdisc type \"%s\" not supported.", kind);
+
+ tm->tm_parse_argv(tc, argc, argv);
+
+ if (!quiet) {
+ printf("Adding ");
+ nl_object_dump(OBJ_CAST(qdisc), &dp);
+ }
+
+ if ((err = rtnl_qdisc_add(sock, qdisc, flags)) < 0)
+ nl_cli_fatal(EINVAL, "Unable to add qdisc: %s", nl_geterror(err));
+
+ return 0;
+}
diff --git a/src/nl-qdisc-delete.c b/src/nl-qdisc-delete.c
index 5cb7aa35..2f945bb2 100644
--- a/src/nl-qdisc-delete.c
+++ b/src/nl-qdisc-delete.c
@@ -6,32 +6,32 @@
* License as published by the Free Software Foundation version 2.1
* of the License.
*
- * Copyright (c) 2003-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2010 Thomas Graf <tgraf@suug.ch>
*/
#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
#include <netlink/cli/qdisc.h>
#include <netlink/cli/link.h>
static int quiet = 0, default_yes = 0, deleted = 0, interactive = 0;
-struct nl_sock *sock;
+static struct nl_sock *sock;
static void print_usage(void)
{
printf(
"Usage: nl-qdisc-delete [OPTION]... [QDISC]\n"
"\n"
- "Options\n"
- " -i, --interactive Run interactively\n"
- " --yes Set default answer to yes\n"
- " -q, --quiet Do not print informal notifications\n"
- " -h, --help Show this help\n"
- " -v, --version Show versioning information\n"
+ "OPTIONS\n"
+ " --interactive Run interactively.\n"
+ " --yes Set default answer to yes.\n"
+ " -q, --quiet Do not print informal notifications.\n"
+ " -h, --help Show this help text and exit.\n"
+ " -v, --version Show versioning information and exit.\n"
"\n"
- "QDisc Options\n"
- " -d, --dev=DEV Device the qdisc is attached to\n"
- " -p, --parent=HANDLE Identifier of parent qdisc\n"
- " -H, --handle=HANDLE Identifier\n"
+ " -d, --dev=DEV Device the qdisc is attached to.\n"
+ " -p, --parent=ID Identifier of parent qdisc/class.\n"
+ " -i, --id=ID Identifier\n"
" -k, --kind=NAME Kind of qdisc (e.g. pfifo_fast)\n"
);
@@ -47,6 +47,10 @@ static void delete_cb(struct nl_object *obj, void *arg)
};
int err;
+ /* Ignore default qdiscs, unable to delete */
+ if (rtnl_tc_get_handle((struct rtnl_tc *) qdisc) == 0)
+ return;
+
if (interactive && !nl_cli_confirm(obj, &params, default_yes))
return;
@@ -64,49 +68,73 @@ static void delete_cb(struct nl_object *obj, void *arg)
int main(int argc, char *argv[])
{
struct rtnl_qdisc *qdisc;
+ struct rtnl_tc *tc;
struct nl_cache *link_cache, *qdisc_cache;
+ int nfilter = 0;
sock = nl_cli_alloc_socket();
nl_cli_connect(sock, NETLINK_ROUTE);
link_cache = nl_cli_link_alloc_cache(sock);
qdisc_cache = nl_cli_qdisc_alloc_cache(sock);
qdisc = nl_cli_qdisc_alloc();
+ tc = (struct rtnl_tc *) qdisc;
for (;;) {
int c, optidx = 0;
enum {
ARG_YES = 257,
+ ARG_INTERACTIVE = 258,
};
static struct option long_opts[] = {
- { "interactive", 0, 0, 'i' },
+ { "interactive", 0, 0, ARG_INTERACTIVE },
{ "yes", 0, 0, ARG_YES },
{ "quiet", 0, 0, 'q' },
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'v' },
{ "dev", 1, 0, 'd' },
{ "parent", 1, 0, 'p' },
- { "handle", 1, 0, 'H' },
+ { "id", 1, 0, 'i' },
{ "kind", 1, 0, 'k' },
{ 0, 0, 0, 0 }
};
- c = getopt_long(argc, argv, "iqhvd:p:H:k:", long_opts, &optidx);
+ c = getopt_long(argc, argv, "qhvd:p:i:k:", long_opts, &optidx);
if (c == -1)
break;
switch (c) {
- case 'i': interactive = 1; break;
+ case '?': nl_cli_fatal(EINVAL, "Invalid options");
+ case ARG_INTERACTIVE: interactive = 1; break;
case ARG_YES: default_yes = 1; break;
case 'q': quiet = 1; break;
case 'h': print_usage(); break;
case 'v': nl_cli_print_version(); break;
- case 'd': nl_cli_qdisc_parse_dev(qdisc, link_cache, optarg); break;
- case 'p': nl_cli_qdisc_parse_parent(qdisc, optarg); break;
- case 'H': nl_cli_qdisc_parse_handle(qdisc, optarg); break;
- case 'k': nl_cli_qdisc_parse_kind(qdisc, optarg); break;
+ case 'd':
+ nfilter++;
+ nl_cli_tc_parse_dev(tc, link_cache, optarg);
+ break;
+ case 'p':
+ nfilter++;
+ nl_cli_tc_parse_parent(tc, optarg);
+ break;
+ case 'i':
+ nfilter++;
+ nl_cli_tc_parse_handle(tc, optarg, 0);
+ break;
+ case 'k':
+ nfilter++;
+ nl_cli_tc_parse_kind(tc, optarg);
+ break;
}
}
+ if (nfilter == 0 && !interactive && !default_yes) {
+ nl_cli_fatal(EINVAL,
+ "You are attempting to delete all qdiscs on all devices.\n"
+ "If you want to proceed, run nl-qdisc-delete --yes.\n"
+ "Aborting...");
+ }
+
nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(qdisc), delete_cb, NULL);
if (!quiet)
diff --git a/src/nl-qdisc-list.c b/src/nl-qdisc-list.c
index ec6e25f0..5b0a3f00 100644
--- a/src/nl-qdisc-list.c
+++ b/src/nl-qdisc-list.c
@@ -1,90 +1,183 @@
/*
- * src/nl-qdisc-list.c List Qdiscs
+ * src/nl-qdisc-list.c List 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-2009 Thomas Graf <tgraf@suug.ch>
+ * Copyright (c) 2003-2010 Thomas Graf <tgraf@suug.ch>
*/
#include <netlink/cli/utils.h>
+#include <netlink/cli/tc.h>
#include <netlink/cli/qdisc.h>
+#include <netlink/cli/class.h>
+#include <netlink/cli/cls.h>
#include <netlink/cli/link.h>
-static int quiet = 0;
+#define NUM_INDENT 4
+
+static struct nl_sock *sock;
+static int recursive = 0;
+static struct nl_dump_params params = {
+ .dp_type = NL_DUMP_LINE,
+};
static void print_usage(void)
{
printf(
"Usage: nl-qdisc-list [OPTION]... [QDISC]\n"
"\n"
- "Options\n"
- " -f, --format=TYPE Output format { brief | details | stats }\n"
+ "OPTIONS\n"
+ " --details Show details\n"
+ " --stats Show statistics\n"
+ " -r, --recursive Show recursive tree\n"
" -h, --help Show this help\n"
" -v, --version Show versioning information\n"
"\n"
- "QDisc Options\n"
- " -d, --dev=DEV Device the qdisc is attached to\n"
- " -p, --parent=HANDLE Identifier of parent qdisc\n"
- " -H, --handle=HANDLE Identifier\n"
+ " -d, --dev=DEV Device the qdisc is attached to. (default: all)\n"
+ " -p, --parent=ID Identifier of parent qdisc.\n"
+ " -i, --id=ID Identifier.\n"
" -k, --kind=NAME Kind of qdisc (e.g. pfifo_fast)\n"
+ "\n"
+ "EXAMPLE\n"
+ " # Display statistics of all qdiscs attached to eth0\n"
+ " $ nl-qdisc-list --details --dev=eth0\n"
+ "\n"
);
exit(0);
}
+static void list_classes(int ifindex, uint32_t parent);
+static void list_qdiscs(int ifindex, uint32_t parent);
+
+static void list_class(struct nl_object *obj, void *arg)
+{
+ struct rtnl_tc *tc = nl_object_priv(obj);
+ nl_object_dump(obj, &params);
+
+ list_classes(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc));
+ list_qdiscs(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc));
+}
+
+static void list_classes(int ifindex, uint32_t parent)
+{
+ struct nl_cache *class_cache;
+ struct rtnl_class *filter = nl_cli_class_alloc();
+
+ class_cache = nl_cli_class_alloc_cache(sock, ifindex);
+
+ rtnl_tc_set_parent((struct rtnl_tc *) filter, parent);
+ params.dp_prefix += NUM_INDENT;
+ nl_cache_foreach_filter(class_cache, OBJ_CAST(filter), list_class, NULL);
+ params.dp_prefix -= NUM_INDENT;
+
+ rtnl_class_put(filter);
+ nl_cache_free(class_cache);
+}
+
+static void list_cls(int ifindex, uint32_t parent)
+{
+ struct nl_cache *cls_cache;
+
+ cls_cache = nl_cli_cls_alloc_cache(sock, ifindex, parent);
+
+ params.dp_prefix += NUM_INDENT;
+ nl_cache_dump(cls_cache, &params);
+ params.dp_prefix -= NUM_INDENT;
+
+ nl_cache_free(cls_cache);
+}
+
+static void list_qdisc(struct nl_object *obj, void *arg)
+{
+ struct rtnl_qdisc *qdisc = nl_object_priv(obj);
+ struct rtnl_tc *tc = (struct rtnl_tc *) qdisc;
+
+ nl_object_dump(obj, &params);
+
+ list_cls(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc));
+
+ if (rtnl_tc_get_parent(tc) == TC_H_ROOT) {
+ list_cls(rtnl_tc_get_ifindex(tc), TC_H_ROOT);
+ list_classes(rtnl_tc_get_ifindex(tc), TC_H_ROOT);
+ }
+
+ list_classes(rtnl_tc_get_ifindex(tc), rtnl_tc_get_handle(tc));
+}
+
+static void list_qdiscs(int ifindex, uint32_t parent)
+{
+ struct nl_cache *qdisc_cache;
+ struct rtnl_qdisc *filter = nl_cli_qdisc_alloc();
+
+ qdisc_cache = nl_cli_qdisc_alloc_cache(sock);
+
+ rtnl_tc_set_ifindex((struct rtnl_tc *) filter, ifindex);
+ rtnl_tc_set_parent((struct rtnl_tc *) filter, parent);
+ params.dp_prefix += NUM_INDENT;
+ nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(filter), list_qdisc, NULL);
+ params.dp_prefix -= NUM_INDENT;
+
+ rtnl_qdisc_put(filter);
+ nl_cache_free(qdisc_cache);
+}
+
int main(int argc, char *argv[])
{
- struct nl_sock *sock;
struct rtnl_qdisc *qdisc;
+ struct rtnl_tc *tc;
struct nl_cache *link_cache, *qdisc_cache;
- struct nl_dump_params params = {
- .dp_type = NL_DUMP_LINE,
- .dp_fd = stdout,
- };
+ params.dp_fd = stdout;
sock = nl_cli_alloc_socket();
nl_cli_connect(sock, NETLINK_ROUTE);
link_cache = nl_cli_link_alloc_cache(sock);
qdisc_cache = nl_cli_qdisc_alloc_cache(sock);
qdisc = nl_cli_qdisc_alloc();
+ tc = (struct rtnl_tc *) qdisc;
for (;;) {
int c, optidx = 0;
enum {
- ARG_YES = 257,
+ ARG_DETAILS = 257,
+ ARG_STATS = 258,
};
static struct option long_opts[] = {
- { "format", 1, 0, 'f' },
- { "quiet", 0, 0, 'q' },
+ { "details", 0, 0, ARG_DETAILS },
+ { "stats", 0, 0, ARG_STATS },
+ { "recursive", 0, 0, 'r' },
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'v' },
{ "dev", 1, 0, 'd' },
{ "parent", 1, 0, 'p' },
- { "handle", 1, 0, 'H' },
+ { "id", 1, 0, 'i' },
{ "kind", 1, 0, 'k' },
{ 0, 0, 0, 0 }
};
- c = getopt_long(argc, argv, "f:qhvd:p:H:k:",
- long_opts, &optidx);
+ c = getopt_long(argc, argv, "rhvd:p:i:k:", long_opts, &optidx);
if (c == -1)
break;
switch (c) {
- case 'f': params.dp_type = nl_cli_parse_dumptype(optarg); break;
- case 'q': quiet = 1; break;
+ case ARG_DETAILS: params.dp_type = NL_DUMP_DETAILS; break;
+ case ARG_STATS: params.dp_type = NL_DUMP_STATS; break;
+ case 'r': recursive = 1; break;
case 'h': print_usage(); break;
case 'v': nl_cli_print_version(); break;
- case 'd': nl_cli_qdisc_parse_dev(qdisc, link_cache, optarg); break;
- case 'p': nl_cli_qdisc_parse_parent(qdisc, optarg); break;
- case 'H': nl_cli_qdisc_parse_handle(qdisc, optarg); break;
- case 'k': nl_cli_qdisc_parse_kind(qdisc, optarg); break;
+ case 'd': nl_cli_tc_parse_dev(tc, link_cache, optarg); break;
+ case 'p': nl_cli_tc_parse_parent(tc, optarg); break;
+ case 'i': nl_cli_tc_parse_handle(tc, optarg, 0); break;
+ case 'k': nl_cli_tc_parse_kind(tc, optarg); break;
}
}
- nl_cache_dump_filter(qdisc_cache, &params, OBJ_CAST(qdisc));
+ if (recursive)
+ nl_cache_foreach_filter(qdisc_cache, OBJ_CAST(qdisc), list_qdisc, NULL);
+ else
+ nl_cache_dump_filter(qdisc_cache, &params, OBJ_CAST(qdisc));
return 0;
}
diff --git a/src/nl-route-add.c b/src/nl-route-add.c
index 2f187df3..d4aa767e 100644
--- a/src/nl-route-add.c
+++ b/src/nl-route-add.c
@@ -120,7 +120,7 @@ int main(int argc, char *argv[])
}
}
- if ((err = rtnl_route_add(sock, route, 0)) < 0)
+ if ((err = rtnl_route_add(sock, route, NLM_F_EXCL)) < 0)
nl_cli_fatal(err, "Unable to add route: %s", nl_geterror(err));
if (!quiet) {
diff --git a/src/nl-route-get.c b/src/nl-route-get.c
index c2f07d4c..9d9a4484 100644
--- a/src/nl-route-get.c
+++ b/src/nl-route-get.c
@@ -65,8 +65,12 @@ int main(int argc, char *argv[])
};
m = nlmsg_alloc_simple(RTM_GETROUTE, 0);
- nlmsg_append(m, &rmsg, sizeof(rmsg), NLMSG_ALIGNTO);
- nla_put_addr(m, RTA_DST, dst);
+ if (!m)
+ nl_cli_fatal(ENOMEM, "out of memory");
+ if (nlmsg_append(m, &rmsg, sizeof(rmsg), NLMSG_ALIGNTO) < 0)
+ nl_cli_fatal(ENOMEM, "out of memory");
+ if (nla_put_addr(m, RTA_DST, dst) < 0)
+ nl_cli_fatal(ENOMEM, "out of memory");
err = nl_send_auto_complete(sock, m);
nlmsg_free(m);
diff --git a/src/nl-tctree-list.c b/src/nl-tctree-list.c
index a074a512..d90cb28f 100644
--- a/src/nl-tctree-list.c
+++ b/src/nl-tctree-list.c
@@ -12,6 +12,7 @@
#include <netlink/cli/utils.h>
#include <netlink/cli/link.h>
#include <netlink/cli/qdisc.h>
+#include <netlink/cli/class.h>
#include <linux/pkt_sched.h>
static struct nl_sock *sock;
@@ -22,6 +23,7 @@ static struct nl_dump_params params = {
static int ifindex;
static void print_qdisc(struct nl_object *, void *);
+static void print_tc_childs(struct rtnl_tc *, void *);
static void print_usage(void)
{
@@ -41,7 +43,7 @@ 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);
+ uint32_t parent = rtnl_tc_get_handle((struct rtnl_tc *) class);
params.dp_prefix = (int)(long) arg;
nl_object_dump(obj, &params);
@@ -50,7 +52,7 @@ static void print_class(struct nl_object *obj, void *arg)
if (leaf)
print_qdisc((struct nl_object *) leaf, arg + 2);
- rtnl_class_foreach_child(class, class_cache, &print_class, arg + 2);
+ print_tc_childs(TC_CAST(class), arg + 2);
if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0)
return;
@@ -60,16 +62,30 @@ static void print_class(struct nl_object *obj, void *arg)
nl_cache_free(cls_cache);
}
+static void print_tc_childs(struct rtnl_tc *tc, void *arg)
+{
+ struct rtnl_class *filter;
+
+ filter = nl_cli_class_alloc();
+
+ rtnl_tc_set_parent(TC_CAST(filter), rtnl_tc_get_handle(tc));
+ rtnl_tc_set_ifindex(TC_CAST(filter), rtnl_tc_get_ifindex(tc));
+
+ nl_cache_foreach_filter(class_cache, OBJ_CAST(filter), &print_class, arg);
+
+ rtnl_class_put(filter);
+}
+
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);
+ uint32_t parent = rtnl_tc_get_handle((struct rtnl_tc *) qdisc);
params.dp_prefix = (int)(long) arg;
nl_object_dump(obj, &params);
- rtnl_qdisc_foreach_child(qdisc, class_cache, &print_class, arg + 2);
+ print_tc_childs(TC_CAST(qdisc), arg + 2);
if (rtnl_cls_alloc_cache(sock, ifindex, parent, &cls_cache) < 0)
return;
diff --git a/tests/.gitignore b/tests/.gitignore
index 19184ee5..6b77cacc 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -1,4 +1,23 @@
-test-cache-mngr
-test-genl
-test-nf-cache-mngr
-test-socket-creation
+/check-all
+/check-all.log
+/check-all.trs
+/test-*.log
+/test-*.trs
+/test-cache-mngr
+/test-complex-HTB-with-hash-filters
+/test-create-bond
+/test-create-bridge
+/test-create-ip6tnl
+/test-create-ipgre
+/test-create-ipip
+/test-create-ipvti
+/test-create-sit
+/test-create-veth
+/test-create-vlan
+/test-create-vxlan
+/test-delete-link
+/test-genl
+/test-nf-cache-mngr
+/test-socket-creation
+/test-suite.log
+/test-u32-filter-with-actions
diff --git a/tests/Makefile b/tests/Makefile
deleted file mode 100644
index 8494eea0..00000000
--- a/tests/Makefile
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# 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 <tgraf@suug.ch>
-#
-
-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) -lnl-genl -lnl-route
-
-clean:
- @echo " CLEAN src"; \
- rm -f $(TOOLS)
-
-distclean: clean
-
-install:
- @true
-
-include ../Makefile.rules
diff --git a/tests/Makefile.am b/tests/Makefile.am
new file mode 100644
index 00000000..255033d7
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,67 @@
+# -*- Makefile -*-
+
+EXTRA_DIST = \
+ util.h
+
+if ENABLE_UNIT_TESTS
+
+AM_CPPFLAGS = -Wall -I${top_srcdir}/include -I${top_builddir}/include -D_GNU_SOURCE -DSYSCONFDIR=\"$(sysconfdir)/libnl\"
+
+LDADD = \
+ ${top_builddir}/lib/libnl-3.la \
+ ${top_builddir}/lib/libnl-nf-3.la \
+ ${top_builddir}/lib/libnl-genl-3.la \
+ ${top_builddir}/lib/libnl-route-3.la \
+ @CHECK_LIBS@
+
+AM_CFLAGS = @CHECK_CFLAGS@
+
+UNIT_TESTS = check-all
+
+check_PROGRAMS = \
+ test-create-bond \
+ test-create-vlan \
+ test-create-vxlan \
+ test-create-veth \
+ test-create-bridge \
+ test-create-ip6tnl \
+ test-create-ipgre \
+ test-create-ipip \
+ test-create-ipvti \
+ test-create-sit \
+ test-delete-link \
+ test-socket-creation \
+ test-complex-HTB-with-hash-filters \
+ test-u32-filter-with-actions \
+ ${UNIT_TESTS}
+
+TESTS = \
+ ${UNIT_TESTS}
+
+if ENABLE_CLI
+LDADD += ${top_builddir}/src/lib/libnl-cli-3.la
+check_PROGRAMS += \
+ test-cache-mngr \
+ test-genl \
+ test-nf-cache-mngr
+endif
+
+test_cache_mngr_SOURCES = test-cache-mngr.c
+test_create_bond_SOURCES = test-create-bond.c
+test_create_vlan_SOURCES = test-create-vlan.c
+test_create_vxlan_SOURCES = test-create-vxlan.c
+test_create_veth_SOURCES = test-create-veth.c
+test_create_bridge_SOURCES = test-create-bridge.c
+test_delete_link_SOURCES = test-delete-link.c
+test_genl_SOURCES = test-genl.c
+test_nf_cache_mngr_SOURCES = test-nf-cache-mngr.c
+test_socket_creation_SOURCES = test-socket-creation.c
+test_complex_HTB_with_hash_filters_SOURCES = test-complex-HTB-with-hash-filters.c
+test_u32_filter_with_actions_SOURCES = test-u32-filter-with-actions.c
+
+# Unit tests
+check_all_SOURCES = \
+ check-all.c \
+ check-addr.c \
+ check-attr.c
+endif
diff --git a/tests/check-addr.c b/tests/check-addr.c
new file mode 100644
index 00000000..39f3ede4
--- /dev/null
+++ b/tests/check-addr.c
@@ -0,0 +1,212 @@
+/*
+ * tests/check-addr.c nl_addr unit tests
+ *
+ * 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) 2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <check.h>
+#include <netlink/addr.h>
+
+START_TEST(addr_alloc)
+{
+ struct nl_addr *addr;
+
+ addr = nl_addr_alloc(16);
+ fail_if(addr == NULL,
+ "Allocation should not return NULL");
+
+ fail_if(nl_addr_iszero(addr) == 0,
+ "New empty address should be all zeros");
+
+ fail_if(nl_addr_get_family(addr) != AF_UNSPEC,
+ "New empty address should have family AF_UNSPEC");
+
+ fail_if(nl_addr_get_prefixlen(addr) != 0,
+ "New empty address should have prefix length 0");
+
+ fail_if(nl_addr_shared(addr),
+ "New empty address should not be shared");
+
+ fail_if(nl_addr_get(addr) != addr,
+ "nl_addr_get() should return pointer to address");
+
+ fail_if(nl_addr_shared(addr) == 0,
+ "Address should be shared after call to nl_addr_get()");
+
+ nl_addr_put(addr);
+
+ fail_if(nl_addr_shared(addr),
+ "Address should not be shared after call to nl_addr_put()");
+
+ fail_if(nl_addr_fill_sockaddr(addr, NULL, 0) == 0,
+ "Socket address filling should fail for empty address");
+
+ nl_addr_put(addr);
+}
+END_TEST
+
+START_TEST(addr_binary_addr)
+{
+ struct nl_addr *addr, *addr2;
+ char baddr[4] = { 0x1, 0x2, 0x3, 0x4 };
+ char baddr2[6] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6 };
+
+ addr = nl_addr_alloc(4);
+ fail_if(addr == NULL,
+ "Allocation should not return NULL");
+
+ fail_if(nl_addr_set_binary_addr(addr, baddr, 4) < 0,
+ "Valid binary address should be settable");
+
+ fail_if(nl_addr_get_prefixlen(addr) != 0,
+ "Prefix length should be unchanged after nl_addr_set_binary_addr()");
+
+ fail_if(nl_addr_get_len(addr) != 4,
+ "Address length should be 4");
+
+ fail_if(nl_addr_set_binary_addr(addr, baddr2, 6) == 0,
+ "Should not be able to set binary address exceeding maximum length");
+
+ fail_if(nl_addr_get_len(addr) != 4,
+ "Address length should still be 4");
+
+ fail_if(nl_addr_guess_family(addr) != AF_INET,
+ "Binary address of length 4 should be guessed as AF_INET");
+
+ fail_if(memcmp(baddr, nl_addr_get_binary_addr(addr), 4) != 0,
+ "Binary address mismatches");
+
+ addr2 = nl_addr_build(AF_UNSPEC, baddr, 4);
+ fail_if(addr2 == NULL,
+ "Building of address should not fail");
+
+ nl_addr_set_prefixlen(addr, 32);
+ fail_if(nl_addr_get_prefixlen(addr) != 32,
+ "Prefix length should be successful changed after nl_addr_set_prefixlen()");
+
+ fail_if(nl_addr_cmp(addr, addr2),
+ "Addresses built from same binary address should match");
+
+ nl_addr_put(addr);
+ nl_addr_put(addr2);
+}
+END_TEST
+
+START_TEST(addr_parse4)
+{
+ struct nl_addr *addr4, *clone;
+ struct sockaddr_in sin;
+ socklen_t len = sizeof(sin);
+ char *addr_str = "10.0.0.1/16";
+ char buf[128];
+
+ fail_if(nl_addr_parse(addr_str, AF_INET6, &addr4) == 0,
+ "Should not be able to parse IPv4 address in IPv6 mode");
+
+ fail_if(nl_addr_parse(addr_str, AF_UNSPEC, &addr4) != 0,
+ "Should be able to parse \"%s\"", addr_str);
+
+ fail_if(nl_addr_get_family(addr4) != AF_INET,
+ "Address family should be AF_INET");
+
+ fail_if(nl_addr_get_prefixlen(addr4) != 16,
+ "Prefix length should be 16");
+
+ fail_if(nl_addr_iszero(addr4),
+ "Address should not be all zeroes");
+
+ clone = nl_addr_clone(addr4);
+ fail_if(clone == NULL,
+ "Cloned address should not be NULL");
+
+ fail_if(nl_addr_cmp(addr4, clone) != 0,
+ "Cloned address should not mismatch original");
+
+ fail_if(nl_addr_fill_sockaddr(addr4, (struct sockaddr *) &sin, &len) != 0,
+ "Should be able to fill socketaddr");
+
+ fail_if(strcmp(nl_addr2str(addr4, buf, sizeof(buf)), addr_str),
+ "Address translated back to string does not match original");
+
+ nl_addr_put(addr4);
+ nl_addr_put(clone);
+}
+END_TEST
+
+START_TEST(addr_parse6)
+{
+ struct nl_addr *addr6, *clone;
+ struct sockaddr_in6 sin;
+ socklen_t len = sizeof(sin);
+ char *addr_str = "2001:1:2::3/64";
+ char buf[128];
+
+ fail_if(nl_addr_parse(addr_str, AF_INET, &addr6) == 0,
+ "Should not be able to parse IPv6 address in IPv4 mode");
+
+ fail_if(nl_addr_parse(addr_str, AF_UNSPEC, &addr6) != 0,
+ "Should be able to parse \"%s\"", addr_str);
+
+ fail_if(nl_addr_get_family(addr6) != AF_INET6,
+ "Address family should be AF_INET6");
+
+ fail_if(nl_addr_get_prefixlen(addr6) != 64,
+ "Prefix length should be 64");
+
+ fail_if(nl_addr_iszero(addr6),
+ "Address should not be all zeroes");
+
+ clone = nl_addr_clone(addr6);
+ fail_if(clone == NULL,
+ "Cloned address should not be NULL");
+
+ fail_if(nl_addr_cmp(addr6, clone) != 0,
+ "Cloned address should not mismatch original");
+
+ fail_if(nl_addr_fill_sockaddr(addr6, (struct sockaddr *) &sin, &len) != 0,
+ "Should be able to fill socketaddr");
+
+ fail_if(strcmp(nl_addr2str(addr6, buf, sizeof(buf)), addr_str),
+ "Address translated back to string does not match original");
+
+ nl_addr_put(addr6);
+ nl_addr_put(clone);
+}
+END_TEST
+
+START_TEST(addr_info)
+{
+ struct nl_addr *addr;
+ char *addr_str = "127.0.0.1";
+ struct addrinfo *result;
+
+ fail_if(nl_addr_parse(addr_str, AF_UNSPEC, &addr) != 0,
+ "Parsing of valid address should not fail");
+
+ fail_if(nl_addr_info(addr, &result) != 0,
+ "getaddrinfo() on loopback address should work");
+
+ freeaddrinfo(result);
+ nl_addr_put(addr);
+}
+END_TEST
+
+Suite *make_nl_addr_suite(void)
+{
+ Suite *suite = suite_create("Abstract addresses");
+
+ TCase *tc_addr = tcase_create("Core");
+ tcase_add_test(tc_addr, addr_alloc);
+ tcase_add_test(tc_addr, addr_binary_addr);
+ tcase_add_test(tc_addr, addr_parse4);
+ tcase_add_test(tc_addr, addr_parse6);
+ tcase_add_test(tc_addr, addr_info);
+ suite_add_tcase(suite, tc_addr);
+
+ return suite;
+}
diff --git a/tests/check-all.c b/tests/check-all.c
new file mode 100644
index 00000000..e4318024
--- /dev/null
+++ b/tests/check-all.c
@@ -0,0 +1,44 @@
+/*
+ * tests/check-all.c overall unit test program
+ *
+ * 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) 2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <check.h>
+
+extern Suite *make_nl_addr_suite(void);
+extern Suite *make_nl_attr_suite(void);
+
+static Suite *main_suite(void)
+{
+ Suite *suite = suite_create("main");
+
+ return suite;
+}
+
+int main(int argc, char *argv[])
+{
+ SRunner *runner;
+ int nfailed;
+
+ runner = srunner_create(main_suite());
+
+ /* Add testsuites below */
+
+ srunner_add_suite(runner, make_nl_addr_suite());
+ srunner_add_suite(runner, make_nl_attr_suite());
+
+ /* Do not add testsuites below this line */
+
+ srunner_run_all(runner, CK_ENV);
+
+ nfailed = srunner_ntests_failed(runner);
+ srunner_free(runner);
+
+ return nfailed != 0;
+}
diff --git a/tests/check-attr.c b/tests/check-attr.c
new file mode 100644
index 00000000..d8622301
--- /dev/null
+++ b/tests/check-attr.c
@@ -0,0 +1,88 @@
+/*
+ * tests/check-attr.c nla_attr unit tests
+ *
+ * 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) 2013 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include "util.h"
+#include <netlink/attr.h>
+#include <netlink/msg.h>
+
+START_TEST(attr_size)
+{
+ fail_if(nla_attr_size(0) != NLA_HDRLEN,
+ "Length of empty attribute should match header size");
+ fail_if(nla_attr_size(1) != NLA_HDRLEN + 1,
+ "Length of 1 bytes payload should be NLA_HDRLEN + 1");
+ fail_if(nla_attr_size(2) != NLA_HDRLEN + 2,
+ "Length of 2 bytes payload should be NLA_HDRLEN + 2");
+ fail_if(nla_attr_size(3) != NLA_HDRLEN + 3,
+ "Length of 3 bytes payload should be NLA_HDRLEN + 3");
+ fail_if(nla_attr_size(4) != NLA_HDRLEN + 4,
+ "Length of 4 bytes payload should be NLA_HDRLEN + 4");
+
+ fail_if(nla_total_size(1) != NLA_HDRLEN + 4,
+ "Total size of 1 bytes payload should result in 8 bytes");
+ fail_if(nla_total_size(2) != NLA_HDRLEN + 4,
+ "Total size of 2 bytes payload should result in 8 bytes");
+ fail_if(nla_total_size(3) != NLA_HDRLEN + 4,
+ "Total size of 3 bytes payload should result in 8 bytes");
+ fail_if(nla_total_size(4) != NLA_HDRLEN + 4,
+ "Total size of 4 bytes payload should result in 8 bytes");
+
+ fail_if(nla_padlen(1) != 3,
+ "2 bytes of payload should result in 3 padding bytes");
+ fail_if(nla_padlen(2) != 2,
+ "2 bytes of payload should result in 2 padding bytes");
+ fail_if(nla_padlen(3) != 1,
+ "3 bytes of payload should result in 1 padding bytes");
+ fail_if(nla_padlen(4) != 0,
+ "4 bytes of payload should result in 0 padding bytes");
+ fail_if(nla_padlen(5) != 3,
+ "5 bytes of payload should result in 3 padding bytes");
+}
+END_TEST
+
+START_TEST(msg_construct)
+{
+ struct nl_msg *msg;
+ struct nlmsghdr *nlh;
+ struct nlattr *a;
+ int i, rem;
+
+ msg = nlmsg_alloc();
+ fail_if(!msg, "Unable to allocate netlink message");
+
+ for (i = 1; i < 256; i++) {
+ fail_if(nla_put_u32(msg, i, i+1) != 0,
+ "Unable to add attribute %d", i);
+ }
+
+ nlh = nlmsg_hdr(msg);
+ i = 1;
+ nlmsg_for_each_attr(a, nlh, 0, rem) {
+ fail_if(nla_type(a) != i, "Expected attribute %d", i);
+ i++;
+ fail_if(nla_get_u32(a) != i, "Expected attribute value %d", i);
+ }
+
+ nlmsg_free(msg);
+}
+END_TEST
+
+Suite *make_nl_attr_suite(void)
+{
+ Suite *suite = suite_create("Netlink attributes");
+
+ TCase *nl_attr = tcase_create("Core");
+ tcase_add_test(nl_attr, attr_size);
+ tcase_add_test(nl_attr, msg_construct);
+ suite_add_tcase(suite, nl_attr);
+
+ return suite;
+}
diff --git a/tests/test-cache-mngr.c b/tests/test-cache-mngr.c
index 777bce89..8999e587 100644
--- a/tests/test-cache-mngr.c
+++ b/tests/test-cache-mngr.c
@@ -1,16 +1,20 @@
-#include "../src/utils.h"
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/cli/utils.h>
#include <signal.h>
+#include <netlink-private/cache-api.h>
+
static int quit = 0;
+static struct nl_dump_params dp = {
+ .dp_type = NL_DUMP_LINE,
+};
+
+
static void change_cb(struct nl_cache *cache, struct nl_object *obj,
- int action)
+ int action, void *data)
{
- struct nl_dump_params dp = {
- .dp_type = NL_DUMP_LINE,
- .dp_fd = stdout,
- };
-
if (action == NL_ACT_NEW)
printf("NEW ");
else if (action == NL_ACT_DEL)
@@ -29,43 +33,34 @@ static void sigint(int arg)
int main(int argc, char *argv[])
{
struct nl_cache_mngr *mngr;
- struct nl_cache *lc, *nc, *ac, *rc;
- struct nl_sock *sock;
- int err;
+ struct nl_cache *cache;
+ int err, i;
+
+ dp.dp_fd = stdout;
signal(SIGINT, sigint);
- sock = nlt_alloc_socket();
- err = nl_cache_mngr_alloc(sock, NETLINK_ROUTE, NL_AUTO_PROVIDE, &mngr);
+ err = nl_cache_mngr_alloc(NULL, NETLINK_ROUTE, NL_AUTO_PROVIDE, &mngr);
if (err < 0)
- fatal(err, "Unable to allocate cache manager: %s",
- nl_geterror(err));
-
- if ((err = nl_cache_mngr_add(mngr, "route/link", &change_cb, &lc)) < 0)
- fatal(err, "Unable to add cache route/link: %s",
- nl_geterror(err));
-
- if ((err = nl_cache_mngr_add(mngr, "route/neigh", &change_cb, &nc)) < 0)
- fatal(err, "Unable to add cache route/neigh: %s",
- nl_geterror(err));
-
- if ((err = nl_cache_mngr_add(mngr, "route/addr", &change_cb, &ac)) < 0)
- fatal(err, "Unable to add cache route/addr: %s",
- nl_geterror(err));
-
- if ((err = nl_cache_mngr_add(mngr, "route/route", &change_cb, &rc)) < 0)
- fatal(err, "Unable to add cache route/route: %s",
- nl_geterror(err));
+ nl_cli_fatal(err, "Unable to allocate cache manager: %s",
+ nl_geterror(err));
+
+ for (i = 1; i < argc; i++) {
+ err = nl_cache_mngr_add(mngr, argv[i], &change_cb, NULL, &cache);
+ if (err < 0)
+ nl_cli_fatal(err, "Unable to add cache %s: %s",
+ argv[i], nl_geterror(err));
+ }
while (!quit) {
- int err = nl_cache_mngr_poll(mngr, 5000);
+ int err = nl_cache_mngr_poll(mngr, 1000);
if (err < 0 && err != -NLE_INTR)
- fatal(err, "Polling failed: %s", nl_geterror(err));
+ nl_cli_fatal(err, "Polling failed: %s", nl_geterror(err));
+ nl_cache_mngr_info(mngr, &dp);
}
nl_cache_mngr_free(mngr);
- nl_socket_free(sock);
return 0;
}
diff --git a/tests/test-complex-HTB-with-hash-filters.c b/tests/test-complex-HTB-with-hash-filters.c
new file mode 100644
index 00000000..48cf5e32
--- /dev/null
+++ b/tests/test-complex-HTB-with-hash-filters.c
@@ -0,0 +1,761 @@
+/*
+ * test/test-complex-HTB-with-hash-filters.c Add HTB qdisc, HTB classes and creates some hash filters
+ *
+ * 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) 2011 Adrian Ban <adrian.ban@mantech.ro>
+ */
+
+#include <netlink/route/link.h>
+#include <netlink/route/tc.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/qdisc/htb.h>
+#include <netlink/route/qdisc/sfq.h>
+#include <netlink/route/cls/u32.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/class.h>
+#include <linux/if_ether.h>
+
+#include <netlink/attr.h>
+//#include "include/rtnl_u32.h"
+
+#include <stdio.h>
+#include <string.h>
+//#include "include/rtnl_u32_addon.h"
+
+#define TC_HANDLE(maj, min) (TC_H_MAJ((maj) << 16) | TC_H_MIN(min))
+
+/* some functions are copied from iproute-tc tool */
+int get_u32(__u32 *val, const char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+int get_u32_handle(__u32 *handle, const char *str)
+{
+ __u32 htid=0, hash=0, nodeid=0;
+ char *tmp = strchr(str, ':');
+
+ if (tmp == NULL) {
+ if (memcmp("0x", str, 2) == 0)
+ return get_u32(handle, str, 16);
+ return -1;
+ }
+ htid = strtoul(str, &tmp, 16);
+ if (tmp == str && *str != ':' && *str != 0)
+ return -1;
+ if (htid>=0x1000)
+ return -1;
+ if (*tmp) {
+ str = tmp+1;
+ hash = strtoul(str, &tmp, 16);
+ if (tmp == str && *str != ':' && *str != 0)
+ return -1;
+ if (hash>=0x100)
+ return -1;
+ if (*tmp) {
+ str = tmp+1;
+ nodeid = strtoul(str, &tmp, 16);
+ if (tmp == str && *str != 0)
+ return -1;
+ if (nodeid>=0x1000)
+ return -1;
+ }
+ }
+ *handle = (htid<<20)|(hash<<12)|nodeid;
+ return 0;
+}
+
+uint32_t get_u32_parse_handle(const char *cHandle)
+{
+ uint32_t handle=0;
+
+ if(get_u32_handle(&handle, cHandle)) {
+ printf ("Illegal \"ht\"\n");
+ return -1;
+ }
+
+ if (handle && TC_U32_NODE(handle)) {
+ printf("\"link\" must be a hash table.\n");
+ return -1;
+ }
+ return handle;
+}
+
+int get_tc_classid(__u32 *h, const char *str)
+{
+ __u32 maj, min;
+ char *p;
+
+ maj = TC_H_ROOT;
+ if (strcmp(str, "root") == 0)
+ goto ok;
+ maj = TC_H_UNSPEC;
+ if (strcmp(str, "none") == 0)
+ goto ok;
+ maj = strtoul(str, &p, 16);
+ if (p == str) {
+ maj = 0;
+ if (*p != ':')
+ return -1;
+ }
+ if (*p == ':') {
+ if (maj >= (1<<16))
+ return -1;
+ maj <<= 16;
+ str = p+1;
+ min = strtoul(str, &p, 16);
+ if (*p != 0)
+ return -1;
+ if (min >= (1<<16))
+ return -1;
+ maj |= min;
+ } else if (*p != 0)
+ return -1;
+
+ok:
+ *h = maj;
+ return 0;
+}
+
+/*
+ * Function that adds a new filter and attach it to a hash table
+ *
+ */
+int u32_add_filter_on_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio,
+ uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask,
+ uint32_t htid, uint32_t classid
+)
+{
+ struct rtnl_cls *cls;
+ int err;
+
+ //printf("Key Val : 0x%x\n", keyval);
+ //printf("Key Mask : 0x%x\n", keymask);
+
+ cls=rtnl_cls_alloc();
+ if (!(cls)) {
+ printf("Can not allocate classifier\n");
+ nl_socket_free(sock);
+ exit(1);
+ }
+
+ rtnl_tc_set_link(TC_CAST(cls), rtnlLink);
+
+ if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) {
+ printf("Can not set classifier as u32\n");
+ return 1;
+ }
+
+ rtnl_cls_set_prio(cls, prio);
+ rtnl_cls_set_protocol(cls, ETH_P_IP);
+
+ rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0));
+
+ rtnl_u32_set_hashtable(cls, htid);
+
+ rtnl_u32_add_key_uint32(cls, keyval, keymask, keyoff, keyoffmask); /* 10.0.0.0/8 */
+
+ rtnl_u32_set_classid(cls, classid);
+
+ rtnl_u32_set_cls_terminal(cls);
+
+ if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) {
+ printf("Can not add classifier: %s\n", nl_geterror(err));
+ return -1;
+ }
+ rtnl_cls_put(cls);
+ return 0;
+
+}
+
+/*
+ * Function that adds a new filter and attach it to a hash table
+ * and set next hash table link with hash mask
+ *
+ */
+int u32_add_filter_on_ht_with_hashmask(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio,
+ uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask,
+ uint32_t htid, uint32_t htlink, uint32_t hmask, uint32_t hoffset
+)
+{
+ struct rtnl_cls *cls;
+ int err;
+
+ //printf("Key Val : 0x%x\n", keyval);
+ //printf("Key Mask : 0x%x\n", keymask);
+
+ cls=rtnl_cls_alloc();
+ if (!(cls)) {
+ printf("Can not allocate classifier\n");
+ nl_socket_free(sock);
+ exit(1);
+ }
+
+ rtnl_tc_set_link(TC_CAST(cls), rtnlLink);
+
+ if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) {
+ printf("Can not set classifier as u32\n");
+ return 1;
+ }
+
+ rtnl_cls_set_prio(cls, prio);
+ rtnl_cls_set_protocol(cls, ETH_P_IP);
+
+ rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0));
+
+ if (htid)
+ rtnl_u32_set_hashtable(cls, htid);
+
+ rtnl_u32_add_key_uint32(cls, keyval, keymask, keyoff, keyoffmask);
+
+ rtnl_u32_set_hashmask(cls, hmask, hoffset);
+
+ rtnl_u32_set_link(cls, htlink);
+
+
+ if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) {
+ printf("Can not add classifier: %s\n", nl_geterror(err));
+ return -1;
+ }
+ rtnl_cls_put(cls);
+ return 0;
+}
+
+/*
+ * function that creates a new hash table
+ */
+int u32_add_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, uint32_t htid, uint32_t divisor)
+{
+
+ int err;
+ struct rtnl_cls *cls;
+
+ cls=rtnl_cls_alloc();
+ if (!(cls)) {
+ printf("Can not allocate classifier\n");
+ nl_socket_free(sock);
+ exit(1);
+ }
+
+ rtnl_tc_set_link(TC_CAST(cls), rtnlLink);
+
+ if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) {
+ printf("Can not set classifier as u32\n");
+ return 1;
+ }
+
+ rtnl_cls_set_prio(cls, prio);
+ rtnl_cls_set_protocol(cls, ETH_P_IP);
+ rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0));
+
+ rtnl_u32_set_handle(cls, htid, 0x0, 0x0);
+ //printf("htid: 0x%X\n", htid);
+ rtnl_u32_set_divisor(cls, divisor);
+
+ if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) {
+ printf("Can not add classifier: %s\n", nl_geterror(err));
+ return -1;
+ }
+ rtnl_cls_put(cls);
+ return 0;
+}
+
+/*
+ * function that adds a new HTB qdisc and set the default class for unclassified traffic
+ */
+int qdisc_add_HTB(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t defaultClass)
+{
+
+ struct rtnl_qdisc *qdisc;
+ int err;
+
+ /* Allocation of a qdisc object */
+ if (!(qdisc = rtnl_qdisc_alloc())) {
+ printf("Can not allocate Qdisc\n");
+ return -1;
+ }
+
+ //rtnl_tc_set_ifindex(TC_CAST(qdisc), master_index);
+ rtnl_tc_set_link(TC_CAST(qdisc), rtnlLink);
+ rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT);
+
+ //delete the qdisc
+ //printf("Delete current qdisc\n");
+ rtnl_qdisc_delete(sock, qdisc);
+ //rtnl_qdisc_put(qdisc);
+
+ //add a HTB qdisc
+ //printf("Add a new HTB qdisc\n");
+ rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(1,0));
+
+ if ((err = rtnl_tc_set_kind(TC_CAST(qdisc), "htb"))) {
+ printf("Can not allocate HTB\n");
+ return -1;
+ }
+
+ /* Set default class for unclassified traffic */
+ //printf("Set default class for unclassified traffic\n");
+ rtnl_htb_set_defcls(qdisc, TC_HANDLE(1, defaultClass));
+ rtnl_htb_set_rate2quantum(qdisc, 1);
+
+ /* Submit request to kernel and wait for response */
+ if ((err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE))) {
+ printf("Can not allocate HTB Qdisc\n");
+ return -1;
+ }
+
+ /* Return the qdisc object to free memory resources */
+ rtnl_qdisc_put(qdisc);
+
+ return 0;
+}
+
+/*
+ * function that adds a new HTB class and set its parameters
+ */
+int class_add_HTB(struct nl_sock *sock, struct rtnl_link *rtnlLink,
+ uint32_t parentMaj, uint32_t parentMin,
+ uint32_t childMaj, uint32_t childMin,
+ uint64_t rate, uint64_t ceil,
+ uint32_t burst, uint32_t cburst,
+ uint32_t prio
+)
+{
+ int err;
+ struct rtnl_class *class;
+ //struct rtnl_class *class = (struct rtnl_class *) tc;
+
+ //create a HTB class
+ //class = (struct rtnl_class *)rtnl_class_alloc();
+ if (!(class = rtnl_class_alloc())) {
+ printf("Can not allocate class object\n");
+ return 1;
+ }
+ //
+ rtnl_tc_set_link(TC_CAST(class), rtnlLink);
+ //add a HTB qdisc
+ //printf("Add a new HTB class with 0x%X:0x%X on parent 0x%X:0x%X\n", childMaj, childMin, parentMaj, parentMin);
+ rtnl_tc_set_parent(TC_CAST(class), TC_HANDLE(parentMaj, parentMin));
+ rtnl_tc_set_handle(TC_CAST(class), TC_HANDLE(childMaj, childMin));
+
+ if ((err = rtnl_tc_set_kind(TC_CAST(class), "htb"))) {
+ printf("Can not set HTB to class\n");
+ return 1;
+ }
+
+ //printf("set HTB class prio to %u\n", prio);
+ rtnl_htb_set_prio((struct rtnl_class *)class, prio);
+
+ if (rate) {
+ //rate=rate/8;
+ rtnl_htb_set_rate(class, rate);
+ }
+ if (ceil) {
+ //ceil=ceil/8;
+ rtnl_htb_set_ceil(class, ceil);
+ }
+
+ if (burst) {
+ //printf ("Class HTB: set rate burst: %u\n", burst);
+ rtnl_htb_set_rbuffer(class, burst);
+ }
+ if (cburst) {
+ //printf ("Class HTB: set rate cburst: %u\n", cburst);
+ rtnl_htb_set_cbuffer(class, cburst);
+ }
+ /* Submit request to kernel and wait for response */
+ if ((err = rtnl_class_add(sock, class, NLM_F_CREATE))) {
+ printf("Can not allocate HTB Qdisc\n");
+ return 1;
+ }
+ rtnl_class_put(class);
+ return 0;
+}
+
+/*
+ * function that adds a HTB root class and set its parameters
+ */
+int class_add_HTB_root(struct nl_sock *sock, struct rtnl_link *rtnlLink,
+ uint64_t rate, uint64_t ceil,
+ uint32_t burst, uint32_t cburst
+)
+{
+ int err;
+ struct rtnl_class *class;
+
+ //create a HTB class
+ class = (struct rtnl_class *)rtnl_class_alloc();
+ //class = rtnl_class_alloc();
+ if (!class) {
+ printf("Can not allocate class object\n");
+ return 1;
+ }
+ //
+ rtnl_tc_set_link(TC_CAST(class), rtnlLink);
+ rtnl_tc_set_parent(TC_CAST(class), TC_H_ROOT);
+ //add a HTB class
+ //printf("Add a new HTB ROOT class\n");
+ rtnl_tc_set_handle(TC_CAST(class), 1);
+
+ if ((err = rtnl_tc_set_kind(TC_CAST(class), "htb"))) {
+ printf("Can not set HTB to class\n");
+ return 1;
+ }
+
+ if (rate) {
+ //rate=rate/8;
+ rtnl_htb_set_rate(class, rate);
+ }
+ if (ceil) {
+ //ceil=ceil/8;
+ rtnl_htb_set_ceil(class, ceil);
+ }
+
+ if (burst) {
+ rtnl_htb_set_rbuffer(class, burst);
+ }
+ if (cburst) {
+ rtnl_htb_set_cbuffer(class, cburst);
+ }
+
+ /* Submit request to kernel and wait for response */
+ if ((err = rtnl_class_add(sock, class, NLM_F_CREATE))) {
+ printf("Can not allocate HTB Qdisc\n");
+ return 1;
+ }
+ rtnl_class_put(class);
+ return 0;
+}
+
+/*
+ * function that adds a new SFQ qdisc as a leaf for a HTB class
+ */
+int qdisc_add_SFQ_leaf(struct nl_sock *sock, struct rtnl_link *rtnlLink,
+ uint32_t parentMaj, uint32_t parentMin,
+ int quantum, int limit, int perturb
+)
+{
+ int err;
+ struct rtnl_qdisc *qdisc;
+
+ if (!(qdisc = rtnl_qdisc_alloc())) {
+ printf("Can not allocate qdisc object\n");
+ return 1;
+ }
+ rtnl_tc_set_link(TC_CAST(qdisc), rtnlLink);
+ rtnl_tc_set_parent(TC_CAST(qdisc), TC_HANDLE(parentMaj, parentMin));
+
+ rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(parentMin,0));
+
+ if ((err = rtnl_tc_set_kind(TC_CAST(qdisc), "sfq"))) {
+ printf("Can not set SQF class\n");
+ return 1;
+ }
+
+ if(quantum) {
+ rtnl_sfq_set_quantum(qdisc, quantum);
+ } else {
+ rtnl_sfq_set_quantum(qdisc, 16000); // tc default value
+ }
+ if(limit) {
+ rtnl_sfq_set_limit(qdisc, limit); // default is 127
+ }
+ if(perturb) {
+ rtnl_sfq_set_perturb(qdisc, perturb); // default never perturb the hash
+ }
+
+ /* Submit request to kernel and wait for response */
+ if ((err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE))) {
+ printf("Can not allocate SFQ qdisc\n");
+ return -1;
+ }
+
+ /* Return the qdisc object to free memory resources */
+ rtnl_qdisc_put(qdisc);
+ return 0;
+}
+
+
+
+
+int main() {
+
+ struct nl_sock *sock;
+ struct rtnl_link *link;
+
+ //struct rtnl_qdisc *qdisc;
+ //struct rtnl_class *class;
+ //struct rtnl_cls *cls;
+
+ uint32_t ht, htlink, htid, direction, classid;
+ //uint32_t hash, hashmask, nodeid, divisor, handle;
+ //struct rtnl_u32 *f_u32;
+ char chashlink[16]="";
+
+ //uint64_t drops, qlen;
+
+ //int master_index;
+ int err;
+
+ //uint64_t rate=0, ceil=0;
+
+ struct nl_cache *link_cache;
+
+ if (!(sock = nl_socket_alloc())) {
+ printf("Unable to allocate netlink socket\n");
+ exit(1);
+ }
+
+ if ((err = nl_connect(sock, NETLINK_ROUTE)) < 0 ) {
+ printf("Nu s-a putut conecta la NETLINK!\n");
+ nl_socket_free(sock);
+ exit(1);
+ }
+
+
+ if ((err = rtnl_link_alloc_cache(sock, AF_UNSPEC, &link_cache)) < 0) {
+ printf("Unable to allocate link cache: %s\n",
+ nl_geterror(err));
+ nl_socket_free(sock);
+ exit(1);
+ }
+
+ /* lookup interface index of eth0 */
+ if (!(link = rtnl_link_get_by_name(link_cache, "imq0"))) {
+ /* error */
+ printf("Interface not found\n");
+ nl_socket_free(sock);
+ exit(1);
+ }
+
+ err=qdisc_add_HTB(sock, link, 0xffff);
+ //drops = rtnl_tc_get_stat(TC_CAST(qdisc), RTNL_TC_DROPS);
+
+ //printf("Add ROOT HTB class\n");
+ err=class_add_HTB_root(sock, link, 12500000, 12500000, 25000, 25000);
+ err=class_add_HTB(sock, link, 1, 0, 1, 0xffff, 1250000, 12500000, 25000, 25000, 5);
+ err=qdisc_add_SFQ_leaf(sock, link, 1, 0xffff, 16000, 0, 10);
+ err=class_add_HTB(sock, link, 1, 1, 1, 0x5, 2000000, 2000000, 25000, 25000, 5);
+ err=qdisc_add_SFQ_leaf(sock, link, 1, 0x5, 16000, 0, 10);
+ err=class_add_HTB(sock, link, 1, 1, 1, 0x6, 1000000, 1000000, 25000, 25000, 5);
+ err=qdisc_add_SFQ_leaf(sock, link, 1, 0x6, 16000, 0, 10);
+ //err=class_add_HTB(sock, link, 1, 0, 1, 0x7, 1024000, 100000000, 5);
+ //err=class_add_HTB(sock, link, 1, 0, 1, 0x8, 2048000, 100000000, 5);
+ //err=class_add_HTB(sock, link, 1, 0, 1, 0x9, 4096000, 100000000, 5);
+ //err=class_add_HTB(sock, link, 1, 0, 1, 0xa, 8192000, 100000000, 5);
+
+ //printf("Add main hash table\n");
+
+ /* create u32 first hash filter table
+ *
+ */
+ /* formula calcul handle:
+ * uint32_t handle = (htid << 20) | (hash << 12) | nodeid;
+ */
+
+ /*
+ * Upper limit of number of hash tables: 4096 (0xFFF)
+ * Number of hashes in a table: 256 values (0xFF)
+ *
+ */
+
+ /* using 256 values for hash table
+ * each entry in hash table match a byte from IP address specified later by a hash key
+ */
+
+ uint32_t i;
+ for (i = 1; i <= 0xf; i++)
+ u32_add_ht(sock, link, 1, i, 256);
+
+ /*
+ * attach a u32 filter to the first hash
+ * that redirects all traffic and make a hash key
+ * from the fist byte of the IP address
+ *
+ */
+
+ //divisor=0x0; // unused here
+ //handle = 0x0; // unused here
+ //hash = 0x0; // unused here
+ //htid = 0x0; // unused here
+ //nodeid = 0x0; // unused here
+
+ // direction = 12 -> source IP
+ // direction = 16 -> destination IP
+ direction = 16;
+
+ /*
+ * which hash table will use
+ * in our case is hash table no 1 defined previous
+ *
+ * There are 2 posibilities to set the the hash table:
+ * 1. Using function get_u32_handle and sent a string in
+ * format 10: where 10 is number of the hash table
+ * 2. Create your own value in format: 0xa00000
+ *
+ */
+ strcpy(chashlink, "1:");
+ //printf("Hash Link: %s\n", chashlink);
+ //chashlink=malloc(sizeof(char) *
+ htlink = 0x0; // is used by get_u32_handle to return the correct value of hash table (link)
+
+ if(get_u32_handle(&htlink, chashlink)) {
+ printf ("Illegal \"link\"");
+ nl_socket_free(sock);
+ exit(1);
+ }
+ //printf ("hash link : 0x%X\n", htlink);
+ //printf ("hash link test : %u\n", (htlink && TC_U32_NODE(htlink)));
+
+ if (htlink && TC_U32_NODE(htlink)) {
+ printf("\"link\" must be a hash table.\n");
+ nl_socket_free(sock);
+ exit(1);
+ }
+ /* the hash mask will hit the hash table (link) no 1: in our case
+ */
+
+ /* set the hash key mask */
+ //hashmask = 0xFF000000UL; // the mask that is used to match the hash in specific table, in our case for example 1:a with mean the first byte which is 10 in hash table 1
+
+ /* Here we add a hash filter which match the first byte (see the hashmask value)
+ * of the source IP (offset 12 in the packet header)
+ * You can use also offset 16 to match the destination IP
+ */
+
+ /*
+ * Also we need a filter to match our rule
+ * This mean that we will put a 0.0.0.0/0 filter in our first rule
+ * that match the offset 12 (source IP)
+ * Also you can put offset 16 to match the destination IP
+ */
+
+ u32_add_filter_on_ht_with_hashmask(sock, link, 1,
+ 0x0, 0x0, direction, 0,
+ 0, htlink, 0xff000000, direction);
+
+ /*
+ * For each first byte that we need to match we will create a new hash table
+ * For example: you have those clases: 10.0.0.0/24 and 172.16.0.0/23
+ * For byte 10 and byte 172 will create a separate hash table that will match the second
+ * byte from each class.
+ *
+ */
+
+
+ // Create a new hash table with prio 1, id 2 and 256 entries
+// u32_CreateNewHashTable(sock, link, 1, 2, 256);
+ // Create a new hash table with prio 1, id 3 and 256 entries
+// u32_CreateNewHashTable(sock, link, 1, 3, 256);
+// u32_CreateNewHashTable(sock, link, 1, 4, 256);
+// u32_CreateNewHashTable(sock, link, 1, 5, 256);
+
+ /*
+ * Now we will create other filter under (ATENTION) our first hash table (link) 1:
+ * Previous rule redirects the trafic according the hash mask to hash table (link) no 1:
+ * Here we will match the hash tables from 1:0 to 1:ff. Under each hash table we will attach
+ * other rules that matches next byte from IP source/destination IP and we will repeat the
+ * previous steps.
+ *
+ */
+
+
+ // /8 check
+
+ // 10.0.0.0/8
+ ht=get_u32_parse_handle("1:a:");
+ htid = (ht&0xFFFFF000);
+ htlink=get_u32_parse_handle("2:");
+
+ u32_add_filter_on_ht_with_hashmask(sock, link, 1,
+ 0x0a000000, 0xff000000, direction, 0,
+ htid, htlink, 0x00ff0000, direction);
+
+ // 172.0.0.0/8
+ ht=get_u32_parse_handle("1:ac:");
+ htid = (ht&0xFFFFF000);
+ htlink=get_u32_parse_handle("3:");
+
+ u32_add_filter_on_ht_with_hashmask(sock, link, 1,
+ 0xac000000, 0xff000000, direction, 0,
+ htid, htlink, 0x00ff0000, direction);
+
+
+ // /16 check
+ // 10.0.0.0/16
+ ht=get_u32_parse_handle("2:0:");
+ htid = (ht&0xFFFFF000);
+ htlink=get_u32_parse_handle("4:");
+
+ u32_add_filter_on_ht_with_hashmask(sock, link, 1,
+ 0x0a000000, 0xffff0000, direction, 0,
+ htid, htlink, 0x0000ff00, direction);
+
+ // 172.17.0.0/16
+ ht=get_u32_parse_handle("3:11:");
+ htid = (ht&0xFFFFF000);
+ htlink=get_u32_parse_handle("5:");
+
+ u32_add_filter_on_ht_with_hashmask(sock, link, 1,
+ 0xac110000, 0xffff0000, direction, 0,
+ htid, htlink, 0x0000ff00, direction);
+
+ // /24 check
+ // 10.0.9.0/24
+ ht=get_u32_parse_handle("4:9:");
+ htid = (ht&0xFFFFF000);
+ htlink=get_u32_parse_handle("6:");
+
+ u32_add_filter_on_ht_with_hashmask(sock, link, 1,
+ 0x0a000900, 0xffffff00, direction, 0,
+ htid, htlink, 0x000000ff, direction);
+
+ // 172.17.2.0/16
+ ht=get_u32_parse_handle("5:2:");
+ htid = (ht&0xFFFFF000);
+ htlink=get_u32_parse_handle("7:");
+
+ u32_add_filter_on_ht_with_hashmask(sock, link, 1,
+ 0xac110200, 0xffffff00, direction, 0,
+ htid, htlink, 0x000000ff, direction);
+
+
+ // final filters
+ // 10.0.9.20
+ ht=get_u32_parse_handle("6:14:");
+ htid = (ht&0xFFFFF000);
+
+ err = get_tc_classid(&classid, "1:5");
+
+ u32_add_filter_on_ht(sock, link, 1,
+ 0x0a000914, 0xffffffff, direction, 0,
+ htid, classid);
+
+ // 172.17.2.120
+ ht=get_u32_parse_handle("7:78:");
+ htid = (ht&0xFFFFF000);
+
+ err = get_tc_classid(&classid, "1:6");
+
+ u32_add_filter_on_ht(sock, link, 1,
+ 0xac110278, 0xffffffff, direction, 0,
+ htid, classid);
+
+
+
+ nl_socket_free(sock);
+ return 0;
+}
diff --git a/tests/test-create-bond.c b/tests/test-create-bond.c
new file mode 100644
index 00000000..11bc5b09
--- /dev/null
+++ b/tests/test-create-bond.c
@@ -0,0 +1,29 @@
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/bonding.h>
+
+int main(int argc, char *argv[])
+{
+ struct rtnl_link *link;
+ struct nl_sock *sk;
+ int err;
+
+ sk = nl_socket_alloc();
+ if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+ nl_perror(err, "Unable to connect socket");
+ return err;
+ }
+
+ link = rtnl_link_bond_alloc();
+ rtnl_link_set_name(link, "my_bond");
+
+ if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
+ nl_perror(err, "Unable to add link");
+ return err;
+ }
+
+ rtnl_link_put(link);
+ nl_close(sk);
+
+ return 0;
+}
diff --git a/tests/test-create-bridge.c b/tests/test-create-bridge.c
new file mode 100644
index 00000000..7202cd7e
--- /dev/null
+++ b/tests/test-create-bridge.c
@@ -0,0 +1,80 @@
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/bridge.h>
+
+#define TEST_BRIDGE_NAME "testbridge"
+#define TEST_INTERFACE_NAME "testtap1"
+
+int create_bridge(struct nl_sock *sk, struct nl_cache *link_cache, const char *name) {
+ struct rtnl_link *link;
+ int err;
+
+ link = rtnl_link_alloc();
+ if ((err = rtnl_link_set_type(link, "bridge")) < 0) {
+ rtnl_link_put(link);
+ return err;
+ }
+ rtnl_link_set_name(link, name);
+
+ if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
+ return err;
+ }
+ rtnl_link_put(link);
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct rtnl_link *link;
+ struct nl_cache *link_cache;
+ struct nl_sock *sk;
+ int err;
+
+ sk = nl_socket_alloc();
+ if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+ nl_perror(err, "Unable to connect socket");
+ return err;
+ }
+
+ if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) {
+ nl_perror(err, "Unable to allocate cache");
+ return err;
+ }
+
+ if ((err = create_bridge(sk, link_cache, TEST_BRIDGE_NAME)) < 0) {
+ nl_perror(err, "Unable to allocate testbridge");
+ return err;
+ }
+
+ nl_cache_refill(sk, link_cache);
+
+ link = rtnl_link_get_by_name(link_cache, TEST_BRIDGE_NAME);
+ struct rtnl_link *ltap = rtnl_link_get_by_name(link_cache, TEST_INTERFACE_NAME);
+ if (!ltap) {
+ fprintf(stderr, "You should create a tap interface before lunch this test (# tunctl -t %s)\n", TEST_INTERFACE_NAME);
+ return -1;
+ }
+
+ if ((err = rtnl_link_enslave(sk, link, ltap)) < 0) {
+ nl_perror(err, "Unable to enslave interface to his bridge\n");
+ return err;
+ }
+
+ if(rtnl_link_is_bridge(link) == 0) {
+ fprintf(stderr, "Link is not a bridge\n");
+ return -2;
+ }
+ if(rtnl_link_get_master(ltap) <= 0) {
+ fprintf(stderr, "Interface is not attached to a bridge\n");
+ return -3;
+ }
+
+ rtnl_link_put(ltap);
+ rtnl_link_put(link);
+
+ nl_cache_free(link_cache);
+ nl_socket_free(sk);
+
+ return 0;
+}
diff --git a/tests/test-create-ip6tnl.c b/tests/test-create-ip6tnl.c
new file mode 100644
index 00000000..b36ab3d3
--- /dev/null
+++ b/tests/test-create-ip6tnl.c
@@ -0,0 +1,55 @@
+#include <netlink/route/link/ip6tnl.h>
+#include <netlink-private/netlink.h>
+
+int main(int argc, char *argv[])
+{
+ struct nl_cache *link_cache;
+ struct rtnl_link *link;
+ struct in6_addr addr;
+ struct nl_sock *sk;
+ int err, if_index;
+
+ sk = nl_socket_alloc();
+ if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+ nl_perror(err, "Unable to connect socket");
+ return err;
+ }
+
+ err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache);
+ if ( err < 0) {
+ nl_perror(err, "Unable to allocate cache");
+ return err;
+ }
+
+ if_index = rtnl_link_name2i(link_cache, "ens33");
+ if (!if_index) {
+ fprintf(stderr, "Unable to lookup ens33");
+ return -1;
+ }
+
+ link = rtnl_link_ip6_tnl_alloc();
+ if(!link) {
+ nl_perror(err, "Unable to allocate link");
+ return -1;
+
+ }
+ rtnl_link_set_name(link, "ip6tnl-tun");
+ rtnl_link_ip6_tnl_set_link(link, if_index);
+
+ inet_pton(AF_INET6, "2607:f0d0:1002:51::4", &addr);
+ rtnl_link_ip6_tnl_set_local(link, &addr);
+
+ inet_pton(AF_INET6, "2607:f0d0:1002:52::5", &addr);
+ rtnl_link_ip6_tnl_set_remote(link, &addr);
+
+ err = rtnl_link_add(sk, link, NLM_F_CREATE);
+ if (err < 0) {
+ nl_perror(err, "Unable to add link");
+ return err;
+ }
+
+ rtnl_link_put(link);
+ nl_close(sk);
+
+ return 0;
+}
diff --git a/tests/test-create-ipgre.c b/tests/test-create-ipgre.c
new file mode 100644
index 00000000..66ea6da8
--- /dev/null
+++ b/tests/test-create-ipgre.c
@@ -0,0 +1,56 @@
+#include <netlink/route/link/ipgre.h>
+#include <netlink-private/netlink.h>
+
+int main(int argc, char *argv[])
+{
+ struct nl_cache *link_cache;
+ struct rtnl_link *link;
+ struct in_addr addr;
+ struct nl_sock *sk;
+ int err, if_index;
+
+ sk = nl_socket_alloc();
+ if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+ nl_perror(err, "Unable to connect socket");
+ return err;
+ }
+
+ err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache);
+ if ( err < 0) {
+ nl_perror(err, "Unable to allocate cache");
+ return err;
+ }
+
+ if_index = rtnl_link_name2i(link_cache, "eno16777736");
+ if (!if_index) {
+ fprintf(stderr, "Unable to lookup eno16777736");
+ return -1;
+ }
+
+ link = rtnl_link_ipgre_alloc();
+ if(!link) {
+ nl_perror(err, "Unable to allocate link");
+ return -1;
+
+ }
+ rtnl_link_set_name(link, "ipgre-tun");
+ rtnl_link_ipgre_set_link(link, if_index);
+
+ inet_pton(AF_INET, "192.168.254.12", &addr.s_addr);
+ rtnl_link_ipgre_set_local(link, addr.s_addr);
+
+ inet_pton(AF_INET, "192.168.254.13", &addr.s_addr);
+ rtnl_link_ipgre_set_remote(link, addr.s_addr);
+
+ rtnl_link_ipgre_set_ttl(link, 64);
+ err = rtnl_link_add(sk, link, NLM_F_CREATE);
+ if (err < 0) {
+ nl_perror(err, "Unable to add link");
+ return err;
+ }
+
+ rtnl_link_put(link);
+ nl_close(sk);
+
+ return 0;
+}
diff --git a/tests/test-create-ipip.c b/tests/test-create-ipip.c
new file mode 100644
index 00000000..44b9b2c3
--- /dev/null
+++ b/tests/test-create-ipip.c
@@ -0,0 +1,56 @@
+#include <netlink/route/link/ipip.h>
+#include <netlink-private/netlink.h>
+
+int main(int argc, char *argv[])
+{
+ struct nl_cache *link_cache;
+ struct rtnl_link *link;
+ struct in_addr addr;
+ struct nl_sock *sk;
+ int err, if_index;
+
+ sk = nl_socket_alloc();
+ if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+ nl_perror(err, "Unable to connect socket");
+ return err;
+ }
+
+ err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache);
+ if ( err < 0) {
+ nl_perror(err, "Unable to allocate cache");
+ return err;
+ }
+
+ if_index = rtnl_link_name2i(link_cache, "eno16777736");
+ if (!if_index) {
+ fprintf(stderr, "Unable to lookup eno16777736");
+ return -1;
+ }
+
+ link = rtnl_link_ipip_alloc();
+ if(!link) {
+ nl_perror(err, "Unable to allocate link");
+ return -1;
+ }
+
+ rtnl_link_set_name(link, "ipip-tun");
+ rtnl_link_ipip_set_link(link, if_index);
+
+ inet_pton(AF_INET, "192.168.254.12", &addr.s_addr);
+ rtnl_link_ipip_set_local(link, addr.s_addr);
+
+ inet_pton(AF_INET, "192.168.254.13", &addr.s_addr);
+ rtnl_link_ipip_set_remote(link, addr.s_addr);
+
+ rtnl_link_ipip_set_ttl(link, 64);
+ err = rtnl_link_add(sk, link, NLM_F_CREATE);
+ if (err < 0) {
+ nl_perror(err, "Unable to add link");
+ return err;
+ }
+
+ rtnl_link_put(link);
+ nl_close(sk);
+
+ return 0;
+}
diff --git a/tests/test-create-ipvti.c b/tests/test-create-ipvti.c
new file mode 100644
index 00000000..6cb92d72
--- /dev/null
+++ b/tests/test-create-ipvti.c
@@ -0,0 +1,55 @@
+#include <netlink/route/link/ipvti.h>
+#include <netlink-private/netlink.h>
+
+int main(int argc, char *argv[])
+{
+ struct nl_cache *link_cache;
+ struct rtnl_link *link;
+ struct in_addr addr;
+ struct nl_sock *sk;
+ int err, if_index;
+
+ sk = nl_socket_alloc();
+ if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+ nl_perror(err, "Unable to connect socket");
+ return err;
+ }
+
+ err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache);
+ if ( err < 0) {
+ nl_perror(err, "Unable to allocate cache");
+ return err;
+ }
+
+ if_index = rtnl_link_name2i(link_cache, "ens33");
+ if (!if_index) {
+ fprintf(stderr, "Unable to lookup ens33");
+ return -1;
+ }
+
+ link = rtnl_link_ipvti_alloc();
+ if(!link) {
+ nl_perror(err, "Unable to allocate link");
+ return -1;
+
+ }
+ rtnl_link_set_name(link, "ipvti-tun");
+ rtnl_link_ipvti_set_link(link, if_index);
+
+ inet_pton(AF_INET, "192.168.254.12", &addr.s_addr);
+ rtnl_link_ipvti_set_local(link, addr.s_addr);
+
+ inet_pton(AF_INET, "192.168.254.13", &addr.s_addr);
+ rtnl_link_ipvti_set_remote(link, addr.s_addr);
+
+ err = rtnl_link_add(sk, link, NLM_F_CREATE);
+ if (err < 0) {
+ nl_perror(err, "Unable to add link");
+ return err;
+ }
+
+ rtnl_link_put(link);
+ nl_close(sk);
+
+ return 0;
+}
diff --git a/tests/test-create-macvlan.c b/tests/test-create-macvlan.c
new file mode 100644
index 00000000..64779237
--- /dev/null
+++ b/tests/test-create-macvlan.c
@@ -0,0 +1,48 @@
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/macvlan.h>
+
+int main(int argc, char *argv[])
+{
+ struct rtnl_link *link;
+ struct nl_cache *link_cache;
+ struct nl_sock *sk;
+ struct nl_addr* addr;
+ int err, master_index;
+
+ sk = nl_socket_alloc();
+ if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+ nl_perror(err, "Unable to connect socket");
+ return err;
+ }
+
+ if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) {
+ nl_perror(err, "Unable to allocate cache");
+ return err;
+ }
+
+ if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) {
+ fprintf(stderr, "Unable to lookup eth0");
+ return -1;
+ }
+
+ link = rtnl_link_macvlan_alloc();
+
+ rtnl_link_set_link(link, master_index);
+
+ addr = nl_addr_build(AF_LLC, ether_aton("00:11:22:33:44:55"), ETH_ALEN);
+ rtnl_link_set_addr(link, addr);
+ nl_addr_put(addr);
+
+ rtnl_link_macvlan_set_mode(link, rtnl_link_macvlan_str2mode("bridge"));
+
+ if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
+ nl_perror(err, "Unable to add link");
+ return err;
+ }
+
+ rtnl_link_put(link);
+ nl_close(sk);
+
+ return 0;
+}
diff --git a/tests/test-create-sit.c b/tests/test-create-sit.c
new file mode 100644
index 00000000..d33e4963
--- /dev/null
+++ b/tests/test-create-sit.c
@@ -0,0 +1,56 @@
+#include <netlink/route/link/sit.h>
+#include <netlink-private/netlink.h>
+
+int main(int argc, char *argv[])
+{
+ struct nl_cache *link_cache;
+ struct rtnl_link *link;
+ struct in_addr addr;
+ struct nl_sock *sk;
+ int err, if_index;
+
+ sk = nl_socket_alloc();
+ if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+ nl_perror(err, "Unable to connect socket");
+ return err;
+ }
+
+ err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache);
+ if ( err < 0) {
+ nl_perror(err, "Unable to allocate cache");
+ return err;
+ }
+
+ if_index = rtnl_link_name2i(link_cache, "eno16777736");
+ if (!if_index) {
+ fprintf(stderr, "Unable to lookup eno16777736");
+ return -1;
+ }
+
+ link = rtnl_link_sit_alloc();
+ if(!link) {
+ nl_perror(err, "Unable to allocate link");
+ return -1;
+
+ }
+ rtnl_link_set_name(link, "sit-tun");
+ rtnl_link_sit_set_link(link, if_index);
+
+ inet_pton(AF_INET, "192.168.254.12", &addr.s_addr);
+ rtnl_link_sit_set_local(link, addr.s_addr);
+
+ inet_pton(AF_INET, "192.168.254.13", &addr.s_addr);
+ rtnl_link_sit_set_remote(link, addr.s_addr);
+
+ rtnl_link_sit_set_ttl(link, 64);
+ err = rtnl_link_add(sk, link, NLM_F_CREATE);
+ if (err < 0) {
+ nl_perror(err, "Unable to add link");
+ return err;
+ }
+
+ rtnl_link_put(link);
+ nl_close(sk);
+
+ return 0;
+}
diff --git a/tests/test-create-veth.c b/tests/test-create-veth.c
new file mode 100644
index 00000000..db5ab8b1
--- /dev/null
+++ b/tests/test-create-veth.c
@@ -0,0 +1,42 @@
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/veth.h>
+
+int main(int argc, char *argv[])
+{
+ struct rtnl_link *link;
+ struct nl_sock *sk;
+ int err;
+ struct rtnl_link *peer;
+
+ sk = nl_socket_alloc();
+ if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+ nl_perror(err, "Unable to connect socket");
+ return err;
+ }
+
+#if 0
+ rtnl_link_veth_add(sk, "veth2", "veth3", getpid());
+#else
+ link = rtnl_link_veth_alloc();
+ if (!link) {
+ nl_perror(err, "Unable to alloc link");
+ return err;
+ }
+
+ rtnl_link_set_name(link, "veth8");
+ peer = rtnl_link_veth_get_peer(link);
+ rtnl_link_set_name(peer, "veth9");
+
+ if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
+ nl_perror(err, "Unable to add link");
+ return err;
+ }
+ printf("peer is %s\n", rtnl_link_get_name(peer));
+ rtnl_link_put(peer);
+ rtnl_link_put(link);
+#endif
+ nl_close(sk);
+
+ return 0;
+}
diff --git a/tests/test-create-vlan.c b/tests/test-create-vlan.c
new file mode 100644
index 00000000..64e478f4
--- /dev/null
+++ b/tests/test-create-vlan.c
@@ -0,0 +1,43 @@
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/vlan.h>
+
+int main(int argc, char *argv[])
+{
+ struct rtnl_link *link;
+ struct nl_cache *link_cache;
+ struct nl_sock *sk;
+ int err, master_index;
+
+ sk = nl_socket_alloc();
+ if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+ nl_perror(err, "Unable to connect socket");
+ return err;
+ }
+
+ if ((err = rtnl_link_alloc_cache(sk, AF_UNSPEC, &link_cache)) < 0) {
+ nl_perror(err, "Unable to allocate cache");
+ return err;
+ }
+
+ if (!(master_index = rtnl_link_name2i(link_cache, "eth0"))) {
+ fprintf(stderr, "Unable to lookup eth0");
+ return -1;
+ }
+
+ link = rtnl_link_vlan_alloc();
+
+ rtnl_link_set_link(link, master_index);
+
+ rtnl_link_vlan_set_id(link, 10);
+
+ if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
+ nl_perror(err, "Unable to add link");
+ return err;
+ }
+
+ rtnl_link_put(link);
+ nl_close(sk);
+
+ return 0;
+}
diff --git a/tests/test-create-vxlan.c b/tests/test-create-vxlan.c
new file mode 100644
index 00000000..98a5103c
--- /dev/null
+++ b/tests/test-create-vxlan.c
@@ -0,0 +1,47 @@
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/vxlan.h>
+
+int main(int argc, char *argv[])
+{
+ struct rtnl_link *link;
+ struct nl_addr *addr;
+ struct nl_sock *sk;
+ int err;
+
+ sk = nl_socket_alloc();
+ if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+ nl_perror(err, "Unable to connect socket");
+ return err;
+ }
+
+ link = rtnl_link_vxlan_alloc();
+
+ rtnl_link_set_name(link, "vxlan128");
+
+ if ((err = rtnl_link_vxlan_set_id(link, 128)) < 0) {
+ nl_perror(err, "Unable to set VXLAN network identifier");
+ return err;
+ }
+
+ if ((err = nl_addr_parse("239.0.0.1", AF_INET, &addr)) < 0) {
+ nl_perror(err, "Unable to parse IP address");
+ return err;
+ }
+
+ if ((err = rtnl_link_vxlan_set_group(link, addr)) < 0) {
+ nl_perror(err, "Unable to set multicast IP address");
+ return err;
+ }
+ nl_addr_put(addr);
+
+ if ((err = rtnl_link_add(sk, link, NLM_F_CREATE)) < 0) {
+ nl_perror(err, "Unable to add link");
+ return err;
+ }
+
+ rtnl_link_put(link);
+ nl_close(sk);
+
+ return 0;
+}
diff --git a/tests/test-delete-link.c b/tests/test-delete-link.c
new file mode 100644
index 00000000..9cf1034e
--- /dev/null
+++ b/tests/test-delete-link.c
@@ -0,0 +1,28 @@
+#include <netlink/netlink.h>
+#include <netlink/route/link.h>
+
+int main(int argc, char *argv[])
+{
+ struct rtnl_link *link;
+ struct nl_sock *sk;
+ int err;
+
+ sk = nl_socket_alloc();
+ if ((err = nl_connect(sk, NETLINK_ROUTE)) < 0) {
+ nl_perror(err, "Unable to connect socket");
+ return err;
+ }
+
+ link = rtnl_link_alloc();
+ rtnl_link_set_name(link, "my_bond");
+
+ if ((err = rtnl_link_delete(sk, link)) < 0) {
+ nl_perror(err, "Unable to delete link");
+ return err;
+ }
+
+ rtnl_link_put(link);
+ nl_close(sk);
+
+ return 0;
+}
diff --git a/tests/test-genl.c b/tests/test-genl.c
index 8bf60c5c..74aea106 100644
--- a/tests/test-genl.c
+++ b/tests/test-genl.c
@@ -1,4 +1,72 @@
-#include "../src/utils.h"
+#include <netlink/cli/utils.h>
+#include <linux/taskstats.h>
+
+static struct nla_policy attr_policy[TASKSTATS_TYPE_MAX+1] = {
+ [TASKSTATS_TYPE_PID] = { .type = NLA_U32 },
+ [TASKSTATS_TYPE_TGID] = { .type = NLA_U32 },
+ [TASKSTATS_TYPE_STATS] = { .minlen = sizeof(struct taskstats) },
+ [TASKSTATS_TYPE_AGGR_PID] = { .type = NLA_NESTED },
+ [TASKSTATS_TYPE_AGGR_TGID] = { .type = NLA_NESTED },
+};
+
+
+static int parse_cmd_new(struct nl_cache_ops *unused, struct genl_cmd *cmd,
+ struct genl_info *info, void *arg)
+{
+ struct nlattr *attrs[TASKSTATS_TYPE_MAX+1];
+ struct nlattr *nested;
+ int err;
+
+ if (info->attrs[TASKSTATS_TYPE_AGGR_PID])
+ nested = info->attrs[TASKSTATS_TYPE_AGGR_PID];
+ else if (info->attrs[TASKSTATS_TYPE_AGGR_TGID])
+ nested = info->attrs[TASKSTATS_TYPE_AGGR_TGID];
+ else {
+ fprintf(stderr, "Invalid taskstats message: Unable to find "
+ "nested attribute/\n");
+ return NL_SKIP;
+ }
+
+ err = nla_parse_nested(attrs, TASKSTATS_TYPE_MAX, nested, attr_policy);
+ if (err < 0) {
+ nl_perror(err, "Error while parsing generic netlink message");
+ return err;
+ }
+
+
+ if (attrs[TASKSTATS_TYPE_STATS]) {
+ struct taskstats *stats = nla_data(attrs[TASKSTATS_TYPE_STATS]);
+
+ printf("%s pid %u uid %u gid %u parent %u\n",
+ stats->ac_comm, stats->ac_pid, stats->ac_uid,
+ stats->ac_gid, stats->ac_ppid);
+ }
+
+ return 0;
+}
+
+static int parse_cb(struct nl_msg *msg, void *arg)
+{
+ return genl_handle_msg(msg, NULL);
+}
+
+static struct genl_cmd cmds[] = {
+ {
+ .c_id = TASKSTATS_CMD_NEW,
+ .c_name = "taskstats_new()",
+ .c_maxattr = TASKSTATS_TYPE_MAX,
+ .c_attr_policy = attr_policy,
+ .c_msg_parser = &parse_cmd_new,
+ },
+};
+
+#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
+
+static struct genl_ops ops = {
+ .o_name = TASKSTATS_GENL_NAME,
+ .o_cmds = cmds,
+ .o_ncmds = ARRAY_SIZE(cmds),
+};
int main(int argc, char *argv[])
{
@@ -7,28 +75,42 @@ int main(int argc, char *argv[])
void *hdr;
int err;
- sock = nlt_alloc_socket();
- nlt_connect(sock, NETLINK_GENERIC);
+ sock = nl_cli_alloc_socket();
+ nl_cli_connect(sock, NETLINK_GENERIC);
+
+ if ((err = genl_register_family(&ops)) < 0)
+ nl_cli_fatal(err, "Unable to register Generic Netlink family");
+
+ if ((err = genl_ops_resolve(sock, &ops)) < 0)
+ nl_cli_fatal(err, "Unable to resolve family name");
+
+ if (genl_ctrl_resolve(sock, "nlctrl") != GENL_ID_CTRL)
+ nl_cli_fatal(NLE_INVAL, "Resolving of \"nlctrl\" failed");
msg = nlmsg_alloc();
if (msg == NULL)
- fatal(NLE_NOMEM, "Unable to allocate netlink message");
+ nl_cli_fatal(NLE_NOMEM, "Unable to allocate netlink message");
- hdr = genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, GENL_ID_CTRL,
- 0, 0, CTRL_CMD_GETFAMILY, 1);
+ hdr = genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ops.o_id,
+ 0, 0, TASKSTATS_CMD_GET, TASKSTATS_GENL_VERSION);
if (hdr == NULL)
- fatal(ENOMEM, "Unable to write genl header");
+ nl_cli_fatal(ENOMEM, "Unable to write genl header");
- if ((err = nla_put_u32(msg, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL)) < 0)
- fatal(err, "Unable to add attribute: %s", nl_geterror(err));
+ if ((err = nla_put_u32(msg, TASKSTATS_CMD_ATTR_PID, 1)) < 0)
+ nl_cli_fatal(err, "Unable to add attribute: %s", nl_geterror(err));
if ((err = nl_send_auto_complete(sock, msg)) < 0)
- fatal(err, "Unable to send message: %s", nl_geterror(err));
+ nl_cli_fatal(err, "Unable to send message: %s", nl_geterror(err));
+
+ nlmsg_free(msg);
+
+ if ((err = nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM,
+ parse_cb, NULL)) < 0)
+ nl_cli_fatal(err, "Unable to modify valid message callback");
if ((err = nl_recvmsgs_default(sock)) < 0)
- fatal(err, "Unable to receive message: %s", nl_geterror(err));
+ nl_cli_fatal(err, "Unable to receive message: %s", nl_geterror(err));
- nlmsg_free(msg);
nl_close(sock);
nl_socket_free(sock);
diff --git a/tests/test-nf-cache-mngr.c b/tests/test-nf-cache-mngr.c
index 05485bf6..b4f30223 100644
--- a/tests/test-nf-cache-mngr.c
+++ b/tests/test-nf-cache-mngr.c
@@ -1,13 +1,13 @@
-#include "../src/utils.h"
+#include <netlink/cli/utils.h>
static void change_cb(struct nl_cache *cache, struct nl_object *obj,
- int action)
+ int action, void *data)
{
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);
+ nl_addr_parse("194.88.212.233", AF_INET, &hack);
if (!nl_addr_cmp(hack, nfnl_ct_get_src(ct, 1)) ||
!nl_addr_cmp(hack, nfnl_ct_get_dst(ct, 1))) {
@@ -26,25 +26,26 @@ int main(int argc, char *argv[])
struct nl_cache_mngr *mngr;
struct nl_sock *sock;
struct nl_cache *ct;
+ int err;
- sock = nlt_socket_alloc();
+ sock = nl_cli_alloc_socket();
- mngr = nl_cache_mngr_alloc(sock, NETLINK_NETFILTER, NL_AUTO_PROVIDE);
- if (!mngr) {
- nl_perror("nl_cache_mngr_alloc");
+ err = nl_cache_mngr_alloc(sock, NETLINK_NETFILTER, NL_AUTO_PROVIDE, &mngr);
+ if (err < 0) {
+ nl_perror(err, "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)");
+ err = nl_cache_mngr_add(mngr, "netfilter/ct", &change_cb, NULL, &ct);
+ if (err < 0) {
+ nl_perror(err, "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()");
+ nl_perror(err, "nl_cache_mngr_poll()");
return -1;
}
diff --git a/tests/test-socket-creation.c b/tests/test-socket-creation.c
index a170ccd6..83f3ad46 100644
--- a/tests/test-socket-creation.c
+++ b/tests/test-socket-creation.c
@@ -1,23 +1,24 @@
-#include "../src/utils.h"
+#include <netlink/netlink.h>
+#include <errno.h>
int main(int argc, char *argv[])
{
struct nl_sock *h[1025];
int i;
- h[0] = nl_handle_alloc();
+ h[0] = nl_socket_alloc();
printf("Created handle with port 0x%x\n",
nl_socket_get_local_port(h[0]));
- nl_handle_destroy(h[0]);
- h[0] = nl_handle_alloc();
+ nl_socket_free(h[0]);
+ h[0] = nl_socket_alloc();
printf("Created handle with port 0x%x\n",
nl_socket_get_local_port(h[0]));
- nl_handle_destroy(h[0]);
+ nl_socket_free(h[0]);
for (i = 0; i < 1025; i++) {
- h[i] = nl_handle_alloc();
+ h[i] = nl_socket_alloc();
if (h[i] == NULL)
- nl_perror("Unable to allocate socket");
+ nl_perror(ENOMEM, "Unable to allocate socket");
else
printf("Created handle with port 0x%x\n",
nl_socket_get_local_port(h[i]));
diff --git a/tests/test-u32-filter-with-actions.c b/tests/test-u32-filter-with-actions.c
new file mode 100644
index 00000000..55f913af
--- /dev/null
+++ b/tests/test-u32-filter-with-actions.c
@@ -0,0 +1,400 @@
+/*
+ * test/tests-u32-with-actions.c Add ingress qdisc, create some hash filters, and add redirect action
+ *
+ * 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.
+ *
+ * Stolen from tests/test-complex-HTB-with-hash-filters.c
+ *
+ * Copyright (c) 2013 Cong Wang <xiyou.wangcong@gmail.com>
+ */
+
+#include <netlink/route/link.h>
+#include <netlink/route/tc.h>
+#include <netlink/route/qdisc.h>
+#include <netlink/route/cls/u32.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/action.h>
+#include <netlink/route/act/mirred.h>
+#include <netlink/route/class.h>
+#include <linux/if_ether.h>
+
+#include <netlink/attr.h>
+#include <stdio.h>
+#include <string.h>
+
+#define TC_HANDLE(maj, min) (TC_H_MAJ((maj) << 16) | TC_H_MIN(min))
+
+/* some functions are copied from iproute-tc tool */
+static int get_u32(__u32 *val, const char *arg, int base)
+{
+ unsigned long res;
+ char *ptr;
+
+ if (!arg || !*arg)
+ return -1;
+ res = strtoul(arg, &ptr, base);
+ if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL)
+ return -1;
+ *val = res;
+ return 0;
+}
+
+static int get_u32_handle(__u32 *handle, const char *str)
+{
+ __u32 htid=0, hash=0, nodeid=0;
+ char *tmp = strchr(str, ':');
+
+ if (tmp == NULL) {
+ if (memcmp("0x", str, 2) == 0)
+ return get_u32(handle, str, 16);
+ return -1;
+ }
+ htid = strtoul(str, &tmp, 16);
+ if (tmp == str && *str != ':' && *str != 0)
+ return -1;
+ if (htid>=0x1000)
+ return -1;
+ if (*tmp) {
+ str = tmp+1;
+ hash = strtoul(str, &tmp, 16);
+ if (tmp == str && *str != ':' && *str != 0)
+ return -1;
+ if (hash>=0x100)
+ return -1;
+ if (*tmp) {
+ str = tmp+1;
+ nodeid = strtoul(str, &tmp, 16);
+ if (tmp == str && *str != 0)
+ return -1;
+ if (nodeid>=0x1000)
+ return -1;
+ }
+ }
+ *handle = (htid<<20)|(hash<<12)|nodeid;
+ return 0;
+}
+
+static uint32_t get_u32_parse_handle(const char *cHandle)
+{
+ uint32_t handle=0;
+
+ if(get_u32_handle(&handle, cHandle)) {
+ printf ("Illegal \"ht\"\n");
+ return -1;
+ }
+
+ if (handle && TC_U32_NODE(handle)) {
+ printf("\"link\" must be a hash table.\n");
+ return -1;
+ }
+ return handle;
+}
+
+/*
+ * Function that adds a new filter and attach it to a hash table
+ * and set next hash table link with hash mask
+ *
+ */
+static
+int u32_add_filter_on_ht_with_hashmask(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio,
+ uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask,
+ uint32_t htid, uint32_t htlink, uint32_t hmask, uint32_t hoffset, struct rtnl_act *act)
+{
+ struct rtnl_cls *cls;
+ int err;
+
+ cls=rtnl_cls_alloc();
+ if (!(cls)) {
+ printf("Can not allocate classifier\n");
+ nl_socket_free(sock);
+ exit(1);
+ }
+
+ rtnl_tc_set_link(TC_CAST(cls), rtnlLink);
+
+ if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) {
+ printf("Can not set classifier as u32\n");
+ return 1;
+ }
+
+ rtnl_cls_set_prio(cls, prio);
+ rtnl_cls_set_protocol(cls, ETH_P_IP);
+
+ rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(0xffff, 0));
+
+ if (htid)
+ rtnl_u32_set_hashtable(cls, htid);
+
+ rtnl_u32_add_key_uint32(cls, keyval, keymask, keyoff, keyoffmask);
+
+ rtnl_u32_set_hashmask(cls, hmask, hoffset);
+
+ rtnl_u32_set_link(cls, htlink);
+
+ rtnl_u32_add_action(cls, act);
+
+
+ if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) {
+ printf("Can not add classifier: %s\n", nl_geterror(err));
+ return -1;
+ }
+ rtnl_cls_put(cls);
+ return 0;
+}
+
+/*
+ * function that creates a new hash table
+ */
+static
+int u32_add_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, uint32_t htid, uint32_t divisor)
+{
+
+ int err;
+ struct rtnl_cls *cls;
+
+ cls=rtnl_cls_alloc();
+ if (!(cls)) {
+ printf("Can not allocate classifier\n");
+ nl_socket_free(sock);
+ exit(1);
+ }
+
+ rtnl_tc_set_link(TC_CAST(cls), rtnlLink);
+
+ if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) {
+ printf("Can not set classifier as u32\n");
+ return 1;
+ }
+
+ rtnl_cls_set_prio(cls, prio);
+ rtnl_cls_set_protocol(cls, ETH_P_IP);
+ rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(0xffff, 0));
+
+ rtnl_u32_set_handle(cls, htid, 0x0, 0x0);
+ //printf("htid: 0x%X\n", htid);
+ rtnl_u32_set_divisor(cls, divisor);
+
+ if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) {
+ printf("Can not add classifier: %s\n", nl_geterror(err));
+ return -1;
+ }
+ rtnl_cls_put(cls);
+ return 0;
+}
+
+/*
+ * function that adds a new ingress qdisc and set the default class for unclassified traffic
+ */
+static
+int qdisc_add_ingress(struct nl_sock *sock, struct rtnl_link *rtnlLink)
+{
+
+ struct rtnl_qdisc *qdisc;
+ int err;
+
+ /* Allocation of a qdisc object */
+ if (!(qdisc = rtnl_qdisc_alloc())) {
+ printf("Can not allocate Qdisc\n");
+ return -1;
+ }
+
+ //rtnl_tc_set_ifindex(TC_CAST(qdisc), master_index);
+ rtnl_tc_set_link(TC_CAST(qdisc), rtnlLink);
+ rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT);
+
+ //printf("Delete current qdisc\n");
+ rtnl_qdisc_delete(sock, qdisc);
+ //rtnl_qdisc_put(qdisc);
+
+ rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(0xffff, 0));
+
+ if ((err = rtnl_tc_set_kind(TC_CAST(qdisc), "ingress"))) {
+ printf("Can not allocate ingress\n");
+ return -1;
+ }
+
+ /* Submit request to kernel and wait for response */
+ if ((err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE))) {
+ printf("Can not allocate ingress Qdisc\n");
+ return -1;
+ }
+
+ /* Return the qdisc object to free memory resources */
+ rtnl_qdisc_put(qdisc);
+
+ return 0;
+}
+
+int main(void)
+{
+ struct nl_sock *sock;
+ struct rtnl_link *link;
+ uint32_t ht, htlink, htid, direction;
+ char chashlink[16]="";
+ int err;
+ struct nl_cache *link_cache;
+ struct rtnl_act *act;
+
+ if (!(sock = nl_socket_alloc())) {
+ printf("Unable to allocate netlink socket\n");
+ exit(1);
+ }
+
+ if ((err = nl_connect(sock, NETLINK_ROUTE)) < 0 ) {
+ printf("Nu s-a putut conecta la NETLINK!\n");
+ nl_socket_free(sock);
+ exit(1);
+ }
+
+ if ((err = rtnl_link_alloc_cache(sock, AF_UNSPEC, &link_cache)) < 0) {
+ printf("Unable to allocate link cache: %s\n",
+ nl_geterror(err));
+ nl_socket_free(sock);
+ exit(1);
+ }
+
+ /* lookup interface index of eth0 */
+ if (!(link = rtnl_link_get_by_name(link_cache, "eth0"))) {
+ /* error */
+ printf("Interface not found\n");
+ nl_socket_free(sock);
+ exit(1);
+ }
+
+ err=qdisc_add_ingress(sock, link);
+ //printf("Add main hash table\n");
+
+ /* create u32 first hash filter table
+ *
+ */
+ /* formula calcul handle:
+ * uint32_t handle = (htid << 20) | (hash << 12) | nodeid;
+ */
+
+ /*
+ * Upper limit of number of hash tables: 4096 (0xFFF)
+ * Number of hashes in a table: 256 values (0xFF)
+ *
+ */
+
+ /* using 256 values for hash table
+ * each entry in hash table match a byte from IP address specified later by a hash key
+ */
+
+ uint32_t i;
+ for (i = 1; i <= 0xf; i++)
+ u32_add_ht(sock, link, 1, i, 256);
+
+ /*
+ * attach a u32 filter to the first hash
+ * that redirects all traffic and make a hash key
+ * from the fist byte of the IP address
+ *
+ */
+
+ //divisor=0x0; // unused here
+ //handle = 0x0; // unused here
+ //hash = 0x0; // unused here
+ //htid = 0x0; // unused here
+ //nodeid = 0x0; // unused here
+
+ // direction = 12 -> source IP
+ // direction = 16 -> destination IP
+ direction = 16;
+
+ /*
+ * which hash table will use
+ * in our case is hash table no 1 defined previous
+ *
+ * There are 2 posibilities to set the the hash table:
+ * 1. Using function get_u32_handle and sent a string in
+ * format 10: where 10 is number of the hash table
+ * 2. Create your own value in format: 0xa00000
+ *
+ */
+ strcpy(chashlink, "1:");
+ //printf("Hash Link: %s\n", chashlink);
+ //chashlink=malloc(sizeof(char) *
+ htlink = 0x0; // is used by get_u32_handle to return the correct value of hash table (link)
+
+ if(get_u32_handle(&htlink, chashlink)) {
+ printf ("Illegal \"link\"");
+ nl_socket_free(sock);
+ exit(1);
+ }
+ //printf ("hash link : 0x%X\n", htlink);
+ //printf ("hash link test : %u\n", (htlink && TC_U32_NODE(htlink)));
+
+ if (htlink && TC_U32_NODE(htlink)) {
+ printf("\"link\" must be a hash table.\n");
+ nl_socket_free(sock);
+ exit(1);
+ }
+
+ /* the hash mask will hit the hash table (link) no 1: in our case
+ */
+
+ /* set the hash key mask */
+ //hashmask = 0xFF000000UL; // the mask that is used to match the hash in specific table, in our case for example 1:a with mean the first byte which is 10 in hash table 1
+
+ /* Here we add a hash filter which match the first byte (see the hashmask value)
+ * of the source IP (offset 12 in the packet header)
+ * You can use also offset 16 to match the destination IP
+ */
+
+ /*
+ * Also we need a filter to match our rule
+ * This mean that we will put a 0.0.0.0/0 filter in our first rule
+ * that match the offset 12 (source IP)
+ * Also you can put offset 16 to match the destination IP
+ */
+
+ u32_add_filter_on_ht_with_hashmask(sock, link, 1,
+ 0x0, 0x0, direction, 0,
+ 0, htlink, 0xff000000, direction, NULL);
+
+ /*
+ * For each first byte that we need to match we will create a new hash table
+ * For example: you have those clases: 10.0.0.0/24 and 172.16.0.0/23
+ * For byte 10 and byte 172 will create a separate hash table that will match the second
+ * byte from each class.
+ *
+ */
+
+
+ /*
+ * Now we will create other filter under (ATENTION) our first hash table (link) 1:
+ * Previous rule redirects the trafic according the hash mask to hash table (link) no 1:
+ * Here we will match the hash tables from 1:0 to 1:ff. Under each hash table we will attach
+ * other rules that matches next byte from IP source/destination IP and we will repeat the
+ * previous steps.
+ *
+ */
+
+ act = rtnl_act_alloc();
+ if (!act) {
+ printf("rtnl_act_alloc() returns %p\n", act);
+ return -1;
+ }
+ rtnl_tc_set_kind(TC_CAST(act), "mirred");
+ rtnl_mirred_set_action(act, TCA_EGRESS_REDIR);
+ rtnl_mirred_set_policy(act, TC_ACT_STOLEN);
+ rtnl_mirred_set_ifindex(act, rtnl_link_name2i(link_cache, "eth1"));
+ // /8 check
+
+ // 10.0.0.0/8
+ ht=get_u32_parse_handle("1:a:");
+ htid = (ht&0xFFFFF000);
+ htlink=get_u32_parse_handle("2:");
+
+ u32_add_filter_on_ht_with_hashmask(sock, link, 1,
+ 0x0a000000, 0xff000000, direction, 0,
+ htid, htlink, 0x00ff0000, direction, act);
+
+ rtnl_act_put(act);
+ nl_socket_free(sock);
+ return 0;
+}
diff --git a/tests/util.h b/tests/util.h
new file mode 100644
index 00000000..c6753835
--- /dev/null
+++ b/tests/util.h
@@ -0,0 +1,5 @@
+#include <check.h>
+
+#define nl_fail_if(condition, error, message) \
+ fail_if((condition), "nlerr=%d (%s): %s", \
+ (error), nl_geterror(error), (message))