From b55200102d62639bb069a4691b17274bc744b4b6 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 8 May 2009 22:48:17 +0000 Subject: [PATCH] backport libnl-tiny, wprobe, madwifi SVN-Revision: 15719 --- package/libnl-tiny/Makefile | 52 + package/libnl-tiny/files/libnl-tiny.pc | 10 + package/libnl-tiny/src/Makefile | 17 + package/libnl-tiny/src/attr.c | 668 ++++++++ package/libnl-tiny/src/cache.c | 841 ++++++++++ package/libnl-tiny/src/cache_mngt.c | 249 +++ package/libnl-tiny/src/error.c | 108 ++ package/libnl-tiny/src/genl.c | 268 ++++ package/libnl-tiny/src/genl_ctrl.c | 302 ++++ package/libnl-tiny/src/genl_family.c | 136 ++ package/libnl-tiny/src/genl_mngt.c | 273 ++++ package/libnl-tiny/src/handlers.c | 162 ++ .../libnl-tiny/src/include/linux/genetlink.h | 83 + package/libnl-tiny/src/include/linux/if.h | 131 ++ .../libnl-tiny/src/include/linux/if_addr.h | 62 + .../libnl-tiny/src/include/linux/netlink.h | 150 ++ .../libnl-tiny/src/include/netlink-generic.h | 20 + .../libnl-tiny/src/include/netlink-local.h | 154 ++ .../libnl-tiny/src/include/netlink-types.h | 81 + package/libnl-tiny/src/include/netlink/addr.h | 69 + package/libnl-tiny/src/include/netlink/attr.h | 726 +++++++++ .../src/include/netlink/cache-api.h | 199 +++ .../libnl-tiny/src/include/netlink/cache.h | 126 ++ package/libnl-tiny/src/include/netlink/data.h | 41 + .../libnl-tiny/src/include/netlink/errno.h | 59 + .../src/include/netlink/genl/ctrl.h | 37 + .../src/include/netlink/genl/family.h | 130 ++ .../src/include/netlink/genl/genl.h | 47 + .../src/include/netlink/genl/mngt.h | 87 ++ .../libnl-tiny/src/include/netlink/handlers.h | 230 +++ package/libnl-tiny/src/include/netlink/list.h | 88 ++ package/libnl-tiny/src/include/netlink/msg.h | 308 ++++ .../src/include/netlink/netlink-compat.h | 50 + .../src/include/netlink/netlink-kernel.h | 196 +++ .../libnl-tiny/src/include/netlink/netlink.h | 82 + .../src/include/netlink/object-api.h | 331 ++++ .../libnl-tiny/src/include/netlink/object.h | 144 ++ .../libnl-tiny/src/include/netlink/socket.h | 221 +++ .../libnl-tiny/src/include/netlink/types.h | 117 ++ .../libnl-tiny/src/include/netlink/utils.h | 78 + .../libnl-tiny/src/include/netlink/version.h | 18 + package/libnl-tiny/src/msg.c | 566 +++++++ package/libnl-tiny/src/nl.c | 720 +++++++++ package/libnl-tiny/src/object.c | 309 ++++ package/libnl-tiny/src/socket.c | 406 +++++ package/libnl/Makefile | 1 + package/madwifi/Config.in | 6 - package/madwifi/Makefile | 48 +- package/madwifi/files/lib/wifi/madwifi.sh | 16 +- .../104-autocreate_none.patch | 13 - .../patches-upstream/111-minstrel_crash.patch | 14 - package/madwifi/patches/123-ccmp_checks.patch | 77 +- package/madwifi/patches/351-scanlist.patch | 57 +- package/madwifi/patches/389-autochannel.patch | 4 +- .../patches/411-autochannel_multi.patch | 16 +- package/madwifi/patches/416-wprobe.patch | 509 +++++++ package/madwifi/patches/418-turbo.patch | 15 - .../madwifi/patches/420-diversity_fix.patch | 58 +- .../patches/421-channel_handling.patch | 1348 +++++++++++++++++ .../patches/422-confchange_reset.patch | 31 + package/wprobe/Makefile | 123 ++ package/wprobe/files/wprobe.init | 46 + package/wprobe/src/exporter/Makefile | 2 + package/wprobe/src/exporter/wprobe-export.c | 317 ++++ package/wprobe/src/exporter/wprobe-export.h | 34 + package/wprobe/src/kernel/Makefile | 5 + package/wprobe/src/kernel/linux/wprobe.h | 298 ++++ package/wprobe/src/kernel/wprobe-core.c | 669 ++++++++ package/wprobe/src/kernel/wprobe-dummy.c | 96 ++ package/wprobe/src/user/Makefile | 21 + package/wprobe/src/user/list.h | 601 ++++++++ package/wprobe/src/user/wprobe-info.c | 175 +++ package/wprobe/src/user/wprobe.c | 474 ++++++ package/wprobe/src/user/wprobe.h | 143 ++ 74 files changed, 14262 insertions(+), 107 deletions(-) create mode 100644 package/libnl-tiny/Makefile create mode 100644 package/libnl-tiny/files/libnl-tiny.pc create mode 100644 package/libnl-tiny/src/Makefile create mode 100644 package/libnl-tiny/src/attr.c create mode 100644 package/libnl-tiny/src/cache.c create mode 100644 package/libnl-tiny/src/cache_mngt.c create mode 100644 package/libnl-tiny/src/error.c create mode 100644 package/libnl-tiny/src/genl.c create mode 100644 package/libnl-tiny/src/genl_ctrl.c create mode 100644 package/libnl-tiny/src/genl_family.c create mode 100644 package/libnl-tiny/src/genl_mngt.c create mode 100644 package/libnl-tiny/src/handlers.c create mode 100644 package/libnl-tiny/src/include/linux/genetlink.h create mode 100644 package/libnl-tiny/src/include/linux/if.h create mode 100644 package/libnl-tiny/src/include/linux/if_addr.h create mode 100644 package/libnl-tiny/src/include/linux/netlink.h create mode 100644 package/libnl-tiny/src/include/netlink-generic.h create mode 100644 package/libnl-tiny/src/include/netlink-local.h create mode 100644 package/libnl-tiny/src/include/netlink-types.h create mode 100644 package/libnl-tiny/src/include/netlink/addr.h create mode 100644 package/libnl-tiny/src/include/netlink/attr.h create mode 100644 package/libnl-tiny/src/include/netlink/cache-api.h create mode 100644 package/libnl-tiny/src/include/netlink/cache.h create mode 100644 package/libnl-tiny/src/include/netlink/data.h create mode 100644 package/libnl-tiny/src/include/netlink/errno.h create mode 100644 package/libnl-tiny/src/include/netlink/genl/ctrl.h create mode 100644 package/libnl-tiny/src/include/netlink/genl/family.h create mode 100644 package/libnl-tiny/src/include/netlink/genl/genl.h create mode 100644 package/libnl-tiny/src/include/netlink/genl/mngt.h create mode 100644 package/libnl-tiny/src/include/netlink/handlers.h create mode 100644 package/libnl-tiny/src/include/netlink/list.h create mode 100644 package/libnl-tiny/src/include/netlink/msg.h create mode 100644 package/libnl-tiny/src/include/netlink/netlink-compat.h create mode 100644 package/libnl-tiny/src/include/netlink/netlink-kernel.h create mode 100644 package/libnl-tiny/src/include/netlink/netlink.h create mode 100644 package/libnl-tiny/src/include/netlink/object-api.h create mode 100644 package/libnl-tiny/src/include/netlink/object.h create mode 100644 package/libnl-tiny/src/include/netlink/socket.h create mode 100644 package/libnl-tiny/src/include/netlink/types.h create mode 100644 package/libnl-tiny/src/include/netlink/utils.h create mode 100644 package/libnl-tiny/src/include/netlink/version.h create mode 100644 package/libnl-tiny/src/msg.c create mode 100644 package/libnl-tiny/src/nl.c create mode 100644 package/libnl-tiny/src/object.c create mode 100644 package/libnl-tiny/src/socket.c delete mode 100644 package/madwifi/patches-upstream/104-autocreate_none.patch delete mode 100644 package/madwifi/patches-upstream/111-minstrel_crash.patch create mode 100644 package/madwifi/patches/416-wprobe.patch delete mode 100644 package/madwifi/patches/418-turbo.patch create mode 100644 package/madwifi/patches/421-channel_handling.patch create mode 100644 package/madwifi/patches/422-confchange_reset.patch create mode 100644 package/wprobe/Makefile create mode 100755 package/wprobe/files/wprobe.init create mode 100644 package/wprobe/src/exporter/Makefile create mode 100644 package/wprobe/src/exporter/wprobe-export.c create mode 100644 package/wprobe/src/exporter/wprobe-export.h create mode 100644 package/wprobe/src/kernel/Makefile create mode 100644 package/wprobe/src/kernel/linux/wprobe.h create mode 100644 package/wprobe/src/kernel/wprobe-core.c create mode 100644 package/wprobe/src/kernel/wprobe-dummy.c create mode 100644 package/wprobe/src/user/Makefile create mode 100644 package/wprobe/src/user/list.h create mode 100644 package/wprobe/src/user/wprobe-info.c create mode 100644 package/wprobe/src/user/wprobe.c create mode 100644 package/wprobe/src/user/wprobe.h diff --git a/package/libnl-tiny/Makefile b/package/libnl-tiny/Makefile new file mode 100644 index 0000000000..4b02ef16be --- /dev/null +++ b/package/libnl-tiny/Makefile @@ -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 index 0000000000..8c1bf6f2b2 --- /dev/null +++ b/package/libnl-tiny/files/libnl-tiny.pc @@ -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 index 0000000000..f684155ecc --- /dev/null +++ b/package/libnl-tiny/src/Makefile @@ -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 index 0000000000..e0f50611cf --- /dev/null +++ b/package/libnl-tiny/src/attr.c @@ -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 + */ + +#include +#include +#include +#include +#include +#include +#include + +/** + * @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 index 0000000000..fed042d158 --- /dev/null +++ b/package/libnl-tiny/src/cache.c @@ -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 + */ + +/** + * @ingroup cache_mngt + * @defgroup cache Cache + * + * @code + * Cache Management | | Type Specific Cache Operations + * + * | | +----------------+ +------------+ + * | request update | | msg_parser | + * | | +----------------+ +------------+ + * +- - - - -^- - - - - - - -^- -|- - - - + * nl_cache_update: | | | | + * 1) --------- co_request_update ------+ | | + * | | | + * 2) destroy old cache +----------- pp_cb ---------|---+ + * | | | + * 3) ---------- nl_recvmsgs ----------+ +- cb_valid -+ + * +--------------+ | | | | + * | nl_cache_add |<-----+ + - - -v- -|- - - - - - - - - - - + * +--------------+ | | +-------------+ + * | nl_recvmsgs | + * | | +-----|-^-----+ + * +---v-|---+ + * | | | nl_recv | + * +---------+ + * | | Core Netlink + * @endcode + * + * @{ + */ + +#include +#include +#include +#include +#include + +/** + * @name Access Functions + * @{ + */ + +#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 index 0000000000..d57d8364a8 --- /dev/null +++ b/package/libnl-tiny/src/cache_mngt.c @@ -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 + */ + +/** + * @ingroup core + * @defgroup cache_mngt Caching + * @{ + */ + +#include +#include +#include +#include + +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 index 0000000000..71ae7220e8 --- /dev/null +++ b/package/libnl-tiny/src/error.c @@ -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 + */ + +#include +#include + +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 index 0000000000..055be919e1 --- /dev/null +++ b/package/libnl-tiny/src/genl.c @@ -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 + */ + +/** + * @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 + * #include + * #include + * + * 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 +#include +#include +#include + +/** + * @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 index 0000000000..13016420d1 --- /dev/null +++ b/package/libnl-tiny/src/genl_ctrl.c @@ -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 + */ + +/** + * @ingroup genl_mngt + * @defgroup ctrl Controller + * @brief + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define CTRL_VERSION 0x0001 + +static struct nl_cache_ops genl_ctrl_ops; +/** @endcond */ + +static int ctrl_request_update(struct nl_cache *c, struct nl_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 index 0000000000..88aaad9b77 --- /dev/null +++ b/package/libnl-tiny/src/genl_family.c @@ -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 + */ + +/** + * @ingroup genl + * @defgroup genl_family Generic Netlink Family + * @brief + * + * @{ + */ + +#include +#include +#include +#include +#include + +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 index 0000000000..5df9a6efc3 --- /dev/null +++ b/package/libnl-tiny/src/genl_mngt.c @@ -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 + */ + +/** + * @ingroup genl + * @defgroup genl_mngt Management + * + * @par 1) Registering a generic netlink module + * @code + * #include + * + * // First step is to define all the commands being used in + * // particular generic netlink family. The ID and name are + * // mandatory to be filled out. A callback function and + * // most the attribute policy that comes with it must be + * // defined for commands expected to be issued towards + * // userspace. + * static struct genl_cmd foo_cmds[] = { + * { + * .c_id = FOO_CMD_NEW, + * .c_name = "NEWFOO" , + * .c_maxattr = FOO_ATTR_MAX, + * .c_attr_policy = foo_policy, + * .c_msg_parser = foo_msg_parser, + * }, + * { + * .c_id = FOO_CMD_DEL, + * .c_name = "DELFOO" , + * }, + * }; + * + * // The list of commands must then be integrated into a + * // struct genl_ops serving as handle for this particular + * // family. + * static struct genl_ops my_genl_ops = { + * .o_cmds = foo_cmds, + * .o_ncmds = ARRAY_SIZE(foo_cmds), + * }; + * + * // Using the above struct genl_ops an arbitary number of + * // cache handles can be associated to it. + * // + * // The macro GENL_HDRSIZE() must be used to specify the + * // length of the header to automatically take headers on + * // generic layers into account. + * // + * // The macro GENL_FAMILY() is used to represent the generic + * // netlink family id. + * static struct nl_cache_ops genl_foo_ops = { + * .co_name = "genl/foo", + * .co_hdrsize = GENL_HDRSIZE(sizeof(struct my_hdr)), + * .co_msgtypes = GENL_FAMILY(GENL_ID_GENERATE, "foo"), + * .co_genl = &my_genl_ops, + * .co_protocol = NETLINK_GENERIC, + * .co_request_update = foo_request_update, + * .co_obj_ops = &genl_foo_ops, + * }; + * + * // Finally each cache handle for a generic netlink family + * // must be registered using genl_register(). + * static void __init foo_init(void) + * { + * genl_register(&genl_foo_ops); + * } + * + * // ... respectively unregsted again. + * static void __exit foo_exit(void) + * { + * genl_unregister(&genl_foo_ops); + * } + * @endcode + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +static NL_LIST_HEAD(genl_ops_list); + +static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, + struct nlmsghdr *nlh, 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 index 0000000000..12ebda5207 --- /dev/null +++ b/package/libnl-tiny/src/handlers.c @@ -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 + */ + +/** + * @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 +#include +#include +#include +#include + +/** + * @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 index 0000000000..b834ef6d59 --- /dev/null +++ b/package/libnl-tiny/src/include/linux/genetlink.h @@ -0,0 +1,83 @@ +#ifndef __LINUX_GENERIC_NETLINK_H +#define __LINUX_GENERIC_NETLINK_H + +#include +#include + +#define GENL_NAMSIZ 16 /* length of family name */ + +#define GENL_MIN_ID NLMSG_MIN_TYPE +#define GENL_MAX_ID 1023 + +struct genlmsghdr { + __u8 cmd; + __u8 version; + __u16 reserved; +}; + +#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr)) + +#define GENL_ADMIN_PERM 0x01 +#define GENL_CMD_CAP_DO 0x02 +#define GENL_CMD_CAP_DUMP 0x04 +#define GENL_CMD_CAP_HASPOL 0x08 + +/* + * List of reserved static generic netlink identifiers: + */ +#define GENL_ID_GENERATE 0 +#define GENL_ID_CTRL NLMSG_MIN_TYPE + +/************************************************************************** + * Controller + **************************************************************************/ + +enum { + CTRL_CMD_UNSPEC, + CTRL_CMD_NEWFAMILY, + CTRL_CMD_DELFAMILY, + CTRL_CMD_GETFAMILY, + CTRL_CMD_NEWOPS, + CTRL_CMD_DELOPS, + CTRL_CMD_GETOPS, + CTRL_CMD_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 index 0000000000..4c1bcfec7f --- /dev/null +++ b/package/libnl-tiny/src/include/linux/if.h @@ -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, + * + * 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 /* 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 index 0000000000..43f3bedaaf --- /dev/null +++ b/package/libnl-tiny/src/include/linux/if_addr.h @@ -0,0 +1,62 @@ +#ifndef __LINUX_IF_ADDR_H +#define __LINUX_IF_ADDR_H + +#include + +struct ifaddrmsg +{ + __u8 ifa_family; + __u8 ifa_prefixlen; /* The prefix length */ + __u8 ifa_flags; /* Flags */ + __u8 ifa_scope; /* Address scope */ + __u32 ifa_index; /* Link index */ +}; + +/* + * Important comment: + * IFA_ADDRESS is prefix address, rather than local interface address. + * It makes no difference for normally configured broadcast interfaces, + * but for point-to-point IFA_ADDRESS is DESTINATION address, + * local address is supplied in IFA_LOCAL attribute. + */ +enum +{ + IFA_UNSPEC, + IFA_ADDRESS, + IFA_LOCAL, + IFA_LABEL, + IFA_BROADCAST, + IFA_ANYCAST, + IFA_CACHEINFO, + IFA_MULTICAST, + __IFA_MAX, +}; + +#define IFA_MAX (__IFA_MAX - 1) + +/* ifa_flags */ +#define IFA_F_SECONDARY 0x01 +#define IFA_F_TEMPORARY IFA_F_SECONDARY + +#define IFA_F_NODAD 0x02 +#define IFA_F_OPTIMISTIC 0x04 +#define IFA_F_HOMEADDRESS 0x10 +#define IFA_F_DEPRECATED 0x20 +#define IFA_F_TENTATIVE 0x40 +#define IFA_F_PERMANENT 0x80 + +struct ifa_cacheinfo +{ + __u32 ifa_prefered; + __u32 ifa_valid; + __u32 cstamp; /* created timestamp, hundredths of seconds */ + __u32 tstamp; /* updated timestamp, hundredths of seconds */ +}; + +/* backwards compatibility for userspace */ +#ifndef __KERNEL__ +#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg)))) +#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg)) +#endif + +#endif diff --git a/package/libnl-tiny/src/include/linux/netlink.h b/package/libnl-tiny/src/include/linux/netlink.h new file mode 100644 index 0000000000..d252103d2b --- /dev/null +++ b/package/libnl-tiny/src/include/linux/netlink.h @@ -0,0 +1,150 @@ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +#include /* for sa_family_t */ +#include + +#define NETLINK_ROUTE 0 /* Routing/device hook */ +#define NETLINK_UNUSED 1 /* Unused number */ +#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ +#define NETLINK_FIREWALL 3 /* Firewalling hook */ +#define NETLINK_INET_DIAG 4 /* INET socket monitoring */ +#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ +#define NETLINK_XFRM 6 /* ipsec */ +#define NETLINK_SELINUX 7 /* SELinux event notifications */ +#define NETLINK_ISCSI 8 /* Open-iSCSI */ +#define NETLINK_AUDIT 9 /* auditing */ +#define NETLINK_FIB_LOOKUP 10 +#define NETLINK_CONNECTOR 11 +#define NETLINK_NETFILTER 12 /* netfilter subsystem */ +#define NETLINK_IP6_FW 13 +#define NETLINK_DNRTMSG 14 /* DECnet routing messages */ +#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ +#define NETLINK_GENERIC 16 +/* leave room for NETLINK_DM (DM Events) */ +#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ +#define NETLINK_ECRYPTFS 19 + +#define MAX_LINKS 32 + +struct sockaddr_nl +{ + sa_family_t nl_family; /* AF_NETLINK */ + unsigned short nl_pad; /* zero */ + __u32 nl_pid; /* port ID */ + __u32 nl_groups; /* multicast groups mask */ +}; + +struct nlmsghdr +{ + __u32 nlmsg_len; /* Length of message including header */ + __u16 nlmsg_type; /* Message content */ + __u16 nlmsg_flags; /* Additional flags */ + __u32 nlmsg_seq; /* Sequence number */ + __u32 nlmsg_pid; /* Sending process port ID */ +}; + +/* Flags values */ + +#define NLM_F_REQUEST 1 /* It is request message. */ +#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */ +#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */ +#define NLM_F_ECHO 8 /* Echo this request */ + +/* Modifiers to GET request */ +#define NLM_F_ROOT 0x100 /* specify tree root */ +#define NLM_F_MATCH 0x200 /* return all matching */ +#define NLM_F_ATOMIC 0x400 /* atomic GET */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/* Modifiers to NEW request */ +#define NLM_F_REPLACE 0x100 /* Override existing */ +#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */ +#define NLM_F_CREATE 0x400 /* Create, if it does not exist */ +#define NLM_F_APPEND 0x800 /* Add to end of list */ + +/* + 4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL + 4.4BSD CHANGE NLM_F_REPLACE + + True CHANGE NLM_F_CREATE|NLM_F_REPLACE + Append NLM_F_CREATE + Check NLM_F_EXCL + */ + +#define NLMSG_ALIGNTO 4 +#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) ) +#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN)) +#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) +#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) +#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ + (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) +#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \ + (nlh)->nlmsg_len <= (len)) +#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) + +#define NLMSG_NOOP 0x1 /* Nothing. */ +#define NLMSG_ERROR 0x2 /* Error */ +#define NLMSG_DONE 0x3 /* End of a dump */ +#define NLMSG_OVERRUN 0x4 /* Data lost */ + +#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */ + +struct nlmsgerr +{ + int error; + struct nlmsghdr msg; +}; + +#define NETLINK_ADD_MEMBERSHIP 1 +#define NETLINK_DROP_MEMBERSHIP 2 +#define NETLINK_PKTINFO 3 + +struct nl_pktinfo +{ + __u32 group; +}; + +#define NET_MAJOR 36 /* Major 36 is reserved for networking */ + +enum { + NETLINK_UNCONNECTED = 0, + NETLINK_CONNECTED, +}; + +/* + * <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)--> + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * | Header | Pad | Payload | Pad | + * | (struct nlattr) | ing | | ing | + * +---------------------+- - -+- - - - - - - - - -+- - -+ + * <-------------- nlattr->nla_len --------------> + */ + +struct nlattr +{ + __u16 nla_len; + __u16 nla_type; +}; + +/* + * nla_type (16 bits) + * +---+---+-------------------------------+ + * | N | O | Attribute Type | + * +---+---+-------------------------------+ + * N := Carries nested attributes + * O := Payload stored in network byte order + * + * Note: The N and O flag are mutually exclusive. + */ +#define NLA_F_NESTED (1 << 15) +#define NLA_F_NET_BYTEORDER (1 << 14) +#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER) + +#define NLA_ALIGNTO 4 +#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1)) +#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr))) + +#endif /* __LINUX_NETLINK_H */ diff --git a/package/libnl-tiny/src/include/netlink-generic.h b/package/libnl-tiny/src/include/netlink-generic.h new file mode 100644 index 0000000000..10aa2f01bb --- /dev/null +++ b/package/libnl-tiny/src/include/netlink-generic.h @@ -0,0 +1,20 @@ +/* + * netlink-generic.h Local Generic Netlink Interface + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_GENL_PRIV_H_ +#define NETLINK_GENL_PRIV_H_ + +#include +#include + +#define GENL_HDRSIZE(hdrlen) (GENL_HDRLEN + (hdrlen)) + +#endif diff --git a/package/libnl-tiny/src/include/netlink-local.h b/package/libnl-tiny/src/include/netlink-local.h new file mode 100644 index 0000000000..a2000fa2ff --- /dev/null +++ b/package/libnl-tiny/src/include/netlink-local.h @@ -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 + */ + +#ifndef NETLINK_LOCAL_H_ +#define NETLINK_LOCAL_H_ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + +#include + +/* local header copies */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +struct trans_tbl { + int i; + const char *a; +}; + +#define __ADD(id, name) { .i = id, .a = #name }, + +struct trans_list { + int i; + char *a; + struct nl_list_head list; +}; + +#define NL_DEBUG 1 + +#define NL_DBG(LVL,FMT,ARG...) \ + do {} 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 index 0000000000..0d005935c3 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink-types.h @@ -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 + */ + +#ifndef NETLINK_LOCAL_TYPES_H_ +#define NETLINK_LOCAL_TYPES_H_ + +#include + +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 index 0000000000..cc3d201f97 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/addr.h @@ -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 + */ + +#ifndef NETLINK_ADDR_H_ +#define NETLINK_ADDR_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_addr; + +/* Creation */ +extern struct nl_addr * nl_addr_alloc(size_t); +extern struct nl_addr * nl_addr_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 index 0000000000..7076d67e22 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/attr.h @@ -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 + */ + +#ifndef NETLINK_ATTR_H_ +#define NETLINK_ATTR_H_ + +#include +#include +#include +#include +#include + +#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 index 0000000000..22fc449d1b --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/cache-api.h @@ -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 + */ + +#ifndef NETLINK_CACHE_API_H_ +#define NETLINK_CACHE_API_H_ + +#include + +#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 index 0000000000..f4db3b9bf9 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/cache.h @@ -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 + */ + +#ifndef NETLINK_CACHE_H_ +#define NETLINK_CACHE_H_ + +#include +#include +#include +#include +#include + +#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 index 0000000000..071159ee5b --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/data.h @@ -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 + */ + +#ifndef NETLINK_DATA_H_ +#define NETLINK_DATA_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct nl_data; + +/* General */ +extern struct nl_data * nl_data_alloc(void *, size_t); +extern struct nl_data * nl_data_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 index 0000000000..0b43da02a9 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/errno.h @@ -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 + */ + +#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 index 0000000000..1ae62f4480 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/genl/ctrl.h @@ -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 + */ + +#ifndef NETLINK_GENL_CTRL_H_ +#define NETLINK_GENL_CTRL_H_ + +#include +#include +#include + +#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 index 0000000000..0257cbe273 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/genl/family.h @@ -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 + */ + +#ifndef NETLINK_GENL_FAMILY_H_ +#define NETLINK_GENL_FAMILY_H_ + +#include +#include + +#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 index 0000000000..3f3340cfdc --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/genl/genl.h @@ -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 + */ + +#ifndef NETLINK_GENL_H_ +#define NETLINK_GENL_H_ + +#include +#include +#include + +#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 index 0000000000..8b0244f2cc --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/genl/mngt.h @@ -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 + */ + +#ifndef NETLINK_GENL_MNGT_H_ +#define NETLINK_GENL_MNGT_H_ + +#include +#include +#include + +#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 index 0000000000..5c62368a85 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/handlers.h @@ -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 + */ + +#ifndef NETLINK_HANDLERS_H_ +#define NETLINK_HANDLERS_H_ + +#include +#include +#include +#include +#include +#include + +#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 index 0000000000..c6876a77f7 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/list.h @@ -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 + */ + +#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 index 0000000000..aedcb08296 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/msg.h @@ -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 + */ + +#ifndef NETLINK_MSG_H_ +#define NETLINK_MSG_H_ + +#include +#include + +#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 index 0000000000..17ec9fc8d1 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/netlink-compat.h @@ -0,0 +1,50 @@ +/* + * netlink/netlink-compat.h Netlink Compatability + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2003-2006 Thomas Graf + */ + +#ifndef NETLINK_COMPAT_H_ +#define NETLINK_COMPAT_H_ + +#if !defined _LINUX_SOCKET_H && !defined _BITS_SOCKADDR_H +typedef unsigned short sa_family_t; +#endif + +#ifndef IFNAMSIZ +/** Maximum length of a interface name */ +#define IFNAMSIZ 16 +#endif + +/* patch 2.4.x if_arp */ +#ifndef ARPHRD_INFINIBAND +#define ARPHRD_INFINIBAND 32 +#endif + +/* patch 2.4.x eth header file */ +#ifndef ETH_P_MPLS_UC +#define ETH_P_MPLS_UC 0x8847 +#endif + +#ifndef ETH_P_MPLS_MC +#define ETH_P_MPLS_MC 0x8848 +#endif + +#ifndef ETH_P_EDP2 +#define ETH_P_EDP2 0x88A2 +#endif + +#ifndef ETH_P_HDLC +#define ETH_P_HDLC 0x0019 +#endif + +#ifndef AF_LLC +#define AF_LLC 26 +#endif + +#endif diff --git a/package/libnl-tiny/src/include/netlink/netlink-kernel.h b/package/libnl-tiny/src/include/netlink/netlink-kernel.h new file mode 100644 index 0000000000..a0f5535664 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/netlink-kernel.h @@ -0,0 +1,196 @@ +#ifndef __LINUX_NETLINK_H +#define __LINUX_NETLINK_H + +/** + * Netlink socket address + * @ingroup nl + */ +struct sockaddr_nl +{ + /** socket family (AF_NETLINK) */ + sa_family_t nl_family; + + /** Padding (unused) */ + unsigned short nl_pad; + + /** Unique process ID */ + uint32_t nl_pid; + + /** Multicast group subscriptions */ + uint32_t nl_groups; +}; + +/** + * Netlink message header + * @ingroup msg + */ +struct nlmsghdr +{ + /** + * Length of message including header. + */ + uint32_t nlmsg_len; + + /** + * Message type (content type) + */ + uint16_t nlmsg_type; + + /** + * Message flags + */ + uint16_t nlmsg_flags; + + /** + * Sequence number + */ + uint32_t nlmsg_seq; + + /** + * Netlink PID of the proccess sending the message. + */ + uint32_t nlmsg_pid; +}; + +/** + * @name Standard message flags + * @{ + */ + +/** + * Must be set on all request messages (typically from user space to + * kernel space). + * @ingroup msg + */ +#define NLM_F_REQUEST 1 + +/** + * Indicates the message is part of a multipart message terminated + * by NLMSG_DONE. + */ +#define NLM_F_MULTI 2 + +/** + * Request for an acknowledgment on success. + */ +#define NLM_F_ACK 4 + +/** + * Echo this request + */ +#define NLM_F_ECHO 8 + +/** @} */ + +/** + * @name Additional message flags for GET requests + * @{ + */ + +/** + * Return the complete table instead of a single entry. + * @ingroup msg + */ +#define NLM_F_ROOT 0x100 + +/** + * Return all entries matching criteria passed in message content. + */ +#define NLM_F_MATCH 0x200 + +/** + * Return an atomic snapshot of the table being referenced. This + * may require special privileges because it has the potential to + * interrupt service in the FE for a longer time. + */ +#define NLM_F_ATOMIC 0x400 + +/** + * Dump all entries + */ +#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH) + +/** @} */ + +/** + * @name Additional messsage flags for NEW requests + * @{ + */ + +/** + * Replace existing matching config object with this request. + * @ingroup msg + */ +#define NLM_F_REPLACE 0x100 + +/** + * Don't replace the config object if it already exists. + */ +#define NLM_F_EXCL 0x200 + +/** + * Create config object if it doesn't already exist. + */ +#define NLM_F_CREATE 0x400 + +/** + * Add to the end of the object list. + */ +#define NLM_F_APPEND 0x800 + +/** @} */ + +/** + * @name Standard Message types + * @{ + */ + +/** + * No operation, message must be ignored + * @ingroup msg + */ +#define NLMSG_NOOP 0x1 + +/** + * The message signals an error and the payload contains a nlmsgerr + * structure. This can be looked at as a NACK and typically it is + * from FEC to CPC. + */ +#define NLMSG_ERROR 0x2 + +/** + * Message terminates a multipart message. + */ +#define NLMSG_DONE 0x3 + +/** + * The message signals that data got lost + */ +#define NLMSG_OVERRUN 0x4 + +/** + * Lower limit of reserved message types + */ +#define NLMSG_MIN_TYPE 0x10 + +/** @} */ + +/** + * Netlink error message + * @ingroup msg + */ +struct nlmsgerr +{ + /** Error code (errno number) */ + int error; + + /** Original netlink message causing the error */ + struct nlmsghdr msg; +}; + +struct nl_pktinfo +{ + __u32 group; +}; + +#endif /* __LINUX_NETLINK_H */ diff --git a/package/libnl-tiny/src/include/netlink/netlink.h b/package/libnl-tiny/src/include/netlink/netlink.h new file mode 100644 index 0000000000..e06fb7f118 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/netlink.h @@ -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 + */ + +#ifndef NETLINK_NETLINK_H_ +#define NETLINK_NETLINK_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int nl_debug; +extern struct nl_dump_params nl_debug_dp; + +/* Connection Management */ +extern int nl_connect(struct nl_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 index 0000000000..8a44fe9012 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/object-api.h @@ -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 + */ + +#ifndef NETLINK_OBJECT_API_H_ +#define NETLINK_OBJECT_API_H_ + +#include +#include + +#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 index 0000000000..87385c48ef --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/object.h @@ -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 + */ + +#ifndef NETLINK_OBJECT_H_ +#define NETLINK_OBJECT_H_ + +#include +#include +#include + +#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 index 0000000000..9f7f4220aa --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/socket.h @@ -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 + */ + +#ifndef NETLINK_SOCKET_H_ +#define NETLINK_SOCKET_H_ + +#include +#include + +#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 index 0000000000..6d8aaf89ee --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/types.h @@ -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 + */ + +#ifndef __NETLINK_TYPES_H_ +#define __NETLINK_TYPES_H_ + +#include + +/** + * 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 index 0000000000..480bab6186 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/utils.h @@ -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 + */ + +#ifndef NETLINK_UTILS_H_ +#define NETLINK_UTILS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @name Probability Constants + * @{ + */ + +/** + * Lower probability limit + * @ingroup utils + */ +#define NL_PROB_MIN 0x0 + +/** + * Upper probability limit + * @ingroup utils + */ +#define NL_PROB_MAX 0xffffffff + +/** @} */ + +/* 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 index 0000000000..6a316d4933 --- /dev/null +++ b/package/libnl-tiny/src/include/netlink/version.h @@ -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 + */ + +#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 index 0000000000..e84c281fc9 --- /dev/null +++ b/package/libnl-tiny/src/msg.c @@ -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 + */ + +/** + * @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 +#include +#include +#include +#include +#include +#include + +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 index 0000000000..99e9aebeba --- /dev/null +++ b/package/libnl-tiny/src/nl.c @@ -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 + */ + +/** + * @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 +#include +#include +#include +#include +#include + +/** + * @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 index 0000000000..1b18d9a7d7 --- /dev/null +++ b/package/libnl-tiny/src/object.c @@ -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 + */ + +/** + * @ingroup cache + * @defgroup object Object + * @{ + */ + +#include +#include +#include +#include +#include + +static inline struct nl_object_ops *obj_ops(struct nl_object *obj) +{ + if (!obj->ce_ops) + BUG(); + + return obj->ce_ops; +} + +/** + * @name Object Creation/Deletion + * @{ + */ + +/** + * Allocate a new object of kind specified by the operations handle + * @arg ops cache operations handle + * @return The new object or NULL + */ +struct nl_object *nl_object_alloc(struct nl_object_ops *ops) +{ + struct nl_object *new; + + if (ops->oo_size < sizeof(*new)) + BUG(); + + new = calloc(1, ops->oo_size); + if (!new) + 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 index 0000000000..6fe56fdc53 --- /dev/null +++ b/package/libnl-tiny/src/socket.c @@ -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 + */ + +/** + * @ingroup core + * @defgroup socket Socket + * @{ + */ + +#include +#include +#include +#include +#include +#include + +static uint32_t used_ports_map[32]; + +static uint32_t generate_local_port(void) +{ + int i, n; + uint32_t pid = getpid() & 0x3FFFFF; + + for (i = 0; i < 32; i++) { + if (used_ports_map[i] == 0xFFFFFFFF) + continue; + + for (n = 0; n < 32; n++) { + if (1UL & (used_ports_map[i] >> n)) + continue; + + used_ports_map[i] |= (1UL << n); + n += (i * 32); + + /* PID_MAX_LIMIT is currently at 2^22, leaving 10 bit + * to, i.e. 1024 unique ports per application. */ + return pid + (n << 22); + + } + } + + /* Out of sockets in our own PID namespace, what to do? FIXME */ + return UINT_MAX; +} + +static void release_local_port(uint32_t port) +{ + int nr; + + 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; +} + +/** @} */ + +/** @} */ diff --git a/package/libnl/Makefile b/package/libnl/Makefile index c763526f8a..64099f0659 100644 --- a/package/libnl/Makefile +++ b/package/libnl/Makefile @@ -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 diff --git a/package/madwifi/Config.in b/package/madwifi/Config.in index 126639552a..e56bfdd31b 100644 --- a/package/madwifi/Config.in +++ b/package/madwifi/Config.in @@ -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 diff --git a/package/madwifi/Makefile b/package/madwifi/Makefile index f787ffb4c2..93a2aa1ac6 100644 --- a/package/madwifi/Makefile +++ b/package/madwifi/Makefile @@ -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:= \ diff --git a/package/madwifi/files/lib/wifi/madwifi.sh b/package/madwifi/files/lib/wifi/madwifi.sh index 8c4a75426e..a3cf4d75e1 100755 --- a/package/madwifi/files/lib/wifi/madwifi.sh +++ b/package/madwifi/files/lib/wifi/madwifi.sh @@ -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 index e290f86ffc..0000000000 --- a/package/madwifi/patches-upstream/104-autocreate_none.patch +++ /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 index cc1fbe6a89..0000000000 --- a/package/madwifi/patches-upstream/111-minstrel_crash.patch +++ /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) diff --git a/package/madwifi/patches/123-ccmp_checks.patch b/package/madwifi/patches/123-ccmp_checks.patch index 71cede7987..d9a0ad4e2b 100644 --- a/package/madwifi/patches/123-ccmp_checks.patch +++ b/package/madwifi/patches/123-ccmp_checks.patch @@ -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; @@ -20,3 +79,17 @@ 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." diff --git a/package/madwifi/patches/351-scanlist.patch b/package/madwifi/patches/351-scanlist.patch index 67e757468b..c11f28e39d 100644 --- a/package/madwifi/patches/351-scanlist.patch +++ b/package/madwifi/patches/351-scanlist.patch @@ -353,6 +353,17 @@ /* 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 { @@ -387,7 +398,19 @@ 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); @@ -519,7 +542,7 @@ /* * 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); } @@ -549,7 +572,7 @@ /* * 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; @@ -558,10 +581,11 @@ 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) && @@ -574,7 +598,7 @@ - } - } - -- 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. */ @@ -609,8 +633,7 @@ - /* 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; @@ -630,7 +653,8 @@ - /* 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; @@ -640,7 +664,7 @@ 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; @@ -651,9 +675,14 @@ - (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 diff --git a/package/madwifi/patches/389-autochannel.patch b/package/madwifi/patches/389-autochannel.patch index 1c62285904..65e03e7381 100644 --- a/package/madwifi/patches/389-autochannel.patch +++ b/package/madwifi/patches/389-autochannel.patch @@ -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 @@ -115,7 +115,7 @@ 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); diff --git a/package/madwifi/patches/411-autochannel_multi.patch b/package/madwifi/patches/411-autochannel_multi.patch index ab48e70b2c..b6093cfcd5 100644 --- a/package/madwifi/patches/411-autochannel_multi.patch +++ b/package/madwifi/patches/411-autochannel_multi.patch @@ -278,7 +278,7 @@ /* 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; @@ -292,9 +292,9 @@ + 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; @@ -307,7 +307,7 @@ } -@@ -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; @@ -315,7 +315,7 @@ 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; @@ -327,7 +327,7 @@ 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 ?? */ @@ -335,7 +335,7 @@ 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 index 0000000000..4530737829 --- /dev/null +++ b/package/madwifi/patches/416-wprobe.patch @@ -0,0 +1,509 @@ +--- /dev/null ++++ b/ath/ath_wprobe.c +@@ -0,0 +1,392 @@ ++#include ++#include ++ ++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 + #include "net80211/ieee80211.h" /* XXX for WME_NUM_AC */ + #include + #include +@@ -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 index 4e13f90d20..0000000000 --- a/package/madwifi/patches/418-turbo.patch +++ /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; diff --git a/package/madwifi/patches/420-diversity_fix.patch b/package/madwifi/patches/420-diversity_fix.patch index b7d26d976d..e12de72ded 100644 --- a/package/madwifi/patches/420-diversity_fix.patch +++ b/package/madwifi/patches/420-diversity_fix.patch @@ -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 */ @@ -28,3 +54,33 @@ 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 index 0000000000..697dfcf99c --- /dev/null +++ b/package/madwifi/patches/421-channel_handling.patch @@ -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 + 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 index 0000000000..33040ae0dd --- /dev/null +++ b/package/madwifi/patches/422-confchange_reset.patch @@ -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 index 0000000000..fe96512e29 --- /dev/null +++ b/package/wprobe/Makefile @@ -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 index 0000000000..3c62a03066 --- /dev/null +++ b/package/wprobe/files/wprobe.init @@ -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 index 0000000000..c8e489a6b5 --- /dev/null +++ b/package/wprobe/src/exporter/Makefile @@ -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 index 0000000000..e9aa3de8c4 --- /dev/null +++ b/package/wprobe/src/exporter/wprobe-export.c @@ -0,0 +1,317 @@ +/* +** exporter.c - example exporter +** +** Copyright Fraunhofer FOKUS +** +*/ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +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 -c [-p portno]\n" + " -h this help\n" + " -i wprobe interface\n" + " -c collector address\n" + " -p 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 index 0000000000..89da9e86e3 --- /dev/null +++ b/package/wprobe/src/exporter/wprobe-export.h @@ -0,0 +1,34 @@ +#include +#include +#include + +/** + * 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 index 0000000000..2a9875311e --- /dev/null +++ b/package/wprobe/src/kernel/Makefile @@ -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 index 0000000000..f145195cd4 --- /dev/null +++ b/package/wprobe/src/kernel/linux/wprobe.h @@ -0,0 +1,298 @@ +/* + * wprobe.h: API for the wireless probe interface + * Copyright (C) 2008-2009 Felix Fietkau + * + * 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 +#include +#include +#include +#include +#include +#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 index 0000000000..798cd7ddec --- /dev/null +++ b/package/wprobe/src/kernel/wprobe-core.c @@ -0,0 +1,669 @@ +/* + * wprobe-core.c: Wireless probe interface core + * Copyright (C) 2008-2009 Felix Fietkau + * + * 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 +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include +#else +#include +#endif +#include +#include +#include + +#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 index 0000000000..4231223e0e --- /dev/null +++ b/package/wprobe/src/kernel/wprobe-dummy.c @@ -0,0 +1,96 @@ +/* + * wprobe-core.c: Wireless probe interface dummy driver + * Copyright (C) 2008-2009 Felix Fietkau + * + * 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 +#include +#include +#include +#include +#include +#include + +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 index 0000000000..e10dfc0d24 --- /dev/null +++ b/package/wprobe/src/user/Makefile @@ -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 index 0000000000..2959a061d3 --- /dev/null +++ b/package/wprobe/src/user/list.h @@ -0,0 +1,601 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include +/** + * 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 index 0000000000..b8918711c6 --- /dev/null +++ b/package/wprobe/src/user/wprobe-info.c @@ -0,0 +1,175 @@ +/* + * wprobe-test.c: Wireless probe user space test code + * Copyright (C) 2008-2009 Felix Fietkau + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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, "", 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 \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 index 0000000000..1f8df6c7e9 --- /dev/null +++ b/package/wprobe/src/user/wprobe.c @@ -0,0 +1,474 @@ +/* + * wprobe.c: Wireless probe user space library + * Copyright (C) 2008-2009 Felix Fietkau + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 index 0000000000..e0c33faef2 --- /dev/null +++ b/package/wprobe/src/user/wprobe.h @@ -0,0 +1,143 @@ +/* + * wprobe.h: Wireless probe user space library + * Copyright (C) 2008-2009 Felix Fietkau + * + * 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 +#include +#include +#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 -- 2.30.2