blob: cc2f88e6832db37851082926f88a87148ffd8949 [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 <string.h>
21#include <unistd.h>
22#include <fcntl.h>
23#include <sys/socket.h>
24#include "netlink-types.h"
25
26#define NL_BUFFER_SZ (32768U)
27
28/* Checks message for completeness and sends it out */
29int nl_send_auto_complete(struct nl_sock *sk, struct nl_msg *msg)
30{
31 struct nlmsghdr *nlh = msg->nm_nlh;
32 struct timeval tv;
33
34 if (!nlh) {
35 int errsv = errno;
36 fprintf(stderr, "Netlink message header is NULL!\n");
37 return -errsv;
38 }
39
40 /* Complete the nl_msg header */
41 if (gettimeofday(&tv, NULL))
42 nlh->nlmsg_seq = 1;
43 else
44 nlh->nlmsg_seq = (int) tv.tv_sec;
45 nlh->nlmsg_pid = sk->s_local.nl_pid;
46 nlh->nlmsg_flags |= NLM_F_REQUEST | NLM_F_ACK;
47
48 return nl_send(sk, msg);
49}
50
51/* Receives a netlink message, allocates a buffer in *buf and stores
52 * the message content. The peer's netlink address is stored in
53 * *nla. The caller is responsible for freeing the buffer allocated in
54 * *buf if a positive value is returned. Interrupted system calls are
55 * handled by repeating the read. The input buffer size is determined
56 * by peeking before the actual read is done */
57int nl_recv(struct nl_sock *sk, struct sockaddr_nl *nla, \
58 unsigned char **buf, struct ucred **creds)
59{
60 int rc = -1;
61 int sk_flags;
62 int RECV_BUF_SIZE;
63 int errsv;
64 struct iovec recvmsg_iov;
65 struct msghdr msg;
66
67 /* Allocate buffer */
68 RECV_BUF_SIZE = getpagesize();
69 *buf = (unsigned char *) malloc(RECV_BUF_SIZE);
70 if (!buf) {
71 rc = -ENOMEM;
72 goto fail;
73 }
74
75 /* Prepare to receive message */
76 recvmsg_iov.iov_base = *buf;
77 recvmsg_iov.iov_len = RECV_BUF_SIZE;
78
79 msg.msg_name = (void *) &sk->s_peer;
80 msg.msg_namelen = sizeof(sk->s_peer);
81 msg.msg_iov = &recvmsg_iov;
82 msg.msg_iovlen = 1;
83 msg.msg_control = NULL;
84 msg.msg_controllen = 0;
85 msg.msg_flags = 0;
86
87 /* Make non blocking and then restore previous setting */
88 sk_flags = fcntl(sk->s_fd, F_GETFL, 0);
89 fcntl(sk->s_fd, F_SETFL, O_NONBLOCK);
90 rc = recvmsg(sk->s_fd, &msg, 0);
91 errsv = errno;
92 fcntl(sk->s_fd, F_SETFL, sk_flags);
93
94 if (rc < 0)
95 rc = -errsv;
96
97fail:
98 return rc;
99}
100
101/* Receive a set of messages from a netlink socket */
102/* NOTE: Does not currently support callback replacements!!! */
103int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb)
104{
105 struct sockaddr_nl nla;
106 struct ucred *creds;
107
108 int rc, cb_rc = NL_OK, done = 0;
109
110 do {
111
112 unsigned char *buf;
113 int i, rem, flags;
114 struct nlmsghdr *nlh;
115 struct nlmsgerr *nlme;
116 struct nl_msg *msg;
117
118 done = 0;
119 rc = nl_recv(sk, &nla, &buf, &creds);
120 if (rc < 0)
121 break;
122
123 nlmsg_for_each_msg(nlh, (struct nlmsghdr *) buf, rc, rem) {
124
125 if (rc <= 0 || cb_rc == NL_STOP)
126 break;
127
128 /* Check for callbacks */
129
130 msg = (struct nl_msg *)malloc(sizeof(struct nl_msg));
131 memset(msg, 0, sizeof(*msg));
132 msg->nm_nlh = nlh;
133
134 /* Check netlink message type */
135
136 switch (msg->nm_nlh->nlmsg_type) {
137 case NLMSG_ERROR: /* Used for ACK too */
138 /* Certainly we should be doing some
139 * checking here to make sure this
140 * message is intended for us */
141 nlme = nlmsg_data(msg->nm_nlh);
142 if (nlme->error == 0)
143 msg->nm_nlh->nlmsg_flags |= NLM_F_ACK;
144
145 rc = nlme->error;
146 cb_rc = cb->cb_err(&nla, nlme, cb->cb_err_arg);
147 nlme = NULL;
148 break;
149
150 case NLMSG_DONE:
151 done = 1;
152
153 case NLMSG_OVERRUN:
154 case NLMSG_NOOP:
155 default:
156 break;
157 };
158
159 for (i = 0; i <= NL_CB_TYPE_MAX; i++) {
160
161 if (cb->cb_set[i]) {
162 switch (i) {
163 case NL_CB_VALID:
164 if (rc > 0)
165 cb_rc = cb->cb_set[i](msg, cb->cb_args[i]);
166 break;
167
168 case NL_CB_FINISH:
169 if ((msg->nm_nlh->nlmsg_flags & NLM_F_MULTI) &&
170 (msg->nm_nlh->nlmsg_type & NLMSG_DONE))
171 cb_rc = cb->cb_set[i](msg, cb->cb_args[i]);
172
173 break;
174
175 case NL_CB_ACK:
176 if (msg->nm_nlh->nlmsg_flags & NLM_F_ACK)
177 cb_rc = cb->cb_set[i](msg, cb->cb_args[i]);
178
179 break;
180 default:
181 break;
182 }
183 }
184 }
185
186 free(msg);
187 if (done)
188 break;
189 }
190
191 free(buf);
192 buf = NULL;
193
194 if (done)
195 break;
196 } while (rc > 0 && cb_rc != NL_STOP);
197
198success:
199fail:
200 return rc;
201}
202
203/* Send raw data over netlink socket */
204int nl_send(struct nl_sock *sk, struct nl_msg *msg)
205{
206 struct nlmsghdr *nlh = nlmsg_hdr(msg);
207 struct iovec msg_iov;
208
209 /* Create IO vector with Netlink message */
210 msg_iov.iov_base = nlh;
211 msg_iov.iov_len = nlh->nlmsg_len;
212
213 return nl_send_iovec(sk, msg, &msg_iov, 1);
214}
215
216/* Send netlink message */
217int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg,
218 struct iovec *iov, unsigned iovlen)
219{
220 int rc;
221
222 /* Socket message */
223 struct msghdr mh = {
224 .msg_name = (void *) &sk->s_peer,
225 .msg_namelen = sizeof(sk->s_peer),
226 .msg_iov = iov,
227 .msg_iovlen = iovlen,
228 .msg_control = NULL,
229 .msg_controllen = 0,
230 .msg_flags = 0
231 };
232
233 /* Send message and verify sent */
234 rc = nl_sendmsg(sk, (struct nl_msg *) &mh, 0);
235 if (rc < 0)
236 fprintf(stderr, "Error sending netlink message: %d\n", errno);
237 return rc;
238
239}
240
241/* Send netlink message with control over sendmsg() message header */
242int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr)
243{
244 return sendmsg(sk->s_fd, (struct msghdr *) msg, (int) hdr);
245}
246
247/* Create and connect netlink socket */
248int nl_connect(struct nl_sock *sk, int protocol)
249{
250 struct sockaddr addr;
251 socklen_t addrlen;
252 int rc;
253
254 /* Create RX socket */
255 sk->s_fd = socket(PF_NETLINK, SOCK_RAW, protocol);
256 if (sk->s_fd < 0)
257 return -errno;
258
259 /* Set size of RX and TX buffers */
260 if (nl_socket_set_buffer_size(sk, NL_BUFFER_SZ, NL_BUFFER_SZ) < 0)
261 return -errno;
262
263 /* Bind RX socket */
264 rc = bind(sk->s_fd, (struct sockaddr *)&sk->s_local, \
265 sizeof(sk->s_local));
266 if (rc < 0)
267 return -errno;
268 addrlen = sizeof(addr);
269 getsockname(sk->s_fd, &addr, &addrlen);
270
271 return 0;
272
273}