This repository has been archived by the owner on Dec 15, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathnetlink.c
312 lines (258 loc) · 7.37 KB
/
netlink.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/*
* netlink.c - interface with kernel's netlink facility
*
* Copyright 2003 PathScale, Inc.
* Copyright 2003, 2004, 2005 Bryan O'Sullivan
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License,
* version 2, as published by the Free Software Foundation. You are
* forbidden from redistributing or modifying it under the terms of
* any other license, including other versions of the GNU General
* Public License.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* Portions of this file are based on code from Alexey Kuznetsov's
* iproute2 package.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>
#include "netplug.h"
static int seq, dump;
void
netlink_request_dump(int fd)
{
struct {
struct nlmsghdr hdr;
struct rtgenmsg msg;
} req;
struct sockaddr_nl addr;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
memset(&req, 0, sizeof(req));
req.hdr.nlmsg_len = sizeof(req);
req.hdr.nlmsg_type = RTM_GETLINK;
req.hdr.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST;
req.hdr.nlmsg_pid = 0;
req.hdr.nlmsg_seq = dump = ++seq;
req.msg.rtgen_family = AF_UNSPEC;
if (sendto(fd, (void*) &req, sizeof(req), 0,
(struct sockaddr *) &addr, sizeof(addr)) == -1) {
do_log(LOG_ERR, "Could not request interface dump: %m");
exit(1);
}
}
typedef enum {
ok, /* handle the message */
skip, /* skip the message */
done, /* all's well, no more processing */
bail, /* something's wrong, no more processing */
user, /* packet came from someone naughty in user space */
} todo;
static todo
receive(int fd, struct msghdr *msg, int *status)
{
*status = recvmsg(fd, msg, 0);
if (*status == -1) {
if (errno == EINTR) {
return skip;
}
if (errno == EAGAIN) {
/* XXX when will this ever happen? */
return done;
}
do_log(LOG_ERR, "Netlink receive error: %m");
return done;
}
else if (*status == 0) {
do_log(LOG_ERR, "Unexpected EOF on netlink");
return bail;
}
if (msg->msg_namelen != sizeof(struct sockaddr_nl)) {
do_log(LOG_ERR, "Unexpected sender address length: got %d, expected %d",
msg->msg_namelen, (int) sizeof(struct sockaddr_nl));
return done;
}
if (((struct sockaddr_nl *) msg->msg_name)->nl_pid != 0) {
do_log(LOG_ERR, "Netlink packet came from pid %d, not from kernel",
((struct sockaddr_nl *) msg->msg_name)->nl_pid);
return user;
}
return ok;
}
/*
* Return values:
*
* 0 - exit calling loop
* !0 - we have a valid event
*/
int
netlink_listen(int fd, netlink_callback callback, void *arg)
{
char buf[8192];
struct iovec iov = { buf, sizeof(buf) };
struct sockaddr_nl addr;
struct msghdr msg = {
.msg_name = (void *) &addr,
.msg_namelen = sizeof(addr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_pid = 0;
addr.nl_groups = 0;
while (1) {
int status;
switch (receive(fd, &msg, &status)) {
case user:
case done:
return 1;
case bail:
return 0;
case skip:
continue;
case ok:
break;
}
struct nlmsghdr *hdr;
for (hdr = (struct nlmsghdr*) buf; status >= sizeof(*hdr); ) {
int len = hdr->nlmsg_len;
int l = len - sizeof(*hdr);
if (l < 0 || len > status) {
if (msg.msg_flags & MSG_TRUNC) {
do_log(LOG_ERR, "Truncated message");
return 1;
}
do_log(LOG_ERR, "Malformed netlink message");
return 1;
}
if (callback) {
int err;
if ((err = callback(hdr, arg)) == -1) {
do_log(LOG_ERR, "Callback failed");
goto outer;
}
}
status -= NLMSG_ALIGN(len);
hdr = (struct nlmsghdr *) ((char *) hdr + NLMSG_ALIGN(len));
}
if (msg.msg_flags & MSG_TRUNC) {
do_log(LOG_ERR, "Message truncated");
continue;
}
if (status) {
do_log(LOG_ERR, "!!!Remnant of size %d", status);
return 1;
}
outer:
/* do nothing */;
}
}
void
netlink_receive_dump(int fd, netlink_callback callback, void *arg)
{
char buf[8192];
struct sockaddr_nl addr;
struct iovec iov = { buf, sizeof(buf) };
struct msghdr msg = {
.msg_name = (void *) &addr,
.msg_namelen = sizeof(addr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
while (1) {
int status;
switch (receive(fd, &msg, &status)) {
case bail:
case done:
exit(1);
case user:
case skip:
continue;
case ok:
break;
}
struct nlmsghdr *hdr = (struct nlmsghdr *) buf;
while (NLMSG_OK(hdr, status)) {
if (hdr->nlmsg_seq != dump) {
do_log(LOG_ERR, "Skipping junk");
goto skip_it;
}
if (hdr->nlmsg_type == NLMSG_DONE) {
return;
}
else if (hdr->nlmsg_type == NLMSG_ERROR) {
struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA(hdr);
if (hdr->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
do_log(LOG_ERR, "Netlink message truncated");
} else {
errno = -err->error;
do_log(LOG_ERR, "Error from rtnetlink: %m");
}
exit(1);
}
if (callback) {
int err;
if ((err = callback(hdr, arg)) == -1) {
do_log(LOG_ERR, "Callback failed");
exit(1);
}
}
skip_it:
hdr = NLMSG_NEXT(hdr, status);
}
if (msg.msg_flags & MSG_TRUNC) {
do_log(LOG_ERR, "Message truncated");
exit(1);
}
if (status) {
do_log(LOG_ERR, "Dangling remnant of size %d!", status);
exit(1);
}
}
}
int
netlink_open(void)
{
int fd;
if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) {
do_log(LOG_ERR, "Could not create netlink socket: %m");
exit(1);
}
close_on_exec(fd);
struct sockaddr_nl addr;
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
addr.nl_groups = RTMGRP_LINK;
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
do_log(LOG_ERR, "Could not bind netlink socket: %m");
exit(1);
}
socklen_t addr_len = sizeof(addr);
if (getsockname(fd, (struct sockaddr *) &addr, &addr_len) == -1) {
do_log(LOG_ERR, "Could not get socket details: %m");
exit(1);
}
if (addr_len != sizeof(addr)) {
do_log(LOG_ERR, "Our netlink socket size does not match the kernel's!");
exit(1);
}
if (addr.nl_family != AF_NETLINK) {
do_log(LOG_ERR, "The kernel has given us an insane address family!");
exit(1);
}
return fd;
}
/*
* Local variables:
* c-file-style: "stroustrup"
* End:
*/