blob: dd207171a59d1dedc3d89aa36b391e920570c573 [file] [log] [blame]
Frank Makered6b39c2011-05-23 21:14:58 -07001/*
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 gmhhdr.cmd = CTRL_CMD_GETFAMILY;
116 gmhhdr.version = CTRL_ATTR_FAMILY_ID;
117
118 /* Combine netlink and generic netlink headers */
119 memcpy(&sendbuf[0], &nlmhdr, sizeof(nlmhdr));
120 memcpy(&sendbuf[0]+sizeof(nlmhdr), &gmhhdr, sizeof(gmhhdr));
121
122 /* Create IO vector with Netlink message */
123 sendmsg_iov.iov_base = &sendbuf;
124 sendmsg_iov.iov_len = sizeof(sendbuf);
125
126 /* Socket message */
127 msg.msg_name = (void *) &sock->s_peer;
128 msg.msg_namelen = sizeof(sock->s_peer);
129 msg.msg_iov = &sendmsg_iov;
130 msg.msg_iovlen = 1; /* Only sending one iov */
131 msg.msg_control = NULL;
132 msg.msg_controllen = 0;
133 msg.msg_flags = 0;
134
135 /* Send message and verify sent */
136 num_char = sendmsg(sock->s_fd, &msg, 0);
137 if (num_char == -1)
138 return -errno;
139
140 /* RECEIVE GENL CMD RESPONSE */
141
142 /* Create receive iov buffer */
143 recvbuf = (char *) malloc(RECV_BUF_SIZE);
144
145 /* Attach to iov */
146 recvmsg_iov.iov_base = recvbuf;
147 recvmsg_iov.iov_len = RECV_BUF_SIZE;
148
149 msg.msg_iov = &recvmsg_iov;
150 msg.msg_iovlen = 1;
151
152 /***************************************************************/
153 /* Receive message. If multipart message, keep receiving until */
154 /* message type is NLMSG_DONE */
155 /***************************************************************/
156
157 do {
158
159 int recvmsg_len, nlmsg_rem;
160
161 /* Receive message */
162 memset(recvbuf, 0, RECV_BUF_SIZE);
163 recvmsg_len = recvmsg(sock->s_fd, &msg, 0);
164
165 /* Make sure receive successful */
166 if (recvmsg_len < 0) {
167 rc = -errno;
168 goto error_recvbuf;
169 }
170
171 /* Parse nlmsghdr */
172 nlmsg_for_each_msg(nlh, (struct nlmsghdr *) recvbuf, \
173 recvmsg_len, nlmsg_rem) {
174 struct nlattr *nla;
175 int nla_rem;
176
177 /* Check type */
178 switch (nlh->nlmsg_type) {
179 case NLMSG_DONE:
180 goto return_genl_id;
181 break;
182 case NLMSG_ERROR:
183
184 /* Should check nlmsgerr struct received */
185 fprintf(stderr, "Receive message error\n");
186 goto error_recvbuf;
187 case NLMSG_OVERRUN:
188 fprintf(stderr, "Receive data partly lost\n");
189 goto error_recvbuf;
190 case NLMSG_MIN_TYPE:
191 case NLMSG_NOOP:
192 break;
193 default:
194 break;
195 }
196
197
198
199 /* Check flags */
200 if (nlh->nlmsg_flags & NLM_F_MULTI)
201 nlm_f_multi = 1;
202 else
203 nlm_f_multi = 0;
204
205 if (nlh->nlmsg_type & NLMSG_DONE)
206 nlmsg_done = 1;
207 else
208 nlmsg_done = 0;
209
210 /* Iteratve over attributes */
211 nla_for_each_attr(nla,
212 nlmsg_attrdata(nlh, GENL_HDRLEN),
213 nlmsg_attrlen(nlh, GENL_HDRLEN),
214 nla_rem){
215
216 /* If this family is nl80211 */
217 if (nla->nla_type == CTRL_ATTR_FAMILY_NAME &&
218 !strcmp((char *)nla_data(nla),
219 "nl80211"))
220 nl80211_flag = 1;
221
222 /* Save the family id */
223 else if (nl80211_flag &&
224 nla->nla_type == CTRL_ATTR_FAMILY_ID)
225 nl80211_genl_id = \
226 *((int *)nla_data(nla));
227
228 }
229
230 }
231
232 } while (nlm_f_multi && !nlmsg_done);
233
234return_genl_id:
235 /* Return family id as cache pointer */
236 *result = (struct nl_cache *) nl80211_genl_id;
237 rc = 0;
238error_recvbuf:
239 free(recvbuf);
240error:
241 return rc;
242}
243
244/* Checks the netlink cache to find family reference by name string */
245/* NOTE: Caller needs to call genl_family_put() when done with *
246 * returned object */
247struct genl_family *genl_ctrl_search_by_name(struct nl_cache *cache, \
248 const char *name)
249{
250 /* TODO: When will we release this memory ? */
251 struct genl_family *gf = (struct genl_family *) \
252 malloc(sizeof(struct genl_family));
253 if (!gf)
254 goto fail;
255 memset(gf, 0, sizeof(*gf));
256
257 /* Add ref */
258 gf->ce_refcnt++;
259
260 /* Overriding cache pointer as family id for now */
261 gf->gf_id = (uint16_t) ((uint32_t) cache);
262 strcpy(gf->gf_name, "nl80211");
263
264 return gf;
265fail:
266 return NULL;
267
268}
269
270int genl_ctrl_resolve(struct nl_sock *sk, const char *name)
271{
272 /* Hack to support wpa_supplicant */
273 if (strcmp(name, "nlctrl") == 0)
274 return NETLINK_GENERIC;
275 else {
276 int errsv = errno;
277 fprintf(stderr, \
278 "Only nlctrl supported by genl_ctrl_resolve!\n");
279 return -errsv;
280 }
281
282}
283