/branches/gl-inet/package/libs/libnl-tiny/src/attr.c |
@@ -0,0 +1,668 @@ |
/* |
* lib/attr.c Netlink Attributes |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation version 2.1 |
* of the License. |
* |
* Copyright (c) 2003-2008 Thomas Graf <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; |
} |
|
|
|
/** @} */ |
/branches/gl-inet/package/libs/libnl-tiny/src/cache.c |
@@ -0,0 +1,376 @@ |
/* |
* 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 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; |
} |
|
/** |
* 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); |
} |
|
/** |
* 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)); |
} |
|
/** @} */ |
|
/** |
* @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); |
} |
|
|
/** @} */ |
|
/** |
* @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); |
} |
|
/** @} */ |
/branches/gl-inet/package/libs/libnl-tiny/src/error.c |
@@ -0,0 +1,116 @@ |
/* |
* 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", |
[NLE_PKTLOC_FILE] = "Unable to open packet location file", |
[NLE_PARSE_ERR] = "Unable to parse object", |
[NLE_NODEV] = "No such device", |
[NLE_IMMUTABLE] = "Immutable attribute", |
[NLE_DUMP_INTR] = "Dump inconsistency detected, interrupted", |
}; |
|
/** |
* 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 ESRCH: /* fall through */ |
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; |
case ERANGE: return NLE_RANGE; |
case ENODEV: return NLE_NODEV; |
default: return NLE_FAILURE; |
} |
} |
|
/** @} */ |
|
/branches/gl-inet/package/libs/libnl-tiny/src/genl.c |
@@ -0,0 +1,268 @@ |
/* |
* lib/genl/genl.c Generic Netlink |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation version 2.1 |
* of the License. |
* |
* Copyright (c) 2003-2008 Thomas Graf <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; |
} |
|
/** @} */ |
|
/** @} */ |
/branches/gl-inet/package/libs/libnl-tiny/src/genl_ctrl.c |
@@ -0,0 +1,380 @@ |
/* |
* 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 }, |
[CTRL_ATTR_MCAST_GROUPS] = { .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 struct nla_policy family_grp_policy[CTRL_ATTR_MCAST_GRP_MAX+1] = { |
[CTRL_ATTR_MCAST_GRP_NAME] = { .type = NLA_STRING }, |
[CTRL_ATTR_MCAST_GRP_ID] = { .type = NLA_U32 }, |
}; |
|
static int 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; |
|
} |
} |
|
if (info->attrs[CTRL_ATTR_MCAST_GROUPS]) { |
struct nlattr *nla, *nla_grps; |
int remaining; |
|
nla_grps = info->attrs[CTRL_ATTR_MCAST_GROUPS]; |
nla_for_each_nested(nla, nla_grps, remaining) { |
struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX+1]; |
int id; |
const char * name; |
|
err = nla_parse_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, nla, |
family_grp_policy); |
if (err < 0) |
goto errout; |
|
if (tb[CTRL_ATTR_MCAST_GRP_ID] == NULL) { |
err = -NLE_MISSING_ATTR; |
goto errout; |
} |
id = nla_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]); |
|
if (tb[CTRL_ATTR_MCAST_GRP_NAME] == NULL) { |
err = -NLE_MISSING_ATTR; |
goto errout; |
} |
name = nla_get_string(tb[CTRL_ATTR_MCAST_GRP_NAME]); |
|
err = genl_family_add_grp(family, id, name); |
if (err < 0) |
goto errout; |
} |
|
} |
|
err = 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 int genl_ctrl_grp_by_name(const struct genl_family *family, |
const char *grp_name) |
{ |
struct genl_family_grp *grp; |
|
nl_list_for_each_entry(grp, &family->gf_mc_grps, list) { |
if (!strcmp(grp->name, grp_name)) { |
return grp->id; |
} |
} |
|
return -NLE_OBJ_NOTFOUND; |
} |
|
int genl_ctrl_resolve_grp(struct nl_sock *sk, const char *family_name, |
const char *grp_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, family_name); |
if (family == NULL) { |
err = -NLE_OBJ_NOTFOUND; |
goto errout; |
} |
|
err = genl_ctrl_grp_by_name(family, grp_name); |
genl_family_put(family); |
errout: |
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); |
} |
|
/** @} */ |
/branches/gl-inet/package/libs/libnl-tiny/src/genl_mngt.c |
@@ -0,0 +1,193 @@ |
/* |
* 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; |
|
} |
|
/** |
* @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); |
} |
|
/** @} */ |
|
/** @} */ |
/branches/gl-inet/package/libs/libnl-tiny/src/include/netlink/attr.h |
@@ -0,0 +1,726 @@ |
/* |
* netlink/attr.h Netlink Attributes |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation version 2.1 |
* of the License. |
* |
* Copyright (c) 2003-2008 Thomas Graf <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, (struct nlattr *)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 = (size_t)nla_len(nla); |
char *src = (char*)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 = (struct nlattr *)nla_data(nla), rem = nla_len(nla); \ |
nla_ok(pos, rem); \ |
pos = nla_next(pos, &(rem))) |
|
/** @} */ |
|
#ifdef __cplusplus |
} |
#endif |
|
#endif |
/branches/gl-inet/package/libs/libnl-tiny/src/include/netlink/cache-api.h |
@@ -0,0 +1,199 @@ |
/* |
* netlink/cache-api.h Caching API |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation version 2.1 |
* of the License. |
* |
* Copyright (c) 2003-2006 Thomas Graf <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 |
/branches/gl-inet/package/libs/libnl-tiny/src/include/netlink/handlers.h |
@@ -0,0 +1,231 @@ |
/* |
* 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/socket.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,(enum nl_cb_type)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 |
/branches/gl-inet/package/libs/libnl-tiny/src/include/netlink/msg.h |
@@ -0,0 +1,308 @@ |
/* |
* netlink/msg.c Netlink Messages Interface |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation version 2.1 |
* of the License. |
* |
* Copyright (c) 2003-2006 Thomas Graf <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 = (unsigned char*)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 < (uint)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 = (struct nlmsghdr*)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 |
/branches/gl-inet/package/libs/libnl-tiny/src/include/netlink/object-api.h |
@@ -0,0 +1,331 @@ |
/* |
* netlink/object-api.c Object API |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation version 2.1 |
* of the License. |
* |
* Copyright (c) 2003-2007 Thomas Graf <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 |
/branches/gl-inet/package/libs/libnl-tiny/src/include/netlink/socket.h |
@@ -0,0 +1,231 @@ |
/* |
* 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; |
} |
|
static inline uint32_t nl_socket_get_peer_groups(struct nl_sock *sk) |
{ |
return sk->s_peer.nl_groups; |
} |
|
static inline void nl_socket_set_peer_groups(struct nl_sock *sk, uint32_t groups) |
{ |
sk->s_peer.nl_groups = groups; |
} |
|
/** |
* @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 |
/branches/gl-inet/package/libs/libnl-tiny/src/msg.c |
@@ -0,0 +1,561 @@ |
/* |
* 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 = 4096; |
|
/** |
* @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); |
} |
|
/** @} */ |
/branches/gl-inet/package/libs/libnl-tiny/src/nl.c |
@@ -0,0 +1,720 @@ |
/* |
* lib/nl.c Core Netlink Interface |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation version 2.1 |
* of the License. |
* |
* Copyright (c) 2003-2008 Thomas Graf <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. Interrupted 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() * 4; |
|
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; |
} |
|
/** @} */ |
|
/** @} */ |
/branches/gl-inet/package/libs/libnl-tiny/src/socket.c |
@@ -0,0 +1,406 @@ |
/* |
* lib/socket.c Netlink Socket |
* |
* This library is free software; you can redistribute it and/or |
* modify it under the terms of the GNU Lesser General Public |
* License as published by the Free Software Foundation version 2.1 |
* of the License. |
* |
* Copyright (c) 2003-2008 Thomas Graf <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] &= ~(1 << nr % 32); |
} |
|
/** |
* @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; |
} |
|
/** @} */ |
|
/** @} */ |
/branches/gl-inet/package/libs/libnl-tiny/src/unl.c |
@@ -0,0 +1,287 @@ |
#define _GNU_SOURCE |
#include <netlink/netlink.h> |
#include <netlink/genl/genl.h> |
#include <netlink/genl/ctrl.h> |
#include <netlink/genl/family.h> |
#include <sys/types.h> |
#include <net/if.h> |
#include <unistd.h> |
#include <fcntl.h> |
#include <linux/nl80211.h> |
|
#include "unl.h" |
|
static int unl_init(struct unl *unl) |
{ |
unl->sock = nl_socket_alloc(); |
if (!unl->sock) |
return -1; |
|
return 0; |
} |
|
int unl_genl_init(struct unl *unl, const char *family) |
{ |
memset(unl, 0, sizeof(*unl)); |
|
if (unl_init(unl)) |
goto error_out; |
|
unl->hdrlen = NLMSG_ALIGN(sizeof(struct genlmsghdr)); |
unl->family_name = strdup(family); |
if (!unl->family_name) |
goto error; |
|
if (genl_connect(unl->sock)) |
goto error; |
|
if (genl_ctrl_alloc_cache(unl->sock, &unl->cache)) |
goto error; |
|
unl->family = genl_ctrl_search_by_name(unl->cache, family); |
if (!unl->family) |
goto error; |
|
return 0; |
|
error: |
unl_free(unl); |
error_out: |
return -1; |
} |
|
void unl_free(struct unl *unl) |
{ |
if (unl->family_name) |
free(unl->family_name); |
|
if (unl->sock) |
nl_socket_free(unl->sock); |
|
if (unl->cache) |
nl_cache_free(unl->cache); |
|
memset(unl, 0, sizeof(*unl)); |
} |
|
static int |
ack_handler(struct nl_msg *msg, void *arg) |
{ |
int *err = arg; |
*err = 0; |
return NL_STOP; |
} |
|
static int |
finish_handler(struct nl_msg *msg, void *arg) |
{ |
int *err = arg; |
*err = 0; |
return NL_SKIP; |
} |
|
static int |
error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) |
{ |
int *ret = arg; |
*ret = err->error; |
return NL_SKIP; |
} |
|
struct nl_msg *unl_genl_msg(struct unl *unl, int cmd, bool dump) |
{ |
struct nl_msg *msg; |
int flags = 0; |
|
msg = nlmsg_alloc(); |
if (!msg) |
goto out; |
|
if (dump) |
flags |= NLM_F_DUMP; |
|
genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, |
genl_family_get_id(unl->family), 0, flags, cmd, 0); |
|
out: |
return msg; |
} |
|
int unl_genl_request(struct unl *unl, struct nl_msg *msg, unl_cb handler, void *arg) |
{ |
struct nl_cb *cb; |
int err; |
|
cb = nl_cb_alloc(NL_CB_CUSTOM); |
err = nl_send_auto_complete(unl->sock, msg); |
if (err < 0) |
goto out; |
|
err = 1; |
nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); |
nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); |
nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); |
if (handler) |
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, handler, arg); |
|
while (err > 0) |
nl_recvmsgs(unl->sock, cb); |
|
out: |
nlmsg_free(msg); |
nl_cb_put(cb); |
return err; |
} |
|
static int request_single_cb(struct nl_msg *msg, void *arg) |
{ |
struct nl_msg **dest = arg; |
|
if (!*dest) { |
nlmsg_get(msg); |
*dest = msg; |
} |
return NL_SKIP; |
} |
|
int unl_genl_request_single(struct unl *unl, struct nl_msg *msg, struct nl_msg **dest) |
{ |
*dest = NULL; |
return unl_genl_request(unl, msg, request_single_cb, dest); |
} |
|
static int no_seq_check(struct nl_msg *msg, void *arg) |
{ |
return NL_OK; |
} |
|
void unl_genl_loop(struct unl *unl, unl_cb handler, void *arg) |
{ |
struct nl_cb *cb; |
|
cb = nl_cb_alloc(NL_CB_CUSTOM); |
unl->loop_done = false; |
nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); |
nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, handler, arg); |
|
while (!unl->loop_done) |
nl_recvmsgs(unl->sock, cb); |
|
nl_cb_put(cb); |
} |
|
int unl_genl_multicast_id(struct unl *unl, const char *name) |
{ |
struct nlattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1]; |
struct nlattr *groups, *group; |
struct nl_msg *msg; |
int ctrlid; |
int ret = -1; |
int rem; |
|
msg = nlmsg_alloc(); |
if (!msg) |
return -1; |
|
ctrlid = genl_ctrl_resolve(unl->sock, "nlctrl"); |
genlmsg_put(msg, 0, 0, ctrlid, 0, 0, CTRL_CMD_GETFAMILY, 0); |
NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, unl->family_name); |
unl_genl_request_single(unl, msg, &msg); |
if (!msg) |
return -1; |
|
groups = unl_find_attr(unl, msg, CTRL_ATTR_MCAST_GROUPS); |
if (!groups) |
goto nla_put_failure; |
|
nla_for_each_nested(group, groups, rem) { |
const char *gn; |
|
nla_parse(tb, CTRL_ATTR_MCAST_GRP_MAX, nla_data(group), |
nla_len(group), NULL); |
|
if (!tb[CTRL_ATTR_MCAST_GRP_NAME] || |
!tb[CTRL_ATTR_MCAST_GRP_ID]) |
continue; |
|
gn = nla_data(tb[CTRL_ATTR_MCAST_GRP_NAME]); |
if (strcmp(gn, name) != 0) |
continue; |
|
ret = nla_get_u32(tb[CTRL_ATTR_MCAST_GRP_ID]); |
break; |
} |
|
nla_put_failure: |
nlmsg_free(msg); |
return ret; |
} |
|
int unl_genl_subscribe(struct unl *unl, const char *name) |
{ |
int mcid; |
|
mcid = unl_genl_multicast_id(unl, name); |
if (mcid < 0) |
return mcid; |
|
return nl_socket_add_membership(unl->sock, mcid); |
} |
|
int unl_genl_unsubscribe(struct unl *unl, const char *name) |
{ |
int mcid; |
|
mcid = unl_genl_multicast_id(unl, name); |
if (mcid < 0) |
return mcid; |
|
return nl_socket_drop_membership(unl->sock, mcid); |
} |
|
int unl_nl80211_phy_lookup(const char *name) |
{ |
char buf[32]; |
int fd, pos; |
|
snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name); |
|
fd = open(buf, O_RDONLY); |
if (fd < 0) |
return -1; |
pos = read(fd, buf, sizeof(buf) - 1); |
if (pos < 0) { |
close(fd); |
return -1; |
} |
buf[pos] = '\0'; |
close(fd); |
return atoi(buf); |
} |
|
int unl_nl80211_wdev_to_phy(struct unl *unl, int wdev) |
{ |
struct nl_msg *msg; |
struct nlattr *attr; |
int ret = -1; |
|
msg = unl_genl_msg(unl, NL80211_CMD_GET_INTERFACE, false); |
if (!msg) |
return -1; |
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev); |
if (unl_genl_request_single(unl, msg, &msg) < 0) |
return -1; |
|
attr = unl_find_attr(unl, msg, NL80211_ATTR_WIPHY); |
if (!attr) |
goto out; |
|
ret = nla_get_u32(attr); |
out: |
nla_put_failure: |
nlmsg_free(msg); |
return ret; |
} |
|
|