genl.c revision 2d9a6281ca4fdacd88098f3b135b89b9d0c65226
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *	http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* NOTICE: This is a clean room re-implementation of libnl */
18
19#include <errno.h>
20#include <unistd.h>
21#include <stdio.h>
22#include <sys/time.h>
23#include <linux/netlink.h>
24#include <netlink/genl/ctrl.h>
25#include <netlink/genl/family.h>
26#include "netlink-types.h"
27
28/* Get head of attribute data. */
29struct nlattr *genlmsg_attrdata(const struct genlmsghdr *gnlh, int hdrlen)
30{
31	return (struct nlattr *) \
32		((char *) gnlh + GENL_HDRLEN + NLMSG_ALIGN(hdrlen));
33
34}
35
36/* Get length of attribute data. */
37int genlmsg_attrlen(const struct genlmsghdr *gnlh, int hdrlen)
38{
39	struct nlattr *nla;
40	struct nlmsghdr *nlh;
41
42	nla = genlmsg_attrdata(gnlh, hdrlen);
43	nlh = (struct nlmsghdr *) ((char *) gnlh - NLMSG_HDRLEN);
44	return (char *) nlmsg_tail(nlh) - (char *) nla;
45}
46
47/* Add generic netlink header to netlink message. */
48void *genlmsg_put(struct nl_msg *msg, uint32_t pid, uint32_t seq, int family,
49		int hdrlen, int flags, uint8_t cmd, uint8_t version)
50{
51	int new_size;
52	struct nlmsghdr *nlh;
53	struct timeval tv;
54	struct genlmsghdr *gmh;
55
56	/* Make sure nl_msg has enough space */
57	new_size = NLMSG_HDRLEN + GENL_HDRLEN + hdrlen;
58	if ((sizeof(struct nl_msg) + new_size) > msg->nm_size)
59		goto fail;
60
61	/* Fill in netlink header */
62	nlh = msg->nm_nlh;
63	nlh->nlmsg_len = new_size;
64	nlh->nlmsg_type = family;
65	nlh->nlmsg_pid = getpid();
66	nlh->nlmsg_flags = flags | NLM_F_REQUEST | NLM_F_ACK;
67
68	/* Get current time for sequence number */
69	if (gettimeofday(&tv, NULL))
70		nlh->nlmsg_seq = 1;
71	else
72		nlh->nlmsg_seq = (int) tv.tv_sec;
73
74	/* Setup genlmsghdr in new message */
75	gmh = (struct genlmsghdr *) ((char *)nlh + NLMSG_HDRLEN);
76	gmh->cmd = (__u8) cmd;
77	gmh->version = version;
78
79	return gmh;
80fail:
81	return NULL;
82
83}
84
85/* Socket has already been alloced to connect it to kernel? */
86int genl_connect(struct nl_sock *sk)
87{
88	return nl_connect(sk, NETLINK_GENERIC);
89
90}
91
92int genl_ctrl_alloc_cache(struct nl_sock *sock, struct nl_cache **result)
93{
94	int rc = -1;
95	int nl80211_genl_id = -1;
96	char sendbuf[sizeof(struct nlmsghdr)+sizeof(struct genlmsghdr)];
97	struct nlmsghdr nlmhdr;
98	struct genlmsghdr gmhhdr;
99	struct iovec sendmsg_iov;
100	struct msghdr msg;
101	int num_char;
102	const int RECV_BUF_SIZE = getpagesize();
103	char *recvbuf;
104	struct iovec recvmsg_iov;
105	int nl80211_flag = 0, nlm_f_multi = 0, nlmsg_done = 0;
106	struct nlmsghdr *nlh;
107
108	/* REQUEST GENERIC NETLINK FAMILY ID */
109	/* Message buffer */
110	nlmhdr.nlmsg_len = sizeof(sendbuf);
111	nlmhdr.nlmsg_type = NETLINK_GENERIC;
112	nlmhdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP;
113	nlmhdr.nlmsg_seq = sock->s_seq_next;
114	nlmhdr.nlmsg_pid = sock->s_local.nl_pid;
115
116	/* Generic netlink header */
117	memset(&gmhhdr, 0, sizeof(gmhhdr));
118	gmhhdr.cmd = CTRL_CMD_GETFAMILY;
119	gmhhdr.version = CTRL_ATTR_FAMILY_ID;
120
121	/* Combine netlink and generic netlink headers */
122	memcpy(&sendbuf[0], &nlmhdr, sizeof(nlmhdr));
123	memcpy(&sendbuf[0]+sizeof(nlmhdr), &gmhhdr, sizeof(gmhhdr));
124
125	/* Create IO vector with Netlink message */
126	sendmsg_iov.iov_base = &sendbuf;
127	sendmsg_iov.iov_len = sizeof(sendbuf);
128
129	/* Socket message */
130	msg.msg_name = (void *) &sock->s_peer;
131	msg.msg_namelen = sizeof(sock->s_peer);
132	msg.msg_iov = &sendmsg_iov;
133	msg.msg_iovlen = 1; /* Only sending one iov */
134	msg.msg_control = NULL;
135	msg.msg_controllen = 0;
136	msg.msg_flags = 0;
137
138	/* Send message and verify sent */
139	num_char = sendmsg(sock->s_fd, &msg, 0);
140	if (num_char == -1)
141		return -errno;
142
143	/* RECEIVE GENL CMD RESPONSE */
144
145	/* Create receive iov buffer */
146	recvbuf = (char *) malloc(RECV_BUF_SIZE);
147
148	/* Attach to iov */
149	recvmsg_iov.iov_base = recvbuf;
150	recvmsg_iov.iov_len = RECV_BUF_SIZE;
151
152	msg.msg_iov = &recvmsg_iov;
153	msg.msg_iovlen = 1;
154
155	/***************************************************************/
156	/* Receive message. If multipart message, keep receiving until */
157	/* message type is NLMSG_DONE				       */
158	/***************************************************************/
159
160	do {
161
162		int recvmsg_len, nlmsg_rem;
163
164		/* Receive message */
165		memset(recvbuf, 0, RECV_BUF_SIZE);
166		recvmsg_len = recvmsg(sock->s_fd, &msg, 0);
167
168		/* Make sure receive successful */
169		if (recvmsg_len < 0) {
170			rc = -errno;
171			goto error_recvbuf;
172		}
173
174		/* Parse nlmsghdr */
175		nlmsg_for_each_msg(nlh, (struct nlmsghdr *) recvbuf, \
176				recvmsg_len, nlmsg_rem) {
177			struct nlattr *nla;
178			int nla_rem;
179
180			/* Check type */
181			switch (nlh->nlmsg_type) {
182			case NLMSG_DONE:
183				goto return_genl_id;
184				break;
185			case NLMSG_ERROR:
186
187				/* Should check nlmsgerr struct received */
188				fprintf(stderr, "Receive message error\n");
189				goto error_recvbuf;
190			case NLMSG_OVERRUN:
191				fprintf(stderr, "Receive data partly lost\n");
192				goto error_recvbuf;
193			case NLMSG_MIN_TYPE:
194			case NLMSG_NOOP:
195				break;
196			default:
197				break;
198			}
199
200
201
202			/* Check flags */
203			if (nlh->nlmsg_flags & NLM_F_MULTI)
204				nlm_f_multi = 1;
205			else
206				nlm_f_multi = 0;
207
208			if (nlh->nlmsg_type & NLMSG_DONE)
209				nlmsg_done = 1;
210			else
211				nlmsg_done = 0;
212
213			/* Iteratve over attributes */
214			nla_for_each_attr(nla,
215					nlmsg_attrdata(nlh, GENL_HDRLEN),
216					nlmsg_attrlen(nlh, GENL_HDRLEN),
217					nla_rem){
218
219				/* If this family is nl80211 */
220				if (nla->nla_type == CTRL_ATTR_FAMILY_NAME &&
221					!strcmp((char *)nla_data(nla),
222						"nl80211"))
223					nl80211_flag = 1;
224
225				/* Save the family id */
226				else if (nl80211_flag &&
227					nla->nla_type == CTRL_ATTR_FAMILY_ID) {
228					nl80211_genl_id =
229						*((int *)nla_data(nla));
230					nl80211_flag = 0;
231				}
232
233			}
234
235		}
236
237	} while (nlm_f_multi && !nlmsg_done);
238
239return_genl_id:
240	/* Return family id as cache pointer */
241	*result = (struct nl_cache *) nl80211_genl_id;
242	rc = 0;
243error_recvbuf:
244	free(recvbuf);
245error:
246	return rc;
247}
248
249/* Checks the netlink cache to find family reference by name string */
250/* NOTE: Caller needs to call genl_family_put() when done with *
251 * returned object */
252struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, \
253					const char *name)
254{
255	struct genl_family *gf = (struct genl_family *) \
256		malloc(sizeof(struct genl_family));
257	if (!gf)
258		goto fail;
259	memset(gf, 0, sizeof(*gf));
260
261	/* Add ref */
262	gf->ce_refcnt++;
263
264	/* Overriding cache pointer as family id for now */
265	gf->gf_id = (uint16_t) ((uint32_t) cache);
266	strncpy(gf->gf_name, name, GENL_NAMSIZ);
267
268	return gf;
269fail:
270	return NULL;
271
272}
273
274int genl_ctrl_resolve(struct nl_sock *sk, const char *name)
275{
276	struct nl_cache *cache = NULL;
277	struct genl_family *gf = NULL;
278	int id = -1;
279
280	/* Hack to support wpa_supplicant */
281	if (strcmp(name, "nlctrl") == 0)
282		return NETLINK_GENERIC;
283
284	if (strcmp(name, "nl80211") != 0) {
285		fprintf(stderr, "%s is not supported\n", name);
286		return id;
287	}
288
289	if (!genl_ctrl_alloc_cache(sk, &cache)) {
290		gf = genl_ctrl_search_by_name(cache, name);
291		if (gf)
292			id = genl_family_get_id(gf);
293	}
294
295	if (gf)
296		genl_family_put(gf);
297	if (cache)
298		nl_cache_free(cache);
299
300	return id;
301}
302