diff options
author | Colin Cross <ccross@android.com> | 2014-03-21 16:59:20 -0700 |
---|---|---|
committer | Qiwen Zhao <zhao@google.com> | 2014-06-04 08:15:48 -0700 |
commit | 6e3fffeca6ac00379b0a22888d04a58ee0f51057 (patch) | |
tree | 83c5fd1c67e992c1d6ca53094a8dab59921e3e97 /libnl_2 | |
parent | 7ab32aca56fe8df4575fc2e2e40ff7e1d38fca60 (diff) | |
download | system_core-6e3fffeca6ac00379b0a22888d04a58ee0f51057.zip system_core-6e3fffeca6ac00379b0a22888d04a58ee0f51057.tar.gz system_core-6e3fffeca6ac00379b0a22888d04a58ee0f51057.tar.bz2 |
DO NOT MERGE: Revert "delete libnl_2"
This reverts commit 7097f052d946bc9fbe298c7a88e1d943f54f684e.
libnl_2 needs to stay in AOSP for now for compatibility with
GPL test builds.
Diffstat (limited to 'libnl_2')
-rw-r--r-- | libnl_2/.gitignore | 2 | ||||
-rw-r--r-- | libnl_2/Android.mk | 39 | ||||
-rw-r--r-- | libnl_2/README | 88 | ||||
-rw-r--r-- | libnl_2/attr.c | 239 | ||||
-rw-r--r-- | libnl_2/cache.c | 37 | ||||
-rw-r--r-- | libnl_2/dbg.c | 12 | ||||
-rw-r--r-- | libnl_2/genl/family.c | 44 | ||||
-rw-r--r-- | libnl_2/genl/genl.c | 302 | ||||
-rw-r--r-- | libnl_2/handlers.c | 90 | ||||
-rw-r--r-- | libnl_2/msg.c | 150 | ||||
-rw-r--r-- | libnl_2/netlink.c | 273 | ||||
-rw-r--r-- | libnl_2/object.c | 33 | ||||
-rw-r--r-- | libnl_2/socket.c | 141 |
13 files changed, 1450 insertions, 0 deletions
diff --git a/libnl_2/.gitignore b/libnl_2/.gitignore new file mode 100644 index 0000000..d4ca744 --- /dev/null +++ b/libnl_2/.gitignore @@ -0,0 +1,2 @@ +include/netlink/version.h.in +cscope.* diff --git a/libnl_2/Android.mk b/libnl_2/Android.mk new file mode 100644 index 0000000..3721fc6 --- /dev/null +++ b/libnl_2/Android.mk @@ -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) diff --git a/libnl_2/README b/libnl_2/README new file mode 100644 index 0000000..14db6db --- /dev/null +++ b/libnl_2/README @@ -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 diff --git a/libnl_2/attr.c b/libnl_2/attr.c new file mode 100644 index 0000000..2ef7590 --- /dev/null +++ b/libnl_2/attr.c @@ -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; +} diff --git a/libnl_2/cache.c b/libnl_2/cache.c new file mode 100644 index 0000000..c21974d --- /dev/null +++ b/libnl_2/cache.c @@ -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) +{ + +} + + diff --git a/libnl_2/dbg.c b/libnl_2/dbg.c new file mode 100644 index 0000000..9764de6 --- /dev/null +++ b/libnl_2/dbg.c @@ -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); +} diff --git a/libnl_2/genl/family.c b/libnl_2/genl/family.c new file mode 100644 index 0000000..1beee6e --- /dev/null +++ b/libnl_2/genl/family.c @@ -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; + +} + diff --git a/libnl_2/genl/genl.c b/libnl_2/genl/genl.c new file mode 100644 index 0000000..1a39c6a --- /dev/null +++ b/libnl_2/genl/genl.c @@ -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; +} diff --git a/libnl_2/handlers.c b/libnl_2/handlers.c new file mode 100644 index 0000000..48dcab4 --- /dev/null +++ b/libnl_2/handlers.c @@ -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); +} diff --git a/libnl_2/msg.c b/libnl_2/msg.c new file mode 100644 index 0000000..1303e8a --- /dev/null +++ b/libnl_2/msg.c @@ -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; +} diff --git a/libnl_2/netlink.c b/libnl_2/netlink.c new file mode 100644 index 0000000..ee3d600 --- /dev/null +++ b/libnl_2/netlink.c @@ -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; + +} diff --git a/libnl_2/object.c b/libnl_2/object.c new file mode 100644 index 0000000..c53accf --- /dev/null +++ b/libnl_2/object.c @@ -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); +} + + diff --git a/libnl_2/socket.c b/libnl_2/socket.c new file mode 100644 index 0000000..e94eb9e --- /dev/null +++ b/libnl_2/socket.c @@ -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); +} |