/
linux_netlink.c
323 lines (280 loc) · 8.85 KB
/
linux_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
313
314
315
316
317
318
319
320
321
322
323
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: s; c-basic-offset: 4 -*-
*
* Copyright (C) 2010 Richard Hughes <richard@hughsie.com>
*
* Licensed under the GNU Lesser General Public License Version 2.1
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <glib.h>
#include <gio/gio.h>
//#include <gio/gsocket.h>
//#include <gio/gunixsocketaddress.h>
//#include <linux/netlink.h>
#include <errno.h>
#include <math.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>
#include <string.h>
#include <stdlib.h>
#include "ulatency.h"
#include <sys/types.h>
#include <unistd.h>
#define SEND_MESSAGE_LEN (NLMSG_LENGTH(sizeof(struct cn_msg) + \
sizeof(enum proc_cn_mcast_op)))
#define RECV_MESSAGE_LEN (NLMSG_LENGTH(sizeof(struct cn_msg) + \
sizeof(struct proc_event)))
#define SEND_MESSAGE_SIZE (NLMSG_SPACE(SEND_MESSAGE_LEN))
#define RECV_MESSAGE_SIZE (NLMSG_SPACE(RECV_MESSAGE_LEN))
#define BUFF_SIZE (MAX(MAX(SEND_MESSAGE_SIZE, RECV_MESSAGE_SIZE), 1024))
#define MIN_RECV_SIZE (MIN(SEND_MESSAGE_SIZE, RECV_MESSAGE_SIZE))
/**
* Handle a netlink message. In the event of PROC_EVENT_UID or PROC_EVENT_GID,
* we put the new events on the new event stack for processing when they exist a
* given time
* other events are ignored.
* @param cn_hdr The netlink message
* @return 0 on success, > 0 on error
*/
static int nl_handle_msg(struct cn_msg *cn_hdr)
{
/* The event to consider */
struct proc_event *ev;
/* Return codes */
int ret = 0;
/* Get the event data. We only care about two event types. */
ev = (struct proc_event*)cn_hdr->data;
switch (ev->what) {
// quite seldom events on old processes changing important parameters
case PROC_EVENT_UID:
u_trace("UID Event: PID = %d, tGID = %d, rUID = %d,"
" eUID = %d", ev->event_data.id.process_pid,
ev->event_data.id.process_tgid,
ev->event_data.id.r.ruid,
ev->event_data.id.e.euid);
//process_update_pid(ev->event_data.id.process_pid);
process_new(ev->event_data.id.process_pid, FALSE);
break;
case PROC_EVENT_GID:
u_trace("GID Event: PID = %d, tGID = %d, rGID = %d,"
" eGID = %d", ev->event_data.id.process_pid,
ev->event_data.id.process_tgid,
ev->event_data.id.r.rgid,
ev->event_data.id.e.egid);
//process_update_pid(ev->event_data.id.process_pid);
process_new(ev->event_data.id.process_pid, FALSE);
break;
case PROC_EVENT_EXIT:
u_trace("EXIT Event: PID = %d", ev->event_data.exit.process_pid);
//g_ptr_array_foreach(stack, remove_pid_from_stack, &pid);
// if the pid was found in the new stack, pid is set to 0 to indicate
// the removal
process_remove_by_pid(ev->event_data.exit.process_pid);
break;
case PROC_EVENT_EXEC:
u_trace("EXEC Event: PID = %d, tGID = %d",
ev->event_data.exec.process_pid,
ev->event_data.exec.process_tgid);
process_new_delay(ev->event_data.exec.process_tgid, 0);
break;
case PROC_EVENT_FORK:
u_trace("FORK Event: PARENT = %d PID = %d tGID = %d",
ev->event_data.fork.parent_tgid, ev->event_data.fork.child_pid, ev->event_data.fork.child_tgid);
// we skip new threads for now
// FIXME need filter block to get those events
if(ev->event_data.fork.parent_tgid != ev->event_data.fork.child_pid)
break;
// parent does not mean the parent of the new proc, but the parent of
// the forking process. so we lookup the parent of the forking process
// first
u_proc *rparent = proc_by_pid(ev->event_data.fork.parent_tgid);
if(rparent) {
u_proc_ensure(rparent, BASIC, FALSE);
process_new_delay(ev->event_data.fork.child_tgid, rparent->proc.ppid); //ev->event_data.fork.parent_pid);
} else
process_new_delay(ev->event_data.fork.child_tgid, 0);
break;
default:
return 0;
}
return ret;
}
static gboolean
nl_connection_handler (GSocket *socket, GIOCondition condition, gpointer user_data)
{
GError *error = NULL;
gsize len;
gboolean ret = TRUE;
char buff[BUFF_SIZE];
size_t recv_len;
struct sockaddr_nl from_nla;
socklen_t from_nla_len;
struct nlmsghdr *nlh;
struct sockaddr_nl kern_nla;
struct cn_msg *cn_hdr;
kern_nla.nl_family = AF_NETLINK;
kern_nla.nl_groups = CN_IDX_PROC;
kern_nla.nl_pid = 1;
kern_nla.nl_pad = 0;
memset(buff, 0, sizeof(buff));
from_nla_len = sizeof(from_nla);
memcpy(&from_nla, &kern_nla, sizeof(from_nla));
/* the helper process exited */
// this should not happen to netlink
if ((condition & G_IO_HUP) > 0) {
g_warning ("socket was disconnected");
ret = FALSE;
goto out;
}
/* there is data */
if ((condition & G_IO_IN) > 0) {
len = g_socket_receive (socket, buff, sizeof(buff), NULL, &error);
if (error != NULL) {
g_warning ("failed to get data: %s", error->message);
g_error_free (error);
// no reason to stop
goto out;
}
if (len == ENOBUFS) {
g_warning("NETLINK BUFFER FULL, MESSAGE DROPPED!");
return 0;
}
if (len == 0)
goto out;
nlh = (struct nlmsghdr *)buff;
while (NLMSG_OK(nlh, len)) {
cn_hdr = NLMSG_DATA(nlh);
if (nlh->nlmsg_type == NLMSG_NOOP) {
nlh = NLMSG_NEXT(nlh, recv_len);
continue;
}
if ((nlh->nlmsg_type == NLMSG_ERROR) ||
(nlh->nlmsg_type == NLMSG_OVERRUN))
break;
if (nl_handle_msg(cn_hdr) < 0)
return 1;
if (nlh->nlmsg_type == NLMSG_DONE)
break;
nlh = NLMSG_NEXT(nlh, recv_len);
}
}
out:
return ret;
}
int init_netlink(GMainLoop *loop) {
GSocket *gsocket = NULL;
int socket_fd = 0;
GError *error = NULL;
GSource *source;
struct sockaddr_nl my_nla;
struct nlmsghdr *nl_hdr;
char buff[BUFF_SIZE];
struct cn_msg *cn_hdr;
enum proc_cn_mcast_op *mcop_msg;
/* create socket */
/*
* Create an endpoint for communication. Use the kernel user
* interface device (PF_NETLINK) which is a datagram oriented
* service (SOCK_DGRAM). The protocol used is the connector
* protocol (NETLINK_CONNECTOR)
*/
socket_fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (socket == NULL) {
g_warning ("failed to create socket: %s", error->message);
g_error_free (error);
return 1;
}
my_nla.nl_family = AF_NETLINK;
my_nla.nl_groups = CN_IDX_PROC;
my_nla.nl_pid = getpid();
my_nla.nl_pad = 0;
if (bind(socket_fd, (struct sockaddr *)&my_nla, sizeof(my_nla)) < 0) {
g_warning("binding sk_nl error: %s\n", strerror(errno));
g_warning("realtime monitoring disabled. compile kernel with PROC_EVENTS enabled");
goto out;
}
gsocket = g_socket_new_from_fd(socket_fd, NULL);
if(gsocket == NULL) {
g_warning("can't create socket");
goto out;
}
nl_hdr = (struct nlmsghdr *)buff;
cn_hdr = (struct cn_msg *)NLMSG_DATA(nl_hdr);
mcop_msg = (enum proc_cn_mcast_op*)&cn_hdr->data[0];
g_debug("sending proc connector: PROC_CN_MCAST_LISTEN... ");
memset(buff, 0, sizeof(buff));
*mcop_msg = PROC_CN_MCAST_LISTEN;
/* fill the netlink header */
nl_hdr->nlmsg_len = SEND_MESSAGE_LEN;
nl_hdr->nlmsg_type = NLMSG_DONE;
nl_hdr->nlmsg_flags = 0;
nl_hdr->nlmsg_seq = 0;
nl_hdr->nlmsg_pid = getpid();
/* fill the connector header */
cn_hdr->id.idx = CN_IDX_PROC;
cn_hdr->id.val = CN_VAL_PROC;
cn_hdr->seq = 0;
cn_hdr->ack = 0;
cn_hdr->len = sizeof(enum proc_cn_mcast_op);
g_debug("sending netlink message len=%d, cn_msg len=%d\n",
nl_hdr->nlmsg_len, (int) sizeof(struct cn_msg));
if (send(socket_fd, nl_hdr, nl_hdr->nlmsg_len, 0) != nl_hdr->nlmsg_len) {
g_warning("failed to send proc connector mcast ctl op!: %s\n",
strerror(errno));
}
g_debug("sent\n");
/* socket has data */
source = g_socket_create_source (gsocket, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, NULL);
g_source_set_callback (source, (GSourceFunc) nl_connection_handler, loop, NULL);
g_source_attach (source, NULL);
return 0;
out:
return 1;
}
#if 0
gint
main (void)
{
gboolean ret;
GSocket *gsocket = NULL;
int socket_fd = 0;
GSocketAddress *address = NULL;
GError *error = NULL;
gsize wrote;
GSource *source;
GMainLoop *loop;
struct sockaddr_nl my_nla;
struct nlmsghdr *nl_hdr;
char buff[BUFF_SIZE];
struct cn_msg *cn_hdr;
enum proc_cn_mcast_op *mcop_msg;
g_type_init ();
loop = g_main_loop_new (NULL, FALSE);
init_netlink(loop);
g_debug ("running main loop");
g_main_loop_run (loop);
out:
if (loop != NULL)
g_main_loop_unref (loop);
if (socket != NULL)
g_object_unref (socket);
if (address != NULL)
g_object_unref (address);
return 0;
}
#endif