DO NOT MERGE: Revert "delete libnl_2"

This reverts commit 7097f052d9.

libnl_2 needs to stay in AOSP for now for compatibility with
GPL test builds.
This commit is contained in:
Colin Cross 2014-03-21 16:59:20 -07:00 committed by Qiwen Zhao
parent 7ab32aca56
commit 6e3fffeca6
13 changed files with 1450 additions and 0 deletions

2
libnl_2/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
include/netlink/version.h.in
cscope.*

39
libnl_2/Android.mk Normal file
View File

@ -0,0 +1,39 @@
#######################################
# * Netlink cache not implemented
# * Library is not thread safe
#######################################
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
attr.c \
cache.c \
genl/genl.c \
genl/family.c \
handlers.c \
msg.c \
netlink.c \
object.c \
socket.c \
dbg.c
LOCAL_C_INCLUDES += \
external/libnl-headers
# Static Library
LOCAL_MODULE := libnl_2
LOCAL_MODULE_TAGS := optional
LOCAL_32_BIT_ONLY := true
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_SRC_FILES :=
LOCAL_WHOLE_STATIC_LIBRARIES:= libnl_2
LOCAL_SHARED_LIBRARIES:= liblog
LOCAL_MODULE := libnl_2
LOCAL_MODULE_TAGS := optional
LOCAL_32_BIT_ONLY := true
include $(BUILD_SHARED_LIBRARY)

88
libnl_2/README Normal file
View File

@ -0,0 +1,88 @@
Netlink Protocol Library
This library is a clean room re-implementation of libnl 2.0 and
re-licensed under Apache 2.0. It was developed primarily to support
wpa_supplicant. However, with additional development can be extended
to support other netlink applications.
Netlink Protocol Format (RFC3549)
+-----------------+-+-------------------+-+
|Netlink Message |P| Generic Netlink |P|
| Header |A| Message Header |A|
|(struct nlmsghdr)|D|(struct genlmsghdr)|D|
+-----------------+-+-------------------+-+-------------+
|len:4|type:2|flags:2|seq:4 pid:4|cmd:1|ver:1|reserved:2|
+--------------------------------+----------------------+
+-----------------+-+-----------------+-+-----------------+-+-----------------+-+---+
|Netlink Attribute|P|Netlink Attribute|P|Netlink Attribute|P|Netlink Attribute|P|...|
| #0 Header |A| #0 Payload |A| #1 Header |A| #1 Payload |A| |
| (struct nlattr) |D| (void) |D| (struct nlattr) |D| (void) |D| |
+-----------------+-+-----------------+-+-----------------+-+-----------------+-+---+
|len:2(==4+payload)|type:2|payload|pad|
+-------------------------+-------+---+
NETLINK OVERVIEW
* Each netlink message consists of a bitstream with a netlink header.
* After this header a second header *can* be used specific to the netlink
family in use. This library was tested using the generic netlink
protocol defined by struct genlmsghdr to support nl80211.
* After the header(s) netlink attributes can be appended to the message
which hold can hold basic types such as unsigned integers and strings.
* Attributes can also be nested. This is accomplished by calling "nla_nest_start"
which creates an empty attribute with nest attributes as its payload. Then to
close the nest, "nla_nest_end" is called.
* All data structures in this implementation are byte-aligned (Currently 4 bytes).
* Acknowledgements (ACKs) are sent as NLMSG_ERROR netlink message types (0x2) and
have an error value of 0.
KNOWN ISSUES
GENERAL
* Not tested for thread safety
Android.mk
* No dynamic library because of netlink cache not implemented and
not tested for thread safety
attr.c
* nla_parse - does not use nla_policy argument
cache.c
* netlink cache not implemented and only supports one netlink family id
which is stored in the nl_cache pointer instead of an actual cache
netlink.c
* nl_recvmsgs - does not support nl_cb_overwrite_recv()
* nl_recv - sets/unsets asynchronous socket flag
SOURCE FILES
* Android.mk - Android makefile
* README - This file
* attr.c - Netlink attributes
* cache.c - Netlink cache
* genl/family.c - Generic netlink family id
* genl/genl.c - Generic netlink
* handlers.c - Netlink callbacks
* msg.c - Netlink messages construction
* netlink.c - Netlink socket communication
* object.c - libnl object wrapper
* socket.c - Netlink kernel socket utils
IMPORTANT HEADER FILES - NOTE: These are based on the the origin GPL libnl headers
* netlink-types.h - Contains many important structs for libnl
to represent netlink objects
* netlink/netlink-kernel.h - Netlink kernel headers and field constants.
* netlink/msg.h - macros for iterating over netlink messages
* netlink/attr.h - netlink attribute constants, iteration macros and setters
REFERENCES
* nl80211.h
* netlink_types.h
* $LINUX_KERNEL/net/wireless/nl80211.c
* http://www.infradead.org/~tgr/libnl/doc-3.0/index.html
* http://www.netfilter.org/projects/libmnl/doxygen/index.html

239
libnl_2/attr.c Normal file
View File

@ -0,0 +1,239 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* NOTICE: This is a clean room re-implementation of libnl */
#include <errno.h>
#include "netlink/netlink.h"
#include "netlink/msg.h"
#include "netlink/attr.h"
#include "netlink-types.h"
/* Return payload of string attribute. */
char *nla_get_string(struct nlattr *nla)
{
return (char *) nla_data(nla);
}
/* Return payload of 16 bit integer attribute. */
uint16_t nla_get_u16(struct nlattr *nla)
{
return *((uint16_t *) nla_data(nla));
}
/* Return payload of 32 bit integer attribute. */
uint32_t nla_get_u32(struct nlattr *nla)
{
return *((uint32_t *) nla_data(nla));
}
/* Return value of 8 bit integer attribute. */
uint8_t nla_get_u8(struct nlattr *nla)
{
return *((uint8_t *) nla_data(nla));
}
/* Return payload of uint64_t attribute. */
uint64_t nla_get_u64(struct nlattr *nla)
{
uint64_t tmp;
nla_memcpy(&tmp, nla, sizeof(tmp));
return tmp;
}
/* Head of payload */
void *nla_data(const struct nlattr *nla)
{
return (void *) ((char *) nla + NLA_HDRLEN);
}
/* Return length of the payload . */
int nla_len(const struct nlattr *nla)
{
return nla->nla_len - NLA_HDRLEN;
}
int nla_padlen(int payload)
{
return NLA_ALIGN(payload) - payload;
}
/* Start a new level of nested attributes. */
struct nlattr *nla_nest_start(struct nl_msg *msg, int attrtype)
{
struct nlattr *start = (struct nlattr *)nlmsg_tail(msg->nm_nlh);
int rc;
rc = nla_put(msg, attrtype, 0, NULL);
if (rc < 0)
return NULL;
return start;
}
/* Finalize nesting of attributes. */
int nla_nest_end(struct nl_msg *msg, struct nlattr *start)
{
/* Set attribute size */
start->nla_len = (unsigned char *)nlmsg_tail(nlmsg_hdr(msg)) -
(unsigned char *)start;
return 0;
}
/* Return next attribute in a stream of attributes. */
struct nlattr *nla_next(const struct nlattr *nla, int *remaining)
{
struct nlattr *next_nla = NULL;
if (nla->nla_len >= sizeof(struct nlattr) &&
nla->nla_len <= *remaining){
next_nla = (struct nlattr *) \
((char *) nla + NLA_ALIGN(nla->nla_len));
*remaining = *remaining - NLA_ALIGN(nla->nla_len);
}
return next_nla;
}
/* Check if the attribute header and payload can be accessed safely. */
int nla_ok(const struct nlattr *nla, int remaining)
{
return remaining > 0 &&
nla->nla_len >= sizeof(struct nlattr) &&
sizeof(struct nlattr) <= (unsigned int) remaining &&
nla->nla_len <= remaining;
}
/* Create attribute index based on a stream of attributes. */
/* NOTE: Policy not used ! */
int nla_parse(struct nlattr *tb[], int maxtype, struct nlattr *head,
int len, struct nla_policy *policy)
{
struct nlattr *pos;
int rem;
/* First clear table */
memset(tb, 0, (maxtype + 1) * sizeof(struct nlattr *));
nla_for_each_attr(pos, head, len, rem) {
int type = nla_type(pos);
if ((type <= maxtype) && (type != 0))
tb[type] = pos;
}
return 0;
}
/* Create attribute index based on nested attribute. */
int nla_parse_nested(struct nlattr *tb[], int maxtype,
struct nlattr *nla, struct nla_policy *policy)
{
return nla_parse(tb, maxtype, nla_data(nla), nla_len(nla), policy);
}
/* Add a unspecific attribute to netlink message. */
int nla_put(struct nl_msg *msg, int attrtype, int datalen, const void *data)
{
struct nlattr *nla;
/* Reserve space and init nla header */
nla = nla_reserve(msg, attrtype, datalen);
if (nla) {
memcpy(nla_data(nla), data, datalen);
return 0;
}
return -EINVAL;
}
/* Add 8 bit integer attribute to netlink message. */
int nla_put_u8(struct nl_msg *msg, int attrtype, uint8_t value)
{
return nla_put(msg, attrtype, sizeof(uint8_t), &value);
}
/* Add 16 bit integer attribute to netlink message. */
int nla_put_u16(struct nl_msg *msg, int attrtype, uint16_t value)
{
return nla_put(msg, attrtype, sizeof(uint16_t), &value);
}
/* Add 32 bit integer attribute to netlink message. */
int nla_put_u32(struct nl_msg *msg, int attrtype, uint32_t value)
{
return nla_put(msg, attrtype, sizeof(uint32_t), &value);
}
/* Add 64 bit integer attribute to netlink message. */
int nla_put_u64(struct nl_msg *msg, int attrtype, uint64_t value)
{
return nla_put(msg, attrtype, sizeof(uint64_t), &value);
}
/* Add nested attributes to netlink message. */
/* Takes the attributes found in the nested message and appends them
* to the message msg nested in a container of the type attrtype. The
* nested message may not have a family specific header */
int nla_put_nested(struct nl_msg *msg, int attrtype, struct nl_msg *nested)
{
int rc;
rc = nla_put(msg, attrtype, nlmsg_attrlen(nlmsg_hdr(nested), 0),
nlmsg_attrdata(nlmsg_hdr(nested), 0));
return rc;
}
/* Return type of the attribute. */
int nla_type(const struct nlattr *nla)
{
return (int)nla->nla_type & NLA_TYPE_MASK;
}
/* Reserves room for an attribute in specified netlink message and fills
* in the attribute header (type,length). Return NULL if insufficient space */
struct nlattr *nla_reserve(struct nl_msg *msg, int attrtype, int data_len)
{
struct nlattr *nla;
const unsigned int NEW_SIZE = NLMSG_ALIGN(msg->nm_nlh->nlmsg_len) +
NLA_ALIGN(NLA_HDRLEN + data_len);
/* Check enough space for attribute */
if (NEW_SIZE > msg->nm_size)
return NULL;
nla = (struct nlattr *)nlmsg_tail(msg->nm_nlh);
nla->nla_type = attrtype;
nla->nla_len = NLA_HDRLEN + data_len;
memset((unsigned char *)nla + nla->nla_len, 0, nla_padlen(data_len));
msg->nm_nlh->nlmsg_len = NEW_SIZE;
return nla;
}
/* Copy attribute payload to another memory area. */
int nla_memcpy(void *dest, struct nlattr *src, int count)
{
if (!src || !dest)
return 0;
if (count > nla_len(src))
count = nla_len(src);
memcpy(dest, nla_data(src), count);
return count;
}

37
libnl_2/cache.c Normal file
View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* NOTICE: This is a clean room re-implementation of libnl */
#include "netlink/cache.h"
#include "netlink/object.h"
void nl_cache_free(struct nl_cache *cache)
{
}
void nl_cache_clear(struct nl_cache *cache)
{
}
void nl_cache_remove(struct nl_object *obj)
{
}

12
libnl_2/dbg.c Normal file
View File

@ -0,0 +1,12 @@
#include "netlink/netlink.h"
#include <android/log.h>
void libnl_printf(int level, char *format, ...)
{
va_list ap;
level = ANDROID_LOG_ERROR;
va_start(ap, format);
__android_log_vprint(level, "libnl_2", format, ap);
va_end(ap);
}

44
libnl_2/genl/family.c Normal file
View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* NOTICE: This is a clean room re-implementation of libnl */
#include "netlink-types.h"
static struct genl_family *genl_family_find_byname(const char *name)
{
return NULL;
}
/* Release reference and none outstanding */
void genl_family_put(struct genl_family *family)
{
family->ce_refcnt--;
if (family->ce_refcnt <= 0)
free(family);
}
unsigned int genl_family_get_id(struct genl_family *family)
{
const int NO_FAMILY_ID = 0;
if (!family)
return NO_FAMILY_ID;
else
return family->gf_id;
}

302
libnl_2/genl/genl.c Normal file
View File

@ -0,0 +1,302 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* NOTICE: This is a clean room re-implementation of libnl */
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/family.h>
#include "netlink-types.h"
/* Get head of attribute data. */
struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen)
{
return (struct nlattr *) \
((char *) gnlh + GENL_HDRLEN + NLMSG_ALIGN(hdrlen));
}
/* Get length of attribute data. */
int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen)
{
struct nlattr *nla;
struct nlmsghdr *nlh;
nla = genlmsg_attrdata(gnlh, hdrlen);
nlh = (struct nlmsghdr *) ((char *) gnlh - NLMSG_HDRLEN);
return (char *) nlmsg_tail(nlh) - (char *) nla;
}
/* Add generic netlink header to netlink message. */
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)
{
int new_size;
struct nlmsghdr *nlh;
struct timeval tv;
struct genlmsghdr *gmh;
/* Make sure nl_msg has enough space */
new_size = NLMSG_HDRLEN + GENL_HDRLEN + hdrlen;
if ((sizeof(struct nl_msg) + new_size) > msg->nm_size)
goto fail;
/* Fill in netlink header */
nlh = msg->nm_nlh;
nlh->nlmsg_len = new_size;
nlh->nlmsg_type = family;
nlh->nlmsg_pid = getpid();
nlh->nlmsg_flags = flags | NLM_F_REQUEST | NLM_F_ACK;
/* Get current time for sequence number */
if (gettimeofday(&tv, NULL))
nlh->nlmsg_seq = 1;
else
nlh->nlmsg_seq = (int) tv.tv_sec;
/* Setup genlmsghdr in new message */
gmh = (struct genlmsghdr *) ((char *)nlh + NLMSG_HDRLEN);
gmh->cmd = (__u8) cmd;
gmh->version = version;
return gmh;
fail:
return NULL;
}
/* Socket has already been alloced to connect it to kernel? */
int genl_connect(struct nl_sock *sk)
{
return nl_connect(sk, NETLINK_GENERIC);
}
int genl_ctrl_alloc_cache(struct nl_sock *sock, struct nl_cache **result)
{
int rc = -1;
int nl80211_genl_id = -1;
char sendbuf[sizeof(struct nlmsghdr)+sizeof(struct genlmsghdr)];
struct nlmsghdr nlmhdr;
struct genlmsghdr gmhhdr;
struct iovec sendmsg_iov;
struct msghdr msg;
int num_char;
const int RECV_BUF_SIZE = getpagesize();
char *recvbuf;
struct iovec recvmsg_iov;
int nl80211_flag = 0, nlm_f_multi = 0, nlmsg_done = 0;
struct nlmsghdr *nlh;
/* REQUEST GENERIC NETLINK FAMILY ID */
/* Message buffer */
nlmhdr.nlmsg_len = sizeof(sendbuf);
nlmhdr.nlmsg_type = NETLINK_GENERIC;
nlmhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
nlmhdr.nlmsg_seq = sock->s_seq_next;
nlmhdr.nlmsg_pid = sock->s_local.nl_pid;
/* Generic netlink header */
memset(&gmhhdr, 0, sizeof(gmhhdr));
gmhhdr.cmd = CTRL_CMD_GETFAMILY;
gmhhdr.version = CTRL_ATTR_FAMILY_ID;
/* Combine netlink and generic netlink headers */
memcpy(&sendbuf[0], &nlmhdr, sizeof(nlmhdr));
memcpy(&sendbuf[0]+sizeof(nlmhdr), &gmhhdr, sizeof(gmhhdr));
/* Create IO vector with Netlink message */
sendmsg_iov.iov_base = &sendbuf;
sendmsg_iov.iov_len = sizeof(sendbuf);
/* Socket message */
msg.msg_name = (void *) &sock->s_peer;
msg.msg_namelen = sizeof(sock->s_peer);
msg.msg_iov = &sendmsg_iov;
msg.msg_iovlen = 1; /* Only sending one iov */
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
/* Send message and verify sent */
num_char = sendmsg(sock->s_fd, &msg, 0);
if (num_char == -1)
return -errno;
/* RECEIVE GENL CMD RESPONSE */
/* Create receive iov buffer */
recvbuf = (char *) malloc(RECV_BUF_SIZE);
/* Attach to iov */
recvmsg_iov.iov_base = recvbuf;
recvmsg_iov.iov_len = RECV_BUF_SIZE;
msg.msg_iov = &recvmsg_iov;
msg.msg_iovlen = 1;
/***************************************************************/
/* Receive message. If multipart message, keep receiving until */
/* message type is NLMSG_DONE */
/***************************************************************/
do {
int recvmsg_len, nlmsg_rem;
/* Receive message */
memset(recvbuf, 0, RECV_BUF_SIZE);
recvmsg_len = recvmsg(sock->s_fd, &msg, 0);
/* Make sure receive successful */
if (recvmsg_len < 0) {
rc = -errno;
goto error_recvbuf;
}
/* Parse nlmsghdr */
nlmsg_for_each_msg(nlh, (struct nlmsghdr *) recvbuf, \
recvmsg_len, nlmsg_rem) {
struct nlattr *nla;
int nla_rem;
/* Check type */
switch (nlh->nlmsg_type) {
case NLMSG_DONE:
goto return_genl_id;
break;
case NLMSG_ERROR:
/* Should check nlmsgerr struct received */
fprintf(stderr, "Receive message error\n");
goto error_recvbuf;
case NLMSG_OVERRUN:
fprintf(stderr, "Receive data partly lost\n");
goto error_recvbuf;
case NLMSG_MIN_TYPE:
case NLMSG_NOOP:
break;
default:
break;
}
/* Check flags */
if (nlh->nlmsg_flags & NLM_F_MULTI)
nlm_f_multi = 1;
else
nlm_f_multi = 0;
if (nlh->nlmsg_type & NLMSG_DONE)
nlmsg_done = 1;
else
nlmsg_done = 0;
/* Iteratve over attributes */
nla_for_each_attr(nla,
nlmsg_attrdata(nlh, GENL_HDRLEN),
nlmsg_attrlen(nlh, GENL_HDRLEN),
nla_rem){
/* If this family is nl80211 */
if (nla->nla_type == CTRL_ATTR_FAMILY_NAME &&
!strcmp((char *)nla_data(nla),
"nl80211"))
nl80211_flag = 1;
/* Save the family id */
else if (nl80211_flag &&
nla->nla_type == CTRL_ATTR_FAMILY_ID) {
nl80211_genl_id =
*((int *)nla_data(nla));
nl80211_flag = 0;
}
}
}
} while (nlm_f_multi && !nlmsg_done);
return_genl_id:
/* Return family id as cache pointer */
*result = (struct nl_cache *) nl80211_genl_id;
rc = 0;
error_recvbuf:
free(recvbuf);
error:
return rc;
}
/* Checks the netlink cache to find family reference by name string */
/* NOTE: Caller needs to call genl_family_put() when done with *
* returned object */
struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, \
const char *name)
{
struct genl_family *gf = (struct genl_family *) \
malloc(sizeof(struct genl_family));
if (!gf)
goto fail;
memset(gf, 0, sizeof(*gf));
/* Add ref */
gf->ce_refcnt++;
/* Overriding cache pointer as family id for now */
gf->gf_id = (uint16_t) ((uint32_t) cache);
strncpy(gf->gf_name, name, GENL_NAMSIZ);
return gf;
fail:
return NULL;
}
int genl_ctrl_resolve(struct nl_sock *sk, const char *name)
{
struct nl_cache *cache = NULL;
struct genl_family *gf = NULL;
int id = -1;
/* Hack to support wpa_supplicant */
if (strcmp(name, "nlctrl") == 0)
return NETLINK_GENERIC;
if (strcmp(name, "nl80211") != 0) {
fprintf(stderr, "%s is not supported\n", name);
return id;
}
if (!genl_ctrl_alloc_cache(sk, &cache)) {
gf = genl_ctrl_search_by_name(cache, name);
if (gf)
id = genl_family_get_id(gf);
}
if (gf)
genl_family_put(gf);
if (cache)
nl_cache_free(cache);
return id;
}

90
libnl_2/handlers.c Normal file
View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* NOTICE: This is a clean room re-implementation of libnl */
#include <malloc.h>
#include "netlink-types.h"
#include "netlink/handlers.h"
/* Allocate a new callback handle. */
struct nl_cb *nl_cb_alloc(enum nl_cb_kind kind)
{
struct nl_cb *cb;
cb = (struct nl_cb *) malloc(sizeof(struct nl_cb));
if (cb == NULL)
goto fail;
memset(cb, 0, sizeof(*cb));
return nl_cb_get(cb);
fail:
return NULL;
}
/* Clone an existing callback handle */
struct nl_cb *nl_cb_clone(struct nl_cb *orig)
{
struct nl_cb *new_cb;
new_cb = nl_cb_alloc(NL_CB_DEFAULT);
if (new_cb == NULL)
goto fail;
/* Copy original and set refcount to 1 */
memcpy(new_cb, orig, sizeof(*orig));
new_cb->cb_refcnt = 1;
return new_cb;
fail:
return NULL;
}
/* Set up a callback. */
int nl_cb_set(struct nl_cb *cb, enum nl_cb_type type, enum nl_cb_kind kind, \
nl_recvmsg_msg_cb_t func, void *arg)
{
cb->cb_set[type] = func;
cb->cb_args[type] = arg;
return 0;
}
/* Set up an error callback. */
int nl_cb_err(struct nl_cb *cb, enum nl_cb_kind kind, \
nl_recvmsg_err_cb_t func, void *arg)
{
cb->cb_err = func;
cb->cb_err_arg = arg;
return 0;
}
struct nl_cb *nl_cb_get(struct nl_cb *cb)
{
cb->cb_refcnt++;
return cb;
}
void nl_cb_put(struct nl_cb *cb)
{
if (!cb)
return;
cb->cb_refcnt--;
if (cb->cb_refcnt <= 0)
free(cb);
}

150
libnl_2/msg.c Normal file
View File

@ -0,0 +1,150 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* NOTICE: This is a clean room re-implementation of libnl */
#include <malloc.h>
#include <unistd.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include "netlink-types.h"
/* Allocate a new netlink message with the default maximum payload size. */
struct nl_msg *nlmsg_alloc(void)
{
/* Whole page will store nl_msg + nlmsghdr + genlmsghdr + payload */
const int page_sz = getpagesize();
struct nl_msg *nm;
struct nlmsghdr *nlh;
/* Netlink message */
nm = (struct nl_msg *) malloc(page_sz);
if (!nm)
goto fail;
/* Netlink message header pointer */
nlh = (struct nlmsghdr *) ((char *) nm + sizeof(struct nl_msg));
/* Initialize */
memset(nm, 0, page_sz);
nm->nm_size = page_sz;
nm->nm_src.nl_family = AF_NETLINK;
nm->nm_src.nl_pid = getpid();
nm->nm_dst.nl_family = AF_NETLINK;
nm->nm_dst.nl_pid = 0; /* Kernel */
/* Initialize and add to netlink message */
nlh->nlmsg_len = NLMSG_HDRLEN;
nm->nm_nlh = nlh;
/* Add to reference count and return nl_msg */
nlmsg_get(nm);
return nm;
fail:
return NULL;
}
/* Return pointer to message payload. */
void *nlmsg_data(const struct nlmsghdr *nlh)
{
return (char *) nlh + NLMSG_HDRLEN;
}
/* Add reference count to nl_msg */
void nlmsg_get(struct nl_msg *nm)
{
nm->nm_refcnt++;
}
/* Release a reference from an netlink message. */
void nlmsg_free(struct nl_msg *nm)
{
if (nm) {
nm->nm_refcnt--;
if (nm->nm_refcnt <= 0)
free(nm);
}
}
/* Return actual netlink message. */
struct nlmsghdr *nlmsg_hdr(struct nl_msg *n)
{
return n->nm_nlh;
}
/* Return head of attributes data / payload section */
struct nlattr *nlmsg_attrdata(const struct nlmsghdr *nlh, int hdrlen)
{
unsigned char *data = nlmsg_data(nlh);
return (struct nlattr *)(data + NLMSG_ALIGN(hdrlen));
}
/* Returns pointer to end of netlink message */
void *nlmsg_tail(const struct nlmsghdr *nlh)
{
return (void *)((char *)nlh + NLMSG_ALIGN(nlh->nlmsg_len));
}
/* Next netlink message in message stream */
struct nlmsghdr *nlmsg_next(struct nlmsghdr *nlh, int *remaining)
{
struct nlmsghdr *next_nlh = NULL;
int len = nlmsg_len(nlh);
len = NLMSG_ALIGN(len);
if (*remaining > 0 &&
len <= *remaining &&
len >= (int) sizeof(struct nlmsghdr)) {
next_nlh = (struct nlmsghdr *)((char *)nlh + len);
*remaining -= len;
}
return next_nlh;
}
int nlmsg_datalen(const struct nlmsghdr *nlh)
{
return nlh->nlmsg_len - NLMSG_HDRLEN;
}
/* Length of attributes data */
int nlmsg_attrlen(const struct nlmsghdr *nlh, int hdrlen)
{
return nlmsg_datalen(nlh) - NLMSG_ALIGN(hdrlen);
}
/* Length of netlink message */
int nlmsg_len(const struct nlmsghdr *nlh)
{
return nlh->nlmsg_len;
}
/* Check if the netlink message fits into the remaining bytes */
int nlmsg_ok(const struct nlmsghdr *nlh, int rem)
{
return rem >= (int)sizeof(struct nlmsghdr) &&
rem >= nlmsg_len(nlh) &&
nlmsg_len(nlh) >= (int) sizeof(struct nlmsghdr) &&
nlmsg_len(nlh) <= (rem);
}
int nlmsg_padlen(int payload)
{
return NLMSG_ALIGN(payload) - payload;
}

273
libnl_2/netlink.c Normal file
View File

@ -0,0 +1,273 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* NOTICE: This is a clean room re-implementation of libnl */
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include "netlink-types.h"
#define NL_BUFFER_SZ (32768U)
/* Checks message for completeness and sends it out */
int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
{
struct nlmsghdr *nlh = msg->nm_nlh;
struct timeval tv;
if (!nlh) {
int errsv = errno;
fprintf(stderr, "Netlink message header is NULL!\n");
return -errsv;
}
/* Complete the nl_msg header */
if (gettimeofday(&tv, NULL))
nlh->nlmsg_seq = 1;
else
nlh->nlmsg_seq = (int) tv.tv_sec;
nlh->nlmsg_pid = sk->s_local.nl_pid;
nlh->nlmsg_flags |= NLM_F_REQUEST | NLM_F_ACK;
return nl_send(sk, msg);
}
/* Receives a netlink message, allocates a buffer in *buf and stores
* the message content. The peer's netlink address is stored in
* *nla. The caller is responsible for freeing the buffer allocated in
* *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 */
int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, \
unsigned char **buf, struct ucred **creds)
{
int rc = -1;
int sk_flags;
int RECV_BUF_SIZE = getpagesize();
int errsv;
struct iovec recvmsg_iov;
struct msghdr msg;
/* Allocate buffer */
*buf = (unsigned char *) malloc(RECV_BUF_SIZE);
if (!(*buf)) {
rc = -ENOMEM;
goto fail;
}
/* Prepare to receive message */
recvmsg_iov.iov_base = *buf;
recvmsg_iov.iov_len = RECV_BUF_SIZE;
msg.msg_name = (void *) &sk->s_peer;
msg.msg_namelen = sizeof(sk->s_peer);
msg.msg_iov = &recvmsg_iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
/* Make non blocking and then restore previous setting */
sk_flags = fcntl(sk->s_fd, F_GETFL, 0);
fcntl(sk->s_fd, F_SETFL, O_NONBLOCK);
rc = recvmsg(sk->s_fd, &msg, 0);
errsv = errno;
fcntl(sk->s_fd, F_SETFL, sk_flags);
if (rc < 0) {
rc = -errsv;
free(*buf);
*buf = NULL;
}
fail:
return rc;
}
/* Receive a set of messages from a netlink socket */
/* NOTE: Does not currently support callback replacements!!! */
int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb)
{
struct sockaddr_nl nla;
struct ucred *creds;
int rc, cb_rc = NL_OK, done = 0;
do {
unsigned char *buf;
int i, rem, flags;
struct nlmsghdr *nlh;
struct nlmsgerr *nlme;
struct nl_msg *msg;
done = 0;
rc = nl_recv(sk, &nla, &buf, &creds);
if (rc < 0)
break;
nlmsg_for_each_msg(nlh, (struct nlmsghdr *) buf, rc, rem) {
if (rc <= 0 || cb_rc == NL_STOP)
break;
/* Check for callbacks */
msg = (struct nl_msg *) malloc(sizeof(struct nl_msg));
memset(msg, 0, sizeof(*msg));
msg->nm_nlh = nlh;
/* Check netlink message type */
switch (msg->nm_nlh->nlmsg_type) {
case NLMSG_ERROR: /* Used for ACK too */
/* Certainly we should be doing some
* checking here to make sure this
* message is intended for us */
nlme = nlmsg_data(msg->nm_nlh);
if (nlme->error == 0)
msg->nm_nlh->nlmsg_flags |= NLM_F_ACK;
rc = nlme->error;
cb_rc = cb->cb_err(&nla, nlme, cb->cb_err_arg);
nlme = NULL;
break;
case NLMSG_DONE:
done = 1;
case NLMSG_OVERRUN:
case NLMSG_NOOP:
default:
break;
};
for (i = 0; i <= NL_CB_TYPE_MAX; i++) {
if (cb->cb_set[i]) {
switch (i) {
case NL_CB_VALID:
if (rc > 0)
cb_rc = cb->cb_set[i](msg, cb->cb_args[i]);
break;
case NL_CB_FINISH:
if ((msg->nm_nlh->nlmsg_flags & NLM_F_MULTI) &&
(msg->nm_nlh->nlmsg_type & NLMSG_DONE))
cb_rc = cb->cb_set[i](msg, cb->cb_args[i]);
break;
case NL_CB_ACK:
if (msg->nm_nlh->nlmsg_flags & NLM_F_ACK)
cb_rc = cb->cb_set[i](msg, cb->cb_args[i]);
break;
default:
break;
}
}
}
free(msg);
if (done)
break;
}
free(buf);
buf = NULL;
if (done)
break;
} while (rc > 0 && cb_rc != NL_STOP);
success:
fail:
return rc;
}
/* Send raw data over netlink socket */
int nl_send(struct nl_sock *sk, struct nl_msg *msg)
{
struct nlmsghdr *nlh = nlmsg_hdr(msg);
struct iovec msg_iov;
/* Create IO vector with Netlink message */
msg_iov.iov_base = nlh;
msg_iov.iov_len = nlh->nlmsg_len;
return nl_send_iovec(sk, msg, &msg_iov, 1);
}
/* Send netlink message */
int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg,
struct iovec *iov, unsigned iovlen)
{
int rc;
/* Socket message */
struct msghdr mh = {
.msg_name = (void *) &sk->s_peer,
.msg_namelen = sizeof(sk->s_peer),
.msg_iov = iov,
.msg_iovlen = iovlen,
.msg_control = NULL,
.msg_controllen = 0,
.msg_flags = 0
};
/* Send message and verify sent */
rc = nl_sendmsg(sk, (struct nl_msg *) &mh, 0);
if (rc < 0)
fprintf(stderr, "Error sending netlink message: %d\n", errno);
return rc;
}
/* Send netlink message with control over sendmsg() message header */
int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr)
{
return sendmsg(sk->s_fd, (struct msghdr *) msg, (int) hdr);
}
/* Create and connect netlink socket */
int nl_connect(struct nl_sock *sk, int protocol)
{
struct sockaddr addr;
socklen_t addrlen;
int rc;
/* Create RX socket */
sk->s_fd = socket(PF_NETLINK, SOCK_RAW, protocol);
if (sk->s_fd < 0)
return -errno;
/* Set size of RX and TX buffers */
if (nl_socket_set_buffer_size(sk, NL_BUFFER_SZ, NL_BUFFER_SZ) < 0)
return -errno;
/* Bind RX socket */
rc = bind(sk->s_fd, (struct sockaddr *)&sk->s_local, \
sizeof(sk->s_local));
if (rc < 0)
return -errno;
addrlen = sizeof(addr);
getsockname(sk->s_fd, &addr, &addrlen);
return 0;
}

33
libnl_2/object.c Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* NOTICE: This is a clean room re-implementation of libnl */
#include "netlink-types.h"
void nl_object_put(struct nl_object *obj)
{
obj->ce_refcnt--;
if (!obj->ce_refcnt)
nl_object_free(obj);
}
void nl_object_free(struct nl_object *obj)
{
nl_cache_remove(obj);
}

141
libnl_2/socket.c Normal file
View File

@ -0,0 +1,141 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* NOTICE: This is a clean room re-implementation of libnl */
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <sys/time.h>
#include <sys/socket.h>
#include "netlink-types.h"
/* Join group */
int nl_socket_add_membership(struct nl_sock *sk, int group)
{
return setsockopt(sk->s_fd, SOL_NETLINK,
NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
}
/* Allocate new netlink socket. */
static struct nl_sock *_nl_socket_alloc(void)
{
struct nl_sock *sk;
struct timeval tv;
struct nl_cb *cb;
sk = (struct nl_sock *) malloc(sizeof(struct nl_sock));
if (!sk)
return NULL;
memset(sk, 0, sizeof(*sk));
/* Get current time */
if (gettimeofday(&tv, NULL))
goto fail;
else
sk->s_seq_next = (int) tv.tv_sec;
/* Create local socket */
sk->s_local.nl_family = AF_NETLINK;
sk->s_local.nl_pid = 0; /* Kernel fills in pid */
sk->s_local.nl_groups = 0; /* No groups */
/* Create peer socket */
sk->s_peer.nl_family = AF_NETLINK;
sk->s_peer.nl_pid = 0; /* Kernel */
sk->s_peer.nl_groups = 0; /* No groups */
return sk;
fail:
free(sk);
return NULL;
}
/* Allocate new netlink socket. */
struct nl_sock *nl_socket_alloc(void)
{
struct nl_sock *sk = _nl_socket_alloc();
struct nl_cb *cb;
if (!sk)
return NULL;
cb = nl_cb_alloc(NL_CB_DEFAULT);
if (!cb)
goto cb_fail;
sk->s_cb = cb;
return sk;
cb_fail:
free(sk);
return NULL;
}
/* Allocate new socket with custom callbacks. */
struct nl_sock *nl_socket_alloc_cb(struct nl_cb *cb)
{
struct nl_sock *sk = _nl_socket_alloc();
if (!sk)
return NULL;
sk->s_cb = cb;
nl_cb_get(cb);
return sk;
}
/* Free a netlink socket. */
void nl_socket_free(struct nl_sock *sk)
{
nl_cb_put(sk->s_cb);
close(sk->s_fd);
free(sk);
}
/* Sets socket buffer size of netlink socket */
int nl_socket_set_buffer_size(struct nl_sock *sk, int rxbuf, int txbuf)
{
if (setsockopt(sk->s_fd, SOL_SOCKET, SO_SNDBUF, \
&rxbuf, (socklen_t) sizeof(rxbuf)))
goto error;
if (setsockopt(sk->s_fd, SOL_SOCKET, SO_RCVBUF, \
&txbuf, (socklen_t) sizeof(txbuf)))
goto error;
return 0;
error:
return -errno;
}
int nl_socket_get_fd(struct nl_sock *sk)
{
return sk->s_fd;
}
void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb)
{
nl_cb_put(sk->s_cb);
sk->s_cb = cb;
nl_cb_get(cb);
}
struct nl_cb *nl_socket_get_cb(struct nl_sock *sk)
{
return nl_cb_get(sk->s_cb);
}