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