mirror of https://github.com/hak5/openwrt-owl.git
add libnl-tiny as a small replacement for libnl with only genl support included
SVN-Revision: 15507owl
parent
4e12c9d210
commit
938553bd69
|
@ -0,0 +1,49 @@
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
|
define Build/Compile
|
||||||
|
$(MAKE) -C $(PKG_BUILD_DIR) \
|
||||||
|
$(TARGET_CONFIGURE_OPTS) \
|
||||||
|
CFLAGS="$(TARGET_CFLAGS)" \
|
||||||
|
all
|
||||||
|
endef
|
||||||
|
|
||||||
|
define Build/InstallDev
|
||||||
|
$(INSTALL_DIR) $(1)/usr/lib $(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/
|
||||||
|
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))
|
|
@ -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 $@ $^
|
|
@ -0,0 +1,668 @@
|
||||||
|
/*
|
||||||
|
* lib/attr.c Netlink Attributes
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-local.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
#include <netlink/addr.h>
|
||||||
|
#include <netlink/attr.h>
|
||||||
|
#include <netlink/msg.h>
|
||||||
|
#include <linux/socket.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup msg
|
||||||
|
* @defgroup attr Attributes
|
||||||
|
* Netlink Attributes Construction/Parsing Interface
|
||||||
|
*
|
||||||
|
* \section attr_sec Netlink Attributes
|
||||||
|
* Netlink attributes allow for data chunks of arbitary length to be
|
||||||
|
* attached to a netlink message. Each attribute is encoded with a
|
||||||
|
* type and length field, both 16 bits, stored in the attribute header
|
||||||
|
* preceding the attribute data. The main advantage of using attributes
|
||||||
|
* over packing everything into the family header is that the interface
|
||||||
|
* stays extendable as new attributes can supersede old attributes while
|
||||||
|
* remaining backwards compatible. Also attributes can be defined optional
|
||||||
|
* thus avoiding the transmission of unnecessary empty data blocks.
|
||||||
|
* Special nested attributes allow for more complex data structures to
|
||||||
|
* be transmitted, e.g. trees, lists, etc.
|
||||||
|
*
|
||||||
|
* While not required, netlink attributes typically follow the family
|
||||||
|
* header of a netlink message and must be properly aligned to NLA_ALIGNTO:
|
||||||
|
* @code
|
||||||
|
* +----------------+- - -+---------------+- - -+------------+- - -+
|
||||||
|
* | Netlink Header | Pad | Family Header | Pad | Attributes | Pad |
|
||||||
|
* +----------------+- - -+---------------+- - -+------------+- - -+
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* The actual attributes are chained together each separately aligned to
|
||||||
|
* NLA_ALIGNTO. The position of an attribute is defined based on the
|
||||||
|
* length field of the preceding attributes:
|
||||||
|
* @code
|
||||||
|
* +-------------+- - -+-------------+- - -+------
|
||||||
|
* | Attribute 1 | Pad | Attribute 2 | Pad | ...
|
||||||
|
* +-------------+- - -+-------------+- - -+------
|
||||||
|
* nla_next(attr1)------^
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* The attribute itself consists of the attribute header followed by
|
||||||
|
* the actual payload also aligned to NLA_ALIGNTO. The function nla_data()
|
||||||
|
* returns a pointer to the start of the payload while nla_len() returns
|
||||||
|
* the length of the payload in bytes.
|
||||||
|
*
|
||||||
|
* \b Note: Be aware, NLA_ALIGNTO equals to 4 bytes, therefore it is not
|
||||||
|
* safe to dereference any 64 bit data types directly.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* <----------- nla_total_size(payload) ----------->
|
||||||
|
* <-------- nla_attr_size(payload) --------->
|
||||||
|
* +------------------+- - -+- - - - - - - - - +- - -+
|
||||||
|
* | Attribute Header | Pad | Payload | Pad |
|
||||||
|
* +------------------+- - -+- - - - - - - - - +- - -+
|
||||||
|
* nla_data(nla)-------------^
|
||||||
|
* <- nla_len(nla) ->
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @subsection attr_datatypes Attribute Data Types
|
||||||
|
* A number of basic data types are supported to simplify access and
|
||||||
|
* validation of netlink attributes. This data type information is
|
||||||
|
* not encoded in the attribute, both the kernel and userspace part
|
||||||
|
* are required to share this information on their own.
|
||||||
|
*
|
||||||
|
* One of the major advantages of these basic types is the automatic
|
||||||
|
* validation of each attribute based on an attribute policy. The
|
||||||
|
* validation covers most of the checks required to safely use
|
||||||
|
* attributes and thus keeps the individual sanity check to a minimum.
|
||||||
|
*
|
||||||
|
* Never access attribute payload without ensuring basic validation
|
||||||
|
* first, attributes may:
|
||||||
|
* - not be present even though required
|
||||||
|
* - contain less actual payload than expected
|
||||||
|
* - fake a attribute length which exceeds the end of the message
|
||||||
|
* - contain unterminated character strings
|
||||||
|
*
|
||||||
|
* Policies are defined as array of the struct nla_policy. The array is
|
||||||
|
* indexed with the attribute type, therefore the array must be sized
|
||||||
|
* accordingly.
|
||||||
|
* @code
|
||||||
|
* static struct nla_policy my_policy[ATTR_MAX+1] = {
|
||||||
|
* [ATTR_FOO] = { .type = ..., .minlen = ..., .maxlen = ... },
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* err = nla_validate(attrs, attrlen, ATTR_MAX, &my_policy);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* Some basic validations are performed on every attribute, regardless of type.
|
||||||
|
* - If the attribute type exceeds the maximum attribute type specified or
|
||||||
|
* the attribute type is lesser-or-equal than zero, the attribute will
|
||||||
|
* be silently ignored.
|
||||||
|
* - If the payload length falls below the \a minlen value the attribute
|
||||||
|
* will be rejected.
|
||||||
|
* - If \a maxlen is non-zero and the payload length exceeds the \a maxlen
|
||||||
|
* value the attribute will be rejected.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @par Unspecific Attribute (NLA_UNSPEC)
|
||||||
|
* This is the standard type if no type is specified. It is used for
|
||||||
|
* binary data of arbitary length. Typically this attribute carries
|
||||||
|
* a binary structure or a stream of bytes.
|
||||||
|
* @par
|
||||||
|
* @code
|
||||||
|
* // In this example, we will assume a binary structure requires to
|
||||||
|
* // be transmitted. The definition of the structure will typically
|
||||||
|
* // go into a header file available to both the kernel and userspace
|
||||||
|
* // side.
|
||||||
|
* //
|
||||||
|
* // Note: Be careful when putting 64 bit data types into a structure.
|
||||||
|
* // The attribute payload is only aligned to 4 bytes, dereferencing
|
||||||
|
* // the member may fail.
|
||||||
|
* struct my_struct {
|
||||||
|
* int a;
|
||||||
|
* int b;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* // The validation function will not enforce an exact length match to
|
||||||
|
* // allow structures to grow as required. Note: While it is allowed
|
||||||
|
* // to add members to the end of the structure, changing the order or
|
||||||
|
* // inserting members in the middle of the structure will break your
|
||||||
|
* // binary interface.
|
||||||
|
* static struct nla_policy my_policy[ATTR_MAX+1] = {
|
||||||
|
* [ATTR_MY_STRICT] = { .type = NLA_UNSPEC,
|
||||||
|
* .minlen = sizeof(struct my_struct) },
|
||||||
|
*
|
||||||
|
* // The binary structure is appened to the message using nla_put()
|
||||||
|
* struct my_struct foo = { .a = 1, .b = 2 };
|
||||||
|
* nla_put(msg, ATTR_MY_STRUCT, sizeof(foo), &foo);
|
||||||
|
*
|
||||||
|
* // On the receiving side, a pointer to the structure pointing inside
|
||||||
|
* // the message payload is returned by nla_get().
|
||||||
|
* if (attrs[ATTR_MY_STRUCT])
|
||||||
|
* struct my_struct *foo = nla_get(attrs[ATTR_MY_STRUCT]);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par Integers (NLA_U8, NLA_U16, NLA_U32, NLA_U64)
|
||||||
|
* Integers come in different sizes from 8 bit to 64 bit. However, since the
|
||||||
|
* payload length is aligned to 4 bytes, integers smaller than 32 bit are
|
||||||
|
* only useful to enforce the maximum range of values.
|
||||||
|
* @par
|
||||||
|
* \b Note: There is no difference made between signed and unsigned integers.
|
||||||
|
* The validation only enforces the minimal payload length required to store
|
||||||
|
* an integer of specified type.
|
||||||
|
* @par
|
||||||
|
* @code
|
||||||
|
* // Even though possible, it does not make sense to specify .minlen or
|
||||||
|
* // .maxlen for integer types. The data types implies the corresponding
|
||||||
|
* // minimal payload length.
|
||||||
|
* static struct nla_policy my_policy[ATTR_MAX+1] = {
|
||||||
|
* [ATTR_FOO] = { .type = NLA_U32 },
|
||||||
|
*
|
||||||
|
* // Numeric values can be appended directly using the respective
|
||||||
|
* // nla_put_uxxx() function
|
||||||
|
* nla_put_u32(msg, ATTR_FOO, 123);
|
||||||
|
*
|
||||||
|
* // Same for the receiving side.
|
||||||
|
* if (attrs[ATTR_FOO])
|
||||||
|
* uint32_t foo = nla_get_u32(attrs[ATTR_FOO]);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par Character string (NLA_STRING)
|
||||||
|
* This data type represents a NUL terminated character string of variable
|
||||||
|
* length. For binary data streams the type NLA_UNSPEC is recommended.
|
||||||
|
* @par
|
||||||
|
* @code
|
||||||
|
* // Enforce a NUL terminated character string of at most 4 characters
|
||||||
|
* // including the NUL termination.
|
||||||
|
* static struct nla_policy my_policy[ATTR_MAX+1] = {
|
||||||
|
* [ATTR_BAR] = { .type = NLA_STRING, maxlen = 4 },
|
||||||
|
*
|
||||||
|
* // nla_put_string() creates a string attribute of the necessary length
|
||||||
|
* // and appends it to the message including the NUL termination.
|
||||||
|
* nla_put_string(msg, ATTR_BAR, "some text");
|
||||||
|
*
|
||||||
|
* // It is safe to use the returned character string directly if the
|
||||||
|
* // attribute has been validated as the validation enforces the proper
|
||||||
|
* // termination of the string.
|
||||||
|
* if (attrs[ATTR_BAR])
|
||||||
|
* char *text = nla_get_string(attrs[ATTR_BAR]);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par Flag (NLA_FLAG)
|
||||||
|
* This attribute type may be used to indicate the presence of a flag. The
|
||||||
|
* attribute is only valid if the payload length is zero. The presence of
|
||||||
|
* the attribute header indicates the presence of the flag.
|
||||||
|
* @par
|
||||||
|
* @code
|
||||||
|
* // This attribute type is special as .minlen and .maxlen have no effect.
|
||||||
|
* static struct nla_policy my_policy[ATTR_MAX+1] = {
|
||||||
|
* [ATTR_FLAG] = { .type = NLA_FLAG },
|
||||||
|
*
|
||||||
|
* // nla_put_flag() appends a zero sized attribute to the message.
|
||||||
|
* nla_put_flag(msg, ATTR_FLAG);
|
||||||
|
*
|
||||||
|
* // There is no need for a receival function, the presence is the value.
|
||||||
|
* if (attrs[ATTR_FLAG])
|
||||||
|
* // flag is present
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par Micro Seconds (NLA_MSECS)
|
||||||
|
*
|
||||||
|
* @par Nested Attribute (NLA_NESTED)
|
||||||
|
* Attributes can be nested and put into a container to create groups, lists
|
||||||
|
* or to construct trees of attributes. Nested attributes are often used to
|
||||||
|
* pass attributes to a subsystem where the top layer has no knowledge of the
|
||||||
|
* configuration possibilities of each subsystem.
|
||||||
|
* @par
|
||||||
|
* \b Note: When validating the attributes using nlmsg_validate() or
|
||||||
|
* nlmsg_parse() it will only affect the top level attributes. Each
|
||||||
|
* level of nested attributes must be validated seperately using
|
||||||
|
* nla_parse_nested() or nla_validate().
|
||||||
|
* @par
|
||||||
|
* @code
|
||||||
|
* // The minimal length policy may be used to enforce the presence of at
|
||||||
|
* // least one attribute.
|
||||||
|
* static struct nla_policy my_policy[ATTR_MAX+1] = {
|
||||||
|
* [ATTR_OPTS] = { .type = NLA_NESTED, minlen = NLA_HDRLEN },
|
||||||
|
*
|
||||||
|
* // Nested attributes are constructed by enclosing the attributes
|
||||||
|
* // to be nested with calls to nla_nest_start() respetively nla_nest_end().
|
||||||
|
* struct nlattr *opts = nla_nest_start(msg, ATTR_OPTS);
|
||||||
|
* nla_put_u32(msg, ATTR_FOO, 123);
|
||||||
|
* nla_put_string(msg, ATTR_BAR, "some text");
|
||||||
|
* nla_nest_end(msg, opts);
|
||||||
|
*
|
||||||
|
* // Various methods exist to parse nested attributes, the easiest being
|
||||||
|
* // nla_parse_nested() which also allows validation in the same step.
|
||||||
|
* if (attrs[ATTR_OPTS]) {
|
||||||
|
* struct nlattr *nested[ATTR_MAX+1];
|
||||||
|
*
|
||||||
|
* nla_parse_nested(nested, ATTR_MAX, attrs[ATTR_OPTS], &policy);
|
||||||
|
*
|
||||||
|
* if (nested[ATTR_FOO])
|
||||||
|
* uint32_t foo = nla_get_u32(nested[ATTR_FOO]);
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @subsection attr_exceptions Exception Based Attribute Construction
|
||||||
|
* Often a large number of attributes are added to a message in a single
|
||||||
|
* function. In order to simplify error handling, a second set of
|
||||||
|
* construction functions exist which jump to a error label when they
|
||||||
|
* fail instead of returning an error code. This second set consists
|
||||||
|
* of macros which are named after their error code based counterpart
|
||||||
|
* except that the name is written all uppercase.
|
||||||
|
*
|
||||||
|
* All of the macros jump to the target \c nla_put_failure if they fail.
|
||||||
|
* @code
|
||||||
|
* void my_func(struct nl_msg *msg)
|
||||||
|
* {
|
||||||
|
* NLA_PUT_U32(msg, ATTR_FOO, 10);
|
||||||
|
* NLA_PUT_STRING(msg, ATTR_BAR, "bar");
|
||||||
|
*
|
||||||
|
* return 0;
|
||||||
|
*
|
||||||
|
* nla_put_failure:
|
||||||
|
* return -NLE_NOMEM;
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @subsection attr_examples Examples
|
||||||
|
* @par Example 1.1 Constructing a netlink message with attributes.
|
||||||
|
* @code
|
||||||
|
* struct nl_msg *build_msg(int ifindex, struct nl_addr *lladdr, int mtu)
|
||||||
|
* {
|
||||||
|
* struct nl_msg *msg;
|
||||||
|
* struct nlattr *info, *vlan;
|
||||||
|
* struct ifinfomsg ifi = {
|
||||||
|
* .ifi_family = AF_INET,
|
||||||
|
* .ifi_index = ifindex,
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* // Allocate a new netlink message, type=RTM_SETLINK, flags=NLM_F_ECHO
|
||||||
|
* if (!(msg = nlmsg_alloc_simple(RTM_SETLINK, NLM_F_ECHO)))
|
||||||
|
* return NULL;
|
||||||
|
*
|
||||||
|
* // Append the family specific header (struct ifinfomsg)
|
||||||
|
* if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
|
||||||
|
* goto nla_put_failure
|
||||||
|
*
|
||||||
|
* // Append a 32 bit integer attribute to carry the MTU
|
||||||
|
* NLA_PUT_U32(msg, IFLA_MTU, mtu);
|
||||||
|
*
|
||||||
|
* // Append a unspecific attribute to carry the link layer address
|
||||||
|
* NLA_PUT_ADDR(msg, IFLA_ADDRESS, lladdr);
|
||||||
|
*
|
||||||
|
* // Append a container for nested attributes to carry link information
|
||||||
|
* if (!(info = nla_nest_start(msg, IFLA_LINKINFO)))
|
||||||
|
* goto nla_put_failure;
|
||||||
|
*
|
||||||
|
* // Put a string attribute into the container
|
||||||
|
* NLA_PUT_STRING(msg, IFLA_INFO_KIND, "vlan");
|
||||||
|
*
|
||||||
|
* // Append another container inside the open container to carry
|
||||||
|
* // vlan specific attributes
|
||||||
|
* if (!(vlan = nla_nest_start(msg, IFLA_INFO_DATA)))
|
||||||
|
* goto nla_put_failure;
|
||||||
|
*
|
||||||
|
* // add vlan specific info attributes here...
|
||||||
|
*
|
||||||
|
* // Finish nesting the vlan attributes and close the second container.
|
||||||
|
* nla_nest_end(msg, vlan);
|
||||||
|
*
|
||||||
|
* // Finish nesting the link info attribute and close the first container.
|
||||||
|
* nla_nest_end(msg, info);
|
||||||
|
*
|
||||||
|
* return msg;
|
||||||
|
*
|
||||||
|
* // If any of the construction macros fails, we end up here.
|
||||||
|
* nla_put_failure:
|
||||||
|
* nlmsg_free(msg);
|
||||||
|
* return NULL;
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par Example 2.1 Parsing a netlink message with attributes.
|
||||||
|
* @code
|
||||||
|
* int parse_message(struct nl_msg *msg)
|
||||||
|
* {
|
||||||
|
* // The policy defines two attributes: a 32 bit integer and a container
|
||||||
|
* // for nested attributes.
|
||||||
|
* struct nla_policy attr_policy[ATTR_MAX+1] = {
|
||||||
|
* [ATTR_FOO] = { .type = NLA_U32 },
|
||||||
|
* [ATTR_BAR] = { .type = NLA_NESTED },
|
||||||
|
* };
|
||||||
|
* struct nlattr *attrs[ATTR_MAX+1];
|
||||||
|
* int err;
|
||||||
|
*
|
||||||
|
* // The nlmsg_parse() function will make sure that the message contains
|
||||||
|
* // enough payload to hold the header (struct my_hdr), validates any
|
||||||
|
* // attributes attached to the messages and stores a pointer to each
|
||||||
|
* // attribute in the attrs[] array accessable by attribute type.
|
||||||
|
* if ((err = nlmsg_parse(nlmsg_hdr(msg), sizeof(struct my_hdr), attrs,
|
||||||
|
* ATTR_MAX, attr_policy)) < 0)
|
||||||
|
* goto errout;
|
||||||
|
*
|
||||||
|
* if (attrs[ATTR_FOO]) {
|
||||||
|
* // It is safe to directly access the attribute payload without
|
||||||
|
* // any further checks since nlmsg_parse() enforced the policy.
|
||||||
|
* uint32_t foo = nla_get_u32(attrs[ATTR_FOO]);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* if (attrs[ATTR_BAR]) {
|
||||||
|
* struct nlattr *nested[NESTED_MAX+1];
|
||||||
|
*
|
||||||
|
* // Attributes nested in a container can be parsed the same way
|
||||||
|
* // as top level attributes.
|
||||||
|
* if ((err = nla_parse_nested(nested, NESTED_MAX, attrs[ATTR_BAR],
|
||||||
|
* nested_policy)) < 0)
|
||||||
|
* goto errout;
|
||||||
|
*
|
||||||
|
* // Process nested attributes here.
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* err = 0;
|
||||||
|
* errout:
|
||||||
|
* return err;
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Attribute Size Calculation
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Parsing Attributes
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the attribute header and payload can be accessed safely.
|
||||||
|
* @arg nla Attribute of any kind.
|
||||||
|
* @arg remaining Number of bytes remaining in attribute stream.
|
||||||
|
*
|
||||||
|
* Verifies that the header and payload do not exceed the number of
|
||||||
|
* bytes left in the attribute stream. This function must be called
|
||||||
|
* before access the attribute header or payload when iterating over
|
||||||
|
* the attribute stream using nla_next().
|
||||||
|
*
|
||||||
|
* @return True if the attribute can be accessed safely, false otherwise.
|
||||||
|
*/
|
||||||
|
int nla_ok(const struct nlattr *nla, int remaining)
|
||||||
|
{
|
||||||
|
return remaining >= sizeof(*nla) &&
|
||||||
|
nla->nla_len >= sizeof(*nla) &&
|
||||||
|
nla->nla_len <= remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return next attribute in a stream of attributes.
|
||||||
|
* @arg nla Attribute of any kind.
|
||||||
|
* @arg remaining Variable to count remaining bytes in stream.
|
||||||
|
*
|
||||||
|
* Calculates the offset to the next attribute based on the attribute
|
||||||
|
* given. The attribute provided is assumed to be accessible, the
|
||||||
|
* caller is responsible to use nla_ok() beforehand. The offset (length
|
||||||
|
* of specified attribute including padding) is then subtracted from
|
||||||
|
* the remaining bytes variable and a pointer to the next attribute is
|
||||||
|
* returned.
|
||||||
|
*
|
||||||
|
* nla_next() can be called as long as remainig is >0.
|
||||||
|
*
|
||||||
|
* @return Pointer to next attribute.
|
||||||
|
*/
|
||||||
|
struct nlattr *nla_next(const struct nlattr *nla, int *remaining)
|
||||||
|
{
|
||||||
|
int totlen = NLA_ALIGN(nla->nla_len);
|
||||||
|
|
||||||
|
*remaining -= totlen;
|
||||||
|
return (struct nlattr *) ((char *) nla + totlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t nla_attr_minlen[NLA_TYPE_MAX+1] = {
|
||||||
|
[NLA_U8] = sizeof(uint8_t),
|
||||||
|
[NLA_U16] = sizeof(uint16_t),
|
||||||
|
[NLA_U32] = sizeof(uint32_t),
|
||||||
|
[NLA_U64] = sizeof(uint64_t),
|
||||||
|
[NLA_STRING] = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int validate_nla(struct nlattr *nla, int maxtype,
|
||||||
|
struct nla_policy *policy)
|
||||||
|
{
|
||||||
|
struct nla_policy *pt;
|
||||||
|
int minlen = 0, type = nla_type(nla);
|
||||||
|
|
||||||
|
if (type <= 0 || type > maxtype)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
pt = &policy[type];
|
||||||
|
|
||||||
|
if (pt->type > NLA_TYPE_MAX)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
if (pt->minlen)
|
||||||
|
minlen = pt->minlen;
|
||||||
|
else if (pt->type != NLA_UNSPEC)
|
||||||
|
minlen = nla_attr_minlen[pt->type];
|
||||||
|
|
||||||
|
if (pt->type == NLA_FLAG && nla_len(nla) > 0)
|
||||||
|
return -NLE_RANGE;
|
||||||
|
|
||||||
|
if (nla_len(nla) < minlen)
|
||||||
|
return -NLE_RANGE;
|
||||||
|
|
||||||
|
if (pt->maxlen && nla_len(nla) > pt->maxlen)
|
||||||
|
return -NLE_RANGE;
|
||||||
|
|
||||||
|
if (pt->type == NLA_STRING) {
|
||||||
|
char *data = nla_data(nla);
|
||||||
|
if (data[nla_len(nla) - 1] != '\0')
|
||||||
|
return -NLE_INVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create attribute index based on a stream of attributes.
|
||||||
|
* @arg tb Index array to be filled (maxtype+1 elements).
|
||||||
|
* @arg maxtype Maximum attribute type expected and accepted.
|
||||||
|
* @arg head Head of attribute stream.
|
||||||
|
* @arg len Length of attribute stream.
|
||||||
|
* @arg policy Attribute validation policy.
|
||||||
|
*
|
||||||
|
* Iterates over the stream of attributes and stores a pointer to each
|
||||||
|
* attribute in the index array using the attribute type as index to
|
||||||
|
* the array. Attribute with a type greater than the maximum type
|
||||||
|
* specified will be silently ignored in order to maintain backwards
|
||||||
|
* compatibility. If \a policy is not NULL, the attribute will be
|
||||||
|
* validated using the specified policy.
|
||||||
|
*
|
||||||
|
* @see nla_validate
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head, int len,
|
||||||
|
struct nla_policy *policy)
|
||||||
|
{
|
||||||
|
struct nlattr *nla;
|
||||||
|
int rem, err;
|
||||||
|
|
||||||
|
memset(tb, 0, sizeof(struct nlattr *) * (maxtype + 1));
|
||||||
|
|
||||||
|
nla_for_each_attr(nla, head, len, rem) {
|
||||||
|
int type = nla_type(nla);
|
||||||
|
|
||||||
|
if (type == 0) {
|
||||||
|
fprintf(stderr, "Illegal nla->nla_type == 0\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type <= maxtype) {
|
||||||
|
if (policy) {
|
||||||
|
err = validate_nla(nla, maxtype, policy);
|
||||||
|
if (err < 0)
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
tb[type] = nla;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rem > 0)
|
||||||
|
fprintf(stderr, "netlink: %d bytes leftover after parsing "
|
||||||
|
"attributes.\n", rem);
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
errout:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate a stream of attributes.
|
||||||
|
* @arg head Head of attributes stream.
|
||||||
|
* @arg len Length of attributes stream.
|
||||||
|
* @arg maxtype Maximum attribute type expected and accepted.
|
||||||
|
* @arg policy Validation policy.
|
||||||
|
*
|
||||||
|
* Iterates over the stream of attributes and validates each attribute
|
||||||
|
* one by one using the specified policy. Attributes with a type greater
|
||||||
|
* than the maximum type specified will be silently ignored in order to
|
||||||
|
* maintain backwards compatibility.
|
||||||
|
*
|
||||||
|
* See \ref attr_datatypes for more details on what kind of validation
|
||||||
|
* checks are performed on each attribute data type.
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int nla_validate(struct nlattr *head, int len, int maxtype,
|
||||||
|
struct nla_policy *policy)
|
||||||
|
{
|
||||||
|
struct nlattr *nla;
|
||||||
|
int rem, err;
|
||||||
|
|
||||||
|
nla_for_each_attr(nla, head, len, rem) {
|
||||||
|
err = validate_nla(nla, maxtype, policy);
|
||||||
|
if (err < 0)
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
errout:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a single attribute in a stream of attributes.
|
||||||
|
* @arg head Head of attributes stream.
|
||||||
|
* @arg len Length of attributes stream.
|
||||||
|
* @arg attrtype Attribute type to look for.
|
||||||
|
*
|
||||||
|
* Iterates over the stream of attributes and compares each type with
|
||||||
|
* the type specified. Returns the first attribute which matches the
|
||||||
|
* type.
|
||||||
|
*
|
||||||
|
* @return Pointer to attribute found or NULL.
|
||||||
|
*/
|
||||||
|
struct nlattr *nla_find(struct nlattr *head, int len, int attrtype)
|
||||||
|
{
|
||||||
|
struct nlattr *nla;
|
||||||
|
int rem;
|
||||||
|
|
||||||
|
nla_for_each_attr(nla, head, len, rem)
|
||||||
|
if (nla_type(nla) == attrtype)
|
||||||
|
return nla;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Unspecific Attribute
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserve space for a attribute.
|
||||||
|
* @arg msg Netlink Message.
|
||||||
|
* @arg attrtype Attribute Type.
|
||||||
|
* @arg attrlen Length of payload.
|
||||||
|
*
|
||||||
|
* Reserves room for a attribute in the specified netlink message and
|
||||||
|
* fills in the attribute header (type, length). Returns NULL if there
|
||||||
|
* is unsuficient space for the attribute.
|
||||||
|
*
|
||||||
|
* Any padding between payload and the start of the next attribute is
|
||||||
|
* zeroed out.
|
||||||
|
*
|
||||||
|
* @return Pointer to start of attribute or NULL on failure.
|
||||||
|
*/
|
||||||
|
struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int attrlen)
|
||||||
|
{
|
||||||
|
struct nlattr *nla;
|
||||||
|
int tlen;
|
||||||
|
|
||||||
|
tlen = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) + nla_total_size(attrlen);
|
||||||
|
|
||||||
|
if ((tlen + msg->nm_nlh->nlmsg_len) > msg->nm_size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
nla = (struct nlattr *) nlmsg_tail(msg->nm_nlh);
|
||||||
|
nla->nla_type = attrtype;
|
||||||
|
nla->nla_len = nla_attr_size(attrlen);
|
||||||
|
|
||||||
|
memset((unsigned char *) nla + nla->nla_len, 0, nla_padlen(attrlen));
|
||||||
|
msg->nm_nlh->nlmsg_len = tlen;
|
||||||
|
|
||||||
|
NL_DBG(2, "msg %p: Reserved %d bytes at offset +%td for attr %d "
|
||||||
|
"nlmsg_len=%d\n", msg, attrlen,
|
||||||
|
(void *) nla - nlmsg_data(msg->nm_nlh),
|
||||||
|
attrtype, msg->nm_nlh->nlmsg_len);
|
||||||
|
|
||||||
|
return nla;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a unspecific attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg datalen Length of data to be used as payload.
|
||||||
|
* @arg data Pointer to data to be used as attribute payload.
|
||||||
|
*
|
||||||
|
* Reserves room for a unspecific attribute and copies the provided data
|
||||||
|
* into the message as payload of the attribute. Returns an error if there
|
||||||
|
* is insufficient space for the attribute.
|
||||||
|
*
|
||||||
|
* @see nla_reserve
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data)
|
||||||
|
{
|
||||||
|
struct nlattr *nla;
|
||||||
|
|
||||||
|
nla = nla_reserve(msg, attrtype, datalen);
|
||||||
|
if (!nla)
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
memcpy(nla_data(nla), data, datalen);
|
||||||
|
NL_DBG(2, "msg %p: Wrote %d bytes at offset +%td for attr %d\n",
|
||||||
|
msg, datalen, (void *) nla - nlmsg_data(msg->nm_nlh), attrtype);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** @} */
|
|
@ -0,0 +1,841 @@
|
||||||
|
/*
|
||||||
|
* lib/cache.c Caching Module
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup cache_mngt
|
||||||
|
* @defgroup cache Cache
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* Cache Management | | Type Specific Cache Operations
|
||||||
|
*
|
||||||
|
* | | +----------------+ +------------+
|
||||||
|
* | request update | | msg_parser |
|
||||||
|
* | | +----------------+ +------------+
|
||||||
|
* +- - - - -^- - - - - - - -^- -|- - - -
|
||||||
|
* nl_cache_update: | | | |
|
||||||
|
* 1) --------- co_request_update ------+ | |
|
||||||
|
* | | |
|
||||||
|
* 2) destroy old cache +----------- pp_cb ---------|---+
|
||||||
|
* | | |
|
||||||
|
* 3) ---------- nl_recvmsgs ----------+ +- cb_valid -+
|
||||||
|
* +--------------+ | | | |
|
||||||
|
* | nl_cache_add |<-----+ + - - -v- -|- - - - - - - - - - -
|
||||||
|
* +--------------+ | | +-------------+
|
||||||
|
* | nl_recvmsgs |
|
||||||
|
* | | +-----|-^-----+
|
||||||
|
* +---v-|---+
|
||||||
|
* | | | nl_recv |
|
||||||
|
* +---------+
|
||||||
|
* | | Core Netlink
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-local.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/cache.h>
|
||||||
|
#include <netlink/object.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Access Functions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef disabled
|
||||||
|
/**
|
||||||
|
* Return the number of items in the cache
|
||||||
|
* @arg cache cache handle
|
||||||
|
*/
|
||||||
|
int nl_cache_nitems(struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
return cache->c_nitems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of items matching a filter in the cache
|
||||||
|
* @arg cache Cache object.
|
||||||
|
* @arg filter Filter object.
|
||||||
|
*/
|
||||||
|
int nl_cache_nitems_filter(struct nl_cache *cache, struct nl_object *filter)
|
||||||
|
{
|
||||||
|
struct nl_object_ops *ops;
|
||||||
|
struct nl_object *obj;
|
||||||
|
int nitems = 0;
|
||||||
|
|
||||||
|
if (cache->c_ops == NULL)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
ops = cache->c_ops->co_obj_ops;
|
||||||
|
|
||||||
|
nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
|
||||||
|
if (filter && !nl_object_match_filter(obj, filter))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
nitems++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nitems;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns \b true if the cache is empty.
|
||||||
|
* @arg cache Cache to check
|
||||||
|
* @return \a true if the cache is empty, otherwise \b false is returned.
|
||||||
|
*/
|
||||||
|
int nl_cache_is_empty(struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
return nl_list_empty(&cache->c_items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the operations set of the cache
|
||||||
|
* @arg cache cache handle
|
||||||
|
*/
|
||||||
|
struct nl_cache_ops *nl_cache_get_ops(struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
return cache->c_ops;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the first element in the cache
|
||||||
|
* @arg cache cache handle
|
||||||
|
*/
|
||||||
|
struct nl_object *nl_cache_get_first(struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
if (nl_list_empty(&cache->c_items))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return nl_list_entry(cache->c_items.next,
|
||||||
|
struct nl_object, ce_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the last element in the cache
|
||||||
|
* @arg cache cache handle
|
||||||
|
*/
|
||||||
|
struct nl_object *nl_cache_get_last(struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
if (nl_list_empty(&cache->c_items))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return nl_list_entry(cache->c_items.prev,
|
||||||
|
struct nl_object, ce_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the next element in the cache
|
||||||
|
* @arg obj current object
|
||||||
|
*/
|
||||||
|
struct nl_object *nl_cache_get_next(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
if (nl_list_at_tail(obj, &obj->ce_cache->c_items, ce_list))
|
||||||
|
return NULL;
|
||||||
|
else
|
||||||
|
return nl_list_entry(obj->ce_list.next,
|
||||||
|
struct nl_object, ce_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the previous element in the cache
|
||||||
|
* @arg obj current object
|
||||||
|
*/
|
||||||
|
struct nl_object *nl_cache_get_prev(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
if (nl_list_at_head(obj, &obj->ce_cache->c_items, ce_list))
|
||||||
|
return NULL;
|
||||||
|
else
|
||||||
|
return nl_list_entry(obj->ce_list.prev,
|
||||||
|
struct nl_object, ce_list);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Cache Creation/Deletion
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate an empty cache
|
||||||
|
* @arg ops cache operations to base the cache on
|
||||||
|
*
|
||||||
|
* @return A newly allocated and initialized cache.
|
||||||
|
*/
|
||||||
|
struct nl_cache *nl_cache_alloc(struct nl_cache_ops *ops)
|
||||||
|
{
|
||||||
|
struct nl_cache *cache;
|
||||||
|
|
||||||
|
cache = calloc(1, sizeof(*cache));
|
||||||
|
if (!cache)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
nl_init_list_head(&cache->c_items);
|
||||||
|
cache->c_ops = ops;
|
||||||
|
|
||||||
|
NL_DBG(2, "Allocated cache %p <%s>.\n", cache, nl_cache_name(cache));
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nl_cache_alloc_and_fill(struct nl_cache_ops *ops, struct nl_sock *sock,
|
||||||
|
struct nl_cache **result)
|
||||||
|
{
|
||||||
|
struct nl_cache *cache;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!(cache = nl_cache_alloc(ops)))
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
if (sock && (err = nl_cache_refill(sock, cache)) < 0) {
|
||||||
|
nl_cache_free(cache);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
*result = cache;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef disabled
|
||||||
|
/**
|
||||||
|
* Allocate an empty cache based on type name
|
||||||
|
* @arg kind Name of cache type
|
||||||
|
* @return A newly allocated and initialized cache.
|
||||||
|
*/
|
||||||
|
int nl_cache_alloc_name(const char *kind, struct nl_cache **result)
|
||||||
|
{
|
||||||
|
struct nl_cache_ops *ops;
|
||||||
|
struct nl_cache *cache;
|
||||||
|
|
||||||
|
ops = nl_cache_ops_lookup(kind);
|
||||||
|
if (!ops)
|
||||||
|
return -NLE_NOCACHE;
|
||||||
|
|
||||||
|
if (!(cache = nl_cache_alloc(ops)))
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
*result = cache;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new cache containing a subset of a cache
|
||||||
|
* @arg orig Original cache to be based on
|
||||||
|
* @arg filter Filter defining the subset to be filled into new cache
|
||||||
|
* @return A newly allocated cache or NULL.
|
||||||
|
*/
|
||||||
|
struct nl_cache *nl_cache_subset(struct nl_cache *orig,
|
||||||
|
struct nl_object *filter)
|
||||||
|
{
|
||||||
|
struct nl_cache *cache;
|
||||||
|
struct nl_object_ops *ops;
|
||||||
|
struct nl_object *obj;
|
||||||
|
|
||||||
|
if (!filter)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
cache = nl_cache_alloc(orig->c_ops);
|
||||||
|
if (!cache)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ops = orig->c_ops->co_obj_ops;
|
||||||
|
|
||||||
|
nl_list_for_each_entry(obj, &orig->c_items, ce_list) {
|
||||||
|
if (!nl_object_match_filter(obj, filter))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
nl_cache_add(cache, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear a cache.
|
||||||
|
* @arg cache cache to clear
|
||||||
|
*
|
||||||
|
* Removes all elements of a cache.
|
||||||
|
*/
|
||||||
|
void nl_cache_clear(struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
struct nl_object *obj, *tmp;
|
||||||
|
|
||||||
|
NL_DBG(1, "Clearing cache %p <%s>...\n", cache, nl_cache_name(cache));
|
||||||
|
|
||||||
|
nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list)
|
||||||
|
nl_cache_remove(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a cache.
|
||||||
|
* @arg cache Cache to free.
|
||||||
|
*
|
||||||
|
* Removes all elements of a cache and frees all memory.
|
||||||
|
*
|
||||||
|
* @note Use this function if you are working with allocated caches.
|
||||||
|
*/
|
||||||
|
void nl_cache_free(struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
if (!cache)
|
||||||
|
return;
|
||||||
|
|
||||||
|
nl_cache_clear(cache);
|
||||||
|
NL_DBG(1, "Freeing cache %p <%s>...\n", cache, nl_cache_name(cache));
|
||||||
|
free(cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Cache Modifications
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int __cache_add(struct nl_cache *cache, struct nl_object *obj)
|
||||||
|
{
|
||||||
|
obj->ce_cache = cache;
|
||||||
|
|
||||||
|
nl_list_add_tail(&obj->ce_list, &cache->c_items);
|
||||||
|
cache->c_nitems++;
|
||||||
|
|
||||||
|
NL_DBG(1, "Added %p to cache %p <%s>.\n",
|
||||||
|
obj, cache, nl_cache_name(cache));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add object to a cache.
|
||||||
|
* @arg cache Cache to add object to
|
||||||
|
* @arg obj Object to be added to the cache
|
||||||
|
*
|
||||||
|
* Adds the given object to the specified cache. The object is cloned
|
||||||
|
* if it has been added to another cache already.
|
||||||
|
*
|
||||||
|
* @return 0 or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_cache_add(struct nl_cache *cache, struct nl_object *obj)
|
||||||
|
{
|
||||||
|
struct nl_object *new;
|
||||||
|
|
||||||
|
if (cache->c_ops->co_obj_ops != obj->ce_ops)
|
||||||
|
return -NLE_OBJ_MISMATCH;
|
||||||
|
|
||||||
|
if (!nl_list_empty(&obj->ce_list)) {
|
||||||
|
new = nl_object_clone(obj);
|
||||||
|
if (!new)
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
} else {
|
||||||
|
nl_object_get(obj);
|
||||||
|
new = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
return __cache_add(cache, new);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef disabled
|
||||||
|
/**
|
||||||
|
* Move object from one cache to another
|
||||||
|
* @arg cache Cache to move object to.
|
||||||
|
* @arg obj Object subject to be moved
|
||||||
|
*
|
||||||
|
* Removes the given object from its associated cache if needed
|
||||||
|
* and adds it to the new cache.
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_cache_move(struct nl_cache *cache, struct nl_object *obj)
|
||||||
|
{
|
||||||
|
if (cache->c_ops->co_obj_ops != obj->ce_ops)
|
||||||
|
return -NLE_OBJ_MISMATCH;
|
||||||
|
|
||||||
|
NL_DBG(3, "Moving object %p to cache %p\n", obj, cache);
|
||||||
|
|
||||||
|
/* Acquire reference, if already in a cache this will be
|
||||||
|
* reverted during removal */
|
||||||
|
nl_object_get(obj);
|
||||||
|
|
||||||
|
if (!nl_list_empty(&obj->ce_list))
|
||||||
|
nl_cache_remove(obj);
|
||||||
|
|
||||||
|
return __cache_add(cache, obj);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an object from a cache.
|
||||||
|
* @arg obj Object to remove from its cache
|
||||||
|
*
|
||||||
|
* Removes the object \c obj from the cache it is assigned to, since
|
||||||
|
* an object can only be assigned to one cache at a time, the cache
|
||||||
|
* must ne be passed along with it.
|
||||||
|
*/
|
||||||
|
void nl_cache_remove(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
struct nl_cache *cache = obj->ce_cache;
|
||||||
|
|
||||||
|
if (cache == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
nl_list_del(&obj->ce_list);
|
||||||
|
obj->ce_cache = NULL;
|
||||||
|
nl_object_put(obj);
|
||||||
|
cache->c_nitems--;
|
||||||
|
|
||||||
|
NL_DBG(1, "Deleted %p from cache %p <%s>.\n",
|
||||||
|
obj, cache, nl_cache_name(cache));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef disabled
|
||||||
|
/**
|
||||||
|
* Search for an object in a cache
|
||||||
|
* @arg cache Cache to search in.
|
||||||
|
* @arg needle Object to look for.
|
||||||
|
*
|
||||||
|
* Iterates over the cache and looks for an object with identical
|
||||||
|
* identifiers as the needle.
|
||||||
|
*
|
||||||
|
* @return Reference to object or NULL if not found.
|
||||||
|
* @note The returned object must be returned via nl_object_put().
|
||||||
|
*/
|
||||||
|
struct nl_object *nl_cache_search(struct nl_cache *cache,
|
||||||
|
struct nl_object *needle)
|
||||||
|
{
|
||||||
|
struct nl_object *obj;
|
||||||
|
|
||||||
|
nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
|
||||||
|
if (nl_object_identical(obj, needle)) {
|
||||||
|
nl_object_get(obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Synchronization
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a full dump from the kernel to fill a cache
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg cache Cache subjected to be filled.
|
||||||
|
*
|
||||||
|
* Send a dumping request to the kernel causing it to dump all objects
|
||||||
|
* related to the specified cache to the netlink socket.
|
||||||
|
*
|
||||||
|
* Use nl_cache_pickup() to read the objects from the socket and fill them
|
||||||
|
* into a cache.
|
||||||
|
*/
|
||||||
|
int nl_cache_request_full_dump(struct nl_sock *sk, struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
NL_DBG(2, "Requesting dump from kernel for cache %p <%s>...\n",
|
||||||
|
cache, nl_cache_name(cache));
|
||||||
|
|
||||||
|
if (cache->c_ops->co_request_update == NULL)
|
||||||
|
return -NLE_OPNOTSUPP;
|
||||||
|
|
||||||
|
return cache->c_ops->co_request_update(cache, sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @cond SKIP */
|
||||||
|
struct update_xdata {
|
||||||
|
struct nl_cache_ops *ops;
|
||||||
|
struct nl_parser_param *params;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int update_msg_parser(struct nl_msg *msg, void *arg)
|
||||||
|
{
|
||||||
|
struct update_xdata *x = arg;
|
||||||
|
|
||||||
|
return nl_cache_parse(x->ops, &msg->nm_src, msg->nm_nlh, x->params);
|
||||||
|
}
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
int __cache_pickup(struct nl_sock *sk, struct nl_cache *cache,
|
||||||
|
struct nl_parser_param *param)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct nl_cb *cb;
|
||||||
|
struct update_xdata x = {
|
||||||
|
.ops = cache->c_ops,
|
||||||
|
.params = param,
|
||||||
|
};
|
||||||
|
|
||||||
|
NL_DBG(1, "Picking up answer for cache %p <%s>...\n",
|
||||||
|
cache, nl_cache_name(cache));
|
||||||
|
|
||||||
|
cb = nl_cb_clone(sk->s_cb);
|
||||||
|
if (cb == NULL)
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, update_msg_parser, &x);
|
||||||
|
|
||||||
|
err = nl_recvmsgs(sk, cb);
|
||||||
|
if (err < 0)
|
||||||
|
NL_DBG(2, "While picking up for %p <%s>, recvmsgs() returned " \
|
||||||
|
"%d: %s", cache, nl_cache_name(cache),
|
||||||
|
err, nl_geterror(err));
|
||||||
|
|
||||||
|
nl_cb_put(cb);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pickup_cb(struct nl_object *c, struct nl_parser_param *p)
|
||||||
|
{
|
||||||
|
return nl_cache_add((struct nl_cache *) p->pp_arg, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pickup a netlink dump response and put it into a cache.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg cache Cache to put items into.
|
||||||
|
*
|
||||||
|
* Waits for netlink messages to arrive, parses them and puts them into
|
||||||
|
* the specified cache.
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_cache_pickup(struct nl_sock *sk, struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
struct nl_parser_param p = {
|
||||||
|
.pp_cb = pickup_cb,
|
||||||
|
.pp_arg = cache,
|
||||||
|
};
|
||||||
|
|
||||||
|
return __cache_pickup(sk, cache, &p);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef disabled
|
||||||
|
static int cache_include(struct nl_cache *cache, struct nl_object *obj,
|
||||||
|
struct nl_msgtype *type, change_func_t cb)
|
||||||
|
{
|
||||||
|
struct nl_object *old;
|
||||||
|
|
||||||
|
switch (type->mt_act) {
|
||||||
|
case NL_ACT_NEW:
|
||||||
|
case NL_ACT_DEL:
|
||||||
|
old = nl_cache_search(cache, obj);
|
||||||
|
if (old) {
|
||||||
|
nl_cache_remove(old);
|
||||||
|
if (type->mt_act == NL_ACT_DEL) {
|
||||||
|
if (cb)
|
||||||
|
cb(cache, old, NL_ACT_DEL);
|
||||||
|
nl_object_put(old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type->mt_act == NL_ACT_NEW) {
|
||||||
|
nl_cache_move(cache, obj);
|
||||||
|
if (old == NULL && cb)
|
||||||
|
cb(cache, obj, NL_ACT_NEW);
|
||||||
|
else if (old) {
|
||||||
|
if (nl_object_diff(old, obj) && cb)
|
||||||
|
cb(cache, obj, NL_ACT_CHANGE);
|
||||||
|
|
||||||
|
nl_object_put(old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
NL_DBG(2, "Unknown action associated to object %p\n", obj);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nl_cache_include(struct nl_cache *cache, struct nl_object *obj,
|
||||||
|
change_func_t change_cb)
|
||||||
|
{
|
||||||
|
struct nl_cache_ops *ops = cache->c_ops;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (ops->co_obj_ops != obj->ce_ops)
|
||||||
|
return -NLE_OBJ_MISMATCH;
|
||||||
|
|
||||||
|
for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
|
||||||
|
if (ops->co_msgtypes[i].mt_id == obj->ce_msgtype)
|
||||||
|
return cache_include(cache, obj, &ops->co_msgtypes[i],
|
||||||
|
change_cb);
|
||||||
|
|
||||||
|
return -NLE_MSGTYPE_NOSUPPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int resync_cb(struct nl_object *c, struct nl_parser_param *p)
|
||||||
|
{
|
||||||
|
struct nl_cache_assoc *ca = p->pp_arg;
|
||||||
|
|
||||||
|
return nl_cache_include(ca->ca_cache, c, ca->ca_change);
|
||||||
|
}
|
||||||
|
|
||||||
|
int nl_cache_resync(struct nl_sock *sk, struct nl_cache *cache,
|
||||||
|
change_func_t change_cb)
|
||||||
|
{
|
||||||
|
struct nl_object *obj, *next;
|
||||||
|
struct nl_cache_assoc ca = {
|
||||||
|
.ca_cache = cache,
|
||||||
|
.ca_change = change_cb,
|
||||||
|
};
|
||||||
|
struct nl_parser_param p = {
|
||||||
|
.pp_cb = resync_cb,
|
||||||
|
.pp_arg = &ca,
|
||||||
|
};
|
||||||
|
int err;
|
||||||
|
|
||||||
|
NL_DBG(1, "Resyncing cache %p <%s>...\n", cache, nl_cache_name(cache));
|
||||||
|
|
||||||
|
/* Mark all objects so we can see if some of them are obsolete */
|
||||||
|
nl_cache_mark_all(cache);
|
||||||
|
|
||||||
|
err = nl_cache_request_full_dump(sk, cache);
|
||||||
|
if (err < 0)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
err = __cache_pickup(sk, cache, &p);
|
||||||
|
if (err < 0)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
nl_list_for_each_entry_safe(obj, next, &cache->c_items, ce_list)
|
||||||
|
if (nl_object_is_marked(obj))
|
||||||
|
nl_cache_remove(obj);
|
||||||
|
|
||||||
|
NL_DBG(1, "Finished resyncing %p <%s>\n", cache, nl_cache_name(cache));
|
||||||
|
|
||||||
|
err = 0;
|
||||||
|
errout:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Parsing
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @cond SKIP */
|
||||||
|
int nl_cache_parse(struct nl_cache_ops *ops, struct sockaddr_nl *who,
|
||||||
|
struct nlmsghdr *nlh, struct nl_parser_param *params)
|
||||||
|
{
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
if (!nlmsg_valid_hdr(nlh, ops->co_hdrsize))
|
||||||
|
return -NLE_MSG_TOOSHORT;
|
||||||
|
|
||||||
|
for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++) {
|
||||||
|
if (ops->co_msgtypes[i].mt_id == nlh->nlmsg_type) {
|
||||||
|
err = ops->co_msg_parser(ops, who, nlh, params);
|
||||||
|
if (err != -NLE_OPNOTSUPP)
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = -NLE_MSGTYPE_NOSUPPORT;
|
||||||
|
errout:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a netlink message and add it to the cache.
|
||||||
|
* @arg cache cache to add element to
|
||||||
|
* @arg msg netlink message
|
||||||
|
*
|
||||||
|
* Parses a netlink message by calling the cache specific message parser
|
||||||
|
* and adds the new element to the cache.
|
||||||
|
*
|
||||||
|
* @return 0 or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_cache_parse_and_add(struct nl_cache *cache, struct nl_msg *msg)
|
||||||
|
{
|
||||||
|
struct nl_parser_param p = {
|
||||||
|
.pp_cb = pickup_cb,
|
||||||
|
.pp_arg = cache,
|
||||||
|
};
|
||||||
|
|
||||||
|
return nl_cache_parse(cache->c_ops, NULL, nlmsg_hdr(msg), &p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (Re)fill a cache with the contents in the kernel.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg cache cache to update
|
||||||
|
*
|
||||||
|
* Clears the specified cache and fills it with the current state in
|
||||||
|
* the kernel.
|
||||||
|
*
|
||||||
|
* @return 0 or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_cache_refill(struct nl_sock *sk, struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = nl_cache_request_full_dump(sk, cache);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
NL_DBG(2, "Upading cache %p <%s>, request sent, waiting for dump...\n",
|
||||||
|
cache, nl_cache_name(cache));
|
||||||
|
nl_cache_clear(cache);
|
||||||
|
|
||||||
|
return nl_cache_pickup(sk, cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
#ifdef disabled
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Utillities
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark all objects in a cache
|
||||||
|
* @arg cache Cache to mark all objects in
|
||||||
|
*/
|
||||||
|
void nl_cache_mark_all(struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
struct nl_object *obj;
|
||||||
|
|
||||||
|
NL_DBG(2, "Marking all objects in cache %p <%s>...\n",
|
||||||
|
cache, nl_cache_name(cache));
|
||||||
|
|
||||||
|
nl_list_for_each_entry(obj, &cache->c_items, ce_list)
|
||||||
|
nl_object_mark(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Dumping
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#ifdef disabled
|
||||||
|
/**
|
||||||
|
* Dump all elements of a cache.
|
||||||
|
* @arg cache cache to dump
|
||||||
|
* @arg params dumping parameters
|
||||||
|
*
|
||||||
|
* Dumps all elements of the \a cache to the file descriptor \a fd.
|
||||||
|
*/
|
||||||
|
void nl_cache_dump(struct nl_cache *cache, struct nl_dump_params *params)
|
||||||
|
{
|
||||||
|
nl_cache_dump_filter(cache, params, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump all elements of a cache (filtered).
|
||||||
|
* @arg cache cache to dump
|
||||||
|
* @arg params dumping parameters (optional)
|
||||||
|
* @arg filter filter object
|
||||||
|
*
|
||||||
|
* Dumps all elements of the \a cache to the file descriptor \a fd
|
||||||
|
* given they match the given filter \a filter.
|
||||||
|
*/
|
||||||
|
void nl_cache_dump_filter(struct nl_cache *cache,
|
||||||
|
struct nl_dump_params *params,
|
||||||
|
struct nl_object *filter)
|
||||||
|
{
|
||||||
|
int type = params ? params->dp_type : NL_DUMP_DETAILS;
|
||||||
|
struct nl_object_ops *ops;
|
||||||
|
struct nl_object *obj;
|
||||||
|
|
||||||
|
NL_DBG(2, "Dumping cache %p <%s> filter %p\n",
|
||||||
|
cache, nl_cache_name(cache), filter);
|
||||||
|
|
||||||
|
if (type > NL_DUMP_MAX || type < 0)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
if (cache->c_ops == NULL)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
ops = cache->c_ops->co_obj_ops;
|
||||||
|
if (!ops->oo_dump[type])
|
||||||
|
return;
|
||||||
|
|
||||||
|
nl_list_for_each_entry(obj, &cache->c_items, ce_list) {
|
||||||
|
if (filter && !nl_object_match_filter(obj, filter))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
NL_DBG(4, "Dumping object %p...\n", obj);
|
||||||
|
dump_from_ops(obj, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Iterators
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call a callback on each element of the cache.
|
||||||
|
* @arg cache cache to iterate on
|
||||||
|
* @arg cb callback function
|
||||||
|
* @arg arg argument passed to callback function
|
||||||
|
*
|
||||||
|
* Calls a callback function \a cb on each element of the \a cache.
|
||||||
|
* The argument \a arg is passed on the callback function.
|
||||||
|
*/
|
||||||
|
void nl_cache_foreach(struct nl_cache *cache,
|
||||||
|
void (*cb)(struct nl_object *, void *), void *arg)
|
||||||
|
{
|
||||||
|
nl_cache_foreach_filter(cache, NULL, cb, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call a callback on each element of the cache (filtered).
|
||||||
|
* @arg cache cache to iterate on
|
||||||
|
* @arg filter filter object
|
||||||
|
* @arg cb callback function
|
||||||
|
* @arg arg argument passed to callback function
|
||||||
|
*
|
||||||
|
* Calls a callback function \a cb on each element of the \a cache
|
||||||
|
* that matches the \a filter. The argument \a arg is passed on
|
||||||
|
* to the callback function.
|
||||||
|
*/
|
||||||
|
void nl_cache_foreach_filter(struct nl_cache *cache, struct nl_object *filter,
|
||||||
|
void (*cb)(struct nl_object *, void *), void *arg)
|
||||||
|
{
|
||||||
|
struct nl_object *obj, *tmp;
|
||||||
|
struct nl_object_ops *ops;
|
||||||
|
|
||||||
|
if (cache->c_ops == NULL)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
ops = cache->c_ops->co_obj_ops;
|
||||||
|
|
||||||
|
nl_list_for_each_entry_safe(obj, tmp, &cache->c_items, ce_list) {
|
||||||
|
if (filter && !nl_object_match_filter(obj, filter))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
cb(obj, arg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @} */
|
|
@ -0,0 +1,249 @@
|
||||||
|
/*
|
||||||
|
* lib/cache_mngt.c Cache Management
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup core
|
||||||
|
* @defgroup cache_mngt Caching
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-local.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/cache.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
|
||||||
|
static struct nl_cache_ops *cache_ops;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Cache Operations Sets
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup the set cache operations of a certain cache type
|
||||||
|
* @arg name name of the cache type
|
||||||
|
*
|
||||||
|
* @return The cache operations or NULL if no operations
|
||||||
|
* have been registered under the specified name.
|
||||||
|
*/
|
||||||
|
struct nl_cache_ops *nl_cache_ops_lookup(const char *name)
|
||||||
|
{
|
||||||
|
struct nl_cache_ops *ops;
|
||||||
|
|
||||||
|
for (ops = cache_ops; ops; ops = ops->co_next)
|
||||||
|
if (!strcmp(ops->co_name, name))
|
||||||
|
return ops;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associate a message type to a set of cache operations
|
||||||
|
* @arg protocol netlink protocol
|
||||||
|
* @arg msgtype netlink message type
|
||||||
|
*
|
||||||
|
* Associates the specified netlink message type with
|
||||||
|
* a registered set of cache operations.
|
||||||
|
*
|
||||||
|
* @return The cache operations or NULL if no association
|
||||||
|
* could be made.
|
||||||
|
*/
|
||||||
|
struct nl_cache_ops *nl_cache_ops_associate(int protocol, int msgtype)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct nl_cache_ops *ops;
|
||||||
|
|
||||||
|
for (ops = cache_ops; ops; ops = ops->co_next) {
|
||||||
|
if (ops->co_protocol != protocol)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
|
||||||
|
if (ops->co_msgtypes[i].mt_id == msgtype)
|
||||||
|
return ops;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup message type cache association
|
||||||
|
* @arg ops cache operations
|
||||||
|
* @arg msgtype netlink message type
|
||||||
|
*
|
||||||
|
* Searches for a matching message type association ing the specified
|
||||||
|
* cache operations.
|
||||||
|
*
|
||||||
|
* @return A message type association or NULL.
|
||||||
|
*/
|
||||||
|
struct nl_msgtype *nl_msgtype_lookup(struct nl_cache_ops *ops, int msgtype)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; ops->co_msgtypes[i].mt_id >= 0; i++)
|
||||||
|
if (ops->co_msgtypes[i].mt_id == msgtype)
|
||||||
|
return &ops->co_msgtypes[i];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nl_cache_ops *cache_ops_lookup_for_obj(struct nl_object_ops *obj_ops)
|
||||||
|
{
|
||||||
|
struct nl_cache_ops *ops;
|
||||||
|
|
||||||
|
for (ops = cache_ops; ops; ops = ops->co_next)
|
||||||
|
if (ops->co_obj_ops == obj_ops)
|
||||||
|
return ops;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call a function for each registered cache operation
|
||||||
|
* @arg cb Callback function to be called
|
||||||
|
* @arg arg User specific argument.
|
||||||
|
*/
|
||||||
|
void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *arg)
|
||||||
|
{
|
||||||
|
struct nl_cache_ops *ops;
|
||||||
|
|
||||||
|
for (ops = cache_ops; ops; ops = ops->co_next)
|
||||||
|
cb(ops, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a set of cache operations
|
||||||
|
* @arg ops cache operations
|
||||||
|
*
|
||||||
|
* Called by users of caches to announce the avaibility of
|
||||||
|
* a certain cache type.
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_cache_mngt_register(struct nl_cache_ops *ops)
|
||||||
|
{
|
||||||
|
if (!ops->co_name || !ops->co_obj_ops)
|
||||||
|
return -NLE_INVAL;
|
||||||
|
|
||||||
|
if (nl_cache_ops_lookup(ops->co_name))
|
||||||
|
return -NLE_EXIST;
|
||||||
|
|
||||||
|
ops->co_next = cache_ops;
|
||||||
|
cache_ops = ops;
|
||||||
|
|
||||||
|
NL_DBG(1, "Registered cache operations %s\n", ops->co_name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister a set of cache operations
|
||||||
|
* @arg ops cache operations
|
||||||
|
*
|
||||||
|
* Called by users of caches to announce a set of
|
||||||
|
* cache operations is no longer available. The
|
||||||
|
* specified cache operations must have been registered
|
||||||
|
* previously using nl_cache_mngt_register()
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code
|
||||||
|
*/
|
||||||
|
int nl_cache_mngt_unregister(struct nl_cache_ops *ops)
|
||||||
|
{
|
||||||
|
struct nl_cache_ops *t, **tp;
|
||||||
|
|
||||||
|
for (tp = &cache_ops; (t=*tp) != NULL; tp = &t->co_next)
|
||||||
|
if (t == ops)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!t)
|
||||||
|
return -NLE_NOCACHE;
|
||||||
|
|
||||||
|
NL_DBG(1, "Unregistered cache operations %s\n", ops->co_name);
|
||||||
|
|
||||||
|
*tp = t->co_next;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Global Cache Provisioning/Requiring
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provide a cache for global use
|
||||||
|
* @arg cache cache to provide
|
||||||
|
*
|
||||||
|
* Offers the specified cache to be used by other modules.
|
||||||
|
* Only one cache per type may be shared at a time,
|
||||||
|
* a previsouly provided caches will be overwritten.
|
||||||
|
*/
|
||||||
|
void nl_cache_mngt_provide(struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
struct nl_cache_ops *ops;
|
||||||
|
|
||||||
|
ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops);
|
||||||
|
if (!ops)
|
||||||
|
BUG();
|
||||||
|
else
|
||||||
|
ops->co_major_cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unprovide a cache for global use
|
||||||
|
* @arg cache cache to unprovide
|
||||||
|
*
|
||||||
|
* Cancels the offer to use a cache globally. The
|
||||||
|
* cache will no longer be returned via lookups but
|
||||||
|
* may still be in use.
|
||||||
|
*/
|
||||||
|
void nl_cache_mngt_unprovide(struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
struct nl_cache_ops *ops;
|
||||||
|
|
||||||
|
ops = cache_ops_lookup_for_obj(cache->c_ops->co_obj_ops);
|
||||||
|
if (!ops)
|
||||||
|
BUG();
|
||||||
|
else if (ops->co_major_cache == cache)
|
||||||
|
ops->co_major_cache = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Demand the use of a global cache
|
||||||
|
* @arg name name of the required object type
|
||||||
|
*
|
||||||
|
* Trys to find a cache of the specified type for global
|
||||||
|
* use.
|
||||||
|
*
|
||||||
|
* @return A cache provided by another subsystem of the
|
||||||
|
* specified type marked to be available.
|
||||||
|
*/
|
||||||
|
struct nl_cache *nl_cache_mngt_require(const char *name)
|
||||||
|
{
|
||||||
|
struct nl_cache_ops *ops;
|
||||||
|
|
||||||
|
ops = nl_cache_ops_lookup(name);
|
||||||
|
if (!ops || !ops->co_major_cache) {
|
||||||
|
fprintf(stderr, "Application BUG: Your application must "
|
||||||
|
"call nl_cache_mngt_provide() and\nprovide a valid "
|
||||||
|
"%s cache to be used for internal lookups.\nSee the "
|
||||||
|
" API documentation for more details.\n", name);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ops->co_major_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** @} */
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* lib/error.c Error Handling
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-local.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
|
||||||
|
static const char *errmsg[NLE_MAX+1] = {
|
||||||
|
[NLE_SUCCESS] = "Success",
|
||||||
|
[NLE_FAILURE] = "Unspecific failure",
|
||||||
|
[NLE_INTR] = "Interrupted system call",
|
||||||
|
[NLE_BAD_SOCK] = "Bad socket",
|
||||||
|
[NLE_AGAIN] = "Try again",
|
||||||
|
[NLE_NOMEM] = "Out of memory",
|
||||||
|
[NLE_EXIST] = "Object exists",
|
||||||
|
[NLE_INVAL] = "Invalid input data or parameter",
|
||||||
|
[NLE_RANGE] = "Input data out of range",
|
||||||
|
[NLE_MSGSIZE] = "Message size not sufficient",
|
||||||
|
[NLE_OPNOTSUPP] = "Operation not supported",
|
||||||
|
[NLE_AF_NOSUPPORT] = "Address family not supported",
|
||||||
|
[NLE_OBJ_NOTFOUND] = "Object not found",
|
||||||
|
[NLE_NOATTR] = "Attribute not available",
|
||||||
|
[NLE_MISSING_ATTR] = "Missing attribute",
|
||||||
|
[NLE_AF_MISMATCH] = "Address family mismatch",
|
||||||
|
[NLE_SEQ_MISMATCH] = "Message sequence number mismatch",
|
||||||
|
[NLE_MSG_OVERFLOW] = "Kernel reported message overflow",
|
||||||
|
[NLE_MSG_TRUNC] = "Kernel reported truncated message",
|
||||||
|
[NLE_NOADDR] = "Invalid address for specified address family",
|
||||||
|
[NLE_SRCRT_NOSUPPORT] = "Source based routing not supported",
|
||||||
|
[NLE_MSG_TOOSHORT] = "Netlink message is too short",
|
||||||
|
[NLE_MSGTYPE_NOSUPPORT] = "Netlink message type is not supported",
|
||||||
|
[NLE_OBJ_MISMATCH] = "Object type does not match cache",
|
||||||
|
[NLE_NOCACHE] = "Unknown or invalid cache type",
|
||||||
|
[NLE_BUSY] = "Object busy",
|
||||||
|
[NLE_PROTO_MISMATCH] = "Protocol mismatch",
|
||||||
|
[NLE_NOACCESS] = "No Access",
|
||||||
|
[NLE_PERM] = "Operation not permitted",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return error message for an error code
|
||||||
|
* @return error message
|
||||||
|
*/
|
||||||
|
const char *nl_geterror(int error)
|
||||||
|
{
|
||||||
|
error = abs(error);
|
||||||
|
|
||||||
|
if (error > NLE_MAX)
|
||||||
|
error = NLE_FAILURE;
|
||||||
|
|
||||||
|
return errmsg[error];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a libnl error message
|
||||||
|
* @arg s error message prefix
|
||||||
|
*
|
||||||
|
* Prints the error message of the call that failed last.
|
||||||
|
*
|
||||||
|
* If s is not NULL and *s is not a null byte the argument
|
||||||
|
* string is printed, followed by a colon and a blank. Then
|
||||||
|
* the error message and a new-line.
|
||||||
|
*/
|
||||||
|
void nl_perror(int error, const char *s)
|
||||||
|
{
|
||||||
|
if (s && *s)
|
||||||
|
fprintf(stderr, "%s: %s\n", s, nl_geterror(error));
|
||||||
|
else
|
||||||
|
fprintf(stderr, "%s\n", nl_geterror(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
int nl_syserr2nlerr(int error)
|
||||||
|
{
|
||||||
|
error = abs(error);
|
||||||
|
|
||||||
|
switch (error) {
|
||||||
|
case EBADF: return NLE_BAD_SOCK;
|
||||||
|
case EADDRINUSE: return NLE_EXIST;
|
||||||
|
case EEXIST: return NLE_EXIST;
|
||||||
|
case EADDRNOTAVAIL: return NLE_NOADDR;
|
||||||
|
case ENOENT: return NLE_OBJ_NOTFOUND;
|
||||||
|
case EINTR: return NLE_INTR;
|
||||||
|
case EAGAIN: return NLE_AGAIN;
|
||||||
|
case ENOTSOCK: return NLE_BAD_SOCK;
|
||||||
|
case ENOPROTOOPT: return NLE_INVAL;
|
||||||
|
case EFAULT: return NLE_INVAL;
|
||||||
|
case EACCES: return NLE_NOACCESS;
|
||||||
|
case EINVAL: return NLE_INVAL;
|
||||||
|
case ENOBUFS: return NLE_NOMEM;
|
||||||
|
case ENOMEM: return NLE_NOMEM;
|
||||||
|
case EAFNOSUPPORT: return NLE_AF_NOSUPPORT;
|
||||||
|
case EPROTONOSUPPORT: return NLE_PROTO_MISMATCH;
|
||||||
|
case EOPNOTSUPP: return NLE_OPNOTSUPP;
|
||||||
|
case EPERM: return NLE_PERM;
|
||||||
|
case EBUSY: return NLE_BUSY;
|
||||||
|
default: return NLE_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
|
@ -0,0 +1,268 @@
|
||||||
|
/*
|
||||||
|
* lib/genl/genl.c Generic Netlink
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup genl Generic Netlink
|
||||||
|
*
|
||||||
|
* @par Message Format
|
||||||
|
* @code
|
||||||
|
* <------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) --->
|
||||||
|
* +----------------------------+- - -+- - - - - - - - - - -+- - -+
|
||||||
|
* | Header | Pad | Payload | Pad |
|
||||||
|
* | struct nlmsghdr | | | |
|
||||||
|
* +----------------------------+- - -+- - - - - - - - - - -+- - -+
|
||||||
|
* @endcode
|
||||||
|
* @code
|
||||||
|
* <-------- GENL_HDRLEN -------> <--- hdrlen -->
|
||||||
|
* <------- genlmsg_len(ghdr) ------>
|
||||||
|
* +------------------------+- - -+---------------+- - -+------------+
|
||||||
|
* | Generic Netlink Header | Pad | Family Header | Pad | Attributes |
|
||||||
|
* | struct genlmsghdr | | | | |
|
||||||
|
* +------------------------+- - -+---------------+- - -+------------+
|
||||||
|
* genlmsg_data(ghdr)--------------^ ^
|
||||||
|
* genlmsg_attrdata(ghdr, hdrlen)-------------------------
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* @code
|
||||||
|
* #include <netlink/netlink.h>
|
||||||
|
* #include <netlink/genl/genl.h>
|
||||||
|
* #include <netlink/genl/ctrl.h>
|
||||||
|
*
|
||||||
|
* struct nl_sock *sock;
|
||||||
|
* struct nl_msg *msg;
|
||||||
|
* int family;
|
||||||
|
*
|
||||||
|
* // Allocate a new netlink socket
|
||||||
|
* sock = nl_socket_alloc();
|
||||||
|
*
|
||||||
|
* // Connect to generic netlink socket on kernel side
|
||||||
|
* genl_connect(sock);
|
||||||
|
*
|
||||||
|
* // Ask kernel to resolve family name to family id
|
||||||
|
* family = genl_ctrl_resolve(sock, "generic_netlink_family_name");
|
||||||
|
*
|
||||||
|
* // Construct a generic netlink by allocating a new message, fill in
|
||||||
|
* // the header and append a simple integer attribute.
|
||||||
|
* msg = nlmsg_alloc();
|
||||||
|
* genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, NLM_F_ECHO,
|
||||||
|
* CMD_FOO_GET, FOO_VERSION);
|
||||||
|
* nla_put_u32(msg, ATTR_FOO, 123);
|
||||||
|
*
|
||||||
|
* // Send message over netlink socket
|
||||||
|
* nl_send_auto_complete(sock, msg);
|
||||||
|
*
|
||||||
|
* // Free message
|
||||||
|
* nlmsg_free(msg);
|
||||||
|
*
|
||||||
|
* // Prepare socket to receive the answer by specifying the callback
|
||||||
|
* // function to be called for valid messages.
|
||||||
|
* nl_socket_modify_cb(sock, NL_CB_VALID, NL_CB_CUSTOM, parse_cb, NULL);
|
||||||
|
*
|
||||||
|
* // Wait for the answer and receive it
|
||||||
|
* nl_recvmsgs_default(sock);
|
||||||
|
*
|
||||||
|
* static int parse_cb(struct nl_msg *msg, void *arg)
|
||||||
|
* {
|
||||||
|
* struct nlmsghdr *nlh = nlmsg_hdr(msg);
|
||||||
|
* struct nlattr *attrs[ATTR_MAX+1];
|
||||||
|
*
|
||||||
|
* // Validate message and parse attributes
|
||||||
|
* genlmsg_parse(nlh, 0, attrs, ATTR_MAX, policy);
|
||||||
|
*
|
||||||
|
* if (attrs[ATTR_FOO]) {
|
||||||
|
* uint32_t value = nla_get_u32(attrs[ATTR_FOO]);
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* return 0;
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-generic.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/genl/genl.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Socket Creating
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
int genl_connect(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
return nl_connect(sk, NETLINK_GENERIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Sending
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send trivial generic netlink message
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg family Generic netlink family
|
||||||
|
* @arg cmd Command
|
||||||
|
* @arg version Version
|
||||||
|
* @arg flags Additional netlink message flags.
|
||||||
|
*
|
||||||
|
* Fills out a routing netlink request message and sends it out
|
||||||
|
* using nl_send_simple().
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int genl_send_simple(struct nl_sock *sk, int family, int cmd,
|
||||||
|
int version, int flags)
|
||||||
|
{
|
||||||
|
struct genlmsghdr hdr = {
|
||||||
|
.cmd = cmd,
|
||||||
|
.version = version,
|
||||||
|
};
|
||||||
|
|
||||||
|
return nl_send_simple(sk, family, flags, &hdr, sizeof(hdr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Message Parsing
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
int genlmsg_valid_hdr(struct nlmsghdr *nlh, int hdrlen)
|
||||||
|
{
|
||||||
|
struct genlmsghdr *ghdr;
|
||||||
|
|
||||||
|
if (!nlmsg_valid_hdr(nlh, GENL_HDRLEN))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ghdr = nlmsg_data(nlh);
|
||||||
|
if (genlmsg_len(ghdr) < NLMSG_ALIGN(hdrlen))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int genlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype,
|
||||||
|
struct nla_policy *policy)
|
||||||
|
{
|
||||||
|
struct genlmsghdr *ghdr;
|
||||||
|
|
||||||
|
if (!genlmsg_valid_hdr(nlh, hdrlen))
|
||||||
|
return -NLE_MSG_TOOSHORT;
|
||||||
|
|
||||||
|
ghdr = nlmsg_data(nlh);
|
||||||
|
return nla_validate(genlmsg_attrdata(ghdr, hdrlen),
|
||||||
|
genlmsg_attrlen(ghdr, hdrlen), maxtype, policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
int genlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
|
||||||
|
int maxtype, struct nla_policy *policy)
|
||||||
|
{
|
||||||
|
struct genlmsghdr *ghdr;
|
||||||
|
|
||||||
|
if (!genlmsg_valid_hdr(nlh, hdrlen))
|
||||||
|
return -NLE_MSG_TOOSHORT;
|
||||||
|
|
||||||
|
ghdr = nlmsg_data(nlh);
|
||||||
|
return nla_parse(tb, maxtype, genlmsg_attrdata(ghdr, hdrlen),
|
||||||
|
genlmsg_attrlen(ghdr, hdrlen), policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get head of message payload
|
||||||
|
* @arg gnlh genetlink messsage header
|
||||||
|
*/
|
||||||
|
void *genlmsg_data(const struct genlmsghdr *gnlh)
|
||||||
|
{
|
||||||
|
return ((unsigned char *) gnlh + GENL_HDRLEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get lenght of message payload
|
||||||
|
* @arg gnlh genetlink message header
|
||||||
|
*/
|
||||||
|
int genlmsg_len(const struct genlmsghdr *gnlh)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *nlh = (struct nlmsghdr *)((unsigned char *)gnlh -
|
||||||
|
NLMSG_HDRLEN);
|
||||||
|
return (nlh->nlmsg_len - GENL_HDRLEN - NLMSG_HDRLEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get head of attribute data
|
||||||
|
* @arg gnlh generic netlink message header
|
||||||
|
* @arg hdrlen length of family specific header
|
||||||
|
*/
|
||||||
|
struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen)
|
||||||
|
{
|
||||||
|
return genlmsg_data(gnlh) + NLMSG_ALIGN(hdrlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get length of attribute data
|
||||||
|
* @arg gnlh generic netlink message header
|
||||||
|
* @arg hdrlen length of family specific header
|
||||||
|
*/
|
||||||
|
int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen)
|
||||||
|
{
|
||||||
|
return genlmsg_len(gnlh) - NLMSG_ALIGN(hdrlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Message Building
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add generic netlink header to netlink message
|
||||||
|
* @arg msg netlink message
|
||||||
|
* @arg pid netlink process id or NL_AUTO_PID
|
||||||
|
* @arg seq sequence number of message or NL_AUTO_SEQ
|
||||||
|
* @arg family generic netlink family
|
||||||
|
* @arg hdrlen length of user specific header
|
||||||
|
* @arg flags message flags
|
||||||
|
* @arg cmd generic netlink command
|
||||||
|
* @arg version protocol version
|
||||||
|
*
|
||||||
|
* Returns pointer to user specific header.
|
||||||
|
*/
|
||||||
|
void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family,
|
||||||
|
int hdrlen, int flags, uint8_t cmd, uint8_t version)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
struct genlmsghdr hdr = {
|
||||||
|
.cmd = cmd,
|
||||||
|
.version = version,
|
||||||
|
};
|
||||||
|
|
||||||
|
nlh = nlmsg_put(msg, pid, seq, family, GENL_HDRLEN + hdrlen, flags);
|
||||||
|
if (nlh == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
memcpy(nlmsg_data(nlh), &hdr, sizeof(hdr));
|
||||||
|
NL_DBG(2, "msg %p: Added generic netlink header cmd=%d version=%d\n",
|
||||||
|
msg, cmd, version);
|
||||||
|
|
||||||
|
return nlmsg_data(nlh) + GENL_HDRLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** @} */
|
|
@ -0,0 +1,302 @@
|
||||||
|
/*
|
||||||
|
* lib/genl/ctrl.c Generic Netlink Controller
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup genl_mngt
|
||||||
|
* @defgroup ctrl Controller
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-generic.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/genl/genl.h>
|
||||||
|
#include <netlink/genl/family.h>
|
||||||
|
#include <netlink/genl/mngt.h>
|
||||||
|
#include <netlink/genl/ctrl.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
|
||||||
|
/** @cond SKIP */
|
||||||
|
#define CTRL_VERSION 0x0001
|
||||||
|
|
||||||
|
static struct nl_cache_ops genl_ctrl_ops;
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
static int ctrl_request_update(struct nl_cache *c, struct nl_sock *h)
|
||||||
|
{
|
||||||
|
return genl_send_simple(h, GENL_ID_CTRL, CTRL_CMD_GETFAMILY,
|
||||||
|
CTRL_VERSION, NLM_F_DUMP);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct nla_policy ctrl_policy[CTRL_ATTR_MAX+1] = {
|
||||||
|
[CTRL_ATTR_FAMILY_ID] = { .type = NLA_U16 },
|
||||||
|
[CTRL_ATTR_FAMILY_NAME] = { .type = NLA_STRING,
|
||||||
|
.maxlen = GENL_NAMSIZ },
|
||||||
|
[CTRL_ATTR_VERSION] = { .type = NLA_U32 },
|
||||||
|
[CTRL_ATTR_HDRSIZE] = { .type = NLA_U32 },
|
||||||
|
[CTRL_ATTR_MAXATTR] = { .type = NLA_U32 },
|
||||||
|
[CTRL_ATTR_OPS] = { .type = NLA_NESTED },
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct nla_policy family_op_policy[CTRL_ATTR_OP_MAX+1] = {
|
||||||
|
[CTRL_ATTR_OP_ID] = { .type = NLA_U32 },
|
||||||
|
[CTRL_ATTR_OP_FLAGS] = { .type = NLA_U32 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ctrl_msg_parser(struct nl_cache_ops *ops, struct genl_cmd *cmd,
|
||||||
|
struct genl_info *info, void *arg)
|
||||||
|
{
|
||||||
|
struct genl_family *family;
|
||||||
|
struct nl_parser_param *pp = arg;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
family = genl_family_alloc();
|
||||||
|
if (family == NULL) {
|
||||||
|
err = -NLE_NOMEM;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->attrs[CTRL_ATTR_FAMILY_NAME] == NULL) {
|
||||||
|
err = -NLE_MISSING_ATTR;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->attrs[CTRL_ATTR_FAMILY_ID] == NULL) {
|
||||||
|
err = -NLE_MISSING_ATTR;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
family->ce_msgtype = info->nlh->nlmsg_type;
|
||||||
|
genl_family_set_id(family,
|
||||||
|
nla_get_u16(info->attrs[CTRL_ATTR_FAMILY_ID]));
|
||||||
|
genl_family_set_name(family,
|
||||||
|
nla_get_string(info->attrs[CTRL_ATTR_FAMILY_NAME]));
|
||||||
|
|
||||||
|
if (info->attrs[CTRL_ATTR_VERSION]) {
|
||||||
|
uint32_t version = nla_get_u32(info->attrs[CTRL_ATTR_VERSION]);
|
||||||
|
genl_family_set_version(family, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->attrs[CTRL_ATTR_HDRSIZE]) {
|
||||||
|
uint32_t hdrsize = nla_get_u32(info->attrs[CTRL_ATTR_HDRSIZE]);
|
||||||
|
genl_family_set_hdrsize(family, hdrsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->attrs[CTRL_ATTR_MAXATTR]) {
|
||||||
|
uint32_t maxattr = nla_get_u32(info->attrs[CTRL_ATTR_MAXATTR]);
|
||||||
|
genl_family_set_maxattr(family, maxattr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info->attrs[CTRL_ATTR_OPS]) {
|
||||||
|
struct nlattr *nla, *nla_ops;
|
||||||
|
int remaining;
|
||||||
|
|
||||||
|
nla_ops = info->attrs[CTRL_ATTR_OPS];
|
||||||
|
nla_for_each_nested(nla, nla_ops, remaining) {
|
||||||
|
struct nlattr *tb[CTRL_ATTR_OP_MAX+1];
|
||||||
|
int flags = 0, id;
|
||||||
|
|
||||||
|
err = nla_parse_nested(tb, CTRL_ATTR_OP_MAX, nla,
|
||||||
|
family_op_policy);
|
||||||
|
if (err < 0)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
if (tb[CTRL_ATTR_OP_ID] == NULL) {
|
||||||
|
err = -NLE_MISSING_ATTR;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
id = nla_get_u32(tb[CTRL_ATTR_OP_ID]);
|
||||||
|
|
||||||
|
if (tb[CTRL_ATTR_OP_FLAGS])
|
||||||
|
flags = nla_get_u32(tb[CTRL_ATTR_OP_FLAGS]);
|
||||||
|
|
||||||
|
err = genl_family_add_op(family, id, flags);
|
||||||
|
if (err < 0)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = pp->pp_cb((struct nl_object *) family, pp);
|
||||||
|
errout:
|
||||||
|
genl_family_put(family);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Cache Management
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
int genl_ctrl_alloc_cache(struct nl_sock *sock, struct nl_cache **result)
|
||||||
|
{
|
||||||
|
return nl_cache_alloc_and_fill(&genl_ctrl_ops, sock, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up generic netlink family by id in the provided cache.
|
||||||
|
* @arg cache Generic netlink family cache.
|
||||||
|
* @arg id Family identifier.
|
||||||
|
*
|
||||||
|
* Searches through the cache looking for a registered family
|
||||||
|
* matching the specified identifier. The caller will own a
|
||||||
|
* reference on the returned object which needs to be given
|
||||||
|
* back after usage using genl_family_put().
|
||||||
|
*
|
||||||
|
* @return Generic netlink family object or NULL if no match was found.
|
||||||
|
*/
|
||||||
|
struct genl_family *genl_ctrl_search(struct nl_cache *cache, int id)
|
||||||
|
{
|
||||||
|
struct genl_family *fam;
|
||||||
|
|
||||||
|
if (cache->c_ops != &genl_ctrl_ops)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
nl_list_for_each_entry(fam, &cache->c_items, ce_list) {
|
||||||
|
if (fam->gf_id == id) {
|
||||||
|
nl_object_get((struct nl_object *) fam);
|
||||||
|
return fam;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Resolver
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look up generic netlink family by family name in the provided cache.
|
||||||
|
* @arg cache Generic netlink family cache.
|
||||||
|
* @arg name Family name.
|
||||||
|
*
|
||||||
|
* Searches through the cache looking for a registered family
|
||||||
|
* matching the specified name. The caller will own a reference
|
||||||
|
* on the returned object which needs to be given back after
|
||||||
|
* usage using genl_family_put().
|
||||||
|
*
|
||||||
|
* @return Generic netlink family object or NULL if no match was found.
|
||||||
|
*/
|
||||||
|
struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache,
|
||||||
|
const char *name)
|
||||||
|
{
|
||||||
|
struct genl_family *fam;
|
||||||
|
|
||||||
|
if (cache->c_ops != &genl_ctrl_ops)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
nl_list_for_each_entry(fam, &cache->c_items, ce_list) {
|
||||||
|
if (!strcmp(name, fam->gf_name)) {
|
||||||
|
nl_object_get((struct nl_object *) fam);
|
||||||
|
return fam;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve generic netlink family name to its identifier
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg name Name of generic netlink family
|
||||||
|
*
|
||||||
|
* Resolves the generic netlink family name to its identifer and returns
|
||||||
|
* it.
|
||||||
|
*
|
||||||
|
* @return A positive identifier or a negative error code.
|
||||||
|
*/
|
||||||
|
int genl_ctrl_resolve(struct nl_sock *sk, const char *name)
|
||||||
|
{
|
||||||
|
struct nl_cache *cache;
|
||||||
|
struct genl_family *family;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if ((err = genl_ctrl_alloc_cache(sk, &cache)) < 0)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
family = genl_ctrl_search_by_name(cache, name);
|
||||||
|
if (family == NULL) {
|
||||||
|
err = -NLE_OBJ_NOTFOUND;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = genl_family_get_id(family);
|
||||||
|
genl_family_put(family);
|
||||||
|
errout:
|
||||||
|
nl_cache_free(cache);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
static struct genl_cmd genl_cmds[] = {
|
||||||
|
{
|
||||||
|
.c_id = CTRL_CMD_NEWFAMILY,
|
||||||
|
.c_name = "NEWFAMILY" ,
|
||||||
|
.c_maxattr = CTRL_ATTR_MAX,
|
||||||
|
.c_attr_policy = ctrl_policy,
|
||||||
|
.c_msg_parser = ctrl_msg_parser,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.c_id = CTRL_CMD_DELFAMILY,
|
||||||
|
.c_name = "DELFAMILY" ,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.c_id = CTRL_CMD_GETFAMILY,
|
||||||
|
.c_name = "GETFAMILY" ,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.c_id = CTRL_CMD_NEWOPS,
|
||||||
|
.c_name = "NEWOPS" ,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.c_id = CTRL_CMD_DELOPS,
|
||||||
|
.c_name = "DELOPS" ,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct genl_ops genl_ops = {
|
||||||
|
.o_cmds = genl_cmds,
|
||||||
|
.o_ncmds = ARRAY_SIZE(genl_cmds),
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @cond SKIP */
|
||||||
|
extern struct nl_object_ops genl_family_ops;
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
static struct nl_cache_ops genl_ctrl_ops = {
|
||||||
|
.co_name = "genl/family",
|
||||||
|
.co_hdrsize = GENL_HDRSIZE(0),
|
||||||
|
.co_msgtypes = GENL_FAMILY(GENL_ID_CTRL, "nlctrl"),
|
||||||
|
.co_genl = &genl_ops,
|
||||||
|
.co_protocol = NETLINK_GENERIC,
|
||||||
|
.co_request_update = ctrl_request_update,
|
||||||
|
.co_obj_ops = &genl_family_ops,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void __init ctrl_init(void)
|
||||||
|
{
|
||||||
|
genl_register(&genl_ctrl_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit ctrl_exit(void)
|
||||||
|
{
|
||||||
|
genl_unregister(&genl_ctrl_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* lib/genl/family.c Generic Netlink Family
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup genl
|
||||||
|
* @defgroup genl_family Generic Netlink Family
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-generic.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/genl/genl.h>
|
||||||
|
#include <netlink/genl/family.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
|
||||||
|
struct nl_object_ops genl_family_ops;
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
static void family_constructor(struct nl_object *c)
|
||||||
|
{
|
||||||
|
struct genl_family *family = (struct genl_family *) c;
|
||||||
|
|
||||||
|
nl_init_list_head(&family->gf_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void family_free_data(struct nl_object *c)
|
||||||
|
{
|
||||||
|
struct genl_family *family = (struct genl_family *) c;
|
||||||
|
struct genl_family_op *ops, *tmp;
|
||||||
|
|
||||||
|
if (family == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
nl_list_for_each_entry_safe(ops, tmp, &family->gf_ops, o_list) {
|
||||||
|
nl_list_del(&ops->o_list);
|
||||||
|
free(ops);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int family_clone(struct nl_object *_dst, struct nl_object *_src)
|
||||||
|
{
|
||||||
|
struct genl_family *dst = nl_object_priv(_dst);
|
||||||
|
struct genl_family *src = nl_object_priv(_src);
|
||||||
|
struct genl_family_op *ops;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
nl_list_for_each_entry(ops, &src->gf_ops, o_list) {
|
||||||
|
err = genl_family_add_op(dst, ops->o_id, ops->o_flags);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int family_compare(struct nl_object *_a, struct nl_object *_b,
|
||||||
|
uint32_t attrs, int flags)
|
||||||
|
{
|
||||||
|
struct genl_family *a = (struct genl_family *) _a;
|
||||||
|
struct genl_family *b = (struct genl_family *) _b;
|
||||||
|
int diff = 0;
|
||||||
|
|
||||||
|
#define FAM_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, FAMILY_ATTR_##ATTR, a, b, EXPR)
|
||||||
|
|
||||||
|
diff |= FAM_DIFF(ID, a->gf_id != b->gf_id);
|
||||||
|
diff |= FAM_DIFF(VERSION, a->gf_version != b->gf_version);
|
||||||
|
diff |= FAM_DIFF(HDRSIZE, a->gf_hdrsize != b->gf_hdrsize);
|
||||||
|
diff |= FAM_DIFF(MAXATTR, a->gf_maxattr != b->gf_maxattr);
|
||||||
|
diff |= FAM_DIFF(NAME, strcmp(a->gf_name, b->gf_name));
|
||||||
|
|
||||||
|
#undef FAM_DIFF
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Family Object
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct genl_family *genl_family_alloc(void)
|
||||||
|
{
|
||||||
|
return (struct genl_family *) nl_object_alloc(&genl_family_ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
void genl_family_put(struct genl_family *family)
|
||||||
|
{
|
||||||
|
nl_object_put((struct nl_object *) family);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
int genl_family_add_op(struct genl_family *family, int id, int flags)
|
||||||
|
{
|
||||||
|
struct genl_family_op *op;
|
||||||
|
|
||||||
|
op = calloc(1, sizeof(*op));
|
||||||
|
if (op == NULL)
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
op->o_id = id;
|
||||||
|
op->o_flags = flags;
|
||||||
|
|
||||||
|
nl_list_add_tail(&op->o_list, &family->gf_ops);
|
||||||
|
family->ce_mask |= FAMILY_ATTR_OPS;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** @cond SKIP */
|
||||||
|
struct nl_object_ops genl_family_ops = {
|
||||||
|
.oo_name = "genl/family",
|
||||||
|
.oo_size = sizeof(struct genl_family),
|
||||||
|
.oo_constructor = family_constructor,
|
||||||
|
.oo_free_data = family_free_data,
|
||||||
|
.oo_clone = family_clone,
|
||||||
|
.oo_compare = family_compare,
|
||||||
|
.oo_id_attrs = FAMILY_ATTR_ID,
|
||||||
|
};
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
/** @} */
|
|
@ -0,0 +1,273 @@
|
||||||
|
/*
|
||||||
|
* lib/genl/mngt.c Generic Netlink Management
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup genl
|
||||||
|
* @defgroup genl_mngt Management
|
||||||
|
*
|
||||||
|
* @par 1) Registering a generic netlink module
|
||||||
|
* @code
|
||||||
|
* #include <netlink/genl/mngt.h>
|
||||||
|
*
|
||||||
|
* // First step is to define all the commands being used in
|
||||||
|
* // particular generic netlink family. The ID and name are
|
||||||
|
* // mandatory to be filled out. A callback function and
|
||||||
|
* // most the attribute policy that comes with it must be
|
||||||
|
* // defined for commands expected to be issued towards
|
||||||
|
* // userspace.
|
||||||
|
* static struct genl_cmd foo_cmds[] = {
|
||||||
|
* {
|
||||||
|
* .c_id = FOO_CMD_NEW,
|
||||||
|
* .c_name = "NEWFOO" ,
|
||||||
|
* .c_maxattr = FOO_ATTR_MAX,
|
||||||
|
* .c_attr_policy = foo_policy,
|
||||||
|
* .c_msg_parser = foo_msg_parser,
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* .c_id = FOO_CMD_DEL,
|
||||||
|
* .c_name = "DELFOO" ,
|
||||||
|
* },
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* // The list of commands must then be integrated into a
|
||||||
|
* // struct genl_ops serving as handle for this particular
|
||||||
|
* // family.
|
||||||
|
* static struct genl_ops my_genl_ops = {
|
||||||
|
* .o_cmds = foo_cmds,
|
||||||
|
* .o_ncmds = ARRAY_SIZE(foo_cmds),
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* // Using the above struct genl_ops an arbitary number of
|
||||||
|
* // cache handles can be associated to it.
|
||||||
|
* //
|
||||||
|
* // The macro GENL_HDRSIZE() must be used to specify the
|
||||||
|
* // length of the header to automatically take headers on
|
||||||
|
* // generic layers into account.
|
||||||
|
* //
|
||||||
|
* // The macro GENL_FAMILY() is used to represent the generic
|
||||||
|
* // netlink family id.
|
||||||
|
* static struct nl_cache_ops genl_foo_ops = {
|
||||||
|
* .co_name = "genl/foo",
|
||||||
|
* .co_hdrsize = GENL_HDRSIZE(sizeof(struct my_hdr)),
|
||||||
|
* .co_msgtypes = GENL_FAMILY(GENL_ID_GENERATE, "foo"),
|
||||||
|
* .co_genl = &my_genl_ops,
|
||||||
|
* .co_protocol = NETLINK_GENERIC,
|
||||||
|
* .co_request_update = foo_request_update,
|
||||||
|
* .co_obj_ops = &genl_foo_ops,
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* // Finally each cache handle for a generic netlink family
|
||||||
|
* // must be registered using genl_register().
|
||||||
|
* static void __init foo_init(void)
|
||||||
|
* {
|
||||||
|
* genl_register(&genl_foo_ops);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // ... respectively unregsted again.
|
||||||
|
* static void __exit foo_exit(void)
|
||||||
|
* {
|
||||||
|
* genl_unregister(&genl_foo_ops);
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-generic.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/genl/genl.h>
|
||||||
|
#include <netlink/genl/mngt.h>
|
||||||
|
#include <netlink/genl/family.h>
|
||||||
|
#include <netlink/genl/ctrl.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
|
||||||
|
static NL_LIST_HEAD(genl_ops_list);
|
||||||
|
|
||||||
|
static int genl_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
|
||||||
|
struct nlmsghdr *nlh, struct nl_parser_param *pp)
|
||||||
|
{
|
||||||
|
int i, err;
|
||||||
|
struct genlmsghdr *ghdr;
|
||||||
|
struct genl_cmd *cmd;
|
||||||
|
|
||||||
|
ghdr = nlmsg_data(nlh);
|
||||||
|
|
||||||
|
if (ops->co_genl == NULL)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
for (i = 0; i < ops->co_genl->o_ncmds; i++) {
|
||||||
|
cmd = &ops->co_genl->o_cmds[i];
|
||||||
|
if (cmd->c_id == ghdr->cmd)
|
||||||
|
goto found;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = -NLE_MSGTYPE_NOSUPPORT;
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
found:
|
||||||
|
if (cmd->c_msg_parser == NULL)
|
||||||
|
err = -NLE_OPNOTSUPP;
|
||||||
|
else {
|
||||||
|
struct nlattr *tb[cmd->c_maxattr + 1];
|
||||||
|
struct genl_info info = {
|
||||||
|
.who = who,
|
||||||
|
.nlh = nlh,
|
||||||
|
.genlhdr = ghdr,
|
||||||
|
.userhdr = genlmsg_data(ghdr),
|
||||||
|
.attrs = tb,
|
||||||
|
};
|
||||||
|
|
||||||
|
err = nlmsg_parse(nlh, ops->co_hdrsize, tb, cmd->c_maxattr,
|
||||||
|
cmd->c_attr_policy);
|
||||||
|
if (err < 0)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
err = cmd->c_msg_parser(ops, cmd, &info, pp);
|
||||||
|
}
|
||||||
|
errout:
|
||||||
|
return err;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
char *genl_op2name(int family, int op, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct genl_ops *ops;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
|
||||||
|
if (ops->o_family == family) {
|
||||||
|
for (i = 0; i < ops->o_ncmds; i++) {
|
||||||
|
struct genl_cmd *cmd;
|
||||||
|
cmd = &ops->o_cmds[i];
|
||||||
|
|
||||||
|
if (cmd->c_id == op) {
|
||||||
|
strncpy(buf, cmd->c_name, len - 1);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(buf, "unknown", len - 1);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Register/Unregister
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register generic netlink operations
|
||||||
|
* @arg ops cache operations
|
||||||
|
*/
|
||||||
|
int genl_register(struct nl_cache_ops *ops)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (ops->co_protocol != NETLINK_GENERIC) {
|
||||||
|
err = -NLE_PROTO_MISMATCH;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ops->co_hdrsize < GENL_HDRSIZE(0)) {
|
||||||
|
err = -NLE_INVAL;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ops->co_genl == NULL) {
|
||||||
|
err = -NLE_INVAL;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
ops->co_genl->o_cache_ops = ops;
|
||||||
|
ops->co_genl->o_name = ops->co_msgtypes[0].mt_name;
|
||||||
|
ops->co_genl->o_family = ops->co_msgtypes[0].mt_id;
|
||||||
|
ops->co_msg_parser = genl_msg_parser;
|
||||||
|
|
||||||
|
/* FIXME: check for dup */
|
||||||
|
|
||||||
|
nl_list_add_tail(&ops->co_genl->o_list, &genl_ops_list);
|
||||||
|
|
||||||
|
err = nl_cache_mngt_register(ops);
|
||||||
|
errout:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregister generic netlink operations
|
||||||
|
* @arg ops cache operations
|
||||||
|
*/
|
||||||
|
void genl_unregister(struct nl_cache_ops *ops)
|
||||||
|
{
|
||||||
|
nl_cache_mngt_unregister(ops);
|
||||||
|
nl_list_del(&ops->co_genl->o_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Resolving ID/Name
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
#ifdef disabled
|
||||||
|
static int __genl_ops_resolve(struct nl_cache *ctrl, struct genl_ops *ops)
|
||||||
|
{
|
||||||
|
struct genl_family *family;
|
||||||
|
|
||||||
|
family = genl_ctrl_search_by_name(ctrl, ops->o_name);
|
||||||
|
if (family != NULL) {
|
||||||
|
ops->o_id = genl_family_get_id(family);
|
||||||
|
genl_family_put(family);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -NLE_OBJ_NOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
int genl_ops_resolve(struct nl_sock *sk, struct genl_ops *ops)
|
||||||
|
{
|
||||||
|
struct nl_cache *ctrl;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
err = __genl_ops_resolve(ctrl, ops);
|
||||||
|
|
||||||
|
nl_cache_free(ctrl);
|
||||||
|
errout:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
int genl_mngt_resolve(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
struct nl_cache *ctrl;
|
||||||
|
struct genl_ops *ops;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
if ((err = genl_ctrl_alloc_cache(sk, &ctrl)) < 0)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
nl_list_for_each_entry(ops, &genl_ops_list, o_list) {
|
||||||
|
err = __genl_ops_resolve(ctrl, ops);
|
||||||
|
}
|
||||||
|
|
||||||
|
nl_cache_free(ctrl);
|
||||||
|
errout:
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
/** @} */
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* lib/handlers.c default netlink message handlers
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup core
|
||||||
|
* @defgroup cb Callbacks/Customization
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* @par 1) Setting up a callback set
|
||||||
|
* @code
|
||||||
|
* // Allocate a callback set and initialize it to the verbose default set
|
||||||
|
* struct nl_cb *cb = nl_cb_alloc(NL_CB_VERBOSE);
|
||||||
|
*
|
||||||
|
* // Modify the set to call my_func() for all valid messages
|
||||||
|
* nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, my_func, NULL);
|
||||||
|
*
|
||||||
|
* // Set the error message handler to the verbose default implementation
|
||||||
|
* // and direct it to print all errors to the given file descriptor.
|
||||||
|
* FILE *file = fopen(...);
|
||||||
|
* nl_cb_err(cb, NL_CB_VERBOSE, NULL, file);
|
||||||
|
* @endcode
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-local.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
#include <netlink/msg.h>
|
||||||
|
#include <netlink/handlers.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Callback Handle Management
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new callback handle
|
||||||
|
* @arg kind callback kind to be used for initialization
|
||||||
|
* @return Newly allocated callback handle or NULL
|
||||||
|
*/
|
||||||
|
struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct nl_cb *cb;
|
||||||
|
|
||||||
|
if (kind < 0 || kind > NL_CB_KIND_MAX)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
cb = calloc(1, sizeof(*cb));
|
||||||
|
if (!cb)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
cb->cb_refcnt = 1;
|
||||||
|
|
||||||
|
for (i = 0; i <= NL_CB_TYPE_MAX; i++)
|
||||||
|
nl_cb_set(cb, i, kind, NULL, NULL);
|
||||||
|
|
||||||
|
nl_cb_err(cb, kind, NULL, NULL);
|
||||||
|
|
||||||
|
return cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clone an existing callback handle
|
||||||
|
* @arg orig original callback handle
|
||||||
|
* @return Newly allocated callback handle being a duplicate of
|
||||||
|
* orig or NULL
|
||||||
|
*/
|
||||||
|
struct nl_cb *nl_cb_clone(struct nl_cb *orig)
|
||||||
|
{
|
||||||
|
struct nl_cb *cb;
|
||||||
|
|
||||||
|
cb = nl_cb_alloc(NL_CB_DEFAULT);
|
||||||
|
if (!cb)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
memcpy(cb, orig, sizeof(*orig));
|
||||||
|
cb->cb_refcnt = 1;
|
||||||
|
|
||||||
|
return cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nl_cb_put(struct nl_cb *cb)
|
||||||
|
{
|
||||||
|
if (!cb)
|
||||||
|
return;
|
||||||
|
|
||||||
|
cb->cb_refcnt--;
|
||||||
|
|
||||||
|
if (cb->cb_refcnt < 0)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
if (cb->cb_refcnt <= 0)
|
||||||
|
free(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Callback Setup
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up a callback
|
||||||
|
* @arg cb callback set
|
||||||
|
* @arg type callback to modify
|
||||||
|
* @arg kind kind of implementation
|
||||||
|
* @arg func callback function (NL_CB_CUSTOM)
|
||||||
|
* @arg arg argument passed to callback
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code
|
||||||
|
*/
|
||||||
|
int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind,
|
||||||
|
nl_recvmsg_msg_cb_t func, void *arg)
|
||||||
|
{
|
||||||
|
if (type < 0 || type > NL_CB_TYPE_MAX)
|
||||||
|
return -NLE_RANGE;
|
||||||
|
|
||||||
|
if (kind < 0 || kind > NL_CB_KIND_MAX)
|
||||||
|
return -NLE_RANGE;
|
||||||
|
|
||||||
|
if (kind == NL_CB_CUSTOM) {
|
||||||
|
cb->cb_set[type] = func;
|
||||||
|
cb->cb_args[type] = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up an error callback
|
||||||
|
* @arg cb callback set
|
||||||
|
* @arg kind kind of callback
|
||||||
|
* @arg func callback function
|
||||||
|
* @arg arg argument to be passed to callback function
|
||||||
|
*/
|
||||||
|
int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind,
|
||||||
|
nl_recvmsg_err_cb_t func, void *arg)
|
||||||
|
{
|
||||||
|
if (kind < 0 || kind > NL_CB_KIND_MAX)
|
||||||
|
return -NLE_RANGE;
|
||||||
|
|
||||||
|
if (kind == NL_CB_CUSTOM) {
|
||||||
|
cb->cb_err = func;
|
||||||
|
cb->cb_err_arg = arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** @} */
|
|
@ -0,0 +1,69 @@
|
||||||
|
#ifndef __LINUX_GENERIC_NETLINK_H
|
||||||
|
#define __LINUX_GENERIC_NETLINK_H
|
||||||
|
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
|
||||||
|
#define GENL_NAMSIZ 16 /* length of family name */
|
||||||
|
|
||||||
|
#define GENL_MIN_ID NLMSG_MIN_TYPE
|
||||||
|
#define GENL_MAX_ID 1023
|
||||||
|
|
||||||
|
struct genlmsghdr {
|
||||||
|
__u8 cmd;
|
||||||
|
__u8 version;
|
||||||
|
__u16 reserved;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr))
|
||||||
|
|
||||||
|
#define GENL_ADMIN_PERM 0x01
|
||||||
|
#define GENL_CMD_CAP_DO 0x02
|
||||||
|
#define GENL_CMD_CAP_DUMP 0x04
|
||||||
|
#define GENL_CMD_CAP_HASPOL 0x08
|
||||||
|
|
||||||
|
/*
|
||||||
|
* List of reserved static generic netlink identifiers:
|
||||||
|
*/
|
||||||
|
#define GENL_ID_GENERATE 0
|
||||||
|
#define GENL_ID_CTRL NLMSG_MIN_TYPE
|
||||||
|
|
||||||
|
/**************************************************************************
|
||||||
|
* Controller
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CTRL_CMD_UNSPEC,
|
||||||
|
CTRL_CMD_NEWFAMILY,
|
||||||
|
CTRL_CMD_DELFAMILY,
|
||||||
|
CTRL_CMD_GETFAMILY,
|
||||||
|
CTRL_CMD_NEWOPS,
|
||||||
|
CTRL_CMD_DELOPS,
|
||||||
|
CTRL_CMD_GETOPS,
|
||||||
|
__CTRL_CMD_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CTRL_ATTR_UNSPEC,
|
||||||
|
CTRL_ATTR_FAMILY_ID,
|
||||||
|
CTRL_ATTR_FAMILY_NAME,
|
||||||
|
CTRL_ATTR_VERSION,
|
||||||
|
CTRL_ATTR_HDRSIZE,
|
||||||
|
CTRL_ATTR_MAXATTR,
|
||||||
|
CTRL_ATTR_OPS,
|
||||||
|
__CTRL_ATTR_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CTRL_ATTR_OP_UNSPEC,
|
||||||
|
CTRL_ATTR_OP_ID,
|
||||||
|
CTRL_ATTR_OP_FLAGS,
|
||||||
|
__CTRL_ATTR_OP_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1)
|
||||||
|
|
||||||
|
#endif /* __LINUX_GENERIC_NETLINK_H */
|
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* INET An implementation of the TCP/IP protocol suite for the LINUX
|
||||||
|
* operating system. INET is implemented using the BSD Socket
|
||||||
|
* interface as the means of communication with the user level.
|
||||||
|
*
|
||||||
|
* Global definitions for the INET interface module.
|
||||||
|
*
|
||||||
|
* Version: @(#)if.h 1.0.2 04/18/93
|
||||||
|
*
|
||||||
|
* Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988
|
||||||
|
* Ross Biro
|
||||||
|
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation; either version
|
||||||
|
* 2 of the License, or (at your option) any later version.
|
||||||
|
*/
|
||||||
|
#ifndef _LINUX_IF_H
|
||||||
|
#define _LINUX_IF_H
|
||||||
|
|
||||||
|
#include <linux/types.h> /* for "__kernel_caddr_t" et al */
|
||||||
|
|
||||||
|
#define IFNAMSIZ 16
|
||||||
|
|
||||||
|
/* Standard interface flags (netdevice->flags). */
|
||||||
|
#define IFF_UP 0x1 /* interface is up */
|
||||||
|
#define IFF_BROADCAST 0x2 /* broadcast address valid */
|
||||||
|
#define IFF_DEBUG 0x4 /* turn on debugging */
|
||||||
|
#define IFF_LOOPBACK 0x8 /* is a loopback net */
|
||||||
|
#define IFF_POINTOPOINT 0x10 /* interface is has p-p link */
|
||||||
|
#define IFF_NOTRAILERS 0x20 /* avoid use of trailers */
|
||||||
|
#define IFF_RUNNING 0x40 /* interface RFC2863 OPER_UP */
|
||||||
|
#define IFF_NOARP 0x80 /* no ARP protocol */
|
||||||
|
#define IFF_PROMISC 0x100 /* receive all packets */
|
||||||
|
#define IFF_ALLMULTI 0x200 /* receive all multicast packets*/
|
||||||
|
|
||||||
|
#define IFF_MASTER 0x400 /* master of a load balancer */
|
||||||
|
#define IFF_SLAVE 0x800 /* slave of a load balancer */
|
||||||
|
|
||||||
|
#define IFF_MULTICAST 0x1000 /* Supports multicast */
|
||||||
|
|
||||||
|
#define IFF_PORTSEL 0x2000 /* can set media type */
|
||||||
|
#define IFF_AUTOMEDIA 0x4000 /* auto media select active */
|
||||||
|
#define IFF_DYNAMIC 0x8000 /* dialup device with changing addresses*/
|
||||||
|
|
||||||
|
#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */
|
||||||
|
#define IFF_DORMANT 0x20000 /* driver signals dormant */
|
||||||
|
|
||||||
|
#define IFF_ECHO 0x40000 /* echo sent packets */
|
||||||
|
|
||||||
|
#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|\
|
||||||
|
IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
|
||||||
|
|
||||||
|
/* Private (from user) interface flags (netdevice->priv_flags). */
|
||||||
|
#define IFF_802_1Q_VLAN 0x1 /* 802.1Q VLAN device. */
|
||||||
|
#define IFF_EBRIDGE 0x2 /* Ethernet bridging device. */
|
||||||
|
#define IFF_SLAVE_INACTIVE 0x4 /* bonding slave not the curr. active */
|
||||||
|
#define IFF_MASTER_8023AD 0x8 /* bonding master, 802.3ad. */
|
||||||
|
#define IFF_MASTER_ALB 0x10 /* bonding master, balance-alb. */
|
||||||
|
#define IFF_BONDING 0x20 /* bonding master or slave */
|
||||||
|
#define IFF_SLAVE_NEEDARP 0x40 /* need ARPs for validation */
|
||||||
|
#define IFF_ISATAP 0x80 /* ISATAP interface (RFC4214) */
|
||||||
|
|
||||||
|
#define IF_GET_IFACE 0x0001 /* for querying only */
|
||||||
|
#define IF_GET_PROTO 0x0002
|
||||||
|
|
||||||
|
/* For definitions see hdlc.h */
|
||||||
|
#define IF_IFACE_V35 0x1000 /* V.35 serial interface */
|
||||||
|
#define IF_IFACE_V24 0x1001 /* V.24 serial interface */
|
||||||
|
#define IF_IFACE_X21 0x1002 /* X.21 serial interface */
|
||||||
|
#define IF_IFACE_T1 0x1003 /* T1 telco serial interface */
|
||||||
|
#define IF_IFACE_E1 0x1004 /* E1 telco serial interface */
|
||||||
|
#define IF_IFACE_SYNC_SERIAL 0x1005 /* can't be set by software */
|
||||||
|
#define IF_IFACE_X21D 0x1006 /* X.21 Dual Clocking (FarSite) */
|
||||||
|
|
||||||
|
/* For definitions see hdlc.h */
|
||||||
|
#define IF_PROTO_HDLC 0x2000 /* raw HDLC protocol */
|
||||||
|
#define IF_PROTO_PPP 0x2001 /* PPP protocol */
|
||||||
|
#define IF_PROTO_CISCO 0x2002 /* Cisco HDLC protocol */
|
||||||
|
#define IF_PROTO_FR 0x2003 /* Frame Relay protocol */
|
||||||
|
#define IF_PROTO_FR_ADD_PVC 0x2004 /* Create FR PVC */
|
||||||
|
#define IF_PROTO_FR_DEL_PVC 0x2005 /* Delete FR PVC */
|
||||||
|
#define IF_PROTO_X25 0x2006 /* X.25 */
|
||||||
|
#define IF_PROTO_HDLC_ETH 0x2007 /* raw HDLC, Ethernet emulation */
|
||||||
|
#define IF_PROTO_FR_ADD_ETH_PVC 0x2008 /* Create FR Ethernet-bridged PVC */
|
||||||
|
#define IF_PROTO_FR_DEL_ETH_PVC 0x2009 /* Delete FR Ethernet-bridged PVC */
|
||||||
|
#define IF_PROTO_FR_PVC 0x200A /* for reading PVC status */
|
||||||
|
#define IF_PROTO_FR_ETH_PVC 0x200B
|
||||||
|
#define IF_PROTO_RAW 0x200C /* RAW Socket */
|
||||||
|
|
||||||
|
/* RFC 2863 operational status */
|
||||||
|
enum {
|
||||||
|
IF_OPER_UNKNOWN,
|
||||||
|
IF_OPER_NOTPRESENT,
|
||||||
|
IF_OPER_DOWN,
|
||||||
|
IF_OPER_LOWERLAYERDOWN,
|
||||||
|
IF_OPER_TESTING,
|
||||||
|
IF_OPER_DORMANT,
|
||||||
|
IF_OPER_UP,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* link modes */
|
||||||
|
enum {
|
||||||
|
IF_LINK_MODE_DEFAULT,
|
||||||
|
IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Device mapping structure. I'd just gone off and designed a
|
||||||
|
* beautiful scheme using only loadable modules with arguments
|
||||||
|
* for driver options and along come the PCMCIA people 8)
|
||||||
|
*
|
||||||
|
* Ah well. The get() side of this is good for WDSETUP, and it'll
|
||||||
|
* be handy for debugging things. The set side is fine for now and
|
||||||
|
* being very small might be worth keeping for clean configuration.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ifmap
|
||||||
|
{
|
||||||
|
unsigned long mem_start;
|
||||||
|
unsigned long mem_end;
|
||||||
|
unsigned short base_addr;
|
||||||
|
unsigned char irq;
|
||||||
|
unsigned char dma;
|
||||||
|
unsigned char port;
|
||||||
|
/* 3 bytes spare */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _LINUX_IF_H */
|
|
@ -0,0 +1,62 @@
|
||||||
|
#ifndef __LINUX_IF_ADDR_H
|
||||||
|
#define __LINUX_IF_ADDR_H
|
||||||
|
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
|
||||||
|
struct ifaddrmsg
|
||||||
|
{
|
||||||
|
__u8 ifa_family;
|
||||||
|
__u8 ifa_prefixlen; /* The prefix length */
|
||||||
|
__u8 ifa_flags; /* Flags */
|
||||||
|
__u8 ifa_scope; /* Address scope */
|
||||||
|
__u32 ifa_index; /* Link index */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Important comment:
|
||||||
|
* IFA_ADDRESS is prefix address, rather than local interface address.
|
||||||
|
* It makes no difference for normally configured broadcast interfaces,
|
||||||
|
* but for point-to-point IFA_ADDRESS is DESTINATION address,
|
||||||
|
* local address is supplied in IFA_LOCAL attribute.
|
||||||
|
*/
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
IFA_UNSPEC,
|
||||||
|
IFA_ADDRESS,
|
||||||
|
IFA_LOCAL,
|
||||||
|
IFA_LABEL,
|
||||||
|
IFA_BROADCAST,
|
||||||
|
IFA_ANYCAST,
|
||||||
|
IFA_CACHEINFO,
|
||||||
|
IFA_MULTICAST,
|
||||||
|
__IFA_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFA_MAX (__IFA_MAX - 1)
|
||||||
|
|
||||||
|
/* ifa_flags */
|
||||||
|
#define IFA_F_SECONDARY 0x01
|
||||||
|
#define IFA_F_TEMPORARY IFA_F_SECONDARY
|
||||||
|
|
||||||
|
#define IFA_F_NODAD 0x02
|
||||||
|
#define IFA_F_OPTIMISTIC 0x04
|
||||||
|
#define IFA_F_HOMEADDRESS 0x10
|
||||||
|
#define IFA_F_DEPRECATED 0x20
|
||||||
|
#define IFA_F_TENTATIVE 0x40
|
||||||
|
#define IFA_F_PERMANENT 0x80
|
||||||
|
|
||||||
|
struct ifa_cacheinfo
|
||||||
|
{
|
||||||
|
__u32 ifa_prefered;
|
||||||
|
__u32 ifa_valid;
|
||||||
|
__u32 cstamp; /* created timestamp, hundredths of seconds */
|
||||||
|
__u32 tstamp; /* updated timestamp, hundredths of seconds */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* backwards compatibility for userspace */
|
||||||
|
#ifndef __KERNEL__
|
||||||
|
#define IFA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
|
||||||
|
#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,150 @@
|
||||||
|
#ifndef __LINUX_NETLINK_H
|
||||||
|
#define __LINUX_NETLINK_H
|
||||||
|
|
||||||
|
#include <linux/socket.h> /* for sa_family_t */
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#define NETLINK_ROUTE 0 /* Routing/device hook */
|
||||||
|
#define NETLINK_UNUSED 1 /* Unused number */
|
||||||
|
#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
|
||||||
|
#define NETLINK_FIREWALL 3 /* Firewalling hook */
|
||||||
|
#define NETLINK_INET_DIAG 4 /* INET socket monitoring */
|
||||||
|
#define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */
|
||||||
|
#define NETLINK_XFRM 6 /* ipsec */
|
||||||
|
#define NETLINK_SELINUX 7 /* SELinux event notifications */
|
||||||
|
#define NETLINK_ISCSI 8 /* Open-iSCSI */
|
||||||
|
#define NETLINK_AUDIT 9 /* auditing */
|
||||||
|
#define NETLINK_FIB_LOOKUP 10
|
||||||
|
#define NETLINK_CONNECTOR 11
|
||||||
|
#define NETLINK_NETFILTER 12 /* netfilter subsystem */
|
||||||
|
#define NETLINK_IP6_FW 13
|
||||||
|
#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
|
||||||
|
#define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */
|
||||||
|
#define NETLINK_GENERIC 16
|
||||||
|
/* leave room for NETLINK_DM (DM Events) */
|
||||||
|
#define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */
|
||||||
|
#define NETLINK_ECRYPTFS 19
|
||||||
|
|
||||||
|
#define MAX_LINKS 32
|
||||||
|
|
||||||
|
struct sockaddr_nl
|
||||||
|
{
|
||||||
|
sa_family_t nl_family; /* AF_NETLINK */
|
||||||
|
unsigned short nl_pad; /* zero */
|
||||||
|
__u32 nl_pid; /* port ID */
|
||||||
|
__u32 nl_groups; /* multicast groups mask */
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nlmsghdr
|
||||||
|
{
|
||||||
|
__u32 nlmsg_len; /* Length of message including header */
|
||||||
|
__u16 nlmsg_type; /* Message content */
|
||||||
|
__u16 nlmsg_flags; /* Additional flags */
|
||||||
|
__u32 nlmsg_seq; /* Sequence number */
|
||||||
|
__u32 nlmsg_pid; /* Sending process port ID */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Flags values */
|
||||||
|
|
||||||
|
#define NLM_F_REQUEST 1 /* It is request message. */
|
||||||
|
#define NLM_F_MULTI 2 /* Multipart message, terminated by NLMSG_DONE */
|
||||||
|
#define NLM_F_ACK 4 /* Reply with ack, with zero or error code */
|
||||||
|
#define NLM_F_ECHO 8 /* Echo this request */
|
||||||
|
|
||||||
|
/* Modifiers to GET request */
|
||||||
|
#define NLM_F_ROOT 0x100 /* specify tree root */
|
||||||
|
#define NLM_F_MATCH 0x200 /* return all matching */
|
||||||
|
#define NLM_F_ATOMIC 0x400 /* atomic GET */
|
||||||
|
#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
|
||||||
|
|
||||||
|
/* Modifiers to NEW request */
|
||||||
|
#define NLM_F_REPLACE 0x100 /* Override existing */
|
||||||
|
#define NLM_F_EXCL 0x200 /* Do not touch, if it exists */
|
||||||
|
#define NLM_F_CREATE 0x400 /* Create, if it does not exist */
|
||||||
|
#define NLM_F_APPEND 0x800 /* Add to end of list */
|
||||||
|
|
||||||
|
/*
|
||||||
|
4.4BSD ADD NLM_F_CREATE|NLM_F_EXCL
|
||||||
|
4.4BSD CHANGE NLM_F_REPLACE
|
||||||
|
|
||||||
|
True CHANGE NLM_F_CREATE|NLM_F_REPLACE
|
||||||
|
Append NLM_F_CREATE
|
||||||
|
Check NLM_F_EXCL
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define NLMSG_ALIGNTO 4
|
||||||
|
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
|
||||||
|
#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
|
||||||
|
#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN))
|
||||||
|
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
|
||||||
|
#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
|
||||||
|
#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
|
||||||
|
(struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
|
||||||
|
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
|
||||||
|
(nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
|
||||||
|
(nlh)->nlmsg_len <= (len))
|
||||||
|
#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
|
||||||
|
|
||||||
|
#define NLMSG_NOOP 0x1 /* Nothing. */
|
||||||
|
#define NLMSG_ERROR 0x2 /* Error */
|
||||||
|
#define NLMSG_DONE 0x3 /* End of a dump */
|
||||||
|
#define NLMSG_OVERRUN 0x4 /* Data lost */
|
||||||
|
|
||||||
|
#define NLMSG_MIN_TYPE 0x10 /* < 0x10: reserved control messages */
|
||||||
|
|
||||||
|
struct nlmsgerr
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
struct nlmsghdr msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NETLINK_ADD_MEMBERSHIP 1
|
||||||
|
#define NETLINK_DROP_MEMBERSHIP 2
|
||||||
|
#define NETLINK_PKTINFO 3
|
||||||
|
|
||||||
|
struct nl_pktinfo
|
||||||
|
{
|
||||||
|
__u32 group;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NET_MAJOR 36 /* Major 36 is reserved for networking */
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NETLINK_UNCONNECTED = 0,
|
||||||
|
NETLINK_CONNECTED,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* <------- NLA_HDRLEN ------> <-- NLA_ALIGN(payload)-->
|
||||||
|
* +---------------------+- - -+- - - - - - - - - -+- - -+
|
||||||
|
* | Header | Pad | Payload | Pad |
|
||||||
|
* | (struct nlattr) | ing | | ing |
|
||||||
|
* +---------------------+- - -+- - - - - - - - - -+- - -+
|
||||||
|
* <-------------- nlattr->nla_len -------------->
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct nlattr
|
||||||
|
{
|
||||||
|
__u16 nla_len;
|
||||||
|
__u16 nla_type;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* nla_type (16 bits)
|
||||||
|
* +---+---+-------------------------------+
|
||||||
|
* | N | O | Attribute Type |
|
||||||
|
* +---+---+-------------------------------+
|
||||||
|
* N := Carries nested attributes
|
||||||
|
* O := Payload stored in network byte order
|
||||||
|
*
|
||||||
|
* Note: The N and O flag are mutually exclusive.
|
||||||
|
*/
|
||||||
|
#define NLA_F_NESTED (1 << 15)
|
||||||
|
#define NLA_F_NET_BYTEORDER (1 << 14)
|
||||||
|
#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
|
||||||
|
|
||||||
|
#define NLA_ALIGNTO 4
|
||||||
|
#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
|
||||||
|
#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr)))
|
||||||
|
|
||||||
|
#endif /* __LINUX_NETLINK_H */
|
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* netlink-generic.h Local Generic Netlink Interface
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_GENL_PRIV_H_
|
||||||
|
#define NETLINK_GENL_PRIV_H_
|
||||||
|
|
||||||
|
#include <netlink-local.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
|
||||||
|
#define GENL_HDRSIZE(hdrlen) (GENL_HDRLEN + (hdrlen))
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,153 @@
|
||||||
|
/*
|
||||||
|
* netlink-local.h Local Netlink Interface
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_LOCAL_H_
|
||||||
|
#define NETLINK_LOCAL_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
#ifndef SOL_NETLINK
|
||||||
|
#define SOL_NETLINK 270
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
/* local header copies */
|
||||||
|
#include <linux/if.h>
|
||||||
|
#include <linux/if_arp.h>
|
||||||
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/pkt_sched.h>
|
||||||
|
#include <linux/pkt_cls.h>
|
||||||
|
#include <linux/gen_stats.h>
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/handlers.h>
|
||||||
|
#include <netlink/cache.h>
|
||||||
|
#include <netlink/object-api.h>
|
||||||
|
#include <netlink/cache-api.h>
|
||||||
|
#include <netlink-types.h>
|
||||||
|
|
||||||
|
struct trans_tbl {
|
||||||
|
int i;
|
||||||
|
const char *a;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define __ADD(id, name) { .i = id, .a = #name },
|
||||||
|
|
||||||
|
struct trans_list {
|
||||||
|
int i;
|
||||||
|
char *a;
|
||||||
|
struct nl_list_head list;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NL_DEBUG 1
|
||||||
|
|
||||||
|
#define NL_DBG(LVL,FMT,ARG...) \
|
||||||
|
do {} while (0)
|
||||||
|
|
||||||
|
#define BUG() \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, "BUG: %s:%d\n", \
|
||||||
|
__FILE__, __LINE__); \
|
||||||
|
assert(0); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
extern int __nl_read_num_str_file(const char *path,
|
||||||
|
int (*cb)(long, const char *));
|
||||||
|
|
||||||
|
extern int __trans_list_add(int, const char *, struct nl_list_head *);
|
||||||
|
extern void __trans_list_clear(struct nl_list_head *);
|
||||||
|
|
||||||
|
extern char *__type2str(int, char *, size_t, struct trans_tbl *, size_t);
|
||||||
|
extern int __str2type(const char *, struct trans_tbl *, size_t);
|
||||||
|
|
||||||
|
extern char *__list_type2str(int, char *, size_t, struct nl_list_head *);
|
||||||
|
extern int __list_str2type(const char *, struct nl_list_head *);
|
||||||
|
|
||||||
|
extern char *__flags2str(int, char *, size_t, struct trans_tbl *, size_t);
|
||||||
|
extern int __str2flags(const char *, struct trans_tbl *, size_t);
|
||||||
|
|
||||||
|
extern void dump_from_ops(struct nl_object *, struct nl_dump_params *);
|
||||||
|
|
||||||
|
static inline struct nl_cache *dp_cache(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
if (obj->ce_cache == NULL)
|
||||||
|
return nl_cache_mngt_require(obj->ce_ops->oo_name);
|
||||||
|
|
||||||
|
return obj->ce_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int nl_cb_call(struct nl_cb *cb, int type, struct nl_msg *msg)
|
||||||
|
{
|
||||||
|
return cb->cb_set[type](msg, cb->cb_args[type]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0]))
|
||||||
|
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||||
|
|
||||||
|
#define __init __attribute__ ((constructor))
|
||||||
|
#define __exit __attribute__ ((destructor))
|
||||||
|
#undef __deprecated
|
||||||
|
#define __deprecated __attribute__ ((deprecated))
|
||||||
|
|
||||||
|
#define min(x,y) ({ \
|
||||||
|
typeof(x) _x = (x); \
|
||||||
|
typeof(y) _y = (y); \
|
||||||
|
(void) (&_x == &_y); \
|
||||||
|
_x < _y ? _x : _y; })
|
||||||
|
|
||||||
|
#define max(x,y) ({ \
|
||||||
|
typeof(x) _x = (x); \
|
||||||
|
typeof(y) _y = (y); \
|
||||||
|
(void) (&_x == &_y); \
|
||||||
|
_x > _y ? _x : _y; })
|
||||||
|
|
||||||
|
extern int nl_cache_parse(struct nl_cache_ops *, struct sockaddr_nl *,
|
||||||
|
struct nlmsghdr *, struct nl_parser_param *);
|
||||||
|
|
||||||
|
|
||||||
|
static inline char *nl_cache_name(struct nl_cache *cache)
|
||||||
|
{
|
||||||
|
return cache->c_ops ? cache->c_ops->co_name : "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
#define GENL_FAMILY(id, name) \
|
||||||
|
{ \
|
||||||
|
{ id, NL_ACT_UNSPEC, name }, \
|
||||||
|
END_OF_MSGTYPES_LIST, \
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int wait_for_ack(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
if (sk->s_flags & NL_NO_AUTO_ACK)
|
||||||
|
return 0;
|
||||||
|
else
|
||||||
|
return nl_wait_for_ack(sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* netlink-types.h Netlink Types (Private)
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_LOCAL_TYPES_H_
|
||||||
|
#define NETLINK_LOCAL_TYPES_H_
|
||||||
|
|
||||||
|
#include <netlink/list.h>
|
||||||
|
|
||||||
|
struct nl_cache_ops;
|
||||||
|
struct nl_sock;
|
||||||
|
struct nl_object;
|
||||||
|
|
||||||
|
struct nl_cache
|
||||||
|
{
|
||||||
|
struct nl_list_head c_items;
|
||||||
|
int c_nitems;
|
||||||
|
int c_iarg1;
|
||||||
|
int c_iarg2;
|
||||||
|
struct nl_cache_ops * c_ops;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nl_cache_assoc
|
||||||
|
{
|
||||||
|
struct nl_cache * ca_cache;
|
||||||
|
change_func_t ca_change;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nl_cache_mngr
|
||||||
|
{
|
||||||
|
int cm_protocol;
|
||||||
|
int cm_flags;
|
||||||
|
int cm_nassocs;
|
||||||
|
struct nl_sock * cm_handle;
|
||||||
|
struct nl_cache_assoc * cm_assocs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nl_parser_param;
|
||||||
|
|
||||||
|
#define LOOSE_COMPARISON 1
|
||||||
|
|
||||||
|
|
||||||
|
struct nl_data
|
||||||
|
{
|
||||||
|
size_t d_size;
|
||||||
|
void * d_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nl_addr
|
||||||
|
{
|
||||||
|
int a_family;
|
||||||
|
unsigned int a_maxsize;
|
||||||
|
unsigned int a_len;
|
||||||
|
int a_prefixlen;
|
||||||
|
int a_refcnt;
|
||||||
|
char a_addr[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define IFQDISCSIZ 32
|
||||||
|
|
||||||
|
#define GENL_OP_HAS_POLICY 1
|
||||||
|
#define GENL_OP_HAS_DOIT 2
|
||||||
|
#define GENL_OP_HAS_DUMPIT 4
|
||||||
|
|
||||||
|
struct genl_family_op
|
||||||
|
{
|
||||||
|
uint32_t o_id;
|
||||||
|
uint32_t o_flags;
|
||||||
|
|
||||||
|
struct nl_list_head o_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* netlink/addr.h Abstract Address
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_ADDR_H_
|
||||||
|
#define NETLINK_ADDR_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct nl_addr;
|
||||||
|
|
||||||
|
/* Creation */
|
||||||
|
extern struct nl_addr * nl_addr_alloc(size_t);
|
||||||
|
extern struct nl_addr * nl_addr_alloc_attr(struct nlattr *, int);
|
||||||
|
extern struct nl_addr * nl_addr_build(int, void *, size_t);
|
||||||
|
extern int nl_addr_parse(const char *, int, struct nl_addr **);
|
||||||
|
extern struct nl_addr * nl_addr_clone(struct nl_addr *);
|
||||||
|
|
||||||
|
/* Destroyage */
|
||||||
|
extern void nl_addr_destroy(struct nl_addr *);
|
||||||
|
|
||||||
|
/* Usage Management */
|
||||||
|
extern struct nl_addr * nl_addr_get(struct nl_addr *);
|
||||||
|
extern void nl_addr_put(struct nl_addr *);
|
||||||
|
extern int nl_addr_shared(struct nl_addr *);
|
||||||
|
|
||||||
|
extern int nl_addr_cmp(struct nl_addr *, struct nl_addr *);
|
||||||
|
extern int nl_addr_cmp_prefix(struct nl_addr *, struct nl_addr *);
|
||||||
|
extern int nl_addr_iszero(struct nl_addr *);
|
||||||
|
extern int nl_addr_valid(char *, int);
|
||||||
|
extern int nl_addr_guess_family(struct nl_addr *);
|
||||||
|
extern int nl_addr_fill_sockaddr(struct nl_addr *,
|
||||||
|
struct sockaddr *, socklen_t *);
|
||||||
|
extern int nl_addr_info(struct nl_addr *, struct addrinfo **);
|
||||||
|
extern int nl_addr_resolve(struct nl_addr *addr, char *host, size_t hostlen);
|
||||||
|
|
||||||
|
/* Access Functions */
|
||||||
|
extern void nl_addr_set_family(struct nl_addr *, int);
|
||||||
|
extern int nl_addr_get_family(struct nl_addr *);
|
||||||
|
extern int nl_addr_set_binary_addr(struct nl_addr *, void *,
|
||||||
|
size_t);
|
||||||
|
extern void * nl_addr_get_binary_addr(struct nl_addr *);
|
||||||
|
extern unsigned int nl_addr_get_len(struct nl_addr *);
|
||||||
|
extern void nl_addr_set_prefixlen(struct nl_addr *, int);
|
||||||
|
extern unsigned int nl_addr_get_prefixlen(struct nl_addr *);
|
||||||
|
|
||||||
|
/* Address Family Translations */
|
||||||
|
extern char * nl_af2str(int, char *, size_t);
|
||||||
|
extern int nl_str2af(const char *);
|
||||||
|
|
||||||
|
/* Translations to Strings */
|
||||||
|
extern char * nl_addr2str(struct nl_addr *, char *, size_t);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,726 @@
|
||||||
|
/*
|
||||||
|
* netlink/attr.h Netlink Attributes
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_ATTR_H_
|
||||||
|
#define NETLINK_ATTR_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/object.h>
|
||||||
|
#include <netlink/addr.h>
|
||||||
|
#include <netlink/data.h>
|
||||||
|
#include <netlink/msg.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct nl_msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Basic Attribute Data Types
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup attr
|
||||||
|
* Basic attribute data types
|
||||||
|
*
|
||||||
|
* See \ref attr_datatypes for more details.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
NLA_UNSPEC, /**< Unspecified type, binary data chunk */
|
||||||
|
NLA_U8, /**< 8 bit integer */
|
||||||
|
NLA_U16, /**< 16 bit integer */
|
||||||
|
NLA_U32, /**< 32 bit integer */
|
||||||
|
NLA_U64, /**< 64 bit integer */
|
||||||
|
NLA_STRING, /**< NUL terminated character string */
|
||||||
|
NLA_FLAG, /**< Flag */
|
||||||
|
NLA_MSECS, /**< Micro seconds (64bit) */
|
||||||
|
NLA_NESTED, /**< Nested attributes */
|
||||||
|
__NLA_TYPE_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NLA_TYPE_MAX (__NLA_TYPE_MAX - 1)
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup attr
|
||||||
|
* Attribute validation policy.
|
||||||
|
*
|
||||||
|
* See \ref attr_datatypes for more details.
|
||||||
|
*/
|
||||||
|
struct nla_policy {
|
||||||
|
/** Type of attribute or NLA_UNSPEC */
|
||||||
|
uint16_t type;
|
||||||
|
|
||||||
|
/** Minimal length of payload required */
|
||||||
|
uint16_t minlen;
|
||||||
|
|
||||||
|
/** Maximal length of payload allowed */
|
||||||
|
uint16_t maxlen;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Attribute parsing */
|
||||||
|
extern int nla_ok(const struct nlattr *, int);
|
||||||
|
extern struct nlattr * nla_next(const struct nlattr *, int *);
|
||||||
|
extern int nla_parse(struct nlattr **, int, struct nlattr *,
|
||||||
|
int, struct nla_policy *);
|
||||||
|
extern int nla_validate(struct nlattr *, int, int,
|
||||||
|
struct nla_policy *);
|
||||||
|
extern struct nlattr * nla_find(struct nlattr *, int, int);
|
||||||
|
|
||||||
|
/* Unspecific attribute */
|
||||||
|
extern struct nlattr * nla_reserve(struct nl_msg *, int, int);
|
||||||
|
extern int nla_put(struct nl_msg *, int, int, const void *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nlmsg_find_attr - find a specific attribute in a netlink message
|
||||||
|
* @arg nlh netlink message header
|
||||||
|
* @arg hdrlen length of familiy specific header
|
||||||
|
* @arg attrtype type of attribute to look for
|
||||||
|
*
|
||||||
|
* Returns the first attribute which matches the specified type.
|
||||||
|
*/
|
||||||
|
static inline struct nlattr *nlmsg_find_attr(struct nlmsghdr *nlh, int hdrlen, int attrtype)
|
||||||
|
{
|
||||||
|
return nla_find(nlmsg_attrdata(nlh, hdrlen),
|
||||||
|
nlmsg_attrlen(nlh, hdrlen), attrtype);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return size of attribute whithout padding.
|
||||||
|
* @arg payload Payload length of attribute.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* <-------- nla_attr_size(payload) --------->
|
||||||
|
* +------------------+- - -+- - - - - - - - - +- - -+
|
||||||
|
* | Attribute Header | Pad | Payload | Pad |
|
||||||
|
* +------------------+- - -+- - - - - - - - - +- - -+
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @return Size of attribute in bytes without padding.
|
||||||
|
*/
|
||||||
|
static inline int nla_attr_size(int payload)
|
||||||
|
{
|
||||||
|
return NLA_HDRLEN + payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return size of attribute including padding.
|
||||||
|
* @arg payload Payload length of attribute.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* <----------- nla_total_size(payload) ----------->
|
||||||
|
* +------------------+- - -+- - - - - - - - - +- - -+
|
||||||
|
* | Attribute Header | Pad | Payload | Pad |
|
||||||
|
* +------------------+- - -+- - - - - - - - - +- - -+
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @return Size of attribute in bytes.
|
||||||
|
*/
|
||||||
|
static inline int nla_total_size(int payload)
|
||||||
|
{
|
||||||
|
return NLA_ALIGN(nla_attr_size(payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return length of padding at the tail of the attribute.
|
||||||
|
* @arg payload Payload length of attribute.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* +------------------+- - -+- - - - - - - - - +- - -+
|
||||||
|
* | Attribute Header | Pad | Payload | Pad |
|
||||||
|
* +------------------+- - -+- - - - - - - - - +- - -+
|
||||||
|
* <--->
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @return Length of padding in bytes.
|
||||||
|
*/
|
||||||
|
static inline int nla_padlen(int payload)
|
||||||
|
{
|
||||||
|
return nla_total_size(payload) - nla_attr_size(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return type of the attribute.
|
||||||
|
* @arg nla Attribute.
|
||||||
|
*
|
||||||
|
* @return Type of attribute.
|
||||||
|
*/
|
||||||
|
static inline int nla_type(const struct nlattr *nla)
|
||||||
|
{
|
||||||
|
return nla->nla_type & NLA_TYPE_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return pointer to the payload section.
|
||||||
|
* @arg nla Attribute.
|
||||||
|
*
|
||||||
|
* @return Pointer to start of payload section.
|
||||||
|
*/
|
||||||
|
static inline void *nla_data(const struct nlattr *nla)
|
||||||
|
{
|
||||||
|
return (char *) nla + NLA_HDRLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return length of the payload .
|
||||||
|
* @arg nla Attribute
|
||||||
|
*
|
||||||
|
* @return Length of payload in bytes.
|
||||||
|
*/
|
||||||
|
static inline int nla_len(const struct nlattr *nla)
|
||||||
|
{
|
||||||
|
return nla->nla_len - NLA_HDRLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy attribute payload to another memory area.
|
||||||
|
* @arg dest Pointer to destination memory area.
|
||||||
|
* @arg src Attribute
|
||||||
|
* @arg count Number of bytes to copy at most.
|
||||||
|
*
|
||||||
|
* Note: The number of bytes copied is limited by the length of
|
||||||
|
* the attribute payload.
|
||||||
|
*
|
||||||
|
* @return The number of bytes copied to dest.
|
||||||
|
*/
|
||||||
|
static inline int nla_memcpy(void *dest, struct nlattr *src, int count)
|
||||||
|
{
|
||||||
|
int minlen;
|
||||||
|
|
||||||
|
if (!src)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
minlen = min_t(int, count, nla_len(src));
|
||||||
|
memcpy(dest, nla_data(src), minlen);
|
||||||
|
|
||||||
|
return minlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add abstract data as unspecific attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg data Abstract data object.
|
||||||
|
*
|
||||||
|
* Equivalent to nla_put() except that the length of the payload is
|
||||||
|
* derived from the abstract data object.
|
||||||
|
*
|
||||||
|
* @see nla_put
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
static inline int nla_put_data(struct nl_msg *msg, int attrtype, struct nl_data *data)
|
||||||
|
{
|
||||||
|
return nla_put(msg, attrtype, nl_data_get_size(data),
|
||||||
|
nl_data_get(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add abstract address as unspecific attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg addr Abstract address object.
|
||||||
|
*
|
||||||
|
* @see nla_put
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
static inline int nla_put_addr(struct nl_msg *msg, int attrtype, struct nl_addr *addr)
|
||||||
|
{
|
||||||
|
return nla_put(msg, attrtype, nl_addr_get_len(addr),
|
||||||
|
nl_addr_get_binary_addr(addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Integer Attributes
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add 8 bit integer attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg value Numeric value to store as payload.
|
||||||
|
*
|
||||||
|
* @see nla_put
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
static inline int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value)
|
||||||
|
{
|
||||||
|
return nla_put(msg, attrtype, sizeof(uint8_t), &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return value of 8 bit integer attribute.
|
||||||
|
* @arg nla 8 bit integer attribute
|
||||||
|
*
|
||||||
|
* @return Payload as 8 bit integer.
|
||||||
|
*/
|
||||||
|
static inline uint8_t nla_get_u8(struct nlattr *nla)
|
||||||
|
{
|
||||||
|
return *(uint8_t *) nla_data(nla);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add 16 bit integer attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg value Numeric value to store as payload.
|
||||||
|
*
|
||||||
|
* @see nla_put
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
static inline int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value)
|
||||||
|
{
|
||||||
|
return nla_put(msg, attrtype, sizeof(uint16_t), &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return payload of 16 bit integer attribute.
|
||||||
|
* @arg nla 16 bit integer attribute
|
||||||
|
*
|
||||||
|
* @return Payload as 16 bit integer.
|
||||||
|
*/
|
||||||
|
static inline uint16_t nla_get_u16(struct nlattr *nla)
|
||||||
|
{
|
||||||
|
return *(uint16_t *) nla_data(nla);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add 32 bit integer attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg value Numeric value to store as payload.
|
||||||
|
*
|
||||||
|
* @see nla_put
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
static inline int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value)
|
||||||
|
{
|
||||||
|
return nla_put(msg, attrtype, sizeof(uint32_t), &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return payload of 32 bit integer attribute.
|
||||||
|
* @arg nla 32 bit integer attribute.
|
||||||
|
*
|
||||||
|
* @return Payload as 32 bit integer.
|
||||||
|
*/
|
||||||
|
static inline uint32_t nla_get_u32(struct nlattr *nla)
|
||||||
|
{
|
||||||
|
return *(uint32_t *) nla_data(nla);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add 64 bit integer attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg value Numeric value to store as payload.
|
||||||
|
*
|
||||||
|
* @see nla_put
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
static inline int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value)
|
||||||
|
{
|
||||||
|
return nla_put(msg, attrtype, sizeof(uint64_t), &value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return payload of u64 attribute
|
||||||
|
* @arg nla u64 netlink attribute
|
||||||
|
*
|
||||||
|
* @return Payload as 64 bit integer.
|
||||||
|
*/
|
||||||
|
static inline uint64_t nla_get_u64(struct nlattr *nla)
|
||||||
|
{
|
||||||
|
uint64_t tmp;
|
||||||
|
|
||||||
|
nla_memcpy(&tmp, nla, sizeof(tmp));
|
||||||
|
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add string attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg str NUL terminated string.
|
||||||
|
*
|
||||||
|
* @see nla_put
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
static inline int nla_put_string(struct nl_msg *msg, int attrtype, const char *str)
|
||||||
|
{
|
||||||
|
return nla_put(msg, attrtype, strlen(str) + 1, str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return payload of string attribute.
|
||||||
|
* @arg nla String attribute.
|
||||||
|
*
|
||||||
|
* @return Pointer to attribute payload.
|
||||||
|
*/
|
||||||
|
static inline char *nla_get_string(struct nlattr *nla)
|
||||||
|
{
|
||||||
|
return (char *) nla_data(nla);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *nla_strdup(struct nlattr *nla)
|
||||||
|
{
|
||||||
|
return strdup(nla_get_string(nla));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Flag Attribute
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add flag netlink attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
*
|
||||||
|
* @see nla_put
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
static inline int nla_put_flag(struct nl_msg *msg, int attrtype)
|
||||||
|
{
|
||||||
|
return nla_put(msg, attrtype, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if flag attribute is set.
|
||||||
|
* @arg nla Flag netlink attribute.
|
||||||
|
*
|
||||||
|
* @return True if flag is set, otherwise false.
|
||||||
|
*/
|
||||||
|
static inline int nla_get_flag(struct nlattr *nla)
|
||||||
|
{
|
||||||
|
return !!nla;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Microseconds Attribute
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a msecs netlink attribute to a netlink message
|
||||||
|
* @arg n netlink message
|
||||||
|
* @arg attrtype attribute type
|
||||||
|
* @arg msecs number of msecs
|
||||||
|
*/
|
||||||
|
static inline int nla_put_msecs(struct nl_msg *n, int attrtype, unsigned long msecs)
|
||||||
|
{
|
||||||
|
return nla_put_u64(n, attrtype, msecs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return payload of msecs attribute
|
||||||
|
* @arg nla msecs netlink attribute
|
||||||
|
*
|
||||||
|
* @return the number of milliseconds.
|
||||||
|
*/
|
||||||
|
static inline unsigned long nla_get_msecs(struct nlattr *nla)
|
||||||
|
{
|
||||||
|
return nla_get_u64(nla);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add nested attributes to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg nested Message containing attributes to be nested.
|
||||||
|
*
|
||||||
|
* Takes the attributes found in the \a nested message and appends them
|
||||||
|
* to the message \a msg nested in a container of the type \a attrtype.
|
||||||
|
* The \a nested message may not have a family specific header.
|
||||||
|
*
|
||||||
|
* @see nla_put
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
static inline int nla_put_nested(struct nl_msg *msg, int attrtype, struct nl_msg *nested)
|
||||||
|
{
|
||||||
|
return nla_put(msg, attrtype, nlmsg_len(nested->nm_nlh),
|
||||||
|
nlmsg_data(nested->nm_nlh));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a new level of nested attributes.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type of container.
|
||||||
|
*
|
||||||
|
* @return Pointer to container attribute.
|
||||||
|
*/
|
||||||
|
static inline struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype)
|
||||||
|
{
|
||||||
|
struct nlattr *start = (struct nlattr *) nlmsg_tail(msg->nm_nlh);
|
||||||
|
|
||||||
|
if (nla_put(msg, attrtype, 0, NULL) < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finalize nesting of attributes.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg start Container attribute as returned from nla_nest_start().
|
||||||
|
*
|
||||||
|
* Corrects the container attribute header to include the appeneded attributes.
|
||||||
|
*
|
||||||
|
* @return 0
|
||||||
|
*/
|
||||||
|
static inline int nla_nest_end(struct nl_msg *msg, struct nlattr *start)
|
||||||
|
{
|
||||||
|
start->nla_len = (unsigned char *) nlmsg_tail(msg->nm_nlh) -
|
||||||
|
(unsigned char *) start;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create attribute index based on nested attribute
|
||||||
|
* @arg tb Index array to be filled (maxtype+1 elements).
|
||||||
|
* @arg maxtype Maximum attribute type expected and accepted.
|
||||||
|
* @arg nla Nested Attribute.
|
||||||
|
* @arg policy Attribute validation policy.
|
||||||
|
*
|
||||||
|
* Feeds the stream of attributes nested into the specified attribute
|
||||||
|
* to nla_parse().
|
||||||
|
*
|
||||||
|
* @see nla_parse
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
static inline int nla_parse_nested(struct nlattr *tb[], int maxtype, struct nlattr *nla,
|
||||||
|
struct nla_policy *policy)
|
||||||
|
{
|
||||||
|
return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare attribute payload with memory area.
|
||||||
|
* @arg nla Attribute.
|
||||||
|
* @arg data Memory area to compare to.
|
||||||
|
* @arg size Number of bytes to compare.
|
||||||
|
*
|
||||||
|
* @see memcmp(3)
|
||||||
|
* @return An integer less than, equal to, or greater than zero.
|
||||||
|
*/
|
||||||
|
static inline int nla_memcmp(const struct nlattr *nla, const void *data, size_t size)
|
||||||
|
{
|
||||||
|
int d = nla_len(nla) - size;
|
||||||
|
|
||||||
|
if (d == 0)
|
||||||
|
d = memcmp(nla_data(nla), data, size);
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare string attribute payload with string
|
||||||
|
* @arg nla Attribute of type NLA_STRING.
|
||||||
|
* @arg str NUL terminated string.
|
||||||
|
*
|
||||||
|
* @see strcmp(3)
|
||||||
|
* @return An integer less than, equal to, or greater than zero.
|
||||||
|
*/
|
||||||
|
static inline int nla_strcmp(const struct nlattr *nla, const char *str)
|
||||||
|
{
|
||||||
|
int len = strlen(str) + 1;
|
||||||
|
int d = nla_len(nla) - len;
|
||||||
|
|
||||||
|
if (d == 0)
|
||||||
|
d = memcmp(nla_data(nla), str, len);
|
||||||
|
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy string attribute payload to a buffer.
|
||||||
|
* @arg dst Pointer to destination buffer.
|
||||||
|
* @arg nla Attribute of type NLA_STRING.
|
||||||
|
* @arg dstsize Size of destination buffer in bytes.
|
||||||
|
*
|
||||||
|
* Copies at most dstsize - 1 bytes to the destination buffer.
|
||||||
|
* The result is always a valid NUL terminated string. Unlike
|
||||||
|
* strlcpy the destination buffer is always padded out.
|
||||||
|
*
|
||||||
|
* @return The length of string attribute without the terminating NUL.
|
||||||
|
*/
|
||||||
|
static inline size_t nla_strlcpy(char *dst, const struct nlattr *nla, size_t dstsize)
|
||||||
|
{
|
||||||
|
size_t srclen = nla_len(nla);
|
||||||
|
char *src = nla_data(nla);
|
||||||
|
|
||||||
|
if (srclen > 0 && src[srclen - 1] == '\0')
|
||||||
|
srclen--;
|
||||||
|
|
||||||
|
if (dstsize > 0) {
|
||||||
|
size_t len = (srclen >= dstsize) ? dstsize - 1 : srclen;
|
||||||
|
|
||||||
|
memset(dst, 0, dstsize);
|
||||||
|
memcpy(dst, src, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return srclen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Attribute Construction (Exception Based)
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup attr
|
||||||
|
* Add unspecific attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg attrlen Length of attribute payload.
|
||||||
|
* @arg data Head of attribute payload.
|
||||||
|
*/
|
||||||
|
#define NLA_PUT(msg, attrtype, attrlen, data) \
|
||||||
|
do { \
|
||||||
|
if (nla_put(msg, attrtype, attrlen, data) < 0) \
|
||||||
|
goto nla_put_failure; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup attr
|
||||||
|
* Add atomic type attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg type Atomic type.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg value Head of attribute payload.
|
||||||
|
*/
|
||||||
|
#define NLA_PUT_TYPE(msg, type, attrtype, value) \
|
||||||
|
do { \
|
||||||
|
type __tmp = value; \
|
||||||
|
NLA_PUT(msg, attrtype, sizeof(type), &__tmp); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add 8 bit integer attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg value Numeric value.
|
||||||
|
*/
|
||||||
|
#define NLA_PUT_U8(msg, attrtype, value) \
|
||||||
|
NLA_PUT_TYPE(msg, uint8_t, attrtype, value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add 16 bit integer attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg value Numeric value.
|
||||||
|
*/
|
||||||
|
#define NLA_PUT_U16(msg, attrtype, value) \
|
||||||
|
NLA_PUT_TYPE(msg, uint16_t, attrtype, value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add 32 bit integer attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg value Numeric value.
|
||||||
|
*/
|
||||||
|
#define NLA_PUT_U32(msg, attrtype, value) \
|
||||||
|
NLA_PUT_TYPE(msg, uint32_t, attrtype, value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add 64 bit integer attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg value Numeric value.
|
||||||
|
*/
|
||||||
|
#define NLA_PUT_U64(msg, attrtype, value) \
|
||||||
|
NLA_PUT_TYPE(msg, uint64_t, attrtype, value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add string attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg value NUL terminated character string.
|
||||||
|
*/
|
||||||
|
#define NLA_PUT_STRING(msg, attrtype, value) \
|
||||||
|
NLA_PUT(msg, attrtype, strlen(value) + 1, value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add flag attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
*/
|
||||||
|
#define NLA_PUT_FLAG(msg, attrtype) \
|
||||||
|
NLA_PUT(msg, attrtype, 0, NULL)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add msecs attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg msecs Numeric value in micro seconds.
|
||||||
|
*/
|
||||||
|
#define NLA_PUT_MSECS(msg, attrtype, msecs) \
|
||||||
|
NLA_PUT_U64(msg, attrtype, msecs)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add address attribute to netlink message.
|
||||||
|
* @arg msg Netlink message.
|
||||||
|
* @arg attrtype Attribute type.
|
||||||
|
* @arg addr Abstract address object.
|
||||||
|
*/
|
||||||
|
#define NLA_PUT_ADDR(msg, attrtype, addr) \
|
||||||
|
NLA_PUT(msg, attrtype, nl_addr_get_len(addr), \
|
||||||
|
nl_addr_get_binary_addr(addr))
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Iterators
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup attr
|
||||||
|
* Iterate over a stream of attributes
|
||||||
|
* @arg pos loop counter, set to current attribute
|
||||||
|
* @arg head head of attribute stream
|
||||||
|
* @arg len length of attribute stream
|
||||||
|
* @arg rem initialized to len, holds bytes currently remaining in stream
|
||||||
|
*/
|
||||||
|
#define nla_for_each_attr(pos, head, len, rem) \
|
||||||
|
for (pos = head, rem = len; \
|
||||||
|
nla_ok(pos, rem); \
|
||||||
|
pos = nla_next(pos, &(rem)))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup attr
|
||||||
|
* Iterate over a stream of nested attributes
|
||||||
|
* @arg pos loop counter, set to current attribute
|
||||||
|
* @arg nla attribute containing the nested attributes
|
||||||
|
* @arg rem initialized to len, holds bytes currently remaining in stream
|
||||||
|
*/
|
||||||
|
#define nla_for_each_nested(pos, nla, rem) \
|
||||||
|
for (pos = nla_data(nla), rem = nla_len(nla); \
|
||||||
|
nla_ok(pos, rem); \
|
||||||
|
pos = nla_next(pos, &(rem)))
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,199 @@
|
||||||
|
/*
|
||||||
|
* netlink/cache-api.h Caching API
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_CACHE_API_H_
|
||||||
|
#define NETLINK_CACHE_API_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup cache
|
||||||
|
* @defgroup cache_api Cache Implementation
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @par 1) Cache Definition
|
||||||
|
* @code
|
||||||
|
* struct nl_cache_ops my_cache_ops = {
|
||||||
|
* .co_name = "route/link",
|
||||||
|
* .co_protocol = NETLINK_ROUTE,
|
||||||
|
* .co_hdrsize = sizeof(struct ifinfomsg),
|
||||||
|
* .co_obj_ops = &my_obj_ops,
|
||||||
|
* };
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par 2)
|
||||||
|
* @code
|
||||||
|
* // The simplest way to fill a cache is by providing a request-update
|
||||||
|
* // function which must trigger a complete dump on the kernel-side of
|
||||||
|
* // whatever the cache covers.
|
||||||
|
* static int my_request_update(struct nl_cache *cache,
|
||||||
|
* struct nl_sock *socket)
|
||||||
|
* {
|
||||||
|
* // In this example, we request a full dump of the interface table
|
||||||
|
* return nl_rtgen_request(socket, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // The resulting netlink messages sent back will be fed into a message
|
||||||
|
* // parser one at a time. The message parser has to extract all relevant
|
||||||
|
* // information from the message and create an object reflecting the
|
||||||
|
* // contents of the message and pass it on to the parser callback function
|
||||||
|
* // provide which will add the object to the cache.
|
||||||
|
* static int my_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
|
||||||
|
* struct nlmsghdr *nlh, struct nl_parser_param *pp)
|
||||||
|
* {
|
||||||
|
* struct my_obj *obj;
|
||||||
|
*
|
||||||
|
* obj = my_obj_alloc();
|
||||||
|
* obj->ce_msgtype = nlh->nlmsg_type;
|
||||||
|
*
|
||||||
|
* // Parse the netlink message and continue creating the object.
|
||||||
|
*
|
||||||
|
* err = pp->pp_cb((struct nl_object *) obj, pp);
|
||||||
|
* if (err < 0)
|
||||||
|
* goto errout;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* struct nl_cache_ops my_cache_ops = {
|
||||||
|
* ...
|
||||||
|
* .co_request_update = my_request_update,
|
||||||
|
* .co_msg_parser = my_msg_parser,
|
||||||
|
* };
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par 3) Notification based Updates
|
||||||
|
* @code
|
||||||
|
* // Caches can be kept up-to-date based on notifications if the kernel
|
||||||
|
* // sends out notifications whenever an object is added/removed/changed.
|
||||||
|
* //
|
||||||
|
* // It is trivial to support this, first a list of groups needs to be
|
||||||
|
* // defined which are required to join in order to receive all necessary
|
||||||
|
* // notifications. The groups are separated by address family to support
|
||||||
|
* // the common situation where a separate group is used for each address
|
||||||
|
* // family. If there is only one group, simply specify AF_UNSPEC.
|
||||||
|
* static struct nl_af_group addr_groups[] = {
|
||||||
|
* { AF_INET, RTNLGRP_IPV4_IFADDR },
|
||||||
|
* { AF_INET6, RTNLGRP_IPV6_IFADDR },
|
||||||
|
* { END_OF_GROUP_LIST },
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* // In order for the caching system to know the meaning of each message
|
||||||
|
* // type it requires a table which maps each supported message type to
|
||||||
|
* // a cache action, e.g. RTM_NEWADDR means address has been added or
|
||||||
|
* // updated, RTM_DELADDR means address has been removed.
|
||||||
|
* static struct nl_cache_ops rtnl_addr_ops = {
|
||||||
|
* ...
|
||||||
|
* .co_msgtypes = {
|
||||||
|
* { RTM_NEWADDR, NL_ACT_NEW, "new" },
|
||||||
|
* { RTM_DELADDR, NL_ACT_DEL, "del" },
|
||||||
|
* { RTM_GETADDR, NL_ACT_GET, "get" },
|
||||||
|
* END_OF_MSGTYPES_LIST,
|
||||||
|
* },
|
||||||
|
* .co_groups = addr_groups,
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* // It is now possible to keep the cache up-to-date using the cache manager.
|
||||||
|
* @endcode
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum {
|
||||||
|
NL_ACT_UNSPEC,
|
||||||
|
NL_ACT_NEW,
|
||||||
|
NL_ACT_DEL,
|
||||||
|
NL_ACT_GET,
|
||||||
|
NL_ACT_SET,
|
||||||
|
NL_ACT_CHANGE,
|
||||||
|
__NL_ACT_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NL_ACT_MAX (__NL_ACT_MAX - 1)
|
||||||
|
|
||||||
|
#define END_OF_MSGTYPES_LIST { -1, -1, NULL }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Message type to cache action association
|
||||||
|
*/
|
||||||
|
struct nl_msgtype
|
||||||
|
{
|
||||||
|
/** Netlink message type */
|
||||||
|
int mt_id;
|
||||||
|
|
||||||
|
/** Cache action to take */
|
||||||
|
int mt_act;
|
||||||
|
|
||||||
|
/** Name of operation for human-readable printing */
|
||||||
|
char * mt_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address family to netlink group association
|
||||||
|
*/
|
||||||
|
struct nl_af_group
|
||||||
|
{
|
||||||
|
/** Address family */
|
||||||
|
int ag_family;
|
||||||
|
|
||||||
|
/** Netlink group identifier */
|
||||||
|
int ag_group;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define END_OF_GROUP_LIST AF_UNSPEC, 0
|
||||||
|
|
||||||
|
struct nl_parser_param
|
||||||
|
{
|
||||||
|
int (*pp_cb)(struct nl_object *, struct nl_parser_param *);
|
||||||
|
void * pp_arg;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache Operations
|
||||||
|
*/
|
||||||
|
struct nl_cache_ops
|
||||||
|
{
|
||||||
|
char * co_name;
|
||||||
|
|
||||||
|
int co_hdrsize;
|
||||||
|
int co_protocol;
|
||||||
|
struct nl_af_group * co_groups;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called whenever an update of the cache is required. Must send
|
||||||
|
* a request message to the kernel requesting a complete dump.
|
||||||
|
*/
|
||||||
|
int (*co_request_update)(struct nl_cache *, struct nl_sock *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called whenever a message was received that needs to be parsed.
|
||||||
|
* Must parse the message and call the paser callback function
|
||||||
|
* (nl_parser_param) provided via the argument.
|
||||||
|
*/
|
||||||
|
int (*co_msg_parser)(struct nl_cache_ops *, struct sockaddr_nl *,
|
||||||
|
struct nlmsghdr *, struct nl_parser_param *);
|
||||||
|
|
||||||
|
struct nl_object_ops * co_obj_ops;
|
||||||
|
|
||||||
|
struct nl_cache_ops *co_next;
|
||||||
|
struct nl_cache *co_major_cache;
|
||||||
|
struct genl_ops * co_genl;
|
||||||
|
struct nl_msgtype co_msgtypes[];
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,126 @@
|
||||||
|
/*
|
||||||
|
* netlink/cache.h Caching Module
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_CACHE_H_
|
||||||
|
#define NETLINK_CACHE_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/msg.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
#include <netlink/object.h>
|
||||||
|
#include <netlink/cache-api.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct nl_cache;
|
||||||
|
|
||||||
|
typedef void (*change_func_t)(struct nl_cache *, struct nl_object *, int);
|
||||||
|
|
||||||
|
/* Access Functions */
|
||||||
|
extern int nl_cache_nitems(struct nl_cache *);
|
||||||
|
extern int nl_cache_nitems_filter(struct nl_cache *,
|
||||||
|
struct nl_object *);
|
||||||
|
extern struct nl_cache_ops * nl_cache_get_ops(struct nl_cache *);
|
||||||
|
extern struct nl_object * nl_cache_get_first(struct nl_cache *);
|
||||||
|
extern struct nl_object * nl_cache_get_last(struct nl_cache *);
|
||||||
|
extern struct nl_object * nl_cache_get_next(struct nl_object *);
|
||||||
|
extern struct nl_object * nl_cache_get_prev(struct nl_object *);
|
||||||
|
|
||||||
|
extern struct nl_cache * nl_cache_alloc(struct nl_cache_ops *);
|
||||||
|
extern int nl_cache_alloc_and_fill(struct nl_cache_ops *,
|
||||||
|
struct nl_sock *,
|
||||||
|
struct nl_cache **);
|
||||||
|
extern int nl_cache_alloc_name(const char *,
|
||||||
|
struct nl_cache **);
|
||||||
|
extern struct nl_cache * nl_cache_subset(struct nl_cache *,
|
||||||
|
struct nl_object *);
|
||||||
|
extern void nl_cache_clear(struct nl_cache *);
|
||||||
|
extern void nl_cache_free(struct nl_cache *);
|
||||||
|
|
||||||
|
/* Cache modification */
|
||||||
|
extern int nl_cache_add(struct nl_cache *,
|
||||||
|
struct nl_object *);
|
||||||
|
extern int nl_cache_parse_and_add(struct nl_cache *,
|
||||||
|
struct nl_msg *);
|
||||||
|
extern void nl_cache_remove(struct nl_object *);
|
||||||
|
extern int nl_cache_refill(struct nl_sock *,
|
||||||
|
struct nl_cache *);
|
||||||
|
extern int nl_cache_pickup(struct nl_sock *,
|
||||||
|
struct nl_cache *);
|
||||||
|
extern int nl_cache_resync(struct nl_sock *,
|
||||||
|
struct nl_cache *,
|
||||||
|
change_func_t);
|
||||||
|
extern int nl_cache_include(struct nl_cache *,
|
||||||
|
struct nl_object *,
|
||||||
|
change_func_t);
|
||||||
|
|
||||||
|
/* General */
|
||||||
|
extern int nl_cache_is_empty(struct nl_cache *);
|
||||||
|
extern void nl_cache_mark_all(struct nl_cache *);
|
||||||
|
|
||||||
|
/* Dumping */
|
||||||
|
extern void nl_cache_dump(struct nl_cache *,
|
||||||
|
struct nl_dump_params *);
|
||||||
|
extern void nl_cache_dump_filter(struct nl_cache *,
|
||||||
|
struct nl_dump_params *,
|
||||||
|
struct nl_object *);
|
||||||
|
|
||||||
|
/* Iterators */
|
||||||
|
extern void nl_cache_foreach(struct nl_cache *,
|
||||||
|
void (*cb)(struct nl_object *,
|
||||||
|
void *),
|
||||||
|
void *arg);
|
||||||
|
extern void nl_cache_foreach_filter(struct nl_cache *,
|
||||||
|
struct nl_object *,
|
||||||
|
void (*cb)(struct
|
||||||
|
nl_object *,
|
||||||
|
void *),
|
||||||
|
void *arg);
|
||||||
|
|
||||||
|
/* --- cache management --- */
|
||||||
|
|
||||||
|
/* Cache type management */
|
||||||
|
extern struct nl_cache_ops * nl_cache_ops_lookup(const char *);
|
||||||
|
extern struct nl_cache_ops * nl_cache_ops_associate(int, int);
|
||||||
|
extern struct nl_msgtype * nl_msgtype_lookup(struct nl_cache_ops *, int);
|
||||||
|
extern void nl_cache_ops_foreach(void (*cb)(struct nl_cache_ops *, void *), void *);
|
||||||
|
extern int nl_cache_mngt_register(struct nl_cache_ops *);
|
||||||
|
extern int nl_cache_mngt_unregister(struct nl_cache_ops *);
|
||||||
|
|
||||||
|
/* Global cache provisioning/requiring */
|
||||||
|
extern void nl_cache_mngt_provide(struct nl_cache *);
|
||||||
|
extern void nl_cache_mngt_unprovide(struct nl_cache *);
|
||||||
|
extern struct nl_cache * nl_cache_mngt_require(const char *);
|
||||||
|
|
||||||
|
struct nl_cache_mngr;
|
||||||
|
|
||||||
|
#define NL_AUTO_PROVIDE 1
|
||||||
|
|
||||||
|
extern int nl_cache_mngr_alloc(struct nl_sock *,
|
||||||
|
int, int,
|
||||||
|
struct nl_cache_mngr **);
|
||||||
|
extern int nl_cache_mngr_add(struct nl_cache_mngr *,
|
||||||
|
const char *,
|
||||||
|
change_func_t,
|
||||||
|
struct nl_cache **);
|
||||||
|
extern int nl_cache_mngr_get_fd(struct nl_cache_mngr *);
|
||||||
|
extern int nl_cache_mngr_poll(struct nl_cache_mngr *,
|
||||||
|
int);
|
||||||
|
extern int nl_cache_mngr_data_ready(struct nl_cache_mngr *);
|
||||||
|
extern void nl_cache_mngr_free(struct nl_cache_mngr *);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* netlink/data.h Abstract Data
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_DATA_H_
|
||||||
|
#define NETLINK_DATA_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct nl_data;
|
||||||
|
|
||||||
|
/* General */
|
||||||
|
extern struct nl_data * nl_data_alloc(void *, size_t);
|
||||||
|
extern struct nl_data * nl_data_alloc_attr(struct nlattr *);
|
||||||
|
extern struct nl_data * nl_data_clone(struct nl_data *);
|
||||||
|
extern int nl_data_append(struct nl_data *, void *, size_t);
|
||||||
|
extern void nl_data_free(struct nl_data *);
|
||||||
|
|
||||||
|
/* Access Functions */
|
||||||
|
extern void * nl_data_get(struct nl_data *);
|
||||||
|
extern size_t nl_data_get_size(struct nl_data *);
|
||||||
|
|
||||||
|
/* Misc */
|
||||||
|
extern int nl_data_cmp(struct nl_data *, struct nl_data *);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* netlink/errno.h Error Numbers
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_ERRNO_H_
|
||||||
|
#define NETLINK_ERRNO_H_
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NLE_SUCCESS 0
|
||||||
|
#define NLE_FAILURE 1
|
||||||
|
#define NLE_INTR 2
|
||||||
|
#define NLE_BAD_SOCK 3
|
||||||
|
#define NLE_AGAIN 4
|
||||||
|
#define NLE_NOMEM 5
|
||||||
|
#define NLE_EXIST 6
|
||||||
|
#define NLE_INVAL 7
|
||||||
|
#define NLE_RANGE 8
|
||||||
|
#define NLE_MSGSIZE 9
|
||||||
|
#define NLE_OPNOTSUPP 10
|
||||||
|
#define NLE_AF_NOSUPPORT 11
|
||||||
|
#define NLE_OBJ_NOTFOUND 12
|
||||||
|
#define NLE_NOATTR 13
|
||||||
|
#define NLE_MISSING_ATTR 14
|
||||||
|
#define NLE_AF_MISMATCH 15
|
||||||
|
#define NLE_SEQ_MISMATCH 16
|
||||||
|
#define NLE_MSG_OVERFLOW 17
|
||||||
|
#define NLE_MSG_TRUNC 18
|
||||||
|
#define NLE_NOADDR 19
|
||||||
|
#define NLE_SRCRT_NOSUPPORT 20
|
||||||
|
#define NLE_MSG_TOOSHORT 21
|
||||||
|
#define NLE_MSGTYPE_NOSUPPORT 22
|
||||||
|
#define NLE_OBJ_MISMATCH 23
|
||||||
|
#define NLE_NOCACHE 24
|
||||||
|
#define NLE_BUSY 25
|
||||||
|
#define NLE_PROTO_MISMATCH 26
|
||||||
|
#define NLE_NOACCESS 27
|
||||||
|
#define NLE_PERM 28
|
||||||
|
|
||||||
|
#define NLE_MAX NLE_PERM
|
||||||
|
|
||||||
|
extern const char * nl_geterror(int);
|
||||||
|
extern void nl_perror(int, const char *);
|
||||||
|
extern int nl_syserr2nlerr(int);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* netlink/genl/ctrl.h Generic Netlink Controller
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_GENL_CTRL_H_
|
||||||
|
#define NETLINK_GENL_CTRL_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/cache.h>
|
||||||
|
#include <netlink/addr.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct genl_family;
|
||||||
|
|
||||||
|
extern int genl_ctrl_alloc_cache(struct nl_sock *,
|
||||||
|
struct nl_cache **);
|
||||||
|
extern struct genl_family * genl_ctrl_search(struct nl_cache *, int);
|
||||||
|
extern struct genl_family * genl_ctrl_search_by_name(struct nl_cache *,
|
||||||
|
const char *);
|
||||||
|
extern int genl_ctrl_resolve(struct nl_sock *,
|
||||||
|
const char *);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,130 @@
|
||||||
|
/*
|
||||||
|
* netlink/genl/family.h Generic Netlink Family
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_GENL_FAMILY_H_
|
||||||
|
#define NETLINK_GENL_FAMILY_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/cache.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** @cond SKIP */
|
||||||
|
#define FAMILY_ATTR_ID 0x01
|
||||||
|
#define FAMILY_ATTR_NAME 0x02
|
||||||
|
#define FAMILY_ATTR_VERSION 0x04
|
||||||
|
#define FAMILY_ATTR_HDRSIZE 0x08
|
||||||
|
#define FAMILY_ATTR_MAXATTR 0x10
|
||||||
|
#define FAMILY_ATTR_OPS 0x20
|
||||||
|
|
||||||
|
|
||||||
|
struct genl_family
|
||||||
|
{
|
||||||
|
NLHDR_COMMON
|
||||||
|
|
||||||
|
uint16_t gf_id;
|
||||||
|
char gf_name[GENL_NAMSIZ];
|
||||||
|
uint32_t gf_version;
|
||||||
|
uint32_t gf_hdrsize;
|
||||||
|
uint32_t gf_maxattr;
|
||||||
|
|
||||||
|
struct nl_list_head gf_ops;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern struct genl_family * genl_family_alloc(void);
|
||||||
|
extern void genl_family_put(struct genl_family *);
|
||||||
|
|
||||||
|
extern int genl_family_add_op(struct genl_family *,
|
||||||
|
int, int);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Attributes
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline unsigned int genl_family_get_id(struct genl_family *family)
|
||||||
|
{
|
||||||
|
if (family->ce_mask & FAMILY_ATTR_ID)
|
||||||
|
return family->gf_id;
|
||||||
|
else
|
||||||
|
return GENL_ID_GENERATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void genl_family_set_id(struct genl_family *family, unsigned int id)
|
||||||
|
{
|
||||||
|
family->gf_id = id;
|
||||||
|
family->ce_mask |= FAMILY_ATTR_ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline char *genl_family_get_name(struct genl_family *family)
|
||||||
|
{
|
||||||
|
if (family->ce_mask & FAMILY_ATTR_NAME)
|
||||||
|
return family->gf_name;
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void genl_family_set_name(struct genl_family *family, const char *name)
|
||||||
|
{
|
||||||
|
strncpy(family->gf_name, name, GENL_NAMSIZ-1);
|
||||||
|
family->ce_mask |= FAMILY_ATTR_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint8_t genl_family_get_version(struct genl_family *family)
|
||||||
|
{
|
||||||
|
if (family->ce_mask & FAMILY_ATTR_VERSION)
|
||||||
|
return family->gf_version;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void genl_family_set_version(struct genl_family *family, uint8_t version)
|
||||||
|
{
|
||||||
|
family->gf_version = version;
|
||||||
|
family->ce_mask |= FAMILY_ATTR_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t genl_family_get_hdrsize(struct genl_family *family)
|
||||||
|
{
|
||||||
|
if (family->ce_mask & FAMILY_ATTR_HDRSIZE)
|
||||||
|
return family->gf_hdrsize;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void genl_family_set_hdrsize(struct genl_family *family, uint32_t hdrsize)
|
||||||
|
{
|
||||||
|
family->gf_hdrsize = hdrsize;
|
||||||
|
family->ce_mask |= FAMILY_ATTR_HDRSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t genl_family_get_maxattr(struct genl_family *family)
|
||||||
|
{
|
||||||
|
if (family->ce_mask & FAMILY_ATTR_MAXATTR)
|
||||||
|
return family->gf_maxattr;
|
||||||
|
else
|
||||||
|
return family->gf_maxattr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void genl_family_set_maxattr(struct genl_family *family, uint32_t maxattr)
|
||||||
|
{
|
||||||
|
family->gf_maxattr = maxattr;
|
||||||
|
family->ce_mask |= FAMILY_ATTR_MAXATTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* netlink/genl/genl.h Generic Netlink
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_GENL_H_
|
||||||
|
#define NETLINK_GENL_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/msg.h>
|
||||||
|
#include <netlink/attr.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int genl_connect(struct nl_sock *);
|
||||||
|
|
||||||
|
extern int genl_send_simple(struct nl_sock *, int, int,
|
||||||
|
int, int);
|
||||||
|
|
||||||
|
extern void * genlmsg_put(struct nl_msg *, uint32_t, uint32_t,
|
||||||
|
int, int, int, uint8_t, uint8_t);
|
||||||
|
|
||||||
|
extern int genlmsg_valid_hdr(struct nlmsghdr *, int);
|
||||||
|
extern int genlmsg_validate(struct nlmsghdr *, int, int,
|
||||||
|
struct nla_policy *);
|
||||||
|
extern int genlmsg_parse(struct nlmsghdr *, int, struct nlattr **,
|
||||||
|
int, struct nla_policy *);
|
||||||
|
extern void * genlmsg_data(const struct genlmsghdr *);
|
||||||
|
extern int genlmsg_len(const struct genlmsghdr *);
|
||||||
|
extern struct nlattr * genlmsg_attrdata(const struct genlmsghdr *, int);
|
||||||
|
extern int genlmsg_attrlen(const struct genlmsghdr *, int);
|
||||||
|
|
||||||
|
extern char * genl_op2name(int, int, char *, size_t);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* netlink/genl/mngt.h Generic Netlink Management
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_GENL_MNGT_H_
|
||||||
|
#define NETLINK_GENL_MNGT_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/attr.h>
|
||||||
|
#include <netlink/list.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct nl_cache_ops;
|
||||||
|
|
||||||
|
struct genl_info
|
||||||
|
{
|
||||||
|
struct sockaddr_nl * who;
|
||||||
|
struct nlmsghdr * nlh;
|
||||||
|
struct genlmsghdr * genlhdr;
|
||||||
|
void * userhdr;
|
||||||
|
struct nlattr ** attrs;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup genl_mngt
|
||||||
|
* Generic Netlink Command
|
||||||
|
*/
|
||||||
|
struct genl_cmd
|
||||||
|
{
|
||||||
|
/** Unique command identifier */
|
||||||
|
int c_id;
|
||||||
|
|
||||||
|
/** Name/description of command */
|
||||||
|
char * c_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum attribute identifier, must be provided if
|
||||||
|
* a message parser is available.
|
||||||
|
*/
|
||||||
|
int c_maxattr;
|
||||||
|
|
||||||
|
int (*c_msg_parser)(struct nl_cache_ops *,
|
||||||
|
struct genl_cmd *,
|
||||||
|
struct genl_info *, void *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attribute validation policy (optional)
|
||||||
|
*/
|
||||||
|
struct nla_policy * c_attr_policy;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup genl_mngt
|
||||||
|
* Generic Netlink Operations
|
||||||
|
*/
|
||||||
|
struct genl_ops
|
||||||
|
{
|
||||||
|
int o_family;
|
||||||
|
int o_id;
|
||||||
|
char * o_name;
|
||||||
|
struct nl_cache_ops * o_cache_ops;
|
||||||
|
struct genl_cmd * o_cmds;
|
||||||
|
int o_ncmds;
|
||||||
|
|
||||||
|
/* linked list of all genl cache operations */
|
||||||
|
struct nl_list_head o_list;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern int genl_register(struct nl_cache_ops *);
|
||||||
|
extern void genl_unregister(struct nl_cache_ops *);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,230 @@
|
||||||
|
/*
|
||||||
|
* netlink/handlers.c default netlink message handlers
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_HANDLERS_H_
|
||||||
|
#define NETLINK_HANDLERS_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <netlink/netlink-compat.h>
|
||||||
|
#include <netlink/netlink-kernel.h>
|
||||||
|
#include <netlink/types.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct nl_sock;
|
||||||
|
struct nl_msg;
|
||||||
|
struct nl_cb;
|
||||||
|
/**
|
||||||
|
* @name Callback Typedefs
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nl_recvmsgs() callback for message processing customization
|
||||||
|
* @ingroup cb
|
||||||
|
* @arg msg netlink message being processed
|
||||||
|
* @arg arg argument passwd on through caller
|
||||||
|
*/
|
||||||
|
typedef int (*nl_recvmsg_msg_cb_t)(struct nl_msg *msg, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nl_recvmsgs() callback for error message processing customization
|
||||||
|
* @ingroup cb
|
||||||
|
* @arg nla netlink address of the peer
|
||||||
|
* @arg nlerr netlink error message being processed
|
||||||
|
* @arg arg argument passed on through caller
|
||||||
|
*/
|
||||||
|
typedef int (*nl_recvmsg_err_cb_t)(struct sockaddr_nl *nla,
|
||||||
|
struct nlmsgerr *nlerr, void *arg);
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback actions
|
||||||
|
* @ingroup cb
|
||||||
|
*/
|
||||||
|
enum nl_cb_action {
|
||||||
|
/** Proceed with wathever would come next */
|
||||||
|
NL_OK,
|
||||||
|
/** Skip this message */
|
||||||
|
NL_SKIP,
|
||||||
|
/** Stop parsing altogether and discard remaining messages */
|
||||||
|
NL_STOP,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback kinds
|
||||||
|
* @ingroup cb
|
||||||
|
*/
|
||||||
|
enum nl_cb_kind {
|
||||||
|
/** Default handlers (quiet) */
|
||||||
|
NL_CB_DEFAULT,
|
||||||
|
/** Verbose default handlers (error messages printed) */
|
||||||
|
NL_CB_VERBOSE,
|
||||||
|
/** Debug handlers for debugging */
|
||||||
|
NL_CB_DEBUG,
|
||||||
|
/** Customized handler specified by the user */
|
||||||
|
NL_CB_CUSTOM,
|
||||||
|
__NL_CB_KIND_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NL_CB_KIND_MAX (__NL_CB_KIND_MAX - 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback types
|
||||||
|
* @ingroup cb
|
||||||
|
*/
|
||||||
|
enum nl_cb_type {
|
||||||
|
/** Message is valid */
|
||||||
|
NL_CB_VALID,
|
||||||
|
/** Last message in a series of multi part messages received */
|
||||||
|
NL_CB_FINISH,
|
||||||
|
/** Report received that data was lost */
|
||||||
|
NL_CB_OVERRUN,
|
||||||
|
/** Message wants to be skipped */
|
||||||
|
NL_CB_SKIPPED,
|
||||||
|
/** Message is an acknowledge */
|
||||||
|
NL_CB_ACK,
|
||||||
|
/** Called for every message received */
|
||||||
|
NL_CB_MSG_IN,
|
||||||
|
/** Called for every message sent out except for nl_sendto() */
|
||||||
|
NL_CB_MSG_OUT,
|
||||||
|
/** Message is malformed and invalid */
|
||||||
|
NL_CB_INVALID,
|
||||||
|
/** Called instead of internal sequence number checking */
|
||||||
|
NL_CB_SEQ_CHECK,
|
||||||
|
/** Sending of an acknowledge message has been requested */
|
||||||
|
NL_CB_SEND_ACK,
|
||||||
|
__NL_CB_TYPE_MAX,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NL_CB_TYPE_MAX (__NL_CB_TYPE_MAX - 1)
|
||||||
|
|
||||||
|
struct nl_cb
|
||||||
|
{
|
||||||
|
nl_recvmsg_msg_cb_t cb_set[NL_CB_TYPE_MAX+1];
|
||||||
|
void * cb_args[NL_CB_TYPE_MAX+1];
|
||||||
|
|
||||||
|
nl_recvmsg_err_cb_t cb_err;
|
||||||
|
void * cb_err_arg;
|
||||||
|
|
||||||
|
/** May be used to replace nl_recvmsgs with your own implementation
|
||||||
|
* in all internal calls to nl_recvmsgs. */
|
||||||
|
int (*cb_recvmsgs_ow)(struct nl_sock *,
|
||||||
|
struct nl_cb *);
|
||||||
|
|
||||||
|
/** Overwrite internal calls to nl_recv, must return the number of
|
||||||
|
* octets read and allocate a buffer for the received data. */
|
||||||
|
int (*cb_recv_ow)(struct nl_sock *,
|
||||||
|
struct sockaddr_nl *,
|
||||||
|
unsigned char **,
|
||||||
|
struct ucred **);
|
||||||
|
|
||||||
|
/** Overwrites internal calls to nl_send, must send the netlink
|
||||||
|
* message. */
|
||||||
|
int (*cb_send_ow)(struct nl_sock *,
|
||||||
|
struct nl_msg *);
|
||||||
|
|
||||||
|
int cb_refcnt;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern struct nl_cb * nl_cb_alloc(enum nl_cb_kind);
|
||||||
|
extern struct nl_cb * nl_cb_clone(struct nl_cb *);
|
||||||
|
extern void nl_cb_put(struct nl_cb *);
|
||||||
|
|
||||||
|
extern int nl_cb_set(struct nl_cb *, enum nl_cb_type, enum nl_cb_kind,
|
||||||
|
nl_recvmsg_msg_cb_t, void *);
|
||||||
|
extern int nl_cb_err(struct nl_cb *, enum nl_cb_kind, nl_recvmsg_err_cb_t,
|
||||||
|
void *);
|
||||||
|
|
||||||
|
static inline struct nl_cb *nl_cb_get(struct nl_cb *cb)
|
||||||
|
{
|
||||||
|
cb->cb_refcnt++;
|
||||||
|
|
||||||
|
return cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up a all callbacks
|
||||||
|
* @arg cb callback set
|
||||||
|
* @arg kind kind of callback
|
||||||
|
* @arg func callback function
|
||||||
|
* @arg arg argument to be passwd to callback function
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code
|
||||||
|
*/
|
||||||
|
static inline int nl_cb_set_all(struct nl_cb *cb, enum nl_cb_kind kind,
|
||||||
|
nl_recvmsg_msg_cb_t func, void *arg)
|
||||||
|
{
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
for (i = 0; i <= NL_CB_TYPE_MAX; i++) {
|
||||||
|
err = nl_cb_set(cb, i, kind, func, arg);
|
||||||
|
if (err < 0)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Overwriting
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrite internal calls to nl_recvmsgs()
|
||||||
|
* @arg cb callback set
|
||||||
|
* @arg func replacement callback for nl_recvmsgs()
|
||||||
|
*/
|
||||||
|
static inline void nl_cb_overwrite_recvmsgs(struct nl_cb *cb,
|
||||||
|
int (*func)(struct nl_sock *, struct nl_cb *))
|
||||||
|
{
|
||||||
|
cb->cb_recvmsgs_ow = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrite internal calls to nl_recv()
|
||||||
|
* @arg cb callback set
|
||||||
|
* @arg func replacement callback for nl_recv()
|
||||||
|
*/
|
||||||
|
static inline void nl_cb_overwrite_recv(struct nl_cb *cb,
|
||||||
|
int (*func)(struct nl_sock *, struct sockaddr_nl *,
|
||||||
|
unsigned char **, struct ucred **))
|
||||||
|
{
|
||||||
|
cb->cb_recv_ow = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overwrite internal calls to nl_send()
|
||||||
|
* @arg cb callback set
|
||||||
|
* @arg func replacement callback for nl_send()
|
||||||
|
*/
|
||||||
|
static inline void nl_cb_overwrite_send(struct nl_cb *cb,
|
||||||
|
int (*func)(struct nl_sock *, struct nl_msg *))
|
||||||
|
{
|
||||||
|
cb->cb_send_ow = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* netlink/list.h Netlink List Utilities
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_LIST_H_
|
||||||
|
#define NETLINK_LIST_H_
|
||||||
|
|
||||||
|
struct nl_list_head
|
||||||
|
{
|
||||||
|
struct nl_list_head * next;
|
||||||
|
struct nl_list_head * prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static inline void __nl_list_add(struct nl_list_head *obj,
|
||||||
|
struct nl_list_head *prev,
|
||||||
|
struct nl_list_head *next)
|
||||||
|
{
|
||||||
|
prev->next = obj;
|
||||||
|
obj->prev = prev;
|
||||||
|
next->prev = obj;
|
||||||
|
obj->next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nl_list_add_tail(struct nl_list_head *obj,
|
||||||
|
struct nl_list_head *head)
|
||||||
|
{
|
||||||
|
__nl_list_add(obj, head->prev, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nl_list_add_head(struct nl_list_head *obj,
|
||||||
|
struct nl_list_head *head)
|
||||||
|
{
|
||||||
|
__nl_list_add(obj, head, head->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nl_list_del(struct nl_list_head *obj)
|
||||||
|
{
|
||||||
|
obj->next->prev = obj->prev;
|
||||||
|
obj->prev->next = obj->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int nl_list_empty(struct nl_list_head *head)
|
||||||
|
{
|
||||||
|
return head->next == head;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define nl_container_of(ptr, type, member) ({ \
|
||||||
|
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||||
|
(type *)( (char *)__mptr - ((size_t) &((type *)0)->member));})
|
||||||
|
|
||||||
|
#define nl_list_entry(ptr, type, member) \
|
||||||
|
nl_container_of(ptr, type, member)
|
||||||
|
|
||||||
|
#define nl_list_at_tail(pos, head, member) \
|
||||||
|
((pos)->member.next == (head))
|
||||||
|
|
||||||
|
#define nl_list_at_head(pos, head, member) \
|
||||||
|
((pos)->member.prev == (head))
|
||||||
|
|
||||||
|
#define NL_LIST_HEAD(name) \
|
||||||
|
struct nl_list_head name = { &(name), &(name) }
|
||||||
|
|
||||||
|
#define nl_list_first_entry(head, type, member) \
|
||||||
|
nl_list_entry((head)->next, type, member)
|
||||||
|
|
||||||
|
#define nl_list_for_each_entry(pos, head, member) \
|
||||||
|
for (pos = nl_list_entry((head)->next, typeof(*pos), member); \
|
||||||
|
&(pos)->member != (head); \
|
||||||
|
(pos) = nl_list_entry((pos)->member.next, typeof(*(pos)), member))
|
||||||
|
|
||||||
|
#define nl_list_for_each_entry_safe(pos, n, head, member) \
|
||||||
|
for (pos = nl_list_entry((head)->next, typeof(*pos), member), \
|
||||||
|
n = nl_list_entry(pos->member.next, typeof(*pos), member); \
|
||||||
|
&(pos)->member != (head); \
|
||||||
|
pos = n, n = nl_list_entry(n->member.next, typeof(*n), member))
|
||||||
|
|
||||||
|
#define nl_init_list_head(head) \
|
||||||
|
do { (head)->next = (head); (head)->prev = (head); } while (0)
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,308 @@
|
||||||
|
/*
|
||||||
|
* netlink/msg.c Netlink Messages Interface
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_MSG_H_
|
||||||
|
#define NETLINK_MSG_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/object.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct nla_policy;
|
||||||
|
|
||||||
|
#define NL_DONTPAD 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup msg
|
||||||
|
* @brief
|
||||||
|
* Will cause the netlink pid to be set to the pid assigned to
|
||||||
|
* the netlink handle (socket) just before sending the message off.
|
||||||
|
* @note Requires the use of nl_send_auto_complete()!
|
||||||
|
*/
|
||||||
|
#define NL_AUTO_PID 0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup msg
|
||||||
|
* @brief
|
||||||
|
* May be used to refer to a sequence number which should be
|
||||||
|
* automatically set just before sending the message off.
|
||||||
|
* @note Requires the use of nl_send_auto_complete()!
|
||||||
|
*/
|
||||||
|
#define NL_AUTO_SEQ 0
|
||||||
|
|
||||||
|
#define NL_MSG_CRED_PRESENT 1
|
||||||
|
|
||||||
|
struct nl_msg
|
||||||
|
{
|
||||||
|
int nm_protocol;
|
||||||
|
int nm_flags;
|
||||||
|
struct sockaddr_nl nm_src;
|
||||||
|
struct sockaddr_nl nm_dst;
|
||||||
|
struct ucred nm_creds;
|
||||||
|
struct nlmsghdr * nm_nlh;
|
||||||
|
size_t nm_size;
|
||||||
|
int nm_refcnt;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct nl_msg;
|
||||||
|
struct nl_tree;
|
||||||
|
struct ucred;
|
||||||
|
|
||||||
|
/* message parsing */
|
||||||
|
extern int nlmsg_ok(const struct nlmsghdr *, int);
|
||||||
|
extern struct nlmsghdr * nlmsg_next(struct nlmsghdr *, int *);
|
||||||
|
extern int nlmsg_parse(struct nlmsghdr *, int, struct nlattr **,
|
||||||
|
int, struct nla_policy *);
|
||||||
|
extern int nlmsg_validate(struct nlmsghdr *, int, int,
|
||||||
|
struct nla_policy *);
|
||||||
|
|
||||||
|
extern struct nl_msg * nlmsg_alloc(void);
|
||||||
|
extern struct nl_msg * nlmsg_alloc_size(size_t);
|
||||||
|
extern struct nl_msg * nlmsg_alloc_simple(int, int);
|
||||||
|
extern void nlmsg_set_default_size(size_t);
|
||||||
|
extern struct nl_msg * nlmsg_inherit(struct nlmsghdr *);
|
||||||
|
extern struct nl_msg * nlmsg_convert(struct nlmsghdr *);
|
||||||
|
extern void * nlmsg_reserve(struct nl_msg *, size_t, int);
|
||||||
|
extern int nlmsg_append(struct nl_msg *, void *, size_t, int);
|
||||||
|
|
||||||
|
extern struct nlmsghdr * nlmsg_put(struct nl_msg *, uint32_t, uint32_t,
|
||||||
|
int, int, int);
|
||||||
|
extern void nlmsg_free(struct nl_msg *);
|
||||||
|
|
||||||
|
extern int nl_msg_parse(struct nl_msg *,
|
||||||
|
void (*cb)(struct nl_object *, void *),
|
||||||
|
void *);
|
||||||
|
|
||||||
|
extern void nl_msg_dump(struct nl_msg *, FILE *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* length of netlink message not including padding
|
||||||
|
* @arg payload length of message payload
|
||||||
|
*/
|
||||||
|
static inline int nlmsg_msg_size(int payload)
|
||||||
|
{
|
||||||
|
return NLMSG_HDRLEN + payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* length of netlink message including padding
|
||||||
|
* @arg payload length of message payload
|
||||||
|
*/
|
||||||
|
static inline int nlmsg_total_size(int payload)
|
||||||
|
{
|
||||||
|
return NLMSG_ALIGN(nlmsg_msg_size(payload));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* length of padding at the message's tail
|
||||||
|
* @arg payload length of message payload
|
||||||
|
*/
|
||||||
|
static inline int nlmsg_padlen(int payload)
|
||||||
|
{
|
||||||
|
return nlmsg_total_size(payload) - nlmsg_msg_size(payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* head of message payload
|
||||||
|
* @arg nlh netlink messsage header
|
||||||
|
*/
|
||||||
|
static inline void *nlmsg_data(const struct nlmsghdr *nlh)
|
||||||
|
{
|
||||||
|
return (unsigned char *) nlh + NLMSG_HDRLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *nlmsg_tail(const struct nlmsghdr *nlh)
|
||||||
|
{
|
||||||
|
return (unsigned char *) nlh + NLMSG_ALIGN(nlh->nlmsg_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* length of message payload
|
||||||
|
* @arg nlh netlink message header
|
||||||
|
*/
|
||||||
|
static inline int nlmsg_len(const struct nlmsghdr *nlh)
|
||||||
|
{
|
||||||
|
return nlh->nlmsg_len - NLMSG_HDRLEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* head of attributes data
|
||||||
|
* @arg nlh netlink message header
|
||||||
|
* @arg hdrlen length of family specific header
|
||||||
|
*/
|
||||||
|
static inline struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen)
|
||||||
|
{
|
||||||
|
unsigned char *data = nlmsg_data(nlh);
|
||||||
|
return (struct nlattr *) (data + NLMSG_ALIGN(hdrlen));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* length of attributes data
|
||||||
|
* @arg nlh netlink message header
|
||||||
|
* @arg hdrlen length of family specific header
|
||||||
|
*/
|
||||||
|
static inline int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen)
|
||||||
|
{
|
||||||
|
return nlmsg_len(nlh) - NLMSG_ALIGN(hdrlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int nlmsg_valid_hdr(const struct nlmsghdr *nlh, int hdrlen)
|
||||||
|
{
|
||||||
|
if (nlh->nlmsg_len < nlmsg_msg_size(hdrlen))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline void nlmsg_set_proto(struct nl_msg *msg, int protocol)
|
||||||
|
{
|
||||||
|
msg->nm_protocol = protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int nlmsg_get_proto(struct nl_msg *msg)
|
||||||
|
{
|
||||||
|
return msg->nm_protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline size_t nlmsg_get_max_size(struct nl_msg *msg)
|
||||||
|
{
|
||||||
|
return msg->nm_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nlmsg_set_src(struct nl_msg *msg, struct sockaddr_nl *addr)
|
||||||
|
{
|
||||||
|
memcpy(&msg->nm_src, addr, sizeof(*addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct sockaddr_nl *nlmsg_get_src(struct nl_msg *msg)
|
||||||
|
{
|
||||||
|
return &msg->nm_src;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nlmsg_set_dst(struct nl_msg *msg, struct sockaddr_nl *addr)
|
||||||
|
{
|
||||||
|
memcpy(&msg->nm_dst, addr, sizeof(*addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct sockaddr_nl *nlmsg_get_dst(struct nl_msg *msg)
|
||||||
|
{
|
||||||
|
return &msg->nm_dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nlmsg_set_creds(struct nl_msg *msg, struct ucred *creds)
|
||||||
|
{
|
||||||
|
memcpy(&msg->nm_creds, creds, sizeof(*creds));
|
||||||
|
msg->nm_flags |= NL_MSG_CRED_PRESENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct ucred *nlmsg_get_creds(struct nl_msg *msg)
|
||||||
|
{
|
||||||
|
if (msg->nm_flags & NL_MSG_CRED_PRESENT)
|
||||||
|
return &msg->nm_creds;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return actual netlink message
|
||||||
|
* @arg n netlink message
|
||||||
|
*
|
||||||
|
* Returns the actual netlink message casted to the type of the netlink
|
||||||
|
* message header.
|
||||||
|
*
|
||||||
|
* @return A pointer to the netlink message.
|
||||||
|
*/
|
||||||
|
static inline struct nlmsghdr *nlmsg_hdr(struct nl_msg *n)
|
||||||
|
{
|
||||||
|
return n->nm_nlh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acquire a reference on a netlink message
|
||||||
|
* @arg msg message to acquire reference from
|
||||||
|
*/
|
||||||
|
static inline void nlmsg_get(struct nl_msg *msg)
|
||||||
|
{
|
||||||
|
msg->nm_refcnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand maximum payload size of a netlink message
|
||||||
|
* @arg n Netlink message.
|
||||||
|
* @arg newlen New maximum payload size.
|
||||||
|
*
|
||||||
|
* Reallocates the payload section of a netlink message and increases
|
||||||
|
* the maximum payload size of the message.
|
||||||
|
*
|
||||||
|
* @note Any pointers pointing to old payload block will be stale and
|
||||||
|
* need to be refetched. Therfore, do not expand while constructing
|
||||||
|
* nested attributes or while reserved data blocks are held.
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
static inline int nlmsg_expand(struct nl_msg *n, size_t newlen)
|
||||||
|
{
|
||||||
|
void *tmp;
|
||||||
|
|
||||||
|
if (newlen <= n->nm_size)
|
||||||
|
return -NLE_INVAL;
|
||||||
|
|
||||||
|
tmp = realloc(n->nm_nlh, newlen);
|
||||||
|
if (tmp == NULL)
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
n->nm_nlh = tmp;
|
||||||
|
n->nm_size = newlen;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Iterators
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup msg
|
||||||
|
* Iterate over a stream of attributes in a message
|
||||||
|
* @arg pos loop counter, set to current attribute
|
||||||
|
* @arg nlh netlink message header
|
||||||
|
* @arg hdrlen length of family header
|
||||||
|
* @arg rem initialized to len, holds bytes currently remaining in stream
|
||||||
|
*/
|
||||||
|
#define nlmsg_for_each_attr(pos, nlh, hdrlen, rem) \
|
||||||
|
nla_for_each_attr(pos, nlmsg_attrdata(nlh, hdrlen), \
|
||||||
|
nlmsg_attrlen(nlh, hdrlen), rem)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over a stream of messages
|
||||||
|
* @arg pos loop counter, set to current message
|
||||||
|
* @arg head head of message stream
|
||||||
|
* @arg len length of message stream
|
||||||
|
* @arg rem initialized to len, holds bytes currently remaining in stream
|
||||||
|
*/
|
||||||
|
#define nlmsg_for_each_msg(pos, head, len, rem) \
|
||||||
|
for (pos = head, rem = len; \
|
||||||
|
nlmsg_ok(pos, rem); \
|
||||||
|
pos = nlmsg_next(pos, &(rem)))
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* netlink/netlink-compat.h Netlink Compatability
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_COMPAT_H_
|
||||||
|
#define NETLINK_COMPAT_H_
|
||||||
|
|
||||||
|
#if !defined _LINUX_SOCKET_H && !defined _BITS_SOCKADDR_H
|
||||||
|
typedef unsigned short sa_family_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef IFNAMSIZ
|
||||||
|
/** Maximum length of a interface name */
|
||||||
|
#define IFNAMSIZ 16
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* patch 2.4.x if_arp */
|
||||||
|
#ifndef ARPHRD_INFINIBAND
|
||||||
|
#define ARPHRD_INFINIBAND 32
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* patch 2.4.x eth header file */
|
||||||
|
#ifndef ETH_P_MPLS_UC
|
||||||
|
#define ETH_P_MPLS_UC 0x8847
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ETH_P_MPLS_MC
|
||||||
|
#define ETH_P_MPLS_MC 0x8848
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ETH_P_EDP2
|
||||||
|
#define ETH_P_EDP2 0x88A2
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ETH_P_HDLC
|
||||||
|
#define ETH_P_HDLC 0x0019
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef AF_LLC
|
||||||
|
#define AF_LLC 26
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -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 */
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* netlink/netlink.h Netlink Interface
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_NETLINK_H_
|
||||||
|
#define NETLINK_NETLINK_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/poll.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netlink/netlink-compat.h>
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/genetlink.h>
|
||||||
|
#include <netlink/version.h>
|
||||||
|
#include <netlink/errno.h>
|
||||||
|
#include <netlink/types.h>
|
||||||
|
#include <netlink/handlers.h>
|
||||||
|
#include <netlink/socket.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int nl_debug;
|
||||||
|
extern struct nl_dump_params nl_debug_dp;
|
||||||
|
|
||||||
|
/* Connection Management */
|
||||||
|
extern int nl_connect(struct nl_sock *, int);
|
||||||
|
extern void nl_close(struct nl_sock *);
|
||||||
|
|
||||||
|
/* Send */
|
||||||
|
extern int nl_sendto(struct nl_sock *, void *, size_t);
|
||||||
|
extern int nl_sendmsg(struct nl_sock *, struct nl_msg *,
|
||||||
|
struct msghdr *);
|
||||||
|
extern int nl_send(struct nl_sock *, struct nl_msg *);
|
||||||
|
extern int nl_send_auto_complete(struct nl_sock *,
|
||||||
|
struct nl_msg *);
|
||||||
|
extern int nl_send_simple(struct nl_sock *, int, int,
|
||||||
|
void *, size_t);
|
||||||
|
|
||||||
|
/* Receive */
|
||||||
|
extern int nl_recv(struct nl_sock *,
|
||||||
|
struct sockaddr_nl *, unsigned char **,
|
||||||
|
struct ucred **);
|
||||||
|
extern int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb);
|
||||||
|
|
||||||
|
extern int nl_wait_for_ack(struct nl_sock *);
|
||||||
|
|
||||||
|
/* Netlink Family Translations */
|
||||||
|
extern char * nl_nlfamily2str(int, char *, size_t);
|
||||||
|
extern int nl_str2nlfamily(const char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a set of message from a netlink socket using handlers in nl_sock.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
*
|
||||||
|
* Calls nl_recvmsgs() with the handlers configured in the netlink socket.
|
||||||
|
*/
|
||||||
|
static inline int nl_recvmsgs_default(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
return nl_recvmsgs(sk, sk->s_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,331 @@
|
||||||
|
/*
|
||||||
|
* netlink/object-api.c Object API
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2007 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_OBJECT_API_H_
|
||||||
|
#define NETLINK_OBJECT_API_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup object
|
||||||
|
* @defgroup object_api Object API
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* @par 1) Object Definition
|
||||||
|
* @code
|
||||||
|
* // Define your object starting with the common object header
|
||||||
|
* struct my_obj {
|
||||||
|
* NLHDR_COMMON
|
||||||
|
* int my_data;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* // Fill out the object operations structure
|
||||||
|
* struct nl_object_ops my_ops = {
|
||||||
|
* .oo_name = "my_obj",
|
||||||
|
* .oo_size = sizeof(struct my_obj),
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* // At this point the object can be allocated, you may want to provide a
|
||||||
|
* // separate _alloc() function to ease allocting objects of this kind.
|
||||||
|
* struct nl_object *obj = nl_object_alloc(&my_ops);
|
||||||
|
*
|
||||||
|
* // And release it again...
|
||||||
|
* nl_object_put(obj);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par 2) Allocating additional data
|
||||||
|
* @code
|
||||||
|
* // You may require to allocate additional data and store it inside
|
||||||
|
* // object, f.e. assuming there is a field `ptr'.
|
||||||
|
* struct my_obj {
|
||||||
|
* NLHDR_COMMON
|
||||||
|
* void * ptr;
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* // And at some point you may assign allocated data to this field:
|
||||||
|
* my_obj->ptr = calloc(1, ...);
|
||||||
|
*
|
||||||
|
* // In order to not introduce any memory leaks you have to release
|
||||||
|
* // this data again when the last reference is given back.
|
||||||
|
* static void my_obj_free_data(struct nl_object *obj)
|
||||||
|
* {
|
||||||
|
* struct my_obj *my_obj = nl_object_priv(obj);
|
||||||
|
*
|
||||||
|
* free(my_obj->ptr);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // Also when the object is cloned, you must ensure for your pointer
|
||||||
|
* // stay valid even if one of the clones is freed by either making
|
||||||
|
* // a clone as well or increase the reference count.
|
||||||
|
* static int my_obj_clone(struct nl_object *src, struct nl_object *dst)
|
||||||
|
* {
|
||||||
|
* struct my_obj *my_src = nl_object_priv(src);
|
||||||
|
* struct my_obj *my_dst = nl_object_priv(dst);
|
||||||
|
*
|
||||||
|
* if (src->ptr) {
|
||||||
|
* dst->ptr = calloc(1, ...);
|
||||||
|
* memcpy(dst->ptr, src->ptr, ...);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* struct nl_object_ops my_ops = {
|
||||||
|
* ...
|
||||||
|
* .oo_free_data = my_obj_free_data,
|
||||||
|
* .oo_clone = my_obj_clone,
|
||||||
|
* };
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par 3) Object Dumping
|
||||||
|
* @code
|
||||||
|
* static int my_obj_dump_detailed(struct nl_object *obj,
|
||||||
|
* struct nl_dump_params *params)
|
||||||
|
* {
|
||||||
|
* struct my_obj *my_obj = nl_object_priv(obj);
|
||||||
|
*
|
||||||
|
* // It is absolutely essential to use nl_dump() when printing
|
||||||
|
* // any text to make sure the dumping parameters are respected.
|
||||||
|
* nl_dump(params, "Obj Integer: %d\n", my_obj->my_int);
|
||||||
|
*
|
||||||
|
* // Before we can dump the next line, make sure to prefix
|
||||||
|
* // this line correctly.
|
||||||
|
* nl_new_line(params);
|
||||||
|
*
|
||||||
|
* // You may also split a line into multiple nl_dump() calls.
|
||||||
|
* nl_dump(params, "String: %s ", my_obj->my_string);
|
||||||
|
* nl_dump(params, "String-2: %s\n", my_obj->another_string);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* struct nl_object_ops my_ops = {
|
||||||
|
* ...
|
||||||
|
* .oo_dump[NL_DUMP_FULL] = my_obj_dump_detailed,
|
||||||
|
* };
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par 4) Object Attributes
|
||||||
|
* @code
|
||||||
|
* // The concept of object attributes is optional but can ease the typical
|
||||||
|
* // case of objects that have optional attributes, e.g. a route may have a
|
||||||
|
* // nexthop assigned but it is not required to.
|
||||||
|
*
|
||||||
|
* // The first step to define your object specific bitmask listing all
|
||||||
|
* // attributes
|
||||||
|
* #define MY_ATTR_FOO (1<<0)
|
||||||
|
* #define MY_ATTR_BAR (1<<1)
|
||||||
|
*
|
||||||
|
* // When assigning an optional attribute to the object, make sure
|
||||||
|
* // to mark its availability.
|
||||||
|
* my_obj->foo = 123123;
|
||||||
|
* my_obj->ce_mask |= MY_ATTR_FOO;
|
||||||
|
*
|
||||||
|
* // At any time you may use this mask to check for the availability
|
||||||
|
* // of the attribute, e.g. while dumping
|
||||||
|
* if (my_obj->ce_mask & MY_ATTR_FOO)
|
||||||
|
* nl_dump(params, "foo %d ", my_obj->foo);
|
||||||
|
*
|
||||||
|
* // One of the big advantages of this concept is that it allows for
|
||||||
|
* // standardized comparisons which make it trivial for caches to
|
||||||
|
* // identify unique objects by use of unified comparison functions.
|
||||||
|
* // In order for it to work, your object implementation must provide
|
||||||
|
* // a comparison function and define a list of attributes which
|
||||||
|
* // combined together make an object unique.
|
||||||
|
*
|
||||||
|
* static int my_obj_compare(struct nl_object *_a, struct nl_object *_b,
|
||||||
|
* uint32_t attrs, int flags)
|
||||||
|
* {
|
||||||
|
* struct my_obj *a = nl_object_priv(_a):
|
||||||
|
* struct my_obj *b = nl_object_priv(_b):
|
||||||
|
* int diff = 0;
|
||||||
|
*
|
||||||
|
* // We help ourselves in defining our own DIFF macro which will
|
||||||
|
* // call ATTR_DIFF() on both objects which will make sure to only
|
||||||
|
* // compare the attributes if required.
|
||||||
|
* #define MY_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, MY_ATTR_##ATTR, a, b, EXPR)
|
||||||
|
*
|
||||||
|
* // Call our own diff macro for each attribute to build a bitmask
|
||||||
|
* // representing the attributes which mismatch.
|
||||||
|
* diff |= MY_DIFF(FOO, a->foo != b->foo)
|
||||||
|
* diff |= MY_DIFF(BAR, strcmp(a->bar, b->bar))
|
||||||
|
*
|
||||||
|
* return diff;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // In order to identify identical objects with differing attributes
|
||||||
|
* // you must specify the attributes required to uniquely identify
|
||||||
|
* // your object. Make sure to not include too many attributes, this
|
||||||
|
* // list is used when caches look for an old version of an object.
|
||||||
|
* struct nl_object_ops my_ops = {
|
||||||
|
* ...
|
||||||
|
* .oo_id_attrs = MY_ATTR_FOO,
|
||||||
|
* .oo_compare = my_obj_compare,
|
||||||
|
* };
|
||||||
|
* @endcode
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common Object Header
|
||||||
|
*
|
||||||
|
* This macro must be included as first member in every object
|
||||||
|
* definition to allow objects to be cached.
|
||||||
|
*/
|
||||||
|
#define NLHDR_COMMON \
|
||||||
|
int ce_refcnt; \
|
||||||
|
struct nl_object_ops * ce_ops; \
|
||||||
|
struct nl_cache * ce_cache; \
|
||||||
|
struct nl_list_head ce_list; \
|
||||||
|
int ce_msgtype; \
|
||||||
|
int ce_flags; \
|
||||||
|
uint32_t ce_mask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if attribute is available in both objects
|
||||||
|
* @arg A an object
|
||||||
|
* @arg B another object
|
||||||
|
* @arg ATTR attribute bit
|
||||||
|
*
|
||||||
|
* @return True if the attribute is available, otherwise false is returned.
|
||||||
|
*/
|
||||||
|
#define AVAILABLE(A, B, ATTR) (((A)->ce_mask & (B)->ce_mask) & (ATTR))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if attributes mismatch
|
||||||
|
* @arg A an object
|
||||||
|
* @arg B another object
|
||||||
|
* @arg ATTR attribute bit
|
||||||
|
* @arg EXPR Comparison expression
|
||||||
|
*
|
||||||
|
* This function will check if the attribute in question is available
|
||||||
|
* in both objects, if not this will count as a mismatch.
|
||||||
|
*
|
||||||
|
* If available the function will execute the expression which must
|
||||||
|
* return true if the attributes mismatch.
|
||||||
|
*
|
||||||
|
* @return True if the attribute mismatch, or false if they match.
|
||||||
|
*/
|
||||||
|
#define ATTR_MISMATCH(A, B, ATTR, EXPR) (!AVAILABLE(A, B, ATTR) || (EXPR))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return attribute bit if attribute does not match
|
||||||
|
* @arg LIST list of attributes to be compared
|
||||||
|
* @arg ATTR attribute bit
|
||||||
|
* @arg A an object
|
||||||
|
* @arg B another object
|
||||||
|
* @arg EXPR Comparison expression
|
||||||
|
*
|
||||||
|
* This function will check if the attribute in question is available
|
||||||
|
* in both objects, if not this will count as a mismatch.
|
||||||
|
*
|
||||||
|
* If available the function will execute the expression which must
|
||||||
|
* return true if the attributes mismatch.
|
||||||
|
*
|
||||||
|
* In case the attributes mismatch, the attribute is returned, otherwise
|
||||||
|
* 0 is returned.
|
||||||
|
*
|
||||||
|
* @code
|
||||||
|
* diff |= ATTR_DIFF(attrs, MY_ATTR_FOO, a, b, a->foo != b->foo);
|
||||||
|
* @endcode
|
||||||
|
*/
|
||||||
|
#define ATTR_DIFF(LIST, ATTR, A, B, EXPR) \
|
||||||
|
({ int diff = 0; \
|
||||||
|
if (((LIST) & (ATTR)) && ATTR_MISMATCH(A, B, ATTR, EXPR)) \
|
||||||
|
diff = ATTR; \
|
||||||
|
diff; })
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object Operations
|
||||||
|
*/
|
||||||
|
struct nl_object;
|
||||||
|
struct nl_object_ops
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Unique name of object type
|
||||||
|
*
|
||||||
|
* Must be in the form family/name, e.g. "route/addr"
|
||||||
|
*/
|
||||||
|
char * oo_name;
|
||||||
|
|
||||||
|
/** Size of object including its header */
|
||||||
|
size_t oo_size;
|
||||||
|
|
||||||
|
/* List of attributes needed to uniquely identify the object */
|
||||||
|
uint32_t oo_id_attrs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor function
|
||||||
|
*
|
||||||
|
* Will be called when a new object of this type is allocated.
|
||||||
|
* Can be used to initialize members such as lists etc.
|
||||||
|
*/
|
||||||
|
void (*oo_constructor)(struct nl_object *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destructor function
|
||||||
|
*
|
||||||
|
* Will be called when an object is freed. Must free all
|
||||||
|
* resources which may have been allocated as part of this
|
||||||
|
* object.
|
||||||
|
*/
|
||||||
|
void (*oo_free_data)(struct nl_object *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cloning function
|
||||||
|
*
|
||||||
|
* Will be called when an object needs to be cloned. Please
|
||||||
|
* note that the generic object code will make an exact
|
||||||
|
* copy of the object first, therefore you only need to take
|
||||||
|
* care of members which require reference counting etc.
|
||||||
|
*
|
||||||
|
* May return a negative error code to abort cloning.
|
||||||
|
*/
|
||||||
|
int (*oo_clone)(struct nl_object *, struct nl_object *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumping functions
|
||||||
|
*
|
||||||
|
* Will be called when an object is dumped. The implementations
|
||||||
|
* have to use nl_dump(), nl_dump_line(), and nl_new_line() to
|
||||||
|
* dump objects.
|
||||||
|
*
|
||||||
|
* The functions must return the number of lines printed.
|
||||||
|
*/
|
||||||
|
void (*oo_dump[NL_DUMP_MAX+1])(struct nl_object *,
|
||||||
|
struct nl_dump_params *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Comparison function
|
||||||
|
*
|
||||||
|
* Will be called when two objects of the same type are
|
||||||
|
* compared. It takes the two objects in question, an object
|
||||||
|
* specific bitmask defining which attributes should be
|
||||||
|
* compared and flags to control the behaviour.
|
||||||
|
*
|
||||||
|
* The function must return a bitmask with the relevant bit
|
||||||
|
* set for each attribute that mismatches.
|
||||||
|
*/
|
||||||
|
int (*oo_compare)(struct nl_object *, struct nl_object *,
|
||||||
|
uint32_t, int);
|
||||||
|
|
||||||
|
|
||||||
|
char *(*oo_attrs2str)(int, char *, size_t);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* netlink/object.c Generic Cacheable Object
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_OBJECT_H_
|
||||||
|
#define NETLINK_OBJECT_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
#include <netlink/object-api.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NL_OBJ_MARK 1
|
||||||
|
|
||||||
|
struct nl_cache;
|
||||||
|
struct nl_object;
|
||||||
|
struct nl_object_ops;
|
||||||
|
|
||||||
|
struct nl_object
|
||||||
|
{
|
||||||
|
NLHDR_COMMON
|
||||||
|
};
|
||||||
|
|
||||||
|
#define OBJ_CAST(ptr) ((struct nl_object *) (ptr))
|
||||||
|
|
||||||
|
/* General */
|
||||||
|
extern struct nl_object * nl_object_alloc(struct nl_object_ops *);
|
||||||
|
extern int nl_object_alloc_name(const char *,
|
||||||
|
struct nl_object **);
|
||||||
|
extern void nl_object_free(struct nl_object *);
|
||||||
|
extern struct nl_object * nl_object_clone(struct nl_object *obj);
|
||||||
|
extern void nl_object_get(struct nl_object *);
|
||||||
|
extern void nl_object_put(struct nl_object *);
|
||||||
|
extern void nl_object_dump(struct nl_object *,
|
||||||
|
struct nl_dump_params *);
|
||||||
|
extern int nl_object_identical(struct nl_object *,
|
||||||
|
struct nl_object *);
|
||||||
|
extern uint32_t nl_object_diff(struct nl_object *,
|
||||||
|
struct nl_object *);
|
||||||
|
extern int nl_object_match_filter(struct nl_object *,
|
||||||
|
struct nl_object *);
|
||||||
|
extern char * nl_object_attrs2str(struct nl_object *,
|
||||||
|
uint32_t attrs, char *buf,
|
||||||
|
size_t);
|
||||||
|
/**
|
||||||
|
* Check whether this object is used by multiple users
|
||||||
|
* @arg obj object to check
|
||||||
|
* @return true or false
|
||||||
|
*/
|
||||||
|
static inline int nl_object_shared(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
return obj->ce_refcnt > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Marks
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add mark to object
|
||||||
|
* @arg obj Object to mark
|
||||||
|
*/
|
||||||
|
static inline void nl_object_mark(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
obj->ce_flags |= NL_OBJ_MARK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove mark from object
|
||||||
|
* @arg obj Object to unmark
|
||||||
|
*/
|
||||||
|
static inline void nl_object_unmark(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
obj->ce_flags &= ~NL_OBJ_MARK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if object is marked
|
||||||
|
* @arg obj Object to check
|
||||||
|
* @return true if object is marked, otherwise false
|
||||||
|
*/
|
||||||
|
static inline int nl_object_is_marked(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
return (obj->ce_flags & NL_OBJ_MARK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return list of attributes present in an object
|
||||||
|
* @arg obj an object
|
||||||
|
* @arg buf destination buffer
|
||||||
|
* @arg len length of destination buffer
|
||||||
|
*
|
||||||
|
* @return destination buffer.
|
||||||
|
*/
|
||||||
|
static inline char *nl_object_attr_list(struct nl_object *obj, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
return nl_object_attrs2str(obj, obj->ce_mask, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Attributes
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline int nl_object_get_refcnt(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
return obj->ce_refcnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline struct nl_cache *nl_object_get_cache(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
return obj->ce_cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void * nl_object_priv(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,221 @@
|
||||||
|
/*
|
||||||
|
* netlink/socket.h Netlink Socket
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_SOCKET_H_
|
||||||
|
#define NETLINK_SOCKET_H_
|
||||||
|
|
||||||
|
#include <netlink/types.h>
|
||||||
|
#include <netlink/handlers.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define NL_SOCK_BUFSIZE_SET (1<<0)
|
||||||
|
#define NL_SOCK_PASSCRED (1<<1)
|
||||||
|
#define NL_OWN_PORT (1<<2)
|
||||||
|
#define NL_MSG_PEEK (1<<3)
|
||||||
|
#define NL_NO_AUTO_ACK (1<<4)
|
||||||
|
|
||||||
|
struct nl_cb;
|
||||||
|
struct nl_sock
|
||||||
|
{
|
||||||
|
struct sockaddr_nl s_local;
|
||||||
|
struct sockaddr_nl s_peer;
|
||||||
|
int s_fd;
|
||||||
|
int s_proto;
|
||||||
|
unsigned int s_seq_next;
|
||||||
|
unsigned int s_seq_expect;
|
||||||
|
int s_flags;
|
||||||
|
struct nl_cb * s_cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
extern struct nl_sock * nl_socket_alloc(void);
|
||||||
|
extern struct nl_sock * nl_socket_alloc_cb(struct nl_cb *);
|
||||||
|
extern void nl_socket_free(struct nl_sock *);
|
||||||
|
|
||||||
|
extern void nl_socket_set_local_port(struct nl_sock *, uint32_t);
|
||||||
|
|
||||||
|
extern int nl_socket_add_memberships(struct nl_sock *, int, ...);
|
||||||
|
extern int nl_socket_drop_memberships(struct nl_sock *, int, ...);
|
||||||
|
|
||||||
|
extern int nl_socket_set_buffer_size(struct nl_sock *, int, int);
|
||||||
|
extern int nl_socket_set_passcred(struct nl_sock *, int);
|
||||||
|
extern int nl_socket_recv_pktinfo(struct nl_sock *, int);
|
||||||
|
|
||||||
|
extern void nl_socket_disable_seq_check(struct nl_sock *);
|
||||||
|
|
||||||
|
extern int nl_socket_set_nonblocking(struct nl_sock *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use next sequence number
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
*
|
||||||
|
* Uses the next available sequence number and increases the counter
|
||||||
|
* by one for subsequent calls.
|
||||||
|
*
|
||||||
|
* @return Unique serial sequence number
|
||||||
|
*/
|
||||||
|
static inline unsigned int nl_socket_use_seq(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
return sk->s_seq_next++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable automatic request for ACK
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
*
|
||||||
|
* The default behaviour of a socket is to request an ACK for
|
||||||
|
* each message sent to allow for the caller to synchronize to
|
||||||
|
* the completion of the netlink operation. This function
|
||||||
|
* disables this behaviour and will result in requests being
|
||||||
|
* sent which will not have the NLM_F_ACK flag set automatically.
|
||||||
|
* However, it is still possible for the caller to set the
|
||||||
|
* NLM_F_ACK flag explicitely.
|
||||||
|
*/
|
||||||
|
static inline void nl_socket_disable_auto_ack(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
sk->s_flags |= NL_NO_AUTO_ACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable automatic request for ACK (default)
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @see nl_socket_disable_auto_ack
|
||||||
|
*/
|
||||||
|
static inline void nl_socket_enable_auto_ack(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
sk->s_flags &= ~NL_NO_AUTO_ACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Source Idenficiation
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline uint32_t nl_socket_get_local_port(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
return sk->s_local.nl_pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join multicast groups (deprecated)
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg groups Bitmask of groups to join.
|
||||||
|
*
|
||||||
|
* This function defines the old way of joining multicast group which
|
||||||
|
* has to be done prior to calling nl_connect(). It works on any kernel
|
||||||
|
* version but is very limited as only 32 groups can be joined.
|
||||||
|
*/
|
||||||
|
static inline void nl_join_groups(struct nl_sock *sk, int groups)
|
||||||
|
{
|
||||||
|
sk->s_local.nl_groups |= groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Peer Identfication
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline uint32_t nl_socket_get_peer_port(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
return sk->s_peer.nl_pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nl_socket_set_peer_port(struct nl_sock *sk, uint32_t port)
|
||||||
|
{
|
||||||
|
sk->s_peer.nl_pid = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name File Descriptor
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline int nl_socket_get_fd(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
return sk->s_fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable use of MSG_PEEK when reading from socket
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
*/
|
||||||
|
static inline void nl_socket_enable_msg_peek(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
sk->s_flags |= NL_MSG_PEEK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable use of MSG_PEEK when reading from socket
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
*/
|
||||||
|
static inline void nl_socket_disable_msg_peek(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
sk->s_flags &= ~NL_MSG_PEEK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Callback Handler
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
static inline struct nl_cb *nl_socket_get_cb(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
return nl_cb_get(sk->s_cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb)
|
||||||
|
{
|
||||||
|
nl_cb_put(sk->s_cb);
|
||||||
|
sk->s_cb = nl_cb_get(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify the callback handler associated to the socket
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg type which type callback to set
|
||||||
|
* @arg kind kind of callback
|
||||||
|
* @arg func callback function
|
||||||
|
* @arg arg argument to be passwd to callback function
|
||||||
|
*
|
||||||
|
* @see nl_cb_set
|
||||||
|
*/
|
||||||
|
static inline int nl_socket_modify_cb(struct nl_sock *sk, enum nl_cb_type type,
|
||||||
|
enum nl_cb_kind kind, nl_recvmsg_msg_cb_t func,
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
return nl_cb_set(sk->s_cb, type, kind, func, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
static inline int nl_socket_add_membership(struct nl_sock *sk, int group)
|
||||||
|
{
|
||||||
|
return nl_socket_add_memberships(sk, group, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static inline int nl_socket_drop_membership(struct nl_sock *sk, int group)
|
||||||
|
{
|
||||||
|
return nl_socket_drop_memberships(sk, group, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,117 @@
|
||||||
|
/*
|
||||||
|
* netlink/netlink-types.h Netlink Types
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __NETLINK_TYPES_H_
|
||||||
|
#define __NETLINK_TYPES_H_
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumping types (dp_type)
|
||||||
|
* @ingroup utils
|
||||||
|
*/
|
||||||
|
enum nl_dump_type {
|
||||||
|
NL_DUMP_LINE, /**< Dump object briefly on one line */
|
||||||
|
NL_DUMP_DETAILS, /**< Dump all attributes but no statistics */
|
||||||
|
NL_DUMP_STATS, /**< Dump all attributes including statistics */
|
||||||
|
NL_DUMP_ENV, /**< Dump all attribtues as env variables */
|
||||||
|
__NL_DUMP_MAX,
|
||||||
|
};
|
||||||
|
#define NL_DUMP_MAX (__NL_DUMP_MAX - 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dumping parameters
|
||||||
|
* @ingroup utils
|
||||||
|
*/
|
||||||
|
struct nl_dump_params
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Specifies the type of dump that is requested.
|
||||||
|
*/
|
||||||
|
enum nl_dump_type dp_type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the number of whitespaces to be put in front
|
||||||
|
* of every new line (indentation).
|
||||||
|
*/
|
||||||
|
int dp_prefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes the cache index to be printed for each element.
|
||||||
|
*/
|
||||||
|
int dp_print_index;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Causes each element to be prefixed with the message type.
|
||||||
|
*/
|
||||||
|
int dp_dump_msgtype;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback invoked for output
|
||||||
|
*
|
||||||
|
* Passed arguments are:
|
||||||
|
* - dumping parameters
|
||||||
|
* - string to append to the output
|
||||||
|
*/
|
||||||
|
void (*dp_cb)(struct nl_dump_params *, char *);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A callback invoked for every new line, can be used to
|
||||||
|
* customize the indentation.
|
||||||
|
*
|
||||||
|
* Passed arguments are:
|
||||||
|
* - dumping parameters
|
||||||
|
* - line number starting from 0
|
||||||
|
*/
|
||||||
|
void (*dp_nl_cb)(struct nl_dump_params *, int);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User data pointer, can be used to pass data to callbacks.
|
||||||
|
*/
|
||||||
|
void *dp_data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* File descriptor the dumping output should go to
|
||||||
|
*/
|
||||||
|
FILE * dp_fd;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alternatively the output may be redirected into a buffer
|
||||||
|
*/
|
||||||
|
char * dp_buf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Length of the buffer dp_buf
|
||||||
|
*/
|
||||||
|
size_t dp_buflen;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PRIVATE
|
||||||
|
* Set if a dump was performed prior to the actual dump handler.
|
||||||
|
*/
|
||||||
|
int dp_pre_dump;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PRIVATE
|
||||||
|
* Owned by the current caller
|
||||||
|
*/
|
||||||
|
int dp_ivar;
|
||||||
|
|
||||||
|
unsigned int dp_line;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define min_t(type,x,y) \
|
||||||
|
({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
|
||||||
|
#define max_t(type,x,y) \
|
||||||
|
({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* netlink/utils.h Utility Functions
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_UTILS_H_
|
||||||
|
#define NETLINK_UTILS_H_
|
||||||
|
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/list.h>
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Probability Constants
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lower probability limit
|
||||||
|
* @ingroup utils
|
||||||
|
*/
|
||||||
|
#define NL_PROB_MIN 0x0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upper probability limit
|
||||||
|
* @ingroup utils
|
||||||
|
*/
|
||||||
|
#define NL_PROB_MAX 0xffffffff
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/* unit pretty-printing */
|
||||||
|
extern double nl_cancel_down_bytes(unsigned long long, char **);
|
||||||
|
extern double nl_cancel_down_bits(unsigned long long, char **);
|
||||||
|
extern double nl_cancel_down_us(uint32_t, char **);
|
||||||
|
|
||||||
|
/* generic unit translations */
|
||||||
|
extern long nl_size2int(const char *);
|
||||||
|
extern long nl_prob2int(const char *);
|
||||||
|
|
||||||
|
/* time translations */
|
||||||
|
extern int nl_get_hz(void);
|
||||||
|
extern uint32_t nl_us2ticks(uint32_t);
|
||||||
|
extern uint32_t nl_ticks2us(uint32_t);
|
||||||
|
extern int nl_str2msec(const char *, uint64_t *);
|
||||||
|
extern char * nl_msec2str(uint64_t, char *, size_t);
|
||||||
|
|
||||||
|
/* link layer protocol translations */
|
||||||
|
extern char * nl_llproto2str(int, char *, size_t);
|
||||||
|
extern int nl_str2llproto(const char *);
|
||||||
|
|
||||||
|
/* ethernet protocol translations */
|
||||||
|
extern char * nl_ether_proto2str(int, char *, size_t);
|
||||||
|
extern int nl_str2ether_proto(const char *);
|
||||||
|
|
||||||
|
/* IP protocol translations */
|
||||||
|
extern char * nl_ip_proto2str(int, char *, size_t);
|
||||||
|
extern int nl_str2ip_proto(const char *);
|
||||||
|
|
||||||
|
/* Dumping helpers */
|
||||||
|
extern void nl_new_line(struct nl_dump_params *);
|
||||||
|
extern void nl_dump(struct nl_dump_params *, const char *, ...);
|
||||||
|
extern void nl_dump_line(struct nl_dump_params *, const char *, ...);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* netlink/version.h Compile Time Versioning Information
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef NETLINK_VERSION_H_
|
||||||
|
#define NETLINK_VERSION_H_
|
||||||
|
|
||||||
|
#define LIBNL_STRING "libnl"
|
||||||
|
#define LIBNL_VERSION "2.0"
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,566 @@
|
||||||
|
/*
|
||||||
|
* lib/msg.c Netlink Messages Interface
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup core
|
||||||
|
* @defgroup msg Messages
|
||||||
|
* Netlink Message Construction/Parsing Interface
|
||||||
|
*
|
||||||
|
* The following information is partly extracted from RFC3549
|
||||||
|
* (ftp://ftp.rfc-editor.org/in-notes/rfc3549.txt)
|
||||||
|
*
|
||||||
|
* @par Message Format
|
||||||
|
* Netlink messages consist of a byte stream with one or multiple
|
||||||
|
* Netlink headers and an associated payload. If the payload is too big
|
||||||
|
* to fit into a single message it, can be split over multiple Netlink
|
||||||
|
* messages, collectively called a multipart message. For multipart
|
||||||
|
* messages, the first and all following headers have the \c NLM_F_MULTI
|
||||||
|
* Netlink header flag set, except for the last header which has the
|
||||||
|
* Netlink header type \c NLMSG_DONE.
|
||||||
|
*
|
||||||
|
* @par
|
||||||
|
* The Netlink message header (\link nlmsghdr struct nlmsghdr\endlink) is shown below.
|
||||||
|
* @code
|
||||||
|
* 0 1 2 3
|
||||||
|
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* | Length |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* | Type | Flags |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* | Sequence Number |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* | Process ID (PID) |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par
|
||||||
|
* The netlink message header and payload must be aligned properly:
|
||||||
|
* @code
|
||||||
|
* <------- NLMSG_ALIGN(hlen) ------> <---- NLMSG_ALIGN(len) --->
|
||||||
|
* +----------------------------+- - -+- - - - - - - - - - -+- - -+
|
||||||
|
* | Header | Pad | Payload | Pad |
|
||||||
|
* | struct nlmsghdr | | | |
|
||||||
|
* +----------------------------+- - -+- - - - - - - - - - -+- - -+
|
||||||
|
* @endcode
|
||||||
|
* @par
|
||||||
|
* Message Format:
|
||||||
|
* @code
|
||||||
|
* <--- nlmsg_total_size(payload) --->
|
||||||
|
* <-- nlmsg_msg_size(payload) ->
|
||||||
|
* +----------+- - -+-------------+- - -+-------- - -
|
||||||
|
* | nlmsghdr | Pad | Payload | Pad | nlmsghdr
|
||||||
|
* +----------+- - -+-------------+- - -+-------- - -
|
||||||
|
* nlmsg_data(nlh)---^ ^
|
||||||
|
* nlmsg_next(nlh)-----------------------+
|
||||||
|
* @endcode
|
||||||
|
* @par
|
||||||
|
* The payload may consist of arbitary data but may have strict
|
||||||
|
* alignment and formatting rules depening on the specific netlink
|
||||||
|
* families.
|
||||||
|
* @par
|
||||||
|
* @code
|
||||||
|
* <---------------------- nlmsg_len(nlh) --------------------->
|
||||||
|
* <------ hdrlen ------> <- nlmsg_attrlen(nlh, hdrlen) ->
|
||||||
|
* +----------------------+- - -+--------------------------------+
|
||||||
|
* | Family Header | Pad | Attributes |
|
||||||
|
* +----------------------+- - -+--------------------------------+
|
||||||
|
* nlmsg_attrdata(nlh, hdrlen)---^
|
||||||
|
* @endcode
|
||||||
|
* @par The ACK Netlink Message
|
||||||
|
* This message is actually used to denote both an ACK and a NACK.
|
||||||
|
* Typically, the direction is from FEC to CPC (in response to an ACK
|
||||||
|
* request message). However, the CPC should be able to send ACKs back
|
||||||
|
* to FEC when requested.
|
||||||
|
* @code
|
||||||
|
* 0 1 2 3
|
||||||
|
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* | Netlink message header |
|
||||||
|
* | type = NLMSG_ERROR |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* | Error code |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* | OLD Netlink message header |
|
||||||
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par Example
|
||||||
|
* @code
|
||||||
|
* // Various methods exist to create/allocate a new netlink
|
||||||
|
* // message.
|
||||||
|
* //
|
||||||
|
* // nlmsg_alloc() will allocate an empty netlink message with
|
||||||
|
* // a maximum payload size which defaults to the page size of
|
||||||
|
* // the system. This default size can be modified using the
|
||||||
|
* // function nlmsg_set_default_size().
|
||||||
|
* struct nl_msg *msg = nlmsg_alloc();
|
||||||
|
*
|
||||||
|
* // Very often, the message type and message flags are known
|
||||||
|
* // at allocation time while the other fields are auto generated:
|
||||||
|
* struct nl_msg *msg = nlmsg_alloc_simple(MY_TYPE, MY_FLAGS);
|
||||||
|
*
|
||||||
|
* // Alternatively an existing netlink message header can be used
|
||||||
|
* // to inherit the header values:
|
||||||
|
* struct nlmsghdr hdr = {
|
||||||
|
* .nlmsg_type = MY_TYPE,
|
||||||
|
* .nlmsg_flags = MY_FLAGS,
|
||||||
|
* };
|
||||||
|
* struct nl_msg *msg = nlmsg_inherit(&hdr);
|
||||||
|
*
|
||||||
|
* // Last but not least, netlink messages received from netlink sockets
|
||||||
|
* // can be converted into nl_msg objects using nlmsg_convert(). This
|
||||||
|
* // will create a message with a maximum payload size which equals the
|
||||||
|
* // length of the existing netlink message, therefore no more data can
|
||||||
|
* // be appened without calling nlmsg_expand() first.
|
||||||
|
* struct nl_msg *msg = nlmsg_convert(nlh_from_nl_sock);
|
||||||
|
*
|
||||||
|
* // Payload may be added to the message via nlmsg_append(). The fourth
|
||||||
|
* // parameter specifies the number of alignment bytes the data should
|
||||||
|
* // be padding with at the end. Common values are 0 to disable it or
|
||||||
|
* // NLMSG_ALIGNTO to ensure proper netlink message padding.
|
||||||
|
* nlmsg_append(msg, &mydata, sizeof(mydata), 0);
|
||||||
|
*
|
||||||
|
* // Sometimes it may be necessary to reserve room for data but defer
|
||||||
|
* // the actual copying to a later point, nlmsg_reserve() can be used
|
||||||
|
* // for this purpose:
|
||||||
|
* void *data = nlmsg_reserve(msg, sizeof(mydata), NLMSG_ALIGNTO);
|
||||||
|
*
|
||||||
|
* // Attributes may be added using the attributes interface.
|
||||||
|
*
|
||||||
|
* // After successful use of the message, the memory must be freed
|
||||||
|
* // using nlmsg_free()
|
||||||
|
* nlmsg_free(msg);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par 4) Parsing messages
|
||||||
|
* @code
|
||||||
|
* int n;
|
||||||
|
* unsigned char *buf;
|
||||||
|
* struct nlmsghdr *hdr;
|
||||||
|
*
|
||||||
|
* n = nl_recv(handle, NULL, &buf);
|
||||||
|
*
|
||||||
|
* hdr = (struct nlmsghdr *) buf;
|
||||||
|
* while (nlmsg_ok(hdr, n)) {
|
||||||
|
* // Process message here...
|
||||||
|
* hdr = nlmsg_next(hdr, &n);
|
||||||
|
* }
|
||||||
|
* @endcode
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-local.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
#include <netlink/cache.h>
|
||||||
|
#include <netlink/attr.h>
|
||||||
|
#include <netlink/msg.h>
|
||||||
|
#include <linux/socket.h>
|
||||||
|
|
||||||
|
static size_t default_msg_size;
|
||||||
|
|
||||||
|
static void __init init_msg_size(void)
|
||||||
|
{
|
||||||
|
default_msg_size = getpagesize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Attribute Access
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
//** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Message Parsing
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* check if the netlink message fits into the remaining bytes
|
||||||
|
* @arg nlh netlink message header
|
||||||
|
* @arg remaining number of bytes remaining in message stream
|
||||||
|
*/
|
||||||
|
int nlmsg_ok(const struct nlmsghdr *nlh, int remaining)
|
||||||
|
{
|
||||||
|
return (remaining >= sizeof(struct nlmsghdr) &&
|
||||||
|
nlh->nlmsg_len >= sizeof(struct nlmsghdr) &&
|
||||||
|
nlh->nlmsg_len <= remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* next netlink message in message stream
|
||||||
|
* @arg nlh netlink message header
|
||||||
|
* @arg remaining number of bytes remaining in message stream
|
||||||
|
*
|
||||||
|
* @returns the next netlink message in the message stream and
|
||||||
|
* decrements remaining by the size of the current message.
|
||||||
|
*/
|
||||||
|
struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining)
|
||||||
|
{
|
||||||
|
int totlen = NLMSG_ALIGN(nlh->nlmsg_len);
|
||||||
|
|
||||||
|
*remaining -= totlen;
|
||||||
|
|
||||||
|
return (struct nlmsghdr *) ((unsigned char *) nlh + totlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* parse attributes of a netlink message
|
||||||
|
* @arg nlh netlink message header
|
||||||
|
* @arg hdrlen length of family specific header
|
||||||
|
* @arg tb destination array with maxtype+1 elements
|
||||||
|
* @arg maxtype maximum attribute type to be expected
|
||||||
|
* @arg policy validation policy
|
||||||
|
*
|
||||||
|
* See nla_parse()
|
||||||
|
*/
|
||||||
|
int nlmsg_parse(struct nlmsghdr *nlh, int hdrlen, struct nlattr *tb[],
|
||||||
|
int maxtype, struct nla_policy *policy)
|
||||||
|
{
|
||||||
|
if (!nlmsg_valid_hdr(nlh, hdrlen))
|
||||||
|
return -NLE_MSG_TOOSHORT;
|
||||||
|
|
||||||
|
return nla_parse(tb, maxtype, nlmsg_attrdata(nlh, hdrlen),
|
||||||
|
nlmsg_attrlen(nlh, hdrlen), policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nlmsg_validate - validate a netlink message including attributes
|
||||||
|
* @arg nlh netlinket message header
|
||||||
|
* @arg hdrlen length of familiy specific header
|
||||||
|
* @arg maxtype maximum attribute type to be expected
|
||||||
|
* @arg policy validation policy
|
||||||
|
*/
|
||||||
|
int nlmsg_validate(struct nlmsghdr *nlh, int hdrlen, int maxtype,
|
||||||
|
struct nla_policy *policy)
|
||||||
|
{
|
||||||
|
if (!nlmsg_valid_hdr(nlh, hdrlen))
|
||||||
|
return -NLE_MSG_TOOSHORT;
|
||||||
|
|
||||||
|
return nla_validate(nlmsg_attrdata(nlh, hdrlen),
|
||||||
|
nlmsg_attrlen(nlh, hdrlen), maxtype, policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Message Building/Access
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct nl_msg *__nlmsg_alloc(size_t len)
|
||||||
|
{
|
||||||
|
struct nl_msg *nm;
|
||||||
|
|
||||||
|
nm = calloc(1, sizeof(*nm));
|
||||||
|
if (!nm)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
nm->nm_refcnt = 1;
|
||||||
|
|
||||||
|
nm->nm_nlh = malloc(len);
|
||||||
|
if (!nm->nm_nlh)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
memset(nm->nm_nlh, 0, sizeof(struct nlmsghdr));
|
||||||
|
|
||||||
|
nm->nm_protocol = -1;
|
||||||
|
nm->nm_size = len;
|
||||||
|
nm->nm_nlh->nlmsg_len = nlmsg_total_size(0);
|
||||||
|
|
||||||
|
NL_DBG(2, "msg %p: Allocated new message, maxlen=%zu\n", nm, len);
|
||||||
|
|
||||||
|
return nm;
|
||||||
|
errout:
|
||||||
|
free(nm);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new netlink message with the default maximum payload size.
|
||||||
|
*
|
||||||
|
* Allocates a new netlink message without any further payload. The
|
||||||
|
* maximum payload size defaults to PAGESIZE or as otherwise specified
|
||||||
|
* with nlmsg_set_default_size().
|
||||||
|
*
|
||||||
|
* @return Newly allocated netlink message or NULL.
|
||||||
|
*/
|
||||||
|
struct nl_msg *nlmsg_alloc(void)
|
||||||
|
{
|
||||||
|
return __nlmsg_alloc(default_msg_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new netlink message with maximum payload size specified.
|
||||||
|
*/
|
||||||
|
struct nl_msg *nlmsg_alloc_size(size_t max)
|
||||||
|
{
|
||||||
|
return __nlmsg_alloc(max);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new netlink message and inherit netlink message header
|
||||||
|
* @arg hdr Netlink message header template
|
||||||
|
*
|
||||||
|
* Allocates a new netlink message and inherits the original message
|
||||||
|
* header. If \a hdr is not NULL it will be used as a template for
|
||||||
|
* the netlink message header, otherwise the header is left blank.
|
||||||
|
*
|
||||||
|
* @return Newly allocated netlink message or NULL
|
||||||
|
*/
|
||||||
|
struct nl_msg *nlmsg_inherit(struct nlmsghdr *hdr)
|
||||||
|
{
|
||||||
|
struct nl_msg *nm;
|
||||||
|
|
||||||
|
nm = nlmsg_alloc();
|
||||||
|
if (nm && hdr) {
|
||||||
|
struct nlmsghdr *new = nm->nm_nlh;
|
||||||
|
|
||||||
|
new->nlmsg_type = hdr->nlmsg_type;
|
||||||
|
new->nlmsg_flags = hdr->nlmsg_flags;
|
||||||
|
new->nlmsg_seq = hdr->nlmsg_seq;
|
||||||
|
new->nlmsg_pid = hdr->nlmsg_pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new netlink message
|
||||||
|
* @arg nlmsgtype Netlink message type
|
||||||
|
* @arg flags Message flags.
|
||||||
|
*
|
||||||
|
* @return Newly allocated netlink message or NULL.
|
||||||
|
*/
|
||||||
|
struct nl_msg *nlmsg_alloc_simple(int nlmsgtype, int flags)
|
||||||
|
{
|
||||||
|
struct nl_msg *msg;
|
||||||
|
struct nlmsghdr nlh = {
|
||||||
|
.nlmsg_type = nlmsgtype,
|
||||||
|
.nlmsg_flags = flags,
|
||||||
|
};
|
||||||
|
|
||||||
|
msg = nlmsg_inherit(&nlh);
|
||||||
|
if (msg)
|
||||||
|
NL_DBG(2, "msg %p: Allocated new simple message\n", msg);
|
||||||
|
|
||||||
|
return msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the default maximum message payload size for allocated messages
|
||||||
|
* @arg max Size of payload in bytes.
|
||||||
|
*/
|
||||||
|
void nlmsg_set_default_size(size_t max)
|
||||||
|
{
|
||||||
|
if (max < nlmsg_total_size(0))
|
||||||
|
max = nlmsg_total_size(0);
|
||||||
|
|
||||||
|
default_msg_size = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a netlink message received from a netlink socket to a nl_msg
|
||||||
|
* @arg hdr Netlink message received from netlink socket.
|
||||||
|
*
|
||||||
|
* Allocates a new netlink message and copies all of the data pointed to
|
||||||
|
* by \a hdr into the new message object.
|
||||||
|
*
|
||||||
|
* @return Newly allocated netlink message or NULL.
|
||||||
|
*/
|
||||||
|
struct nl_msg *nlmsg_convert(struct nlmsghdr *hdr)
|
||||||
|
{
|
||||||
|
struct nl_msg *nm;
|
||||||
|
|
||||||
|
nm = __nlmsg_alloc(NLMSG_ALIGN(hdr->nlmsg_len));
|
||||||
|
if (!nm)
|
||||||
|
goto errout;
|
||||||
|
|
||||||
|
memcpy(nm->nm_nlh, hdr, hdr->nlmsg_len);
|
||||||
|
|
||||||
|
return nm;
|
||||||
|
errout:
|
||||||
|
nlmsg_free(nm);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserve room for additional data in a netlink message
|
||||||
|
* @arg n netlink message
|
||||||
|
* @arg len length of additional data to reserve room for
|
||||||
|
* @arg pad number of bytes to align data to
|
||||||
|
*
|
||||||
|
* Reserves room for additional data at the tail of the an
|
||||||
|
* existing netlink message. Eventual padding required will
|
||||||
|
* be zeroed out.
|
||||||
|
*
|
||||||
|
* @return Pointer to start of additional data tailroom or NULL.
|
||||||
|
*/
|
||||||
|
void *nlmsg_reserve(struct nl_msg *n, size_t len, int pad)
|
||||||
|
{
|
||||||
|
void *buf = n->nm_nlh;
|
||||||
|
size_t nlmsg_len = n->nm_nlh->nlmsg_len;
|
||||||
|
size_t tlen;
|
||||||
|
|
||||||
|
tlen = pad ? ((len + (pad - 1)) & ~(pad - 1)) : len;
|
||||||
|
|
||||||
|
if ((tlen + nlmsg_len) > n->nm_size)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
buf += nlmsg_len;
|
||||||
|
n->nm_nlh->nlmsg_len += tlen;
|
||||||
|
|
||||||
|
if (tlen > len)
|
||||||
|
memset(buf + len, 0, tlen - len);
|
||||||
|
|
||||||
|
NL_DBG(2, "msg %p: Reserved %zu bytes, pad=%d, nlmsg_len=%d\n",
|
||||||
|
n, len, pad, n->nm_nlh->nlmsg_len);
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append data to tail of a netlink message
|
||||||
|
* @arg n netlink message
|
||||||
|
* @arg data data to add
|
||||||
|
* @arg len length of data
|
||||||
|
* @arg pad Number of bytes to align data to.
|
||||||
|
*
|
||||||
|
* Extends the netlink message as needed and appends the data of given
|
||||||
|
* length to the message.
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code
|
||||||
|
*/
|
||||||
|
int nlmsg_append(struct nl_msg *n, void *data, size_t len, int pad)
|
||||||
|
{
|
||||||
|
void *tmp;
|
||||||
|
|
||||||
|
tmp = nlmsg_reserve(n, len, pad);
|
||||||
|
if (tmp == NULL)
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
memcpy(tmp, data, len);
|
||||||
|
NL_DBG(2, "msg %p: Appended %zu bytes with padding %d\n", n, len, pad);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a netlink message header to a netlink message
|
||||||
|
* @arg n netlink message
|
||||||
|
* @arg pid netlink process id or NL_AUTO_PID
|
||||||
|
* @arg seq sequence number of message or NL_AUTO_SEQ
|
||||||
|
* @arg type message type
|
||||||
|
* @arg payload length of message payload
|
||||||
|
* @arg flags message flags
|
||||||
|
*
|
||||||
|
* Adds or overwrites the netlink message header in an existing message
|
||||||
|
* object. If \a payload is greater-than zero additional room will be
|
||||||
|
* reserved, f.e. for family specific headers. It can be accesed via
|
||||||
|
* nlmsg_data().
|
||||||
|
*
|
||||||
|
* @return A pointer to the netlink message header or NULL.
|
||||||
|
*/
|
||||||
|
struct nlmsghdr *nlmsg_put(struct nl_msg *n, uint32_t pid, uint32_t seq,
|
||||||
|
int type, int payload, int flags)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
|
||||||
|
if (n->nm_nlh->nlmsg_len < NLMSG_HDRLEN)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
nlh = (struct nlmsghdr *) n->nm_nlh;
|
||||||
|
nlh->nlmsg_type = type;
|
||||||
|
nlh->nlmsg_flags = flags;
|
||||||
|
nlh->nlmsg_pid = pid;
|
||||||
|
nlh->nlmsg_seq = seq;
|
||||||
|
|
||||||
|
NL_DBG(2, "msg %p: Added netlink header type=%d, flags=%d, pid=%d, "
|
||||||
|
"seq=%d\n", n, type, flags, pid, seq);
|
||||||
|
|
||||||
|
if (payload > 0 &&
|
||||||
|
nlmsg_reserve(n, payload, NLMSG_ALIGNTO) == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return nlh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release a reference from an netlink message
|
||||||
|
* @arg msg message to release reference from
|
||||||
|
*
|
||||||
|
* Frees memory after the last reference has been released.
|
||||||
|
*/
|
||||||
|
void nlmsg_free(struct nl_msg *msg)
|
||||||
|
{
|
||||||
|
if (!msg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
msg->nm_refcnt--;
|
||||||
|
NL_DBG(4, "Returned message reference %p, %d remaining\n",
|
||||||
|
msg, msg->nm_refcnt);
|
||||||
|
|
||||||
|
if (msg->nm_refcnt < 0)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
if (msg->nm_refcnt <= 0) {
|
||||||
|
free(msg->nm_nlh);
|
||||||
|
free(msg);
|
||||||
|
NL_DBG(2, "msg %p: Freed\n", msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Direct Parsing
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @cond SKIP */
|
||||||
|
struct dp_xdata {
|
||||||
|
void (*cb)(struct nl_object *, void *);
|
||||||
|
void *arg;
|
||||||
|
};
|
||||||
|
/** @endcond */
|
||||||
|
|
||||||
|
static int parse_cb(struct nl_object *obj, struct nl_parser_param *p)
|
||||||
|
{
|
||||||
|
struct dp_xdata *x = p->pp_arg;
|
||||||
|
|
||||||
|
x->cb(obj, x->arg);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nl_msg_parse(struct nl_msg *msg, void (*cb)(struct nl_object *, void *),
|
||||||
|
void *arg)
|
||||||
|
{
|
||||||
|
struct nl_cache_ops *ops;
|
||||||
|
struct nl_parser_param p = {
|
||||||
|
.pp_cb = parse_cb
|
||||||
|
};
|
||||||
|
struct dp_xdata x = {
|
||||||
|
.cb = cb,
|
||||||
|
.arg = arg,
|
||||||
|
};
|
||||||
|
|
||||||
|
ops = nl_cache_ops_associate(nlmsg_get_proto(msg),
|
||||||
|
nlmsg_hdr(msg)->nlmsg_type);
|
||||||
|
if (ops == NULL)
|
||||||
|
return -NLE_MSGTYPE_NOSUPPORT;
|
||||||
|
p.pp_arg = &x;
|
||||||
|
|
||||||
|
return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
|
@ -0,0 +1,720 @@
|
||||||
|
/*
|
||||||
|
* lib/nl.c Core Netlink Interface
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup core Core
|
||||||
|
*
|
||||||
|
* @details
|
||||||
|
* @par 1) Connecting the socket
|
||||||
|
* @code
|
||||||
|
* // Bind and connect the socket to a protocol, NETLINK_ROUTE in this example.
|
||||||
|
* nl_connect(sk, NETLINK_ROUTE);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par 2) Sending data
|
||||||
|
* @code
|
||||||
|
* // The most rudimentary method is to use nl_sendto() simply pushing
|
||||||
|
* // a piece of data to the other netlink peer. This method is not
|
||||||
|
* // recommended.
|
||||||
|
* const char buf[] = { 0x01, 0x02, 0x03, 0x04 };
|
||||||
|
* nl_sendto(sk, buf, sizeof(buf));
|
||||||
|
*
|
||||||
|
* // A more comfortable interface is nl_send() taking a pointer to
|
||||||
|
* // a netlink message.
|
||||||
|
* struct nl_msg *msg = my_msg_builder();
|
||||||
|
* nl_send(sk, nlmsg_hdr(msg));
|
||||||
|
*
|
||||||
|
* // nl_sendmsg() provides additional control over the sendmsg() message
|
||||||
|
* // header in order to allow more specific addressing of multiple peers etc.
|
||||||
|
* struct msghdr hdr = { ... };
|
||||||
|
* nl_sendmsg(sk, nlmsg_hdr(msg), &hdr);
|
||||||
|
*
|
||||||
|
* // You're probably too lazy to fill out the netlink pid, sequence number
|
||||||
|
* // and message flags all the time. nl_send_auto_complete() automatically
|
||||||
|
* // extends your message header as needed with an appropriate sequence
|
||||||
|
* // number, the netlink pid stored in the netlink socket and the message
|
||||||
|
* // flags NLM_F_REQUEST and NLM_F_ACK (if not disabled in the socket)
|
||||||
|
* nl_send_auto_complete(sk, nlmsg_hdr(msg));
|
||||||
|
*
|
||||||
|
* // Simple protocols don't require the complex message construction interface
|
||||||
|
* // and may favour nl_send_simple() to easly send a bunch of payload
|
||||||
|
* // encapsulated in a netlink message header.
|
||||||
|
* nl_send_simple(sk, MY_MSG_TYPE, 0, buf, sizeof(buf));
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par 3) Receiving data
|
||||||
|
* @code
|
||||||
|
* // nl_recv() receives a single message allocating a buffer for the message
|
||||||
|
* // content and gives back the pointer to you.
|
||||||
|
* struct sockaddr_nl peer;
|
||||||
|
* unsigned char *msg;
|
||||||
|
* nl_recv(sk, &peer, &msg);
|
||||||
|
*
|
||||||
|
* // nl_recvmsgs() receives a bunch of messages until the callback system
|
||||||
|
* // orders it to state, usually after receving a compolete multi part
|
||||||
|
* // message series.
|
||||||
|
* nl_recvmsgs(sk, my_callback_configuration);
|
||||||
|
*
|
||||||
|
* // nl_recvmsgs_default() acts just like nl_recvmsg() but uses the callback
|
||||||
|
* // configuration stored in the socket.
|
||||||
|
* nl_recvmsgs_default(sk);
|
||||||
|
*
|
||||||
|
* // In case you want to wait for the ACK to be recieved that you requested
|
||||||
|
* // with your latest message, you can call nl_wait_for_ack()
|
||||||
|
* nl_wait_for_ack(sk);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @par 4) Closing
|
||||||
|
* @code
|
||||||
|
* // Close the socket first to release kernel memory
|
||||||
|
* nl_close(sk);
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-local.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
#include <netlink/handlers.h>
|
||||||
|
#include <netlink/msg.h>
|
||||||
|
#include <netlink/attr.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Connection Management
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and connect netlink socket.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg protocol Netlink protocol to use.
|
||||||
|
*
|
||||||
|
* Creates a netlink socket using the specified protocol, binds the socket
|
||||||
|
* and issues a connection attempt.
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_connect(struct nl_sock *sk, int protocol)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
socklen_t addrlen;
|
||||||
|
|
||||||
|
sk->s_fd = socket(AF_NETLINK, SOCK_RAW, protocol);
|
||||||
|
if (sk->s_fd < 0) {
|
||||||
|
err = -nl_syserr2nlerr(errno);
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(sk->s_flags & NL_SOCK_BUFSIZE_SET)) {
|
||||||
|
err = nl_socket_set_buffer_size(sk, 0, 0);
|
||||||
|
if (err < 0)
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bind(sk->s_fd, (struct sockaddr*) &sk->s_local,
|
||||||
|
sizeof(sk->s_local));
|
||||||
|
if (err < 0) {
|
||||||
|
err = -nl_syserr2nlerr(errno);
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
addrlen = sizeof(sk->s_local);
|
||||||
|
err = getsockname(sk->s_fd, (struct sockaddr *) &sk->s_local,
|
||||||
|
&addrlen);
|
||||||
|
if (err < 0) {
|
||||||
|
err = -nl_syserr2nlerr(errno);
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addrlen != sizeof(sk->s_local)) {
|
||||||
|
err = -NLE_NOADDR;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sk->s_local.nl_family != AF_NETLINK) {
|
||||||
|
err = -NLE_AF_NOSUPPORT;
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
sk->s_proto = protocol;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
errout:
|
||||||
|
close(sk->s_fd);
|
||||||
|
sk->s_fd = -1;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close/Disconnect netlink socket.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
*/
|
||||||
|
void nl_close(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
if (sk->s_fd >= 0) {
|
||||||
|
close(sk->s_fd);
|
||||||
|
sk->s_fd = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sk->s_proto = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Send
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send raw data over netlink socket.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg buf Data buffer.
|
||||||
|
* @arg size Size of data buffer.
|
||||||
|
* @return Number of characters written on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_sendto(struct nl_sock *sk, void *buf, size_t size)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = sendto(sk->s_fd, buf, size, 0, (struct sockaddr *)
|
||||||
|
&sk->s_peer, sizeof(sk->s_peer));
|
||||||
|
if (ret < 0)
|
||||||
|
return -nl_syserr2nlerr(errno);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send netlink message with control over sendmsg() message header.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg msg Netlink message to be sent.
|
||||||
|
* @arg hdr Sendmsg() message header.
|
||||||
|
* @return Number of characters sent on sucess or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr)
|
||||||
|
{
|
||||||
|
struct nl_cb *cb;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
struct iovec iov = {
|
||||||
|
.iov_base = (void *) nlmsg_hdr(msg),
|
||||||
|
.iov_len = nlmsg_hdr(msg)->nlmsg_len,
|
||||||
|
};
|
||||||
|
|
||||||
|
hdr->msg_iov = &iov;
|
||||||
|
hdr->msg_iovlen = 1;
|
||||||
|
|
||||||
|
nlmsg_set_src(msg, &sk->s_local);
|
||||||
|
|
||||||
|
cb = sk->s_cb;
|
||||||
|
if (cb->cb_set[NL_CB_MSG_OUT])
|
||||||
|
if (nl_cb_call(cb, NL_CB_MSG_OUT, msg) != NL_OK)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = sendmsg(sk->s_fd, hdr, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
return -nl_syserr2nlerr(errno);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send netlink message.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg msg Netlink message to be sent.
|
||||||
|
* @see nl_sendmsg()
|
||||||
|
* @return Number of characters sent on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_send(struct nl_sock *sk, struct nl_msg *msg)
|
||||||
|
{
|
||||||
|
struct sockaddr_nl *dst;
|
||||||
|
struct ucred *creds;
|
||||||
|
|
||||||
|
struct msghdr hdr = {
|
||||||
|
.msg_name = (void *) &sk->s_peer,
|
||||||
|
.msg_namelen = sizeof(struct sockaddr_nl),
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Overwrite destination if specified in the message itself, defaults
|
||||||
|
* to the peer address of the socket.
|
||||||
|
*/
|
||||||
|
dst = nlmsg_get_dst(msg);
|
||||||
|
if (dst->nl_family == AF_NETLINK)
|
||||||
|
hdr.msg_name = dst;
|
||||||
|
|
||||||
|
/* Add credentials if present. */
|
||||||
|
creds = nlmsg_get_creds(msg);
|
||||||
|
if (creds != NULL) {
|
||||||
|
char buf[CMSG_SPACE(sizeof(struct ucred))];
|
||||||
|
struct cmsghdr *cmsg;
|
||||||
|
|
||||||
|
hdr.msg_control = buf;
|
||||||
|
hdr.msg_controllen = sizeof(buf);
|
||||||
|
|
||||||
|
cmsg = CMSG_FIRSTHDR(&hdr);
|
||||||
|
cmsg->cmsg_level = SOL_SOCKET;
|
||||||
|
cmsg->cmsg_type = SCM_CREDENTIALS;
|
||||||
|
cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
|
||||||
|
memcpy(CMSG_DATA(cmsg), creds, sizeof(struct ucred));
|
||||||
|
}
|
||||||
|
|
||||||
|
return nl_sendmsg(sk, msg, &hdr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send netlink message and check & extend header values as needed.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg msg Netlink message to be sent.
|
||||||
|
*
|
||||||
|
* Checks the netlink message \c nlh for completness and extends it
|
||||||
|
* as required before sending it out. Checked fields include pid,
|
||||||
|
* sequence nr, and flags.
|
||||||
|
*
|
||||||
|
* @see nl_send()
|
||||||
|
* @return Number of characters sent or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
struct nl_cb *cb = sk->s_cb;
|
||||||
|
|
||||||
|
nlh = nlmsg_hdr(msg);
|
||||||
|
if (nlh->nlmsg_pid == 0)
|
||||||
|
nlh->nlmsg_pid = sk->s_local.nl_pid;
|
||||||
|
|
||||||
|
if (nlh->nlmsg_seq == 0)
|
||||||
|
nlh->nlmsg_seq = sk->s_seq_next++;
|
||||||
|
|
||||||
|
if (msg->nm_protocol == -1)
|
||||||
|
msg->nm_protocol = sk->s_proto;
|
||||||
|
|
||||||
|
nlh->nlmsg_flags |= NLM_F_REQUEST;
|
||||||
|
|
||||||
|
if (!(sk->s_flags & NL_NO_AUTO_ACK))
|
||||||
|
nlh->nlmsg_flags |= NLM_F_ACK;
|
||||||
|
|
||||||
|
if (cb->cb_send_ow)
|
||||||
|
return cb->cb_send_ow(sk, msg);
|
||||||
|
else
|
||||||
|
return nl_send(sk, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send simple netlink message using nl_send_auto_complete()
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg type Netlink message type.
|
||||||
|
* @arg flags Netlink message flags.
|
||||||
|
* @arg buf Data buffer.
|
||||||
|
* @arg size Size of data buffer.
|
||||||
|
*
|
||||||
|
* Builds a netlink message with the specified type and flags and
|
||||||
|
* appends the specified data as payload to the message.
|
||||||
|
*
|
||||||
|
* @see nl_send_auto_complete()
|
||||||
|
* @return Number of characters sent on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf,
|
||||||
|
size_t size)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct nl_msg *msg;
|
||||||
|
|
||||||
|
msg = nlmsg_alloc_simple(type, flags);
|
||||||
|
if (!msg)
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
if (buf && size) {
|
||||||
|
err = nlmsg_append(msg, buf, size, NLMSG_ALIGNTO);
|
||||||
|
if (err < 0)
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err = nl_send_auto_complete(sk, msg);
|
||||||
|
errout:
|
||||||
|
nlmsg_free(msg);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Receive
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive data from netlink socket
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg nla Destination pointer for peer's netlink address.
|
||||||
|
* @arg buf Destination pointer for message content.
|
||||||
|
* @arg creds Destination pointer for credentials.
|
||||||
|
*
|
||||||
|
* Receives a netlink message, allocates a buffer in \c *buf and
|
||||||
|
* stores the message content. The peer's netlink address is stored
|
||||||
|
* in \c *nla. The caller is responsible for freeing the buffer allocated
|
||||||
|
* in \c *buf if a positive value is returned. Interruped system calls
|
||||||
|
* are handled by repeating the read. The input buffer size is determined
|
||||||
|
* by peeking before the actual read is done.
|
||||||
|
*
|
||||||
|
* A non-blocking sockets causes the function to return immediately with
|
||||||
|
* a return value of 0 if no data is available.
|
||||||
|
*
|
||||||
|
* @return Number of octets read, 0 on EOF or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla,
|
||||||
|
unsigned char **buf, struct ucred **creds)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
int flags = 0;
|
||||||
|
static int page_size = 0;
|
||||||
|
struct iovec iov;
|
||||||
|
struct msghdr msg = {
|
||||||
|
.msg_name = (void *) nla,
|
||||||
|
.msg_namelen = sizeof(struct sockaddr_nl),
|
||||||
|
.msg_iov = &iov,
|
||||||
|
.msg_iovlen = 1,
|
||||||
|
.msg_control = NULL,
|
||||||
|
.msg_controllen = 0,
|
||||||
|
.msg_flags = 0,
|
||||||
|
};
|
||||||
|
struct cmsghdr *cmsg;
|
||||||
|
|
||||||
|
if (sk->s_flags & NL_MSG_PEEK)
|
||||||
|
flags |= MSG_PEEK;
|
||||||
|
|
||||||
|
if (page_size == 0)
|
||||||
|
page_size = getpagesize();
|
||||||
|
|
||||||
|
iov.iov_len = page_size;
|
||||||
|
iov.iov_base = *buf = malloc(iov.iov_len);
|
||||||
|
|
||||||
|
if (sk->s_flags & NL_SOCK_PASSCRED) {
|
||||||
|
msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred));
|
||||||
|
msg.msg_control = calloc(1, msg.msg_controllen);
|
||||||
|
}
|
||||||
|
retry:
|
||||||
|
|
||||||
|
n = recvmsg(sk->s_fd, &msg, flags);
|
||||||
|
if (!n)
|
||||||
|
goto abort;
|
||||||
|
else if (n < 0) {
|
||||||
|
if (errno == EINTR) {
|
||||||
|
NL_DBG(3, "recvmsg() returned EINTR, retrying\n");
|
||||||
|
goto retry;
|
||||||
|
} else if (errno == EAGAIN) {
|
||||||
|
NL_DBG(3, "recvmsg() returned EAGAIN, aborting\n");
|
||||||
|
goto abort;
|
||||||
|
} else {
|
||||||
|
free(msg.msg_control);
|
||||||
|
free(*buf);
|
||||||
|
return -nl_syserr2nlerr(errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iov.iov_len < n ||
|
||||||
|
msg.msg_flags & MSG_TRUNC) {
|
||||||
|
/* Provided buffer is not long enough, enlarge it
|
||||||
|
* and try again. */
|
||||||
|
iov.iov_len *= 2;
|
||||||
|
iov.iov_base = *buf = realloc(*buf, iov.iov_len);
|
||||||
|
goto retry;
|
||||||
|
} else if (msg.msg_flags & MSG_CTRUNC) {
|
||||||
|
msg.msg_controllen *= 2;
|
||||||
|
msg.msg_control = realloc(msg.msg_control, msg.msg_controllen);
|
||||||
|
goto retry;
|
||||||
|
} else if (flags != 0) {
|
||||||
|
/* Buffer is big enough, do the actual reading */
|
||||||
|
flags = 0;
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.msg_namelen != sizeof(struct sockaddr_nl)) {
|
||||||
|
free(msg.msg_control);
|
||||||
|
free(*buf);
|
||||||
|
return -NLE_NOADDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||||
|
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||||
|
cmsg->cmsg_type == SCM_CREDENTIALS) {
|
||||||
|
*creds = calloc(1, sizeof(struct ucred));
|
||||||
|
memcpy(*creds, CMSG_DATA(cmsg), sizeof(struct ucred));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(msg.msg_control);
|
||||||
|
return n;
|
||||||
|
|
||||||
|
abort:
|
||||||
|
free(msg.msg_control);
|
||||||
|
free(*buf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NL_CB_CALL(cb, type, msg) \
|
||||||
|
do { \
|
||||||
|
err = nl_cb_call(cb, type, msg); \
|
||||||
|
switch (err) { \
|
||||||
|
case NL_OK: \
|
||||||
|
err = 0; \
|
||||||
|
break; \
|
||||||
|
case NL_SKIP: \
|
||||||
|
goto skip; \
|
||||||
|
case NL_STOP: \
|
||||||
|
goto stop; \
|
||||||
|
default: \
|
||||||
|
goto out; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static int recvmsgs(struct nl_sock *sk, struct nl_cb *cb)
|
||||||
|
{
|
||||||
|
int n, err = 0, multipart = 0;
|
||||||
|
unsigned char *buf = NULL;
|
||||||
|
struct nlmsghdr *hdr;
|
||||||
|
struct sockaddr_nl nla = {0};
|
||||||
|
struct nl_msg *msg = NULL;
|
||||||
|
struct ucred *creds = NULL;
|
||||||
|
|
||||||
|
continue_reading:
|
||||||
|
NL_DBG(3, "Attempting to read from %p\n", sk);
|
||||||
|
if (cb->cb_recv_ow)
|
||||||
|
n = cb->cb_recv_ow(sk, &nla, &buf, &creds);
|
||||||
|
else
|
||||||
|
n = nl_recv(sk, &nla, &buf, &creds);
|
||||||
|
|
||||||
|
if (n <= 0)
|
||||||
|
return n;
|
||||||
|
|
||||||
|
NL_DBG(3, "recvmsgs(%p): Read %d bytes\n", sk, n);
|
||||||
|
|
||||||
|
hdr = (struct nlmsghdr *) buf;
|
||||||
|
while (nlmsg_ok(hdr, n)) {
|
||||||
|
NL_DBG(3, "recgmsgs(%p): Processing valid message...\n", sk);
|
||||||
|
|
||||||
|
nlmsg_free(msg);
|
||||||
|
msg = nlmsg_convert(hdr);
|
||||||
|
if (!msg) {
|
||||||
|
err = -NLE_NOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlmsg_set_proto(msg, sk->s_proto);
|
||||||
|
nlmsg_set_src(msg, &nla);
|
||||||
|
if (creds)
|
||||||
|
nlmsg_set_creds(msg, creds);
|
||||||
|
|
||||||
|
/* Raw callback is the first, it gives the most control
|
||||||
|
* to the user and he can do his very own parsing. */
|
||||||
|
if (cb->cb_set[NL_CB_MSG_IN])
|
||||||
|
NL_CB_CALL(cb, NL_CB_MSG_IN, msg);
|
||||||
|
|
||||||
|
/* Sequence number checking. The check may be done by
|
||||||
|
* the user, otherwise a very simple check is applied
|
||||||
|
* enforcing strict ordering */
|
||||||
|
if (cb->cb_set[NL_CB_SEQ_CHECK])
|
||||||
|
NL_CB_CALL(cb, NL_CB_SEQ_CHECK, msg);
|
||||||
|
else if (hdr->nlmsg_seq != sk->s_seq_expect) {
|
||||||
|
if (cb->cb_set[NL_CB_INVALID])
|
||||||
|
NL_CB_CALL(cb, NL_CB_INVALID, msg);
|
||||||
|
else {
|
||||||
|
err = -NLE_SEQ_MISMATCH;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdr->nlmsg_type == NLMSG_DONE ||
|
||||||
|
hdr->nlmsg_type == NLMSG_ERROR ||
|
||||||
|
hdr->nlmsg_type == NLMSG_NOOP ||
|
||||||
|
hdr->nlmsg_type == NLMSG_OVERRUN) {
|
||||||
|
/* We can't check for !NLM_F_MULTI since some netlink
|
||||||
|
* users in the kernel are broken. */
|
||||||
|
sk->s_seq_expect++;
|
||||||
|
NL_DBG(3, "recvmsgs(%p): Increased expected " \
|
||||||
|
"sequence number to %d\n",
|
||||||
|
sk, sk->s_seq_expect);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hdr->nlmsg_flags & NLM_F_MULTI)
|
||||||
|
multipart = 1;
|
||||||
|
|
||||||
|
/* Other side wishes to see an ack for this message */
|
||||||
|
if (hdr->nlmsg_flags & NLM_F_ACK) {
|
||||||
|
if (cb->cb_set[NL_CB_SEND_ACK])
|
||||||
|
NL_CB_CALL(cb, NL_CB_SEND_ACK, msg);
|
||||||
|
else {
|
||||||
|
/* FIXME: implement */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* messages terminates a multpart message, this is
|
||||||
|
* usually the end of a message and therefore we slip
|
||||||
|
* out of the loop by default. the user may overrule
|
||||||
|
* this action by skipping this packet. */
|
||||||
|
if (hdr->nlmsg_type == NLMSG_DONE) {
|
||||||
|
multipart = 0;
|
||||||
|
if (cb->cb_set[NL_CB_FINISH])
|
||||||
|
NL_CB_CALL(cb, NL_CB_FINISH, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Message to be ignored, the default action is to
|
||||||
|
* skip this message if no callback is specified. The
|
||||||
|
* user may overrule this action by returning
|
||||||
|
* NL_PROCEED. */
|
||||||
|
else if (hdr->nlmsg_type == NLMSG_NOOP) {
|
||||||
|
if (cb->cb_set[NL_CB_SKIPPED])
|
||||||
|
NL_CB_CALL(cb, NL_CB_SKIPPED, msg);
|
||||||
|
else
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Data got lost, report back to user. The default action is to
|
||||||
|
* quit parsing. The user may overrule this action by retuning
|
||||||
|
* NL_SKIP or NL_PROCEED (dangerous) */
|
||||||
|
else if (hdr->nlmsg_type == NLMSG_OVERRUN) {
|
||||||
|
if (cb->cb_set[NL_CB_OVERRUN])
|
||||||
|
NL_CB_CALL(cb, NL_CB_OVERRUN, msg);
|
||||||
|
else {
|
||||||
|
err = -NLE_MSG_OVERFLOW;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Message carries a nlmsgerr */
|
||||||
|
else if (hdr->nlmsg_type == NLMSG_ERROR) {
|
||||||
|
struct nlmsgerr *e = nlmsg_data(hdr);
|
||||||
|
|
||||||
|
if (hdr->nlmsg_len < nlmsg_msg_size(sizeof(*e))) {
|
||||||
|
/* Truncated error message, the default action
|
||||||
|
* is to stop parsing. The user may overrule
|
||||||
|
* this action by returning NL_SKIP or
|
||||||
|
* NL_PROCEED (dangerous) */
|
||||||
|
if (cb->cb_set[NL_CB_INVALID])
|
||||||
|
NL_CB_CALL(cb, NL_CB_INVALID, msg);
|
||||||
|
else {
|
||||||
|
err = -NLE_MSG_TRUNC;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else if (e->error) {
|
||||||
|
/* Error message reported back from kernel. */
|
||||||
|
if (cb->cb_err) {
|
||||||
|
err = cb->cb_err(&nla, e,
|
||||||
|
cb->cb_err_arg);
|
||||||
|
if (err < 0)
|
||||||
|
goto out;
|
||||||
|
else if (err == NL_SKIP)
|
||||||
|
goto skip;
|
||||||
|
else if (err == NL_STOP) {
|
||||||
|
err = -nl_syserr2nlerr(e->error);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = -nl_syserr2nlerr(e->error);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else if (cb->cb_set[NL_CB_ACK])
|
||||||
|
NL_CB_CALL(cb, NL_CB_ACK, msg);
|
||||||
|
} else {
|
||||||
|
/* Valid message (not checking for MULTIPART bit to
|
||||||
|
* get along with broken kernels. NL_SKIP has no
|
||||||
|
* effect on this. */
|
||||||
|
if (cb->cb_set[NL_CB_VALID])
|
||||||
|
NL_CB_CALL(cb, NL_CB_VALID, msg);
|
||||||
|
}
|
||||||
|
skip:
|
||||||
|
err = 0;
|
||||||
|
hdr = nlmsg_next(hdr, &n);
|
||||||
|
}
|
||||||
|
|
||||||
|
nlmsg_free(msg);
|
||||||
|
free(buf);
|
||||||
|
free(creds);
|
||||||
|
buf = NULL;
|
||||||
|
msg = NULL;
|
||||||
|
creds = NULL;
|
||||||
|
|
||||||
|
if (multipart) {
|
||||||
|
/* Multipart message not yet complete, continue reading */
|
||||||
|
goto continue_reading;
|
||||||
|
}
|
||||||
|
stop:
|
||||||
|
err = 0;
|
||||||
|
out:
|
||||||
|
nlmsg_free(msg);
|
||||||
|
free(buf);
|
||||||
|
free(creds);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a set of messages from a netlink socket.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg cb set of callbacks to control behaviour.
|
||||||
|
*
|
||||||
|
* Repeatedly calls nl_recv() or the respective replacement if provided
|
||||||
|
* by the application (see nl_cb_overwrite_recv()) and parses the
|
||||||
|
* received data as netlink messages. Stops reading if one of the
|
||||||
|
* callbacks returns NL_STOP or nl_recv returns either 0 or a negative error code.
|
||||||
|
*
|
||||||
|
* A non-blocking sockets causes the function to return immediately if
|
||||||
|
* no data is available.
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code from nl_recv().
|
||||||
|
*/
|
||||||
|
int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb)
|
||||||
|
{
|
||||||
|
if (cb->cb_recvmsgs_ow)
|
||||||
|
return cb->cb_recvmsgs_ow(sk, cb);
|
||||||
|
else
|
||||||
|
return recvmsgs(sk, cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int ack_wait_handler(struct nl_msg *msg, void *arg)
|
||||||
|
{
|
||||||
|
return NL_STOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for ACK.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @pre The netlink socket must be in blocking state.
|
||||||
|
*
|
||||||
|
* Waits until an ACK is received for the latest not yet acknowledged
|
||||||
|
* netlink message.
|
||||||
|
*/
|
||||||
|
int nl_wait_for_ack(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct nl_cb *cb;
|
||||||
|
|
||||||
|
cb = nl_cb_clone(sk->s_cb);
|
||||||
|
if (cb == NULL)
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, NULL);
|
||||||
|
err = nl_recvmsgs(sk, cb);
|
||||||
|
nl_cb_put(cb);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** @} */
|
|
@ -0,0 +1,309 @@
|
||||||
|
/*
|
||||||
|
* lib/object.c Generic Cacheable Object
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup cache
|
||||||
|
* @defgroup object Object
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-local.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/cache.h>
|
||||||
|
#include <netlink/object.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
|
||||||
|
static inline struct nl_object_ops *obj_ops(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
if (!obj->ce_ops)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
return obj->ce_ops;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Object Creation/Deletion
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new object of kind specified by the operations handle
|
||||||
|
* @arg ops cache operations handle
|
||||||
|
* @return The new object or NULL
|
||||||
|
*/
|
||||||
|
struct nl_object *nl_object_alloc(struct nl_object_ops *ops)
|
||||||
|
{
|
||||||
|
struct nl_object *new;
|
||||||
|
|
||||||
|
if (ops->oo_size < sizeof(*new))
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
new = calloc(1, ops->oo_size);
|
||||||
|
if (!new)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
new->ce_refcnt = 1;
|
||||||
|
nl_init_list_head(&new->ce_list);
|
||||||
|
|
||||||
|
new->ce_ops = ops;
|
||||||
|
if (ops->oo_constructor)
|
||||||
|
ops->oo_constructor(new);
|
||||||
|
|
||||||
|
NL_DBG(4, "Allocated new object %p\n", new);
|
||||||
|
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef disabled
|
||||||
|
/**
|
||||||
|
* Allocate a new object of kind specified by the name
|
||||||
|
* @arg kind name of object type
|
||||||
|
* @return The new object or nULL
|
||||||
|
*/
|
||||||
|
int nl_object_alloc_name(const char *kind, struct nl_object **result)
|
||||||
|
{
|
||||||
|
struct nl_cache_ops *ops;
|
||||||
|
|
||||||
|
ops = nl_cache_ops_lookup(kind);
|
||||||
|
if (!ops)
|
||||||
|
return -NLE_OPNOTSUPP;
|
||||||
|
|
||||||
|
if (!(*result = nl_object_alloc(ops->co_obj_ops)))
|
||||||
|
return -NLE_NOMEM;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct nl_derived_object {
|
||||||
|
NLHDR_COMMON
|
||||||
|
char data;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new object and copy all data from an existing object
|
||||||
|
* @arg obj object to inherite data from
|
||||||
|
* @return The new object or NULL.
|
||||||
|
*/
|
||||||
|
struct nl_object *nl_object_clone(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
struct nl_object *new;
|
||||||
|
struct nl_object_ops *ops = obj_ops(obj);
|
||||||
|
int doff = offsetof(struct nl_derived_object, data);
|
||||||
|
int size;
|
||||||
|
|
||||||
|
new = nl_object_alloc(ops);
|
||||||
|
if (!new)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size = ops->oo_size - doff;
|
||||||
|
if (size < 0)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
new->ce_ops = obj->ce_ops;
|
||||||
|
new->ce_msgtype = obj->ce_msgtype;
|
||||||
|
|
||||||
|
if (size)
|
||||||
|
memcpy((void *)new + doff, (void *)obj + doff, size);
|
||||||
|
|
||||||
|
if (ops->oo_clone) {
|
||||||
|
if (ops->oo_clone(new, obj) < 0) {
|
||||||
|
nl_object_free(new);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
} else if (size && ops->oo_free_data)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a cacheable object
|
||||||
|
* @arg obj object to free
|
||||||
|
*
|
||||||
|
* @return 0 or a negative error code.
|
||||||
|
*/
|
||||||
|
void nl_object_free(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
struct nl_object_ops *ops = obj_ops(obj);
|
||||||
|
|
||||||
|
if (obj->ce_refcnt > 0)
|
||||||
|
NL_DBG(1, "Warning: Freeing object in use...\n");
|
||||||
|
|
||||||
|
if (obj->ce_cache)
|
||||||
|
nl_cache_remove(obj);
|
||||||
|
|
||||||
|
if (ops->oo_free_data)
|
||||||
|
ops->oo_free_data(obj);
|
||||||
|
|
||||||
|
free(obj);
|
||||||
|
|
||||||
|
NL_DBG(4, "Freed object %p\n", obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Reference Management
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acquire a reference on a object
|
||||||
|
* @arg obj object to acquire reference from
|
||||||
|
*/
|
||||||
|
void nl_object_get(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
obj->ce_refcnt++;
|
||||||
|
NL_DBG(4, "New reference to object %p, total %d\n",
|
||||||
|
obj, obj->ce_refcnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release a reference from an object
|
||||||
|
* @arg obj object to release reference from
|
||||||
|
*/
|
||||||
|
void nl_object_put(struct nl_object *obj)
|
||||||
|
{
|
||||||
|
if (!obj)
|
||||||
|
return;
|
||||||
|
|
||||||
|
obj->ce_refcnt--;
|
||||||
|
NL_DBG(4, "Returned object reference %p, %d remaining\n",
|
||||||
|
obj, obj->ce_refcnt);
|
||||||
|
|
||||||
|
if (obj->ce_refcnt < 0)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
if (obj->ce_refcnt <= 0)
|
||||||
|
nl_object_free(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Utillities
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef disabled
|
||||||
|
/**
|
||||||
|
* Dump this object according to the specified parameters
|
||||||
|
* @arg obj object to dump
|
||||||
|
* @arg params dumping parameters
|
||||||
|
*/
|
||||||
|
void nl_object_dump(struct nl_object *obj, struct nl_dump_params *params)
|
||||||
|
{
|
||||||
|
dump_from_ops(obj, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the identifiers of two objects are identical
|
||||||
|
* @arg a an object
|
||||||
|
* @arg b another object of same type
|
||||||
|
*
|
||||||
|
* @return true if both objects have equal identifiers, otherwise false.
|
||||||
|
*/
|
||||||
|
int nl_object_identical(struct nl_object *a, struct nl_object *b)
|
||||||
|
{
|
||||||
|
struct nl_object_ops *ops = obj_ops(a);
|
||||||
|
int req_attrs;
|
||||||
|
|
||||||
|
/* Both objects must be of same type */
|
||||||
|
if (ops != obj_ops(b))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
req_attrs = ops->oo_id_attrs;
|
||||||
|
|
||||||
|
/* Both objects must provide all required attributes to uniquely
|
||||||
|
* identify an object */
|
||||||
|
if ((a->ce_mask & req_attrs) != req_attrs ||
|
||||||
|
(b->ce_mask & req_attrs) != req_attrs)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Can't judge unless we can compare */
|
||||||
|
if (ops->oo_compare == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return !(ops->oo_compare(a, b, req_attrs, 0));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute bitmask representing difference in attribute values
|
||||||
|
* @arg a an object
|
||||||
|
* @arg b another object of same type
|
||||||
|
*
|
||||||
|
* The bitmask returned is specific to an object type, each bit set represents
|
||||||
|
* an attribute which mismatches in either of the two objects. Unavailability
|
||||||
|
* of an attribute in one object and presence in the other is regarded a
|
||||||
|
* mismatch as well.
|
||||||
|
*
|
||||||
|
* @return Bitmask describing differences or 0 if they are completely identical.
|
||||||
|
*/
|
||||||
|
uint32_t nl_object_diff(struct nl_object *a, struct nl_object *b)
|
||||||
|
{
|
||||||
|
struct nl_object_ops *ops = obj_ops(a);
|
||||||
|
|
||||||
|
if (ops != obj_ops(b) || ops->oo_compare == NULL)
|
||||||
|
return UINT_MAX;
|
||||||
|
|
||||||
|
return ops->oo_compare(a, b, ~0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Match a filter against an object
|
||||||
|
* @arg obj object to check
|
||||||
|
* @arg filter object of same type acting as filter
|
||||||
|
*
|
||||||
|
* @return 1 if the object matches the filter or 0
|
||||||
|
* if no filter procedure is available or if the
|
||||||
|
* filter does not match.
|
||||||
|
*/
|
||||||
|
int nl_object_match_filter(struct nl_object *obj, struct nl_object *filter)
|
||||||
|
{
|
||||||
|
struct nl_object_ops *ops = obj_ops(obj);
|
||||||
|
|
||||||
|
if (ops != obj_ops(filter) || ops->oo_compare == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return !(ops->oo_compare(obj, filter, filter->ce_mask,
|
||||||
|
LOOSE_COMPARISON));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert bitmask of attributes to a character string
|
||||||
|
* @arg obj object of same type as attribute bitmask
|
||||||
|
* @arg attrs bitmask of attribute types
|
||||||
|
* @arg buf destination buffer
|
||||||
|
* @arg len length of destination buffer
|
||||||
|
*
|
||||||
|
* Converts the bitmask of attribute types into a list of attribute
|
||||||
|
* names separated by comas.
|
||||||
|
*
|
||||||
|
* @return destination buffer.
|
||||||
|
*/
|
||||||
|
char *nl_object_attrs2str(struct nl_object *obj, uint32_t attrs,
|
||||||
|
char *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct nl_object_ops *ops = obj_ops(obj);
|
||||||
|
|
||||||
|
if (ops->oo_attrs2str != NULL)
|
||||||
|
return ops->oo_attrs2str(attrs, buf, len);
|
||||||
|
else {
|
||||||
|
memset(buf, 0, len);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** @} */
|
|
@ -0,0 +1,406 @@
|
||||||
|
/*
|
||||||
|
* lib/socket.c Netlink Socket
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation version 2.1
|
||||||
|
* of the License.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2003-2008 Thomas Graf <tgraf@suug.ch>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup core
|
||||||
|
* @defgroup socket Socket
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <netlink-local.h>
|
||||||
|
#include <netlink/netlink.h>
|
||||||
|
#include <netlink/utils.h>
|
||||||
|
#include <netlink/handlers.h>
|
||||||
|
#include <netlink/msg.h>
|
||||||
|
#include <netlink/attr.h>
|
||||||
|
|
||||||
|
static uint32_t used_ports_map[32];
|
||||||
|
|
||||||
|
static uint32_t generate_local_port(void)
|
||||||
|
{
|
||||||
|
int i, n;
|
||||||
|
uint32_t pid = getpid() & 0x3FFFFF;
|
||||||
|
|
||||||
|
for (i = 0; i < 32; i++) {
|
||||||
|
if (used_ports_map[i] == 0xFFFFFFFF)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (n = 0; n < 32; n++) {
|
||||||
|
if (1UL & (used_ports_map[i] >> n))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
used_ports_map[i] |= (1UL << n);
|
||||||
|
n += (i * 32);
|
||||||
|
|
||||||
|
/* PID_MAX_LIMIT is currently at 2^22, leaving 10 bit
|
||||||
|
* to, i.e. 1024 unique ports per application. */
|
||||||
|
return pid + (n << 22);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Out of sockets in our own PID namespace, what to do? FIXME */
|
||||||
|
return UINT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release_local_port(uint32_t port)
|
||||||
|
{
|
||||||
|
int nr;
|
||||||
|
|
||||||
|
if (port == UINT_MAX)
|
||||||
|
return;
|
||||||
|
|
||||||
|
nr = port >> 22;
|
||||||
|
used_ports_map[nr / 32] &= ~((nr % 32) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Allocation
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct nl_sock *__alloc_socket(struct nl_cb *cb)
|
||||||
|
{
|
||||||
|
struct nl_sock *sk;
|
||||||
|
|
||||||
|
sk = calloc(1, sizeof(*sk));
|
||||||
|
if (!sk)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sk->s_fd = -1;
|
||||||
|
sk->s_cb = cb;
|
||||||
|
sk->s_local.nl_family = AF_NETLINK;
|
||||||
|
sk->s_peer.nl_family = AF_NETLINK;
|
||||||
|
sk->s_seq_expect = sk->s_seq_next = time(0);
|
||||||
|
sk->s_local.nl_pid = generate_local_port();
|
||||||
|
if (sk->s_local.nl_pid == UINT_MAX) {
|
||||||
|
nl_socket_free(sk);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate new netlink socket
|
||||||
|
*
|
||||||
|
* @return Newly allocated netlink socket or NULL.
|
||||||
|
*/
|
||||||
|
struct nl_sock *nl_socket_alloc(void)
|
||||||
|
{
|
||||||
|
struct nl_cb *cb;
|
||||||
|
|
||||||
|
cb = nl_cb_alloc(NL_CB_DEFAULT);
|
||||||
|
if (!cb)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return __alloc_socket(cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate new socket with custom callbacks
|
||||||
|
* @arg cb Callback handler
|
||||||
|
*
|
||||||
|
* The reference to the callback handler is taken into account
|
||||||
|
* automatically, it is released again upon calling nl_socket_free().
|
||||||
|
*
|
||||||
|
*@return Newly allocted socket handle or NULL.
|
||||||
|
*/
|
||||||
|
struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb)
|
||||||
|
{
|
||||||
|
if (cb == NULL)
|
||||||
|
BUG();
|
||||||
|
|
||||||
|
return __alloc_socket(nl_cb_get(cb));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free a netlink socket.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
*/
|
||||||
|
void nl_socket_free(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
if (!sk)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (sk->s_fd >= 0)
|
||||||
|
close(sk->s_fd);
|
||||||
|
|
||||||
|
if (!(sk->s_flags & NL_OWN_PORT))
|
||||||
|
release_local_port(sk->s_local.nl_pid);
|
||||||
|
|
||||||
|
nl_cb_put(sk->s_cb);
|
||||||
|
free(sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Sequence Numbers
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int noop_seq_check(struct nl_msg *msg, void *arg)
|
||||||
|
{
|
||||||
|
return NL_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disable sequence number checking.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
*
|
||||||
|
* Disables checking of sequence numbers on the netlink socket This is
|
||||||
|
* required to allow messages to be processed which were not requested by
|
||||||
|
* a preceding request message, e.g. netlink events.
|
||||||
|
*
|
||||||
|
* @note This function modifies the NL_CB_SEQ_CHECK configuration in
|
||||||
|
* the callback handle associated with the socket.
|
||||||
|
*/
|
||||||
|
void nl_socket_disable_seq_check(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
nl_cb_set(sk->s_cb, NL_CB_SEQ_CHECK,
|
||||||
|
NL_CB_CUSTOM, noop_seq_check, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set local port of socket
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg port Local port identifier
|
||||||
|
*
|
||||||
|
* Assigns a local port identifier to the socket. If port is 0
|
||||||
|
* a unique port identifier will be generated automatically.
|
||||||
|
*/
|
||||||
|
void nl_socket_set_local_port(struct nl_sock *sk, uint32_t port)
|
||||||
|
{
|
||||||
|
if (port == 0) {
|
||||||
|
port = generate_local_port();
|
||||||
|
sk->s_flags &= ~NL_OWN_PORT;
|
||||||
|
} else {
|
||||||
|
if (!(sk->s_flags & NL_OWN_PORT))
|
||||||
|
release_local_port(sk->s_local.nl_pid);
|
||||||
|
sk->s_flags |= NL_OWN_PORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
sk->s_local.nl_pid = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Group Subscriptions
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join groups
|
||||||
|
* @arg sk Netlink socket
|
||||||
|
* @arg group Group identifier
|
||||||
|
*
|
||||||
|
* Joins the specified groups using the modern socket option which
|
||||||
|
* is available since kernel version 2.6.14. It allows joining an
|
||||||
|
* almost arbitary number of groups without limitation. The list
|
||||||
|
* of groups has to be terminated by 0 (%NFNLGRP_NONE).
|
||||||
|
*
|
||||||
|
* Make sure to use the correct group definitions as the older
|
||||||
|
* bitmask definitions for nl_join_groups() are likely to still
|
||||||
|
* be present for backward compatibility reasons.
|
||||||
|
*
|
||||||
|
* @return 0 on sucess or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_socket_add_memberships(struct nl_sock *sk, int group, ...)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (sk->s_fd == -1)
|
||||||
|
return -NLE_BAD_SOCK;
|
||||||
|
|
||||||
|
va_start(ap, group);
|
||||||
|
|
||||||
|
while (group != 0) {
|
||||||
|
if (group < 0)
|
||||||
|
return -NLE_INVAL;
|
||||||
|
|
||||||
|
err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
|
||||||
|
&group, sizeof(group));
|
||||||
|
if (err < 0)
|
||||||
|
return -nl_syserr2nlerr(errno);
|
||||||
|
|
||||||
|
group = va_arg(ap, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leave groups
|
||||||
|
* @arg sk Netlink socket
|
||||||
|
* @arg group Group identifier
|
||||||
|
*
|
||||||
|
* Leaves the specified groups using the modern socket option
|
||||||
|
* which is available since kernel version 2.6.14. The list of groups
|
||||||
|
* has to terminated by 0 (%NFNLGRP_NONE).
|
||||||
|
*
|
||||||
|
* @see nl_socket_add_membership
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_socket_drop_memberships(struct nl_sock *sk, int group, ...)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
if (sk->s_fd == -1)
|
||||||
|
return -NLE_BAD_SOCK;
|
||||||
|
|
||||||
|
va_start(ap, group);
|
||||||
|
|
||||||
|
while (group != 0) {
|
||||||
|
if (group < 0)
|
||||||
|
return -NLE_INVAL;
|
||||||
|
|
||||||
|
err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
|
||||||
|
&group, sizeof(group));
|
||||||
|
if (err < 0)
|
||||||
|
return -nl_syserr2nlerr(errno);
|
||||||
|
|
||||||
|
group = va_arg(ap, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
va_end(ap);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set file descriptor of socket to non-blocking state
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_socket_set_nonblocking(struct nl_sock *sk)
|
||||||
|
{
|
||||||
|
if (sk->s_fd == -1)
|
||||||
|
return -NLE_BAD_SOCK;
|
||||||
|
|
||||||
|
if (fcntl(sk->s_fd, F_SETFL, O_NONBLOCK) < 0)
|
||||||
|
return -nl_syserr2nlerr(errno);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name Utilities
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set socket buffer size of netlink socket.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg rxbuf New receive socket buffer size in bytes.
|
||||||
|
* @arg txbuf New transmit socket buffer size in bytes.
|
||||||
|
*
|
||||||
|
* Sets the socket buffer size of a netlink socket to the specified
|
||||||
|
* values \c rxbuf and \c txbuf. Providing a value of \c 0 assumes a
|
||||||
|
* good default value.
|
||||||
|
*
|
||||||
|
* @note It is not required to call this function prior to nl_connect().
|
||||||
|
* @return 0 on sucess or a negative error code.
|
||||||
|
*/
|
||||||
|
int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (rxbuf <= 0)
|
||||||
|
rxbuf = 32768;
|
||||||
|
|
||||||
|
if (txbuf <= 0)
|
||||||
|
txbuf = 32768;
|
||||||
|
|
||||||
|
if (sk->s_fd == -1)
|
||||||
|
return -NLE_BAD_SOCK;
|
||||||
|
|
||||||
|
err = setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF,
|
||||||
|
&txbuf, sizeof(txbuf));
|
||||||
|
if (err < 0)
|
||||||
|
return -nl_syserr2nlerr(errno);
|
||||||
|
|
||||||
|
err = setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF,
|
||||||
|
&rxbuf, sizeof(rxbuf));
|
||||||
|
if (err < 0)
|
||||||
|
return -nl_syserr2nlerr(errno);
|
||||||
|
|
||||||
|
sk->s_flags |= NL_SOCK_BUFSIZE_SET;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable/disable credential passing on netlink socket.
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg state New state (0 - disabled, 1 - enabled)
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code
|
||||||
|
*/
|
||||||
|
int nl_socket_set_passcred(struct nl_sock *sk, int state)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (sk->s_fd == -1)
|
||||||
|
return -NLE_BAD_SOCK;
|
||||||
|
|
||||||
|
err = setsockopt(sk->s_fd, SOL_SOCKET, SO_PASSCRED,
|
||||||
|
&state, sizeof(state));
|
||||||
|
if (err < 0)
|
||||||
|
return -nl_syserr2nlerr(errno);
|
||||||
|
|
||||||
|
if (state)
|
||||||
|
sk->s_flags |= NL_SOCK_PASSCRED;
|
||||||
|
else
|
||||||
|
sk->s_flags &= ~NL_SOCK_PASSCRED;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable/disable receival of additional packet information
|
||||||
|
* @arg sk Netlink socket.
|
||||||
|
* @arg state New state (0 - disabled, 1 - enabled)
|
||||||
|
*
|
||||||
|
* @return 0 on success or a negative error code
|
||||||
|
*/
|
||||||
|
int nl_socket_recv_pktinfo(struct nl_sock *sk, int state)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (sk->s_fd == -1)
|
||||||
|
return -NLE_BAD_SOCK;
|
||||||
|
|
||||||
|
err = setsockopt(sk->s_fd, SOL_NETLINK, NETLINK_PKTINFO,
|
||||||
|
&state, sizeof(state));
|
||||||
|
if (err < 0)
|
||||||
|
return -nl_syserr2nlerr(errno);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
/** @} */
|
Loading…
Reference in New Issue