backport libnl-tiny, wprobe, madwifi
authorFelix Fietkau <nbd@openwrt.org>
Fri, 8 May 2009 22:48:17 +0000 (22:48 +0000)
committerFelix Fietkau <nbd@openwrt.org>
Fri, 8 May 2009 22:48:17 +0000 (22:48 +0000)
SVN-Revision: 15719

74 files changed:
package/libnl-tiny/Makefile [new file with mode: 0644]
package/libnl-tiny/files/libnl-tiny.pc [new file with mode: 0644]
package/libnl-tiny/src/Makefile [new file with mode: 0644]
package/libnl-tiny/src/attr.c [new file with mode: 0644]
package/libnl-tiny/src/cache.c [new file with mode: 0644]
package/libnl-tiny/src/cache_mngt.c [new file with mode: 0644]
package/libnl-tiny/src/error.c [new file with mode: 0644]
package/libnl-tiny/src/genl.c [new file with mode: 0644]
package/libnl-tiny/src/genl_ctrl.c [new file with mode: 0644]
package/libnl-tiny/src/genl_family.c [new file with mode: 0644]
package/libnl-tiny/src/genl_mngt.c [new file with mode: 0644]
package/libnl-tiny/src/handlers.c [new file with mode: 0644]
package/libnl-tiny/src/include/linux/genetlink.h [new file with mode: 0644]
package/libnl-tiny/src/include/linux/if.h [new file with mode: 0644]
package/libnl-tiny/src/include/linux/if_addr.h [new file with mode: 0644]
package/libnl-tiny/src/include/linux/netlink.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink-generic.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink-local.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink-types.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/addr.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/attr.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/cache-api.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/cache.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/data.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/errno.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/genl/ctrl.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/genl/family.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/genl/genl.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/genl/mngt.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/handlers.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/list.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/msg.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/netlink-compat.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/netlink-kernel.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/netlink.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/object-api.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/object.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/socket.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/types.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/utils.h [new file with mode: 0644]
package/libnl-tiny/src/include/netlink/version.h [new file with mode: 0644]
package/libnl-tiny/src/msg.c [new file with mode: 0644]
package/libnl-tiny/src/nl.c [new file with mode: 0644]
package/libnl-tiny/src/object.c [new file with mode: 0644]
package/libnl-tiny/src/socket.c [new file with mode: 0644]
package/libnl/Makefile
package/madwifi/Config.in
package/madwifi/Makefile
package/madwifi/files/lib/wifi/madwifi.sh
package/madwifi/patches-upstream/104-autocreate_none.patch [deleted file]
package/madwifi/patches-upstream/111-minstrel_crash.patch [deleted file]
package/madwifi/patches/123-ccmp_checks.patch
package/madwifi/patches/351-scanlist.patch
package/madwifi/patches/389-autochannel.patch
package/madwifi/patches/411-autochannel_multi.patch
package/madwifi/patches/416-wprobe.patch [new file with mode: 0644]
package/madwifi/patches/418-turbo.patch [deleted file]
package/madwifi/patches/420-diversity_fix.patch
package/madwifi/patches/421-channel_handling.patch [new file with mode: 0644]
package/madwifi/patches/422-confchange_reset.patch [new file with mode: 0644]
package/wprobe/Makefile [new file with mode: 0644]
package/wprobe/files/wprobe.init [new file with mode: 0755]
package/wprobe/src/exporter/Makefile [new file with mode: 0644]
package/wprobe/src/exporter/wprobe-export.c [new file with mode: 0644]
package/wprobe/src/exporter/wprobe-export.h [new file with mode: 0644]
package/wprobe/src/kernel/Makefile [new file with mode: 0644]
package/wprobe/src/kernel/linux/wprobe.h [new file with mode: 0644]
package/wprobe/src/kernel/wprobe-core.c [new file with mode: 0644]
package/wprobe/src/kernel/wprobe-dummy.c [new file with mode: 0644]
package/wprobe/src/user/Makefile [new file with mode: 0644]
package/wprobe/src/user/list.h [new file with mode: 0644]
package/wprobe/src/user/wprobe-info.c [new file with mode: 0644]
package/wprobe/src/user/wprobe.c [new file with mode: 0644]
package/wprobe/src/user/wprobe.h [new file with mode: 0644]

diff --git a/package/libnl-tiny/Makefile b/package/libnl-tiny/Makefile
new file mode 100644 (file)
index 0000000..4b02ef1
--- /dev/null
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2006-2009 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=libnl-tiny
+PKG_VERSION:=0.1
+PKG_RELEASE:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/libnl-tiny
+  SECTION:=libs
+  CATEGORY:=Libraries
+  TITLE:=netlink socket library
+endef
+
+define Package/libnl-tiny/description
+ This package contains a stripped down version of libnl
+endef
+
+define Build/Prepare
+       mkdir -p $(PKG_BUILD_DIR)
+       $(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+
+TARGET_CFLAGS += $(FPIC)
+
+define Build/Compile
+       $(MAKE) -C $(PKG_BUILD_DIR) \
+               $(TARGET_CONFIGURE_OPTS) \
+               CFLAGS="$(TARGET_CFLAGS)" \
+               all
+endef
+
+define Build/InstallDev
+       $(INSTALL_DIR) $(1)/usr/lib/pkgconfig $(1)/usr/include/libnl-tiny
+       $(CP) $(PKG_BUILD_DIR)/include/* $(1)/usr/include/libnl-tiny
+       $(CP) $(PKG_BUILD_DIR)/libnl-tiny.so $(1)/usr/lib/
+       $(CP) ./files/libnl-tiny.pc $(1)/usr/lib/pkgconfig
+endef
+
+define Package/libnl-tiny/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(CP) $(PKG_BUILD_DIR)/libnl-tiny.so $(1)/usr/lib/
+endef
+
+$(eval $(call BuildPackage,libnl-tiny))
diff --git a/package/libnl-tiny/files/libnl-tiny.pc b/package/libnl-tiny/files/libnl-tiny.pc
new file mode 100644 (file)
index 0000000..8c1bf6f
--- /dev/null
@@ -0,0 +1,10 @@
+prefix=/usr
+exec_prefix=/usr
+libdir=${exec_prefix}/lib
+includedir=${prefix}/include/libnl-tiny
+
+Name: libnl-tiny
+Description: Convenience library for netlink sockets
+Version: 2.0
+Libs: -L${libdir} -lnl-tiny
+Cflags:
diff --git a/package/libnl-tiny/src/Makefile b/package/libnl-tiny/src/Makefile
new file mode 100644 (file)
index 0000000..f684155
--- /dev/null
@@ -0,0 +1,17 @@
+CC=gcc
+WFLAGS=-Wall
+CFLAGS=-O2
+INCLUDES=-Iinclude
+
+LIBNAME=libnl-tiny.so
+
+all: $(LIBNAME)
+
+%.o: %.c
+       $(CC) $(WFLAGS) -c -o $@ $(INCLUDES) $(CFLAGS) $<
+
+LIBNL_OBJ=nl.o handlers.o msg.o attr.o cache.o cache_mngt.o object.o socket.o error.o
+GENL_OBJ=genl.o genl_family.o genl_ctrl.o genl_mngt.o
+
+$(LIBNAME): $(LIBNL_OBJ) $(GENL_OBJ)
+       $(CC) -shared -o $@ $^
diff --git a/package/libnl-tiny/src/attr.c b/package/libnl-tiny/src/attr.c
new file mode 100644 (file)
index 0000000..e0f5061
--- /dev/null
@@ -0,0 +1,668 @@
+/*
+ * lib/attr.c          Netlink Attributes
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/addr.h>
+#include <netlink/attr.h>
+#include <netlink/msg.h>
+#include <linux/socket.h>
+
+/**
+ * @ingroup msg
+ * @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
+ *
+ * @{
+ */
+
+/**
+ * @name Attribute Size Calculation
+ * @{
+ */
+
+/** @} */
+
+/**
+ * @name Parsing Attributes
+ * @{
+ */
+
+/**
+ * Check if the attribute header and payload can be accessed safely.
+ * @arg nla            Attribute of any kind.
+ * @arg remaining      Number of bytes remaining in attribute stream.
+ *
+ * Verifies that the header and payload do not exceed the number of
+ * bytes left in the attribute stream. This function must be called
+ * before access the attribute header or payload when iterating over
+ * the attribute stream using nla_next().
+ *
+ * @return True if the attribute can be accessed safely, false otherwise.
+ */
+int nla_ok(const struct nlattr *nla, int remaining)
+{
+       return remaining >= sizeof(*nla) &&
+              nla->nla_len >= sizeof(*nla) &&
+              nla->nla_len <= remaining;
+}
+
+/**
+ * Return next attribute in a stream of attributes.
+ * @arg nla            Attribute of any kind.
+ * @arg remaining      Variable to count remaining bytes in stream.
+ *
+ * Calculates the offset to the next attribute based on the attribute
+ * given. The attribute provided is assumed to be accessible, the
+ * caller is responsible to use nla_ok() beforehand. The offset (length
+ * of specified attribute including padding) is then subtracted from
+ * the remaining bytes variable and a pointer to the next attribute is
+ * returned.
+ *
+ * nla_next() can be called as long as remainig is >0.
+ *
+ * @return Pointer to next attribute.
+ */
+struct nlattr *nla_next(const struct nlattr *nla, int *remaining)
+{
+       int totlen = NLA_ALIGN(nla->nla_len);
+
+       *remaining -= totlen;
+       return (struct nlattr *) ((char *) nla + totlen);
+}
+
+static uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = {
+       [NLA_U8]        = sizeof(uint8_t),
+       [NLA_U16]       = sizeof(uint16_t),
+       [NLA_U32]       = sizeof(uint32_t),
+       [NLA_U64]       = sizeof(uint64_t),
+       [NLA_STRING]    = 1,
+};
+
+static int validate_nla(struct nlattr *nla, int maxtype,
+                       struct nla_policy *policy)
+{
+       struct nla_policy *pt;
+       int minlen = 0, type = nla_type(nla);
+
+       if (type <= 0 || type > maxtype)
+               return 0;
+
+       pt = &policy[type];
+
+       if (pt->type > NLA_TYPE_MAX)
+               BUG();
+
+       if (pt->minlen)
+               minlen = pt->minlen;
+       else if (pt->type != NLA_UNSPEC)
+               minlen = nla_attr_minlen[pt->type];
+
+       if (pt->type == NLA_FLAG && nla_len(nla) > 0)
+               return -NLE_RANGE;
+
+       if (nla_len(nla) < minlen)
+               return -NLE_RANGE;
+
+       if (pt->maxlen && nla_len(nla) > pt->maxlen)
+               return -NLE_RANGE;
+
+       if (pt->type == NLA_STRING) {
+               char *data = nla_data(nla);
+               if (data[nla_len(nla) - 1] != '\0')
+                       return -NLE_INVAL;
+       }
+
+       return 0;
+}
+
+
+/**
+ * Create attribute index based on a stream of attributes.
+ * @arg tb             Index array to be filled (maxtype+1 elements).
+ * @arg maxtype                Maximum attribute type expected and accepted.
+ * @arg head           Head of attribute stream.
+ * @arg len            Length of attribute stream.
+ * @arg policy         Attribute validation policy.
+ *
+ * Iterates over the stream of attributes and stores a pointer to each
+ * attribute in the index array using the attribute type as index to
+ * the array. Attribute with a type greater than the maximum type
+ * specified will be silently ignored in order to maintain backwards
+ * compatibility. If \a policy is not NULL, the attribute will be
+ * validated using the specified policy.
+ *
+ * @see nla_validate
+ * @return 0 on success or a negative error code.
+ */
+int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
+             struct nla_policy *policy)
+{
+       struct nlattr *nla;
+       int rem, err;
+
+       memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
+
+       nla_for_each_attr(nla, head, len, rem) {
+               int type = nla_type(nla);
+
+               if (type == 0) {
+                       fprintf(stderr, "Illegal nla->nla_type == 0\n");
+                       continue;
+               }
+
+               if (type <= maxtype) {
+                       if (policy) {
+                               err = validate_nla(nla, maxtype, policy);
+                               if (err < 0)
+                                       goto errout;
+                       }
+
+                       tb[type] = nla;
+               }
+       }
+
+       if (rem > 0)
+               fprintf(stderr, "netlink: %d bytes leftover after parsing "
+                      "attributes.\n", rem);
+
+       err = 0;
+errout:
+       return err;
+}
+
+/**
+ * Validate a stream of attributes.
+ * @arg head           Head of attributes stream.
+ * @arg len            Length of attributes stream.
+ * @arg maxtype                Maximum attribute type expected and accepted.
+ * @arg policy         Validation policy.
+ *
+ * Iterates over the stream of attributes and validates each attribute
+ * one by one using the specified policy. Attributes with a type greater
+ * 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.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nla_validate(struct nlattr *head, int len, int maxtype,
+                struct nla_policy *policy)
+{
+       struct nlattr *nla;
+       int rem, err;
+
+       nla_for_each_attr(nla, head, len, rem) {
+               err = validate_nla(nla, maxtype, policy);
+               if (err < 0)
+                       goto errout;
+       }
+
+       err = 0;
+errout:
+       return err;
+}
+
+/**
+ * Find a single attribute in a stream of attributes.
+ * @arg head           Head of attributes stream.
+ * @arg len            Length of attributes stream.
+ * @arg attrtype       Attribute type to look for.
+ *
+ * Iterates over the stream of attributes and compares each type with
+ * the type specified. Returns the first attribute which matches the
+ * type.
+ *
+ * @return Pointer to attribute found or NULL.
+ */
+struct nlattr *nla_find(struct nlattr *head, int len, int attrtype)
+{
+       struct nlattr *nla;
+       int rem;
+
+       nla_for_each_attr(nla, head, len, rem)
+               if (nla_type(nla) == attrtype)
+                       return nla;
+
+       return NULL;
+}
+
+/** @} */
+
+/**
+ * @name Unspecific Attribute
+ * @{
+ */
+
+/**
+ * Reserve space for a attribute.
+ * @arg msg            Netlink Message.
+ * @arg attrtype       Attribute Type.
+ * @arg attrlen                Length of payload.
+ *
+ * Reserves room for a attribute in the specified netlink message and
+ * fills in the attribute header (type, length). Returns NULL if there
+ * is unsuficient space for the attribute.
+ *
+ * Any padding between payload and the start of the next attribute is
+ * zeroed out.
+ *
+ * @return Pointer to start of attribute or NULL on failure.
+ */
+struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int attrlen)
+{
+       struct nlattr *nla;
+       int tlen;
+       
+       tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen);
+
+       if ((tlen + msg->nm_nlh->nlmsg_len) > 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));
+       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,
+                 (void *) nla - nlmsg_data(msg->nm_nlh),
+                 attrtype, msg->nm_nlh->nlmsg_len);
+
+       return nla;
+}
+
+/**
+ * Add a unspecific attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg datalen                Length of data to be used as payload.
+ * @arg data           Pointer to data to be used as attribute payload.
+ *
+ * Reserves room for a unspecific attribute and copies the provided data
+ * into the message as payload of the attribute. Returns an error if there
+ * is insufficient space for the attribute.
+ *
+ * @see nla_reserve
+ * @return 0 on success or a negative error code.
+ */
+int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data)
+{
+       struct nlattr *nla;
+
+       nla = nla_reserve(msg, attrtype, datalen);
+       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);
+
+       return 0;
+}
+
+
+
+/** @} */
diff --git a/package/libnl-tiny/src/cache.c b/package/libnl-tiny/src/cache.c
new file mode 100644 (file)
index 0000000..fed042d
--- /dev/null
@@ -0,0 +1,841 @@
+/*
+ * lib/cache.c         Caching Module
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cache_mngt
+ * @defgroup cache Cache
+ *
+ * @code
+ *   Cache Management             |    | Type Specific Cache Operations
+ *                                      
+ *                                |    | +----------------+ +------------+
+ *                                       | request update | | msg_parser |
+ *                                |    | +----------------+ +------------+
+ *                                     +- - - - -^- - - - - - - -^- -|- - - -
+ *    nl_cache_update:            |              |               |   |
+ *          1) --------- co_request_update ------+               |   |
+ *                                |                              |   |
+ *          2) destroy old cache     +----------- pp_cb ---------|---+
+ *                                |  |                           |
+ *          3) ---------- nl_recvmsgs ----------+   +- cb_valid -+
+ *             +--------------+   |  |          |   |
+ *             | nl_cache_add |<-----+   + - - -v- -|- - - - - - - - - - -
+ *             +--------------+   |      | +-------------+
+ *                                         | nl_recvmsgs |
+ *                                |      | +-----|-^-----+
+ *                                           +---v-|---+
+ *                                |      |   | nl_recv |
+ *                                           +---------+
+ *                                |      |                 Core Netlink
+ * @endcode
+ * 
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/object.h>
+#include <netlink/utils.h>
+
+/**
+ * @name Access Functions
+ * @{
+ */
+
+#ifdef disabled
+/**
+ * Return the number of items in the cache
+ * @arg cache          cache handle
+ */
+int nl_cache_nitems(struct nl_cache *cache)
+{
+       return cache->c_nitems;
+}
+
+/**
+ * Return the number of items matching a filter in the cache
+ * @arg cache          Cache object.
+ * @arg filter         Filter object.
+ */
+int nl_cache_nitems_filter(struct nl_cache *cache, struct nl_object *filter)
+{
+       struct nl_object_ops *ops;
+       struct nl_object *obj;
+       int nitems = 0;
+
+       if (cache->c_ops == NULL)
+               BUG();
+
+       ops = cache->c_ops->co_obj_ops;
+       
+       nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
+               if (filter && !nl_object_match_filter(obj, filter))
+                       continue;
+
+               nitems++;
+       }
+
+       return nitems;
+}
+
+/**
+ * Returns \b true if the cache is empty.
+ * @arg cache          Cache to check
+ * @return \a true if the cache is empty, otherwise \b false is returned.
+ */
+int nl_cache_is_empty(struct nl_cache *cache)
+{
+       return nl_list_empty(&cache->c_items);
+}
+
+/**
+ * Return the operations set of the cache
+ * @arg cache          cache handle
+ */
+struct nl_cache_ops *nl_cache_get_ops(struct nl_cache *cache)
+{
+       return cache->c_ops;
+}
+
+/**
+ * Return the first element in the cache
+ * @arg cache          cache handle
+ */
+struct nl_object *nl_cache_get_first(struct nl_cache *cache)
+{
+       if (nl_list_empty(&cache->c_items))
+               return NULL;
+
+       return nl_list_entry(cache->c_items.next,
+                            struct nl_object, ce_list);
+}
+
+/**
+ * Return the last element in the cache
+ * @arg cache          cache handle
+ */
+struct nl_object *nl_cache_get_last(struct nl_cache *cache)
+{
+       if (nl_list_empty(&cache->c_items))
+               return NULL;
+
+       return nl_list_entry(cache->c_items.prev,
+                            struct nl_object, ce_list);
+}
+
+/**
+ * Return the next element in the cache
+ * @arg obj            current object
+ */
+struct nl_object *nl_cache_get_next(struct nl_object *obj)
+{
+       if (nl_list_at_tail(obj, &obj->ce_cache->c_items, ce_list))
+               return NULL;
+       else
+               return nl_list_entry(obj->ce_list.next,
+                                    struct nl_object, ce_list);
+}
+
+/**
+ * Return the previous element in the cache
+ * @arg obj            current object
+ */
+struct nl_object *nl_cache_get_prev(struct nl_object *obj)
+{
+       if (nl_list_at_head(obj, &obj->ce_cache->c_items, ce_list))
+               return NULL;
+       else
+               return nl_list_entry(obj->ce_list.prev,
+                                    struct nl_object, ce_list);
+}
+#endif
+
+/** @} */
+
+/**
+ * @name Cache Creation/Deletion
+ * @{
+ */
+
+/**
+ * Allocate an empty cache
+ * @arg ops            cache operations to base the cache on
+ * 
+ * @return A newly allocated and initialized cache.
+ */
+struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops)
+{
+       struct nl_cache *cache;
+
+       cache = calloc(1, sizeof(*cache));
+       if (!cache)
+               return NULL;
+
+       nl_init_list_head(&cache->c_items);
+       cache->c_ops = ops;
+
+       NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache));
+
+       return cache;
+}
+
+int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock,
+                           struct nl_cache **result)
+{
+       struct nl_cache *cache;
+       int err;
+       
+       if (!(cache = nl_cache_alloc(ops)))
+               return -NLE_NOMEM;
+
+       if (sock && (err = nl_cache_refill(sock, cache)) < 0) {
+               nl_cache_free(cache);
+               return err;
+       }
+
+       *result = cache;
+       return 0;
+}
+
+#ifdef disabled
+/**
+ * Allocate an empty cache based on type name
+ * @arg kind           Name of cache type
+ * @return A newly allocated and initialized cache.
+ */
+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);
+       if (!ops)
+               return -NLE_NOCACHE;
+
+       if (!(cache = nl_cache_alloc(ops)))
+               return -NLE_NOMEM;
+
+       *result = cache;
+       return 0;
+}
+
+/**
+ * Allocate a new cache containing a subset of a cache
+ * @arg orig           Original cache to be based on
+ * @arg filter         Filter defining the subset to be filled into new cache
+ * @return A newly allocated cache or NULL.
+ */
+struct nl_cache *nl_cache_subset(struct nl_cache *orig,
+                                struct nl_object *filter)
+{
+       struct nl_cache *cache;
+       struct nl_object_ops *ops;
+       struct nl_object *obj;
+
+       if (!filter)
+               BUG();
+
+       cache = nl_cache_alloc(orig->c_ops);
+       if (!cache)
+               return NULL;
+
+       ops = orig->c_ops->co_obj_ops;
+
+       nl_list_for_each_entry(obj, &orig->c_items, ce_list) {
+               if (!nl_object_match_filter(obj, filter))
+                       continue;
+
+               nl_cache_add(cache, obj);
+       }
+
+       return cache;
+}
+#endif
+
+/**
+ * Clear a cache.
+ * @arg cache          cache to clear
+ *
+ * Removes all elements of a cache.
+ */
+void nl_cache_clear(struct nl_cache *cache)
+{
+       struct nl_object *obj, *tmp;
+
+       NL_DBG(1, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache));
+
+       nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list)
+               nl_cache_remove(obj);
+}
+
+/**
+ * Free a cache.
+ * @arg cache          Cache to free.
+ *
+ * Removes all elements of a cache and frees all memory.
+ *
+ * @note Use this function if you are working with allocated caches.
+ */
+void nl_cache_free(struct nl_cache *cache)
+{
+       if (!cache)
+               return;
+
+       nl_cache_clear(cache);
+       NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache));
+       free(cache);
+}
+
+/** @} */
+
+/**
+ * @name Cache Modifications
+ * @{
+ */
+
+static int __cache_add(struct nl_cache *cache, struct nl_object *obj)
+{
+       obj->ce_cache = cache;
+
+       nl_list_add_tail(&obj->ce_list, &cache->c_items);
+       cache->c_nitems++;
+
+       NL_DBG(1, "Added %p to cache %p <%s>.\n",
+              obj, cache, nl_cache_name(cache));
+
+       return 0;
+}
+
+/**
+ * Add object to a cache.
+ * @arg cache          Cache to add object to
+ * @arg obj            Object to be added to the cache
+ *
+ * Adds the given object to the specified cache. The object is cloned
+ * if it has been added to another cache already.
+ *
+ * @return 0 or a negative error code.
+ */
+int nl_cache_add(struct nl_cache *cache, struct nl_object *obj)
+{
+       struct nl_object *new;
+
+       if (cache->c_ops->co_obj_ops != obj->ce_ops)
+               return -NLE_OBJ_MISMATCH;
+
+       if (!nl_list_empty(&obj->ce_list)) {
+               new = nl_object_clone(obj);
+               if (!new)
+                       return -NLE_NOMEM;
+       } else {
+               nl_object_get(obj);
+               new = obj;
+       }
+
+       return __cache_add(cache, new);
+}
+
+#ifdef disabled
+/**
+ * Move object from one cache to another
+ * @arg cache          Cache to move object to.
+ * @arg obj            Object subject to be moved
+ *
+ * Removes the given object from its associated cache if needed
+ * and adds it to the new cache.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nl_cache_move(struct nl_cache *cache, struct nl_object *obj)
+{
+       if (cache->c_ops->co_obj_ops != obj->ce_ops)
+               return -NLE_OBJ_MISMATCH;
+
+       NL_DBG(3, "Moving object %p to cache %p\n", obj, cache);
+       
+       /* Acquire reference, if already in a cache this will be
+        * reverted during removal */
+       nl_object_get(obj);
+
+       if (!nl_list_empty(&obj->ce_list))
+               nl_cache_remove(obj);
+
+       return __cache_add(cache, obj);
+}
+#endif
+
+/**
+ * Removes an object from a cache.
+ * @arg obj            Object to remove from its cache
+ *
+ * Removes the object \c obj from the cache it is assigned to, since
+ * an object can only be assigned to one cache at a time, the cache
+ * must ne be passed along with it.
+ */
+void nl_cache_remove(struct nl_object *obj)
+{
+       struct nl_cache *cache = obj->ce_cache;
+
+       if (cache == NULL)
+               return;
+
+       nl_list_del(&obj->ce_list);
+       obj->ce_cache = NULL;
+       nl_object_put(obj);
+       cache->c_nitems--;
+
+       NL_DBG(1, "Deleted %p from cache %p <%s>.\n",
+              obj, cache, nl_cache_name(cache));
+}
+
+#ifdef disabled
+/**
+ * Search for an object in a cache
+ * @arg cache          Cache to search in.
+ * @arg needle         Object to look for.
+ *
+ * Iterates over the cache and looks for an object with identical
+ * identifiers as the needle.
+ *
+ * @return Reference to object or NULL if not found.
+ * @note The returned object must be returned via nl_object_put().
+ */
+struct nl_object *nl_cache_search(struct nl_cache *cache,
+                                 struct nl_object *needle)
+{
+       struct nl_object *obj;
+
+       nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
+               if (nl_object_identical(obj, needle)) {
+                       nl_object_get(obj);
+                       return obj;
+               }
+       }
+
+       return NULL;
+}
+#endif
+
+/** @} */
+
+/**
+ * @name Synchronization
+ * @{
+ */
+
+/**
+ * Request a full dump from the kernel to fill a cache
+ * @arg sk             Netlink socket.
+ * @arg cache          Cache subjected to be filled.
+ *
+ * Send a dumping request to the kernel causing it to dump all objects
+ * related to the specified cache to the netlink socket.
+ *
+ * Use nl_cache_pickup() to read the objects from the socket and fill them
+ * into a cache.
+ */
+int nl_cache_request_full_dump(struct nl_sock *sk, struct nl_cache *cache)
+{
+       NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n",
+                 cache, nl_cache_name(cache));
+
+       if (cache->c_ops->co_request_update == NULL)
+               return -NLE_OPNOTSUPP;
+
+       return cache->c_ops->co_request_update(cache, sk);
+}
+
+/** @cond SKIP */
+struct update_xdata {
+       struct nl_cache_ops *ops;
+       struct nl_parser_param *params;
+};
+
+static int update_msg_parser(struct nl_msg *msg, void *arg)
+{
+       struct update_xdata *x = arg;
+       
+       return nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params);
+}
+/** @endcond */
+
+int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
+                  struct nl_parser_param *param)
+{
+       int err;
+       struct nl_cb *cb;
+       struct update_xdata x = {
+               .ops = cache->c_ops,
+               .params = param,
+       };
+
+       NL_DBG(1, "Picking up answer for cache %p <%s>...\n",
+                 cache, nl_cache_name(cache));
+
+       cb = nl_cb_clone(sk->s_cb);
+       if (cb == NULL)
+               return -NLE_NOMEM;
+
+       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x);
+
+       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_cb_put(cb);
+
+       return err;
+}
+
+static int pickup_cb(struct nl_object *c, struct nl_parser_param *p)
+{
+       return nl_cache_add((struct nl_cache *) p->pp_arg, c);
+}
+
+/**
+ * Pickup a netlink dump response and put it into a cache.
+ * @arg sk             Netlink socket.
+ * @arg cache          Cache to put items into.
+ *
+ * Waits for netlink messages to arrive, parses them and puts them into
+ * the specified cache.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache)
+{
+       struct nl_parser_param p = {
+               .pp_cb = pickup_cb,
+               .pp_arg = cache,
+       };
+
+       return __cache_pickup(sk, cache, &p);
+}
+
+#ifdef disabled
+static int cache_include(struct nl_cache *cache, struct nl_object *obj,
+                        struct nl_msgtype *type, change_func_t cb)
+{
+       struct nl_object *old;
+
+       switch (type->mt_act) {
+       case NL_ACT_NEW:
+       case NL_ACT_DEL:
+               old = nl_cache_search(cache, obj);
+               if (old) {
+                       nl_cache_remove(old);
+                       if (type->mt_act == NL_ACT_DEL) {
+                               if (cb)
+                                       cb(cache, old, NL_ACT_DEL);
+                               nl_object_put(old);
+                       }
+               }
+
+               if (type->mt_act == NL_ACT_NEW) {
+                       nl_cache_move(cache, obj);
+                       if (old == NULL && cb)
+                               cb(cache, obj, NL_ACT_NEW);
+                       else if (old) {
+                               if (nl_object_diff(old, obj) && cb)
+                                       cb(cache, obj, NL_ACT_CHANGE);
+
+                               nl_object_put(old);
+                       }
+               }
+               break;
+       default:
+               NL_DBG(2, "Unknown action associated to object %p\n", obj);
+               return 0;
+       }
+
+       return 0;
+}
+
+int nl_cache_include(struct nl_cache *cache, struct nl_object *obj,
+                    change_func_t change_cb)
+{
+       struct nl_cache_ops *ops = cache->c_ops;
+       int i;
+
+       if (ops->co_obj_ops != obj->ce_ops)
+               return -NLE_OBJ_MISMATCH;
+
+       for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
+               if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype)
+                       return cache_include(cache, obj, &ops->co_msgtypes[i],
+                                            change_cb);
+
+       return -NLE_MSGTYPE_NOSUPPORT;
+}
+
+static int resync_cb(struct nl_object *c, struct nl_parser_param *p)
+{
+       struct nl_cache_assoc *ca = p->pp_arg;
+
+       return nl_cache_include(ca->ca_cache, c, ca->ca_change);
+}
+
+int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache,
+                   change_func_t change_cb)
+{
+       struct nl_object *obj, *next;
+       struct nl_cache_assoc ca = {
+               .ca_cache = cache,
+               .ca_change = change_cb,
+       };
+       struct nl_parser_param p = {
+               .pp_cb = resync_cb,
+               .pp_arg = &ca,
+       };
+       int err;
+
+       NL_DBG(1, "Resyncing cache %p <%s>...\n", cache, nl_cache_name(cache));
+
+       /* Mark all objects so we can see if some of them are obsolete */
+       nl_cache_mark_all(cache);
+
+       err = nl_cache_request_full_dump(sk, cache);
+       if (err < 0)
+               goto errout;
+
+       err = __cache_pickup(sk, cache, &p);
+       if (err < 0)
+               goto errout;
+
+       nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list)
+               if (nl_object_is_marked(obj))
+                       nl_cache_remove(obj);
+
+       NL_DBG(1, "Finished resyncing %p <%s>\n", cache, nl_cache_name(cache));
+
+       err = 0;
+errout:
+       return err;
+}
+#endif
+
+/** @} */
+
+/**
+ * @name Parsing
+ * @{
+ */
+
+/** @cond SKIP */
+int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who,
+                  struct nlmsghdr *nlh, struct nl_parser_param *params)
+{
+       int i, err;
+
+       if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize))
+               return -NLE_MSG_TOOSHORT;
+
+       for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) {
+               if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) {
+                       err = ops->co_msg_parser(ops, who, nlh, params);
+                       if (err != -NLE_OPNOTSUPP)
+                               goto errout;
+               }
+       }
+
+
+       err = -NLE_MSGTYPE_NOSUPPORT;
+errout:
+       return err;
+}
+/** @endcond */
+
+/**
+ * Parse a netlink message and add it to the cache.
+ * @arg cache          cache to add element to
+ * @arg msg            netlink message
+ *
+ * Parses a netlink message by calling the cache specific message parser
+ * and adds the new element to the cache.
+ *
+ * @return 0 or a negative error code.
+ */
+int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg)
+{
+       struct nl_parser_param p = {
+               .pp_cb = pickup_cb,
+               .pp_arg = cache,
+       };
+
+       return nl_cache_parse(cache->c_ops, NULL, nlmsg_hdr(msg), &p);
+}
+
+/**
+ * (Re)fill a cache with the contents in the kernel.
+ * @arg sk             Netlink socket.
+ * @arg cache          cache to update
+ *
+ * Clears the specified cache and fills it with the current state in
+ * the kernel.
+ *
+ * @return 0 or a negative error code.
+ */
+int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache)
+{
+       int err;
+
+       err = nl_cache_request_full_dump(sk, cache);
+       if (err < 0)
+               return err;
+
+       NL_DBG(2, "Upading cache %p <%s>, request sent, waiting for dump...\n",
+              cache, nl_cache_name(cache));
+       nl_cache_clear(cache);
+
+       return nl_cache_pickup(sk, cache);
+}
+
+/** @} */
+#ifdef disabled
+
+/**
+ * @name Utillities
+ * @{
+ */
+
+/**
+ * Mark all objects in a cache
+ * @arg cache          Cache to mark all objects in
+ */
+void nl_cache_mark_all(struct nl_cache *cache)
+{
+       struct nl_object *obj;
+
+       NL_DBG(2, "Marking all objects in cache %p <%s>...\n",
+                 cache, nl_cache_name(cache));
+
+       nl_list_for_each_entry(obj, &cache->c_items, ce_list)
+               nl_object_mark(obj);
+}
+
+/** @} */
+
+/**
+ * @name Dumping
+ * @{
+ */
+#ifdef disabled
+/**
+ * Dump all elements of a cache.
+ * @arg cache          cache to dump
+ * @arg params         dumping parameters
+ *
+ * Dumps all elements of the \a cache to the file descriptor \a fd.
+ */
+void nl_cache_dump(struct nl_cache *cache, struct nl_dump_params *params)
+{
+       nl_cache_dump_filter(cache, params, NULL);
+}
+
+/**
+ * Dump all elements of a cache (filtered).
+ * @arg cache          cache to dump
+ * @arg params         dumping parameters (optional)
+ * @arg filter         filter object
+ *
+ * Dumps all elements of the \a cache to the file descriptor \a fd
+ * given they match the given filter \a filter.
+ */
+void nl_cache_dump_filter(struct nl_cache *cache,
+                         struct nl_dump_params *params,
+                         struct nl_object *filter)
+{
+       int type = params ? params->dp_type : NL_DUMP_DETAILS;
+       struct nl_object_ops *ops;
+       struct nl_object *obj;
+
+       NL_DBG(2, "Dumping cache %p <%s> filter %p\n",
+              cache, nl_cache_name(cache), filter);
+
+       if (type > NL_DUMP_MAX || type < 0)
+               BUG();
+
+       if (cache->c_ops == NULL)
+               BUG();
+
+       ops = cache->c_ops->co_obj_ops;
+       if (!ops->oo_dump[type])
+               return;
+
+       nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
+               if (filter && !nl_object_match_filter(obj, filter))
+                       continue;
+
+               NL_DBG(4, "Dumping object %p...\n", obj);
+               dump_from_ops(obj, params);
+       }
+}
+#endif
+
+/** @} */
+
+/**
+ * @name Iterators
+ * @{
+ */
+
+/**
+ * Call a callback on each element of the cache.
+ * @arg cache          cache to iterate on
+ * @arg cb             callback function
+ * @arg arg            argument passed to callback function
+ *
+ * Calls a callback function \a cb on each element of the \a cache.
+ * The argument \a arg is passed on the callback function.
+ */
+void nl_cache_foreach(struct nl_cache *cache,
+                     void (*cb)(struct nl_object *, void *), void *arg)
+{
+       nl_cache_foreach_filter(cache, NULL, cb, arg);
+}
+
+/**
+ * Call a callback on each element of the cache (filtered).
+ * @arg cache          cache to iterate on
+ * @arg filter         filter object
+ * @arg cb             callback function
+ * @arg arg            argument passed to callback function
+ *
+ * Calls a callback function \a cb on each element of the \a cache
+ * that matches the \a filter. The argument \a arg is passed on
+ * to the callback function.
+ */
+void nl_cache_foreach_filter(struct nl_cache *cache, struct nl_object *filter,
+                            void (*cb)(struct nl_object *, void *), void *arg)
+{
+       struct nl_object *obj, *tmp;
+       struct nl_object_ops *ops;
+
+       if (cache->c_ops == NULL)
+               BUG();
+
+       ops = cache->c_ops->co_obj_ops;
+
+       nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) {
+               if (filter && !nl_object_match_filter(obj, filter))
+                       continue;
+
+               cb(obj, arg);
+       }
+}
+
+/** @} */
+#endif
+
+/** @} */
diff --git a/package/libnl-tiny/src/cache_mngt.c b/package/libnl-tiny/src/cache_mngt.c
new file mode 100644 (file)
index 0000000..d57d836
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * lib/cache_mngt.c    Cache Management
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup core
+ * @defgroup cache_mngt Caching
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/utils.h>
+
+static struct nl_cache_ops *cache_ops;
+
+/**
+ * @name Cache Operations Sets
+ * @{
+ */
+
+/**
+ * Lookup the set cache operations of a certain cache type
+ * @arg name           name of the cache type
+ *
+ * @return The cache operations or NULL if no operations
+ *         have been registered under the specified name.
+ */
+struct nl_cache_ops *nl_cache_ops_lookup(const char *name)
+{
+       struct nl_cache_ops *ops;
+
+       for (ops = cache_ops; ops; ops = ops->co_next)
+               if (!strcmp(ops->co_name, name))
+                       return ops;
+
+       return NULL;
+}
+
+/**
+ * Associate a message type to a set of cache operations
+ * @arg protocol               netlink protocol
+ * @arg msgtype                        netlink message type
+ *
+ * Associates the specified netlink message type with
+ * a registered set of cache operations.
+ *
+ * @return The cache operations or NULL if no association
+ *         could be made.
+ */
+struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype)
+{
+       int i;
+       struct nl_cache_ops *ops;
+
+       for (ops = cache_ops; ops; ops = ops->co_next) {
+               if (ops->co_protocol != protocol)
+                       continue;
+
+               for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
+                       if (ops->co_msgtypes[i].mt_id == msgtype)
+                               return ops;
+       }
+
+       return NULL;
+}
+
+/**
+ * Lookup message type cache association
+ * @arg ops                    cache operations
+ * @arg msgtype                        netlink message type
+ *
+ * Searches for a matching message type association ing the specified
+ * cache operations.
+ *
+ * @return A message type association or NULL.
+ */
+struct nl_msgtype *nl_msgtype_lookup(struct nl_cache_ops *ops, int msgtype)
+{
+       int i;
+
+       for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
+               if (ops->co_msgtypes[i].mt_id == msgtype)
+                       return &ops->co_msgtypes[i];
+
+       return NULL;
+}
+
+static struct nl_cache_ops *cache_ops_lookup_for_obj(struct nl_object_ops *obj_ops)
+{
+       struct nl_cache_ops *ops;
+
+       for (ops = cache_ops; ops; ops = ops->co_next)
+               if (ops->co_obj_ops == obj_ops)
+                       return ops;
+
+       return NULL;
+
+}
+
+/**
+ * Call a function for each registered cache operation
+ * @arg cb             Callback function to be called
+ * @arg arg            User specific argument.
+ */
+void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *arg)
+{
+       struct nl_cache_ops *ops;
+
+       for (ops = cache_ops; ops; ops = ops->co_next)
+               cb(ops, arg);
+}
+
+/**
+ * Register a set of cache operations
+ * @arg ops            cache operations
+ *
+ * Called by users of caches to announce the avaibility of
+ * a certain cache type.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nl_cache_mngt_register(struct nl_cache_ops *ops)
+{
+       if (!ops->co_name || !ops->co_obj_ops)
+               return -NLE_INVAL;
+
+       if (nl_cache_ops_lookup(ops->co_name))
+               return -NLE_EXIST;
+
+       ops->co_next = cache_ops;
+       cache_ops = ops;
+
+       NL_DBG(1, "Registered cache operations %s\n", ops->co_name);
+
+       return 0;
+}
+
+/**
+ * Unregister a set of cache operations
+ * @arg ops            cache operations
+ *
+ * Called by users of caches to announce a set of
+ * cache operations is no longer available. The
+ * specified cache operations must have been registered
+ * previously using nl_cache_mngt_register()
+ *
+ * @return 0 on success or a negative error code
+ */
+int nl_cache_mngt_unregister(struct nl_cache_ops *ops)
+{
+       struct nl_cache_ops *t, **tp;
+
+       for (tp = &cache_ops; (t=*tp) != NULL; tp = &t->co_next)
+               if (t == ops)
+                       break;
+
+       if (!t)
+               return -NLE_NOCACHE;
+
+       NL_DBG(1, "Unregistered cache operations %s\n", ops->co_name);
+
+       *tp = t->co_next;
+       return 0;
+}
+
+/** @} */
+
+/**
+ * @name Global Cache Provisioning/Requiring
+ * @{
+ */
+
+/**
+ * Provide a cache for global use
+ * @arg cache          cache to provide
+ *
+ * Offers the specified cache to be used by other modules.
+ * Only one cache per type may be shared at a time,
+ * a previsouly provided caches will be overwritten.
+ */
+void nl_cache_mngt_provide(struct nl_cache *cache)
+{
+       struct nl_cache_ops *ops;
+
+       ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops);
+       if (!ops)
+               BUG();
+       else
+               ops->co_major_cache = cache;
+}
+
+/**
+ * Unprovide a cache for global use
+ * @arg cache          cache to unprovide
+ *
+ * Cancels the offer to use a cache globally. The
+ * cache will no longer be returned via lookups but
+ * may still be in use.
+ */
+void nl_cache_mngt_unprovide(struct nl_cache *cache)
+{
+       struct nl_cache_ops *ops;
+
+       ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops);
+       if (!ops)
+               BUG();
+       else if (ops->co_major_cache == cache)
+               ops->co_major_cache = NULL;
+}
+
+/**
+ * Demand the use of a global cache
+ * @arg name           name of the required object type
+ *
+ * Trys to find a cache of the specified type for global
+ * use.
+ *
+ * @return A cache provided by another subsystem of the
+ *         specified type marked to be available.
+ */
+struct nl_cache *nl_cache_mngt_require(const char *name)
+{
+       struct nl_cache_ops *ops;
+
+       ops = nl_cache_ops_lookup(name);
+       if (!ops || !ops->co_major_cache) {
+               fprintf(stderr, "Application BUG: Your application must "
+                       "call nl_cache_mngt_provide() and\nprovide a valid "
+                       "%s cache to be used for internal lookups.\nSee the "
+                       " API documentation for more details.\n", name);
+
+               return NULL;
+       }
+       
+       return ops->co_major_cache;
+}
+
+/** @} */
+
+/** @} */
diff --git a/package/libnl-tiny/src/error.c b/package/libnl-tiny/src/error.c
new file mode 100644 (file)
index 0000000..71ae722
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * lib/error.c         Error Handling
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under 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>
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+
+static const char *errmsg[NLE_MAX+1] = {
+[NLE_SUCCESS]          = "Success",
+[NLE_FAILURE]          = "Unspecific failure",
+[NLE_INTR]             = "Interrupted system call",
+[NLE_BAD_SOCK]         = "Bad socket",
+[NLE_AGAIN]            = "Try again",
+[NLE_NOMEM]            = "Out of memory",
+[NLE_EXIST]            = "Object exists",
+[NLE_INVAL]            = "Invalid input data or parameter",
+[NLE_RANGE]            = "Input data out of range",
+[NLE_MSGSIZE]          = "Message size not sufficient",
+[NLE_OPNOTSUPP]                = "Operation not supported",
+[NLE_AF_NOSUPPORT]     = "Address family not supported",
+[NLE_OBJ_NOTFOUND]     = "Object not found",
+[NLE_NOATTR]           = "Attribute not available",
+[NLE_MISSING_ATTR]     = "Missing attribute",
+[NLE_AF_MISMATCH]      = "Address family mismatch",
+[NLE_SEQ_MISMATCH]     = "Message sequence number mismatch",
+[NLE_MSG_OVERFLOW]     = "Kernel reported message overflow",
+[NLE_MSG_TRUNC]                = "Kernel reported truncated message",
+[NLE_NOADDR]           = "Invalid address for specified address family",
+[NLE_SRCRT_NOSUPPORT]  = "Source based routing not supported",
+[NLE_MSG_TOOSHORT]     = "Netlink message is too short",
+[NLE_MSGTYPE_NOSUPPORT]        = "Netlink message type is not supported",
+[NLE_OBJ_MISMATCH]     = "Object type does not match cache",
+[NLE_NOCACHE]          = "Unknown or invalid cache type",
+[NLE_BUSY]             = "Object busy",
+[NLE_PROTO_MISMATCH]   = "Protocol mismatch",
+[NLE_NOACCESS]         = "No Access",
+[NLE_PERM]             = "Operation not permitted",
+};
+
+/**
+ * Return error message for an error code
+ * @return error message
+ */
+const char *nl_geterror(int error)
+{
+       error = abs(error);
+
+       if (error > NLE_MAX)
+               error = NLE_FAILURE;
+
+       return errmsg[error];
+}
+
+/**
+ * Print a libnl error message
+ * @arg s              error message prefix
+ *
+ * Prints the error message of the call that failed last.
+ *
+ * If s is not NULL and *s is not a null byte the argument
+ * string is printed, followed by a colon and a blank. Then
+ * the error message and a new-line.
+ */
+void nl_perror(int error, const char *s)
+{
+       if (s && *s)
+               fprintf(stderr, "%s: %s\n", s, nl_geterror(error));
+       else
+               fprintf(stderr, "%s\n", nl_geterror(error));
+}
+
+int nl_syserr2nlerr(int error)
+{
+       error = abs(error);
+
+       switch (error) {
+       case EBADF:             return NLE_BAD_SOCK;
+       case EADDRINUSE:        return NLE_EXIST;
+       case EEXIST:            return NLE_EXIST;
+       case EADDRNOTAVAIL:     return NLE_NOADDR;
+       case ENOENT:            return NLE_OBJ_NOTFOUND;
+       case EINTR:             return NLE_INTR;
+       case EAGAIN:            return NLE_AGAIN;
+       case ENOTSOCK:          return NLE_BAD_SOCK;
+       case ENOPROTOOPT:       return NLE_INVAL;
+       case EFAULT:            return NLE_INVAL;
+       case EACCES:            return NLE_NOACCESS;
+       case EINVAL:            return NLE_INVAL;
+       case ENOBUFS:           return NLE_NOMEM;
+       case ENOMEM:            return NLE_NOMEM;
+       case EAFNOSUPPORT:      return NLE_AF_NOSUPPORT;
+       case EPROTONOSUPPORT:   return NLE_PROTO_MISMATCH;
+       case EOPNOTSUPP:        return NLE_OPNOTSUPP;
+       case EPERM:             return NLE_PERM;
+       case EBUSY:             return NLE_BUSY;
+       default:                return NLE_FAILURE;
+       }
+}
+
+/** @} */
+
diff --git a/package/libnl-tiny/src/genl.c b/package/libnl-tiny/src/genl.c
new file mode 100644 (file)
index 0000000..055be91
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * lib/genl/genl.c             Generic Netlink
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @defgroup genl Generic Netlink
+ *
+ * @par Message Format
+ * @code
+ *  <------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) --->
+ * +----------------------------+- - -+- - - - - - - - - - -+- - -+
+ * |           Header           | Pad |       Payload       | Pad |
+ * |      struct nlmsghdr       |     |                     |     |
+ * +----------------------------+- - -+- - - - - - - - - - -+- - -+
+ * @endcode
+ * @code
+ *  <-------- GENL_HDRLEN -------> <--- hdrlen -->
+ *                                 <------- genlmsg_len(ghdr) ------>
+ * +------------------------+- - -+---------------+- - -+------------+
+ * | Generic Netlink Header | Pad | Family Header | Pad | Attributes |
+ * |    struct genlmsghdr   |     |               |     |            |
+ * +------------------------+- - -+---------------+- - -+------------+
+ * genlmsg_data(ghdr)--------------^                     ^
+ * genlmsg_attrdata(ghdr, hdrlen)-------------------------
+ * @endcode
+ *
+ * @par 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/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/utils.h>
+
+/**
+ * @name Socket Creating
+ * @{
+ */
+
+int genl_connect(struct nl_sock *sk)
+{
+       return nl_connect(sk, NETLINK_GENERIC);
+}
+
+/** @} */
+
+/**
+ * @name Sending
+ * @{
+ */
+
+/**
+ * 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.
+ *
+ * Fills out a routing netlink request message and sends it out
+ * using nl_send_simple().
+ *
+ * @return 0 on success or a negative error code.
+ */
+int genl_send_simple(struct nl_sock *sk, int family, int cmd,
+                    int version, int flags)
+{
+       struct genlmsghdr hdr = {
+               .cmd = cmd,
+               .version = version,
+       };
+
+       return nl_send_simple(sk, family, flags, &hdr, sizeof(hdr));
+}
+
+/** @} */
+
+
+/**
+ * @name Message Parsing
+ * @{
+ */
+
+int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen)
+{
+       struct genlmsghdr *ghdr;
+
+       if (!nlmsg_valid_hdr(nlh, GENL_HDRLEN))
+               return 0;
+
+       ghdr = nlmsg_data(nlh);
+       if (genlmsg_len(ghdr) < NLMSG_ALIGN(hdrlen))
+               return 0;
+
+       return 1;
+}
+
+int genlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype,
+                  struct nla_policy *policy)
+{
+       struct genlmsghdr *ghdr;
+
+       if (!genlmsg_valid_hdr(nlh, hdrlen))
+               return -NLE_MSG_TOOSHORT;
+
+       ghdr = nlmsg_data(nlh);
+       return nla_validate(genlmsg_attrdata(ghdr, hdrlen),
+                           genlmsg_attrlen(ghdr, hdrlen), maxtype, policy);
+}
+
+int genlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
+                 int maxtype, struct nla_policy *policy)
+{
+       struct genlmsghdr *ghdr;
+
+       if (!genlmsg_valid_hdr(nlh, hdrlen))
+               return -NLE_MSG_TOOSHORT;
+
+       ghdr = nlmsg_data(nlh);
+       return nla_parse(tb, maxtype, genlmsg_attrdata(ghdr, hdrlen),
+                        genlmsg_attrlen(ghdr, hdrlen), policy);
+}
+
+/**
+ * Get head of message payload
+ * @arg gnlh   genetlink messsage header
+ */
+void *genlmsg_data(const struct genlmsghdr *gnlh)
+{
+       return ((unsigned char *) gnlh + GENL_HDRLEN);
+}
+
+/**
+ * Get lenght of message payload
+ * @arg gnlh   genetlink message header
+ */
+int genlmsg_len(const struct genlmsghdr *gnlh)
+{
+       struct nlmsghdr *nlh = (struct nlmsghdr *)((unsigned char *)gnlh -
+                                                       NLMSG_HDRLEN);
+       return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN);
+}
+
+/**
+ * Get head of attribute data
+ * @arg gnlh   generic netlink message header
+ * @arg hdrlen length of family specific header
+ */
+struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen)
+{
+       return genlmsg_data(gnlh) + NLMSG_ALIGN(hdrlen);
+}
+
+/**
+ * Get length of attribute data
+ * @arg gnlh   generic netlink message header
+ * @arg hdrlen length of family specific header
+ */
+int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen)
+{
+       return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen);
+}
+
+/** @} */
+
+/**
+ * @name Message Building
+ * @{
+ */
+
+/**
+ * Add generic netlink header to netlink message
+ * @arg msg            netlink message
+ * @arg pid            netlink process id or NL_AUTO_PID
+ * @arg seq            sequence number of message or NL_AUTO_SEQ
+ * @arg family         generic netlink family
+ * @arg hdrlen         length of user specific header
+ * @arg flags          message flags
+ * @arg cmd            generic netlink command
+ * @arg version                protocol version
+ *
+ * Returns pointer to user specific header.
+ */
+void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family,
+                 int hdrlen, int flags, uint8_t cmd, uint8_t version)
+{
+       struct nlmsghdr *nlh;
+       struct genlmsghdr hdr = {
+               .cmd = cmd,
+               .version = version,
+       };
+
+       nlh = nlmsg_put(msg, pid, seq, family, GENL_HDRLEN + hdrlen, flags);
+       if (nlh == NULL)
+               return NULL;
+
+       memcpy(nlmsg_data(nlh), &hdr, sizeof(hdr));
+       NL_DBG(2, "msg %p: Added generic netlink header cmd=%d version=%d\n",
+              msg, cmd, version);
+
+       return nlmsg_data(nlh) + GENL_HDRLEN;
+}
+
+/** @} */
+
+/** @} */
diff --git a/package/libnl-tiny/src/genl_ctrl.c b/package/libnl-tiny/src/genl_ctrl.c
new file mode 100644 (file)
index 0000000..1301642
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * lib/genl/ctrl.c             Generic Netlink Controller
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup genl_mngt
+ * @defgroup ctrl Controller
+ * @brief
+ *
+ * @{
+ */
+
+#include <netlink-generic.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/mngt.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/utils.h>
+
+/** @cond SKIP */
+#define CTRL_VERSION           0x0001
+
+static struct nl_cache_ops genl_ctrl_ops;
+/** @endcond */
+
+static int ctrl_request_update(struct nl_cache *c, struct nl_sock *h)
+{
+       return genl_send_simple(h, GENL_ID_CTRL, CTRL_CMD_GETFAMILY,
+                               CTRL_VERSION, NLM_F_DUMP);
+}
+
+static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
+       [CTRL_ATTR_FAMILY_ID]   = { .type = NLA_U16 },
+       [CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING,
+                                   .maxlen = GENL_NAMSIZ },
+       [CTRL_ATTR_VERSION]     = { .type = NLA_U32 },
+       [CTRL_ATTR_HDRSIZE]     = { .type = NLA_U32 },
+       [CTRL_ATTR_MAXATTR]     = { .type = NLA_U32 },
+       [CTRL_ATTR_OPS]         = { .type = NLA_NESTED },
+};
+
+static struct nla_policy family_op_policy[CTRL_ATTR_OP_MAX+1] = {
+       [CTRL_ATTR_OP_ID]       = { .type = NLA_U32 },
+       [CTRL_ATTR_OP_FLAGS]    = { .type = NLA_U32 },
+};
+
+static int ctrl_msg_parser(struct nl_cache_ops *ops, struct genl_cmd *cmd,
+                          struct genl_info *info, void *arg)
+{
+       struct genl_family *family;
+       struct nl_parser_param *pp = arg;
+       int err;
+
+       family = genl_family_alloc();
+       if (family == NULL) {
+               err = -NLE_NOMEM;
+               goto errout;
+       }
+
+       if (info->attrs[CTRL_ATTR_FAMILY_NAME] == NULL) {
+               err = -NLE_MISSING_ATTR;
+               goto errout;
+       }
+
+       if (info->attrs[CTRL_ATTR_FAMILY_ID] == NULL) {
+               err = -NLE_MISSING_ATTR;
+               goto errout;
+       }
+
+       family->ce_msgtype = info->nlh->nlmsg_type;
+       genl_family_set_id(family,
+                          nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]));
+       genl_family_set_name(family,
+                    nla_get_string(info->attrs[CTRL_ATTR_FAMILY_NAME]));
+
+       if (info->attrs[CTRL_ATTR_VERSION]) {
+               uint32_t version = nla_get_u32(info->attrs[CTRL_ATTR_VERSION]);
+               genl_family_set_version(family, version);
+       }
+
+       if (info->attrs[CTRL_ATTR_HDRSIZE]) {
+               uint32_t hdrsize = nla_get_u32(info->attrs[CTRL_ATTR_HDRSIZE]);
+               genl_family_set_hdrsize(family, hdrsize);
+       }
+
+       if (info->attrs[CTRL_ATTR_MAXATTR]) {
+               uint32_t maxattr = nla_get_u32(info->attrs[CTRL_ATTR_MAXATTR]);
+               genl_family_set_maxattr(family, maxattr);
+       }
+
+       if (info->attrs[CTRL_ATTR_OPS]) {
+               struct nlattr *nla, *nla_ops;
+               int remaining;
+
+               nla_ops = info->attrs[CTRL_ATTR_OPS];
+               nla_for_each_nested(nla, nla_ops, remaining) {
+                       struct nlattr *tb[CTRL_ATTR_OP_MAX+1];
+                       int flags = 0, id;
+
+                       err = nla_parse_nested(tb, CTRL_ATTR_OP_MAX, nla,
+                                              family_op_policy);
+                       if (err < 0)
+                               goto errout;
+
+                       if (tb[CTRL_ATTR_OP_ID] == NULL) {
+                               err = -NLE_MISSING_ATTR;
+                               goto errout;
+                       }
+                       
+                       id = nla_get_u32(tb[CTRL_ATTR_OP_ID]);
+
+                       if (tb[CTRL_ATTR_OP_FLAGS])
+                               flags = nla_get_u32(tb[CTRL_ATTR_OP_FLAGS]);
+
+                       err = genl_family_add_op(family, id, flags);
+                       if (err < 0)
+                               goto errout;
+
+               }
+       }
+
+       err = pp->pp_cb((struct nl_object *) family, pp);
+errout:
+       genl_family_put(family);
+       return err;
+}
+
+/**
+ * @name Cache Management
+ * @{
+ */
+
+int genl_ctrl_alloc_cache(struct nl_sock *sock, struct nl_cache **result)
+{
+       return nl_cache_alloc_and_fill(&genl_ctrl_ops, sock, result);
+}
+
+/**
+ * Look up generic netlink family by id in the provided cache.
+ * @arg cache          Generic netlink family cache.
+ * @arg id             Family identifier.
+ *
+ * Searches through the cache looking for a registered family
+ * matching the specified identifier. The caller will own a
+ * reference on the returned object which needs to be given
+ * back after usage using genl_family_put().
+ *
+ * @return Generic netlink family object or NULL if no match was found.
+ */
+struct genl_family *genl_ctrl_search(struct nl_cache *cache, int id)
+{
+       struct genl_family *fam;
+
+       if (cache->c_ops != &genl_ctrl_ops)
+               BUG();
+
+       nl_list_for_each_entry(fam, &cache->c_items, ce_list) {
+               if (fam->gf_id == id) {
+                       nl_object_get((struct nl_object *) fam);
+                       return fam;
+               }
+       }
+
+       return NULL;
+}
+
+/**
+ * @name Resolver
+ * @{
+ */
+
+/**
+ * Look up generic netlink family by family name in the provided cache.
+ * @arg cache          Generic netlink family cache.
+ * @arg name           Family name.
+ *
+ * Searches through the cache looking for a registered family
+ * matching the specified name. The caller will own a reference
+ * on the returned object which needs to be given back after
+ * usage using genl_family_put().
+ *
+ * @return Generic netlink family object or NULL if no match was found.
+ */
+struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache,
+                                           const char *name)
+{
+       struct genl_family *fam;
+
+       if (cache->c_ops != &genl_ctrl_ops)
+               BUG();
+
+       nl_list_for_each_entry(fam, &cache->c_items, ce_list) {
+               if (!strcmp(name, fam->gf_name)) {
+                       nl_object_get((struct nl_object *) fam);
+                       return fam;
+               }
+       }
+
+       return NULL;
+}
+
+/** @} */
+
+/**
+ * Resolve generic netlink family name to its identifier
+ * @arg sk             Netlink socket.
+ * @arg name           Name of generic netlink family
+ *
+ * Resolves the generic netlink family name to its identifer and returns
+ * it.
+ *
+ * @return A positive identifier or a negative error code.
+ */
+int genl_ctrl_resolve(struct nl_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);
+       if (family == NULL) {
+               err = -NLE_OBJ_NOTFOUND;
+               goto errout;
+       }
+
+       err = genl_family_get_id(family);
+       genl_family_put(family);
+errout:
+       nl_cache_free(cache);
+
+       return err;
+}
+
+/** @} */
+
+static struct genl_cmd genl_cmds[] = {
+       {
+               .c_id           = CTRL_CMD_NEWFAMILY,
+               .c_name         = "NEWFAMILY" ,
+               .c_maxattr      = CTRL_ATTR_MAX,
+               .c_attr_policy  = ctrl_policy,
+               .c_msg_parser   = ctrl_msg_parser,
+       },
+       {
+               .c_id           = CTRL_CMD_DELFAMILY,
+               .c_name         = "DELFAMILY" ,
+       },
+       {
+               .c_id           = CTRL_CMD_GETFAMILY,
+               .c_name         = "GETFAMILY" ,
+       },
+       {
+               .c_id           = CTRL_CMD_NEWOPS,
+               .c_name         = "NEWOPS" ,
+       },
+       {
+               .c_id           = CTRL_CMD_DELOPS,
+               .c_name         = "DELOPS" ,
+       },
+};
+
+static struct genl_ops genl_ops = {
+       .o_cmds                 = genl_cmds,
+       .o_ncmds                = ARRAY_SIZE(genl_cmds),
+};
+
+/** @cond SKIP */
+extern struct nl_object_ops genl_family_ops;
+/** @endcond */
+
+static struct nl_cache_ops genl_ctrl_ops = {
+       .co_name                = "genl/family",
+       .co_hdrsize             = GENL_HDRSIZE(0),
+       .co_msgtypes            = GENL_FAMILY(GENL_ID_CTRL, "nlctrl"),
+       .co_genl                = &genl_ops,
+       .co_protocol            = NETLINK_GENERIC,
+       .co_request_update      = ctrl_request_update,
+       .co_obj_ops             = &genl_family_ops,
+};
+
+static void __init ctrl_init(void)
+{
+       genl_register(&genl_ctrl_ops);
+}
+
+static void __exit ctrl_exit(void)
+{
+       genl_unregister(&genl_ctrl_ops);
+}
+
+/** @} */
diff --git a/package/libnl-tiny/src/genl_family.c b/package/libnl-tiny/src/genl_family.c
new file mode 100644 (file)
index 0000000..88aaad9
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * lib/genl/family.c           Generic Netlink Family
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup genl
+ * @defgroup genl_family Generic Netlink Family
+ * @brief
+ *
+ * @{
+ */
+
+#include <netlink-generic.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/utils.h>
+
+struct nl_object_ops genl_family_ops;
+/** @endcond */
+
+static void family_constructor(struct nl_object *c)
+{
+       struct genl_family *family = (struct genl_family *) c;
+
+       nl_init_list_head(&family->gf_ops);
+}
+
+static void family_free_data(struct nl_object *c)
+{
+       struct genl_family *family = (struct genl_family *) c;
+       struct genl_family_op *ops, *tmp;
+
+       if (family == NULL)
+               return;
+
+       nl_list_for_each_entry_safe(ops, tmp, &family->gf_ops, o_list) {
+               nl_list_del(&ops->o_list);
+               free(ops);
+       }
+}
+
+static int family_clone(struct nl_object *_dst, struct nl_object *_src)
+{
+       struct genl_family *dst = nl_object_priv(_dst);
+       struct genl_family *src = nl_object_priv(_src);
+       struct genl_family_op *ops;
+       int err;
+
+       nl_list_for_each_entry(ops, &src->gf_ops, o_list) {
+               err = genl_family_add_op(dst, ops->o_id, ops->o_flags);
+               if (err < 0)
+                       return err;
+       }
+       
+       return 0;
+}
+
+static int family_compare(struct nl_object *_a, struct nl_object *_b,
+                         uint32_t attrs, int flags)
+{
+       struct genl_family *a = (struct genl_family *) _a;
+       struct genl_family *b = (struct genl_family *) _b;
+       int diff = 0;
+
+#define FAM_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, FAMILY_ATTR_##ATTR, a, b, EXPR)
+
+       diff |= FAM_DIFF(ID,            a->gf_id != b->gf_id);
+       diff |= FAM_DIFF(VERSION,       a->gf_version != b->gf_version);
+       diff |= FAM_DIFF(HDRSIZE,       a->gf_hdrsize != b->gf_hdrsize);
+       diff |= FAM_DIFF(MAXATTR,       a->gf_maxattr != b->gf_maxattr);
+       diff |= FAM_DIFF(NAME,          strcmp(a->gf_name, b->gf_name));
+
+#undef FAM_DIFF
+
+       return diff;
+}
+
+
+/**
+ * @name Family Object
+ * @{
+ */
+
+struct genl_family *genl_family_alloc(void)
+{
+       return (struct genl_family *) nl_object_alloc(&genl_family_ops);
+}
+
+void genl_family_put(struct genl_family *family)
+{
+       nl_object_put((struct nl_object *) family);
+}
+
+/** @} */
+
+
+int genl_family_add_op(struct genl_family *family, int id, int flags)
+{
+       struct genl_family_op *op;
+
+       op = calloc(1, sizeof(*op));
+       if (op == NULL)
+               return -NLE_NOMEM;
+
+       op->o_id = id;
+       op->o_flags = flags;
+
+       nl_list_add_tail(&op->o_list, &family->gf_ops);
+       family->ce_mask |= FAMILY_ATTR_OPS;
+
+       return 0;
+}
+
+/** @} */
+
+/** @cond SKIP */
+struct nl_object_ops genl_family_ops = {
+       .oo_name                = "genl/family",
+       .oo_size                = sizeof(struct genl_family),
+       .oo_constructor         = family_constructor,
+       .oo_free_data           = family_free_data,
+       .oo_clone               = family_clone,
+       .oo_compare             = family_compare,
+       .oo_id_attrs            = FAMILY_ATTR_ID,
+};
+/** @endcond */
+
+/** @} */
diff --git a/package/libnl-tiny/src/genl_mngt.c b/package/libnl-tiny/src/genl_mngt.c
new file mode 100644 (file)
index 0000000..5df9a6e
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * lib/genl/mngt.c             Generic Netlink Management
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup genl
+ * @defgroup genl_mngt Management
+ *
+ * @par 1) Registering a generic netlink module
+ * @code
+ * #include <netlink/genl/mngt.h>
+ *
+ * // 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/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/mngt.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/utils.h>
+
+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)
+{
+       int i, err;
+       struct genlmsghdr *ghdr;
+       struct genl_cmd *cmd;
+
+       ghdr = nlmsg_data(nlh);
+
+       if (ops->co_genl == NULL)
+               BUG();
+
+       for (i = 0; i < ops->co_genl->o_ncmds; i++) {
+               cmd = &ops->co_genl->o_cmds[i];
+               if (cmd->c_id == ghdr->cmd)
+                       goto found;
+       }
+
+       err = -NLE_MSGTYPE_NOSUPPORT;
+       goto errout;
+
+found:
+       if (cmd->c_msg_parser == NULL)
+               err = -NLE_OPNOTSUPP;
+       else {
+               struct nlattr *tb[cmd->c_maxattr + 1];
+               struct genl_info info = {
+                       .who = who,
+                       .nlh = nlh,
+                       .genlhdr = ghdr,
+                       .userhdr = genlmsg_data(ghdr),
+                       .attrs = tb,
+               };
+
+               err = nlmsg_parse(nlh, ops->co_hdrsize, tb, cmd->c_maxattr,
+                                 cmd->c_attr_policy);
+               if (err < 0)
+                       goto errout;
+
+               err = cmd->c_msg_parser(ops, cmd, &info, pp);
+       }
+errout:
+       return err;
+
+}
+
+char *genl_op2name(int family, int op, char *buf, size_t len)
+{
+       struct genl_ops *ops;
+       int i;
+
+       nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
+               if (ops->o_family == family) {
+                       for (i = 0; i < ops->o_ncmds; i++) {
+                               struct genl_cmd *cmd;
+                               cmd = &ops->o_cmds[i];
+
+                               if (cmd->c_id == op) {
+                                       strncpy(buf, cmd->c_name, len - 1);
+                                       return buf;
+                               }
+                       }
+               }
+       }
+
+       strncpy(buf, "unknown", len - 1);
+       return NULL;
+}
+
+
+/**
+ * @name Register/Unregister
+ * @{
+ */
+
+/**
+ * Register generic netlink operations
+ * @arg ops            cache operations
+ */
+int genl_register(struct nl_cache_ops *ops)
+{
+       int err;
+
+       if (ops->co_protocol != NETLINK_GENERIC) {
+               err = -NLE_PROTO_MISMATCH;
+               goto errout;
+       }
+
+       if (ops->co_hdrsize < GENL_HDRSIZE(0)) {
+               err = -NLE_INVAL;
+               goto errout;
+       }
+
+       if (ops->co_genl == NULL) {
+               err = -NLE_INVAL;
+               goto errout;
+       }
+
+       ops->co_genl->o_cache_ops = ops;
+       ops->co_genl->o_name = ops->co_msgtypes[0].mt_name;
+       ops->co_genl->o_family = ops->co_msgtypes[0].mt_id;
+       ops->co_msg_parser = genl_msg_parser;
+
+       /* FIXME: check for dup */
+
+       nl_list_add_tail(&ops->co_genl->o_list, &genl_ops_list);
+
+       err = nl_cache_mngt_register(ops);
+errout:
+       return err;
+}
+
+/**
+ * Unregister generic netlink operations
+ * @arg ops            cache operations
+ */
+void genl_unregister(struct nl_cache_ops *ops)
+{
+       nl_cache_mngt_unregister(ops);
+       nl_list_del(&ops->co_genl->o_list);
+}
+
+/** @} */
+
+/**
+ * @name Resolving ID/Name
+ * @{
+ */
+#ifdef disabled
+static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops)
+{
+       struct genl_family *family;
+
+       family = genl_ctrl_search_by_name(ctrl, ops->o_name);
+       if (family != NULL) {
+               ops->o_id = genl_family_get_id(family);
+               genl_family_put(family);
+
+               return 0;
+       }
+
+       return -NLE_OBJ_NOTFOUND;
+}
+
+int genl_ops_resolve(struct nl_sock *sk, struct genl_ops *ops)
+{
+       struct nl_cache *ctrl;
+       int err;
+
+       if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0)
+               goto errout;
+
+       err = __genl_ops_resolve(ctrl, ops);
+
+       nl_cache_free(ctrl);
+errout:
+       return err;
+}
+
+int genl_mngt_resolve(struct nl_sock *sk)
+{
+       struct nl_cache *ctrl;
+       struct genl_ops *ops;
+       int err = 0;
+
+       if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0)
+               goto errout;
+
+       nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
+               err = __genl_ops_resolve(ctrl, ops);
+       }
+
+       nl_cache_free(ctrl);
+errout:
+       return err;
+}
+#endif
+/** @} */
+
+
+/** @} */
diff --git a/package/libnl-tiny/src/handlers.c b/package/libnl-tiny/src/handlers.c
new file mode 100644 (file)
index 0000000..12ebda5
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * lib/handlers.c      default netlink message handlers
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @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);
+ *
+ * // Modify the set to call my_func() for all valid messages
+ * nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL);
+ *
+ * // Set the error message handler to the verbose default implementation
+ * // and direct it to print all errors to the given file descriptor.
+ * FILE *file = fopen(...);
+ * nl_cb_err(cb, NL_CB_VERBOSE, NULL, file);
+ * @endcode
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/msg.h>
+#include <netlink/handlers.h>
+
+/**
+ * @name Callback Handle Management
+ * @{
+ */
+
+/**
+ * Allocate a new callback handle
+ * @arg kind           callback kind to be used for initialization
+ * @return Newly allocated callback handle or NULL
+ */
+struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind)
+{
+       int i;
+       struct nl_cb *cb;
+
+       if (kind < 0 || kind > NL_CB_KIND_MAX)
+               return NULL;
+
+       cb = calloc(1, sizeof(*cb));
+       if (!cb)
+               return NULL;
+
+       cb->cb_refcnt = 1;
+
+       for (i = 0; i <= NL_CB_TYPE_MAX; i++)
+               nl_cb_set(cb, i, kind, NULL, NULL);
+
+       nl_cb_err(cb, kind, NULL, NULL);
+
+       return cb;
+}
+
+/**
+ * Clone an existing callback handle
+ * @arg orig           original callback handle
+ * @return Newly allocated callback handle being a duplicate of
+ *         orig or NULL
+ */
+struct nl_cb *nl_cb_clone(struct nl_cb *orig)
+{
+       struct nl_cb *cb;
+       
+       cb = nl_cb_alloc(NL_CB_DEFAULT);
+       if (!cb)
+               return NULL;
+
+       memcpy(cb, orig, sizeof(*orig));
+       cb->cb_refcnt = 1;
+
+       return cb;
+}
+
+void nl_cb_put(struct nl_cb *cb)
+{
+       if (!cb)
+               return;
+
+       cb->cb_refcnt--;
+
+       if (cb->cb_refcnt < 0)
+               BUG();
+
+       if (cb->cb_refcnt <= 0)
+               free(cb);
+}
+
+/** @} */
+
+/**
+ * @name Callback Setup
+ * @{
+ */
+
+/**
+ * Set up a callback 
+ * @arg cb             callback set
+ * @arg type           callback to modify
+ * @arg kind           kind of implementation
+ * @arg func           callback function (NL_CB_CUSTOM)
+ * @arg arg            argument passed to callback
+ *
+ * @return 0 on success or a negative error code
+ */
+int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind,
+             nl_recvmsg_msg_cb_t func, void *arg)
+{
+       if (type < 0 || type > NL_CB_TYPE_MAX)
+               return -NLE_RANGE;
+
+       if (kind < 0 || kind > NL_CB_KIND_MAX)
+               return -NLE_RANGE;
+
+       if (kind == NL_CB_CUSTOM) {
+               cb->cb_set[type] = func;
+               cb->cb_args[type] = arg;
+       }
+
+       return 0;
+}
+
+/**
+ * Set up an error callback
+ * @arg cb             callback set
+ * @arg kind           kind of callback
+ * @arg func           callback function
+ * @arg arg            argument to be passed to callback function
+ */
+int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind,
+             nl_recvmsg_err_cb_t func, void *arg)
+{
+       if (kind < 0 || kind > NL_CB_KIND_MAX)
+               return -NLE_RANGE;
+
+       if (kind == NL_CB_CUSTOM) {
+               cb->cb_err = func;
+               cb->cb_err_arg = arg;
+       }
+
+       return 0;
+}
+
+/** @} */
+
+/** @} */
diff --git a/package/libnl-tiny/src/include/linux/genetlink.h b/package/libnl-tiny/src/include/linux/genetlink.h
new file mode 100644 (file)
index 0000000..b834ef6
--- /dev/null
@@ -0,0 +1,83 @@
+#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 */
+
+#define GENL_MIN_ID    NLMSG_MIN_TYPE
+#define GENL_MAX_ID    1023
+
+struct genlmsghdr {
+       __u8    cmd;
+       __u8    version;
+       __u16   reserved;
+};
+
+#define GENL_HDRLEN    NLMSG_ALIGN(sizeof(struct genlmsghdr))
+
+#define GENL_ADMIN_PERM                0x01
+#define GENL_CMD_CAP_DO                0x02
+#define GENL_CMD_CAP_DUMP      0x04
+#define GENL_CMD_CAP_HASPOL    0x08
+
+/*
+ * List of reserved static generic netlink identifiers:
+ */
+#define GENL_ID_GENERATE       0
+#define GENL_ID_CTRL           NLMSG_MIN_TYPE
+
+/**************************************************************************
+ * Controller
+ **************************************************************************/
+
+enum {
+       CTRL_CMD_UNSPEC,
+       CTRL_CMD_NEWFAMILY,
+       CTRL_CMD_DELFAMILY,
+       CTRL_CMD_GETFAMILY,
+       CTRL_CMD_NEWOPS,
+       CTRL_CMD_DELOPS,
+       CTRL_CMD_GETOPS,
+       CTRL_CMD_NEWMCAST_GRP,
+       CTRL_CMD_DELMCAST_GRP,
+       CTRL_CMD_GETMCAST_GRP, /* unused */
+       __CTRL_CMD_MAX,
+};
+
+#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1)
+
+enum {
+       CTRL_ATTR_UNSPEC,
+       CTRL_ATTR_FAMILY_ID,
+       CTRL_ATTR_FAMILY_NAME,
+       CTRL_ATTR_VERSION,
+       CTRL_ATTR_HDRSIZE,
+       CTRL_ATTR_MAXATTR,
+       CTRL_ATTR_OPS,
+       CTRL_ATTR_MCAST_GROUPS,
+       __CTRL_ATTR_MAX,
+};
+
+#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1)
+
+enum {
+       CTRL_ATTR_OP_UNSPEC,
+       CTRL_ATTR_OP_ID,
+       CTRL_ATTR_OP_FLAGS,
+       __CTRL_ATTR_OP_MAX,
+};
+
+#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1)
+
+enum {
+       CTRL_ATTR_MCAST_GRP_UNSPEC,
+       CTRL_ATTR_MCAST_GRP_NAME,
+       CTRL_ATTR_MCAST_GRP_ID,
+       __CTRL_ATTR_MCAST_GRP_MAX,
+};
+
+#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)
+
+#endif /* __LINUX_GENERIC_NETLINK_H */
diff --git a/package/libnl-tiny/src/include/linux/if.h b/package/libnl-tiny/src/include/linux/if.h
new file mode 100644 (file)
index 0000000..4c1bcfe
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * INET                An implementation of the TCP/IP protocol suite for the LINUX
+ *             operating system.  INET is implemented using the  BSD Socket
+ *             interface as the means of communication with the user level.
+ *
+ *             Global definitions for the INET interface module.
+ *
+ * Version:    @(#)if.h        1.0.2   04/18/93
+ *
+ * Authors:    Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988
+ *             Ross Biro
+ *             Fred N. van Kempen, <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 _LINUX_IF_H
+#define _LINUX_IF_H
+
+#include <linux/types.h>               /* for "__kernel_caddr_t" et al */
+
+#define        IFNAMSIZ        16
+
+/* Standard interface flags (netdevice->flags). */
+#define        IFF_UP          0x1             /* interface is up              */
+#define        IFF_BROADCAST   0x2             /* broadcast address valid      */
+#define        IFF_DEBUG       0x4             /* turn on debugging            */
+#define        IFF_LOOPBACK    0x8             /* is a loopback net            */
+#define        IFF_POINTOPOINT 0x10            /* interface is has p-p link    */
+#define        IFF_NOTRAILERS  0x20            /* avoid use of trailers        */
+#define        IFF_RUNNING     0x40            /* interface RFC2863 OPER_UP    */
+#define        IFF_NOARP       0x80            /* no ARP protocol              */
+#define        IFF_PROMISC     0x100           /* receive all packets          */
+#define        IFF_ALLMULTI    0x200           /* receive all multicast packets*/
+
+#define IFF_MASTER     0x400           /* master of a load balancer    */
+#define IFF_SLAVE      0x800           /* slave of a load balancer     */
+
+#define IFF_MULTICAST  0x1000          /* Supports multicast           */
+
+#define IFF_PORTSEL    0x2000          /* can set media type           */
+#define IFF_AUTOMEDIA  0x4000          /* auto media select active     */
+#define IFF_DYNAMIC    0x8000          /* dialup device with changing addresses*/
+
+#define IFF_LOWER_UP   0x10000         /* driver signals L1 up         */
+#define IFF_DORMANT    0x20000         /* driver signals dormant       */
+
+#define IFF_ECHO       0x40000         /* echo sent packets            */
+
+#define IFF_VOLATILE   (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\
+               IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
+
+/* Private (from user) interface flags (netdevice->priv_flags). */
+#define IFF_802_1Q_VLAN 0x1             /* 802.1Q VLAN device.          */
+#define IFF_EBRIDGE    0x2             /* Ethernet bridging device.    */
+#define IFF_SLAVE_INACTIVE     0x4     /* bonding slave not the curr. active */
+#define IFF_MASTER_8023AD      0x8     /* bonding master, 802.3ad.     */
+#define IFF_MASTER_ALB 0x10            /* bonding master, balance-alb. */
+#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 IF_GET_IFACE   0x0001          /* for querying only */
+#define IF_GET_PROTO   0x0002
+
+/* For definitions see hdlc.h */
+#define IF_IFACE_V35   0x1000          /* V.35 serial interface        */
+#define IF_IFACE_V24   0x1001          /* V.24 serial interface        */
+#define IF_IFACE_X21   0x1002          /* X.21 serial interface        */
+#define IF_IFACE_T1    0x1003          /* T1 telco serial interface    */
+#define IF_IFACE_E1    0x1004          /* E1 telco serial interface    */
+#define IF_IFACE_SYNC_SERIAL 0x1005    /* can't be set by software     */
+#define IF_IFACE_X21D   0x1006          /* X.21 Dual Clocking (FarSite) */
+
+/* For definitions see hdlc.h */
+#define IF_PROTO_HDLC  0x2000          /* raw HDLC protocol            */
+#define IF_PROTO_PPP   0x2001          /* PPP protocol                 */
+#define IF_PROTO_CISCO 0x2002          /* Cisco HDLC protocol          */
+#define IF_PROTO_FR    0x2003          /* Frame Relay protocol         */
+#define IF_PROTO_FR_ADD_PVC 0x2004     /*    Create FR PVC             */
+#define IF_PROTO_FR_DEL_PVC 0x2005     /*    Delete FR PVC             */
+#define IF_PROTO_X25   0x2006          /* X.25                         */
+#define IF_PROTO_HDLC_ETH 0x2007       /* raw HDLC, Ethernet emulation */
+#define IF_PROTO_FR_ADD_ETH_PVC 0x2008 /*  Create FR Ethernet-bridged PVC */
+#define IF_PROTO_FR_DEL_ETH_PVC 0x2009 /*  Delete FR Ethernet-bridged PVC */
+#define IF_PROTO_FR_PVC        0x200A          /* for reading PVC status       */
+#define IF_PROTO_FR_ETH_PVC 0x200B
+#define IF_PROTO_RAW    0x200C          /* RAW Socket                   */
+
+/* RFC 2863 operational status */
+enum {
+       IF_OPER_UNKNOWN,
+       IF_OPER_NOTPRESENT,
+       IF_OPER_DOWN,
+       IF_OPER_LOWERLAYERDOWN,
+       IF_OPER_TESTING,
+       IF_OPER_DORMANT,
+       IF_OPER_UP,
+};
+
+/* link modes */
+enum {
+       IF_LINK_MODE_DEFAULT,
+       IF_LINK_MODE_DORMANT,   /* limit upward transition to dormant */
+};
+
+/*
+ *     Device mapping structure. I'd just gone off and designed a 
+ *     beautiful scheme using only loadable modules with arguments
+ *     for driver options and along come the PCMCIA people 8)
+ *
+ *     Ah well. The get() side of this is good for WDSETUP, and it'll
+ *     be handy for debugging things. The set side is fine for now and
+ *     being very small might be worth keeping for clean configuration.
+ */
+
+struct ifmap 
+{
+       unsigned long mem_start;
+       unsigned long mem_end;
+       unsigned short base_addr; 
+       unsigned char irq;
+       unsigned char dma;
+       unsigned char port;
+       /* 3 bytes spare */
+};
+
+
+#endif /* _LINUX_IF_H */
diff --git a/package/libnl-tiny/src/include/linux/if_addr.h b/package/libnl-tiny/src/include/linux/if_addr.h
new file mode 100644 (file)
index 0000000..43f3bed
--- /dev/null
@@ -0,0 +1,62 @@
+#ifndef __LINUX_IF_ADDR_H
+#define __LINUX_IF_ADDR_H
+
+#include <linux/netlink.h>
+
+struct ifaddrmsg
+{
+       __u8            ifa_family;
+       __u8            ifa_prefixlen;  /* The prefix length            */
+       __u8            ifa_flags;      /* Flags                        */
+       __u8            ifa_scope;      /* Address scope                */
+       __u32           ifa_index;      /* Link index                   */
+};
+
+/*
+ * Important comment:
+ * IFA_ADDRESS is prefix address, rather than local interface address.
+ * It makes no difference for normally configured broadcast interfaces,
+ * but for point-to-point IFA_ADDRESS is DESTINATION address,
+ * local address is supplied in IFA_LOCAL attribute.
+ */
+enum
+{
+       IFA_UNSPEC,
+       IFA_ADDRESS,
+       IFA_LOCAL,
+       IFA_LABEL,
+       IFA_BROADCAST,
+       IFA_ANYCAST,
+       IFA_CACHEINFO,
+       IFA_MULTICAST,
+       __IFA_MAX,
+};
+
+#define IFA_MAX (__IFA_MAX - 1)
+
+/* ifa_flags */
+#define IFA_F_SECONDARY                0x01
+#define IFA_F_TEMPORARY                IFA_F_SECONDARY
+
+#define        IFA_F_NODAD             0x02
+#define IFA_F_OPTIMISTIC       0x04
+#define        IFA_F_HOMEADDRESS       0x10
+#define IFA_F_DEPRECATED       0x20
+#define IFA_F_TENTATIVE                0x40
+#define IFA_F_PERMANENT                0x80
+
+struct ifa_cacheinfo
+{
+       __u32   ifa_prefered;
+       __u32   ifa_valid;
+       __u32   cstamp; /* created timestamp, hundredths of seconds */
+       __u32   tstamp; /* updated timestamp, hundredths of seconds */
+};
+
+/* backwards compatibility for userspace */
+#ifndef __KERNEL__
+#define IFA_RTA(r)  ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/linux/netlink.h b/package/libnl-tiny/src/include/linux/netlink.h
new file mode 100644 (file)
index 0000000..d252103
--- /dev/null
@@ -0,0 +1,150 @@
+#ifndef __LINUX_NETLINK_H
+#define __LINUX_NETLINK_H
+
+#include <linux/socket.h> /* for sa_family_t */
+#include <linux/types.h>
+
+#define NETLINK_ROUTE          0       /* Routing/device hook                          */
+#define NETLINK_UNUSED         1       /* Unused number                                */
+#define NETLINK_USERSOCK       2       /* Reserved for user mode socket protocols      */
+#define NETLINK_FIREWALL       3       /* Firewalling hook                             */
+#define NETLINK_INET_DIAG      4       /* INET socket monitoring                       */
+#define NETLINK_NFLOG          5       /* netfilter/iptables ULOG */
+#define NETLINK_XFRM           6       /* ipsec */
+#define NETLINK_SELINUX                7       /* SELinux event notifications */
+#define NETLINK_ISCSI          8       /* Open-iSCSI */
+#define NETLINK_AUDIT          9       /* auditing */
+#define NETLINK_FIB_LOOKUP     10      
+#define NETLINK_CONNECTOR      11
+#define NETLINK_NETFILTER      12      /* netfilter subsystem */
+#define NETLINK_IP6_FW         13
+#define NETLINK_DNRTMSG                14      /* DECnet routing messages */
+#define NETLINK_KOBJECT_UEVENT 15      /* Kernel messages to userspace */
+#define NETLINK_GENERIC                16
+/* leave room for NETLINK_DM (DM Events) */
+#define NETLINK_SCSITRANSPORT  18      /* SCSI Transports */
+#define NETLINK_ECRYPTFS       19
+
+#define MAX_LINKS 32           
+
+struct sockaddr_nl
+{
+       sa_family_t     nl_family;      /* AF_NETLINK   */
+       unsigned short  nl_pad;         /* zero         */
+       __u32           nl_pid;         /* port ID      */
+               __u32           nl_groups;      /* multicast groups mask */
+};
+
+struct nlmsghdr
+{
+       __u32           nlmsg_len;      /* Length of message including header */
+       __u16           nlmsg_type;     /* Message content */
+       __u16           nlmsg_flags;    /* Additional flags */
+       __u32           nlmsg_seq;      /* Sequence number */
+       __u32           nlmsg_pid;      /* Sending process port ID */
+};
+
+/* Flags values */
+
+#define NLM_F_REQUEST          1       /* It is request message.       */
+#define NLM_F_MULTI            2       /* Multipart message, terminated by NLMSG_DONE */
+#define NLM_F_ACK              4       /* Reply with ack, with zero or error code */
+#define NLM_F_ECHO             8       /* Echo this request            */
+
+/* Modifiers to GET request */
+#define NLM_F_ROOT     0x100   /* specify tree root    */
+#define NLM_F_MATCH    0x200   /* return all matching  */
+#define NLM_F_ATOMIC   0x400   /* atomic GET           */
+#define NLM_F_DUMP     (NLM_F_ROOT|NLM_F_MATCH)
+
+/* Modifiers to NEW request */
+#define NLM_F_REPLACE  0x100   /* Override existing            */
+#define NLM_F_EXCL     0x200   /* Do not touch, if it exists   */
+#define NLM_F_CREATE   0x400   /* Create, if it does not exist */
+#define NLM_F_APPEND   0x800   /* Add to end of list           */
+
+/*
+   4.4BSD ADD          NLM_F_CREATE|NLM_F_EXCL
+   4.4BSD CHANGE       NLM_F_REPLACE
+
+   True CHANGE         NLM_F_CREATE|NLM_F_REPLACE
+   Append              NLM_F_CREATE
+   Check               NLM_F_EXCL
+ */
+
+#define NLMSG_ALIGNTO  4
+#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
+#define NLMSG_HDRLEN    ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN))
+#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
+#define NLMSG_NEXT(nlh,len)     ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
+                                 (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
+#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
+                          (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+                          (nlh)->nlmsg_len <= (len))
+#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define NLMSG_NOOP             0x1     /* Nothing.             */
+#define NLMSG_ERROR            0x2     /* Error                */
+#define NLMSG_DONE             0x3     /* End of a dump        */
+#define NLMSG_OVERRUN          0x4     /* Data lost            */
+
+#define NLMSG_MIN_TYPE         0x10    /* < 0x10: reserved control messages */
+
+struct nlmsgerr
+{
+       int             error;
+       struct nlmsghdr msg;
+};
+
+#define NETLINK_ADD_MEMBERSHIP 1
+#define NETLINK_DROP_MEMBERSHIP        2
+#define NETLINK_PKTINFO                3
+
+struct nl_pktinfo
+{
+       __u32   group;
+};
+
+#define NET_MAJOR 36           /* Major 36 is reserved for networking                                          */
+
+enum {
+       NETLINK_UNCONNECTED = 0,
+       NETLINK_CONNECTED,
+};
+
+/*
+ *  <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ * |        Header       | Pad |     Payload       | Pad |
+ * |   (struct nlattr)   | ing |                   | ing |
+ * +---------------------+- - -+- - - - - - - - - -+- - -+
+ *  <-------------- nlattr->nla_len -------------->
+ */
+
+struct nlattr
+{
+       __u16           nla_len;
+       __u16           nla_type;
+};
+
+/*
+ * nla_type (16 bits)
+ * +---+---+-------------------------------+
+ * | N | O | Attribute Type                |
+ * +---+---+-------------------------------+
+ * N := Carries nested attributes
+ * O := Payload stored in network byte order
+ *
+ * Note: The N and O flag are mutually exclusive.
+ */
+#define NLA_F_NESTED           (1 << 15)
+#define NLA_F_NET_BYTEORDER    (1 << 14)
+#define NLA_TYPE_MASK          ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
+
+#define NLA_ALIGNTO            4
+#define NLA_ALIGN(len)         (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
+#define NLA_HDRLEN             ((int) NLA_ALIGN(sizeof(struct nlattr)))
+
+#endif /* __LINUX_NETLINK_H */
diff --git a/package/libnl-tiny/src/include/netlink-generic.h b/package/libnl-tiny/src/include/netlink-generic.h
new file mode 100644 (file)
index 0000000..10aa2f0
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * netlink-generic.h   Local Generic Netlink Interface
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_GENL_PRIV_H_
+#define NETLINK_GENL_PRIV_H_
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+
+#define GENL_HDRSIZE(hdrlen) (GENL_HDRLEN + (hdrlen))
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink-local.h b/package/libnl-tiny/src/include/netlink-local.h
new file mode 100644 (file)
index 0000000..a2000fa
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * 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_
+#define _GNU_SOURCE
+
+#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/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 <netlink/netlink.h>
+#include <netlink/handlers.h>
+#include <netlink/cache.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 {} 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 offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+#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; })
+
+extern int nl_cache_parse(struct nl_cache_ops *, struct sockaddr_nl *,
+                         struct nlmsghdr *, struct nl_parser_param *);
+
+
+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/package/libnl-tiny/src/include/netlink-types.h b/package/libnl-tiny/src/include/netlink-types.h
new file mode 100644 (file)
index 0000000..0d00593
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * netlink-types.h     Netlink Types (Private)
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_LOCAL_TYPES_H_
+#define NETLINK_LOCAL_TYPES_H_
+
+#include <netlink/list.h>
+
+struct nl_cache_ops;
+struct nl_sock;
+struct nl_object;
+
+struct nl_cache
+{
+       struct nl_list_head     c_items;
+       int                     c_nitems;
+       int                     c_iarg1;
+       int                     c_iarg2;
+       struct nl_cache_ops *   c_ops;
+};
+
+struct nl_cache_assoc
+{
+       struct nl_cache *       ca_cache;
+       change_func_t           ca_change;
+};
+
+struct nl_cache_mngr
+{
+       int                     cm_protocol;
+       int                     cm_flags;
+       int                     cm_nassocs;
+       struct nl_sock *        cm_handle;
+       struct nl_cache_assoc * cm_assocs;
+};
+
+struct nl_parser_param;
+
+#define LOOSE_COMPARISON       1
+
+
+struct nl_data
+{
+       size_t                  d_size;
+       void *                  d_data;
+};
+
+struct nl_addr
+{
+       int                     a_family;
+       unsigned int            a_maxsize;
+       unsigned int            a_len;
+       int                     a_prefixlen;
+       int                     a_refcnt;
+       char                    a_addr[0];
+};
+
+#define IFQDISCSIZ     32
+
+#define GENL_OP_HAS_POLICY     1
+#define GENL_OP_HAS_DOIT       2
+#define GENL_OP_HAS_DUMPIT     4
+
+struct genl_family_op
+{
+       uint32_t                o_id;
+       uint32_t                o_flags;
+
+       struct nl_list_head     o_list;
+};
+
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/addr.h b/package/libnl-tiny/src/include/netlink/addr.h
new file mode 100644 (file)
index 0000000..cc3d201
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * netlink/addr.h              Abstract Address
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_ADDR_H_
+#define NETLINK_ADDR_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nl_addr;
+
+/* Creation */
+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 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 *);
+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);
+
+/* Access Functions */
+extern void            nl_addr_set_family(struct nl_addr *, int);
+extern int             nl_addr_get_family(struct nl_addr *);
+extern int             nl_addr_set_binary_addr(struct nl_addr *, void *,
+                                               size_t);
+extern void *          nl_addr_get_binary_addr(struct nl_addr *);
+extern unsigned int    nl_addr_get_len(struct nl_addr *);
+extern void            nl_addr_set_prefixlen(struct nl_addr *, int);
+extern unsigned int    nl_addr_get_prefixlen(struct nl_addr *);
+
+/* Address Family Translations */
+extern char *          nl_af2str(int, char *, size_t);
+extern int             nl_str2af(const char *);
+
+/* Translations to Strings */
+extern char *          nl_addr2str(struct nl_addr *, char *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/attr.h b/package/libnl-tiny/src/include/netlink/attr.h
new file mode 100644 (file)
index 0000000..7076d67
--- /dev/null
@@ -0,0 +1,726 @@
+/*
+ * netlink/attr.h              Netlink Attributes
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_ATTR_H_
+#define NETLINK_ATTR_H_
+
+#include <netlink/netlink.h>
+#include <netlink/object.h>
+#include <netlink/addr.h>
+#include <netlink/data.h>
+#include <netlink/msg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nl_msg;
+
+/**
+ * @name Basic Attribute Data Types
+ * @{
+ */
+
+ /**
+  * @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;
+};
+
+/* Attribute parsing */
+extern int             nla_ok(const struct nlattr *, int);
+extern struct nlattr * nla_next(const struct nlattr *, int *);
+extern int             nla_parse(struct nlattr **, int, struct nlattr *,
+                                 int, struct nla_policy *);
+extern int             nla_validate(struct nlattr *, int, int,
+                                    struct nla_policy *);
+extern struct nlattr * nla_find(struct nlattr *, int, int);
+
+/* Unspecific attribute */
+extern struct nlattr * nla_reserve(struct nl_msg *, int, int);
+extern int             nla_put(struct nl_msg *, int, int, const void *);
+
+/**
+ * nlmsg_find_attr - find a specific attribute in a netlink message
+ * @arg nlh            netlink message header
+ * @arg hdrlen         length of familiy specific header
+ * @arg attrtype       type of attribute to look for
+ *
+ * Returns the first attribute which matches the specified type.
+ */
+static inline struct nlattr *nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype)
+{
+       return nla_find(nlmsg_attrdata(nlh, hdrlen),
+                       nlmsg_attrlen(nlh, hdrlen), attrtype);
+}
+
+
+/**
+ * Return size of attribute whithout padding.
+ * @arg payload                Payload length of attribute.
+ *
+ * @code
+ *    <-------- nla_attr_size(payload) --------->
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ *   | Attribute Header | Pad |     Payload      | Pad |
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ * @endcode
+ *
+ * @return Size of attribute in bytes without padding.
+ */
+static inline int nla_attr_size(int payload)
+{
+       return NLA_HDRLEN + payload;
+}
+
+/**
+ * Return size of attribute including padding.
+ * @arg payload                Payload length of attribute.
+ *
+ * @code
+ *    <----------- nla_total_size(payload) ----------->
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ *   | Attribute Header | Pad |     Payload      | Pad |
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ * @endcode
+ *
+ * @return Size of attribute in bytes.
+ */
+static inline int nla_total_size(int payload)
+{
+       return NLA_ALIGN(nla_attr_size(payload));
+}
+
+/**
+ * Return length of padding at the tail of the attribute.
+ * @arg payload                Payload length of attribute.
+ *
+ * @code
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ *   | Attribute Header | Pad |     Payload      | Pad |
+ *   +------------------+- - -+- - - - - - - - - +- - -+
+ *                                                <--->  
+ * @endcode
+ *
+ * @return Length of padding in bytes.
+ */
+static inline int nla_padlen(int payload)
+{
+       return nla_total_size(payload) - nla_attr_size(payload);
+}
+
+/**
+ * Return type of the attribute.
+ * @arg nla            Attribute.
+ *
+ * @return Type of attribute.
+ */
+static inline int nla_type(const struct nlattr *nla)
+{
+       return nla->nla_type & NLA_TYPE_MASK;
+}
+
+/**
+ * Return pointer to the payload section.
+ * @arg nla            Attribute.
+ *
+ * @return Pointer to start of payload section.
+ */
+static inline void *nla_data(const struct nlattr *nla)
+{
+       return (char *) nla + NLA_HDRLEN;
+}
+
+/**
+ * Return length of the payload .
+ * @arg nla            Attribute
+ *
+ * @return Length of payload in bytes.
+ */
+static inline int nla_len(const struct nlattr *nla)
+{
+       return nla->nla_len - NLA_HDRLEN;
+}
+
+/**
+ * Copy attribute payload to another memory area.
+ * @arg dest           Pointer to destination memory area.
+ * @arg src            Attribute
+ * @arg count          Number of bytes to copy at most.
+ *
+ * Note: The number of bytes copied is limited by the length of
+ *       the attribute payload.
+ *
+ * @return The number of bytes copied to dest.
+ */
+static inline int nla_memcpy(void *dest, struct nlattr *src, int count)
+{
+       int minlen;
+
+       if (!src)
+               return 0;
+       
+       minlen = min_t(int, count, nla_len(src));
+       memcpy(dest, nla_data(src), minlen);
+
+       return minlen;
+}
+
+
+/**
+ * Add abstract data as unspecific attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg data           Abstract data object.
+ *
+ * Equivalent to nla_put() except that the length of the payload is
+ * derived from the abstract data object.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+static inline int nla_put_data(struct nl_msg *msg, int attrtype, struct nl_data *data)
+{
+       return nla_put(msg, attrtype, nl_data_get_size(data),
+                      nl_data_get(data));
+}
+
+/**
+ * Add abstract address as unspecific attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg addr           Abstract address object.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+static inline int nla_put_addr(struct nl_msg *msg, int attrtype, struct nl_addr *addr)
+{
+       return nla_put(msg, attrtype, nl_addr_get_len(addr),
+                      nl_addr_get_binary_addr(addr));
+}
+
+/** @} */
+
+/**
+ * @name Integer Attributes
+ */
+
+/**
+ * Add 8 bit integer attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg value          Numeric value to store as payload.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+static inline int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value)
+{
+       return nla_put(msg, attrtype, sizeof(uint8_t), &value);
+}
+
+/**
+ * Return value of 8 bit integer attribute.
+ * @arg nla            8 bit integer attribute
+ *
+ * @return Payload as 8 bit integer.
+ */
+static inline uint8_t nla_get_u8(struct nlattr *nla)
+{
+       return *(uint8_t *) nla_data(nla);
+}
+
+/**
+ * Add 16 bit integer attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg value          Numeric value to store as payload.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+static inline int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value)
+{
+       return nla_put(msg, attrtype, sizeof(uint16_t), &value);
+}
+
+/**
+ * Return payload of 16 bit integer attribute.
+ * @arg nla            16 bit integer attribute
+ *
+ * @return Payload as 16 bit integer.
+ */
+static inline uint16_t nla_get_u16(struct nlattr *nla)
+{
+       return *(uint16_t *) nla_data(nla);
+}
+
+/**
+ * Add 32 bit integer attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg value          Numeric value to store as payload.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+static inline int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value)
+{
+       return nla_put(msg, attrtype, sizeof(uint32_t), &value);
+}
+
+/**
+ * Return payload of 32 bit integer attribute.
+ * @arg nla            32 bit integer attribute.
+ *
+ * @return Payload as 32 bit integer.
+ */
+static inline uint32_t nla_get_u32(struct nlattr *nla)
+{
+       return *(uint32_t *) nla_data(nla);
+}
+
+/**
+ * Add 64 bit integer attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg value          Numeric value to store as payload.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+static inline int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value)
+{
+       return nla_put(msg, attrtype, sizeof(uint64_t), &value);
+}
+
+/**
+ * Return payload of u64 attribute
+ * @arg nla            u64 netlink attribute
+ *
+ * @return Payload as 64 bit integer.
+ */
+static inline uint64_t nla_get_u64(struct nlattr *nla)
+{
+       uint64_t tmp;
+
+       nla_memcpy(&tmp, nla, sizeof(tmp));
+
+       return tmp;
+}
+
+/**
+ * Add string attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg str            NUL terminated string.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+static inline int nla_put_string(struct nl_msg *msg, int attrtype, const char *str)
+{
+       return nla_put(msg, attrtype, strlen(str) + 1, str);
+}
+
+/**
+ * Return payload of string attribute.
+ * @arg nla            String attribute.
+ *
+ * @return Pointer to attribute payload.
+ */
+static inline char *nla_get_string(struct nlattr *nla)
+{
+       return (char *) nla_data(nla);
+}
+
+static inline char *nla_strdup(struct nlattr *nla)
+{
+       return strdup(nla_get_string(nla));
+}
+
+/** @} */
+
+/**
+ * @name Flag Attribute
+ */
+
+/**
+ * Add flag netlink attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+static inline int nla_put_flag(struct nl_msg *msg, int attrtype)
+{
+       return nla_put(msg, attrtype, 0, NULL);
+}
+
+/**
+ * Return true if flag attribute is set.
+ * @arg nla            Flag netlink attribute.
+ *
+ * @return True if flag is set, otherwise false.
+ */
+static inline int nla_get_flag(struct nlattr *nla)
+{
+       return !!nla;
+}
+
+/** @} */
+
+/**
+ * @name Microseconds Attribute
+ */
+
+/**
+ * Add a msecs netlink attribute to a netlink message
+ * @arg n              netlink message
+ * @arg attrtype       attribute type
+ * @arg msecs          number of msecs
+ */
+static inline int nla_put_msecs(struct nl_msg *n, int attrtype, unsigned long msecs)
+{
+       return nla_put_u64(n, attrtype, msecs);
+}
+
+/**
+ * Return payload of msecs attribute
+ * @arg nla            msecs netlink attribute
+ *
+ * @return the number of milliseconds.
+ */
+static inline unsigned long nla_get_msecs(struct nlattr *nla)
+{
+       return nla_get_u64(nla);
+}
+
+/**
+ * Add nested attributes to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg nested         Message containing attributes to be nested.
+ *
+ * Takes the attributes found in the \a nested message and appends them
+ * to the message \a msg nested in a container of the type \a attrtype.
+ * The \a nested message may not have a family specific header.
+ *
+ * @see nla_put
+ * @return 0 on success or a negative error code.
+ */
+static inline int nla_put_nested(struct nl_msg *msg, int attrtype, struct nl_msg *nested)
+{
+       return nla_put(msg, attrtype, nlmsg_len(nested->nm_nlh),
+                      nlmsg_data(nested->nm_nlh));
+}
+
+/**
+ * Start a new level of nested attributes.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type of container.
+ *
+ * @return Pointer to container attribute.
+ */
+static inline struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype)
+{
+       struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh);
+
+       if (nla_put(msg, attrtype, 0, NULL) < 0)
+               return NULL;
+
+       return start;
+}
+
+/**
+ * Finalize nesting of attributes.
+ * @arg msg            Netlink message.
+ * @arg start          Container attribute as returned from nla_nest_start().
+ *
+ * Corrects the container attribute header to include the appeneded attributes.
+ *
+ * @return 0
+ */
+static inline int nla_nest_end(struct nl_msg *msg, struct nlattr *start)
+{
+       start->nla_len = (unsigned char *) nlmsg_tail(msg->nm_nlh) -
+                               (unsigned char *) start;
+       return 0;
+}
+
+/**
+ * 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.
+ * @arg nla            Nested Attribute.
+ * @arg policy         Attribute validation policy.
+ *
+ * Feeds the stream of attributes nested into the specified attribute
+ * to nla_parse().
+ *
+ * @see nla_parse
+ * @return 0 on success or a negative error code.
+ */
+static inline int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
+                    struct nla_policy *policy)
+{
+       return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy);
+}
+
+/**
+ * Compare attribute payload with memory area.
+ * @arg nla            Attribute.
+ * @arg data           Memory area to compare to.
+ * @arg size           Number of bytes to compare.
+ *
+ * @see memcmp(3)
+ * @return An integer less than, equal to, or greater than zero.
+ */
+static inline int nla_memcmp(const struct nlattr *nla, const void *data, size_t size)
+{
+       int d = nla_len(nla) - size;
+
+       if (d == 0)
+               d = memcmp(nla_data(nla), data, size);
+
+       return d;
+}
+
+/**
+ * Compare string attribute payload with string
+ * @arg nla            Attribute of type NLA_STRING.
+ * @arg str            NUL terminated string.
+ *
+ * @see strcmp(3)
+ * @return An integer less than, equal to, or greater than zero.
+ */
+static inline int nla_strcmp(const struct nlattr *nla, const char *str)
+{
+       int len = strlen(str) + 1;
+       int d = nla_len(nla) - len;
+
+       if (d == 0)
+               d = memcmp(nla_data(nla), str, len);
+
+       return d;
+}
+
+/**
+ * Copy string attribute payload to a buffer.
+ * @arg dst            Pointer to destination buffer.
+ * @arg nla            Attribute of type NLA_STRING.
+ * @arg dstsize                Size of destination buffer in bytes.
+ *
+ * Copies at most dstsize - 1 bytes to the destination buffer.
+ * The result is always a valid NUL terminated string. Unlike
+ * strlcpy the destination buffer is always padded out.
+ *
+ * @return The length of string attribute without the terminating NUL.
+ */
+static inline size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize)
+{
+       size_t srclen = nla_len(nla);
+       char *src = nla_data(nla);
+
+       if (srclen > 0 && src[srclen - 1] == '\0')
+               srclen--;
+
+       if (dstsize > 0) {
+               size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen;
+
+               memset(dst, 0, dstsize);
+               memcpy(dst, src, len);
+       }
+
+       return srclen;
+}
+
+
+/**
+ * @name Attribute Construction (Exception Based)
+ * @{
+ */
+
+/**
+ * @ingroup attr
+ * Add unspecific attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg attrlen                Length of attribute payload.
+ * @arg data           Head of attribute payload.
+ */
+#define NLA_PUT(msg, attrtype, attrlen, data) \
+       do { \
+               if (nla_put(msg, attrtype, attrlen, data) < 0) \
+                       goto nla_put_failure; \
+       } while(0)
+
+/**
+ * @ingroup attr
+ * Add atomic type attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg type           Atomic type.
+ * @arg attrtype       Attribute type.
+ * @arg value          Head of attribute payload.
+ */
+#define NLA_PUT_TYPE(msg, type, attrtype, value) \
+       do { \
+               type __tmp = value; \
+               NLA_PUT(msg, attrtype, sizeof(type), &__tmp); \
+       } while(0)
+
+/**
+ * Add 8 bit integer attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg value          Numeric value.
+ */
+#define NLA_PUT_U8(msg, attrtype, value) \
+       NLA_PUT_TYPE(msg, uint8_t, attrtype, value)
+
+/**
+ * Add 16 bit integer attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg value          Numeric value.
+ */
+#define NLA_PUT_U16(msg, attrtype, value) \
+       NLA_PUT_TYPE(msg, uint16_t, attrtype, value)
+
+/**
+ * Add 32 bit integer attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg value          Numeric value.
+ */
+#define NLA_PUT_U32(msg, attrtype, value) \
+       NLA_PUT_TYPE(msg, uint32_t, attrtype, value)
+
+/**
+ * Add 64 bit integer attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg value          Numeric value.
+ */
+#define NLA_PUT_U64(msg, attrtype, value) \
+       NLA_PUT_TYPE(msg, uint64_t, attrtype, value)
+
+/**
+ * Add string attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg value          NUL terminated character string.
+ */
+#define NLA_PUT_STRING(msg, attrtype, value) \
+       NLA_PUT(msg, attrtype, strlen(value) + 1, value)
+
+/**
+ * Add flag attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ */
+#define NLA_PUT_FLAG(msg, attrtype) \
+       NLA_PUT(msg, attrtype, 0, NULL)
+
+/**
+ * Add msecs attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg msecs          Numeric value in micro seconds.
+ */
+#define NLA_PUT_MSECS(msg, attrtype, msecs) \
+       NLA_PUT_U64(msg, attrtype, msecs)
+
+/**
+ * Add address attribute to netlink message.
+ * @arg msg            Netlink message.
+ * @arg attrtype       Attribute type.
+ * @arg addr           Abstract address object.
+ */
+#define NLA_PUT_ADDR(msg, attrtype, addr) \
+       NLA_PUT(msg, attrtype, nl_addr_get_len(addr), \
+               nl_addr_get_binary_addr(addr))
+
+/** @} */
+
+/**
+ * @name Iterators
+ * @{
+ */
+
+/**
+ * @ingroup attr
+ * Iterate over a stream of attributes
+ * @arg pos    loop counter, set to current attribute
+ * @arg head   head of attribute stream
+ * @arg len    length of attribute stream
+ * @arg rem    initialized to len, holds bytes currently remaining in stream
+ */
+#define nla_for_each_attr(pos, head, len, rem) \
+       for (pos = head, rem = len; \
+            nla_ok(pos, rem); \
+            pos = nla_next(pos, &(rem)))
+
+/**
+ * @ingroup attr
+ * Iterate over a stream of nested attributes
+ * @arg pos    loop counter, set to current attribute
+ * @arg nla    attribute containing the nested attributes
+ * @arg rem    initialized to len, holds bytes currently remaining in stream
+ */
+#define nla_for_each_nested(pos, nla, rem) \
+       for (pos = nla_data(nla), rem = nla_len(nla); \
+            nla_ok(pos, rem); \
+            pos = nla_next(pos, &(rem)))
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/cache-api.h b/package/libnl-tiny/src/include/netlink/cache-api.h
new file mode 100644 (file)
index 0000000..22fc449
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * netlink/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-2006 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_CACHE_API_H_
+#define NETLINK_CACHE_API_H_
+
+#include <netlink/netlink.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
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/cache.h b/package/libnl-tiny/src/include/netlink/cache.h
new file mode 100644 (file)
index 0000000..f4db3b9
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * netlink/cache.h             Caching Module
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_CACHE_H_
+#define NETLINK_CACHE_H_
+
+#include <netlink/netlink.h>
+#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;
+
+typedef void (*change_func_t)(struct nl_cache *, struct nl_object *, int);
+
+/* Access Functions */
+extern int                     nl_cache_nitems(struct nl_cache *);
+extern int                     nl_cache_nitems_filter(struct nl_cache *,
+                                                      struct nl_object *);
+extern struct nl_cache_ops *   nl_cache_get_ops(struct nl_cache *);
+extern struct nl_object *      nl_cache_get_first(struct nl_cache *);
+extern struct nl_object *      nl_cache_get_last(struct nl_cache *);
+extern struct nl_object *      nl_cache_get_next(struct nl_object *);
+extern struct nl_object *      nl_cache_get_prev(struct nl_object *);
+
+extern struct nl_cache *       nl_cache_alloc(struct nl_cache_ops *);
+extern int                     nl_cache_alloc_and_fill(struct nl_cache_ops *,
+                                                       struct nl_sock *,
+                                                       struct nl_cache **);
+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 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);
+extern int                     nl_cache_include(struct nl_cache *,
+                                                struct nl_object *,
+                                                change_func_t);
+
+/* General */
+extern int                     nl_cache_is_empty(struct nl_cache *);
+extern void                    nl_cache_mark_all(struct nl_cache *);
+
+/* Dumping */
+extern void                    nl_cache_dump(struct nl_cache *,
+                                             struct nl_dump_params *);
+extern void                    nl_cache_dump_filter(struct nl_cache *,
+                                                    struct nl_dump_params *,
+                                                    struct nl_object *);
+
+/* Iterators */
+extern void                    nl_cache_foreach(struct nl_cache *,
+                                                void (*cb)(struct nl_object *,
+                                                           void *),
+                                                void *arg);
+extern void                    nl_cache_foreach_filter(struct nl_cache *,
+                                                       struct nl_object *,
+                                                       void (*cb)(struct
+                                                                  nl_object *,
+                                                                  void *),
+                                                       void *arg);
+
+/* --- cache management --- */
+
+/* Cache type management */
+extern struct nl_cache_ops *   nl_cache_ops_lookup(const char *);
+extern struct nl_cache_ops *   nl_cache_ops_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_alloc(struct nl_sock *,
+                                                   int, int,
+                                                   struct nl_cache_mngr **);
+extern int                     nl_cache_mngr_add(struct nl_cache_mngr *,
+                                                 const char *,
+                                                 change_func_t,
+                                                 struct nl_cache **);
+extern int                     nl_cache_mngr_get_fd(struct nl_cache_mngr *);
+extern int                     nl_cache_mngr_poll(struct nl_cache_mngr *,
+                                                  int);
+extern int                     nl_cache_mngr_data_ready(struct nl_cache_mngr *);
+extern void                    nl_cache_mngr_free(struct nl_cache_mngr *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/data.h b/package/libnl-tiny/src/include/netlink/data.h
new file mode 100644 (file)
index 0000000..071159e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * netlink/data.h      Abstract Data
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_DATA_H_
+#define NETLINK_DATA_H_
+
+#include <netlink/netlink.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nl_data;
+
+/* General */
+extern struct nl_data *        nl_data_alloc(void *, size_t);
+extern struct nl_data * nl_data_alloc_attr(struct nlattr *);
+extern struct nl_data *        nl_data_clone(struct nl_data *);
+extern int             nl_data_append(struct nl_data *, void *, size_t);
+extern void            nl_data_free(struct nl_data *);
+
+/* Access Functions */
+extern void *          nl_data_get(struct nl_data *);
+extern size_t          nl_data_get_size(struct nl_data *);
+
+/* Misc */
+extern int             nl_data_cmp(struct nl_data *, struct nl_data *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/errno.h b/package/libnl-tiny/src/include/netlink/errno.h
new file mode 100644 (file)
index 0000000..0b43da0
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * netlink/errno.h             Error Numbers
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under 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>
+ */
+
+#ifndef NETLINK_ERRNO_H_
+#define NETLINK_ERRNO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NLE_SUCCESS            0
+#define NLE_FAILURE            1
+#define NLE_INTR               2
+#define NLE_BAD_SOCK           3
+#define NLE_AGAIN              4
+#define NLE_NOMEM              5
+#define NLE_EXIST              6
+#define NLE_INVAL              7
+#define NLE_RANGE              8
+#define NLE_MSGSIZE            9
+#define NLE_OPNOTSUPP          10
+#define NLE_AF_NOSUPPORT       11
+#define NLE_OBJ_NOTFOUND       12
+#define NLE_NOATTR             13
+#define NLE_MISSING_ATTR       14
+#define NLE_AF_MISMATCH                15
+#define NLE_SEQ_MISMATCH       16
+#define NLE_MSG_OVERFLOW       17
+#define NLE_MSG_TRUNC          18
+#define NLE_NOADDR             19
+#define NLE_SRCRT_NOSUPPORT    20
+#define NLE_MSG_TOOSHORT       21
+#define NLE_MSGTYPE_NOSUPPORT  22
+#define NLE_OBJ_MISMATCH       23
+#define NLE_NOCACHE            24
+#define NLE_BUSY               25
+#define NLE_PROTO_MISMATCH     26
+#define NLE_NOACCESS           27
+#define NLE_PERM               28
+
+#define NLE_MAX                        NLE_PERM
+
+extern const char *    nl_geterror(int);
+extern void            nl_perror(int, const char *);
+extern int             nl_syserr2nlerr(int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/genl/ctrl.h b/package/libnl-tiny/src/include/netlink/genl/ctrl.h
new file mode 100644 (file)
index 0000000..1ae62f4
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * netlink/genl/ctrl.h         Generic Netlink Controller
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_GENL_CTRL_H_
+#define NETLINK_GENL_CTRL_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/addr.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct genl_family;
+
+extern int                     genl_ctrl_alloc_cache(struct nl_sock *,
+                                                     struct nl_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 *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/genl/family.h b/package/libnl-tiny/src/include/netlink/genl/family.h
new file mode 100644 (file)
index 0000000..0257cbe
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * netlink/genl/family.h       Generic Netlink Family
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_GENL_FAMILY_H_
+#define NETLINK_GENL_FAMILY_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @cond SKIP */
+#define FAMILY_ATTR_ID         0x01
+#define FAMILY_ATTR_NAME       0x02
+#define FAMILY_ATTR_VERSION    0x04
+#define FAMILY_ATTR_HDRSIZE    0x08
+#define FAMILY_ATTR_MAXATTR    0x10
+#define FAMILY_ATTR_OPS                0x20
+
+
+struct genl_family
+{
+       NLHDR_COMMON
+
+       uint16_t                gf_id;
+       char                    gf_name[GENL_NAMSIZ];
+       uint32_t                gf_version;
+       uint32_t                gf_hdrsize;
+       uint32_t                gf_maxattr;
+
+       struct nl_list_head     gf_ops;
+};
+
+
+extern struct genl_family *    genl_family_alloc(void);
+extern void                    genl_family_put(struct genl_family *);
+
+extern int                     genl_family_add_op(struct genl_family *,
+                                                  int, int);
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+static inline unsigned int genl_family_get_id(struct genl_family *family)
+{
+       if (family->ce_mask & FAMILY_ATTR_ID)
+               return family->gf_id;
+       else
+               return GENL_ID_GENERATE;
+}
+
+static inline void genl_family_set_id(struct genl_family *family, unsigned int id)
+{
+       family->gf_id = id;
+       family->ce_mask |= FAMILY_ATTR_ID;
+}
+
+static inline char *genl_family_get_name(struct genl_family *family)
+{
+       if (family->ce_mask & FAMILY_ATTR_NAME)
+               return family->gf_name;
+       else
+               return NULL;
+}
+
+static inline 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;
+}
+
+static inline uint8_t genl_family_get_version(struct genl_family *family)
+{
+       if (family->ce_mask & FAMILY_ATTR_VERSION)
+               return family->gf_version;
+       else
+               return 0;
+}
+
+static inline void genl_family_set_version(struct genl_family *family, uint8_t version)
+{
+       family->gf_version = version;
+       family->ce_mask |= FAMILY_ATTR_VERSION;
+}
+
+static inline uint32_t genl_family_get_hdrsize(struct genl_family *family)
+{
+       if (family->ce_mask & FAMILY_ATTR_HDRSIZE)
+               return family->gf_hdrsize;
+       else
+               return 0;
+}
+
+static inline void genl_family_set_hdrsize(struct genl_family *family, uint32_t hdrsize)
+{
+       family->gf_hdrsize = hdrsize;
+       family->ce_mask |= FAMILY_ATTR_HDRSIZE;
+}
+
+static inline uint32_t genl_family_get_maxattr(struct genl_family *family)
+{
+       if (family->ce_mask & FAMILY_ATTR_MAXATTR)
+               return family->gf_maxattr;
+       else
+               return family->gf_maxattr;
+}
+
+static inline void genl_family_set_maxattr(struct genl_family *family, uint32_t maxattr)
+{
+       family->gf_maxattr = maxattr;
+       family->ce_mask |= FAMILY_ATTR_MAXATTR;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/genl/genl.h b/package/libnl-tiny/src/include/netlink/genl/genl.h
new file mode 100644 (file)
index 0000000..3f3340c
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * netlink/genl/genl.h         Generic Netlink
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_GENL_H_
+#define NETLINK_GENL_H_
+
+#include <netlink/netlink.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int             genl_connect(struct nl_sock *);
+
+extern int             genl_send_simple(struct nl_sock *, int, int,
+                                        int, int);
+
+extern void *          genlmsg_put(struct nl_msg *, uint32_t, uint32_t,
+                                   int, int, int, uint8_t, uint8_t);
+
+extern int             genlmsg_valid_hdr(struct nlmsghdr *, int);
+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 void *          genlmsg_data(const struct genlmsghdr *);
+extern int             genlmsg_len(const struct genlmsghdr *);
+extern struct nlattr * genlmsg_attrdata(const struct genlmsghdr *, int);
+extern int             genlmsg_attrlen(const struct genlmsghdr *, int);
+
+extern char *          genl_op2name(int, int, char *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/genl/mngt.h b/package/libnl-tiny/src/include/netlink/genl/mngt.h
new file mode 100644 (file)
index 0000000..8b0244f
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * netlink/genl/mngt.h         Generic Netlink Management
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_GENL_MNGT_H_
+#define NETLINK_GENL_MNGT_H_
+
+#include <netlink/netlink.h>
+#include <netlink/attr.h>
+#include <netlink/list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nl_cache_ops;
+
+struct genl_info
+{
+       struct sockaddr_nl *    who;
+       struct nlmsghdr *       nlh;
+       struct genlmsghdr *     genlhdr;
+       void *                  userhdr;
+       struct nlattr **        attrs;
+};
+
+/**
+ * @ingroup genl_mngt
+ * Generic Netlink Command
+ */
+struct genl_cmd
+{
+       /** Unique command identifier */
+       int                     c_id;
+
+       /** Name/description of command */
+       char *                  c_name;
+
+       /**
+        * Maximum attribute identifier, must be provided if
+        * a message parser is available.
+        */
+       int                     c_maxattr;
+
+       int                   (*c_msg_parser)(struct nl_cache_ops *,
+                                             struct genl_cmd *,
+                                             struct genl_info *, void *);
+
+       /**
+        * Attribute validation policy (optional)
+        */
+       struct nla_policy *     c_attr_policy;
+};
+
+/**
+ * @ingroup genl_mngt
+ * Generic Netlink Operations
+ */
+struct genl_ops
+{
+       int                     o_family;
+       int                     o_id;
+       char *                  o_name;
+       struct nl_cache_ops *   o_cache_ops;
+       struct genl_cmd *       o_cmds;
+       int                     o_ncmds;
+
+       /* linked list of all genl cache operations */
+       struct nl_list_head     o_list;
+};
+
+
+extern int             genl_register(struct nl_cache_ops *);
+extern void            genl_unregister(struct nl_cache_ops *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/handlers.h b/package/libnl-tiny/src/include/netlink/handlers.h
new file mode 100644 (file)
index 0000000..5c62368
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * netlink/handlers.c  default netlink message handlers
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_HANDLERS_H_
+#define NETLINK_HANDLERS_H_
+
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <netlink/netlink-compat.h>
+#include <netlink/netlink-kernel.h>
+#include <netlink/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nl_sock;
+struct nl_msg;
+struct nl_cb;
+/**
+ * @name Callback Typedefs
+ * @{
+ */
+
+/**
+ * nl_recvmsgs() callback for message processing customization
+ * @ingroup cb
+ * @arg msg            netlink message being processed
+ * @arg arg            argument passwd on through caller
+ */
+typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg);
+
+/**
+ * nl_recvmsgs() callback for error message processing customization
+ * @ingroup cb
+ * @arg nla            netlink address of the peer
+ * @arg nlerr          netlink error message being processed
+ * @arg arg            argument passed on through caller
+ */
+typedef int (*nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla,
+                                  struct nlmsgerr *nlerr, void *arg);
+
+/** @} */
+
+/**
+ * Callback actions
+ * @ingroup cb
+ */
+enum nl_cb_action {
+       /** Proceed with wathever would come next */
+       NL_OK,
+       /** Skip this message */
+       NL_SKIP,
+       /** Stop parsing altogether and discard remaining messages */
+       NL_STOP,
+};
+
+/**
+ * Callback kinds
+ * @ingroup cb
+ */
+enum nl_cb_kind {
+       /** Default handlers (quiet) */
+       NL_CB_DEFAULT,
+       /** Verbose default handlers (error messages printed) */
+       NL_CB_VERBOSE,
+       /** Debug handlers for debugging */
+       NL_CB_DEBUG,
+       /** Customized handler specified by the user */
+       NL_CB_CUSTOM,
+       __NL_CB_KIND_MAX,
+};
+
+#define NL_CB_KIND_MAX (__NL_CB_KIND_MAX - 1)
+
+/**
+ * Callback types
+ * @ingroup cb
+ */
+enum nl_cb_type {
+       /** Message is valid */
+       NL_CB_VALID,
+       /** Last message in a series of multi part messages received */
+       NL_CB_FINISH,
+       /** Report received that data was lost */
+       NL_CB_OVERRUN,
+       /** Message wants to be skipped */
+       NL_CB_SKIPPED,
+       /** Message is an acknowledge */
+       NL_CB_ACK,
+       /** Called for every message received */
+       NL_CB_MSG_IN,
+       /** Called for every message sent out except for nl_sendto() */
+       NL_CB_MSG_OUT,
+       /** Message is malformed and invalid */
+       NL_CB_INVALID,
+       /** Called instead of internal sequence number checking */
+       NL_CB_SEQ_CHECK,
+       /** Sending of an acknowledge message has been requested */
+       NL_CB_SEND_ACK,
+       __NL_CB_TYPE_MAX,
+};
+
+#define NL_CB_TYPE_MAX (__NL_CB_TYPE_MAX - 1)
+
+struct nl_cb
+{
+       nl_recvmsg_msg_cb_t     cb_set[NL_CB_TYPE_MAX+1];
+       void *                  cb_args[NL_CB_TYPE_MAX+1];
+       
+       nl_recvmsg_err_cb_t     cb_err;
+       void *                  cb_err_arg;
+
+       /** May be used to replace nl_recvmsgs with your own implementation
+        * in all internal calls to nl_recvmsgs. */
+       int                     (*cb_recvmsgs_ow)(struct nl_sock *,
+                                                 struct nl_cb *);
+
+       /** Overwrite internal calls to nl_recv, must return the number of
+        * octets read and allocate a buffer for the received data. */
+       int                     (*cb_recv_ow)(struct nl_sock *,
+                                             struct sockaddr_nl *,
+                                             unsigned char **,
+                                             struct ucred **);
+
+       /** Overwrites internal calls to nl_send, must send the netlink
+        * message. */
+       int                     (*cb_send_ow)(struct nl_sock *,
+                                             struct nl_msg *);
+
+       int                     cb_refcnt;
+};
+
+
+extern struct nl_cb *  nl_cb_alloc(enum nl_cb_kind);
+extern struct nl_cb *  nl_cb_clone(struct nl_cb *);
+extern void            nl_cb_put(struct nl_cb *);
+
+extern int  nl_cb_set(struct nl_cb *, enum nl_cb_type, enum nl_cb_kind,
+                     nl_recvmsg_msg_cb_t, void *);
+extern int  nl_cb_err(struct nl_cb *, enum nl_cb_kind, nl_recvmsg_err_cb_t,
+                     void *);
+
+static inline struct nl_cb *nl_cb_get(struct nl_cb *cb)
+{
+       cb->cb_refcnt++;
+
+       return cb;
+}
+
+/**
+ * Set up a all callbacks
+ * @arg cb             callback set
+ * @arg kind           kind of callback
+ * @arg func           callback function
+ * @arg arg            argument to be passwd to callback function
+ *
+ * @return 0 on success or a negative error code
+ */
+static inline int nl_cb_set_all(struct nl_cb *cb, enum nl_cb_kind kind,
+                 nl_recvmsg_msg_cb_t func, void *arg)
+{
+       int i, err;
+
+       for (i = 0; i <= NL_CB_TYPE_MAX; i++) {
+               err = nl_cb_set(cb, i, kind, func, arg);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+
+/**
+ * @name Overwriting
+ * @{
+ */
+
+/**
+ * Overwrite internal calls to nl_recvmsgs()
+ * @arg cb             callback set
+ * @arg func           replacement callback for nl_recvmsgs()
+ */
+static inline void nl_cb_overwrite_recvmsgs(struct nl_cb *cb,
+                             int (*func)(struct nl_sock *, struct nl_cb *))
+{
+       cb->cb_recvmsgs_ow = func;
+}
+
+/**
+ * Overwrite internal calls to nl_recv()
+ * @arg cb             callback set
+ * @arg func           replacement callback for nl_recv()
+ */
+static inline void nl_cb_overwrite_recv(struct nl_cb *cb,
+                         int (*func)(struct nl_sock *, struct sockaddr_nl *,
+                                     unsigned char **, struct ucred **))
+{
+       cb->cb_recv_ow = func;
+}
+
+/**
+ * Overwrite internal calls to nl_send()
+ * @arg cb             callback set
+ * @arg func           replacement callback for nl_send()
+ */
+static inline void nl_cb_overwrite_send(struct nl_cb *cb,
+                         int (*func)(struct nl_sock *, struct nl_msg *))
+{
+       cb->cb_send_ow = func;
+}
+
+/** @} */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/list.h b/package/libnl-tiny/src/include/netlink/list.h
new file mode 100644 (file)
index 0000000..c6876a7
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * netlink/list.h      Netlink List Utilities
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_LIST_H_
+#define NETLINK_LIST_H_
+
+struct nl_list_head
+{
+       struct nl_list_head *   next;
+       struct nl_list_head *   prev;
+};
+
+
+static inline void __nl_list_add(struct nl_list_head *obj,
+                                struct nl_list_head *prev,
+                                struct nl_list_head *next)
+{
+       prev->next = obj;
+       obj->prev = prev;
+       next->prev = obj;
+       obj->next = next;
+}
+
+static inline void nl_list_add_tail(struct nl_list_head *obj,
+                                   struct nl_list_head *head)
+{
+       __nl_list_add(obj, head->prev, head);
+}
+
+static inline void nl_list_add_head(struct nl_list_head *obj,
+                                   struct nl_list_head *head)
+{
+       __nl_list_add(obj, head, head->next);
+}
+
+static inline void nl_list_del(struct nl_list_head *obj)
+{
+       obj->next->prev = obj->prev;
+       obj->prev->next = obj->next;
+}
+
+static inline int nl_list_empty(struct nl_list_head *head)
+{
+       return head->next == head;
+}
+
+#define nl_container_of(ptr, type, member) ({                  \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);   \
+        (type *)( (char *)__mptr - ((size_t) &((type *)0)->member));})
+
+#define nl_list_entry(ptr, type, member) \
+       nl_container_of(ptr, type, member)
+
+#define nl_list_at_tail(pos, head, member) \
+       ((pos)->member.next == (head))
+
+#define nl_list_at_head(pos, head, member) \
+       ((pos)->member.prev == (head))
+
+#define NL_LIST_HEAD(name) \
+       struct nl_list_head name = { &(name), &(name) }
+
+#define nl_list_first_entry(head, type, member)                        \
+       nl_list_entry((head)->next, type, member)
+
+#define nl_list_for_each_entry(pos, head, member)                              \
+       for (pos = nl_list_entry((head)->next, typeof(*pos), member);   \
+            &(pos)->member != (head);  \
+            (pos) = nl_list_entry((pos)->member.next, typeof(*(pos)), member))
+
+#define nl_list_for_each_entry_safe(pos, n, head, member)                      \
+       for (pos = nl_list_entry((head)->next, typeof(*pos), member),   \
+               n = nl_list_entry(pos->member.next, typeof(*pos), member);      \
+            &(pos)->member != (head);                                  \
+            pos = n, n = nl_list_entry(n->member.next, typeof(*n), member))
+
+#define nl_init_list_head(head) \
+       do { (head)->next = (head); (head)->prev = (head); } while (0)
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/msg.h b/package/libnl-tiny/src/include/netlink/msg.h
new file mode 100644 (file)
index 0000000..aedcb08
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * netlink/msg.c               Netlink Messages Interface
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_MSG_H_
+#define NETLINK_MSG_H_
+
+#include <netlink/netlink.h>
+#include <netlink/object.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nla_policy;
+
+#define NL_DONTPAD     0
+
+/**
+ * @ingroup msg
+ * @brief
+ * Will cause the netlink pid to be set to the pid assigned to
+ * the netlink handle (socket) just before sending the message off.
+ * @note Requires the use of nl_send_auto_complete()!
+ */
+#define NL_AUTO_PID    0
+
+/**
+ * @ingroup msg
+ * @brief
+ * May be used to refer to a sequence number which should be
+ * automatically set just before sending the message off.
+ * @note Requires the use of nl_send_auto_complete()!
+ */
+#define NL_AUTO_SEQ    0
+
+#define NL_MSG_CRED_PRESENT 1
+
+struct nl_msg
+{
+       int                     nm_protocol;
+       int                     nm_flags;
+       struct sockaddr_nl      nm_src;
+       struct sockaddr_nl      nm_dst;
+       struct ucred            nm_creds;
+       struct nlmsghdr *       nm_nlh;
+       size_t                  nm_size;
+       int                     nm_refcnt;
+};
+
+
+struct nl_msg;
+struct nl_tree;
+struct ucred;
+
+/* message parsing */
+extern int               nlmsg_ok(const struct nlmsghdr *, int);
+extern struct nlmsghdr *  nlmsg_next(struct nlmsghdr *, int *);
+extern int               nlmsg_parse(struct nlmsghdr *, int, struct nlattr **,
+                                     int, struct nla_policy *);
+extern 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 struct nlmsghdr *  nlmsg_put(struct nl_msg *, uint32_t, uint32_t,
+                                   int, int, int);
+extern void              nlmsg_free(struct nl_msg *);
+
+extern int               nl_msg_parse(struct nl_msg *,
+                                      void (*cb)(struct nl_object *, void *),
+                                      void *);
+
+extern void            nl_msg_dump(struct nl_msg *, FILE *);
+
+/**
+ * length of netlink message not including padding
+ * @arg payload                length of message payload
+ */
+static inline int nlmsg_msg_size(int payload)
+{
+       return NLMSG_HDRLEN + payload;
+}
+
+/**
+ * length of netlink message including padding
+ * @arg payload                length of message payload
+ */
+static inline int nlmsg_total_size(int payload)
+{
+       return NLMSG_ALIGN(nlmsg_msg_size(payload));
+}
+
+/**
+ * length of padding at the message's tail
+ * @arg payload                length of message payload
+ */
+static inline int nlmsg_padlen(int payload)
+{
+       return nlmsg_total_size(payload) - nlmsg_msg_size(payload);
+}
+
+/**
+ * head of message payload
+ * @arg nlh            netlink messsage header
+ */
+static inline void *nlmsg_data(const struct nlmsghdr *nlh)
+{
+       return (unsigned char *) nlh + NLMSG_HDRLEN;
+}
+
+static inline void *nlmsg_tail(const struct nlmsghdr *nlh)
+{
+       return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len);
+}
+
+/**
+ * length of message payload
+ * @arg nlh            netlink message header
+ */
+static inline int nlmsg_len(const struct nlmsghdr *nlh)
+{
+       return nlh->nlmsg_len - NLMSG_HDRLEN;
+}
+
+/**
+ * head of attributes data
+ * @arg nlh            netlink message header
+ * @arg hdrlen         length of family specific header
+ */
+static inline struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen)
+{
+       unsigned char *data = nlmsg_data(nlh);
+       return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen));
+}
+
+/**
+ * length of attributes data
+ * @arg nlh            netlink message header
+ * @arg hdrlen         length of family specific header
+ */
+static inline int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen)
+{
+       return nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen);
+}
+
+static inline int nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen)
+{
+       if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
+               return 0;
+
+       return 1;
+}
+
+
+static inline void nlmsg_set_proto(struct nl_msg *msg, int protocol)
+{
+       msg->nm_protocol = protocol;
+}
+
+static inline int nlmsg_get_proto(struct nl_msg *msg)
+{
+       return msg->nm_protocol;
+}
+
+static inline size_t nlmsg_get_max_size(struct nl_msg *msg)
+{
+       return msg->nm_size;
+}
+
+static inline void nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr)
+{
+       memcpy(&msg->nm_src, addr, sizeof(*addr));
+}
+
+static inline struct sockaddr_nl *nlmsg_get_src(struct nl_msg *msg)
+{
+       return &msg->nm_src;
+}
+
+static inline void nlmsg_set_dst(struct nl_msg *msg, struct sockaddr_nl *addr)
+{
+       memcpy(&msg->nm_dst, addr, sizeof(*addr));
+}
+
+static inline struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *msg)
+{
+       return &msg->nm_dst;
+}
+
+static inline void nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds)
+{
+       memcpy(&msg->nm_creds, creds, sizeof(*creds));
+       msg->nm_flags |= NL_MSG_CRED_PRESENT;
+}
+
+static inline struct ucred *nlmsg_get_creds(struct nl_msg *msg)
+{
+       if (msg->nm_flags & NL_MSG_CRED_PRESENT)
+               return &msg->nm_creds;
+       return NULL;
+}
+
+/**
+ * Return actual netlink message
+ * @arg n              netlink message
+ * 
+ * Returns the actual netlink message casted to the type of the netlink
+ * message header.
+ * 
+ * @return A pointer to the netlink message.
+ */
+static inline struct nlmsghdr *nlmsg_hdr(struct nl_msg *n)
+{
+       return n->nm_nlh;
+}
+
+/**
+ * Acquire a reference on a netlink message
+ * @arg msg            message to acquire reference from
+ */
+static inline void nlmsg_get(struct nl_msg *msg)
+{
+       msg->nm_refcnt++;
+}
+
+/**
+ * Expand maximum payload size of a netlink message
+ * @arg n              Netlink message.
+ * @arg newlen         New maximum payload size.
+ *
+ * Reallocates the payload section of a netlink message and increases
+ * the maximum payload size of the message.
+ *
+ * @note Any pointers pointing to old payload block will be stale and
+ *       need to be refetched. Therfore, do not expand while constructing
+ *       nested attributes or while reserved data blocks are held.
+ *
+ * @return 0 on success or a negative error code.
+ */
+static inline int nlmsg_expand(struct nl_msg *n, size_t newlen)
+{
+       void *tmp;
+
+       if (newlen <= n->nm_size)
+               return -NLE_INVAL;
+
+       tmp = realloc(n->nm_nlh, newlen);
+       if (tmp == NULL)
+               return -NLE_NOMEM;
+
+       n->nm_nlh = tmp;
+       n->nm_size = newlen;
+
+       return 0;
+}
+
+
+/**
+ * @name Iterators
+ * @{
+ */
+
+/**
+ * @ingroup msg
+ * Iterate over a stream of attributes in a message
+ * @arg pos    loop counter, set to current attribute
+ * @arg nlh    netlink message header
+ * @arg hdrlen length of family header
+ * @arg rem    initialized to len, holds bytes currently remaining in stream
+ */
+#define nlmsg_for_each_attr(pos, nlh, hdrlen, rem) \
+       nla_for_each_attr(pos, nlmsg_attrdata(nlh, hdrlen), \
+                         nlmsg_attrlen(nlh, hdrlen), rem)
+
+/**
+ * Iterate over a stream of messages
+ * @arg pos    loop counter, set to current message
+ * @arg head   head of message stream
+ * @arg len    length of message stream
+ * @arg rem    initialized to len, holds bytes currently remaining in stream
+ */
+#define nlmsg_for_each_msg(pos, head, len, rem) \
+       for (pos = head, rem = len; \
+            nlmsg_ok(pos, rem); \
+            pos = nlmsg_next(pos, &(rem)))
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/netlink-compat.h b/package/libnl-tiny/src/include/netlink/netlink-compat.h
new file mode 100644 (file)
index 0000000..17ec9fc
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * netlink/netlink-compat.h    Netlink Compatability
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_COMPAT_H_
+#define NETLINK_COMPAT_H_
+
+#if !defined _LINUX_SOCKET_H && !defined _BITS_SOCKADDR_H
+typedef unsigned short  sa_family_t;
+#endif
+
+#ifndef IFNAMSIZ 
+/** Maximum length of a interface name */
+#define IFNAMSIZ 16
+#endif
+
+/* patch 2.4.x if_arp */
+#ifndef ARPHRD_INFINIBAND
+#define ARPHRD_INFINIBAND 32
+#endif
+
+/* patch 2.4.x eth header file */
+#ifndef ETH_P_MPLS_UC
+#define ETH_P_MPLS_UC  0x8847 
+#endif
+
+#ifndef ETH_P_MPLS_MC
+#define ETH_P_MPLS_MC   0x8848
+#endif
+
+#ifndef  ETH_P_EDP2
+#define ETH_P_EDP2      0x88A2
+#endif
+
+#ifndef ETH_P_HDLC
+#define ETH_P_HDLC      0x0019 
+#endif
+
+#ifndef AF_LLC
+#define AF_LLC         26
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/netlink-kernel.h b/package/libnl-tiny/src/include/netlink/netlink-kernel.h
new file mode 100644 (file)
index 0000000..a0f5535
--- /dev/null
@@ -0,0 +1,196 @@
+#ifndef __LINUX_NETLINK_H
+#define __LINUX_NETLINK_H
+
+/**
+ * Netlink socket address
+ * @ingroup nl
+ */
+struct sockaddr_nl
+{
+       /** socket family (AF_NETLINK) */
+       sa_family_t     nl_family;
+
+       /** Padding (unused) */
+       unsigned short  nl_pad;
+
+       /** Unique process ID  */
+       uint32_t        nl_pid;
+
+       /** Multicast group subscriptions */
+       uint32_t        nl_groups;
+};
+
+/**
+ * Netlink message header
+ * @ingroup msg
+ */
+struct nlmsghdr
+{
+       /**
+        * Length of message including header.
+        */
+       uint32_t        nlmsg_len;
+
+       /**
+        * Message type (content type)
+        */
+       uint16_t        nlmsg_type;
+
+       /**
+        * Message flags
+        */
+       uint16_t        nlmsg_flags;
+
+       /**
+        * Sequence number
+        */
+       uint32_t        nlmsg_seq;
+
+       /**
+        * Netlink PID of the proccess sending the message.
+        */
+       uint32_t        nlmsg_pid;
+};
+
+/**
+ * @name Standard message flags
+ * @{
+ */
+
+/**
+ * Must be set on all request messages (typically from user space to
+ * kernel space).
+ * @ingroup msg
+ */
+#define NLM_F_REQUEST          1
+
+/**
+ * Indicates the message is part of a multipart message terminated
+ * by NLMSG_DONE.
+ */
+#define NLM_F_MULTI            2
+
+/**
+ * Request for an acknowledgment on success.
+ */
+#define NLM_F_ACK              4
+
+/**
+ * Echo this request
+ */
+#define NLM_F_ECHO             8
+
+/** @} */
+
+/**
+ * @name Additional message flags for GET requests
+ * @{
+ */
+
+/**
+ * Return the complete table instead of a single entry.
+ * @ingroup msg
+ */
+#define NLM_F_ROOT     0x100
+
+/**
+ * Return all entries matching criteria passed in message content.
+ */
+#define NLM_F_MATCH    0x200
+
+/**
+ * Return an atomic snapshot of the table being referenced. This
+ * may require special privileges because it has the potential to
+ * interrupt service in the FE for a longer time.
+ */
+#define NLM_F_ATOMIC   0x400
+
+/**
+ * Dump all entries
+ */
+#define NLM_F_DUMP     (NLM_F_ROOT|NLM_F_MATCH)
+
+/** @} */
+
+/**
+ * @name Additional messsage flags for NEW requests
+ * @{
+ */
+
+/**
+ * Replace existing matching config object with this request.
+ * @ingroup msg
+ */
+#define NLM_F_REPLACE  0x100
+
+/**
+ * Don't replace the config object if it already exists.
+ */
+#define NLM_F_EXCL     0x200
+
+/**
+ * Create config object if it doesn't already exist.
+ */
+#define NLM_F_CREATE   0x400
+
+/**
+ * Add to the end of the object list.
+ */
+#define NLM_F_APPEND   0x800
+
+/** @} */
+
+/**
+ * @name Standard Message types
+ * @{
+ */
+
+/**
+ * No operation, message must be ignored
+ * @ingroup msg
+ */
+#define NLMSG_NOOP             0x1
+
+/**
+ * The message signals an error and the payload contains a nlmsgerr
+ * structure. This can be looked at as a NACK and typically it is
+ * from FEC to CPC.
+ */
+#define NLMSG_ERROR            0x2
+
+/**
+ * Message terminates a multipart message.
+ */
+#define NLMSG_DONE             0x3
+
+/**
+ * The message signals that data got lost
+ */
+#define NLMSG_OVERRUN          0x4
+
+/**
+ * Lower limit of reserved message types
+ */
+#define NLMSG_MIN_TYPE         0x10
+
+/** @} */
+
+/**
+ * Netlink error message
+ * @ingroup msg
+ */
+struct nlmsgerr
+{
+       /** Error code (errno number) */
+       int             error;
+
+       /** Original netlink message causing the error */
+       struct nlmsghdr msg;
+};
+
+struct nl_pktinfo
+{
+       __u32   group;
+};
+
+#endif /* __LINUX_NETLINK_H */
diff --git a/package/libnl-tiny/src/include/netlink/netlink.h b/package/libnl-tiny/src/include/netlink/netlink.h
new file mode 100644 (file)
index 0000000..e06fb7f
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * netlink/netlink.h           Netlink Interface
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_NETLINK_H_
+#define NETLINK_NETLINK_H_
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <netlink/netlink-compat.h>
+#include <linux/netlink.h>
+#include <linux/genetlink.h>
+#include <netlink/version.h>
+#include <netlink/errno.h>
+#include <netlink/types.h>
+#include <netlink/handlers.h>
+#include <netlink/socket.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int nl_debug;
+extern struct nl_dump_params nl_debug_dp;
+
+/* Connection Management */
+extern int                     nl_connect(struct nl_sock *, int);
+extern void                    nl_close(struct nl_sock *);
+
+/* Send */
+extern int                     nl_sendto(struct nl_sock *, void *, size_t);
+extern int                     nl_sendmsg(struct nl_sock *, struct nl_msg *,
+                                          struct msghdr *);
+extern int                     nl_send(struct nl_sock *, struct nl_msg *);
+extern int                     nl_send_auto_complete(struct nl_sock *,
+                                                     struct nl_msg *);
+extern int                     nl_send_simple(struct nl_sock *, int, int,
+                                              void *, size_t);
+
+/* Receive */
+extern int                     nl_recv(struct nl_sock *,
+                                       struct sockaddr_nl *, unsigned char **,
+                                       struct ucred **);
+extern int                     nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb);
+
+extern int                     nl_wait_for_ack(struct nl_sock *);
+
+/* Netlink Family Translations */
+extern char *                  nl_nlfamily2str(int, char *, size_t);
+extern int                     nl_str2nlfamily(const char *);
+
+/**
+ * Receive a set of message from a netlink socket using handlers in nl_sock.
+ * @arg sk             Netlink socket.
+ *
+ * Calls nl_recvmsgs() with the handlers configured in the netlink socket.
+ */
+static inline int nl_recvmsgs_default(struct nl_sock *sk)
+{
+       return nl_recvmsgs(sk, sk->s_cb);
+}
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/object-api.h b/package/libnl-tiny/src/include/netlink/object-api.h
new file mode 100644 (file)
index 0000000..8a44fe9
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * netlink/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-2007 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_OBJECT_API_H_
+#define NETLINK_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 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(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;
+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
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/object.h b/package/libnl-tiny/src/include/netlink/object.h
new file mode 100644 (file)
index 0000000..87385c4
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * netlink/object.c    Generic Cacheable Object
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_OBJECT_H_
+#define NETLINK_OBJECT_H_
+
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/object-api.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NL_OBJ_MARK            1
+
+struct nl_cache;
+struct nl_object;
+struct nl_object_ops;
+
+struct nl_object
+{
+       NLHDR_COMMON
+};
+
+#define OBJ_CAST(ptr)          ((struct nl_object *) (ptr))
+
+/* General */
+extern struct nl_object *      nl_object_alloc(struct nl_object_ops *);
+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 void                    nl_object_get(struct nl_object *);
+extern void                    nl_object_put(struct nl_object *);
+extern void                    nl_object_dump(struct nl_object *,
+                                              struct nl_dump_params *);
+extern int                     nl_object_identical(struct nl_object *,
+                                                   struct nl_object *);
+extern uint32_t                        nl_object_diff(struct nl_object *,
+                                              struct nl_object *);
+extern int                     nl_object_match_filter(struct nl_object *,
+                                                      struct nl_object *);
+extern char *                  nl_object_attrs2str(struct nl_object *,
+                                                   uint32_t attrs, char *buf,
+                                                   size_t);
+/**
+ * Check whether this object is used by multiple users
+ * @arg obj            object to check
+ * @return true or false
+ */
+static inline int nl_object_shared(struct nl_object *obj)
+{
+       return obj->ce_refcnt > 1;
+}
+
+
+
+/**
+ * @name Marks
+ * @{
+ */
+
+/**
+ * Add mark to object
+ * @arg obj            Object to mark
+ */
+static inline void nl_object_mark(struct nl_object *obj)
+{
+       obj->ce_flags |= NL_OBJ_MARK;
+}
+
+/**
+ * Remove mark from object
+ * @arg obj            Object to unmark
+ */
+static inline void nl_object_unmark(struct nl_object *obj)
+{
+       obj->ce_flags &= ~NL_OBJ_MARK;
+}
+
+/**
+ * Return true if object is marked
+ * @arg obj            Object to check
+ * @return true if object is marked, otherwise false
+ */
+static inline int nl_object_is_marked(struct nl_object *obj)
+{
+       return (obj->ce_flags & NL_OBJ_MARK);
+}
+
+/** @} */
+
+/**
+ * Return list of attributes present in an object
+ * @arg obj            an object
+ * @arg buf            destination buffer
+ * @arg len            length of destination buffer
+ *
+ * @return destination buffer.
+ */
+static inline char *nl_object_attr_list(struct nl_object *obj, char *buf, size_t len)
+{
+       return nl_object_attrs2str(obj, obj->ce_mask, buf, len);
+}
+
+
+/**
+ * @name Attributes
+ * @{
+ */
+
+static inline int nl_object_get_refcnt(struct nl_object *obj)
+{
+       return obj->ce_refcnt;
+}
+
+static inline struct nl_cache *nl_object_get_cache(struct nl_object *obj)
+{
+       return obj->ce_cache;
+}
+
+static inline void *           nl_object_priv(struct nl_object *obj)
+{
+       return obj;
+}
+
+
+/** @} */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/socket.h b/package/libnl-tiny/src/include/netlink/socket.h
new file mode 100644 (file)
index 0000000..9f7f422
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * netlink/socket.h            Netlink Socket
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_SOCKET_H_
+#define NETLINK_SOCKET_H_
+
+#include <netlink/types.h>
+#include <netlink/handlers.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NL_SOCK_BUFSIZE_SET    (1<<0)
+#define NL_SOCK_PASSCRED       (1<<1)
+#define NL_OWN_PORT            (1<<2)
+#define NL_MSG_PEEK            (1<<3)
+#define NL_NO_AUTO_ACK         (1<<4)
+
+struct nl_cb;
+struct nl_sock
+{
+       struct sockaddr_nl      s_local;
+       struct sockaddr_nl      s_peer;
+       int                     s_fd;
+       int                     s_proto;
+       unsigned int            s_seq_next;
+       unsigned int            s_seq_expect;
+       int                     s_flags;
+       struct nl_cb *          s_cb;
+};
+
+
+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 void            nl_socket_set_local_port(struct nl_sock *, uint32_t);
+
+extern int             nl_socket_add_memberships(struct nl_sock *, int, ...);
+extern int             nl_socket_drop_memberships(struct nl_sock *, int, ...);
+
+extern int             nl_socket_set_buffer_size(struct nl_sock *, int, int);
+extern int             nl_socket_set_passcred(struct nl_sock *, int);
+extern int             nl_socket_recv_pktinfo(struct nl_sock *, int);
+
+extern void            nl_socket_disable_seq_check(struct nl_sock *);
+
+extern int             nl_socket_set_nonblocking(struct nl_sock *);
+
+/**
+ * Use next sequence number
+ * @arg sk             Netlink socket.
+ *
+ * Uses the next available sequence number and increases the counter
+ * by one for subsequent calls.
+ *
+ * @return Unique serial sequence number
+ */
+static inline unsigned int nl_socket_use_seq(struct nl_sock *sk)
+{
+       return sk->s_seq_next++;
+}
+
+/**
+ * Disable automatic request for ACK
+ * @arg sk             Netlink socket.
+ *
+ * The default behaviour of a socket is to request an ACK for
+ * each message sent to allow for the caller to synchronize to
+ * the completion of the netlink operation. This function
+ * disables this behaviour and will result in requests being
+ * sent which will not have the NLM_F_ACK flag set automatically.
+ * However, it is still possible for the caller to set the
+ * NLM_F_ACK flag explicitely.
+ */
+static inline void nl_socket_disable_auto_ack(struct nl_sock *sk)
+{
+       sk->s_flags |= NL_NO_AUTO_ACK;
+}
+
+/**
+ * Enable automatic request for ACK (default)
+ * @arg sk             Netlink socket.
+ * @see nl_socket_disable_auto_ack
+ */
+static inline void nl_socket_enable_auto_ack(struct nl_sock *sk)
+{
+       sk->s_flags &= ~NL_NO_AUTO_ACK;
+}
+
+/**
+ * @name Source Idenficiation
+ * @{
+ */
+
+static inline uint32_t nl_socket_get_local_port(struct nl_sock *sk)
+{
+       return sk->s_local.nl_pid;
+}
+
+/**
+ * Join multicast groups (deprecated)
+ * @arg sk             Netlink socket.
+ * @arg groups         Bitmask of groups to join.
+ *
+ * This function defines the old way of joining multicast group which
+ * has to be done prior to calling nl_connect(). It works on any kernel
+ * version but is very limited as only 32 groups can be joined.
+ */
+static inline void nl_join_groups(struct nl_sock *sk, int groups)
+{
+       sk->s_local.nl_groups |= groups;
+}
+
+/**
+ * @name Peer Identfication
+ * @{
+ */
+
+static inline uint32_t nl_socket_get_peer_port(struct nl_sock *sk)
+{
+       return sk->s_peer.nl_pid;
+}
+
+static inline void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port)
+{
+       sk->s_peer.nl_pid = port;
+}
+
+/** @} */
+
+/**
+ * @name File Descriptor
+ * @{
+ */
+
+static inline int nl_socket_get_fd(struct nl_sock *sk)
+{
+       return sk->s_fd;
+}
+
+/**
+ * Enable use of MSG_PEEK when reading from socket
+ * @arg sk             Netlink socket.
+ */
+static inline void nl_socket_enable_msg_peek(struct nl_sock *sk)
+{
+       sk->s_flags |= NL_MSG_PEEK;
+}
+
+/**
+ * Disable use of MSG_PEEK when reading from socket
+ * @arg sk             Netlink socket.
+ */
+static inline void nl_socket_disable_msg_peek(struct nl_sock *sk)
+{
+       sk->s_flags &= ~NL_MSG_PEEK;
+}
+
+/**
+ * @name Callback Handler
+ * @{
+ */
+
+static inline struct nl_cb *nl_socket_get_cb(struct nl_sock *sk)
+{
+       return nl_cb_get(sk->s_cb);
+}
+
+static inline void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb)
+{
+       nl_cb_put(sk->s_cb);
+       sk->s_cb = nl_cb_get(cb);
+}
+
+/**
+ * Modify the callback handler associated to 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
+ *
+ * @see nl_cb_set
+ */
+static inline 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)
+{
+       return nl_cb_set(sk->s_cb, type, kind, func, arg);
+}
+
+/** @} */
+
+static inline int nl_socket_add_membership(struct nl_sock *sk, int group)
+{
+       return nl_socket_add_memberships(sk, group, 0);
+}
+
+
+static inline int nl_socket_drop_membership(struct nl_sock *sk, int group)
+{
+       return nl_socket_drop_memberships(sk, group, 0);
+}
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/types.h b/package/libnl-tiny/src/include/netlink/types.h
new file mode 100644 (file)
index 0000000..6d8aaf8
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * netlink/netlink-types.h     Netlink Types
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef __NETLINK_TYPES_H_
+#define __NETLINK_TYPES_H_
+
+#include <stdio.h>
+
+/**
+ * Dumping types (dp_type)
+ * @ingroup utils
+ */
+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
+ */
+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;
+};
+
+#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; })
+
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/utils.h b/package/libnl-tiny/src/include/netlink/utils.h
new file mode 100644 (file)
index 0000000..480bab6
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * netlink/utils.h             Utility Functions
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+#ifndef NETLINK_UTILS_H_
+#define NETLINK_UTILS_H_
+
+#include <netlink/netlink.h>
+#include <netlink/list.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @name Probability Constants
+ * @{
+ */
+
+/**
+ * Lower probability limit
+ * @ingroup utils
+ */
+#define NL_PROB_MIN 0x0
+
+/**
+ * Upper probability limit
+ * @ingroup utils
+ */
+#define NL_PROB_MAX 0xffffffff
+
+/** @} */
+
+/* unit pretty-printing */
+extern double  nl_cancel_down_bytes(unsigned long long, char **);
+extern double  nl_cancel_down_bits(unsigned long long, char **);
+extern double  nl_cancel_down_us(uint32_t, char **);
+
+/* generic unit translations */
+extern long    nl_size2int(const char *);
+extern long    nl_prob2int(const char *);
+
+/* time translations */
+extern int     nl_get_hz(void);
+extern uint32_t        nl_us2ticks(uint32_t);
+extern uint32_t        nl_ticks2us(uint32_t);
+extern int     nl_str2msec(const char *, uint64_t *);
+extern char *  nl_msec2str(uint64_t, char *, size_t);
+
+/* link layer protocol translations */
+extern char *  nl_llproto2str(int, char *, size_t);
+extern int     nl_str2llproto(const char *);
+
+/* ethernet protocol translations */
+extern char *  nl_ether_proto2str(int, char *, size_t);
+extern int     nl_str2ether_proto(const char *);
+
+/* IP protocol translations */
+extern char *  nl_ip_proto2str(int, char *, size_t);
+extern int     nl_str2ip_proto(const char *);
+
+/* Dumping helpers */
+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 *, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/package/libnl-tiny/src/include/netlink/version.h b/package/libnl-tiny/src/include/netlink/version.h
new file mode 100644 (file)
index 0000000..6a316d4
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * netlink/version.h   Compile Time 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>
+ */
+
+#ifndef NETLINK_VERSION_H_
+#define NETLINK_VERSION_H_
+
+#define LIBNL_STRING "libnl"
+#define LIBNL_VERSION "2.0"
+
+#endif
diff --git a/package/libnl-tiny/src/msg.c b/package/libnl-tiny/src/msg.c
new file mode 100644 (file)
index 0000000..e84c281
--- /dev/null
@@ -0,0 +1,566 @@
+/*
+ * lib/msg.c           Netlink Messages Interface
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup core
+ * @defgroup msg Messages
+ * Netlink Message Construction/Parsing Interface
+ * 
+ * The following information is partly extracted from RFC3549
+ * (ftp://ftp.rfc-editor.org/in-notes/rfc3549.txt)
+ *
+ * @par Message Format
+ * Netlink messages consist of a byte stream with one or multiple
+ * Netlink headers and an associated payload.  If the payload is too big
+ * to fit into a single message it, can be split over multiple Netlink
+ * messages, collectively called a multipart message.  For multipart
+ * messages, the first and all following headers have the \c NLM_F_MULTI
+ * Netlink header flag set, except for the last header which has the
+ * Netlink header type \c NLMSG_DONE.
+ *
+ * @par
+ * The Netlink message header (\link nlmsghdr struct nlmsghdr\endlink) is shown below.
+ * @code   
+ * 0                   1                   2                   3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                          Length                             |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |            Type              |           Flags              |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                      Sequence Number                        |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                      Process ID (PID)                       |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * @endcode
+ *
+ * @par
+ * The netlink message header and payload must be aligned properly:
+ * @code
+ *  <------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) --->
+ * +----------------------------+- - -+- - - - - - - - - - -+- - -+
+ * |           Header           | Pad |       Payload       | Pad |
+ * |      struct nlmsghdr       |     |                     |     |
+ * +----------------------------+- - -+- - - - - - - - - - -+- - -+
+ * @endcode
+ * @par
+ * Message Format:
+ * @code
+ *    <--- nlmsg_total_size(payload)  --->
+ *    <-- nlmsg_msg_size(payload) ->
+ *   +----------+- - -+-------------+- - -+-------- - -
+ *   | nlmsghdr | Pad |   Payload   | Pad | nlmsghdr
+ *   +----------+- - -+-------------+- - -+-------- - -
+ *   nlmsg_data(nlh)---^                   ^
+ *   nlmsg_next(nlh)-----------------------+
+ * @endcode
+ * @par
+ * The payload may consist of arbitary data but may have strict
+ * alignment and formatting rules depening on the specific netlink
+ * families.
+ * @par
+ * @code
+ *    <---------------------- nlmsg_len(nlh) --------------------->
+ *    <------ hdrlen ------>       <- nlmsg_attrlen(nlh, hdrlen) ->
+ *   +----------------------+- - -+--------------------------------+
+ *   |     Family Header    | Pad |           Attributes           |
+ *   +----------------------+- - -+--------------------------------+
+ *   nlmsg_attrdata(nlh, hdrlen)---^
+ * @endcode
+ * @par The ACK Netlink Message
+ * This message is actually used to denote both an ACK and a NACK.
+ * Typically, the direction is from FEC to CPC (in response to an ACK
+ * request message).  However, the CPC should be able to send ACKs back
+ * to FEC when requested.
+ * @code
+ *  0                   1                   2                   3
+ *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                       Netlink message header                  |
+ * |                       type = NLMSG_ERROR                      |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                          Error code                           |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |                       OLD Netlink message header              |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * @endcode
+ *
+ * @par 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
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/cache.h>
+#include <netlink/attr.h>
+#include <netlink/msg.h>
+#include <linux/socket.h>
+
+static size_t default_msg_size;
+
+static void __init init_msg_size(void)
+{
+       default_msg_size = getpagesize();
+}
+
+/**
+ * @name Attribute Access
+ * @{
+ */
+
+//** @} */
+
+/**
+ * @name Message Parsing
+ * @{
+ */
+
+/**
+ * check if the netlink message fits into the remaining bytes
+ * @arg nlh            netlink message header
+ * @arg remaining      number of bytes remaining in message stream
+ */
+int nlmsg_ok(const struct nlmsghdr *nlh, int remaining)
+{
+       return (remaining >= sizeof(struct nlmsghdr) &&
+               nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
+               nlh->nlmsg_len <= remaining);
+}
+
+/**
+ * next netlink message in message stream
+ * @arg nlh            netlink message header
+ * @arg remaining      number of bytes remaining in message stream
+ *
+ * @returns the next netlink message in the message stream and
+ * decrements remaining by the size of the current message.
+ */
+struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining)
+{
+       int totlen = NLMSG_ALIGN(nlh->nlmsg_len);
+
+       *remaining -= totlen;
+
+       return (struct nlmsghdr *) ((unsigned char *) nlh + totlen);
+}
+
+/**
+ * parse attributes of a netlink message
+ * @arg nlh            netlink message header
+ * @arg hdrlen         length of family specific header
+ * @arg tb             destination array with maxtype+1 elements
+ * @arg maxtype                maximum attribute type to be expected
+ * @arg policy         validation policy
+ *
+ * See nla_parse()
+ */
+int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
+               int maxtype, struct nla_policy *policy)
+{
+       if (!nlmsg_valid_hdr(nlh, hdrlen))
+               return -NLE_MSG_TOOSHORT;
+
+       return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen),
+                        nlmsg_attrlen(nlh, hdrlen), policy);
+}
+
+/**
+ * nlmsg_validate - validate a netlink message including attributes
+ * @arg nlh            netlinket message header
+ * @arg hdrlen         length of familiy specific header
+ * @arg maxtype                maximum attribute type to be expected
+ * @arg policy         validation policy
+ */
+int nlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype,
+                  struct nla_policy *policy)
+{
+       if (!nlmsg_valid_hdr(nlh, hdrlen))
+               return -NLE_MSG_TOOSHORT;
+
+       return nla_validate(nlmsg_attrdata(nlh, hdrlen),
+                           nlmsg_attrlen(nlh, hdrlen), maxtype, policy);
+}
+
+/** @} */
+
+/**
+ * @name Message Building/Access
+ * @{
+ */
+
+static struct nl_msg *__nlmsg_alloc(size_t len)
+{
+       struct nl_msg *nm;
+
+       nm = calloc(1, sizeof(*nm));
+       if (!nm)
+               goto errout;
+
+       nm->nm_refcnt = 1;
+
+       nm->nm_nlh = malloc(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);
+
+       NL_DBG(2, "msg %p: Allocated new message, maxlen=%zu\n", nm, len);
+
+       return nm;
+errout:
+       free(nm);
+       return NULL;
+}
+
+/**
+ * Allocate a new netlink message with the default maximum payload size.
+ *
+ * Allocates a new netlink message without any further payload. The
+ * maximum payload size defaults to PAGESIZE or as otherwise specified
+ * with nlmsg_set_default_size().
+ *
+ * @return Newly allocated netlink message or NULL.
+ */
+struct nl_msg *nlmsg_alloc(void)
+{
+       return __nlmsg_alloc(default_msg_size);
+}
+
+/**
+ * Allocate a new netlink message with maximum payload size specified.
+ */
+struct nl_msg *nlmsg_alloc_size(size_t max)
+{
+       return __nlmsg_alloc(max);
+}
+
+/**
+ * Allocate a new netlink message and inherit netlink message header
+ * @arg hdr            Netlink message header template
+ *
+ * Allocates a new netlink message and inherits the original message
+ * header. If \a hdr is not NULL it will be used as a template for
+ * the netlink message header, otherwise the header is left blank.
+ * 
+ * @return Newly allocated netlink message or NULL
+ */
+struct nl_msg *nlmsg_inherit(struct nlmsghdr *hdr)
+{
+       struct nl_msg *nm;
+
+       nm = nlmsg_alloc();
+       if (nm && hdr) {
+               struct nlmsghdr *new = nm->nm_nlh;
+
+               new->nlmsg_type = hdr->nlmsg_type;
+               new->nlmsg_flags = hdr->nlmsg_flags;
+               new->nlmsg_seq = hdr->nlmsg_seq;
+               new->nlmsg_pid = hdr->nlmsg_pid;
+       }
+
+       return nm;
+}
+
+/**
+ * Allocate a new netlink message
+ * @arg nlmsgtype      Netlink message type
+ * @arg flags          Message flags.
+ *
+ * @return Newly allocated netlink message or NULL.
+ */
+struct nl_msg *nlmsg_alloc_simple(int nlmsgtype, int flags)
+{
+       struct nl_msg *msg;
+       struct nlmsghdr nlh = {
+               .nlmsg_type = nlmsgtype,
+               .nlmsg_flags = flags,
+       };
+
+       msg = nlmsg_inherit(&nlh);
+       if (msg)
+               NL_DBG(2, "msg %p: Allocated new simple message\n", msg);
+
+       return msg;
+}
+
+/**
+ * Set the default maximum message payload size for allocated messages
+ * @arg max            Size of payload in bytes.
+ */
+void nlmsg_set_default_size(size_t max)
+{
+       if (max < nlmsg_total_size(0))
+               max = nlmsg_total_size(0);
+
+       default_msg_size = max;
+}
+
+/**
+ * Convert a netlink message received from a netlink socket to a nl_msg
+ * @arg hdr            Netlink message received from netlink socket.
+ *
+ * Allocates a new netlink message and copies all of the data pointed to
+ * by \a hdr into the new message object.
+ *
+ * @return Newly allocated netlink message or NULL.
+ */
+struct nl_msg *nlmsg_convert(struct nlmsghdr *hdr)
+{
+       struct nl_msg *nm;
+
+       nm = __nlmsg_alloc(NLMSG_ALIGN(hdr->nlmsg_len));
+       if (!nm)
+               goto errout;
+
+       memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len);
+
+       return nm;
+errout:
+       nlmsg_free(nm);
+       return NULL;
+}
+
+/**
+ * Reserve room for additional data in a netlink message
+ * @arg n              netlink message
+ * @arg len            length of additional data to reserve room for
+ * @arg pad            number of bytes to align data to
+ *
+ * Reserves room for additional data at the tail of the an
+ * existing netlink message. Eventual padding required will
+ * be zeroed out.
+ *
+ * @return Pointer to start of additional data tailroom or NULL.
+ */
+void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad)
+{
+       void *buf = n->nm_nlh;
+       size_t nlmsg_len = n->nm_nlh->nlmsg_len;
+       size_t tlen;
+
+       tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len;
+
+       if ((tlen + nlmsg_len) > n->nm_size)
+               return NULL;
+
+       buf += nlmsg_len;
+       n->nm_nlh->nlmsg_len += tlen;
+
+       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);
+
+       return buf;
+}
+
+/**
+ * Append data to tail of a netlink message
+ * @arg n              netlink message
+ * @arg data           data to add
+ * @arg len            length of data
+ * @arg pad            Number of bytes to align data to.
+ *
+ * Extends the netlink message as needed and appends the data of given
+ * length to the message. 
+ *
+ * @return 0 on success or a negative error code
+ */
+int nlmsg_append(struct nl_msg *n, void *data, size_t len, int pad)
+{
+       void *tmp;
+
+       tmp = nlmsg_reserve(n, len, pad);
+       if (tmp == NULL)
+               return -NLE_NOMEM;
+
+       memcpy(tmp, data, len);
+       NL_DBG(2, "msg %p: Appended %zu bytes with padding %d\n", n, len, pad);
+
+       return 0;
+}
+
+/**
+ * Add a netlink message header to a netlink message
+ * @arg n              netlink message
+ * @arg pid            netlink process id or NL_AUTO_PID
+ * @arg seq            sequence number of message or NL_AUTO_SEQ
+ * @arg type           message type
+ * @arg payload                length of message payload
+ * @arg flags          message flags
+ *
+ * Adds or overwrites the netlink message header in an existing message
+ * object. If \a payload is greater-than zero additional room will be
+ * reserved, f.e. for family specific headers. It can be accesed via
+ * nlmsg_data().
+ *
+ * @return A pointer to the netlink message header or NULL.
+ */
+struct nlmsghdr *nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq,
+                          int type, int payload, int flags)
+{
+       struct nlmsghdr *nlh;
+
+       if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN)
+               BUG();
+
+       nlh = (struct nlmsghdr *) n->nm_nlh;
+       nlh->nlmsg_type = type;
+       nlh->nlmsg_flags = flags;
+       nlh->nlmsg_pid = pid;
+       nlh->nlmsg_seq = seq;
+
+       NL_DBG(2, "msg %p: Added netlink header type=%d, flags=%d, pid=%d, "
+                 "seq=%d\n", n, type, flags, pid, seq);
+
+       if (payload > 0 &&
+           nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL)
+               return NULL;
+
+       return nlh;
+}
+
+/**
+ * Release a reference from an netlink message
+ * @arg msg            message to release reference from
+ *
+ * Frees memory after the last reference has been released.
+ */
+void nlmsg_free(struct nl_msg *msg)
+{
+       if (!msg)
+               return;
+
+       msg->nm_refcnt--;
+       NL_DBG(4, "Returned message reference %p, %d remaining\n",
+              msg, msg->nm_refcnt);
+
+       if (msg->nm_refcnt < 0)
+               BUG();
+
+       if (msg->nm_refcnt <= 0) {
+               free(msg->nm_nlh);
+               free(msg);
+               NL_DBG(2, "msg %p: Freed\n", msg);
+       }
+}
+
+/** @} */
+
+/**
+ * @name Direct Parsing
+ * @{
+ */
+
+/** @cond SKIP */
+struct dp_xdata {
+       void (*cb)(struct nl_object *, void *);
+       void *arg;
+};
+/** @endcond */
+
+static int parse_cb(struct nl_object *obj, struct nl_parser_param *p)
+{
+       struct dp_xdata *x = p->pp_arg;
+
+       x->cb(obj, x->arg);
+       return 0;
+}
+
+int nl_msg_parse(struct nl_msg *msg, void (*cb)(struct nl_object *, void *),
+                void *arg)
+{
+       struct nl_cache_ops *ops;
+       struct nl_parser_param p = {
+               .pp_cb = parse_cb
+       };
+       struct dp_xdata x = {
+               .cb = cb,
+               .arg = arg,
+       };
+
+       ops = nl_cache_ops_associate(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);
+}
+
+/** @} */
diff --git a/package/libnl-tiny/src/nl.c b/package/libnl-tiny/src/nl.c
new file mode 100644 (file)
index 0000000..99e9aeb
--- /dev/null
@@ -0,0 +1,720 @@
+/*
+ * lib/nl.c            Core Netlink Interface
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 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
+ * 
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+/**
+ * @name Connection Management
+ * @{
+ */
+
+/**
+ * Create and connect netlink socket.
+ * @arg sk             Netlink socket.
+ * @arg protocol       Netlink protocol to use.
+ *
+ * Creates a netlink socket using the specified protocol, binds the socket
+ * and issues a connection attempt.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nl_connect(struct nl_sock *sk, int protocol)
+{
+       int err;
+       socklen_t addrlen;
+
+       sk->s_fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+       if (sk->s_fd < 0) {
+               err = -nl_syserr2nlerr(errno);
+               goto errout;
+       }
+
+       if (!(sk->s_flags & NL_SOCK_BUFSIZE_SET)) {
+               err = nl_socket_set_buffer_size(sk, 0, 0);
+               if (err < 0)
+                       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;
+       }
+
+       addrlen = sizeof(sk->s_local);
+       err = getsockname(sk->s_fd, (struct sockaddr *) &sk->s_local,
+                         &addrlen);
+       if (err < 0) {
+               err = -nl_syserr2nlerr(errno);
+               goto errout;
+       }
+
+       if (addrlen != sizeof(sk->s_local)) {
+               err = -NLE_NOADDR;
+               goto errout;
+       }
+
+       if (sk->s_local.nl_family != AF_NETLINK) {
+               err = -NLE_AF_NOSUPPORT;
+               goto errout;
+       }
+
+       sk->s_proto = protocol;
+
+       return 0;
+errout:
+       close(sk->s_fd);
+       sk->s_fd = -1;
+
+       return err;
+}
+
+/**
+ * Close/Disconnect netlink socket.
+ * @arg sk             Netlink socket.
+ */
+void nl_close(struct nl_sock *sk)
+{
+       if (sk->s_fd >= 0) {
+               close(sk->s_fd);
+               sk->s_fd = -1;
+       }
+
+       sk->s_proto = 0;
+}
+
+/** @} */
+
+/**
+ * @name Send
+ * @{
+ */
+
+/**
+ * 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.
+ */
+int nl_sendto(struct nl_sock *sk, void *buf, size_t size)
+{
+       int ret;
+
+       ret = sendto(sk->s_fd, buf, size, 0, (struct sockaddr *)
+                    &sk->s_peer, sizeof(sk->s_peer));
+       if (ret < 0)
+               return -nl_syserr2nlerr(errno);
+
+       return ret;
+}
+
+/**
+ * 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.
+ */
+int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr)
+{
+       struct nl_cb *cb;
+       int ret;
+
+       struct iovec iov = {
+               .iov_base = (void *) nlmsg_hdr(msg),
+               .iov_len = nlmsg_hdr(msg)->nlmsg_len,
+       };
+
+       hdr->msg_iov = &iov;
+       hdr->msg_iovlen = 1;
+
+       nlmsg_set_src(msg, &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;
+
+       ret = sendmsg(sk->s_fd, hdr, 0);
+       if (ret < 0)
+               return -nl_syserr2nlerr(errno);
+
+       return ret;
+}
+
+
+/**
+ * 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.
+ */
+int nl_send(struct nl_sock *sk, struct nl_msg *msg)
+{
+       struct sockaddr_nl *dst;
+       struct ucred *creds;
+       
+       struct msghdr hdr = {
+               .msg_name = (void *) &sk->s_peer,
+               .msg_namelen = sizeof(struct sockaddr_nl),
+       };
+
+       /* Overwrite destination if specified in the message itself, defaults
+        * to the peer address of the socket.
+        */
+       dst = nlmsg_get_dst(msg);
+       if (dst->nl_family == AF_NETLINK)
+               hdr.msg_name = dst;
+
+       /* Add credentials if present. */
+       creds = nlmsg_get_creds(msg);
+       if (creds != NULL) {
+               char buf[CMSG_SPACE(sizeof(struct ucred))];
+               struct cmsghdr *cmsg;
+
+               hdr.msg_control = buf;
+               hdr.msg_controllen = sizeof(buf);
+
+               cmsg = CMSG_FIRSTHDR(&hdr);
+               cmsg->cmsg_level = SOL_SOCKET;
+               cmsg->cmsg_type = SCM_CREDENTIALS;
+               cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+               memcpy(CMSG_DATA(cmsg), creds, sizeof(struct ucred));
+       }
+
+       return nl_sendmsg(sk, msg, &hdr);
+}
+
+/**
+ * Send netlink message and check & extend header values as needed.
+ * @arg sk             Netlink socket.
+ * @arg msg            Netlink message to be sent.
+ *
+ * Checks the netlink message \c nlh for completness and extends it
+ * as required before sending it out. Checked fields include pid,
+ * sequence nr, and flags.
+ *
+ * @see nl_send()
+ * @return Number of characters sent or a negative error code.
+ */
+int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
+{
+       struct nlmsghdr *nlh;
+       struct nl_cb *cb = sk->s_cb;
+
+       nlh = nlmsg_hdr(msg);
+       if (nlh->nlmsg_pid == 0)
+               nlh->nlmsg_pid = sk->s_local.nl_pid;
+
+       if (nlh->nlmsg_seq == 0)
+               nlh->nlmsg_seq = sk->s_seq_next++;
+
+       if (msg->nm_protocol == -1)
+               msg->nm_protocol = sk->s_proto;
+       
+       nlh->nlmsg_flags |= NLM_F_REQUEST;
+
+       if (!(sk->s_flags & NL_NO_AUTO_ACK))
+               nlh->nlmsg_flags |= NLM_F_ACK;
+
+       if (cb->cb_send_ow)
+               return cb->cb_send_ow(sk, msg);
+       else
+               return nl_send(sk, msg);
+}
+
+/**
+ * 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.
+ *
+ * Builds a netlink message with the specified type and flags and
+ * appends the specified data as payload to the message.
+ *
+ * @see nl_send_auto_complete()
+ * @return Number of characters sent on success or a negative error code.
+ */
+int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf,
+                  size_t size)
+{
+       int err;
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc_simple(type, flags);
+       if (!msg)
+               return -NLE_NOMEM;
+
+       if (buf && size) {
+               err = nlmsg_append(msg, buf, size, NLMSG_ALIGNTO);
+               if (err < 0)
+                       goto errout;
+       }
+       
+
+       err = nl_send_auto_complete(sk, msg);
+errout:
+       nlmsg_free(msg);
+
+       return err;
+}
+
+/** @} */
+
+/**
+ * @name Receive
+ * @{
+ */
+
+/**
+ * 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.
+ *
+ * Receives a netlink message, allocates a buffer in \c *buf and
+ * stores the message content. The peer's netlink address is stored
+ * in \c *nla. The caller is responsible for freeing the buffer allocated
+ * in \c *buf if a positive value is returned.  Interruped system calls
+ * are handled by repeating the read. The input buffer size is determined
+ * by peeking before the actual read is done.
+ *
+ * A non-blocking sockets causes the function to return immediately with
+ * a return value of 0 if no data is available.
+ *
+ * @return Number of octets read, 0 on EOF or a negative error code.
+ */
+int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla,
+           unsigned char **buf, struct ucred **creds)
+{
+       int n;
+       int flags = 0;
+       static int page_size = 0;
+       struct iovec iov;
+       struct msghdr msg = {
+               .msg_name = (void *) nla,
+               .msg_namelen = sizeof(struct sockaddr_nl),
+               .msg_iov = &iov,
+               .msg_iovlen = 1,
+               .msg_control = NULL,
+               .msg_controllen = 0,
+               .msg_flags = 0,
+       };
+       struct cmsghdr *cmsg;
+
+       if (sk->s_flags & NL_MSG_PEEK)
+               flags |= MSG_PEEK;
+
+       if (page_size == 0)
+               page_size = getpagesize();
+
+       iov.iov_len = page_size;
+       iov.iov_base = *buf = malloc(iov.iov_len);
+
+       if (sk->s_flags & NL_SOCK_PASSCRED) {
+               msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred));
+               msg.msg_control = calloc(1, msg.msg_controllen);
+       }
+retry:
+
+       n = recvmsg(sk->s_fd, &msg, flags);
+       if (!n)
+               goto abort;
+       else if (n < 0) {
+               if (errno == EINTR) {
+                       NL_DBG(3, "recvmsg() returned EINTR, retrying\n");
+                       goto retry;
+               } else if (errno == EAGAIN) {
+                       NL_DBG(3, "recvmsg() returned EAGAIN, aborting\n");
+                       goto abort;
+               } else {
+                       free(msg.msg_control);
+                       free(*buf);
+                       return -nl_syserr2nlerr(errno);
+               }
+       }
+
+       if (iov.iov_len < n ||
+           msg.msg_flags & MSG_TRUNC) {
+               /* Provided buffer is not long enough, enlarge it
+                * and try again. */
+               iov.iov_len *= 2;
+               iov.iov_base = *buf = realloc(*buf, iov.iov_len);
+               goto retry;
+       } else if (msg.msg_flags & MSG_CTRUNC) {
+               msg.msg_controllen *= 2;
+               msg.msg_control = realloc(msg.msg_control, msg.msg_controllen);
+               goto retry;
+       } else if (flags != 0) {
+               /* Buffer is big enough, do the actual reading */
+               flags = 0;
+               goto retry;
+       }
+
+       if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
+               free(msg.msg_control);
+               free(*buf);
+               return -NLE_NOADDR;
+       }
+
+       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+               if (cmsg->cmsg_level == SOL_SOCKET &&
+                   cmsg->cmsg_type == SCM_CREDENTIALS) {
+                       *creds = calloc(1, sizeof(struct ucred));
+                       memcpy(*creds, CMSG_DATA(cmsg), sizeof(struct ucred));
+                       break;
+               }
+       }
+
+       free(msg.msg_control);
+       return n;
+
+abort:
+       free(msg.msg_control);
+       free(*buf);
+       return 0;
+}
+
+#define NL_CB_CALL(cb, type, msg) \
+do { \
+       err = nl_cb_call(cb, type, msg); \
+       switch (err) { \
+       case NL_OK: \
+               err = 0; \
+               break; \
+       case NL_SKIP: \
+               goto skip; \
+       case NL_STOP: \
+               goto stop; \
+       default: \
+               goto out; \
+       } \
+} while (0)
+
+static int recvmsgs(struct nl_sock *sk, struct nl_cb *cb)
+{
+       int n, err = 0, multipart = 0;
+       unsigned char *buf = NULL;
+       struct nlmsghdr *hdr;
+       struct sockaddr_nl nla = {0};
+       struct nl_msg *msg = NULL;
+       struct ucred *creds = NULL;
+
+continue_reading:
+       NL_DBG(3, "Attempting to read from %p\n", sk);
+       if (cb->cb_recv_ow)
+               n = cb->cb_recv_ow(sk, &nla, &buf, &creds);
+       else
+               n = nl_recv(sk, &nla, &buf, &creds);
+
+       if (n <= 0)
+               return n;
+
+       NL_DBG(3, "recvmsgs(%p): Read %d bytes\n", sk, n);
+
+       hdr = (struct nlmsghdr *) buf;
+       while (nlmsg_ok(hdr, n)) {
+               NL_DBG(3, "recgmsgs(%p): Processing valid message...\n", sk);
+
+               nlmsg_free(msg);
+               msg = nlmsg_convert(hdr);
+               if (!msg) {
+                       err = -NLE_NOMEM;
+                       goto out;
+               }
+
+               nlmsg_set_proto(msg, sk->s_proto);
+               nlmsg_set_src(msg, &nla);
+               if (creds)
+                       nlmsg_set_creds(msg, creds);
+
+               /* Raw callback is the first, it gives the most control
+                * to the user and he can do his very own parsing. */
+               if (cb->cb_set[NL_CB_MSG_IN])
+                       NL_CB_CALL(cb, NL_CB_MSG_IN, msg);
+
+               /* Sequence number checking. The check may be done by
+                * the user, otherwise a very simple check is applied
+                * enforcing strict ordering */
+               if (cb->cb_set[NL_CB_SEQ_CHECK])
+                       NL_CB_CALL(cb, NL_CB_SEQ_CHECK, msg);
+               else if (hdr->nlmsg_seq != 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;
+                       }
+               }
+
+               if (hdr->nlmsg_type == NLMSG_DONE ||
+                   hdr->nlmsg_type == NLMSG_ERROR ||
+                   hdr->nlmsg_type == NLMSG_NOOP ||
+                   hdr->nlmsg_type == NLMSG_OVERRUN) {
+                       /* We can't check for !NLM_F_MULTI since some netlink
+                        * users in the kernel are broken. */
+                       sk->s_seq_expect++;
+                       NL_DBG(3, "recvmsgs(%p): Increased expected " \
+                              "sequence number to %d\n",
+                              sk, sk->s_seq_expect);
+               }
+
+               if (hdr->nlmsg_flags & NLM_F_MULTI)
+                       multipart = 1;
+       
+               /* Other side wishes to see an ack for this message */
+               if (hdr->nlmsg_flags & NLM_F_ACK) {
+                       if (cb->cb_set[NL_CB_SEND_ACK])
+                               NL_CB_CALL(cb, NL_CB_SEND_ACK, msg);
+                       else {
+                               /* FIXME: implement */
+                       }
+               }
+
+               /* messages terminates a multpart message, this is
+                * usually the end of a message and therefore we slip
+                * out of the loop by default. the user may overrule
+                * this action by skipping this packet. */
+               if (hdr->nlmsg_type == NLMSG_DONE) {
+                       multipart = 0;
+                       if (cb->cb_set[NL_CB_FINISH])
+                               NL_CB_CALL(cb, NL_CB_FINISH, msg);
+               }
+
+               /* Message to be ignored, the default action is to
+                * skip this message if no callback is specified. The
+                * user may overrule this action by returning
+                * NL_PROCEED. */
+               else if (hdr->nlmsg_type == NLMSG_NOOP) {
+                       if (cb->cb_set[NL_CB_SKIPPED])
+                               NL_CB_CALL(cb, NL_CB_SKIPPED, msg);
+                       else
+                               goto skip;
+               }
+
+               /* Data got lost, report back to user. The default action is to
+                * quit parsing. The user may overrule this action by retuning
+                * NL_SKIP or NL_PROCEED (dangerous) */
+               else if (hdr->nlmsg_type == NLMSG_OVERRUN) {
+                       if (cb->cb_set[NL_CB_OVERRUN])
+                               NL_CB_CALL(cb, NL_CB_OVERRUN, msg);
+                       else {
+                               err = -NLE_MSG_OVERFLOW;
+                               goto out;
+                       }
+               }
+
+               /* Message carries a nlmsgerr */
+               else if (hdr->nlmsg_type == NLMSG_ERROR) {
+                       struct nlmsgerr *e = nlmsg_data(hdr);
+
+                       if (hdr->nlmsg_len < nlmsg_msg_size(sizeof(*e))) {
+                               /* Truncated error message, the default action
+                                * is to stop parsing. The user may overrule
+                                * this action by returning NL_SKIP or
+                                * NL_PROCEED (dangerous) */
+                               if (cb->cb_set[NL_CB_INVALID])
+                                       NL_CB_CALL(cb, NL_CB_INVALID, msg);
+                               else {
+                                       err = -NLE_MSG_TRUNC;
+                                       goto out;
+                               }
+                       } else if (e->error) {
+                               /* Error message reported back from kernel. */
+                               if (cb->cb_err) {
+                                       err = cb->cb_err(&nla, e,
+                                                          cb->cb_err_arg);
+                                       if (err < 0)
+                                               goto out;
+                                       else if (err == NL_SKIP)
+                                               goto skip;
+                                       else if (err == NL_STOP) {
+                                               err = -nl_syserr2nlerr(e->error);
+                                               goto out;
+                                       }
+                               } else {
+                                       err = -nl_syserr2nlerr(e->error);
+                                       goto out;
+                               }
+                       } else if (cb->cb_set[NL_CB_ACK])
+                               NL_CB_CALL(cb, NL_CB_ACK, msg);
+               } else {
+                       /* Valid message (not checking for MULTIPART bit to
+                        * get along with broken kernels. NL_SKIP has no
+                        * effect on this.  */
+                       if (cb->cb_set[NL_CB_VALID])
+                               NL_CB_CALL(cb, NL_CB_VALID, msg);
+               }
+skip:
+               err = 0;
+               hdr = nlmsg_next(hdr, &n);
+       }
+       
+       nlmsg_free(msg);
+       free(buf);
+       free(creds);
+       buf = NULL;
+       msg = NULL;
+       creds = NULL;
+
+       if (multipart) {
+               /* Multipart message not yet complete, continue reading */
+               goto continue_reading;
+       }
+stop:
+       err = 0;
+out:
+       nlmsg_free(msg);
+       free(buf);
+       free(creds);
+
+       return err;
+}
+
+/**
+ * Receive a set of messages from a netlink socket.
+ * @arg sk             Netlink socket.
+ * @arg cb             set of callbacks to control behaviour.
+ *
+ * Repeatedly calls nl_recv() or the respective replacement if provided
+ * by the application (see nl_cb_overwrite_recv()) and parses the
+ * received data as netlink messages. Stops reading if one of the
+ * callbacks returns NL_STOP or nl_recv returns either 0 or a negative error code.
+ *
+ * A non-blocking sockets causes the function to return immediately if
+ * no data is available.
+ *
+ * @return 0 on success or a negative error code from nl_recv().
+ */
+int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb)
+{
+       if (cb->cb_recvmsgs_ow)
+               return cb->cb_recvmsgs_ow(sk, cb);
+       else
+               return recvmsgs(sk, cb);
+}
+
+
+static int ack_wait_handler(struct nl_msg *msg, void *arg)
+{
+       return NL_STOP;
+}
+
+/**
+ * Wait for ACK.
+ * @arg sk             Netlink socket.
+ * @pre The netlink socket must be in blocking state.
+ *
+ * Waits until an ACK is received for the latest not yet acknowledged
+ * netlink message.
+ */
+int nl_wait_for_ack(struct nl_sock *sk)
+{
+       int err;
+       struct nl_cb *cb;
+
+       cb = nl_cb_clone(sk->s_cb);
+       if (cb == NULL)
+               return -NLE_NOMEM;
+
+       nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, NULL);
+       err = nl_recvmsgs(sk, cb);
+       nl_cb_put(cb);
+
+       return err;
+}
+
+/** @} */
+
+/** @} */
diff --git a/package/libnl-tiny/src/object.c b/package/libnl-tiny/src/object.c
new file mode 100644 (file)
index 0000000..1b18d9a
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * lib/object.c                Generic Cacheable Object
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup cache
+ * @defgroup object Object
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/object.h>
+#include <netlink/utils.h>
+
+static inline struct nl_object_ops *obj_ops(struct nl_object *obj)
+{
+       if (!obj->ce_ops)
+               BUG();
+
+       return obj->ce_ops;
+}
+
+/**
+ * @name Object Creation/Deletion
+ * @{
+ */
+
+/**
+ * Allocate a new object of kind specified by the operations handle
+ * @arg ops            cache operations handle
+ * @return The new object or NULL
+ */
+struct nl_object *nl_object_alloc(struct nl_object_ops *ops)
+{
+       struct nl_object *new;
+
+       if (ops->oo_size < sizeof(*new))
+               BUG();
+
+       new = calloc(1, ops->oo_size);
+       if (!new)
+               return NULL;
+
+       new->ce_refcnt = 1;
+       nl_init_list_head(&new->ce_list);
+
+       new->ce_ops = ops;
+       if (ops->oo_constructor)
+               ops->oo_constructor(new);
+
+       NL_DBG(4, "Allocated new object %p\n", new);
+
+       return new;
+}
+
+#ifdef disabled
+/**
+ * Allocate a new object of kind specified by the name
+ * @arg kind           name of object type
+ * @return The new object or nULL
+ */
+int nl_object_alloc_name(const char *kind, struct nl_object **result)
+{
+       struct nl_cache_ops *ops;
+
+       ops = nl_cache_ops_lookup(kind);
+       if (!ops)
+               return -NLE_OPNOTSUPP;
+
+       if (!(*result = nl_object_alloc(ops->co_obj_ops)))
+               return -NLE_NOMEM;
+
+       return 0;
+}
+#endif
+
+struct nl_derived_object {
+       NLHDR_COMMON
+       char data;
+};
+
+/**
+ * Allocate a new object and copy all data from an existing object
+ * @arg obj            object to inherite data from
+ * @return The new object or NULL.
+ */
+struct nl_object *nl_object_clone(struct nl_object *obj)
+{
+       struct nl_object *new;
+       struct nl_object_ops *ops = obj_ops(obj);
+       int doff = offsetof(struct nl_derived_object, data);
+       int size;
+
+       new = nl_object_alloc(ops);
+       if (!new)
+               return NULL;
+
+       size = ops->oo_size - doff;
+       if (size < 0)
+               BUG();
+
+       new->ce_ops = obj->ce_ops;
+       new->ce_msgtype = obj->ce_msgtype;
+
+       if (size)
+               memcpy((void *)new + doff, (void *)obj + doff, size);
+
+       if (ops->oo_clone) {
+               if (ops->oo_clone(new, obj) < 0) {
+                       nl_object_free(new);
+                       return NULL;
+               }
+       } else if (size && ops->oo_free_data)
+               BUG();
+
+       return new;
+}
+
+/**
+ * Free a cacheable object
+ * @arg obj            object to free
+ *
+ * @return 0 or a negative error code.
+ */
+void nl_object_free(struct nl_object *obj)
+{
+       struct nl_object_ops *ops = obj_ops(obj);
+
+       if (obj->ce_refcnt > 0)
+               NL_DBG(1, "Warning: Freeing object in use...\n");
+
+       if (obj->ce_cache)
+               nl_cache_remove(obj);
+
+       if (ops->oo_free_data)
+               ops->oo_free_data(obj);
+
+       free(obj);
+
+       NL_DBG(4, "Freed object %p\n", obj);
+}
+
+/** @} */
+
+/**
+ * @name Reference Management
+ * @{
+ */
+
+/**
+ * Acquire a reference on a object
+ * @arg obj            object to acquire reference from
+ */
+void nl_object_get(struct nl_object *obj)
+{
+       obj->ce_refcnt++;
+       NL_DBG(4, "New reference to object %p, total %d\n",
+              obj, obj->ce_refcnt);
+}
+
+/**
+ * Release a reference from an object
+ * @arg obj            object to release reference from
+ */
+void nl_object_put(struct nl_object *obj)
+{
+       if (!obj)
+               return;
+
+       obj->ce_refcnt--;
+       NL_DBG(4, "Returned object reference %p, %d remaining\n",
+              obj, obj->ce_refcnt);
+
+       if (obj->ce_refcnt < 0)
+               BUG();
+
+       if (obj->ce_refcnt <= 0)
+               nl_object_free(obj);
+}
+
+/** @} */
+
+/**
+ * @name Utillities
+ * @{
+ */
+
+#ifdef disabled
+/**
+ * Dump this object according to the specified parameters
+ * @arg obj            object to dump
+ * @arg params         dumping parameters
+ */
+void nl_object_dump(struct nl_object *obj, struct nl_dump_params *params)
+{
+       dump_from_ops(obj, params);
+}
+
+/**
+ * Check if the identifiers of two objects are identical 
+ * @arg a              an object
+ * @arg b              another object of same type
+ *
+ * @return true if both objects have equal identifiers, otherwise false.
+ */
+int nl_object_identical(struct nl_object *a, struct nl_object *b)
+{
+       struct nl_object_ops *ops = obj_ops(a);
+       int req_attrs;
+
+       /* Both objects must be of same type */
+       if (ops != obj_ops(b))
+               return 0;
+
+       req_attrs = ops->oo_id_attrs;
+
+       /* Both objects must provide all required attributes to uniquely
+        * identify an object */
+       if ((a->ce_mask & req_attrs) != req_attrs ||
+           (b->ce_mask & req_attrs) != req_attrs)
+               return 0;
+
+       /* Can't judge unless we can compare */
+       if (ops->oo_compare == NULL)
+               return 0;
+
+       return !(ops->oo_compare(a, b, req_attrs, 0));
+}
+#endif
+
+/**
+ * Compute bitmask representing difference in attribute values
+ * @arg a              an object
+ * @arg b              another object of same type
+ *
+ * The bitmask returned is specific to an object type, each bit set represents
+ * an attribute which mismatches in either of the two objects. Unavailability
+ * of an attribute in one object and presence in the other is regarded a
+ * mismatch as well.
+ *
+ * @return Bitmask describing differences or 0 if they are completely identical.
+ */
+uint32_t nl_object_diff(struct nl_object *a, struct nl_object *b)
+{
+       struct nl_object_ops *ops = obj_ops(a);
+
+       if (ops != obj_ops(b) || ops->oo_compare == NULL)
+               return UINT_MAX;
+
+       return ops->oo_compare(a, b, ~0, 0);
+}
+
+/**
+ * Match a filter against an object
+ * @arg obj            object to check
+ * @arg filter         object of same type acting as filter
+ *
+ * @return 1 if the object matches the filter or 0
+ *           if no filter procedure is available or if the
+ *           filter does not match.
+ */
+int nl_object_match_filter(struct nl_object *obj, struct nl_object *filter)
+{
+       struct nl_object_ops *ops = obj_ops(obj);
+
+       if (ops != obj_ops(filter) || ops->oo_compare == NULL)
+               return 0;
+       
+       return !(ops->oo_compare(obj, filter, filter->ce_mask,
+                                LOOSE_COMPARISON));
+}
+
+/**
+ * Convert bitmask of attributes to a character string
+ * @arg obj            object of same type as attribute bitmask
+ * @arg attrs          bitmask of attribute types
+ * @arg buf            destination buffer
+ * @arg len            length of destination buffer
+ *
+ * Converts the bitmask of attribute types into a list of attribute
+ * names separated by comas.
+ *
+ * @return destination buffer.
+ */
+char *nl_object_attrs2str(struct nl_object *obj, uint32_t attrs,
+                         char *buf, size_t len)
+{
+       struct nl_object_ops *ops = obj_ops(obj);
+
+       if (ops->oo_attrs2str != NULL)
+               return ops->oo_attrs2str(attrs, buf, len);
+       else {
+               memset(buf, 0, len);
+               return buf;
+       }
+}
+
+/** @} */
+
+/** @} */
diff --git a/package/libnl-tiny/src/socket.c b/package/libnl-tiny/src/socket.c
new file mode 100644 (file)
index 0000000..6fe56fd
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ * lib/socket.c                Netlink Socket
+ *
+ *     This library is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation version 2.1
+ *     of the License.
+ *
+ * Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
+ */
+
+/**
+ * @ingroup core
+ * @defgroup socket Socket
+ * @{
+ */
+
+#include <netlink-local.h>
+#include <netlink/netlink.h>
+#include <netlink/utils.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+static uint32_t used_ports_map[32];
+
+static uint32_t generate_local_port(void)
+{
+       int i, n;
+       uint32_t pid = getpid() & 0x3FFFFF;
+
+       for (i = 0; i < 32; i++) {
+               if (used_ports_map[i] == 0xFFFFFFFF)
+                       continue;
+
+               for (n = 0; n < 32; n++) {
+                       if (1UL & (used_ports_map[i] >> n))
+                               continue;
+
+                       used_ports_map[i] |= (1UL << n);
+                       n += (i * 32);
+
+                       /* PID_MAX_LIMIT is currently at 2^22, leaving 10 bit
+                        * to, i.e. 1024 unique ports per application. */
+                       return pid + (n << 22);
+
+               }
+       }
+
+       /* Out of sockets in our own PID namespace, what to do? FIXME */
+       return UINT_MAX;
+}
+
+static void release_local_port(uint32_t port)
+{
+       int nr;
+
+       if (port == UINT_MAX)
+               return;
+       
+       nr = port >> 22;
+       used_ports_map[nr / 32] &= ~((nr % 32) + 1);
+}
+
+/**
+ * @name Allocation
+ * @{
+ */
+
+static struct nl_sock *__alloc_socket(struct nl_cb *cb)
+{
+       struct nl_sock *sk;
+
+       sk = calloc(1, sizeof(*sk));
+       if (!sk)
+               return NULL;
+
+       sk->s_fd = -1;
+       sk->s_cb = 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;
+       }
+
+       return sk;
+}
+
+/**
+ * Allocate new netlink socket
+ *
+ * @return Newly allocated netlink socket or NULL.
+ */
+struct nl_sock *nl_socket_alloc(void)
+{
+       struct nl_cb *cb;
+       
+       cb = nl_cb_alloc(NL_CB_DEFAULT);
+       if (!cb)
+               return NULL;
+
+       return __alloc_socket(cb);
+}
+
+/**
+ * Allocate new socket with custom callbacks
+ * @arg cb             Callback handler
+ *
+ * The reference to the callback handler is taken into account
+ * automatically, it is released again upon calling nl_socket_free().
+ *
+ *@return Newly allocted socket handle or NULL.
+ */
+struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb)
+{
+       if (cb == NULL)
+               BUG();
+
+       return __alloc_socket(nl_cb_get(cb));
+}
+
+/**
+ * Free a netlink socket.
+ * @arg sk             Netlink socket.
+ */
+void nl_socket_free(struct nl_sock *sk)
+{
+       if (!sk)
+               return;
+
+       if (sk->s_fd >= 0)
+               close(sk->s_fd);
+
+       if (!(sk->s_flags & NL_OWN_PORT))
+               release_local_port(sk->s_local.nl_pid);
+
+       nl_cb_put(sk->s_cb);
+       free(sk);
+}
+
+/** @} */
+
+/**
+ * @name Sequence Numbers
+ * @{
+ */
+
+static int noop_seq_check(struct nl_msg *msg, void *arg)
+{
+       return NL_OK;
+}
+
+
+/**
+ * Disable sequence number checking.
+ * @arg sk             Netlink socket.
+ *
+ * Disables checking of sequence numbers on the netlink socket This is
+ * required to allow messages to be processed which were not requested by
+ * a preceding request message, e.g. netlink events.
+ *
+ * @note This function modifies the NL_CB_SEQ_CHECK configuration in
+ * the callback handle associated with the socket.
+ */
+void nl_socket_disable_seq_check(struct nl_sock *sk)
+{
+       nl_cb_set(sk->s_cb, NL_CB_SEQ_CHECK,
+                 NL_CB_CUSTOM, noop_seq_check, NULL);
+}
+
+/** @} */
+
+/**
+ * Set local port of socket
+ * @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.
+ */
+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;
+       }
+
+       sk->s_local.nl_pid = port;
+}
+
+/** @} */
+
+/**
+ * @name Group Subscriptions
+ * @{
+ */
+
+/**
+ * Join groups
+ * @arg sk             Netlink socket
+ * @arg group          Group identifier
+ *
+ * Joins the specified groups using the modern socket option which
+ * is available since kernel version 2.6.14. It allows joining an
+ * almost arbitary number of groups without limitation.  The list
+ * of groups has to be terminated by 0 (%NFNLGRP_NONE).
+ *
+ * Make sure to use the correct group definitions as the older
+ * bitmask definitions for nl_join_groups() are likely to still
+ * be present for backward compatibility reasons.
+ *
+ * @return 0 on sucess or a negative error code.
+ */
+int nl_socket_add_memberships(struct nl_sock *sk, int group, ...)
+{
+       int err;
+       va_list ap;
+
+       if (sk->s_fd == -1)
+               return -NLE_BAD_SOCK;
+
+       va_start(ap, group);
+
+       while (group != 0) {
+               if (group < 0)
+                       return -NLE_INVAL;
+
+               err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
+                                                &group, sizeof(group));
+               if (err < 0)
+                       return -nl_syserr2nlerr(errno);
+
+               group = va_arg(ap, int);
+       }
+
+       va_end(ap);
+
+       return 0;
+}
+
+/**
+ * Leave groups
+ * @arg sk             Netlink socket
+ * @arg group          Group identifier
+ *
+ * Leaves the specified groups using the modern socket option
+ * which is available since kernel version 2.6.14. The list of groups
+ * has to terminated by 0 (%NFNLGRP_NONE).
+ *
+ * @see nl_socket_add_membership
+ * @return 0 on success or a negative error code.
+ */
+int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...)
+{
+       int err;
+       va_list ap;
+
+       if (sk->s_fd == -1)
+               return -NLE_BAD_SOCK;
+
+       va_start(ap, group);
+
+       while (group != 0) {
+               if (group < 0)
+                       return -NLE_INVAL;
+
+               err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
+                                                &group, sizeof(group));
+               if (err < 0)
+                       return -nl_syserr2nlerr(errno);
+
+               group = va_arg(ap, int);
+       }
+
+       va_end(ap);
+
+       return 0;
+}
+
+
+/** @} */
+
+/**
+ * Set file descriptor of socket to non-blocking state
+ * @arg sk             Netlink socket.
+ *
+ * @return 0 on success or a negative error code.
+ */
+int nl_socket_set_nonblocking(struct nl_sock *sk)
+{
+       if (sk->s_fd == -1)
+               return -NLE_BAD_SOCK;
+
+       if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0)
+               return -nl_syserr2nlerr(errno);
+
+       return 0;
+}
+
+/** @} */
+
+/**
+ * @name Utilities
+ * @{
+ */
+
+/**
+ * Set socket buffer size of netlink socket.
+ * @arg sk             Netlink socket.
+ * @arg rxbuf          New receive socket buffer size in bytes.
+ * @arg txbuf          New transmit socket buffer size in bytes.
+ *
+ * Sets the socket buffer size of a netlink socket to the specified
+ * values \c rxbuf and \c txbuf. Providing a value of \c 0 assumes a
+ * good default value.
+ *
+ * @note It is not required to call this function prior to nl_connect().
+ * @return 0 on sucess or a negative error code.
+ */
+int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf)
+{
+       int err;
+
+       if (rxbuf <= 0)
+               rxbuf = 32768;
+
+       if (txbuf <= 0)
+               txbuf = 32768;
+
+       if (sk->s_fd == -1)
+               return -NLE_BAD_SOCK;
+       
+       err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF,
+                        &txbuf, sizeof(txbuf));
+       if (err < 0)
+               return -nl_syserr2nlerr(errno);
+
+       err = setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF,
+                        &rxbuf, sizeof(rxbuf));
+       if (err < 0)
+               return -nl_syserr2nlerr(errno);
+
+       sk->s_flags |= NL_SOCK_BUFSIZE_SET;
+
+       return 0;
+}
+
+/**
+ * Enable/disable credential passing on netlink socket.
+ * @arg sk             Netlink socket.
+ * @arg state          New state (0 - disabled, 1 - enabled)
+ *
+ * @return 0 on success or a negative error code
+ */
+int nl_socket_set_passcred(struct nl_sock *sk, int state)
+{
+       int err;
+
+       if (sk->s_fd == -1)
+               return -NLE_BAD_SOCK;
+
+       err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED,
+                        &state, sizeof(state));
+       if (err < 0)
+               return -nl_syserr2nlerr(errno);
+
+       if (state)
+               sk->s_flags |= NL_SOCK_PASSCRED;
+       else
+               sk->s_flags &= ~NL_SOCK_PASSCRED;
+
+       return 0;
+}
+
+/**
+ * Enable/disable receival of additional packet information
+ * @arg sk             Netlink socket.
+ * @arg state          New state (0 - disabled, 1 - enabled)
+ *
+ * @return 0 on success or a negative error code
+ */
+int nl_socket_recv_pktinfo(struct nl_sock *sk, int state)
+{
+       int err;
+
+       if (sk->s_fd == -1)
+               return -NLE_BAD_SOCK;
+
+       err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_PKTINFO,
+                        &state, sizeof(state));
+       if (err < 0)
+               return -nl_syserr2nlerr(errno);
+
+       return 0;
+}
+
+/** @} */
+
+/** @} */
index c763526f8a400f65da9b055526dd149bfc70f8ca..64099f06593f26e34e6aaeaa0c491d067d85d516 100644 (file)
@@ -23,6 +23,7 @@ define Package/libnl
   CATEGORY:=Libraries
   TITLE:=netlink socket library
   URL:=http://people.suug.ch/~tgr/libnl/
+  DEPENDS:=@!LINUX_2_4
 endef
 
 define Package/libnl/description
index 126639552a4f2e13070b519b04c4a4d37d153db3..e56bfdd31b1b9d1fb8923ae55796b36a1a7610cc 100644 (file)
@@ -15,12 +15,6 @@ choice
 config MADWIFI_STABLE
        bool "Use the OpenWrt stable version of madwifi"
 
-config MADWIFI_UPSTREAM
-       depends BROKEN
-       depends !LINUX_2_6_26
-       depends !TARGET_atheros
-       bool "Use the upstream release version 0.9.4"
-
 endchoice
 
 choice
index f787ffb4c2cadaffc7e7827f823de4d50bcdcd6c..93a2aa1ac64eb1a51827f6de45db92de41398e83 100644 (file)
@@ -4,40 +4,27 @@
 # This is free software, licensed under the GNU General Public License v2.
 # See /LICENSE for more information.
 #
-# $Id$
 
 include $(TOPDIR)/rules.mk
 include $(INCLUDE_DIR)/kernel.mk
 
 PKG_NAME:=madwifi
 
-ifneq ($(CONFIG_MADWIFI_UPSTREAM),)
-  PKG_VERSION:=0.9.4
-  PKG_RELEASE:=2.1
+PKG_REV:=3314
+PKG_VERSION:=r$(PKG_REV)
+PKG_RELEASE:=2
 
-  PKG_SOURCE:=madwifi-$(PKG_VERSION).tar.gz
-  PKG_SOURCE_URL:=http://downloads.sourceforge.net/madwifi/
-  PKG_MD5SUM:=399d20de8d855a59f20058857c2178ad
+PKG_SOURCE_PROTO:=svn
+PKG_SOURCE_VERSION:=$(PKG_REV)
+PKG_SOURCE_SUBDIR:=$(if $(PKG_BRANCH),$(PKG_BRANCH),madwifi-trunk)-$(PKG_VERSION)
+PKG_SOURCE_URL:=http://svn.madwifi.org/madwifi/$(if $(PKG_BRANCH),branches/$(PKG_BRANCH),trunk)
+PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.gz
 
-  PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/madwifi-$(PKG_VERSION)
+PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(if $(PKG_BRANCH),$(PKG_BRANCH),madwifi-trunk)-$(PKG_VERSION)
 
-  PATCH_DIR=./patches-upstream
-else
-# PKG_BRANCH:=madwifi-dfs
-  PKG_REV:=3314
-  PKG_VERSION:=r$(PKG_REV)
-  PKG_RELEASE:=2.1
-
-  PKG_SOURCE_PROTO:=svn
-  PKG_SOURCE_VERSION:=$(PKG_REV)
-  PKG_SOURCE_SUBDIR:=$(if $(PKG_BRANCH),$(PKG_BRANCH),madwifi-trunk)-$(PKG_VERSION)
-  PKG_SOURCE_URL:=http://svn.madwifi.org/madwifi/$(if $(PKG_BRANCH),branches/$(PKG_BRANCH),trunk)
-  PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.gz
+PATCH_DIR=./patches
 
-  PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(if $(PKG_BRANCH),$(PKG_BRANCH),madwifi-trunk)-$(PKG_VERSION)
-
-  PATCH_DIR=./patches
-endif
+PKG_BUILD_DEPENDS:=wprobe
 
 include $(INCLUDE_DIR)/package.mk
 
@@ -150,7 +137,7 @@ define KernelPackage/madwifi
   SUBMENU:=Wireless Drivers
   TITLE:=Driver for Atheros wireless chipsets
   URL:=http://madwifi.org/
-  DEPENDS:=+wireless-tools @PCI_SUPPORT||TARGET_atheros @!TARGET_avr32
+  DEPENDS:=+wireless-tools @PCI_SUPPORT||TARGET_atheros @!TARGET_avr32 @!TARGET_etrax @LINUX_2_6
   FILES:=$(MADWIFI_FILES)
   AUTOLOAD:=$(call AutoLoad,50,$(MADWIFI_AUTOLOAD))
 endef
@@ -163,6 +150,16 @@ define KernelPackage/madwifi/config
        source "$(SOURCE)/Config.in"
 endef
 
+MADWIFI_INC = \
+       -I$(PKG_BUILD_DIR) \
+       -I$(PKG_BUILD_DIR)/include \
+       -I$(PKG_BUILD_DIR)/hal \
+       -I$(PKG_BUILD_DIR)/ath \
+       -I$(PKG_BUILD_DIR)/ath_hal \
+       -I$(PKG_BUILD_DIR)/net80211 \
+       -I$(STAGING_DIR)/usr/include/wprobe \
+       -include $(PKG_BUILD_DIR)/include/compat.h
+
 MAKE_ARGS:= \
        PATH="$(TARGET_PATH)" \
        ARCH="$(LINUX_KARCH)" \
@@ -174,6 +171,7 @@ MAKE_ARGS:= \
        LDOPTS="--no-warn-mismatch " \
        ATH_RATE="ath_rate/$(RATE_CONTROL)" \
        DO_MULTI=1 \
+       INCS="$(MADWIFI_INC)" \
        $(if $(CONFIG_MADWIFI_DEBUG),,DEBUG=) WARNINGS="-Wno-unused"
 
 MAKE_VARS:= \
index 8c4a75426e94fce4d0bf9a5429d3cadf16ba1a31..a3cf4d75e16035d5013438535f96fd87ee0b14ce 100755 (executable)
@@ -67,15 +67,17 @@ disable_atheros() (
 
 enable_atheros() {
        local device="$1"
-       # Can only set the country code to one setting for the entire system. The last country code is the one that will be applied.
+
+       config_get regdomain "$device" regdomain
+       [ -n "$regdomain" ] && echo "$regdomain" > /proc/sys/dev/$device/regdomain
+
        config_get country "$device" country
        [ -z "$country" ] && country="0"
-       local cc="0"
-       [ -e /proc/sys/dev/$device/countrycode ] && cc="$(cat /proc/sys/dev/$device/countrycode)"
-       if [ ! "$cc" = "$country" ] ; then
-               rmmod ath_pci
-               insmod ath_pci countrycode=$country
-       fi
+       echo "$country" > /proc/sys/dev/$device/countrycode
+
+       config_get_bool outdoor "$device" outdoor "0"
+       echo "$outdoor" > /proc/sys/dev/$device/outdoor
+
        config_get channel "$device" channel
        config_get vifs "$device" vifs
        config_get txpower "$device" txpower
diff --git a/package/madwifi/patches-upstream/104-autocreate_none.patch b/package/madwifi/patches-upstream/104-autocreate_none.patch
deleted file mode 100644 (file)
index e290f86..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-Index: madwifi-0.9.4/ath/if_ath.c
-===================================================================
---- madwifi-0.9.4.old/ath/if_ath.c     2008-02-13 06:13:10.000000000 +0100
-+++ madwifi-0.9.4/ath/if_ath.c 2008-05-06 10:25:15.000000000 +0200
-@@ -404,7 +404,7 @@
-       struct ath_hal *ah;
-       HAL_STATUS status;
-       int error = 0, i;
--      int autocreatemode = IEEE80211_M_STA;
-+      int autocreatemode = -1;
-       u_int8_t csz;
-       sc->devid = devid;
diff --git a/package/madwifi/patches-upstream/111-minstrel_crash.patch b/package/madwifi/patches-upstream/111-minstrel_crash.patch
deleted file mode 100644 (file)
index cc1fbe6..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-Index: madwifi-0.9.4/ath_rate/minstrel/minstrel.c
-===================================================================
---- madwifi-0.9.4.old/ath_rate/minstrel/minstrel.c     2007-12-12 05:11:07.000000000 +0100
-+++ madwifi-0.9.4/ath_rate/minstrel/minstrel.c 2008-07-24 15:15:41.000000000 +0200
-@@ -394,6 +394,9 @@
-       int rc1, rc2, rc3;         /* Index into the rate table, so for example, it is  0..11 */
-       int rixc1, rixc2, rixc3;   /* The actual bit rate used */
-+      if (sn->num_rates <= 0)
-+              return; 
-+
-       if (sn->is_sampling) {
-               sn->is_sampling = 0;
-               if (sn->rs_sample_rate_slower)
index 71cede79872b840e1b0c5b52711f75f06980a805..d9a0ad4e2b96f045453c4aa71340e6d555374191 100644 (file)
@@ -1,6 +1,65 @@
 --- a/net80211/ieee80211_crypto_ccmp.c
 +++ b/net80211/ieee80211_crypto_ccmp.c
-@@ -475,6 +475,9 @@ ccmp_encrypt(struct ieee80211_key *key, 
+@@ -115,6 +115,7 @@ ccmp_attach(struct ieee80211vap *vap, st
+ /* This function (crypto_alloc_foo might sleep. Therefore:
+  * Context: process
+  */
++#ifdef CONFIG_CRYPTO
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+       ctx->cc_tfm = crypto_alloc_tfm("aes", 0);
+ #else
+@@ -123,7 +124,8 @@ ccmp_attach(struct ieee80211vap *vap, st
+       if (IS_ERR(ctx->cc_tfm))
+               ctx->cc_tfm = NULL;
+ #endif
+-      
++#endif
++
+       if (ctx->cc_tfm == NULL) {
+               IEEE80211_DPRINTF(vap, IEEE80211_MSG_CRYPTO,
+                               "%s: unable to load kernel AES crypto support\n",
+@@ -138,12 +140,14 @@ ccmp_detach(struct ieee80211_key *k)
+ {
+       struct ccmp_ctx *ctx = k->wk_private;
++#ifdef CONFIG_CRYPTO
+       if (ctx->cc_tfm != NULL)
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)
+               crypto_free_tfm(ctx->cc_tfm);
+ #else
+               crypto_free_cipher(ctx->cc_tfm);
+ #endif
++#endif
+       FREE(ctx, M_DEVBUF);
+       _MOD_DEC_USE(THIS_MODULE);
+@@ -169,7 +173,9 @@ ccmp_setkey(struct ieee80211_key *k)
+                       return 0;
+               }
++#ifdef CONFIG_CRYPTO
+               crypto_cipher_setkey(ctx->cc_tfm, k->wk_key, k->wk_keylen);
++#endif
+       }
+       return 1;
+@@ -324,6 +330,7 @@ xor_block(u8 *b, const u8 *a, size_t len
+ static void
+ rijndael_encrypt(struct crypto_cipher *tfm, const void *src, void *dst)
+ {
++#ifdef CONFIG_CRYPTO
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
+       crypto_cipher_encrypt_one(tfm, dst, src);
+ #else
+@@ -339,6 +346,7 @@ rijndael_encrypt(struct crypto_cipher *t
+       sg_dst.length = AES_BLOCK_LEN;
+       crypto_cipher_encrypt(tfm, &sg_dst, &sg_src, AES_BLOCK_LEN);
+ #endif
++#endif
+ }
+ /*
+@@ -475,6 +483,9 @@ ccmp_encrypt(struct ieee80211_key *key, 
        uint8_t *mic, *pos;
        u_int space;
  
@@ -10,7 +69,7 @@
        ctx->cc_vap->iv_stats.is_crypto_ccmp++;
  
        skb = skb0;
-@@ -589,6 +592,9 @@ ccmp_decrypt(struct ieee80211_key *key, 
+@@ -589,6 +600,9 @@ ccmp_decrypt(struct ieee80211_key *key, 
        uint8_t *pos, *mic;
        u_int space;
  
        ctx->cc_vap->iv_stats.is_crypto_ccmp++;
  
        skb = skb0;
+--- a/Makefile
++++ b/Makefile
+@@ -192,11 +192,4 @@ endif
+           exit 1; \
+       fi
+       
+-      @# check crypto support is enabled
+-      @if [ -z "$(CONFIG_CRYPTO)" ]; then \
+-          echo "FAILED"; \
+-          echo "Please enable crypto API."; \
+-          exit 1; \
+-      fi
+-      
+       @echo "ok."
index 67e757468b95d538806e4f094469c0775fb138cd..c11f28e39d3c8ee4a7c3b864e7db8749aeb27d7b 100644 (file)
                /* Identify mode capabilities. */
                if (IEEE80211_IS_CHAN_A(c))
                        ic->ic_modecaps |= 1 << IEEE80211_MODE_11A;
+@@ -1447,10 +1452,6 @@ ieee80211_media_change(struct net_device
+               vap->iv_fixed_rate = newrate;           /* fixed TX rate */
+               error = -ENETRESET;
+       }
+-      if (vap->iv_des_mode != newmode) {
+-              vap->iv_des_mode = newmode;             /* desired PHY mode */
+-              error = -ENETRESET;
+-      }
+       return error;
+ }
+ EXPORT_SYMBOL(ieee80211_media_change);
 --- a/net80211/_ieee80211.h
 +++ b/net80211/_ieee80211.h
 @@ -132,6 +132,11 @@ enum ieee80211_scanmode {
        IEEE80211_WMMPARAMS_CWMIN       = 1,
 --- a/net80211/ieee80211_scan_ap.c
 +++ b/net80211/ieee80211_scan_ap.c
-@@ -129,131 +129,7 @@ struct ap_state {
+@@ -105,11 +105,6 @@ struct scan_entry {
+ };
+ struct ap_state {
+-      unsigned int as_vap_desired_mode;       /* Used for channel selection, 
+-                                               * vap->iv_des_mode */
+-      unsigned int as_required_mode;          /* Used for channel selection, 
+-                                               * filtered version of 
+-                                               * as_vap_desired_mode */
+       int as_maxrssi[IEEE80211_CHAN_MAX];     /* Used for channel selection */
+       /* These fields are just for scan caching for returning responses to
+@@ -129,131 +124,7 @@ struct ap_state {
  
  static int ap_flush(struct ieee80211_scan_state *);
  static void action_tasklet(IEEE80211_TQUEUE_ARG);
  
  /*
   * Attach prior to any scanning work.
-@@ -327,29 +203,6 @@ saveie(u_int8_t **iep, const u_int8_t *i
+@@ -327,29 +198,6 @@ saveie(u_int8_t **iep, const u_int8_t *i
                ieee80211_saveie(iep, ie);
  }
  
  /*
   * Start an ap scan by populating the channel list.
   */
-@@ -358,8 +211,6 @@ ap_start(struct ieee80211_scan_state *ss
+@@ -358,90 +206,15 @@ ap_start(struct ieee80211_scan_state *ss
  {
        struct ap_state *as         = ss->ss_priv;
        struct ieee80211com *ic     = NULL;
        int i;
        unsigned int mode = 0;
  
-@@ -368,80 +219,8 @@ ap_start(struct ieee80211_scan_state *ss
+       SCAN_AP_LOCK_IRQ(as);
+       ic = vap->iv_ic;
        /* Determine mode flags to match, or leave zero for auto mode */
-       as->as_vap_desired_mode = vap->iv_des_mode;
-       as->as_required_mode    = 0;
+-      as->as_vap_desired_mode = vap->iv_des_mode;
+-      as->as_required_mode    = 0;
 -      if (as->as_vap_desired_mode != IEEE80211_MODE_AUTO) {
 -              as->as_required_mode = chanflags[as->as_vap_desired_mode];
 -              if ((vap->iv_ath_cap & IEEE80211_ATHC_TURBOP) && 
 -              }
 -      }
 -
--      ss->ss_last = 0;
+       ss->ss_last = 0;
 -      /* Use the table of ordered channels to construct the list
 -       * of channels for scanning.  Any channels in the ordered
 -       * list not in the master list will be discarded. */
 -              /* XR is not supported on turbo channels */
 -              if (IEEE80211_IS_CHAN_TURBO(c) && vap->iv_flags & IEEE80211_F_XR)
 -                      continue;
-+      ieee80211_scan_add_channels(ic, ss, vap->iv_des_mode);
+-
 -              /* Dynamic channels are scanned in base mode */
 -              if (!as->as_required_mode && !IEEE80211_IS_CHAN_ST(c))
 -                      continue;
 -              /* Make sure the channel is active */
 -              if ((c == NULL) || isclr(ic->ic_chan_active, c->ic_ieee))
 -                      continue;
--
++      ieee80211_scan_add_channels(ic, ss, vap->iv_des_mode);
 -              /* Don't overrun */
 -              if (ss->ss_last >= IEEE80211_SCAN_MAX)
 -                      break;
        ss->ss_next = 0;
        /* XXX tunables */
        ss->ss_mindwell = msecs_to_jiffies(200);        /* 200ms */
-@@ -761,13 +540,6 @@ pick_channel(struct ieee80211_scan_state
+@@ -761,18 +534,6 @@ pick_channel(struct ieee80211_scan_state
                if (IEEE80211_IS_CHAN_RADAR(c->chan))
                        continue;
  
 -                  (as->as_vap_desired_mode != IEEE80211_MODE_TURBO_STATIC_A))
 -                      continue;
 -
-               /* Verify mode matches any fixed mode specified */
-               if((c->chan->ic_flags & as->as_required_mode) != 
-                               as->as_required_mode)
+-              /* Verify mode matches any fixed mode specified */
+-              if((c->chan->ic_flags & as->as_required_mode) != 
+-                              as->as_required_mode)
+-                      continue;
+-
+               if ((ic->ic_bsschan != NULL) &&
+                       (ic->ic_bsschan != IEEE80211_CHAN_ANYC)) {
 --- a/net80211/ieee80211_scan.c
 +++ b/net80211/ieee80211_scan.c
 @@ -958,6 +958,80 @@ ieee80211_scan_flush(struct ieee80211com
index 1c62285904d97c3d4e50dff3e7f9b3da731047a9..65e03e73814a100712fe5a1a8226dbb28cf62a4c 100644 (file)
@@ -95,7 +95,7 @@
  #define       IEEE80211_CHAN_MAX      255
 --- a/net80211/ieee80211_scan_ap.c
 +++ b/net80211/ieee80211_scan_ap.c
-@@ -423,6 +423,19 @@ pc_cmp_rssi(struct ap_state *as, struct 
+@@ -417,6 +417,19 @@ pc_cmp_rssi(struct ap_state *as, struct 
  
  /* This function must be invoked with locks acquired */
  static int
  pc_cmp_samechan(struct ieee80211com *ic, struct ieee80211_channel *a,
                struct ieee80211_channel *b)
  {
-@@ -457,6 +470,7 @@ pc_cmp(const void *_a, const void *_b)
+@@ -451,6 +464,7 @@ pc_cmp(const void *_a, const void *_b)
  
        EVALUATE_CRITERION(radar, a, b);
        EVALUATE_CRITERION(keepmode, params, a, b);
index ab48e70b2ce152e940c60d77c1df6cf9388e6973..b6093cfcd567a0a28bffd97c72dba12a4ac42bd1 100644 (file)
        /* update Supported Channels information element */
 --- a/net80211/ieee80211_scan_ap.c
 +++ b/net80211/ieee80211_scan_ap.c
-@@ -213,9 +213,15 @@ ap_start(struct ieee80211_scan_state *ss
+@@ -208,9 +208,15 @@ ap_start(struct ieee80211_scan_state *ss
        struct ieee80211com *ic     = NULL;
        int i;
        unsigned int mode = 0;
 +      spin_unlock_irqrestore(&channel_lock, sflags);
 +
        /* Determine mode flags to match, or leave zero for auto mode */
-       as->as_vap_desired_mode = vap->iv_des_mode;
-       as->as_required_mode    = 0;
-@@ -429,8 +435,10 @@ pc_cmp_idletime(struct ieee80211_channel
+       ss->ss_last = 0;
+       ieee80211_scan_add_channels(ic, ss, vap->iv_des_mode);
+@@ -423,8 +429,10 @@ pc_cmp_idletime(struct ieee80211_channel
        if (!a->ic_idletime || !b->ic_idletime)
                return 0;
  
  }
  
  
-@@ -616,6 +624,7 @@ ap_end(struct ieee80211_scan_state *ss, 
+@@ -605,6 +613,7 @@ ap_end(struct ieee80211_scan_state *ss, 
        struct ap_state *as = ss->ss_priv;
        struct ieee80211_channel *bestchan = NULL;
        struct ieee80211com *ic = NULL;
        int res = 1;
  
        SCAN_AP_LOCK_IRQ(as);
-@@ -624,8 +633,11 @@ ap_end(struct ieee80211_scan_state *ss, 
+@@ -613,8 +622,11 @@ ap_end(struct ieee80211_scan_state *ss, 
                ("wrong opmode %u", vap->iv_opmode));
  
        ic = vap->iv_ic;
                if (ss->ss_last > 0) {
                        /* no suitable channel, should not happen */
                        printk(KERN_ERR "%s: %s: no suitable channel! "
-@@ -644,6 +656,7 @@ ap_end(struct ieee80211_scan_state *ss, 
+@@ -633,6 +645,7 @@ ap_end(struct ieee80211_scan_state *ss, 
                                        bestchan->ic_freq, bestchan->ic_flags &
                                        ~IEEE80211_CHAN_TURBO)) == NULL) {
                                /* should never happen ?? */
                                SCAN_AP_UNLOCK_IRQ_EARLY(as);
                                return 0;
                        }
-@@ -656,6 +669,9 @@ ap_end(struct ieee80211_scan_state *ss, 
+@@ -645,6 +658,9 @@ ap_end(struct ieee80211_scan_state *ss, 
                        as->as_action = action;
                as->as_selbss = se;
  
diff --git a/package/madwifi/patches/416-wprobe.patch b/package/madwifi/patches/416-wprobe.patch
new file mode 100644 (file)
index 0000000..4530737
--- /dev/null
@@ -0,0 +1,509 @@
+--- /dev/null
++++ b/ath/ath_wprobe.c
+@@ -0,0 +1,392 @@
++#include <net80211/ieee80211_node.h>
++#include <linux/wprobe.h>
++
++atomic_t cleanup_tasks = ATOMIC_INIT(0);
++
++enum wp_node_val {
++      WP_NODE_RSSI,
++      WP_NODE_SIGNAL,
++      WP_NODE_RX_RATE,
++      WP_NODE_TX_RATE,
++      WP_NODE_RETRANSMIT_200,
++      WP_NODE_RETRANSMIT_400,
++      WP_NODE_RETRANSMIT_800,
++      WP_NODE_RETRANSMIT_1600,
++};
++
++enum wp_global_val {
++      WP_GLOBAL_NOISE,
++      WP_GLOBAL_PHY_BUSY,
++      WP_GLOBAL_PHY_RX,
++      WP_GLOBAL_PHY_TX,
++      WP_GLOBAL_FRAMES,
++      WP_GLOBAL_PROBEREQ,
++};
++
++static struct wprobe_item ath_wprobe_globals[] = {
++      [WP_GLOBAL_NOISE] = {
++              .name = "noise",
++              .type = WPROBE_VAL_S16,
++              .flags = WPROBE_F_KEEPSTAT
++      },
++      [WP_GLOBAL_PHY_BUSY] = {
++              .name = "phy_busy",
++              .type = WPROBE_VAL_U8,
++              .flags = WPROBE_F_KEEPSTAT
++      },
++      [WP_GLOBAL_PHY_RX] = {
++              .name = "phy_rx",
++              .type = WPROBE_VAL_U8,
++              .flags = WPROBE_F_KEEPSTAT
++      },
++      [WP_GLOBAL_PHY_TX] = {
++              .name = "phy_tx",
++              .type = WPROBE_VAL_U8,
++              .flags = WPROBE_F_KEEPSTAT
++      },
++      [WP_GLOBAL_FRAMES] = {
++              .name = "frames",
++              .type = WPROBE_VAL_U32,
++      },
++      [WP_GLOBAL_PROBEREQ] = {
++              .name = "probereq",
++              .type = WPROBE_VAL_U32,
++      },
++};
++
++static struct wprobe_item ath_wprobe_link[] = {
++      [WP_NODE_RSSI] = {
++              .name = "rssi",
++              .type = WPROBE_VAL_U8,
++              .flags = WPROBE_F_KEEPSTAT
++      },
++      [WP_NODE_SIGNAL] = {
++              .name = "signal",
++              .type = WPROBE_VAL_S16,
++              .flags = WPROBE_F_KEEPSTAT
++      },
++      [WP_NODE_RX_RATE] = {
++              .name = "rx_rate",
++              .type = WPROBE_VAL_U16,
++              .flags = WPROBE_F_KEEPSTAT
++      },
++      [WP_NODE_TX_RATE] = {
++              .name = "tx_rate",
++              .type = WPROBE_VAL_U16,
++              .flags = WPROBE_F_KEEPSTAT
++      },
++      [WP_NODE_RETRANSMIT_200] = {
++              .name = "retransmit_200",
++              .type = WPROBE_VAL_U8,
++              .flags = WPROBE_F_KEEPSTAT
++      },
++      [WP_NODE_RETRANSMIT_400] = {
++              .name = "retransmit_400",
++              .type = WPROBE_VAL_U8,
++              .flags = WPROBE_F_KEEPSTAT
++      },
++      [WP_NODE_RETRANSMIT_800] = {
++              .name = "retransmit_800",
++              .type = WPROBE_VAL_U8,
++              .flags = WPROBE_F_KEEPSTAT
++      },
++      [WP_NODE_RETRANSMIT_1600] = {
++              .name = "retransmit_1600",
++              .type = WPROBE_VAL_U8,
++              .flags = WPROBE_F_KEEPSTAT
++      },
++};
++
++#define AR5K_MIBC       0x0040
++#define AR5K_MIBC_FREEZE      (1 << 1)
++#define AR5K_TXFC       0x80ec
++#define AR5K_RXFC       0x80f0
++#define AR5K_RXCLEAR    0x80f4
++#define AR5K_CYCLES     0x80f8
++
++#define READ_CLR(_ah, _reg) \
++      ({ u32 __val = OS_REG_READ(_ah, _reg); OS_REG_WRITE(_ah, _reg, 0); __val; })
++
++static bool
++wprobe_disabled(void)
++{
++      return (!wprobe_add_iface || IS_ERR(wprobe_add_iface));
++}
++
++static int
++ath_wprobe_sync(struct wprobe_iface *dev, struct wprobe_link *l, struct wprobe_value *val, bool measure)
++{
++      struct ath_vap *avp = container_of(dev, struct ath_vap, av_wpif);
++      struct ieee80211vap *vap = &avp->av_vap;
++      struct ieee80211com *ic = vap->iv_ic;
++      struct ath_softc *sc = ic->ic_dev->priv;
++      struct ath_hal *ah = sc->sc_ah;
++      u32 cc, busy, rx, tx;
++      s16 noise;
++
++      if (l)
++              goto out;
++
++      OS_REG_WRITE(ah, AR5K_MIBC, AR5K_MIBC_FREEZE);
++      cc = READ_CLR(ah, AR5K_CYCLES);
++      busy = READ_CLR(ah, AR5K_RXCLEAR);
++      rx = READ_CLR(ah, AR5K_RXFC);
++      tx = READ_CLR(ah, AR5K_TXFC);
++      OS_REG_WRITE(ah, AR5K_MIBC, 0);
++      noise = ath_hal_get_channel_noise(sc->sc_ah, &(sc->sc_curchan));
++      ic->ic_channoise = noise;
++
++      WPROBE_FILL_BEGIN(val, ath_wprobe_globals);
++      if (cc & 0xf0000000) {
++              /* scale down if the counters are near max */
++              cc >>= 8;
++              busy >>= 8;
++              rx >>= 8;
++              tx >>= 8;
++      }
++      if (ah->ah_macType < 5212)
++              goto phy_skip;
++      if (!cc)
++              goto phy_skip;
++      if (busy > cc)
++              goto phy_skip;
++      if (rx > cc)
++              goto phy_skip;
++      if (tx > cc)
++              goto phy_skip;
++      busy = (busy * 100) / cc;
++      rx = (rx * 100) / cc;
++      tx = (tx * 100) / cc;
++      WPROBE_SET(WP_GLOBAL_PHY_BUSY, U8, busy);
++      WPROBE_SET(WP_GLOBAL_PHY_RX, U8, rx);
++      WPROBE_SET(WP_GLOBAL_PHY_TX, U8, tx);
++      WPROBE_SET(WP_GLOBAL_FRAMES, U32, avp->av_rxframes);
++      WPROBE_SET(WP_GLOBAL_PROBEREQ, U32, avp->av_rxprobereq);
++
++phy_skip:
++      WPROBE_SET(WP_GLOBAL_NOISE, S16, noise);
++      WPROBE_FILL_END();
++
++out:
++      return 0;
++}
++
++#undef AR5K_TXFC
++#undef AR5K_RXFC
++#undef AR5K_RXCLEAR
++#undef AR5K_CYCLES
++#undef AR5K_MIBC
++#undef AR5K_MIBC_FREEZE
++#undef READ_CLR
++
++static const struct wprobe_iface ath_wprobe_dev = {
++      .link_items = ath_wprobe_link,
++      .n_link_items = ARRAY_SIZE(ath_wprobe_link),
++      .global_items = ath_wprobe_globals,
++      .n_global_items = ARRAY_SIZE(ath_wprobe_globals),
++      .sync_data = ath_wprobe_sync,
++};
++
++static int
++ath_lookup_rateval(struct ieee80211_node *ni, int rate)
++{
++      struct ieee80211vap *vap = ni->ni_vap;
++      struct ieee80211com *ic = vap->iv_ic;
++      struct ath_softc *sc = ic->ic_dev->priv;
++      const HAL_RATE_TABLE *rt = sc->sc_currates;
++
++      if ((!rt) || (rate < 0) || (rate >= ARRAY_SIZE(sc->sc_hwmap)))
++              return -1;
++
++      rate = sc->sc_hwmap[rate].ieeerate;
++      rate = sc->sc_rixmap[rate & IEEE80211_RATE_VAL];
++      if ((rate < 0) || (rate >= rt->rateCount))
++              return -1;
++
++      return rt->info[rate].rateKbps / 10;
++}
++
++static void
++ath_node_sample_rx(struct ieee80211_node *ni, struct ath_rx_status *rs)
++{
++      struct ath_node *an = ATH_NODE(ni);
++      struct ieee80211vap *vap = ni->ni_vap;
++      struct ieee80211com *ic = vap->iv_ic;
++      struct wprobe_link *l = &an->an_wplink;
++      struct wprobe_value *v = l->val;
++      unsigned long flags;
++      int rate;
++
++      if (wprobe_disabled() || !an->an_wplink_active || !l->val)
++              return;
++
++      rate = ath_lookup_rateval(ni, rs->rs_rate);
++
++      spin_lock_irqsave(&l->iface->lock, flags);
++      WPROBE_FILL_BEGIN(v, ath_wprobe_link);
++      WPROBE_SET(WP_NODE_RSSI, U8, rs->rs_rssi);
++      WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + rs->rs_rssi);
++      if ((rate > 0) && (rate <= 600000))
++              WPROBE_SET(WP_NODE_RX_RATE, U16, rate);
++      WPROBE_FILL_END();
++      wprobe_update_stats(l->iface, l);
++      spin_unlock_irqrestore(&l->iface->lock, flags);
++}
++
++static void
++ath_node_sample_tx(struct ieee80211_node *ni, struct ath_tx_status *ts, int len)
++{
++      struct ath_node *an = ATH_NODE(ni);
++      struct ieee80211vap *vap = ni->ni_vap;
++      struct ieee80211com *ic = vap->iv_ic;
++      struct wprobe_link *l = &an->an_wplink;
++      struct wprobe_value *v = l->val;
++      unsigned long flags;
++      int rate, rexmit_counter;
++
++      if (wprobe_disabled() || !an->an_wplink_active || !l->val)
++              return;
++
++      rate = ath_lookup_rateval(ni, ts->ts_rate);
++
++      spin_lock_irqsave(&l->iface->lock, flags);
++      WPROBE_FILL_BEGIN(v, ath_wprobe_link);
++      WPROBE_SET(WP_NODE_RSSI, U8, ts->ts_rssi);
++      WPROBE_SET(WP_NODE_SIGNAL, S16, ic->ic_channoise + ts->ts_rssi);
++
++      if (len <= 200)
++              rexmit_counter = WP_NODE_RETRANSMIT_200;
++      else if (len <= 400)
++              rexmit_counter = WP_NODE_RETRANSMIT_400;
++      else if (len <= 800)
++              rexmit_counter = WP_NODE_RETRANSMIT_800;
++      else
++              rexmit_counter = WP_NODE_RETRANSMIT_1600;
++      WPROBE_SET(rexmit_counter, U8, ts->ts_longretry);
++
++      if ((rate > 0) && (rate <= 600000))
++              WPROBE_SET(WP_NODE_TX_RATE, U16, rate);
++      WPROBE_FILL_END();
++      wprobe_update_stats(l->iface, l);
++      spin_unlock_irqrestore(&l->iface->lock, flags);
++}
++
++static void
++ath_wprobe_report_rx(struct ieee80211vap *vap, struct sk_buff *skb)
++{
++      struct ieee80211_frame *wh = (struct ieee80211_frame *)skb->data;
++      struct ath_vap *avp;
++
++      if (wprobe_disabled())
++              return;
++
++      avp = ATH_VAP(vap);
++      avp->av_rxframes++;
++      if (wh->i_fc[0] == (IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ))
++              avp->av_rxprobereq++;
++}
++
++static void
++ath_wprobe_node_join(struct ieee80211vap *vap, struct ieee80211_node *ni)
++{
++      struct wprobe_iface *dev;
++      struct wprobe_link *l;
++      struct ath_vap *avp;
++      struct ath_node *an = ATH_NODE(ni);
++
++      if (wprobe_disabled() || an->an_wplink_active)
++              return;
++
++      avp = ATH_VAP(vap);
++      dev = &avp->av_wpif;
++      l = &an->an_wplink;
++
++      ieee80211_ref_node(ni);
++      wprobe_add_link(dev, l, ni->ni_macaddr);
++      an->an_wplink_active = 1;
++}
++
++static void
++ath_wprobe_do_node_leave(struct work_struct *work)
++{
++      struct ath_node *an = container_of(work, struct ath_node, an_destroy);
++      struct ieee80211_node *ni = &an->an_node;
++      struct ieee80211vap *vap = ni->ni_vap;
++      struct wprobe_iface *dev;
++      struct wprobe_link *l;
++      struct ath_vap *avp;
++
++      avp = ATH_VAP(vap);
++      dev = &avp->av_wpif;
++      l = &an->an_wplink;
++
++      wprobe_remove_link(dev, l);
++      ieee80211_unref_node(&ni);
++      atomic_dec(&cleanup_tasks);
++}
++
++static void
++ath_wprobe_node_leave(struct ieee80211vap *vap, struct ieee80211_node *ni)
++{
++      struct ath_node *an = ATH_NODE(ni);
++
++      if (wprobe_disabled() || !an->an_wplink_active)
++              return;
++
++      atomic_inc(&cleanup_tasks);
++      an->an_wplink_active = 0;
++      IEEE80211_INIT_WORK(&an->an_destroy, ath_wprobe_do_node_leave);
++      schedule_work(&an->an_destroy);
++}
++
++static void
++ath_init_wprobe_dev(struct ath_vap *avp)
++{
++      struct ieee80211vap *vap = &avp->av_vap;
++      struct wprobe_iface *dev = &avp->av_wpif;
++
++      if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS))
++              return;
++
++      memcpy(dev, &ath_wprobe_dev, sizeof(struct wprobe_iface));
++      dev->addr = vap->iv_myaddr;
++      dev->name = vap->iv_dev->name;
++      wprobe_add_iface(dev);
++}
++
++static void
++ath_remove_wprobe_dev(struct ath_vap *avp)
++{
++      struct ieee80211vap *vap = &avp->av_vap;
++      struct ieee80211com *ic = vap->iv_ic;
++      struct ieee80211_node *ni;
++      struct wprobe_iface *dev = &avp->av_wpif;
++      struct wprobe_link *l;
++      struct ath_node *an;
++      unsigned long flags;
++
++      if (wprobe_disabled() || (vap->iv_opmode == IEEE80211_M_WDS))
++              return;
++
++restart:
++      rcu_read_lock();
++      list_for_each_entry_rcu(l, &dev->links, list) {
++              an = container_of(l, struct ath_node, an_wplink);
++
++              if (!an->an_wplink_active)
++                      continue;
++
++              ni = &an->an_node;
++              ath_wprobe_node_leave(vap, ni);
++              rcu_read_unlock();
++              goto restart;
++      }
++      rcu_read_unlock();
++
++      /* wait for the cleanup tasks to finish */
++      while (atomic_read(&cleanup_tasks) != 0) {
++              schedule();
++      }
++
++      wprobe_remove_iface(dev);
++}
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -400,6 +400,7 @@ static int countrycode = -1;
+ static int maxvaps = -1;
+ static int outdoor = -1;
+ static int xchanmode = -1;
++#include "ath_wprobe.c"
+ static int beacon_cal = 1;
+ static const struct ath_hw_detect generic_hw_info = {
+@@ -1525,6 +1526,7 @@ ath_vap_create(struct ieee80211com *ic, 
+               ath_hal_intrset(ah, sc->sc_imask);
+       }
++      ath_init_wprobe_dev(avp);
+       return vap;
+ }
+@@ -1605,6 +1607,7 @@ ath_vap_delete(struct ieee80211vap *vap)
+               decrease = 0;
+       ieee80211_vap_detach(vap);
++      ath_remove_wprobe_dev(ATH_VAP(vap));
+       /* NB: memory is reclaimed through dev->destructor callback */
+       if (decrease)
+               sc->sc_nvaps--;
+@@ -5931,6 +5934,7 @@ ath_node_cleanup(struct ieee80211_node *
+       /* Clean up node-specific rate things - this currently appears to 
+        * always be a no-op */
+       sc->sc_rc->ops->node_cleanup(sc, ATH_NODE(ni));
++      ath_wprobe_node_leave(ni->ni_vap, ni);
+       ATH_NODE_UAPSD_LOCK_IRQ(an);
+ #ifdef IEEE80211_DEBUG_REFCNT
+@@ -7001,6 +7005,8 @@ drop_micfail:
+                               goto lookup_slowpath;
+                       }
+                       ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
++                      ath_node_sample_rx(ni, rs);
++                      ath_wprobe_report_rx(ni->ni_vap, skb);
+                       type = ieee80211_input(ni->ni_vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
+                       ieee80211_unref_node(&ni);
+               } else {
+@@ -7011,15 +7017,22 @@ drop_micfail:
+ lookup_slowpath:
+                       vap = ieee80211_find_rxvap(ic, wh->i_addr1);
+-                      if (vap)
++                      if (vap) {
++                              ath_wprobe_report_rx(vap, skb);
+                               ni = ieee80211_find_rxnode(ic, vap, wh);
+-                      else
++                      } else {
++                              TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++                                      ath_wprobe_report_rx(vap, skb);
++                              }
++                              vap = NULL;
+                               ni = NULL;
++                      }
+                       if (ni != NULL) {
+                               ieee80211_keyix_t keyix;
+                               ATH_RSSI_LPF(ATH_NODE(ni)->an_avgrssi, rs->rs_rssi);
++                              ath_node_sample_rx(ni, rs);
+                               type = ieee80211_input(vap, ni, skb, rs->rs_rssi, bf->bf_tsf);
+                               /*
+                                * If the station has a key cache slot assigned
+@@ -8599,6 +8612,7 @@ ath_tx_processq(struct ath_softc *sc, st
+                               sc->sc_stats.ast_tx_rssi = ts->ts_rssi;
+                               ATH_RSSI_LPF(an->an_halstats.ns_avgtxrssi,
+                                       ts->ts_rssi);
++                                      ath_node_sample_tx(&an->an_node, ts, bf->bf_skb->len);
+                               if (bf->bf_skb->priority == WME_AC_VO ||
+                                   bf->bf_skb->priority == WME_AC_VI)
+                                       ni->ni_ic->ic_wme.wme_hipri_traffic++;
+@@ -10090,6 +10104,7 @@ ath_newassoc(struct ieee80211_node *ni, 
+       struct ath_softc *sc = ic->ic_dev->priv;
+       sc->sc_rc->ops->newassoc(sc, ATH_NODE(ni), isnew);
++      ath_wprobe_node_join(ni->ni_vap, ni);
+       /* are we supporting compression? */
+       if (!(vap->iv_ath_cap & ni->ni_ath_flags & IEEE80211_NODE_COMP))
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -46,6 +46,7 @@
+ #include "ah_desc.h"
+ #include "ah_os.h"
+ #include "if_athioctl.h"
++#include <linux/wprobe.h>
+ #include "net80211/ieee80211.h"               /* XXX for WME_NUM_AC */
+ #include <asm/io.h>
+ #include <linux/list.h>
+@@ -352,6 +353,9 @@ typedef STAILQ_HEAD(, ath_buf) ath_bufhe
+ /* driver-specific node state */
+ struct ath_node {
+       struct ieee80211_node an_node;          /* base class */
++      struct wprobe_link an_wplink;
++      uint8_t an_wplink_active;
++      struct work_struct an_destroy;
+       u_int16_t an_decomp_index;              /* decompression mask index */
+       u_int32_t an_avgrssi;                   /* average rssi over all rx frames */
+       u_int8_t  an_prevdatarix;               /* rate ix of last data frame */
+@@ -521,6 +525,9 @@ struct ath_vap {
+ #else
+       unsigned int av_beacon_alloc;
+ #endif
++      struct wprobe_iface av_wpif;
++      u32 av_rxframes;
++      u32 av_rxprobereq;
+ };
+ #define       ATH_VAP(_v)     ((struct ath_vap *)(_v))
diff --git a/package/madwifi/patches/418-turbo.patch b/package/madwifi/patches/418-turbo.patch
deleted file mode 100644 (file)
index 4e13f90..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
---- a/net80211/ieee80211_scan.c
-+++ b/net80211/ieee80211_scan.c
-@@ -1129,7 +1129,11 @@ ieee80211_scan_add_channels(struct ieee8
-                       continue;
-               if (c->ic_scanflags & IEEE80211_NOSCAN_SET)
-                       continue;
--              if (modeflags &&
-+              if (ss->ss_vap->iv_opmode == IEEE80211_M_HOSTAP) {
-+                      if ((c->ic_flags & (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)) !=
-+                          (modeflags & (IEEE80211_CHAN_TURBO | IEEE80211_CHAN_STURBO)))
-+                              continue;
-+              } else if (modeflags &&
-                       ((c->ic_flags & IEEE80211_CHAN_ALLTURBO) !=
-                        (modeflags & IEEE80211_CHAN_ALLTURBO)))
-                       continue;
index b7d26d976dc32d0f2352ade922e9bcdb9f679e13..e12de72ded3409c9e7d6dbd18d465433c6eb27be 100644 (file)
@@ -1,6 +1,32 @@
 --- a/ath/if_ath.c
 +++ b/ath/if_ath.c
-@@ -5344,27 +5344,6 @@ ath_beacon_send(struct ath_softc *sc, in
+@@ -797,7 +797,6 @@ ath_attach(u_int16_t devid, struct net_d
+               break;
+       }
+-      sc->sc_setdefantenna = ath_setdefantenna;
+       sc->sc_rc = ieee80211_rate_attach(sc, ratectl);
+       if (sc->sc_rc == NULL) {
+               error = EIO;
+@@ -2623,9 +2622,6 @@ ath_init(struct net_device *dev)
+       ath_radar_update(sc);
+       ath_rp_flush(sc);
+-      /* Set the default RX antenna; it may get lost on reset. */
+-      ath_setdefantenna(sc, sc->sc_defant);
+-
+       /*
+        * Setup the hardware after reset: the key cache
+        * is filled as needed and the receive engine is
+@@ -3010,7 +3006,6 @@ ath_reset(struct net_device *dev)
+       ath_setintmit(sc);
+       ath_update_txpow(sc);           /* update tx power state */
+       ath_radar_update(sc);
+-      ath_setdefantenna(sc, sc->sc_defant);
+       if (ath_startrecv(sc) != 0)     /* restart recv */
+               EPRINTF(sc, "Unable to start receive logic.\n");
+       if (sc->sc_softled)
+@@ -5344,27 +5339,6 @@ ath_beacon_send(struct ath_softc *sc, in
        } else if ((sc->sc_updateslot == COMMIT) && (sc->sc_slotupdate == slot))
                ath_setslottime(sc);            /* commit change to hardware */
  
        if (bfaddr != 0) {
                /*
                 * Stop any current DMA and put the new frame(s) on the queue.
+@@ -6725,9 +6699,8 @@ ath_setdefantenna(struct ath_softc *sc, 
+ {
+       struct ath_hal *ah = sc->sc_ah;
+-      /* XXX block beacon interrupts */
+-      ath_hal_setdiversity(ah, (sc->sc_diversity != 0));
+       ath_hal_setdefantenna(ah, antenna);
++      ath_hal_setantennaswitch(ah, sc->sc_diversity ? 0 : sc->sc_defant);
+       if (sc->sc_defant != antenna)
+               sc->sc_stats.ast_ant_defswitch++;
+       sc->sc_defant = antenna;
+@@ -11138,7 +11111,7 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+                                       break;
+                               }
+                               sc->sc_diversity = val;
+-                              ath_hal_setdiversity(ah, val);
++                              ath_setdefantenna(sc, sc->sc_defant);
+                               break;
+                       case ATH_TXINTRPERIOD:
+                               /* XXX: validate? */
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -640,7 +640,6 @@ struct ath_softc {
+       spinlock_t sc_hal_lock;                 /* hardware access lock */
+       struct ath_ratectrl *sc_rc;             /* tx rate control support */
+       struct ath_tx99 *sc_tx99;               /* tx99 support */
+-      void (*sc_setdefantenna)(struct ath_softc *, u_int);
+       const struct ath_hw_detect *sc_hwinfo;
+       unsigned int    sc_invalid:1;           /* being detached */
diff --git a/package/madwifi/patches/421-channel_handling.patch b/package/madwifi/patches/421-channel_handling.patch
new file mode 100644 (file)
index 0000000..697dfcf
--- /dev/null
@@ -0,0 +1,1348 @@
+--- a/ath/if_ath.c
++++ b/ath/if_ath.c
+@@ -148,7 +148,6 @@ static int ath_key_set(struct ieee80211v
+ static void ath_key_update_begin(struct ieee80211vap *);
+ static void ath_key_update_end(struct ieee80211vap *);
+ static void ath_mode_init(struct net_device *);
+-static void ath_setslottime(struct ath_softc *);
+ static void ath_updateslot(struct net_device *);
+ static int ath_beaconq_setup(struct ath_softc *);
+ static int ath_beacon_alloc(struct ath_softc *, struct ieee80211_node *);
+@@ -240,7 +239,7 @@ static void ath_setup_stationkey(struct 
+ static void ath_setup_stationwepkey(struct ieee80211_node *);
+ static void ath_setup_keycacheslot(struct ath_softc *, struct ieee80211_node *);
+ static void ath_newassoc(struct ieee80211_node *, int);
+-static int ath_getchannels(struct net_device *, u_int, HAL_BOOL, HAL_BOOL);
++static int ath_getchannels(struct net_device *);
+ static void ath_led_event(struct ath_softc *, int);
+ static void ath_update_txpow(struct ath_softc *);
+@@ -265,7 +264,6 @@ static int ath_change_mtu(struct net_dev
+ static int ath_ioctl(struct net_device *, struct ifreq *, int);
+ static int ath_rate_setup(struct net_device *, u_int);
+-static void ath_setup_subrates(struct net_device *);
+ #ifdef ATH_SUPERG_XR
+ static int ath_xr_rate_setup(struct net_device *);
+ static void ath_grppoll_txq_setup(struct ath_softc *, int, int);
+@@ -387,8 +385,6 @@ static void ath_fetch_idle_time(struct a
+ /* calibrate every 30 secs in steady state but check every second at first. */
+ static int ath_calinterval = ATH_SHORT_CALINTERVAL;
+-static int ath_countrycode = CTRY_DEFAULT;    /* country code */
+-static int ath_outdoor = AH_FALSE;            /* enable outdoor use */
+ static int ath_xchanmode = AH_TRUE;           /* enable extended channels */
+ static int ath_maxvaps = ATH_MAXVAPS_DEFAULT;   /* set default maximum vaps */
+ static int bstuck_thresh = BSTUCK_THRESH;       /* Stuck beacon count required for reset */
+@@ -396,9 +392,7 @@ static char *autocreate = NULL;
+ static char *ratectl = DEF_RATE_CTL;
+ static int rfkill = 0;
+ static int tpc = 1;
+-static int countrycode = -1;
+ static int maxvaps = -1;
+-static int outdoor = -1;
+ static int xchanmode = -1;
+ #include "ath_wprobe.c"
+ static int beacon_cal = 1;
+@@ -437,9 +431,7 @@ static struct notifier_block ath_event_b
+ #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,52))
+ MODULE_PARM(beacon_cal, "i");
+-MODULE_PARM(countrycode, "i");
+ MODULE_PARM(maxvaps, "i");
+-MODULE_PARM(outdoor, "i");
+ MODULE_PARM(xchanmode, "i");
+ MODULE_PARM(rfkill, "i");
+ #ifdef ATH_CAP_TPC
+@@ -451,9 +443,7 @@ MODULE_PARM(ratectl, "s");
+ #else
+ #include <linux/moduleparam.h>
+ module_param(beacon_cal, int, 0600);
+-module_param(countrycode, int, 0600);
+ module_param(maxvaps, int, 0600);
+-module_param(outdoor, int, 0600);
+ module_param(xchanmode, int, 0600);
+ module_param(rfkill, int, 0600);
+ #ifdef ATH_CAP_TPC
+@@ -463,9 +453,7 @@ module_param(bstuck_thresh, int, 0600);
+ module_param(autocreate, charp, 0600);
+ module_param(ratectl, charp, 0600);
+ #endif
+-MODULE_PARM_DESC(countrycode, "Override default country code");
+ MODULE_PARM_DESC(maxvaps, "Maximum VAPs");
+-MODULE_PARM_DESC(outdoor, "Enable/disable outdoor use");
+ MODULE_PARM_DESC(xchanmode, "Enable/disable extended channel mode");
+ MODULE_PARM_DESC(rfkill, "Enable/disable RFKILL capability");
+ #ifdef ATH_CAP_TPC
+@@ -531,6 +519,50 @@ MODULE_PARM_DESC(ieee80211_debug, "Load-
+                               (bssid)[0] |= (((id) << 2) | 0x02);     \
+               } while (0)
++static inline int ath_chan2mode(struct ieee80211_channel *c)
++{
++      if (IEEE80211_IS_CHAN_HALF(c))
++              return ATH_MODE_HALF;
++      else if (IEEE80211_IS_CHAN_QUARTER(c))
++              return ATH_MODE_QUARTER;
++      else
++              return ieee80211_chan2mode(c);
++}
++
++static inline int rate_hal2ieee(int dot11Rate, int f)
++{
++      int flag = dot11Rate & ~(IEEE80211_RATE_VAL);
++      dot11Rate &= IEEE80211_RATE_VAL;
++
++      if (f == 4) { /* Quarter */
++              if (dot11Rate == 4)
++                      return 18 | flag;
++      }
++      return (dot11Rate * f) | flag;
++}
++
++static inline int rate_factor(int mode)
++{
++      int f;
++
++      /*
++       * NB: Fix up rates. HAL returns half or quarter dot11Rates,
++       * while the stack deals with full rates only
++       */
++      switch(mode) {
++              case ATH_MODE_HALF:
++                      f = 2;
++                      break;
++              case ATH_MODE_QUARTER:
++                      f = 4;
++                      break;
++              default:
++                      f = 1;
++                      break;
++      }
++      return f;
++}
++
+ /* Initialize ath_softc structure */
+ int
+@@ -647,14 +679,6 @@ ath_attach(u_int16_t devid, struct net_d
+       for (i = 0; i < sc->sc_keymax; i++)
+               ath_hal_keyreset(ah, i);
+-      /*
+-       * Collect the channel list using the default country
+-       * code and including outdoor channels.  The 802.11 layer
+-       * is responsible for filtering this list based on settings
+-       * like the phy mode.
+-       */
+-      if (countrycode != -1)
+-              ath_countrycode = countrycode;
+       if (maxvaps != -1) {
+               ath_maxvaps = maxvaps;
+               if (ath_maxvaps < ATH_MAXVAPS_MIN)
+@@ -662,17 +686,14 @@ ath_attach(u_int16_t devid, struct net_d
+               else if (ath_maxvaps > ATH_MAXVAPS_MAX)
+                       ath_maxvaps = ATH_MAXVAPS_MAX;
+       }
+-      if (outdoor != -1)
+-              ath_outdoor = outdoor;
+       if (xchanmode != -1)
+               ath_xchanmode = xchanmode;
+-      error = ath_getchannels(dev, ath_countrycode,
+-                      ath_outdoor, ath_xchanmode);
++      error = ath_getchannels(dev);
+       if (error != 0)
+               goto bad;
+-      ic->ic_country_code = ath_countrycode;
+-      ic->ic_country_outdoor = ath_outdoor;
++      ic->ic_country_code = CTRY_DEFAULT;
++      ic->ic_country_outdoor = 0;
+       IPRINTF(sc, "Switching rfkill capability %s\n",
+               rfkill ? "on" : "off");
+@@ -686,9 +707,8 @@ ath_attach(u_int16_t devid, struct net_d
+       ath_rate_setup(dev, IEEE80211_MODE_11G);
+       ath_rate_setup(dev, IEEE80211_MODE_TURBO_A);
+       ath_rate_setup(dev, IEEE80211_MODE_TURBO_G);
+-
+-      /* Setup for half/quarter rates */
+-      ath_setup_subrates(dev);
++      ath_rate_setup(dev, ATH_MODE_HALF);
++      ath_rate_setup(dev, ATH_MODE_QUARTER);
+       /* NB: setup here so ath_rate_update is happy */
+       ath_setcurmode(sc, IEEE80211_MODE_11A);
+@@ -908,10 +928,6 @@ ath_attach(u_int16_t devid, struct net_d
+                       IEEE80211_ATHC_COMP : 0);
+ #endif
+-#ifdef ATH_SUPERG_DYNTURBO
+-      ic->ic_ath_cap |= (ath_hal_turboagsupported(ah, ath_countrycode) ? 
+-                      (IEEE80211_ATHC_TURBOP | IEEE80211_ATHC_AR) : 0);
+-#endif
+ #ifdef ATH_SUPERG_XR
+       ic->ic_ath_cap |= (ath_hal_xrsupported(ah) ? IEEE80211_ATHC_XR : 0);
+ #endif
+@@ -4461,17 +4477,17 @@ ath_mode_init(struct net_device *dev)
+  * Set the slot time based on the current setting.
+  */
+ static void
+-ath_setslottime(struct ath_softc *sc)
++ath_settiming(struct ath_softc *sc)
+ {
+-      struct ieee80211com *ic = &sc->sc_ic;
+       struct ath_hal *ah = sc->sc_ah;
++      u_int offset = getTimingOffset(sc);
+-      if (sc->sc_slottimeconf > 0) /* manual override */
+-              ath_hal_setslottime(ah, sc->sc_slottimeconf);
+-      else if (ic->ic_flags & IEEE80211_F_SHSLOT)
+-              ath_hal_setslottime(ah, HAL_SLOT_TIME_9);
+-      else
+-              ath_hal_setslottime(ah, HAL_SLOT_TIME_20);
++      if (sc->sc_slottimeconf > 0)
++              ath_hal_setslottime(ah, offset + sc->sc_slottimeconf);
++      if (sc->sc_acktimeconf > 0)
++              ath_hal_setacktimeout(ah, 2 * offset + sc->sc_acktimeconf);
++      if (sc->sc_ctstimeconf > 0)
++              ath_hal_setctstimeout(ah, 2 * offset + sc->sc_ctstimeconf);
+       sc->sc_updateslot = OK;
+ }
+@@ -4493,7 +4509,7 @@ ath_updateslot(struct net_device *dev)
+       if (ic->ic_opmode == IEEE80211_M_HOSTAP)
+               sc->sc_updateslot = UPDATE;
+       else if (dev->flags & IFF_RUNNING)
+-              ath_setslottime(sc);
++              ath_settiming(sc);
+ }
+ #ifdef ATH_SUPERG_DYNTURBO
+@@ -5337,7 +5353,7 @@ ath_beacon_send(struct ath_softc *sc, in
+               sc->sc_updateslot = COMMIT;     /* commit next beacon */
+               sc->sc_slotupdate = slot;
+       } else if ((sc->sc_updateslot == COMMIT) && (sc->sc_slotupdate == slot))
+-              ath_setslottime(sc);            /* commit change to hardware */
++              ath_settiming(sc);              /* commit change to hardware */
+       if (bfaddr != 0) {
+               /*
+@@ -7790,12 +7806,14 @@ ath_get_ivlen(struct ieee80211_key *k)
+  * Get transmit rate index using rate in Kbps
+  */
+ static __inline int
+-ath_tx_findindex(const HAL_RATE_TABLE *rt, int rate)
++ath_tx_findindex(struct ath_softc *sc, const HAL_RATE_TABLE *rt, int rate)
+ {
+       unsigned int i, ndx = 0;
++      int f;
++      f = rate_factor(sc->sc_curmode);
+       for (i = 0; i < rt->rateCount; i++) {
+-              if (rt->info[i].rateKbps == rate) {
++              if ((rt->info[i].rateKbps * f) == rate) {
+                       ndx = i;
+                       break;
+               }
+@@ -8088,7 +8106,7 @@ ath_tx_start(struct net_device *dev, str
+               atype = HAL_PKT_TYPE_NORMAL;            /* default */
+               if (ismcast) {
+-                      rix = ath_tx_findindex(rt, vap->iv_mcast_rate);
++                      rix = ath_tx_findindex(sc, rt, vap->iv_mcast_rate);
+                       txrate = rt->info[rix].rateCode;
+                       if (shortPreamble)
+                               txrate |= rt->info[rix].shortPreamble;
+@@ -9055,7 +9073,7 @@ ath_chan_change(struct ath_softc *sc, st
+       struct net_device *dev = sc->sc_dev;
+       enum ieee80211_phymode mode;
+-      mode = ieee80211_chan2mode(chan);
++      mode = ath_chan2mode(chan);
+       ath_rate_setup(dev, mode);
+       ath_setcurmode(sc, mode);
+@@ -10104,8 +10122,7 @@ ath_newassoc(struct ieee80211_node *ni, 
+ }
+ static int
+-ath_getchannels(struct net_device *dev, u_int cc,
+-      HAL_BOOL outdoor, HAL_BOOL xchanmode)
++ath_getchannels(struct net_device *dev)
+ {
+       struct ath_softc *sc = dev->priv;
+       struct ieee80211com *ic = &sc->sc_ic;
+@@ -10119,17 +10136,31 @@ ath_getchannels(struct net_device *dev, 
+               EPRINTF(sc, "Insufficient memory for channel table!\n");
+               return -ENOMEM;
+       }
++
++restart:
+       if (!ath_hal_init_channels(ah, chans, IEEE80211_CHAN_MAX, &nchan,
+           ic->ic_regclassids, IEEE80211_REGCLASSIDS_MAX, &ic->ic_nregclass,
+-          cc, HAL_MODE_ALL, outdoor, xchanmode)) {
++          ic->ic_country_code, HAL_MODE_ALL, ic->ic_country_outdoor, ath_xchanmode)) {
+               u_int32_t rd;
+               ath_hal_getregdomain(ah, &rd);
+               EPRINTF(sc, "Unable to collect channel list from HAL; "
+-                      "regdomain likely %u country code %u\n", rd, cc);
++                      "regdomain likely %u country code %u\n", rd, ic->ic_country_code);
++              if ((ic->ic_country_code != CTRY_DEFAULT) ||
++                      (ic->ic_country_outdoor != 0)) {
++                      EPRINTF(sc, "Reverting to defaults\n");
++                      ic->ic_country_code = CTRY_DEFAULT;
++                      ic->ic_country_outdoor = 0;
++                      goto restart;
++              }
+               kfree(chans);
+               return -EINVAL;
+       }
++#ifdef ATH_SUPERG_DYNTURBO
++      ic->ic_ath_cap &= ~(IEEE80211_ATHC_TURBOP | IEEE80211_ATHC_AR);
++      ic->ic_ath_cap |= (ath_hal_turboagsupported(ah, ic->ic_country_code) ?
++                      (IEEE80211_ATHC_TURBOP | IEEE80211_ATHC_AR) : 0);
++#endif
+       /*
+        * Convert HAL channels to ieee80211 ones.
+        */
+@@ -10373,7 +10404,7 @@ ath_xr_rate_setup(struct net_device *dev
+       struct ieee80211com *ic = &sc->sc_ic;
+       const HAL_RATE_TABLE *rt;
+       struct ieee80211_rateset *rs;
+-      unsigned int i, maxrates;
++      unsigned int i, j, maxrates;
+       sc->sc_xr_rates = ath_hal_getratetable(ah, HAL_MODE_XR);
+       rt = sc->sc_xr_rates;
+       if (rt == NULL)
+@@ -10386,57 +10417,16 @@ ath_xr_rate_setup(struct net_device *dev
+       } else
+               maxrates = rt->rateCount;
+       rs = &ic->ic_sup_xr_rates;
+-      for (i = 0; i < maxrates; i++)
+-              rs->rs_rates[i] = rt->info[i].dot11Rate;
+-      rs->rs_nrates = maxrates;
++      for (j = 0, i = 0; i < maxrates; i++) {
++              if (!rt->info[i].valid)
++                      continue;
++              rs->rs_rates[j++] = rt->info[i].dot11Rate;
++      }
++      rs->rs_nrates = j;
+       return 1;
+ }
+ #endif
+-/* Setup half/quarter rate table support */
+-static void
+-ath_setup_subrates(struct net_device *dev)
+-{
+-      struct ath_softc *sc = dev->priv;
+-      struct ath_hal *ah = sc->sc_ah;
+-      struct ieee80211com *ic = &sc->sc_ic;
+-      const HAL_RATE_TABLE *rt;
+-      struct ieee80211_rateset *rs;
+-      unsigned int i, maxrates;
+-
+-      sc->sc_half_rates = ath_hal_getratetable(ah, HAL_MODE_11A_HALF_RATE);
+-      rt = sc->sc_half_rates;
+-      if (rt != NULL) {
+-              if (rt->rateCount > IEEE80211_RATE_MAXSIZE) {
+-                      DPRINTF(sc, ATH_DEBUG_ANY,
+-                              "The rate table is too small (%u > %u)\n",
+-                             rt->rateCount, IEEE80211_RATE_MAXSIZE);
+-                      maxrates = IEEE80211_RATE_MAXSIZE;
+-              } else
+-                      maxrates = rt->rateCount;
+-              rs = &ic->ic_sup_half_rates;
+-              for (i = 0; i < maxrates; i++)
+-                      rs->rs_rates[i] = rt->info[i].dot11Rate;
+-              rs->rs_nrates = maxrates;
+-      }
+-
+-      sc->sc_quarter_rates = ath_hal_getratetable(ah, HAL_MODE_11A_QUARTER_RATE);
+-      rt = sc->sc_quarter_rates;
+-      if (rt != NULL) {
+-              if (rt->rateCount > IEEE80211_RATE_MAXSIZE) {
+-                      DPRINTF(sc, ATH_DEBUG_ANY,
+-                              "The rate table is too small (%u > %u)\n",
+-                             rt->rateCount, IEEE80211_RATE_MAXSIZE);
+-                      maxrates = IEEE80211_RATE_MAXSIZE;
+-              } else
+-                      maxrates = rt->rateCount;
+-              rs = &ic->ic_sup_quarter_rates;
+-              for (i = 0; i < maxrates; i++)
+-                      rs->rs_rates[i] = rt->info[i].dot11Rate;
+-              rs->rs_nrates = maxrates;
+-      }
+-}
+-
+ static int
+ ath_rate_setup(struct net_device *dev, u_int mode)
+ {
+@@ -10445,7 +10435,7 @@ ath_rate_setup(struct net_device *dev, u
+       struct ieee80211com *ic = &sc->sc_ic;
+       const HAL_RATE_TABLE *rt;
+       struct ieee80211_rateset *rs;
+-      unsigned int i, maxrates;
++      unsigned int i, j, maxrates, f;
+       switch (mode) {
+       case IEEE80211_MODE_11A:
+@@ -10463,6 +10453,12 @@ ath_rate_setup(struct net_device *dev, u
+       case IEEE80211_MODE_TURBO_G:
+               sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_108G);
+               break;
++      case ATH_MODE_HALF:
++              sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_11A_HALF_RATE);
++              break;
++      case ATH_MODE_QUARTER:
++              sc->sc_rates[mode] = ath_hal_getratetable(ah, HAL_MODE_11A_QUARTER_RATE);
++              break;
+       default:
+               DPRINTF(sc, ATH_DEBUG_ANY, "Invalid mode %u\n", mode);
+               return 0;
+@@ -10477,10 +10473,16 @@ ath_rate_setup(struct net_device *dev, u
+               maxrates = IEEE80211_RATE_MAXSIZE;
+       } else
+               maxrates = rt->rateCount;
++
++      /* NB: quarter/half rate channels hijack the 11A rateset */
++      if (mode >= IEEE80211_MODE_MAX)
++              return 1;
++
+       rs = &ic->ic_sup_rates[mode];
+       for (i = 0; i < maxrates; i++)
+               rs->rs_rates[i] = rt->info[i].dot11Rate;
+       rs->rs_nrates = maxrates;
++
+       return 1;
+ }
+@@ -10509,13 +10511,18 @@ ath_setcurmode(struct ath_softc *sc, enu
+               {   0, 500, 130 },
+       };
+       const HAL_RATE_TABLE *rt;
+-      unsigned int i, j;
++      unsigned int i, j, f;
++      /*
++       * NB: Fix up rixmap. HAL returns half or quarter dot11Rates,
++       * while the stack deals with full rates only
++       */
++      f = rate_factor(mode);
+       memset(sc->sc_rixmap, 0xff, sizeof(sc->sc_rixmap));
+       rt = sc->sc_rates[mode];
+       KASSERT(rt != NULL, ("no h/w rate set for phy mode %u", mode));
+       for (i = 0; i < rt->rateCount; i++)
+-              sc->sc_rixmap[rt->info[i].dot11Rate & IEEE80211_RATE_VAL] = i;
++              sc->sc_rixmap[rate_hal2ieee(rt->info[i].dot11Rate, f) & IEEE80211_RATE_VAL] = i;
+       memset(sc->sc_hwmap, 0, sizeof(sc->sc_hwmap));
+       for (i = 0; i < 32; i++) {
+               u_int8_t ix = rt->rateCodeToIndex[i];
+@@ -10525,7 +10532,7 @@ ath_setcurmode(struct ath_softc *sc, enu
+                       continue;
+               }
+               sc->sc_hwmap[i].ieeerate =
+-                      rt->info[ix].dot11Rate & IEEE80211_RATE_VAL;
++                      rate_hal2ieee(rt->info[ix].dot11Rate, f) & IEEE80211_RATE_VAL;
+               if (rt->info[ix].shortPreamble ||
+                   rt->info[ix].phy == IEEE80211_T_OFDM)
+                       sc->sc_hwmap[i].flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
+@@ -10926,9 +10933,106 @@ enum {
+       ATH_MAXVAPS             = 26,
+       ATH_INTMIT                      = 27,
+       ATH_NOISE_IMMUNITY      = 28,
+-      ATH_OFDM_WEAK_DET       = 29
++      ATH_OFDM_WEAK_DET       = 29,
++      ATH_CHANBW              = 30,
++      ATH_OUTDOOR             = 31,
+ };
++/*
++ * perform the channel related sysctl, reload the channel list
++ * and try to stay on the current frequency
++ */
++static int ath_sysctl_setchanparam(struct ath_softc *sc, unsigned long ctl, u_int val)
++{
++      struct ieee80211com *ic = &sc->sc_ic;
++      struct ath_hal *ah = sc->sc_ah;
++      struct ieee80211_channel *c = NULL;
++      struct ieee80211vap *vap;
++      u_int16_t freq = 0;
++      struct ifreq ifr;
++
++      if (ic->ic_curchan != IEEE80211_CHAN_ANYC)
++              freq = ic->ic_curchan->ic_freq;
++
++      switch(ctl) {
++      case ATH_COUNTRYCODE:
++              ic->ic_country_code = val;
++              break;
++      case ATH_OUTDOOR:
++              ic->ic_country_outdoor = val;
++              break;
++      case ATH_CHANBW:
++              switch(val) {
++              case 0:
++              case 5:
++              case 10:
++              case 20:
++              case 40:
++                      if (ath_hal_setcapability(ah, HAL_CAP_CHANBW, 1, val, NULL) == AH_TRUE) {
++                              sc->sc_chanbw = val;
++                              break;
++                      }
++              default:
++                      return -EINVAL;
++              }
++              break;
++      }
++
++      if (ic->ic_curchan != IEEE80211_CHAN_ANYC)
++              freq = ic->ic_curchan->ic_freq;
++
++      /* clear out any old state */
++      TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++              vap->iv_des_mode = IEEE80211_MODE_AUTO;
++              vap->iv_des_chan = IEEE80211_CHAN_ANYC;
++      }
++      ieee80211_scan_flush(ic);
++
++      IEEE80211_LOCK_IRQ(ic);
++      ath_getchannels(sc->sc_dev);
++      ieee80211_update_channels(ic, 0);
++      if (freq)
++              c = ieee80211_find_channel(ic, freq, IEEE80211_MODE_AUTO);
++      if (!c)
++              c = &ic->ic_channels[0];
++      ic->ic_curchan = c;
++      ic->ic_bsschan = c;
++      ic->ic_curmode = IEEE80211_MODE_AUTO;
++      IEEE80211_UNLOCK_IRQ(ic);
++
++      if (!(sc->sc_dev->flags & IFF_RUNNING)) {
++              ic->ic_bsschan = IEEE80211_CHAN_ANYC;
++              return 0;
++      }
++
++#ifndef ifr_media
++#define    ifr_media       ifr_ifru.ifru_ivalue
++#endif
++      memset(&ifr, 0, sizeof(ifr));
++      ifr.ifr_media = ic->ic_media.ifm_cur->ifm_media & ~IFM_MMASK;
++      ifr.ifr_media |= IFM_MAKEMODE(IEEE80211_MODE_AUTO);
++      ifmedia_ioctl(ic->ic_dev, &ifr, &ic->ic_media, SIOCSIFMEDIA);
++
++      /* apply the channel to the hw */
++      ath_set_channel(ic);
++
++      TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++              struct net_device *dev = vap->iv_dev;
++
++              /* reactivate all active vaps */
++              vap->iv_state = IEEE80211_S_SCAN;
++              if ((vap->iv_opmode == IEEE80211_M_HOSTAP) ||
++                      (vap->iv_opmode == IEEE80211_M_MONITOR) ||
++                      (vap->iv_opmode == IEEE80211_M_WDS))
++                      ieee80211_new_state(vap, IEEE80211_S_RUN, 0);
++              else
++                      ieee80211_new_state(vap, IEEE80211_S_INIT, -1);
++      }
++
++      return 0;
++}
++
++
+ static int
+ ath_sysctl_set_intmit(struct ath_softc *sc, long ctl, u_int val)
+ {
+@@ -11007,6 +11111,7 @@ static int
+ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl, write, filp, buffer, lenp, ppos)
+ {
+       struct ath_softc *sc = ctl->extra1;
++      struct ieee80211com *ic = &sc->sc_ic;
+       struct ath_hal *ah = sc->sc_ah;
+       u_int val;
+       u_int tab_3_val[3];
+@@ -11030,25 +11135,33 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+                               lenp, ppos);
+               if (ret == 0) {
+                       switch ((long)ctl->extra2) {
++                      case ATH_REGDOMAIN:
++                              ath_hal_setregdomain(ah, val);
++                              break;
++                      case ATH_COUNTRYCODE:
++                      case ATH_CHANBW:
++                              ret = ath_sysctl_setchanparam(sc, (long) ctl->extra2, val);
++                              break;
+                       case ATH_SLOTTIME:
+-                              if (val > 0) {
+-                                      if (!ath_hal_setslottime(ah, val))
+-                                              ret = -EINVAL;
+-                                      else
+-                                              sc->sc_slottimeconf = val;
+-                              } else {
+-                                      /* disable manual override */
++                              if (val > 0)
++                                      sc->sc_slottimeconf = val;
++                              else
+                                       sc->sc_slottimeconf = 0;
+-                                      ath_setslottime(sc);
+-                              }
++                              ath_settiming(sc);
+                               break;
+                       case ATH_ACKTIMEOUT:
+-                              if (!ath_hal_setacktimeout(ah, val))
+-                                      ret = -EINVAL;
++                              if (val > 0)
++                                      sc->sc_acktimeconf = val;
++                              else
++                                      sc->sc_acktimeconf = 0;
++                              ath_settiming(sc);
+                               break;
+                       case ATH_CTSTIMEOUT:
+-                              if (!ath_hal_setctstimeout(ah, val))
+-                                      ret = -EINVAL;
++                              if (val > 0)
++                                      sc->sc_ctstimeconf = val;
++                              else
++                                      sc->sc_ctstimeconf = 0;
++                              ath_settiming(sc);
+                               break;
+                       case ATH_SOFTLED:
+                               if (val != sc->sc_softled) {
+@@ -11201,6 +11314,9 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+               }
+       } else {
+               switch ((long)ctl->extra2) {
++              case ATH_CHANBW:
++                      val = sc->sc_chanbw ?: 20;
++                      break;
+               case ATH_SLOTTIME:
+                       val = ath_hal_getslottime(ah);
+                       break;
+@@ -11219,6 +11335,9 @@ ATH_SYSCTL_DECL(ath_sysctl_halparam, ctl
+               case ATH_COUNTRYCODE:
+                       ath_hal_getcountrycode(ah, &val);
+                       break;
++              case ATH_OUTDOOR:
++                      val = ic->ic_country_outdoor;
++                      break;
+               case ATH_MAXVAPS:
+                       val = ath_maxvaps;
+                       break;
+@@ -11332,11 +11451,17 @@ static const ctl_table ath_sysctl_templa
+       },
+       { .ctl_name     = CTL_AUTO,
+         .procname     = "countrycode",
+-        .mode         = 0444,
++        .mode         = 0644,
+         .proc_handler = ath_sysctl_halparam,
+         .extra2       = (void *)ATH_COUNTRYCODE,
+       },
+       { .ctl_name     = CTL_AUTO,
++        .procname     = "outdoor",
++        .mode         = 0644,
++        .proc_handler = ath_sysctl_halparam,
++        .extra2       = (void *)ATH_OUTDOOR,
++      },
++      { .ctl_name     = CTL_AUTO,
+         .procname     = "maxvaps",
+         .mode         = 0444,
+         .proc_handler = ath_sysctl_halparam,
+@@ -11344,7 +11469,7 @@ static const ctl_table ath_sysctl_templa
+       },
+       { .ctl_name     = CTL_AUTO,
+         .procname     = "regdomain",
+-        .mode         = 0444,
++        .mode         = 0644,
+         .proc_handler = ath_sysctl_halparam,
+         .extra2       = (void *)ATH_REGDOMAIN,
+       },
+@@ -11407,6 +11532,12 @@ static const ctl_table ath_sysctl_templa
+         .extra2       = (void *)ATH_ACKRATE,
+       },
+       { .ctl_name     = CTL_AUTO,
++        .procname     = "channelbw",
++        .mode         = 0644,
++        .proc_handler = ath_sysctl_halparam,
++        .extra2       = (void *)ATH_CHANBW,
++      },
++      { .ctl_name     = CTL_AUTO,
+         .procname     = "rp",
+         .mode         = 0200,
+         .proc_handler = ath_sysctl_halparam,
+@@ -11647,13 +11778,6 @@ static ctl_table ath_static_sysctls[] = 
+       },
+ #endif
+       { .ctl_name     = CTL_AUTO,
+-        .procname     = "countrycode",
+-        .mode         = 0444,
+-        .data         = &ath_countrycode,
+-        .maxlen       = sizeof(ath_countrycode),
+-        .proc_handler = proc_dointvec
+-      },
+-      { .ctl_name     = CTL_AUTO,
+         .procname     = "maxvaps",
+         .mode         = 0444,
+         .data         = &ath_maxvaps,
+@@ -11661,13 +11785,6 @@ static ctl_table ath_static_sysctls[] = 
+         .proc_handler = proc_dointvec
+       },
+       { .ctl_name     = CTL_AUTO,
+-        .procname     = "outdoor",
+-        .mode         = 0444,
+-        .data         = &ath_outdoor,
+-        .maxlen       = sizeof(ath_outdoor),
+-        .proc_handler = proc_dointvec
+-      },
+-      { .ctl_name     = CTL_AUTO,
+         .procname     = "xchanmode",
+         .mode         = 0444,
+         .data         = &ath_xchanmode,
+--- a/ath/if_athvar.h
++++ b/ath/if_athvar.h
+@@ -688,16 +688,17 @@ struct ath_softc {
+       int8_t sc_ofdm_weak_det; /* OFDM weak frames detection, -1 == auto */
+       /* rate tables */
+-      const HAL_RATE_TABLE *sc_rates[IEEE80211_MODE_MAX];
++#define ATH_MODE_HALF         (IEEE80211_MODE_MAX)
++#define ATH_MODE_QUARTER      (IEEE80211_MODE_MAX + 1)
++      const HAL_RATE_TABLE *sc_rates[IEEE80211_MODE_MAX + 2];
+       const HAL_RATE_TABLE *sc_currates;      /* current rate table */
+       const HAL_RATE_TABLE *sc_xr_rates;      /* XR rate table */
+-      const HAL_RATE_TABLE *sc_half_rates;    /* half rate table */
+-      const HAL_RATE_TABLE *sc_quarter_rates; /* quarter rate table */
+       HAL_OPMODE sc_opmode;                   /* current hal operating mode */
+       enum ieee80211_phymode sc_curmode;      /* current phy mode */
+       u_int16_t sc_curtxpow;                  /* current tx power limit */
+       u_int16_t sc_curaid;                    /* current association id */
+       HAL_CHANNEL sc_curchan;                 /* current h/w channel */
++      u_int8_t sc_chanbw;                             /* channel bandwidth */
+       u_int8_t sc_curbssid[IEEE80211_ADDR_LEN];
+       u_int8_t        sc_rixmap[256];                 /* IEEE to h/w rate table ix */
+       struct {
+@@ -808,6 +809,8 @@ struct ath_softc {
+       u_int32_t sc_dturbo_bw_turbo;           /* bandwidth threshold */
+ #endif
+       u_int sc_slottimeconf;                  /* manual override for slottime */
++      u_int sc_acktimeconf;                   /* manual override for acktime */
++      u_int sc_ctstimeconf;                   /* manual override for ctstime */
+       struct timer_list sc_dfs_excl_timer;    /* mark expiration timer task */
+       struct timer_list sc_dfs_cac_timer;     /* dfs wait timer */
+@@ -826,6 +829,7 @@ struct ath_softc {
+       int sc_rp_num;
+       int sc_rp_min;
+       HAL_BOOL (*sc_rp_analyse)(struct ath_softc *sc);
++      struct ATH_TQ_STRUCT sc_refresh_tq;
+       struct ATH_TQ_STRUCT sc_rp_tq;
+       
+       int sc_rp_ignored;                      /* if set, we ignored all 
+@@ -941,6 +945,48 @@ int ar_device(int devid);
+         DEV_NAME(_v->iv_ic->ic_dev))
+ void ath_radar_detected(struct ath_softc *sc, const char* message);
++static inline u_int getTimingOffset(struct ath_softc *sc)
++{
++      struct ieee80211com *ic = &sc->sc_ic;
++      u_int usec = 9;
++      if (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan)) {
++              usec = 20;
++              if (ic->ic_flags & IEEE80211_F_SHSLOT)
++                      usec = 9;
++      } else if (IEEE80211_IS_CHAN_A(ic->ic_curchan))
++              usec = 9;
++
++      if (IEEE80211_IS_CHAN_TURBO(ic->ic_curchan))
++              usec = 6;
++
++      if (IEEE80211_IS_CHAN_HALF(ic->ic_curchan))
++              usec = 13;
++      else if (IEEE80211_IS_CHAN_QUARTER(ic->ic_curchan))
++              usec = 21;
++      return usec;
++}
++
++static inline void ath_get_timings(struct ath_softc *sc, u_int *t_slot, u_int *t_sifs, u_int *t_difs)
++{
++      struct ieee80211_channel *c = sc->sc_ic.ic_curchan;
++
++      *t_slot = getTimingOffset(sc) + sc->sc_slottimeconf;
++
++      if (IEEE80211_IS_CHAN_HALF(c)) {
++              *t_sifs = 32;
++              *t_difs = 56;
++      } else if (IEEE80211_IS_CHAN_QUARTER(c)) {
++              *t_sifs = 64;
++              *t_difs = 112;
++      } else if (IEEE80211_IS_CHAN_TURBO(c)) {
++              *t_sifs = 8;
++              *t_difs = 28;
++      } else {
++              *t_sifs = 16;
++              *t_difs = 28;
++      }
++}
++
+ struct ath_hw_detect {
+       const char *vendor_name;
+--- a/tools/athctrl.c
++++ b/tools/athctrl.c
+@@ -118,7 +118,7 @@ CMD(athctrl)(int argc, char *argv[])
+       }
+       if (distance >= 0) {
+-              int slottime = 9 + (distance / 300) + ((distance % 300) ? 1 : 0);
++              int slottime = (distance / 300) + ((distance % 300) ? 1 : 0);
+               int acktimeout = slottime * 2 + 3;
+               int ctstimeout = slottime * 2 + 3;
+--- a/net80211/ieee80211.c
++++ b/net80211/ieee80211.c
+@@ -243,34 +243,17 @@ static const  struct country_code_to_str
+       {CTRY_ZIMBABWE,             "ZW"}
+ };
+-int
+-ieee80211_ifattach(struct ieee80211com *ic)
++void ieee80211_update_channels(struct ieee80211com *ic, int init)
+ {
+-      struct net_device *dev = ic->ic_dev;
+       struct ieee80211_channel *c;
++      struct ieee80211vap *vap;
+       struct ifmediareq imr;
++      int ext = 0;
+       int i;
+-      _MOD_INC_USE(THIS_MODULE, return -ENODEV);
+-
+-      /*
+-       * Pick an initial operating mode until we have a vap
+-       * created to lock it down correctly.  This is only
+-       * drivers have something defined for configuring the
+-       * hardware at startup.
+-       */
+-      ic->ic_opmode = IEEE80211_M_STA;        /* everyone supports this */
+-
+-      /*
+-       * Fill in 802.11 available channel set, mark
+-       * all available channels as active, and pick
+-       * a default channel if not already specified.
+-       */
+-      KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX,
+-              ("invalid number of channels specified: %u", ic->ic_nchans));
+       memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail));
+-      ic->ic_modecaps |= 1 << IEEE80211_MODE_AUTO;
+       ic->ic_max_txpower = IEEE80211_TXPOWER_MIN;
++      ic->ic_modecaps = 1 << IEEE80211_MODE_AUTO;
+       for (i = 0; i < ic->ic_nchans; i++) {
+               c = &ic->ic_channels[i];
+@@ -298,6 +281,8 @@ ieee80211_ifattach(struct ieee80211com *
+                       ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_A;
+               if (IEEE80211_IS_CHAN_108G(c))
+                       ic->ic_modecaps |= 1 << IEEE80211_MODE_TURBO_G;
++              if (IEEE80211_IS_CHAN_HALF(c) || IEEE80211_IS_CHAN_QUARTER(c))
++                      ext = 1;
+       }
+       /* Initialize candidate channels to all available */
+       memcpy(ic->ic_chan_active, ic->ic_chan_avail,
+@@ -311,11 +296,58 @@ ieee80211_ifattach(struct ieee80211com *
+        * When 11g is supported, force the rate set to
+        * include basic rates suitable for a mixed b/g bss.
+        */
+-      if (ic->ic_modecaps & (1 << IEEE80211_MODE_11G))
++      if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11G)) && !ext)
+               ieee80211_set11gbasicrates(
+                       &ic->ic_sup_rates[IEEE80211_MODE_11G],
+                       IEEE80211_MODE_11G);
++      if (init)
++              return;
++
++      ieee80211_media_setup(ic, &ic->ic_media, ic->ic_caps, NULL, NULL);
++      ieee80211com_media_status(ic->ic_dev, &imr);
++      ifmedia_set(&ic->ic_media, imr.ifm_active);
++
++      TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) {
++              struct ieee80211vap *avp;
++              TAILQ_FOREACH(avp, &vap->iv_wdslinks, iv_wdsnext) {
++                      (void) ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, NULL, NULL);
++                      ieee80211_media_status(vap->iv_dev, &imr);
++                      ifmedia_set(&vap->iv_media, imr.ifm_active);
++              }
++              (void) ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, NULL, NULL);
++              ieee80211_media_status(vap->iv_dev, &imr);
++              ifmedia_set(&vap->iv_media, imr.ifm_active);
++      }
++}
++EXPORT_SYMBOL(ieee80211_update_channels);
++
++int
++ieee80211_ifattach(struct ieee80211com *ic)
++{
++      struct net_device *dev = ic->ic_dev;
++      struct ieee80211_channel *c;
++      struct ifmediareq imr;
++
++      _MOD_INC_USE(THIS_MODULE, return -ENODEV);
++
++      /*
++       * Pick an initial operating mode until we have a vap
++       * created to lock it down correctly.  This is only
++       * drivers have something defined for configuring the
++       * hardware at startup.
++       */
++      ic->ic_opmode = IEEE80211_M_STA;        /* everyone supports this */
++
++      /*
++       * Fill in 802.11 available channel set, mark
++       * all available channels as active, and pick
++       * a default channel if not already specified.
++       */
++      KASSERT(0 < ic->ic_nchans && ic->ic_nchans < IEEE80211_CHAN_MAX,
++              ("invalid number of channels specified: %u", ic->ic_nchans));
++      ieee80211_update_channels(ic, 1);
++
+       /* Setup initial channel settings */
+       ic->ic_bsschan = IEEE80211_CHAN_ANYC;
+       /* Arbitrarily pick the first channel */
+@@ -327,6 +359,7 @@ ieee80211_ifattach(struct ieee80211com *
+       /* Enable WME by default, if we're capable. */
+       if (ic->ic_caps & IEEE80211_C_WME)
+               ic->ic_flags |= IEEE80211_F_WME;
++
+       (void) ieee80211_setmode(ic, ic->ic_curmode);
+       /* Store default beacon interval, as nec. */
+@@ -763,7 +796,8 @@ ieee80211_media_setup(struct ieee80211co
+       struct ieee80211_rateset allrates;
+       /* Fill in media characteristics. */
+-      ifmedia_init(media, 0, media_change, media_stat);
++      if (media_change || media_stat)
++              ifmedia_init(media, 0, media_change, media_stat);
+       maxrate = 0;
+       memset(&allrates, 0, sizeof(allrates));
+@@ -793,7 +827,7 @@ ieee80211_media_setup(struct ieee80211co
+                       ADD(media, IFM_AUTO, mopt | IFM_IEEE80211_WDS);
+               if (mode == IEEE80211_MODE_AUTO)
+                       continue;
+-              rs = &ic->ic_sup_rates[mode];
++              rs = &ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, mode)];
+               for (i = 0; i < rs->rs_nrates; i++) {
+                       rate = rs->rs_rates[i];
+@@ -1207,7 +1241,7 @@ ieee80211_announce(struct ieee80211com *
+               if ((ic->ic_modecaps & (1 << mode)) == 0)
+                       continue;
+               if_printf(dev, "%s rates: ", ieee80211_phymode_name[mode]);
+-              rs = &ic->ic_sup_rates[mode];
++              rs = &ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, mode)];
+               for (i = 0; i < rs->rs_nrates; i++) {
+                       rate = rs->rs_rates[i];
+                       mword = ieee80211_rate2media(ic, rate, mode);
+@@ -1417,7 +1451,7 @@ ieee80211com_media_change(struct net_dev
+                        * now so drivers have a consistent state.
+                        */
+                       KASSERT(vap->iv_bss != NULL, ("no bss node"));
+-                      vap->iv_bss->ni_rates = ic->ic_sup_rates[newphymode];
++                      vap->iv_bss->ni_rates = ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, newphymode)];
+               }
+               error = -ENETRESET;
+       }
+@@ -1435,7 +1469,7 @@ findrate(struct ieee80211com *ic, enum i
+ {
+ #define       IEEERATE(_ic,_m,_i) \
+       ((_ic)->ic_sup_rates[_m].rs_rates[_i] & IEEE80211_RATE_VAL)
+-      int i, nrates = ic->ic_sup_rates[mode].rs_nrates;
++      int i, nrates = ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, mode)].rs_nrates;
+       for (i = 0; i < nrates; i++)
+               if (IEEERATE(ic, mode, i) == rate)
+                       return i;
+@@ -1877,11 +1911,6 @@ ieee80211_build_countryie(struct ieee802
+                       if (ieee80211_chan2mode(c) != curmode_noturbo)
+                               continue;
+-                      /* Skip half/quarter rate channels */
+-                      if (IEEE80211_IS_CHAN_HALF(c) ||
+-                          IEEE80211_IS_CHAN_QUARTER(c))
+-                              continue;
+-
+                       if (*cur_runlen == 0) {
+                               (*cur_runlen)++;
+                               *cur_pow = c->ic_maxregpower;
+@@ -1915,7 +1944,7 @@ void
+ ieee80211_build_sc_ie(struct ieee80211com *ic)
+ {
+       struct ieee80211_ie_sc *ie = &ic->ic_sc_ie;
+-      int i, j;
++      int i, j, k;
+       struct ieee80211_channel *c;
+       u_int8_t prevchan;
+--- a/net80211/ieee80211_var.h
++++ b/net80211/ieee80211_var.h
+@@ -336,8 +336,6 @@ struct ieee80211com {
+       u_int8_t ic_nopened;                    /* VAPs been opened */
+       struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
+       struct ieee80211_rateset ic_sup_xr_rates;
+-      struct ieee80211_rateset ic_sup_half_rates;
+-      struct ieee80211_rateset ic_sup_quarter_rates;
+       u_int16_t ic_modecaps;                  /* set of mode capabilities */
+       u_int16_t ic_curmode;                   /* current mode */
+       u_int16_t ic_lintval;                   /* beacon interval */
+@@ -715,6 +713,7 @@ MALLOC_DECLARE(M_80211_VAP);
+ int ieee80211_ifattach(struct ieee80211com *);
+ void ieee80211_ifdetach(struct ieee80211com *);
++void ieee80211_update_channels(struct ieee80211com *ic, int);
+ int ieee80211_vap_setup(struct ieee80211com *, struct net_device *,
+       const char *, int, int, struct ieee80211vap *);
+ int ieee80211_vap_attach(struct ieee80211vap *, ifm_change_cb_t, ifm_stat_cb_t);
+@@ -794,6 +793,23 @@ ieee80211_anyhdrspace(struct ieee80211co
+       return size;
+ }
++static __inline int
++ieee80211_chan2ratemode(struct ieee80211_channel *c, int mode)
++{
++      if (mode == -1)
++              mode = ieee80211_chan2mode(c);
++
++      /*
++       * Use 11a rateset for half/quarter to restrict things
++       * to pure OFDM
++       */
++      if (IEEE80211_IS_CHAN_HALF(c) ||
++              IEEE80211_IS_CHAN_QUARTER(c))
++              return IEEE80211_MODE_11A;
++
++      return mode;
++}
++
+ /* Macros to print MAC address used in 802.11 headers */
+ #define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+--- a/net80211/ieee80211_node.c
++++ b/net80211/ieee80211_node.c
+@@ -287,7 +287,7 @@ ieee80211_node_set_chan(struct ieee80211
+               ni->ni_rates = ic->ic_sup_xr_rates;
+       else
+ #endif
+-      ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2mode(chan)];
++      ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2ratemode(chan, -1)];
+ }
+ static __inline void
+@@ -387,6 +387,8 @@ ieee80211_create_ibss(struct ieee80211va
+       ic->ic_bsschan = chan;
+       ieee80211_node_set_chan(ic, ni);
+       ic->ic_curmode = ieee80211_chan2mode(chan);
++      ni->ni_rates = ic->ic_sup_rates[ieee80211_chan2ratemode(chan, -1)];
++
+       spin_lock_irqsave(&channel_lock, flags);
+       ieee80211_scan_set_bss_channel(ic, ic->ic_bsschan);
+       spin_unlock_irqrestore(&channel_lock, flags);
+@@ -394,14 +396,8 @@ ieee80211_create_ibss(struct ieee80211va
+       /* Update country ie information */
+       ieee80211_build_countryie(ic);
+-      if (IEEE80211_IS_CHAN_HALF(chan)) {
+-              ni->ni_rates = ic->ic_sup_half_rates;
+-      } else if (IEEE80211_IS_CHAN_QUARTER(chan)) {
+-              ni->ni_rates = ic->ic_sup_quarter_rates;
+-      }
+-
+-      if ((vap->iv_flags & IEEE80211_F_PUREG) &&
+-              IEEE80211_IS_CHAN_ANYG(chan)) {
++      if ((ieee80211_chan2ratemode(chan, -1) != IEEE80211_MODE_11A) &&
++              IEEE80211_IS_CHAN_ANYG(chan) && (vap->iv_flags & IEEE80211_F_PUREG)) {
+               ieee80211_setpuregbasicrates(&ni->ni_rates);
+       }
+--- a/net80211/ieee80211_scan_sta.c
++++ b/net80211/ieee80211_scan_sta.c
+@@ -490,12 +490,7 @@ check_rate(struct ieee80211vap *vap, con
+       okrate = badrate = fixedrate = 0;
+-      if (IEEE80211_IS_CHAN_HALF(se->se_chan))
+-              srs = &ic->ic_sup_half_rates;
+-      else if (IEEE80211_IS_CHAN_QUARTER(se->se_chan))
+-              srs = &ic->ic_sup_quarter_rates;
+-      else
+-              srs = &ic->ic_sup_rates[ieee80211_chan2mode(se->se_chan)];
++      srs = &ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, -1)];
+       nrs = se->se_rates[1];
+       rs = se->se_rates + 2;
+       fixedrate = IEEE80211_FIXED_RATE_NONE;
+--- a/net80211/ieee80211_output.c
++++ b/net80211/ieee80211_output.c
+@@ -1676,8 +1676,8 @@ ieee80211_send_probereq(struct ieee80211
+       frm = ieee80211_add_ssid(frm, ssid, ssidlen);
+       mode = ieee80211_chan2mode(ic->ic_curchan);
+-      frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[mode]);
+-      frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[mode]);
++      frm = ieee80211_add_rates(frm, &ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, mode)]);
++      frm = ieee80211_add_xrates(frm, &ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, mode)]);
+       if (optie != NULL) {
+               memcpy(frm, optie, optielen);
+--- a/net80211/ieee80211_proto.c
++++ b/net80211/ieee80211_proto.c
+@@ -404,7 +404,7 @@ ieee80211_fix_rate(struct ieee80211_node
+       error = 0;
+       okrate = badrate = fixedrate = 0;
+-      srs = &ic->ic_sup_rates[ieee80211_chan2mode(ni->ni_chan)];
++      srs = &ic->ic_sup_rates[ieee80211_chan2ratemode(ic->ic_curchan, -1)];
+       nrs = &ni->ni_rates;
+       fixedrate = IEEE80211_FIXED_RATE_NONE;
+       for (i = 0; i < nrs->rs_nrates;) {
+@@ -1401,6 +1401,7 @@ ieee80211_new_state(struct ieee80211vap 
+       IEEE80211_VAPS_UNLOCK_IRQ(ic);
+       return rc;
+ }
++EXPORT_SYMBOL(ieee80211_new_state);
+ static int
+ __ieee80211_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
+--- a/ath_rate/minstrel/minstrel.c
++++ b/ath_rate/minstrel/minstrel.c
+@@ -195,31 +195,7 @@ calc_usecs_unicast_packet(struct ath_sof
+                       return 0;
+               }
+-              /* XXX: Getting MAC/PHY level timings should be fixed for turbo
+-               * rates, and there is probably a way to get this from the
+-               * HAL... */
+-              switch (rt->info[rix].phy) {
+-              case IEEE80211_T_OFDM:
+-#if 0
+-                      t_slot = 9;
+-                      t_sifs = 16;
+-                      t_difs = 28;
+-                      /* fall through */
+-#endif
+-              case IEEE80211_T_TURBO:
+-                      t_slot = 9;
+-                      t_sifs = 8;
+-                      t_difs = 28;
+-                      break;
+-              case IEEE80211_T_DS:
+-                      /* Fall through to default */
+-              default:
+-                      /* pg. 205 ieee.802.11.pdf */
+-                      t_slot = 20;
+-                      t_difs = 50;
+-                      t_sifs = 10;
+-              }
+-
++              ath_get_timings(sc, &t_slot, &t_sifs, &t_difs);
+               if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+               (rt->info[rix].phy == IEEE80211_T_OFDM)) {
+                       if (ic->ic_protmode == IEEE80211_PROT_RTSCTS)
+--- a/ath_rate/sample/sample.c
++++ b/ath_rate/sample/sample.c
+@@ -172,26 +172,7 @@ calc_usecs_unicast_packet(struct ath_sof
+        * rates, and there is probably a way to get this from the
+        * hal...
+        */
+-      switch (rt->info[rix].phy) {
+-      case IEEE80211_T_OFDM:
+-              t_slot = 9;
+-              t_sifs = 16;
+-              t_difs = 28;
+-              /* fall through */
+-      case IEEE80211_T_TURBO:
+-              t_slot = 9;
+-              t_sifs = 8;
+-              t_difs = 28;
+-              break;
+-      case IEEE80211_T_DS:
+-              /* fall through to default */
+-      default:
+-              /* pg 205 ieee.802.11.pdf */
+-              t_slot = 20;
+-              t_difs = 50;
+-              t_sifs = 10;
+-      }
+-
++      ath_get_timings(sc, &t_slot, &t_sifs, &t_difs);
+       rts = cts = 0;
+       if ((ic->ic_flags & IEEE80211_F_USEPROT) &&
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -2133,7 +2133,7 @@ ieee80211_ioctl_setmode(struct net_devic
+               vap->iv_des_mode = mode;
+               if (IS_UP_AUTO(vap))
+-                      ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
++                      ieee80211_init(vap->iv_dev, 0);
+               retv = 0;
+       }
+@@ -4081,46 +4081,60 @@ ieee80211_ioctl_getchanlist(struct net_d
+       return 0;
+ }
++static int alreadyListed(struct ieee80211req_chaninfo *chans, u_int16_t mhz)
++{
++      int i;
++      for (i = 0; i < chans->ic_nchans; i++) {
++              if (chans->ic_chans[i].ic_freq == mhz)
++                      return 1;
++      }
++      return 0;
++}
++
+ static int
+ ieee80211_ioctl_getchaninfo(struct net_device *dev,
+-      struct iw_request_info *info, void *w, char *extra)
++                          struct iw_request_info *info, void *w, char *extra)
+ {
+       struct ieee80211vap *vap = dev->priv;
+       struct ieee80211com *ic = vap->iv_ic;
+-      struct ieee80211req_chaninfo chans;
++      struct ieee80211req_chaninfo *chans =
++          (struct ieee80211req_chaninfo *)extra;
++
+       u_int8_t reported[IEEE80211_CHAN_BYTES];        /* XXX stack usage? */
+       int i;
+-      memset(&chans, 0, sizeof(chans));
+-      memset(&reported, 0, sizeof(reported));
++      memset(chans, 0, sizeof(*chans));
++      memset(reported, 0, sizeof(reported));
+       for (i = 0; i < ic->ic_nchans; i++) {
+               const struct ieee80211_channel *c = &ic->ic_channels[i];
+               const struct ieee80211_channel *c1 = c;
+-              if (isclr(reported, c->ic_ieee)) {
++              if (!alreadyListed(chans, c->ic_freq)) {
+                       setbit(reported, c->ic_ieee);
+-                      /* pick turbo channel over non-turbo channel, and
+-                       * 11g channel over 11b channel */
+                       if (IEEE80211_IS_CHAN_A(c))
+-                              c1 = findchannel(ic, c->ic_ieee, IEEE80211_MODE_TURBO_A);
++                              c1 = findchannel(ic, c->ic_freq,
++                                               IEEE80211_MODE_TURBO_A);
+                       if (IEEE80211_IS_CHAN_ANYG(c))
+-                              c1 = findchannel(ic, c->ic_ieee, IEEE80211_MODE_TURBO_G);
++                              c1 = findchannel(ic, c->ic_freq,
++                                               IEEE80211_MODE_TURBO_G);
+                       else if (IEEE80211_IS_CHAN_B(c)) {
+-                              c1 = findchannel(ic, c->ic_ieee, IEEE80211_MODE_TURBO_G);
++                              c1 = findchannel(ic, c->ic_freq,
++                                               IEEE80211_MODE_TURBO_G);
+                               if (!c1)
+-                                      c1 = findchannel(ic, c->ic_ieee, IEEE80211_MODE_11G);
++                                      c1 = findchannel(ic, c->ic_freq,
++                                                       IEEE80211_MODE_11G);
+                       }
+                       if (c1)
+                               c = c1;
+-                      /* Copy the entire structure, whereas it used to just copy a few fields */
+-                      memcpy(&chans.ic_chans[chans.ic_nchans], c, sizeof(struct ieee80211_channel));
+-                      if (++chans.ic_nchans >= IEEE80211_CHAN_MAX)
++                      chans->ic_chans[chans->ic_nchans].ic_ieee = c->ic_ieee;
++                      chans->ic_chans[chans->ic_nchans].ic_freq = c->ic_freq;
++                      chans->ic_chans[chans->ic_nchans].ic_flags = c->ic_flags;
++                      if (++chans->ic_nchans >= IEEE80211_CHAN_MAX)
+                               break;
+               }
+       }
+-      memcpy(extra, &chans, sizeof(struct ieee80211req_chaninfo));
+       return 0;
+ }
+--- a/net80211/ieee80211_scan_ap.c
++++ b/net80211/ieee80211_scan_ap.c
+@@ -512,12 +512,13 @@ pick_channel(struct ieee80211_scan_state
+       int ss_last = ss->ss_last;
+       struct ieee80211_channel *best;
+       struct ap_state *as = ss->ss_priv;
+-      struct channel chans[ss_last]; /* actually ss_last-1 is required */
++      struct channel *chans; /* actually ss_last-1 is required */
+       struct channel *c = NULL;
+       struct pc_params params = { vap, ss, flags };
+       int benefit = 0;
+       int sta_assoc = 0;
++      chans = (struct channel *)kmalloc(ss_last*sizeof(struct channel),GFP_ATOMIC);
+       for (i = 0; i < ss_last; i++) {
+               chans[i].chan = ss->ss_chans[i];
+               chans[i].orig = i;
+@@ -601,6 +602,7 @@ pick_channel(struct ieee80211_scan_state
+                               "%s: best: channel %u rssi %d\n",
+                               __func__, i, as->as_maxrssi[i]);
+       }
++      kfree(chans);
+       return best;
+ }
+@@ -636,6 +638,7 @@ ap_end(struct ieee80211_scan_state *ss, 
+               res = 1; /* Do NOT restart scan */
+       } else {
+               struct ieee80211_scan_entry se;
++              int i;
+               /* XXX: notify all VAPs? */
+               /* if this is a dynamic turbo frequency , start with normal 
+                * mode first */
+@@ -650,6 +653,11 @@ ap_end(struct ieee80211_scan_state *ss, 
+                               return 0;
+                       }
+               }
++              for (i = (bestchan - &ic->ic_channels[0])/sizeof(*bestchan) + 1; i < ic->ic_nchans; i++) {
++                      if ((ic->ic_channels[i].ic_freq == bestchan->ic_freq) &&
++                              IEEE80211_IS_CHAN_ANYG(&ic->ic_channels[i]))
++                              bestchan = &ic->ic_channels[i];
++              }
+               memset(&se, 0, sizeof(se));
+               se.se_chan = bestchan;
+--- a/tools/wlanconfig.c
++++ b/tools/wlanconfig.c
+@@ -737,7 +737,7 @@ list_channels(const char *ifname, int al
+       if (get80211priv(ifname, IEEE80211_IOCTL_GETCHANINFO, &chans, sizeof(chans)) < 0)
+               errx(1, "unable to get channel information");
+       if (!allchans) {
+-              uint8_t active[32];
++              uint8_t active[IEEE80211_CHAN_BYTES];
+               if (get80211priv(ifname, IEEE80211_IOCTL_GETCHANLIST, &active, sizeof(active)) < 0)
+                       errx(1, "unable to get active channel list");
+--- a/net80211/ieee80211_scan.c
++++ b/net80211/ieee80211_scan.c
+@@ -1044,6 +1044,7 @@ ieee80211_scan_assoc_fail(struct ieee802
+               ss->ss_ops->scan_assoc_fail(ss, mac, reason);
+       }
+ }
++EXPORT_SYMBOL(ieee80211_scan_flush);
+ /*
+  * Iterate over the contents of the scan cache.
+--- a/ath/if_ath_hal_wrappers.h
++++ b/ath/if_ath_hal_wrappers.h
+@@ -111,6 +111,11 @@ static inline HAL_BOOL ath_hal_getregdom
+       return (ath_hal_getcapability(ah, HAL_CAP_REG_DMN, 0, destination) == HAL_OK);
+ }
++static inline HAL_BOOL ath_hal_setregdomain(struct ath_hal *ah, u_int32_t v)
++{
++      return (ath_hal_setcapability(ah, HAL_CAP_REG_DMN, 0, v, NULL));
++}
++
+ static inline HAL_BOOL ath_hal_gettkipmic(struct ath_hal *ah)
+ {
+       return (ath_hal_getcapability(ah, HAL_CAP_TKIP_MIC, 1, NULL) == HAL_OK);
diff --git a/package/madwifi/patches/422-confchange_reset.patch b/package/madwifi/patches/422-confchange_reset.patch
new file mode 100644 (file)
index 0000000..33040ae
--- /dev/null
@@ -0,0 +1,31 @@
+--- a/net80211/ieee80211_wireless.c
++++ b/net80211/ieee80211_wireless.c
+@@ -70,7 +70,8 @@
+       (((_dev)->flags & (IFF_RUNNING|IFF_UP)) == (IFF_RUNNING|IFF_UP))
+ #define       IS_UP_AUTO(_vap) \
+       (IS_UP((_vap)->iv_dev) && \
+-       (_vap)->iv_ic->ic_roaming == IEEE80211_ROAMING_AUTO)
++       (((_vap)->iv_opmode == IEEE80211_M_HOSTAP) || \
++       (_vap)->iv_ic->ic_roaming == IEEE80211_ROAMING_AUTO))
+ #define       RESCAN  1
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
+@@ -283,7 +284,7 @@ ieee80211_ioctl_siwencode(struct net_dev
+                               vap->iv_flags &= ~IEEE80211_F_DROPUNENC;
+               }
+       }
+-      if ((error == 0) && IS_UP(vap->iv_dev)) {
++      if ((error == 0) && IS_UP_AUTO(vap) && wepchange) {
+               /*
+                * Device is up and running; we must kick it to
+                * effect the change.  If we're enabling/disabling
+@@ -291,8 +292,7 @@ ieee80211_ioctl_siwencode(struct net_dev
+                * so the 802.11 state machine is reset.  Otherwise
+                * the key state should have been updated above.
+                */
+-              if (wepchange && IS_UP_AUTO(vap))
+-                      ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
++              ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
+       }
+ #ifdef ATH_SUPERG_XR
+       /* set the same params on the xr vap device if exists */
diff --git a/package/wprobe/Makefile b/package/wprobe/Makefile
new file mode 100644 (file)
index 0000000..fe96512
--- /dev/null
@@ -0,0 +1,123 @@
+# 
+# Copyright (C) 2008 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+PKG_NAME:=wprobe
+PKG_VERSION:=1
+
+PKG_BUILD_DEPENDS:=PACKAGE_wprobe-export:libipfix
+
+PKG_CONFIG_DEPENDS = \
+       CONFIG_PACKAGE_kmod-wprobe \
+       CONFIG_PACKAGE_wprobe-export \
+
+include $(INCLUDE_DIR)/package.mk
+
+define KernelPackage/wprobe
+  SUBMENU:=Network Support
+  TITLE:=Wireless driver probe infrastructure
+  FILES:= \
+       $(PKG_BUILD_DIR)/kernel/wprobe.$(LINUX_KMOD_SUFFIX)
+  AUTOLOAD:=$(call AutoLoad,01,wprobe)
+endef
+
+define KernelPackage/wprobe/description
+  A module that exports measurement data from wireless driver to user space
+endef
+
+define Package/wprobe-info
+  SECTION:=net
+  CATEGORY:=Network
+  DEPENDS:=+kmod-wprobe +libnl-tiny
+  TITLE:=Wireless measurement utility
+endef
+
+define Package/wprobe-info/description
+  wprobe-info uses the wprobe kernel module to query
+  wireless driver measurement data from an interface
+endef
+
+define Package/wprobe-export
+  SECTION:=net
+  CATEGORY:=Network
+  DEPENDS:=+kmod-wprobe +libnl-tiny
+  TITLE:=Wireless measurement data exporter
+endef
+
+define Package/wprobe-export/description
+  wprobe-export uses the wprobe kernel module to export
+  wireless driver measurement data via the IPFIX protocol
+endef
+
+define Build/Prepare
+       mkdir -p $(PKG_BUILD_DIR)
+       $(CP) src/* $(PKG_BUILD_DIR)/
+endef
+
+TARGET_CPPFLAGS := \
+       -I$(STAGING_DIR)/usr/include/libnl-tiny \
+       $(TARGET_CPPFLAGS)
+
+ifdef CONFIG_PACKAGE_kmod-wprobe
+  define Build/Compile/kmod
+       $(MAKE) -C $(LINUX_DIR) \
+               CROSS_COMPILE="$(TARGET_CROSS)" \
+               ARCH="$(LINUX_KARCH)" \
+               SUBDIRS="$(PKG_BUILD_DIR)/kernel" \
+               KERNELDIR=$(LINUX_DIR) \
+               CC="$(TARGET_CC)" \
+               EXTRA_CFLAGS="-I$(PKG_BUILD_DIR)/kernel" \
+               modules
+  endef
+endif
+
+define Build/Compile/lib
+       $(MAKE) -C $(PKG_BUILD_DIR)/user \
+               $(TARGET_CONFIGURE_OPTS) \
+               CFLAGS="$(TARGET_CFLAGS)" \
+               CPPFLAGS="$(TARGET_CPPFLAGS) -I$(PKG_BUILD_DIR)/kernel" \
+               LDFLAGS="$(TARGET_LDFLAGS)" \
+               LIBNL="-lnl-tiny"
+endef
+
+ifdef CONFIG_PACKAGE_wprobe-export
+  define Build/Compile/exporter
+       $(MAKE) -C $(PKG_BUILD_DIR)/exporter \
+               $(TARGET_CONFIGURE_OPTS) \
+               CFLAGS="$(TARGET_CFLAGS)" \
+               CPPFLAGS="$(TARGET_CPPFLAGS) -I$(PKG_BUILD_DIR)/kernel -I$(PKG_BUILD_DIR)/user" \
+               LDFLAGS="$(TARGET_LDFLAGS)" \
+               LIBS="$(PKG_BUILD_DIR)/user/libwprobe.a $(STAGING_DIR)/usr/lib/libipfix.a $(STAGING_DIR)/usr/lib/libmisc.a -lnl-tiny -lm"
+  endef
+endif
+
+define Build/Compile
+       $(Build/Compile/kmod)
+       $(Build/Compile/lib)
+       $(Build/Compile/exporter)
+endef
+
+define Build/InstallDev
+       $(INSTALL_DIR) $(1)/usr/include/wprobe
+       $(CP) $(PKG_BUILD_DIR)/kernel/linux $(1)/usr/include/wprobe
+endef
+
+define Package/wprobe-info/install
+       $(INSTALL_DIR) $(1)/sbin
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/user/wprobe-info $(1)/sbin/
+endef
+
+define Package/wprobe-export/install
+       $(INSTALL_DIR) $(1)/sbin $(1)/etc/init.d
+       $(INSTALL_BIN) ./files/wprobe.init $(1)/etc/init.d/
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/exporter/wprobe-export $(1)/sbin/
+endef
+
+$(eval $(call KernelPackage,wprobe))
+$(eval $(call BuildPackage,wprobe-info))
+$(eval $(call BuildPackage,wprobe-export))
diff --git a/package/wprobe/files/wprobe.init b/package/wprobe/files/wprobe.init
new file mode 100755 (executable)
index 0000000..3c62a03
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh /etc/rc.common
+START=90
+
+wprobe_ssd() {
+       local cfg="$1"; shift
+       local cmd="$1"; shift
+       start-stop-daemon "$cmd" -p "/var/run/wprobe-$cfg.pid" -b -x /sbin/wprobe-export -m -- "$@"
+}
+
+stop_wprobe() {
+       local cfg="$1"
+       [ -f "/var/run/wprobe-$cfg.pid" ] && wprobe_ssd "$cfg" -K
+       rm -f "/var/run/wprobe-$cfg.pid"
+}
+
+start_wprobe() {
+       local cfg="$1"
+       config_get ifname "$cfg" interface
+       config_get host "$cfg" host
+       config_get port "$cfg" port
+       config_get proto "$cfg" proto
+       case "$proto" in
+               sctp) proto="-s";;
+               tcp) proto="-t";;
+               udp) proto="-u";;
+               *) proto="-t";;
+       esac
+       [ -z "$ifname" -o -z "$host" ] && {
+               echo "wprobe-export: missing host or interface name in config $cfg"
+               return
+       }
+       wprobe_ssd "$cfg" -S "$proto" -i "$ifname" -c "$host" -p "${port:-4739}"
+}
+
+stop() {
+       for f in /var/run/wprobe-*.pid; do
+               CFG="${f%%.pid}"
+               CFG="${CFG##/var/run/wprobe-}"
+               stop_wprobe "$CFG"
+       done
+}
+
+start() {
+       config_load wprobe
+       config_foreach start_wprobe wprobe
+}
diff --git a/package/wprobe/src/exporter/Makefile b/package/wprobe/src/exporter/Makefile
new file mode 100644 (file)
index 0000000..c8e489a
--- /dev/null
@@ -0,0 +1,2 @@
+wprobe-export: wprobe-export.c
+       $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)
diff --git a/package/wprobe/src/exporter/wprobe-export.c b/package/wprobe/src/exporter/wprobe-export.c
new file mode 100644 (file)
index 0000000..e9aa3de
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+**     exporter.c - example exporter
+**
+**     Copyright Fraunhofer FOKUS
+**
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <ipfix_def.h>
+#include <ipfix_def_fokus.h>
+#include <ipfix_fields_fokus.h>
+
+#include <ipfix.h>
+#include <mlog.h>
+#include <wprobe.h>
+#include <stdbool.h>
+
+static ipfix_datarecord_t g_data  = { NULL, NULL, 0 };
+static int do_close = 0;
+
+struct wprobe_mapping {
+       int id;
+       bool counter;
+       float scale;
+       const char *wprobe_id;
+       struct wprobe_value *val;
+};
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(_array) (sizeof(_array) / sizeof((_array)[0]))
+#endif
+
+#define WMAP(_id, _name, ...) \
+       { \
+               .scale = 1.0f, \
+               .counter = false, \
+               .id = IPFIX_FT_WPROBE_##_id##_AVG, \
+               .wprobe_id = _name \
+               , ## __VA_ARGS__ \
+       }
+
+#define WMAP_COUNTER(_id, _name, ...) \
+       { \
+               .scale = 1.0f, \
+               .counter = true, \
+               .id = IPFIX_FT_WPROBE_##_id, \
+               .wprobe_id = _name \
+               , ## __VA_ARGS__ \
+       }
+
+
+#define WPROBE_OFFSET  2
+
+static struct wprobe_mapping map_globals[] = {
+       WMAP(NOISE, "noise"),
+       WMAP(PHY_BUSY, "phy_busy"),
+       WMAP(PHY_RX, "phy_rx"),
+       WMAP(PHY_TX, "phy_tx"),
+       WMAP_COUNTER(FRAMES, "frames"),
+       WMAP_COUNTER(PROBEREQ, "probereq"),
+};
+
+static struct wprobe_mapping map_perlink[] = {
+       WMAP(IEEE_TX_RATE, "tx_rate", .scale = 10.0f),
+       WMAP(IEEE_RX_RATE, "rx_rate", .scale = 10.0f),
+       WMAP(RSSI, "rssi"),
+       WMAP(SIGNAL, "signal"),
+       WMAP(RETRANSMIT_200, "retransmit_200"),
+       WMAP(RETRANSMIT_400, "retransmit_400"),
+       WMAP(RETRANSMIT_800, "retransmit_800"),
+       WMAP(RETRANSMIT_1600, "retransmit_1600"),
+};
+
+static unsigned char link_local[6];
+static char link_default[6];
+static LIST_HEAD(global_attr);
+static LIST_HEAD(link_attr);
+static LIST_HEAD(links);
+static int nfields = 0;
+
+#define FOKUS_USERID   12325
+
+static void
+match_template(struct wprobe_mapping *map, int n, struct list_head *list)
+{
+       struct wprobe_attribute *attr;
+       int i, j, last = -1;
+
+       list_for_each_entry(attr, list, list) {
+               for (i = 0; i < n; i++) {
+                       j = (last + 1 + i) % n;
+                       if (!strcmp(attr->name, map[j].wprobe_id))
+                               goto found;
+               }
+               continue;
+found:
+               last = j;
+               map[j].val = &attr->val;
+               memset(&attr->val, 0, sizeof(attr->val));
+               nfields++;
+       }
+}
+
+/* name: export_ipfix_get_template()
+ */
+static ipfix_template_t *
+prepare_template(ipfix_t *handle)
+{
+    ipfix_template_t *t = NULL;
+       int size = 3 * nfields + WPROBE_OFFSET;
+    int i;
+
+    if (ipfix_new_data_template( handle, &t, size) < 0) {
+        mlogf( 0, "ipfix_new_template() failed: %s\n", strerror(errno) ); 
+               exit(1);
+    }
+
+       ipfix_add_field(handle, t, 0, IPFIX_FT_SOURCEMACADDRESS, 6);
+       ipfix_add_field(handle, t, 0, IPFIX_FT_DESTINATIONMACADDRESS, 6);
+
+       g_data.lens = calloc(size, sizeof(g_data.lens[0]));
+       g_data.lens[0] = 6;
+       g_data.lens[1] = 6;
+       for (i = WPROBE_OFFSET; i < size; i++)
+               g_data.lens[i] = 4;
+
+       g_data.addrs = calloc(size, sizeof(g_data.addrs[0]));
+       g_data.addrs[0] = link_local;
+       g_data.maxfields = WPROBE_OFFSET;
+       return t;
+}
+
+static void
+add_template_fields(ipfix_t *handle, ipfix_template_t *t, struct wprobe_mapping *map, int n)
+{
+       int f = g_data.maxfields;
+       int i;
+
+    for (i = 0; i < n; i++) {
+               if (!map[i].val)
+                       continue;
+
+               if (map[i].counter)
+                       g_data.addrs[f++] = &map[i].val->U32;
+               else
+                       g_data.addrs[f++] = &map[i].val->avg;
+
+        if (ipfix_add_field( handle, t, FOKUS_USERID, map[i].id + 0, 4) < 0)
+            exit(1);
+
+               if (map[i].counter)
+                       continue;
+
+               g_data.addrs[f++] = &map[i].val->stdev;
+               g_data.addrs[f++] = &map[i].val->n;
+        if (ipfix_add_field( handle, t, FOKUS_USERID, map[i].id + 1, 4) < 0)
+            exit(1);
+        if (ipfix_add_field( handle, t, FOKUS_USERID, map[i].id + 2, 4) < 0)
+            exit(1);
+    }
+       g_data.maxfields = f;
+}
+
+static void
+wprobe_dump_data(ipfix_t *ipfixh, ipfix_template_t *ipfixt, const char *ifname, struct list_head *gl, struct list_head *ll, struct list_head *ls)
+{
+       struct wprobe_link *link;
+
+       wprobe_update_links(ifname, ls);
+       wprobe_request_data(ifname, gl, NULL, 2);
+       if (list_empty(ls)) {
+               g_data.addrs[1] = link_default;
+               ipfix_export_array(ipfixh, ipfixt, g_data.maxfields, g_data.addrs, g_data.lens);
+               ipfix_export_flush(ipfixh);
+       }
+       list_for_each_entry(link, ls, list) {
+               g_data.addrs[1] = link->addr;
+               wprobe_request_data(ifname, ll, link->addr, 2);
+               ipfix_export_array(ipfixh, ipfixt, g_data.maxfields, g_data.addrs, g_data.lens);
+               ipfix_export_flush(ipfixh);
+       }
+}
+
+int main ( int argc, char **argv )
+{
+    ipfix_template_t  *ipfixt = NULL;
+    ipfix_t *ipfixh = NULL;
+    int protocol = IPFIX_PROTO_TCP;
+    char *chost = NULL;
+       char *ifname = NULL;
+    int sourceid = 12345;
+    int port = IPFIX_PORTNO;
+    int verbose_level = 0;
+    int opt, i = 10;
+
+       while ((opt = getopt(argc, argv, "hi:c:p:vstu")) != EOF) {
+               switch (opt) {
+               case 'p':
+                       if ((port=atoi(optarg)) <0) {
+                               fprintf( stderr, "Invalid -p argument!\n" );
+                               exit(1);
+                       }
+                       break;
+               case 'i':
+                       ifname = optarg;
+                       break;
+               case 'c':
+                       chost = optarg;
+                       break;
+
+               case 's':
+                       protocol = IPFIX_PROTO_SCTP;
+                       break;
+
+               case 't':
+                       protocol = IPFIX_PROTO_TCP;
+                       break;
+
+               case 'u':
+                       protocol = IPFIX_PROTO_UDP;
+                       break;
+
+               case 'v':
+                       verbose_level ++;
+                       break;
+
+               case 'h':
+               default:
+                       fprintf(stderr, "usage: %s [-hstuv] -i <interface> -c <collector> [-p portno]\n"
+                                        "  -h               this help\n"
+                                        "  -i <interface>   wprobe interface\n"
+                                        "  -c <collector>   collector address\n"
+                                        "  -p <portno>      collector port number (default=%d)\n"
+                                        "  -s               send data via SCTP\n"
+                                        "  -t               send data via TCP (default)\n"
+                                        "  -u               send data via UDP\n"
+                                        "  -v               increase verbose level\n\n",
+                                        argv[0], IPFIX_PORTNO  );
+                       exit(1);
+               }
+       }
+
+       if (!ifname) {
+               fprintf(stderr, "No interface specified\n");
+               return -1;
+       }
+
+       if (!chost) {
+               fprintf(stderr, "No collector specified\n");
+               return -1;
+       }
+
+       if (wprobe_init() != 0) {
+               fprintf(stderr, "wprobe init failed\n");
+               return -1;
+       }
+
+       wprobe_dump_attributes(ifname, false, &global_attr, (char *) link_local);
+       wprobe_dump_attributes(ifname, true, &link_attr, NULL);
+       if (list_empty(&global_attr) && list_empty(&link_attr)) {
+               fprintf(stderr, "Cannot connect to wprobe on interface '%s'\n", ifname);
+               return -1;
+       }
+
+       match_template(map_globals, ARRAY_SIZE(map_globals), &global_attr);
+       match_template(map_perlink, ARRAY_SIZE(map_perlink), &link_attr);
+       if (nfields == 0) {
+               fprintf(stderr, "No usable attributes found\n");
+               return -1;
+       }
+
+    mlog_set_vlevel( verbose_level );
+    if (ipfix_init() < 0) {
+        fprintf( stderr, "cannot init ipfix module: %s\n", strerror(errno) );
+        exit(1);
+    }
+
+    ipfix_add_vendor_information_elements(ipfix_ft_fokus);
+    if (ipfix_open(&ipfixh, sourceid, IPFIX_VERSION) < 0) {
+        fprintf( stderr, "ipfix_open() failed: %s\n", strerror(errno) );
+        exit(1);
+    }
+
+    if (ipfix_add_collector( ipfixh, chost, port, protocol ) < 0) {
+        fprintf( stderr, "ipfix_add_collector(%s,%d) failed: %s\n", 
+                 chost, port, strerror(errno));
+        exit(1);
+    }
+
+       fprintf(stderr, "Local link address: %02x:%02x:%02x:%02x:%02x:%02x\n",
+               link_local[0], link_local[1], link_local[2],
+               link_local[3], link_local[4], link_local[5]);
+
+       ipfixt = prepare_template(ipfixh);
+       add_template_fields(ipfixh, ipfixt, map_globals, ARRAY_SIZE(map_globals));
+       add_template_fields(ipfixh, ipfixt, map_perlink, ARRAY_SIZE(map_perlink));
+
+       while (!do_close) {
+               usleep(100 * 1000);
+               wprobe_measure(ifname);
+
+               if (i-- > 0)
+                       continue;
+
+               i = 10;
+               wprobe_dump_data(ipfixh, ipfixt, ifname, &global_attr, &link_attr, &links);
+    }
+
+    ipfix_delete_template( ipfixh, ipfixt );
+    ipfix_close( ipfixh );
+    ipfix_cleanup();
+    exit(0);
+}
diff --git a/package/wprobe/src/exporter/wprobe-export.h b/package/wprobe/src/exporter/wprobe-export.h
new file mode 100644 (file)
index 0000000..89da9e8
--- /dev/null
@@ -0,0 +1,34 @@
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+/**
+ * struct wprobe_value: data structure for attribute values
+ * see kernel api netlink attributes for more information
+ */
+struct wprobe_value {
+       /* attribute value */
+       union data {
+               const char *STRING;
+               uint8_t U8;
+               uint16_t U16;
+               uint32_t U32;
+               uint64_t U64;
+               int8_t S8;
+               int16_t S16;
+               int32_t S32;
+               int64_t S64;
+       } data;
+       /* statistics */
+       int64_t s, ss;
+       double avg, stdev;
+       unsigned int n;
+       void *usedata;  /* Pointer to used data.something */
+};
+
+struct exporter_data {
+       int id; /* ipfix id */
+        int userid; /* focus or global */
+       int size; /* size in byte*/
+       struct wprobe_value val;
+};
diff --git a/package/wprobe/src/kernel/Makefile b/package/wprobe/src/kernel/Makefile
new file mode 100644 (file)
index 0000000..2a98753
--- /dev/null
@@ -0,0 +1,5 @@
+EXTRA_CFLAGS += -I.
+
+obj-m := wprobe.o wprobe-dummy.o
+
+wprobe-objs := wprobe-core.o
diff --git a/package/wprobe/src/kernel/linux/wprobe.h b/package/wprobe/src/kernel/linux/wprobe.h
new file mode 100644 (file)
index 0000000..f145195
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * wprobe.h: API for the wireless probe interface
+ * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.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.
+ *
+ * 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.
+ */
+
+#ifndef __WPROBE_H
+#define __WPROBE_H
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/if_ether.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <net/genetlink.h>
+#endif
+
+/** 
+ * enum wprobe_attr: netlink attribute list
+ *
+ * @WPROBE_ATTR_UNSPEC: unused
+ *
+ * @WPROBE_ATTR_INTERFACE: interface name to process query on (NLA_STRING)
+ * @WPROBE_ATTR_MAC: mac address (used for wireless links) (NLA_STRING)
+ * @WPROBE_ATTR_FLAGS: interface/link/attribute flags (see enum wprobe_flags) (NLA_U32)
+ * @WPROBE_ATTR_DURATION: sampling duration (in milliseconds) (NLA_MSECS)
+ *
+ * @WPROBE_ATTR_ID: attribute id (NLA_U32)
+ * @WPROBE_ATTR_NAME: attribute name (NLA_STRING)
+ * @WPROBE_ATTR_TYPE: attribute type (NLA_U8)
+ * @WPROBE_ATTR_SCALE: attribute scale factor (NLA_U32)
+ *
+ * attribute values:
+ *
+ * @WPROBE_VAL_STRING: string value (NLA_STRING)
+ * @WPROBE_VAL_S8: signed 8-bit integer (NLA_U8)
+ * @WPROBE_VAL_S16: signed 16-bit integer (NLA_U16)
+ * @WPROBE_VAL_S32: signed 32-bit integer (NLA_U32)
+ * @WPROBE_VAL_S64: signed 64-bit integer (NLA_U64)
+ * @WPROBE_VAL_U8: unsigned 8-bit integer (NLA_U8)
+ * @WPROBE_VAL_U16: unsigned 16-bit integer (NLA_U16)
+ * @WPROBE_VAL_U32: unsigned 32-bit integer (NLA_U32)
+ * @WPROBE_VAL_U64: unsigned 64-bit integer (NLA_U64)
+ *
+ * statistics:
+ * @WPROBE_VAL_SUM: sum of all samples
+ * @WPROBE_VAL_SUM_SQ: sum of all samples^2
+ * @WPROBE_VAL_SAMPLES: number of samples
+ *
+ * @WPROBE_ATTR_LAST: unused
+ */
+enum wprobe_attr {
+       WPROBE_ATTR_UNSPEC,
+       WPROBE_ATTR_INTERFACE,
+       WPROBE_ATTR_MAC,
+       WPROBE_ATTR_FLAGS,
+       WPROBE_ATTR_DURATION,
+       WPROBE_ATTR_SCALE,
+       /* end of query attributes */
+
+       /* response data */
+       WPROBE_ATTR_ID,
+       WPROBE_ATTR_NAME,
+       WPROBE_ATTR_TYPE,
+
+       /* value type attributes */
+       WPROBE_VAL_STRING,
+       WPROBE_VAL_S8,
+       WPROBE_VAL_S16,
+       WPROBE_VAL_S32,
+       WPROBE_VAL_S64,
+       WPROBE_VAL_U8,
+       WPROBE_VAL_U16,
+       WPROBE_VAL_U32,
+       WPROBE_VAL_U64,
+
+       /* aggregates for statistics */
+       WPROBE_VAL_SUM,
+       WPROBE_VAL_SUM_SQ,
+       WPROBE_VAL_SAMPLES,
+
+       WPROBE_ATTR_LAST
+};
+
+
+/**
+ * enum wprobe_cmd: netlink commands for interacting with wprobe
+ *
+ * @WPROBE_CMD_UNSPEC: unused
+ *
+ * @WPROBE_CMD_GET_LIST: get global/link property list
+ * @WPROBE_CMD_GET_INFO: get global/link properties
+ * @WPROBE_CMD_SET_FLAGS: set global/link flags
+ * @WPROBE_CMD_MEASURE: take a snapshot of the current data
+ * @WPROBE_CMD_GET_LINKS: get a list of links
+ *
+ * @WPROBE_CMD_LAST: unused
+ * 
+ * options for GET_INFO and SET_FLAGS:
+ *   - mac address set: per-link
+ *   - mac address unset: globalsa
+ */
+enum wprobe_cmd {
+       WPROBE_CMD_UNSPEC,
+       WPROBE_CMD_GET_LIST,
+       WPROBE_CMD_GET_INFO,
+       WPROBE_CMD_SET_FLAGS,
+       WPROBE_CMD_MEASURE,
+       WPROBE_CMD_GET_LINKS,
+       WPROBE_CMD_LAST
+};
+
+/**
+ * enum wprobe_flags: flags for wprobe links and items
+ * @WPROBE_F_KEEPSTAT: keep statistics for this link/device
+ * @WPROBE_F_RESET: reset statistics now (used only in WPROBE_CMD_SET_LINK)
+ * @WPROBE_F_NEWDATA: used to indicate that a value has been updated
+ */
+enum wprobe_flags {
+       WPROBE_F_KEEPSTAT = (1 << 0),
+       WPROBE_F_RESET = (1 << 1),
+       WPROBE_F_NEWDATA = (1 << 2),
+};
+
+#ifdef __KERNEL__
+
+struct wprobe_link;
+struct wprobe_item;
+struct wprobe_source;
+
+/**
+ * struct wprobe_link - data structure describing a wireless link
+ * @iface: pointer to the wprobe_iface that this link belongs to
+ * @addr: BSSID of the remote link partner
+ * @flags: link flags (see wprobe_flags)
+ * @priv: user pointer
+ *
+ * @list: for internal use
+ * @val: for internal use
+ */
+struct wprobe_link {
+       struct list_head list;
+       struct wprobe_iface *iface;
+       char addr[ETH_ALEN];
+       u32 flags;
+       void *priv;
+       void *val;
+};
+
+/** 
+ * struct wprobe_item - data structure describing the format of wprobe_link::data or wprobe_iface::data
+ * @name: name of the field
+ * @type: data type of this field
+ * @flags: measurement item flags (see wprobe_flags)
+ */
+struct wprobe_item {
+       const char *name;
+       enum wprobe_attr type;
+       u32 flags;
+};
+
+struct wprobe_value {
+       bool pending;
+       union {
+               /*
+                * the following are kept uppercase to allow
+                * for automated checking against WPROBE_VAL_*
+                * via BUG_ON()
+                */
+               const char *STRING;
+               u8 U8;
+               u16 U16;
+               u32 U32;
+               u64 U64;
+               s8 S8;
+               s16 S16;
+               s32 S32;
+               s64 S64;
+       };
+       s64 s, ss;
+       unsigned int n;
+
+       /* timestamps */
+       u64 first, last;
+};
+
+/**
+ * struct wprobe_source - data structure describing a wireless interface
+ *
+ * @name: name of the interface
+ * @addr: local mac address of the interface
+ * @links: list of wireless links to poll
+ * @link_items: description of the per-link data structure
+ * @n_link_items: number of link description items
+ * @global_items: description of the per-interface data structure
+ * @n_global_items: number of per-interface description items
+ * @sync_data: callback allowing the driver to prepare data for the wprobe poll
+ *
+ * @list: head for the list of interfaces
+ * @priv: user pointer
+ * @lock: spinlock protecting value data access
+ * @val: internal use
+ * @query_val: internal use
+ *
+ * if sync_data is NULL, wprobe assumes that it can access the data structure
+ * at any time (in atomic context). if sync_data returns a negative error code,
+ * the poll request will not be handled for the given link
+ */
+struct wprobe_iface {
+       /* to be filled in by wprobe source drivers */
+       const char *name;
+       const char *addr;
+       const struct wprobe_item *link_items;
+       int n_link_items;
+       const struct wprobe_item *global_items;
+       int n_global_items;
+
+       int (*sync_data)(struct wprobe_iface *dev, struct wprobe_link *l, struct wprobe_value *val, bool measure);
+       void *priv;
+
+       /* handled by the wprobe core */
+       struct list_head list;
+       struct list_head links;
+       spinlock_t lock;
+       void *val;
+       void *query_val;
+};
+
+#define WPROBE_FILL_BEGIN(_ptr, _list) do {                    \
+       struct wprobe_value *__val = (_ptr);                    \
+       const struct wprobe_item *__item = _list;               \
+       u64 __msecs = jiffies_to_msecs(jiffies)
+
+#define WPROBE_SET(_idx, _type, _value)                                \
+       if (__item[_idx].type != WPROBE_VAL_##_type) {          \
+               printk("ERROR: invalid data type at %s:%d\n", __FILE__, __LINE__); \
+               break;                                          \
+       }                                                       \
+       __val[_idx].pending = true;                             \
+       __val[_idx]._type = _value;                             \
+       if (!__val[_idx].first)                                 \
+               __val[_idx].first = __msecs;                    \
+       __val[_idx].first = __msecs
+
+#define WPROBE_FILL_END()                                      \
+} while(0)
+
+/**
+ * wprobe_add_iface: register an interface with the wireless probe subsystem
+ * @dev: wprobe_iface structure describing the interface
+ */
+extern int __weak wprobe_add_iface(struct wprobe_iface *dev);
+
+/**
+ * wprobe_remove_iface: deregister an interface from the wireless probe subsystem
+ * @dev: wprobe_iface structure describing the interface
+ */
+extern void __weak wprobe_remove_iface(struct wprobe_iface *dev);
+
+/**
+ * wprobe_add_link: register a new wireless link
+ * @dev: wprobe_iface structure describing the interface
+ * @l: storage space for the wprobe_link structure
+ * @addr: mac address of the new link
+ *
+ * the entire wprobe_link structure is overwritten by this function call
+ */
+extern int __weak wprobe_add_link(struct wprobe_iface *dev, struct wprobe_link *l, const char *addr);
+
+/**
+ * wprobe_remove_link: deregister a previously registered wireless link
+ * @dev: wprobe_iface structure describing the interface
+ * @l: wprobe_link data structure
+ */
+extern void __weak wprobe_remove_link(struct wprobe_iface *dev, struct wprobe_link *l);
+
+/**
+ * wprobe_update_stats: update statistics after sampling values
+ * @dev: wprobe_iface structure describing the interface
+ * @l: wprobe_link data structure
+ *
+ * if l == NULL, then the stats for globals are updated
+ */
+extern void __weak wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l);
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/package/wprobe/src/kernel/wprobe-core.c b/package/wprobe/src/kernel/wprobe-core.c
new file mode 100644 (file)
index 0000000..798cd7d
--- /dev/null
@@ -0,0 +1,669 @@
+/*
+ * wprobe-core.c: Wireless probe interface core
+ * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.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.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/rcupdate.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
+#include <linux/rculist.h>
+#else
+#include <linux/list.h>
+#endif
+#include <linux/skbuff.h>
+#include <linux/wprobe.h>
+#include <linux/math64.h>
+
+#define static
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
+#define list_for_each_rcu __list_for_each_rcu
+#endif
+
+static struct list_head wprobe_if;
+static spinlock_t wprobe_lock;
+
+static struct genl_family wprobe_fam = {
+       .id = GENL_ID_GENERATE,
+       .name = "wprobe",
+       .hdrsize = 0,
+       .version = 1,
+       /* only the first set of attributes is used for queries */
+       .maxattr = WPROBE_ATTR_ID,
+};
+
+static void wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l);
+
+int
+wprobe_add_link(struct wprobe_iface *s, struct wprobe_link *l, const char *addr)
+{
+       unsigned long flags;
+
+       INIT_LIST_HEAD(&l->list);
+       l->val = kzalloc(sizeof(struct wprobe_value) * s->n_link_items, GFP_ATOMIC);
+       if (!l->val)
+               return -ENOMEM;
+
+       l->iface = s;
+       memcpy(&l->addr, addr, ETH_ALEN);
+       spin_lock_irqsave(&wprobe_lock, flags);
+       list_add_tail_rcu(&l->list, &s->links);
+       spin_unlock_irqrestore(&wprobe_lock, flags);
+
+       return 0;
+}
+EXPORT_SYMBOL(wprobe_add_link);
+
+void
+wprobe_remove_link(struct wprobe_iface *s, struct wprobe_link *l)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&wprobe_lock, flags);
+       list_del_rcu(&l->list);
+       spin_unlock_irqrestore(&wprobe_lock, flags);
+       synchronize_rcu();
+       kfree(l->val);
+}
+EXPORT_SYMBOL(wprobe_remove_link);
+
+int
+wprobe_add_iface(struct wprobe_iface *s)
+{
+       unsigned long flags;
+       int vsize;
+
+       /* reset only wprobe private area */
+       memset(&s->list, 0, sizeof(struct wprobe_iface) - offsetof(struct wprobe_iface, list));
+
+       BUG_ON(!s->name);
+       INIT_LIST_HEAD(&s->list);
+       INIT_LIST_HEAD(&s->links);
+
+       vsize = max(s->n_link_items, s->n_global_items);
+       s->val = kzalloc(sizeof(struct wprobe_value) * vsize, GFP_ATOMIC);
+       if (!s->val)
+               goto error;
+
+       s->query_val = kzalloc(sizeof(struct wprobe_value) * vsize, GFP_ATOMIC);
+       if (!s->query_val)
+               goto error;
+
+       spin_lock_irqsave(&wprobe_lock, flags);
+       list_add_rcu(&s->list, &wprobe_if);
+       spin_unlock_irqrestore(&wprobe_lock, flags);
+
+       return 0;
+
+error:
+       if (s->val)
+               kfree(s->val);
+       return -ENOMEM;
+}
+EXPORT_SYMBOL(wprobe_add_iface);
+
+void
+wprobe_remove_iface(struct wprobe_iface *s)
+{
+       unsigned long flags;
+
+       BUG_ON(!list_empty(&s->links));
+
+       spin_lock_irqsave(&wprobe_lock, flags);
+       list_del_rcu(&s->list);
+       spin_unlock_irqrestore(&wprobe_lock, flags);
+
+       /* wait for all queries to finish before freeing the
+        * temporary value storage buffer */
+       synchronize_rcu();
+
+       kfree(s->val);
+       kfree(s->query_val);
+}
+EXPORT_SYMBOL(wprobe_remove_iface);
+
+static struct wprobe_iface *
+wprobe_get_dev(struct nlattr *attr)
+{
+       struct wprobe_iface *dev = NULL;
+       struct wprobe_iface *p;
+       const char *name;
+       int i = 0;
+
+       if (!attr)
+               return NULL;
+
+       name = nla_data(attr);
+       list_for_each_entry_rcu(p, &wprobe_if, list) {
+               i++;
+               if (strcmp(name, p->name) != 0)
+                       continue;
+
+               dev = p;
+               break;
+       }
+
+       return dev;
+}
+
+int
+wprobe_sync_data(struct wprobe_iface *dev, struct wprobe_link *l, bool query)
+{
+       struct wprobe_value *val;
+       unsigned long flags;
+       int n, err;
+
+       if (l) {
+               n = dev->n_link_items;
+               val = l->val;
+       } else {
+               n = dev->n_global_items;
+               val = dev->val;
+       }
+
+       spin_lock_irqsave(&dev->lock, flags);
+       err = dev->sync_data(dev, l, val, !query);
+       if (err)
+               goto done;
+
+       if (query)
+               memcpy(dev->query_val, val, sizeof(struct wprobe_value) * n);
+
+       wprobe_update_stats(dev, l);
+done:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return 0;
+}
+EXPORT_SYMBOL(wprobe_sync_data);
+
+void
+wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l)
+{
+       const struct wprobe_item *item;
+       struct wprobe_value *val;
+       int i, n;
+
+       if (l) {
+               n = dev->n_link_items;
+               item = dev->link_items;
+               val = l->val;
+       } else {
+               n = dev->n_global_items;
+               item = dev->global_items;
+               val = dev->val;
+       }
+
+       /* process statistics */
+       for (i = 0; i < n; i++) {
+               s64 v;
+
+               if (!val[i].pending)
+                       continue;
+
+               val[i].n++;
+
+               switch(item[i].type) {
+               case WPROBE_VAL_S8:
+                       v = val[i].S8;
+                       break;
+               case WPROBE_VAL_S16:
+                       v = val[i].S16;
+                       break;
+               case WPROBE_VAL_S32:
+                       v = val[i].S32;
+                       break;
+               case WPROBE_VAL_S64:
+                       v = val[i].S64;
+                       break;
+               case WPROBE_VAL_U8:
+                       v = val[i].U8;
+                       break;
+               case WPROBE_VAL_U16:
+                       v = val[i].U16;
+                       break;
+               case WPROBE_VAL_U32:
+                       v = val[i].U32;
+                       break;
+               case WPROBE_VAL_U64:
+                       v = val[i].U64;
+                       break;
+               default:
+                       continue;
+               }
+
+               val[i].s += v;
+               val[i].ss += v * v;
+               val[i].pending = false;
+       }
+}
+EXPORT_SYMBOL(wprobe_update_stats);
+
+static const struct nla_policy wprobe_policy[WPROBE_ATTR_ID+1] = {
+       [WPROBE_ATTR_INTERFACE] = { .type = NLA_NUL_STRING },
+       [WPROBE_ATTR_MAC] = { .type = NLA_STRING },
+       [WPROBE_ATTR_DURATION] = { .type = NLA_MSECS },
+       [WPROBE_ATTR_FLAGS] = { .type = NLA_U32 },
+       [WPROBE_ATTR_SCALE] = { .type = NLA_U32 },
+};
+
+static bool
+wprobe_check_ptr(struct list_head *list, struct list_head *ptr)
+{
+       struct list_head *p;
+
+       list_for_each_rcu(p, list) {
+               if (ptr == p)
+                       return true;
+       }
+       return false;
+}
+
+static bool
+wprobe_send_item_value(struct sk_buff *msg, struct netlink_callback *cb,
+                       struct wprobe_iface *dev, struct wprobe_link *l,
+                       const struct wprobe_item *item,
+                       int i, u32 flags)
+{
+       struct genlmsghdr *hdr;
+       struct wprobe_value *val = dev->query_val;
+       u64 time = val[i].last - val[i].first;
+
+       hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+                       &wprobe_fam, NLM_F_MULTI, WPROBE_CMD_GET_INFO);
+
+       NLA_PUT_U32(msg, WPROBE_ATTR_ID, i);
+       NLA_PUT_U32(msg, WPROBE_ATTR_FLAGS, flags);
+       NLA_PUT_U8(msg, WPROBE_ATTR_TYPE, item[i].type);
+       NLA_PUT_U64(msg, WPROBE_ATTR_DURATION, time);
+
+       switch(item[i].type) {
+       case WPROBE_VAL_S8:
+       case WPROBE_VAL_U8:
+               NLA_PUT_U8(msg, item[i].type, val[i].U8);
+               break;
+       case WPROBE_VAL_S16:
+       case WPROBE_VAL_U16:
+               NLA_PUT_U16(msg, item[i].type, val[i].U16);
+               break;
+       case WPROBE_VAL_S32:
+       case WPROBE_VAL_U32:
+               NLA_PUT_U32(msg, item[i].type, val[i].U32);
+               break;
+       case WPROBE_VAL_S64:
+       case WPROBE_VAL_U64:
+               NLA_PUT_U64(msg, item[i].type, val[i].U64);
+               break;
+       case WPROBE_VAL_STRING:
+               if (val[i].STRING)
+                       NLA_PUT_STRING(msg, item[i].type, val[i].STRING);
+               else
+                       NLA_PUT_STRING(msg, item[i].type, "");
+               /* bypass avg/stdev */
+               goto done;
+       default:
+               /* skip unknown values */
+               goto done;
+       }
+       if (item[i].flags & WPROBE_F_KEEPSTAT) {
+               NLA_PUT_U64(msg, WPROBE_VAL_SUM, val[i].s);
+               NLA_PUT_U64(msg, WPROBE_VAL_SUM_SQ, val[i].ss);
+               NLA_PUT_U32(msg, WPROBE_VAL_SAMPLES, (u32) val[i].n);
+       }
+done:
+       genlmsg_end(msg, hdr);
+       return true;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return false;
+}
+
+static bool
+wprobe_send_item_info(struct sk_buff *msg, struct netlink_callback *cb,
+                       struct wprobe_iface *dev,
+                       const struct wprobe_item *item, int i)
+{
+       struct genlmsghdr *hdr;
+
+       hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+                       &wprobe_fam, NLM_F_MULTI, WPROBE_CMD_GET_LIST);
+
+       if ((i == 0) && (dev->addr != NULL))
+               NLA_PUT(msg, WPROBE_ATTR_MAC, 6, dev->addr);
+       NLA_PUT_U32(msg, WPROBE_ATTR_ID, (u32) i);
+       NLA_PUT_STRING(msg, WPROBE_ATTR_NAME, item[i].name);
+       NLA_PUT_U8(msg, WPROBE_ATTR_TYPE, item[i].type);
+       NLA_PUT_U32(msg, WPROBE_ATTR_FLAGS, item[i].flags);
+       genlmsg_end(msg, hdr);
+       return true;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return false;
+}
+
+
+static struct wprobe_link *
+wprobe_find_link(struct wprobe_iface *dev, const char *mac)
+{
+       struct wprobe_link *l;
+
+       list_for_each_entry_rcu(l, &dev->links, list) {
+               if (!memcmp(l->addr, mac, 6))
+                       return l;
+       }
+       return NULL;
+}
+
+static bool
+wprobe_dump_link(struct sk_buff *msg, struct wprobe_link *l, struct netlink_callback *cb)
+{
+       struct genlmsghdr *hdr;
+
+       hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq,
+                       &wprobe_fam, NLM_F_MULTI, WPROBE_CMD_GET_LINKS);
+       if (!hdr)
+               return false;
+
+       NLA_PUT(msg, WPROBE_ATTR_MAC, 6, l->addr);
+       genlmsg_end(msg, hdr);
+       return true;
+
+nla_put_failure:
+       genlmsg_cancel(msg, hdr);
+       return false;
+}
+
+static int
+wprobe_dump_links(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct wprobe_iface *dev = (struct wprobe_iface *)cb->args[0];
+       struct wprobe_link *l;
+       int err = 0;
+       int i = 0;
+
+       if (!dev) {
+               err = nlmsg_parse(cb->nlh, GENL_HDRLEN + wprobe_fam.hdrsize,
+                               wprobe_fam.attrbuf, wprobe_fam.maxattr, wprobe_policy);
+               if (err)
+                       goto done;
+
+               dev = wprobe_get_dev(wprobe_fam.attrbuf[WPROBE_ATTR_INTERFACE]);
+               if (!dev) {
+                       err = -ENODEV;
+                       goto done;
+               }
+
+               cb->args[0] = (long) dev;
+       } else {
+               if (!wprobe_check_ptr(&wprobe_if, &dev->list)) {
+                       err = -ENODEV;
+                       goto done;
+               }
+       }
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(l, &dev->links, list) {
+               if (i < cb->args[1])
+                       continue;
+
+               if (unlikely(!wprobe_dump_link(skb, l, cb)))
+                       break;
+
+               i++;
+       }
+       cb->args[1] = i;
+       rcu_read_unlock();
+       err = skb->len;
+done:
+       return err;
+}
+static void
+wprobe_scale_stats(const struct wprobe_item *item, struct wprobe_value *val, int n, u32 flags)
+{
+       u32 scale = 0;
+       int i;
+
+       for (i = 0; i < n; i++) {
+               if (!(item[i].flags & WPROBE_F_KEEPSTAT))
+                       continue;
+
+               /* reset statistics, if requested */
+               if (flags & WPROBE_F_RESET)
+                       scale = val[i].n;
+               else if (wprobe_fam.attrbuf[WPROBE_ATTR_SCALE])
+                       scale = nla_get_u32(wprobe_fam.attrbuf[WPROBE_ATTR_SCALE]);
+
+               if ((scale > 0) && (val[i].n > scale)) {
+                       val[i].s = div_s64(val[i].s, scale);
+                       val[i].ss = div_s64(val[i].ss, scale);
+                       val[i].n = val[i].n / scale + 1;
+               }
+       }
+}
+
+#define WPROBE_F_LINK (1 << 31) /* for internal use */
+static int
+wprobe_dump_info(struct sk_buff *skb, struct netlink_callback *cb)
+{
+       struct wprobe_iface *dev = (struct wprobe_iface *)cb->args[0];
+       struct wprobe_link *l = (struct wprobe_link *)cb->args[1];
+       struct wprobe_value *val;
+       const struct wprobe_item *item;
+       struct genlmsghdr *hdr;
+       unsigned long flags;
+       int cmd, n, i = cb->args[3];
+       u32     vflags = cb->args[2];
+       int err = 0;
+
+       hdr = (struct genlmsghdr *)nlmsg_data(cb->nlh);
+       cmd = hdr->cmd;
+
+       /* since the attribute value list might be too big for a single netlink
+        * message, the device, link and offset get stored in the netlink callback.
+        * if this is the first request, we need to do the full lookup for the device.
+        *
+        * access to the device and link structure is synchronized through rcu.
+        */
+       rcu_read_lock();
+       if (!dev) {
+               err = nlmsg_parse(cb->nlh, GENL_HDRLEN + wprobe_fam.hdrsize,
+                               wprobe_fam.attrbuf, wprobe_fam.maxattr, wprobe_policy);
+               if (err)
+                       goto done;
+
+               err = -ENOENT;
+               dev = wprobe_get_dev(wprobe_fam.attrbuf[WPROBE_ATTR_INTERFACE]);
+               if (!dev)
+                       goto done;
+
+               if (cmd == WPROBE_CMD_GET_INFO) {
+                       if (wprobe_fam.attrbuf[WPROBE_ATTR_MAC]) {
+                               l = wprobe_find_link(dev, nla_data(wprobe_fam.attrbuf[WPROBE_ATTR_MAC]));
+                               if (!l)
+                                       goto done;
+
+                               vflags = l->flags;
+                       }
+
+                       if (l) {
+                               item = dev->link_items;
+                               n = dev->n_link_items;
+                               val = l->val;
+                       } else {
+                               item = dev->global_items;
+                               n = dev->n_global_items;
+                               val = dev->val;
+                       }
+
+                       /* sync data and move to temp storage for the query */
+                       spin_lock_irqsave(&dev->lock, flags);
+                       err = wprobe_sync_data(dev, l, true);
+                       if (!err)
+                               memcpy(dev->query_val, val, n * sizeof(struct wprobe_value));
+                       wprobe_scale_stats(item, val, n, flags);
+                       spin_unlock_irqrestore(&dev->lock, flags);
+
+                       if (err)
+                               goto done;
+               }
+
+               if (wprobe_fam.attrbuf[WPROBE_ATTR_FLAGS])
+                       vflags |= nla_get_u32(wprobe_fam.attrbuf[WPROBE_ATTR_FLAGS]);
+
+               if (wprobe_fam.attrbuf[WPROBE_ATTR_MAC])
+                       vflags |= WPROBE_F_LINK;
+
+               cb->args[0] = (long) dev;
+               cb->args[1] = (long) l;
+               cb->args[2] = vflags;
+               cb->args[3] = 0;
+       } else {
+               /* when pulling pointers from the callback, validate them
+                * against the list using rcu to make sure that we won't
+                * dereference pointers to free'd memory after the last
+                * grace period */
+               err = -ENOENT;
+               if (!wprobe_check_ptr(&wprobe_if, &dev->list))
+                       goto done;
+
+               if (l && !wprobe_check_ptr(&dev->links, &l->list))
+                       goto done;
+       }
+
+       if (vflags & WPROBE_F_LINK) {
+               item = dev->link_items;
+               n = dev->n_link_items;
+       } else {
+               item = dev->global_items;
+               n = dev->n_global_items;
+       }
+
+       err = 0;
+       switch(cmd) {
+       case WPROBE_CMD_GET_INFO:
+               while (i < n) {
+                       if (!wprobe_send_item_value(skb, cb, dev, l, item, i, vflags))
+                               break;
+                       i++;
+               }
+               break;
+       case WPROBE_CMD_GET_LIST:
+               while (i < n) {
+                       if (!wprobe_send_item_info(skb, cb, dev, item, i))
+                               break;
+                       i++;
+               }
+               break;
+       default:
+               err = -EINVAL;
+               goto done;
+       }
+       cb->args[3] = i;
+       err = skb->len;
+
+done:
+       rcu_read_unlock();
+       return err;
+}
+#undef WPROBE_F_LINK
+
+static int
+wprobe_measure(struct sk_buff *skb, struct genl_info *info)
+{
+       struct wprobe_iface *dev;
+       struct wprobe_link *l = NULL;
+       int err = -ENOENT;
+
+       rcu_read_lock();
+       dev = wprobe_get_dev(info->attrs[WPROBE_ATTR_INTERFACE]);
+       if (!dev)
+               goto done;
+
+       if (info->attrs[WPROBE_ATTR_MAC]) {
+               l = wprobe_find_link(dev, nla_data(wprobe_fam.attrbuf[WPROBE_ATTR_MAC]));
+               if (!l)
+                       goto done;
+       }
+
+       err = wprobe_sync_data(dev, l, false);
+
+done:
+       rcu_read_unlock();
+       return err;
+}
+
+static struct genl_ops wprobe_ops[] = {
+       {
+               .cmd = WPROBE_CMD_GET_INFO,
+               .dumpit = wprobe_dump_info,
+               .policy = wprobe_policy,
+       },
+       {
+               .cmd = WPROBE_CMD_GET_LIST,
+               .dumpit = wprobe_dump_info,
+               .policy = wprobe_policy,
+       },
+       {
+               .cmd = WPROBE_CMD_MEASURE,
+               .doit = wprobe_measure,
+               .policy = wprobe_policy,
+       },
+       {
+               .cmd = WPROBE_CMD_GET_LINKS,
+               .dumpit = wprobe_dump_links,
+               .policy = wprobe_policy,
+       }
+};
+
+static void __exit
+wprobe_exit(void)
+{
+       BUG_ON(!list_empty(&wprobe_if));
+       genl_unregister_family(&wprobe_fam);
+}
+
+
+static int __init
+wprobe_init(void)
+{
+       int i, err;
+
+       spin_lock_init(&wprobe_lock);
+       INIT_LIST_HEAD(&wprobe_if);
+
+       err = genl_register_family(&wprobe_fam);
+       if (err)
+               return err;
+
+       for (i = 0; i < ARRAY_SIZE(wprobe_ops); i++) {
+               err = genl_register_ops(&wprobe_fam, &wprobe_ops[i]);
+               if (err)
+                       goto error;
+       }
+
+       return 0;
+
+error:
+       genl_unregister_family(&wprobe_fam);
+       return err;
+}
+
+module_init(wprobe_init);
+module_exit(wprobe_exit);
+MODULE_LICENSE("GPL");
+
diff --git a/package/wprobe/src/kernel/wprobe-dummy.c b/package/wprobe/src/kernel/wprobe-dummy.c
new file mode 100644 (file)
index 0000000..4231223
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * wprobe-core.c: Wireless probe interface dummy driver
+ * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.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.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/wprobe.h>
+
+static const char local_addr[] = "\x00\x13\x37\xba\xbe\x00";
+
+enum dummy_global_values {
+       DUMMY_GLOBAL_MEDIUM_BUSY
+};
+enum dummy_link_values {
+       DUMMY_LINK_SNR
+};
+
+struct wprobe_item dummy_perlink[] = {
+       [DUMMY_LINK_SNR] = {
+               .name = "snr",
+               .type = WPROBE_VAL_U8,
+               .flags = WPROBE_F_KEEPSTAT,
+       },
+};
+
+struct wprobe_item dummy_globals[] = {
+       [DUMMY_GLOBAL_MEDIUM_BUSY] = {
+               .name = "medium_busy",
+               .type = WPROBE_VAL_U8,
+               .flags = WPROBE_F_KEEPSTAT,
+       }
+};
+
+int dummy_sync(struct wprobe_iface *dev, struct wprobe_link *l, struct wprobe_value *val, bool measure)
+{
+       u8 intval = 0;
+
+       get_random_bytes(&intval, 1);
+       if (l) {
+               WPROBE_FILL_BEGIN(val, dummy_perlink);
+               WPROBE_SET(DUMMY_LINK_SNR, U8, (intval % 40));
+               WPROBE_FILL_END();
+       } else {
+               WPROBE_FILL_BEGIN(val, dummy_globals);
+               WPROBE_SET(DUMMY_GLOBAL_MEDIUM_BUSY, U8, (intval % 100));
+               WPROBE_FILL_END();
+       }
+       return 0;
+}
+
+static struct wprobe_iface dummy_dev = {
+       .name = "dummy",
+       .addr = local_addr,
+       .link_items = dummy_perlink,
+       .n_link_items = ARRAY_SIZE(dummy_perlink),
+       .global_items = dummy_globals,
+       .n_global_items = ARRAY_SIZE(dummy_globals),
+       .sync_data = dummy_sync,
+};
+
+static struct wprobe_link dummy_link;
+
+static int __init
+wprobe_dummy_init(void)
+{
+       wprobe_add_iface(&dummy_dev);
+       wprobe_add_link(&dummy_dev, &dummy_link, "\x00\x13\x37\xda\xda\x00");
+       return 0;
+}
+
+static void __exit
+wprobe_dummy_exit(void)
+{
+       wprobe_remove_link(&dummy_dev, &dummy_link);
+       wprobe_remove_iface(&dummy_dev);
+}
+
+module_init(wprobe_dummy_init);
+module_exit(wprobe_dummy_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/package/wprobe/src/user/Makefile b/package/wprobe/src/user/Makefile
new file mode 100644 (file)
index 0000000..e10dfc0
--- /dev/null
@@ -0,0 +1,21 @@
+CFLAGS = -O2
+CPPFLAGS ?= -I../kernel
+WFLAGS = -Wall -Werror
+LDFLAGS =
+
+LIBNL = -lnl
+LIBM = -lm
+LIBS = $(LIBNL) $(LIBM)
+
+all: libwprobe.a wprobe-info
+
+libwprobe.a: wprobe.o
+       rm -f $@
+       $(AR) rcu $@ $^
+       $(RANLIB) $@
+
+%.o: %.c
+       $(CC) $(WFLAGS) -c -o $@ $(CPPFLAGS) $(CFLAGS) $<
+
+wprobe-info: wprobe-info.o wprobe.o
+       $(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
diff --git a/package/wprobe/src/user/list.h b/package/wprobe/src/user/list.h
new file mode 100644 (file)
index 0000000..2959a06
--- /dev/null
@@ -0,0 +1,601 @@
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#include <stddef.h>
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr:       the pointer to the member.
+ * @type:      the type of the container struct this is embedded in.
+ * @member:    the name of the member within the struct.
+ *
+ */
+#ifndef container_of
+#define container_of(ptr, type, member) (                      \
+       (type *)( (char *)ptr - offsetof(type,member) ))
+#endif
+
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+       struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+       struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+       list->next = list;
+       list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+                             struct list_head *prev,
+                             struct list_head *next)
+{
+       next->prev = new;
+       new->next = next;
+       new->prev = prev;
+       prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head, head->next);
+}
+
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       __list_add(new, head->prev, head);
+}
+
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+       next->prev = prev;
+       prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       entry->next = NULL;
+       entry->prev = NULL;
+}
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+                               struct list_head *new)
+{
+       new->next = old->next;
+       new->next->prev = new;
+       new->prev = old->prev;
+       new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+                                       struct list_head *new)
+{
+       list_replace(old, new);
+       INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+       __list_del(entry->prev, entry->next);
+       INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+       __list_del(list->prev, list->next);
+       list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+                                 struct list_head *head)
+{
+       __list_del(list->prev, list->next);
+       list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+                               const struct list_head *head)
+{
+       return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+       return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+       struct list_head *next = head->next;
+       return (next == head) && (next == head->prev);
+}
+
+static inline void __list_splice(struct list_head *list,
+                                struct list_head *head)
+{
+       struct list_head *first = list->next;
+       struct list_head *last = list->prev;
+       struct list_head *at = head->next;
+
+       first->prev = head;
+       head->next = first;
+
+       last->next = at;
+       at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(struct list_head *list, struct list_head *head)
+{
+       if (!list_empty(list))
+               __list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+                                   struct list_head *head)
+{
+       if (!list_empty(list)) {
+               __list_splice(list, head);
+               INIT_LIST_HEAD(list);
+       }
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:       the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+       container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr:       the list head to take the element from.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+       list_entry((ptr)->next, type, member)
+
+/**
+ * list_for_each       -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop cursor.
+ * @head:      the head for your list.
+ */
+#define list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); \
+               pos = pos->next)
+
+/**
+ * __list_for_each     -       iterate over a list
+ * @pos:       the &struct list_head to use as a loop cursor.
+ * @head:      the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+       for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev  -       iterate over a list backwards
+ * @pos:       the &struct list_head to use as a loop cursor.
+ * @head:      the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+       for (pos = (head)->prev; pos != (head); \
+               pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos:       the &struct list_head to use as a loop cursor.
+ * @n:         another &struct list_head to use as temporary storage
+ * @head:      the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+       for (pos = (head)->next, n = pos->next; pos != (head); \
+               pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * @pos:       the &struct list_head to use as a loop cursor.
+ * @n:         another &struct list_head to use as temporary storage
+ * @head:      the head for your list.
+ */
+#define list_for_each_prev_safe(pos, n, head) \
+       for (pos = (head)->prev, n = pos->prev; \
+            pos != (head); \
+            pos = n, n = pos->prev)
+
+/**
+ * list_for_each_entry -       iterate over list of given type
+ * @pos:       the type * to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)                         \
+       for (pos = list_entry((head)->next, typeof(*pos), member);      \
+            &pos->member != (head);    \
+            pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:       the type * to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)                 \
+       for (pos = list_entry((head)->prev, typeof(*pos), member);      \
+            &pos->member != (head);    \
+            pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos:       the type * to use as a start point
+ * @head:      the head of the list
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+       ((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos:       the type * to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member)                \
+       for (pos = list_entry(pos->member.next, typeof(*pos), member);  \
+            &pos->member != (head);    \
+            pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ * @pos:       the type * to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse(pos, head, member)                \
+       for (pos = list_entry(pos->member.prev, typeof(*pos), member);  \
+            &pos->member != (head);    \
+            pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos:       the type * to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member)                    \
+       for (; &pos->member != (head);  \
+            pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:       the type * to use as a loop cursor.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)                 \
+       for (pos = list_entry((head)->next, typeof(*pos), member),      \
+               n = list_entry(pos->member.next, typeof(*pos), member); \
+            &pos->member != (head);                                    \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue
+ * @pos:       the type * to use as a loop cursor.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member)                \
+       for (pos = list_entry(pos->member.next, typeof(*pos), member),          \
+               n = list_entry(pos->member.next, typeof(*pos), member);         \
+            &pos->member != (head);                                            \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_from
+ * @pos:       the type * to use as a loop cursor.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member)                    \
+       for (n = list_entry(pos->member.next, typeof(*pos), member);            \
+            &pos->member != (head);                                            \
+            pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse
+ * @pos:       the type * to use as a loop cursor.
+ * @n:         another type * to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the list_struct within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member)         \
+       for (pos = list_entry((head)->prev, typeof(*pos), member),      \
+               n = list_entry(pos->member.prev, typeof(*pos), member); \
+            &pos->member != (head);                                    \
+            pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+       struct hlist_node *first;
+};
+
+struct hlist_node {
+       struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+static inline void INIT_HLIST_NODE(struct hlist_node *h)
+{
+       h->next = NULL;
+       h->pprev = NULL;
+}
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+       return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+       return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+       struct hlist_node *next = n->next;
+       struct hlist_node **pprev = n->pprev;
+       *pprev = next;
+       if (next)
+               next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+       __hlist_del(n);
+       n->next = NULL;
+       n->pprev = NULL;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+       if (!hlist_unhashed(n)) {
+               __hlist_del(n);
+               INIT_HLIST_NODE(n);
+       }
+}
+
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+       struct hlist_node *first = h->first;
+       n->next = first;
+       if (first)
+               first->pprev = &n->next;
+       h->first = n;
+       n->pprev = &h->first;
+}
+
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+                                       struct hlist_node *next)
+{
+       n->pprev = next->pprev;
+       n->next = next;
+       next->pprev = &n->next;
+       *(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+                                       struct hlist_node *next)
+{
+       next->next = n->next;
+       n->next = next;
+       next->pprev = &n->next;
+
+       if(next->next)
+               next->next->pprev  = &next->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+       for (pos = (head)->first; pos; pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+       for (pos = (head)->first; pos; pos = n)
+
+/**
+ * hlist_for_each_entry        - iterate over list of given type
+ * @tpos:      the type * to use as a loop cursor.
+ * @pos:       the &struct hlist_node to use as a loop cursor.
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member)                   \
+       for (pos = (head)->first; pos &&                                 \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after current point
+ * @tpos:      the type * to use as a loop cursor.
+ * @pos:       the &struct hlist_node to use as a loop cursor.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member)               \
+       for (pos = (pos)->next; pos &&                                  \
+            ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;});   \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from current point
+ * @tpos:      the type * to use as a loop cursor.
+ * @pos:       the &struct hlist_node to use as a loop cursor.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member)                    \
+       for (; pos &&                    \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos:      the type * to use as a loop cursor.
+ * @pos:       the &struct hlist_node to use as a loop cursor.
+ * @n:         another &struct hlist_node to use as temporary storage
+ * @head:      the head for your list.
+ * @member:    the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member)           \
+       for (pos = (head)->first;                                        \
+            pos && ({ n = pos->next; 1; }) &&                           \
+               ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+            pos = n)
+
+#endif
diff --git a/package/wprobe/src/user/wprobe-info.c b/package/wprobe/src/user/wprobe-info.c
new file mode 100644 (file)
index 0000000..b891871
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * wprobe-test.c: Wireless probe user space test code
+ * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.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.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <stdint.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/wprobe.h>
+#include "wprobe.h"
+
+static const char *
+wprobe_dump_value(struct wprobe_attribute *attr)
+{
+       static char buf[128];
+
+#define HANDLE_TYPE(_type, _format) \
+       case WPROBE_VAL_##_type: \
+               snprintf(buf, sizeof(buf), _format, attr->val._type); \
+               break
+
+       switch(attr->type) {
+               HANDLE_TYPE(S8, "%d");
+               HANDLE_TYPE(S16, "%d");
+               HANDLE_TYPE(S32, "%d");
+               HANDLE_TYPE(S64, "%lld");
+               HANDLE_TYPE(U8, "%d");
+               HANDLE_TYPE(U16, "%d");
+               HANDLE_TYPE(U32, "%d");
+               HANDLE_TYPE(U64, "%lld");
+               case WPROBE_VAL_STRING:
+                       /* FIXME: implement this */
+               default:
+                       strncpy(buf, "<unknown>", sizeof(buf));
+                       break;
+       }
+       if ((attr->flags & WPROBE_F_KEEPSTAT) &&
+               (attr->val.n > 0)) {
+               int len = strlen(buf);
+               snprintf(buf + len, sizeof(buf) - len, " (avg: %.02f; stdev: %.02f, n=%d)", attr->val.avg, attr->val.stdev, attr->val.n);
+       }
+#undef HANDLE_TYPE
+
+       return buf;
+}
+
+
+static void
+wprobe_dump_data(const char *ifname, struct list_head *gl, struct list_head *ll, struct list_head *ls)
+{
+       struct wprobe_attribute *attr;
+       struct wprobe_link *link;
+       bool first = true;
+
+       fprintf(stderr, "\n");
+       wprobe_request_data(ifname, gl, NULL, 2);
+       list_for_each_entry(attr, gl, list) {
+               fprintf(stderr, (first ?
+                       "Global:            %s=%s\n" :
+                       "                   %s=%s\n"),
+                       attr->name,
+                       wprobe_dump_value(attr)
+               );
+               first = false;
+       }
+
+       list_for_each_entry(link, ls, list) {
+               first = true;
+               wprobe_request_data(ifname, ll, link->addr, 2);
+               list_for_each_entry(attr, ll, list) {
+                       if (first) {
+                               fprintf(stderr,
+                                       "%02x:%02x:%02x:%02x:%02x:%02x: %s=%s\n",
+                                       link->addr[0], link->addr[1], link->addr[2],
+                                       link->addr[3], link->addr[4], link->addr[5],
+                                       attr->name,
+                                       wprobe_dump_value(attr));
+                               first = false;
+                       } else {
+                               fprintf(stderr,
+                                       "                   %s=%s\n",
+                                       attr->name,
+                                       wprobe_dump_value(attr));
+                       }
+               }
+       }
+}
+
+static const char *attr_typestr[] = {
+       [0] = "Unknown",
+       [WPROBE_VAL_STRING] = "String",
+       [WPROBE_VAL_U8] = "Unsigned 8 bit",
+       [WPROBE_VAL_U16] = "Unsigned 16 bit",
+       [WPROBE_VAL_U32] = "Unsigned 32 bit",
+       [WPROBE_VAL_U64] = "Unsigned 64 bit",
+       [WPROBE_VAL_S8] = "Signed 8 bit",
+       [WPROBE_VAL_S16] = "Signed 16 bit",
+       [WPROBE_VAL_S32] = "Signed 32 bit",
+       [WPROBE_VAL_S64] = "Signed 64 bit",
+};
+
+static int usage(const char *prog)
+{
+       fprintf(stderr, "Usage: %s <interface>\n", prog);
+       return 1;
+}
+
+int main(int argc, char **argv)
+{
+       struct wprobe_attribute *attr;
+       const char *ifname;
+       LIST_HEAD(global_attr);
+       LIST_HEAD(link_attr);
+       LIST_HEAD(links);
+       int i = 0;
+
+       if (argc < 2)
+               return usage(argv[0]);
+
+       ifname = argv[1];
+
+       if (wprobe_init() != 0)
+               return -1;
+
+       wprobe_dump_attributes(ifname, false, &global_attr, NULL);
+       wprobe_dump_attributes(ifname, true, &link_attr, NULL);
+
+       if (list_empty(&global_attr) &&
+               list_empty(&link_attr)) {
+               fprintf(stderr, "Interface '%s' not found\n", ifname);
+               return -1;
+       }
+
+       list_for_each_entry(attr, &global_attr, list) {
+               fprintf(stderr, "Global attribute: '%s' (%s)\n",
+                       attr->name, attr_typestr[attr->type]);
+       }
+       list_for_each_entry(attr, &link_attr, list) {
+               fprintf(stderr, "Link attribute: '%s' (%s)\n",
+                       attr->name, attr_typestr[attr->type]);
+       }
+
+       while (1) {
+               usleep(100 * 1000);
+               wprobe_measure(ifname);
+
+               if (i-- > 0)
+                       continue;
+
+               i = 10;
+               wprobe_update_links(ifname, &links);
+               wprobe_dump_data(ifname, &global_attr, &link_attr, &links);
+       }
+       wprobe_free();
+
+       return 0;
+}
diff --git a/package/wprobe/src/user/wprobe.c b/package/wprobe/src/user/wprobe.c
new file mode 100644 (file)
index 0000000..1f8df6c
--- /dev/null
@@ -0,0 +1,474 @@
+/*
+ * wprobe.c: Wireless probe user space library
+ * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.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.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <math.h>
+#include <linux/wprobe.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/genl/family.h>
+#include "wprobe.h"
+
+#define DEBUG 1
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) fprintf(stderr, "%s(%d): " fmt, __func__, __LINE__, ##__VA_ARGS__)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+static struct nl_sock *handle = NULL;
+static struct nl_cache *cache = NULL;
+static struct genl_family *family = NULL;
+static struct nlattr *tb[WPROBE_ATTR_LAST+1];
+static struct nla_policy attribute_policy[WPROBE_ATTR_LAST+1] = {
+       [WPROBE_ATTR_ID] = { .type = NLA_U32 },
+       [WPROBE_ATTR_MAC] = { .type = NLA_UNSPEC, .minlen = 6, .maxlen = 6 },
+       [WPROBE_ATTR_NAME] = { .type = NLA_STRING },
+       [WPROBE_ATTR_FLAGS] = { .type = NLA_U32 },
+       [WPROBE_ATTR_TYPE] = { .type = NLA_U8 },
+       [WPROBE_VAL_S8] = { .type = NLA_U8 },
+       [WPROBE_VAL_S16] = { .type = NLA_U16 },
+       [WPROBE_VAL_S32] = { .type = NLA_U32 },
+       [WPROBE_VAL_S64] = { .type = NLA_U64 },
+       [WPROBE_VAL_U8] = { .type = NLA_U8 },
+       [WPROBE_VAL_U16] = { .type = NLA_U16 },
+       [WPROBE_VAL_U32] = { .type = NLA_U32 },
+       [WPROBE_VAL_U64] = { .type = NLA_U64 },
+       [WPROBE_VAL_SUM] = { .type = NLA_U64 },
+       [WPROBE_VAL_SUM_SQ] = { .type = NLA_U64 },
+       [WPROBE_VAL_SAMPLES] = { .type = NLA_U32 },
+};
+
+static int
+error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg)
+{
+       int *ret = arg;
+       *ret = err->error;
+       return NL_STOP;
+}
+
+static int
+finish_handler(struct nl_msg *msg, void *arg)
+{
+       int *ret = arg;
+       *ret = 0;
+       return NL_SKIP;
+}
+
+static int
+ack_handler(struct nl_msg *msg, void *arg)
+{
+       int *ret = arg;
+       *ret = 0;
+       return NL_STOP;
+}
+
+
+void
+wprobe_free(void)
+{
+       if (cache)
+               nl_cache_free(cache);
+       if (handle)
+               nl_socket_free(handle);
+       handle = NULL;
+       cache = NULL;
+}
+
+int
+wprobe_init(void)
+{
+       int ret;
+
+       handle = nl_socket_alloc();
+       if (!handle) {
+               DPRINTF("Failed to create handle\n");
+               goto err;
+       }
+
+       if (genl_connect(handle)) {
+               DPRINTF("Failed to connect to generic netlink\n");
+               goto err;
+       }
+
+       ret = genl_ctrl_alloc_cache(handle, &cache);
+       if (ret < 0) {
+               DPRINTF("Failed to allocate netlink cache\n");
+               goto err;
+       }
+
+       family = genl_ctrl_search_by_name(cache, "wprobe");
+       if (!family) {
+               DPRINTF("wprobe API not present\n");
+               goto err;
+       }
+       return 0;
+
+err:
+       wprobe_free();
+       return -EINVAL;
+}
+
+
+static struct nl_msg *
+wprobe_new_msg(const char *ifname, int cmd, bool dump)
+{
+       struct nl_msg *msg;
+       uint32_t flags = 0;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return NULL;
+
+       if (dump)
+               flags |= NLM_F_DUMP;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(family),
+                       0, flags, cmd, 0);
+
+       NLA_PUT_STRING(msg, WPROBE_ATTR_INTERFACE, ifname);
+nla_put_failure:
+       return msg;
+}
+
+static int
+wprobe_send_msg(struct nl_msg *msg, void *callback, void *arg)
+{
+       struct nl_cb *cb;
+       int err = 0;
+
+       cb = nl_cb_alloc(NL_CB_DEFAULT);
+       if (!cb)
+               goto out_no_cb;
+
+       if (callback)
+               nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, callback, arg);
+
+       err = nl_send_auto_complete(handle, msg);
+       if (err < 0)
+               goto out;
+
+       err = 1;
+
+       nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
+       nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
+       nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
+
+       while (err > 0)
+               nl_recvmsgs(handle, cb);
+
+out:
+       nl_cb_put(cb);
+out_no_cb:
+       nlmsg_free(msg);
+       return err;
+}
+
+struct wprobe_attr_cb {
+       struct list_head *list;
+       char *addr;
+};
+
+static int
+save_attribute_handler(struct nl_msg *msg, void *arg)
+{
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       const char *name = "N/A";
+       struct wprobe_attribute *attr;
+       int type = 0;
+       struct wprobe_attr_cb *cb = arg;
+
+       nla_parse(tb, WPROBE_ATTR_LAST, genlmsg_attrdata(gnlh, 0),
+                       genlmsg_attrlen(gnlh, 0), attribute_policy);
+
+       if (tb[WPROBE_ATTR_NAME])
+               name = nla_data(tb[WPROBE_ATTR_NAME]);
+
+       attr = malloc(sizeof(struct wprobe_attribute) + strlen(name) + 1);
+       if (!attr)
+               return -1;
+
+       memset(attr, 0, sizeof(struct wprobe_attribute));
+
+       if (tb[WPROBE_ATTR_ID])
+               attr->id = nla_get_u32(tb[WPROBE_ATTR_ID]);
+
+       if (tb[WPROBE_ATTR_MAC] && cb->addr)
+               memcpy(cb->addr, nla_data(tb[WPROBE_ATTR_MAC]), 6);
+
+       if (tb[WPROBE_ATTR_FLAGS])
+               attr->flags = nla_get_u32(tb[WPROBE_ATTR_FLAGS]);
+
+       if (tb[WPROBE_ATTR_TYPE])
+               type = nla_get_u8(tb[WPROBE_ATTR_TYPE]);
+
+       if ((type < WPROBE_VAL_STRING) ||
+               (type > WPROBE_VAL_U64))
+               type = 0;
+
+       attr->type = type;
+       strcpy(attr->name, name);
+       INIT_LIST_HEAD(&attr->list);
+       list_add(&attr->list, cb->list);
+       return 0;
+}
+
+
+int
+wprobe_dump_attributes(const char *ifname, bool link, struct list_head *list, char *addr)
+{
+       struct nl_msg *msg;
+       struct wprobe_attr_cb cb;
+
+       cb.list = list;
+       cb.addr = addr;
+       msg = wprobe_new_msg(ifname, WPROBE_CMD_GET_LIST, true);
+       if (!msg)
+               return -ENOMEM;
+
+       if (link)
+               NLA_PUT(msg, WPROBE_ATTR_MAC, 6, "\x00\x00\x00\x00\x00\x00");
+
+       return wprobe_send_msg(msg, save_attribute_handler, &cb);
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return -EINVAL;
+}
+
+static struct wprobe_link *
+get_link(struct list_head *list, const char *addr)
+{
+       struct wprobe_link *l;
+
+       list_for_each_entry(l, list, list) {
+               if (!memcmp(l->addr, addr, 6)) {
+                       list_del_init(&l->list);
+                       goto out;
+               }
+       }
+
+       /* no previous link found, allocate a new one */
+       l = malloc(sizeof(struct wprobe_link));
+       if (!l)
+               goto out;
+
+       memset(l, 0, sizeof(struct wprobe_link));
+       memcpy(l->addr, addr, sizeof(l->addr));
+       INIT_LIST_HEAD(&l->list);
+
+out:
+       return l;
+}
+
+struct wprobe_save_cb {
+       struct list_head *list;
+       struct list_head old_list;
+};
+
+static int
+save_link_handler(struct nl_msg *msg, void *arg)
+{
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct wprobe_link *link;
+       struct wprobe_save_cb *cb = arg;
+       const char *addr;
+
+       nla_parse(tb, WPROBE_ATTR_LAST, genlmsg_attrdata(gnlh, 0),
+                       genlmsg_attrlen(gnlh, 0), attribute_policy);
+
+       if (!tb[WPROBE_ATTR_MAC] || (nla_len(tb[WPROBE_ATTR_MAC]) != 6))
+               return -1;
+
+       addr = nla_data(tb[WPROBE_ATTR_MAC]);
+       link = get_link(&cb->old_list, addr);
+       if (!link)
+               return -1;
+
+       if (tb[WPROBE_ATTR_FLAGS])
+               link->flags = nla_get_u32(tb[WPROBE_ATTR_FLAGS]);
+
+       list_add_tail(&link->list, cb->list);
+       return 0;
+}
+
+
+int
+wprobe_update_links(const char *ifname, struct list_head *list)
+{
+       struct wprobe_link *l, *tmp;
+       struct nl_msg *msg;
+       struct wprobe_save_cb cb;
+       int err;
+
+       INIT_LIST_HEAD(&cb.old_list);
+       list_splice_init(list, &cb.old_list);
+       cb.list = list;
+
+       msg = wprobe_new_msg(ifname, WPROBE_CMD_GET_LINKS, true);
+       if (!msg)
+               return -ENOMEM;
+
+       err = wprobe_send_msg(msg, save_link_handler, &cb);
+       if (err < 0)
+               return err;
+
+       list_for_each_entry_safe(l, tmp, &cb.old_list, list) {
+               list_del(&l->list);
+               free(l);
+       }
+
+       return 0;
+}
+
+void
+wprobe_measure(const char *ifname)
+{
+       struct nl_msg *msg;
+
+       msg = wprobe_new_msg(ifname, WPROBE_CMD_MEASURE, false);
+       if (!msg)
+               return;
+
+       wprobe_send_msg(msg, NULL, NULL);
+}
+
+struct wprobe_request_cb {
+       struct list_head *list;
+       struct list_head old_list;
+       char *addr;
+};
+
+static int
+save_attrdata_handler(struct nl_msg *msg, void *arg)
+{
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct wprobe_request_cb *cb = arg;
+       struct wprobe_attribute *attr;
+       int type, id;
+
+       nla_parse(tb, WPROBE_ATTR_LAST, genlmsg_attrdata(gnlh, 0),
+                       genlmsg_attrlen(gnlh, 0), attribute_policy);
+
+       if (!tb[WPROBE_ATTR_ID])
+               return -1;
+
+       if (!tb[WPROBE_ATTR_TYPE])
+               return -1;
+
+       id = nla_get_u32(tb[WPROBE_ATTR_ID]);
+       list_for_each_entry(attr, &cb->old_list, list) {
+               if (attr->id == id)
+                       goto found;
+       }
+       /* not found */
+       return -1;
+
+found:
+       list_del_init(&attr->list);
+
+       type = nla_get_u8(tb[WPROBE_ATTR_TYPE]);
+       if (type != attr->type) {
+               DPRINTF("WARNING: type mismatch for %s attribute '%s' (%d != %d)\n",
+                       (cb->addr ? "link" : "global"),
+                       attr->name,
+                       type, attr->type);
+               goto out;
+       }
+
+       if ((type < WPROBE_VAL_STRING) ||
+               (type > WPROBE_VAL_U64))
+               goto out;
+
+       memset(&attr->val, 0, sizeof(attr->val));
+
+#define HANDLE_INT_TYPE(_idx, _type) \
+       case WPROBE_VAL_S##_type: \
+       case WPROBE_VAL_U##_type: \
+               attr->val.U##_type = nla_get_u##_type(tb[_idx]); \
+               break
+
+       switch(type) {
+               HANDLE_INT_TYPE(type, 8);
+               HANDLE_INT_TYPE(type, 16);
+               HANDLE_INT_TYPE(type, 32);
+               HANDLE_INT_TYPE(type, 64);
+               case WPROBE_VAL_STRING:
+                       /* unimplemented */
+                       break;
+       }
+#undef HANDLE_TYPE
+
+       if (attr->flags & WPROBE_F_KEEPSTAT) {
+               if (tb[WPROBE_VAL_SUM])
+                       attr->val.s = nla_get_u64(tb[WPROBE_VAL_SUM]);
+
+               if (tb[WPROBE_VAL_SUM_SQ])
+                       attr->val.ss = nla_get_u64(tb[WPROBE_VAL_SUM_SQ]);
+
+               if (tb[WPROBE_VAL_SAMPLES])
+                       attr->val.n = nla_get_u32(tb[WPROBE_VAL_SAMPLES]);
+
+               if (attr->val.n > 0) {
+                       float avg = ((float) attr->val.s) / attr->val.n;
+                       float stdev = sqrt((((float) attr->val.ss) / attr->val.n) - (avg * avg));
+                       attr->val.avg = avg;
+                       attr->val.stdev = stdev;
+               }
+       }
+
+out:
+       list_add_tail(&attr->list, cb->list);
+       return 0;
+}
+
+
+int
+wprobe_request_data(const char *ifname, struct list_head *attrs, const unsigned char *addr, int scale)
+{
+       struct wprobe_request_cb cb;
+       struct nl_msg *msg;
+       int err;
+
+       msg = wprobe_new_msg(ifname, WPROBE_CMD_GET_INFO, true);
+       if (!msg)
+               return -ENOMEM;
+
+       if (scale < 0)
+               NLA_PUT_U32(msg, WPROBE_ATTR_FLAGS, WPROBE_F_RESET);
+       else if (scale > 0)
+               NLA_PUT_U32(msg, WPROBE_ATTR_SCALE, scale);
+
+       if (addr)
+               NLA_PUT(msg, WPROBE_ATTR_MAC, 6, addr);
+
+nla_put_failure:
+       INIT_LIST_HEAD(&cb.old_list);
+       list_splice_init(attrs, &cb.old_list);
+       cb.list = attrs;
+
+       err = wprobe_send_msg(msg, save_attrdata_handler, &cb);
+       list_splice(&cb.old_list, attrs->prev);
+       return err;
+}
+
+
diff --git a/package/wprobe/src/user/wprobe.h b/package/wprobe/src/user/wprobe.h
new file mode 100644 (file)
index 0000000..e0c33fa
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * wprobe.h: Wireless probe user space library
+ * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.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.
+ *
+ * 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.
+ */
+
+#ifndef __WPROBE_USER_H
+#define __WPROBE_USER_H
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "list.h"
+
+/**
+ * struct wprobe_value: data structure for attribute values
+ * @STRING: string value (currently unsupported)
+ * @U8: unsigned 8-bit integer value
+ * @U16: unsigned 16-bit integer value
+ * @U32: unsigned 32-bit integer value
+ * @U64: unsigned 64-bit integer value
+ * @S8: signed 8-bit integer value
+ * @S16: signed 16-bit integer value
+ * @S32: signed 32-bit integer value
+ * @S64: signed 64-bit integer value
+ *
+ * @n: number of sample values 
+ * @avg: average value
+ * @stdev: standard deviation
+ * @s: sum of all sample values (internal use)
+ * @ss: sum of all sample values squared (internal use)
+ */
+struct wprobe_value {
+       /* attribute value */
+       union {
+               const char *STRING;
+               uint8_t U8;
+               uint16_t U16;
+               uint32_t U32;
+               uint64_t U64;
+               int8_t S8;
+               int16_t S16;
+               int32_t S32;
+               int64_t S64;
+       };
+       /* statistics */
+       int64_t s, ss;
+       float avg, stdev;
+       unsigned int n;
+};
+
+/**
+ * struct wprobe_attribute: data structures for attribute descriptions
+ * @list: linked list data structure for a list of attributes
+ * @id: attribute id
+ * @type: netlink type for the attribute (see kernel api documentation)
+ * @flags: attribute flags (see kernel api documentation)
+ * @val: cached version of the last netlink query, will be overwritten on each request
+ * @name: attribute name
+ */
+struct wprobe_attribute {
+       struct list_head list;
+       int id;
+       int type;
+       uint32_t flags;
+       struct wprobe_value val;
+       char name[];
+};
+
+/**
+ * struct wprobe_link: data structure for the link description
+ * @list: linked list data structure for a list of links 
+ * @flags: link flags (see kernel api documentation)
+ * @addr: mac address of the remote link partner
+ */
+struct wprobe_link {
+       struct list_head list;
+       uint32_t flags;
+       unsigned char addr[6];
+};
+
+/**
+ * wprobe_init: initialize internal data structures and connect to the wprobe netlink api
+ */
+extern int wprobe_init(void);
+
+/**
+ * wprobe_free: free all internally allocated data structures
+ */
+extern void wprobe_free(void);
+
+/**
+ * wprobe_update_links: get a list of all link partners
+ * @ifname: name of the wprobe interface
+ * @list: linked list for storing link descriptions
+ *
+ * when wprobe_update_links is called multiple times, the linked list 
+ * is updated with new link partners, old entries are automatically expired
+ */
+extern int wprobe_update_links(const char *ifname, struct list_head *list);
+
+/**
+ * wprobe_measure: start a measurement request for all global attributes
+ * @ifname: name of the wprobe interface
+ *
+ * not all attributes are automatically filled with data, since for some
+ * it may be desirable to control the sampling interval from user space
+ * you can use this function to do that.
+ */
+extern void wprobe_measure(const char *ifname);
+
+/**
+ * wprobe_dump_attributes: create a linked list of available attributes
+ * @ifname: name of the wprobe interface
+ * @link: false: get the list of global attributes; true: get the list of per-link attributes
+ * @list: linked list to store the attributes in
+ * @addr: buffer to store the interface's mac address in (optional)
+ *
+ * attributes must be freed by the caller
+ */
+extern int wprobe_dump_attributes(const char *ifname, bool link, struct list_head *list, char *addr);
+
+/**
+ * wprobe_request_data: request new sampling values for the given list of attributes
+ * @ifname: name of the wprobe interface
+ * @attrs: attribute list
+ * @addr: (optional) mac address of the link partner
+ * @scale: scale down values by a factor (scale < 0: reset statistics entirely)
+ *
+ * if addr is unset, attrs must point to the list of global attributes,
+ * if addr is set, attrs must point to the list of per-link attributes
+ */
+extern int wprobe_request_data(const char *ifname, struct list_head *attrs, const unsigned char *addr, int scale);
+
+#endif