4
* Copyright 2012-2013 Alex <alex@linuxonly.ru>
6
* This program is free software: you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 3 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses/>.
26
#include <sys/types.h>
27
#include <sys/socket.h>
29
#include <arpa/inet.h>
30
#include <netinet/tcp.h>
31
#include <netinet/in.h>
32
#include <linux/netlink.h>
33
#include <linux/inet_diag.h>
42
#define MMGUI_NETLINK_INTERNAL_SEQUENCE_NUMBER 100000
44
static gboolean mmgui_netlink_numeric_name(gchar *dirname)
46
if ((dirname == NULL) || (!*dirname)) return FALSE;
49
if (!isdigit(*dirname)) {
59
static gboolean mmgui_netlink_process_access(gchar *dirname, uid_t uid)
64
if (!mmgui_netlink_numeric_name(dirname)) return FALSE;
66
memset(fullpath, 0, sizeof(fullpath));
68
sprintf(fullpath, "/proc/%s", dirname);
70
if (stat(fullpath, &pathstat) == -1) {
74
if (pathstat.st_uid != uid) {
81
static gboolean mmgui_netlink_socket_access(gchar *dirname, gchar *sockname, guint inode)
86
if (!mmgui_netlink_numeric_name(sockname)) return FALSE;
88
memset(fullpath, 0, sizeof(fullpath));
90
sprintf(fullpath, "/proc/%s/fd/%s", dirname, sockname);
92
if (stat(fullpath, &fdstat) == -1) {
96
if (((fdstat.st_mode & S_IFMT) == S_IFSOCK) && (fdstat.st_ino == inode)) {
103
static gchar *mmgui_netlink_process_name(gchar *dirname, gchar *appname, gsize appsize)
109
if ((dirname == NULL) || (dirname[0] == '\0')) return NULL;
110
if ((appname == NULL) || (appsize == 0)) return NULL;
112
memset(fpath, 0, sizeof(fpath));
113
sprintf(fpath, "/proc/%s/exe", dirname);
115
linkchars = readlink(fpath, appname, appsize-1);
117
if (linkchars == 0) {
118
memset(fpath, 0, sizeof(fpath));
119
sprintf(fpath, "/proc/%s/comm", dirname);
121
fd = open(fpath, O_RDONLY);
123
linkchars = read(fd, appname, appsize-1);
130
appname[linkchars] = '\0';
132
for (i=linkchars; i>=0; i--) {
133
if (appname[i] == '/') {
134
memmove(appname+0, appname+i+1, linkchars-i-1);
140
appname[linkchars] = '\0';
145
static gboolean mmgui_netlink_get_process(guint inode, gchar *appname, gsize namesize, pid_t *apppid)
147
DIR *procdir, *fddir;
148
struct dirent *procde, *fdde;
149
char fdirpath[128], fdpath[128];
151
if ((appname == NULL) || (namesize == 0) || (apppid == NULL)) return FALSE;
153
procdir = opendir("/proc");
154
if (procdir != NULL) {
155
while (procde = readdir(procdir)) {
156
if (mmgui_netlink_process_access(procde->d_name, getuid())) {
157
memset(fdirpath, 0, sizeof(fdirpath));
158
sprintf(fdirpath, "/proc/%s/fd", procde->d_name);
159
//enumerate file descriptors
160
fddir = opendir(fdirpath);
162
while (fdde = readdir(fddir)) {
163
if (mmgui_netlink_socket_access(procde->d_name, fdde->d_name, inode)) {
164
//printf("%s:%s (%s)\n", procde->d_name, fdde->d_name, nlconnections_process_name(procde->d_name, appname, sizeof(appname)));
165
*apppid = atoi(procde->d_name);
166
mmgui_netlink_process_name(procde->d_name, appname, namesize);
182
gboolean mmgui_netlink_terminate_application(pid_t pid)
184
if (kill(pid, 0) == 0) {
185
if (kill(pid, SIGTERM) == 0) {
193
gchar *mmgui_netlink_socket_state(guchar state)
196
case TCP_ESTABLISHED:
197
return "Established";
223
void mmgui_netlink_hash_destroy(gpointer data)
225
mmgui_netlink_connection_t connection;
227
connection = (mmgui_netlink_connection_t)data;
229
if (connection == NULL) return;
231
if (connection->appname != NULL) g_free(connection->appname);
232
if (connection->dsthostname != NULL) g_free(connection->dsthostname);
237
static gboolean mmgui_netlink_hash_clear_foreach(gpointer key, gpointer value, gpointer user_data)
239
mmgui_netlink_connection_t connection;
242
connection = (mmgui_netlink_connection_t)value;
243
currenttime = *(time_t *)user_data;
245
if (connection->updatetime == currenttime) {
248
//printf("Remove: %u\n", socket->inode);
253
gboolean mmgui_netlink_request_connections_list(mmgui_netlink_t netlink, guint family)
255
struct _mmgui_netlink_connection_info_request request;
258
if ((netlink == NULL) || ((!(family & AF_INET)) && (!(family & AF_INET6)))) return FALSE;
260
memset(&request.msgheader, 0, sizeof(struct nlmsghdr));
262
request.msgheader.nlmsg_len = sizeof(struct _mmgui_netlink_connection_info_request);
263
request.msgheader.nlmsg_type = TCPDIAG_GETSOCK;
264
request.msgheader.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
265
request.msgheader.nlmsg_pid = getpid();
266
request.msgheader.nlmsg_seq = 0;
268
memset(&request.nlreq, 0, sizeof(struct inet_diag_req));
269
request.nlreq.idiag_family = family;
270
request.nlreq.idiag_states = ((1 << TCP_CLOSING + 1) - 1);
272
request.msgheader.nlmsg_len = NLMSG_ALIGN(request.msgheader.nlmsg_len);
274
status = send(netlink->connsocketfd, &request, request.msgheader.nlmsg_len, 0);
283
gboolean mmgui_netlink_read_connections_list(mmgui_netlink_t netlink, gchar *data, gsize datasize)
286
struct nlmsghdr *msgheader;
287
struct inet_diag_msg *entry;
288
mmgui_netlink_connection_t connection;
289
struct hostent *dsthost;
290
gchar srcbuf[INET6_ADDRSTRLEN];
291
gchar dstbuf[INET6_ADDRSTRLEN];
295
if ((netlink == NULL) || (data == NULL) || (datasize == 0)) return FALSE;
298
currenttime = time(NULL);
301
for (msgheader = (struct nlmsghdr *)data; NLMSG_OK(msgheader, (unsigned int)datasize); msgheader = NLMSG_NEXT(msgheader, datasize)) {
302
if ((msgheader->nlmsg_type == NLMSG_ERROR) || (msgheader->nlmsg_type == NLMSG_DONE)) {
305
//New connections list
306
if (msgheader->nlmsg_type == TCPDIAG_GETSOCK) {
307
entry = (struct inet_diag_msg *)NLMSG_DATA(msgheader);
309
if ((entry->idiag_uid == netlink->userid) || (netlink->userid == 0)) {
310
if (!g_hash_table_contains(netlink->connections, (gconstpointer)&entry->idiag_inode)) {
312
if (mmgui_netlink_get_process(entry->idiag_inode, appname, sizeof(appname), &apppid)) {
313
connection = g_new(struct _mmgui_netlink_connection, 1);
314
connection->inode = entry->idiag_inode;
315
connection->family = entry->idiag_family;
316
connection->userid = entry->idiag_uid;
317
connection->updatetime = currenttime;
318
connection->dqueue = entry->idiag_rqueue + entry->idiag_wqueue;
319
connection->state = entry->idiag_state;
320
connection->srcport = ntohs(entry->id.idiag_sport);
321
g_snprintf(connection->srcaddr, sizeof(connection->srcaddr), "%s:%u", inet_ntop(entry->idiag_family, entry->id.idiag_src, srcbuf, INET6_ADDRSTRLEN), ntohs(entry->id.idiag_sport));
322
g_snprintf(connection->dstaddr, sizeof(connection->dstaddr), "%s:%u", inet_ntop(entry->idiag_family, entry->id.idiag_dst, dstbuf, INET6_ADDRSTRLEN), ntohs(entry->id.idiag_dport));
323
connection->appname = g_strdup(appname);
324
connection->apppid = apppid;
325
connection->dsthostname = NULL;
326
/*dsthost = gethostbyaddr(entry->id.idiag_dst, sizeof(entry->id.idiag_dst), entry->idiag_family);
327
if (dsthost != NULL) {
328
connection->dsthostname = g_strdup(dsthost->h_name);
330
connection->dsthostname = g_strdup(connection->dstaddr);
332
g_hash_table_insert(netlink->connections, (gpointer)&connection->inode, connection);
333
g_debug("Connection added: inode %u\n", entry->idiag_inode);
336
//Update connection information (state, buffers fill, time)
337
connection = g_hash_table_lookup(netlink->connections, (gconstpointer)&entry->idiag_inode);
338
if (connection != NULL) {
339
connection->updatetime = currenttime;
340
connection->dqueue = entry->idiag_rqueue + entry->idiag_wqueue;
341
connection->state = entry->idiag_state;
342
g_debug("Connection updated: inode %u\n", entry->idiag_inode);
350
//Remove connections that disappear
351
g_hash_table_foreach_remove(netlink->connections, mmgui_netlink_hash_clear_foreach, ¤ttime);
356
gboolean mmgui_netlink_request_interface_statistics(mmgui_netlink_t netlink, gchar *interface)
358
struct _mmgui_netlink_interface_info_request request;
362
if ((netlink == NULL) || (interface == NULL)) return FALSE;
363
if ((netlink->intsocketfd == -1) || (interface[0] == '\0')) return FALSE;
365
ifindex = if_nametoindex(interface);
367
if ((ifindex == 0) && (errno == ENXIO)) return FALSE;
369
memset(&request, 0, sizeof(request));
371
request.msgheader.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
372
request.msgheader.nlmsg_type = RTM_GETLINK;
373
request.msgheader.nlmsg_flags = NLM_F_REQUEST;
374
request.msgheader.nlmsg_seq = MMGUI_NETLINK_INTERNAL_SEQUENCE_NUMBER;
375
request.msgheader.nlmsg_pid = getpid();
377
request.ifinfo.ifi_family = AF_UNSPEC;
378
request.ifinfo.ifi_index = ifindex;
379
request.ifinfo.ifi_type = 0;
380
request.ifinfo.ifi_flags = 0;
381
request.ifinfo.ifi_change = 0xFFFFFFFF;
383
request.msgheader.nlmsg_len = NLMSG_ALIGN(request.msgheader.nlmsg_len);
385
status = send(netlink->intsocketfd, &request, request.msgheader.nlmsg_len, 0);
394
gboolean mmgui_netlink_read_interface_event(mmgui_netlink_t netlink, gchar *data, gsize datasize, mmgui_netlink_interface_event_t event)
396
struct nlmsghdr *msgheader;
397
struct ifinfomsg *ifi;
399
struct rtnl_link_stats *ifstats;
400
struct rtnl_link_stats64 *ifstats64;
401
gchar ifname[IFNAMSIZ];
402
gboolean have64bitstats;
404
if ((netlink == NULL) || (data == NULL) || (datasize == 0) || (event == NULL)) return FALSE;
407
for (msgheader = (struct nlmsghdr *)data; NLMSG_OK(msgheader, (unsigned int)datasize); msgheader = NLMSG_NEXT(msgheader, datasize)) {
408
if ((msgheader->nlmsg_type == NLMSG_ERROR) || (msgheader->nlmsg_type == NLMSG_DONE)) {
412
if ((msgheader->nlmsg_type == RTM_NEWLINK) || (msgheader->nlmsg_type == RTM_DELLINK) || (msgheader->nlmsg_type == RTM_GETLINK)) {
413
ifi = NLMSG_DATA(msgheader);
415
//Copy valuable data first
416
event->running = (ifi->ifi_flags & IFF_RUNNING);
417
event->up = (ifi->ifi_flags & IFF_UP);
418
if (if_indextoname(ifi->ifi_index, ifname) != NULL) {
419
strncpy(event->ifname, ifname, sizeof(event->ifname));
421
event->type = MMGUI_NETLINK_INTERFACE_EVENT_TYPE_UNKNOWN;
422
//Detrmine type of event
423
if (msgheader->nlmsg_seq == MMGUI_NETLINK_INTERNAL_SEQUENCE_NUMBER) {
424
event->type = MMGUI_NETLINK_INTERFACE_EVENT_TYPE_STATS;
426
if (msgheader->nlmsg_type == RTM_NEWLINK) {
427
event->type = MMGUI_NETLINK_INTERFACE_EVENT_TYPE_ADD;
428
g_debug("[%u] New interface state: Running: %s, Up: %s, Name: %s\n", msgheader->nlmsg_seq, (ifi->ifi_flags & IFF_RUNNING) ? "Yes" : "No", (ifi->ifi_flags & IFF_UP) ? "Yes" : "No", if_indextoname(ifi->ifi_index, ifname));
429
} else if (msgheader->nlmsg_type == RTM_DELLINK) {
430
event->type = MMGUI_NETLINK_INTERFACE_EVENT_TYPE_REMOVE;
431
g_debug("[%u] Deleted interface state: Running: %s, Up: %s, Name: %s\n", msgheader->nlmsg_seq, (ifi->ifi_flags & IFF_RUNNING) ? "Yes" : "No", (ifi->ifi_flags & IFF_UP) ? "Yes" : "No", if_indextoname(ifi->ifi_index, ifname));
432
} else if (msgheader->nlmsg_type == RTM_GETLINK) {
433
event->type = MMGUI_NETLINK_INTERFACE_EVENT_TYPE_STATS;
434
g_debug("[%u] Requested interface state: Running: %s, Up: %s, Name: %s\n", msgheader->nlmsg_seq, (ifi->ifi_flags & IFF_RUNNING) ? "Yes" : "No", (ifi->ifi_flags & IFF_UP) ? "Yes" : "No", if_indextoname(ifi->ifi_index, ifname));
437
//If 64bit traffic statistics values are not available, 32bit values will be used anyway
438
have64bitstats = FALSE;
439
//Use tags to get additional data
440
while (RTA_OK(rta, msgheader->nlmsg_len)) {
441
if (rta->rta_type == IFLA_IFNAME) {
442
strncpy(event->ifname, (char *)RTA_DATA(rta), sizeof(event->ifname));
443
g_debug("Tag: Device name: %s\n", (char *)RTA_DATA(rta));
444
} else if (rta->rta_type == IFLA_STATS) {
445
ifstats = (struct rtnl_link_stats *)RTA_DATA(rta);
446
if (!have64bitstats) {
447
event->rxbytes = ifstats->rx_bytes;
448
event->txbytes = ifstats->tx_bytes;
449
if (!(event->type & MMGUI_NETLINK_INTERFACE_EVENT_TYPE_STATS)) {
450
event->type |= MMGUI_NETLINK_INTERFACE_EVENT_TYPE_STATS;
453
g_debug("Tag: Interface Statistics (32bit): RX: %u, TX: %u\n", ifstats->rx_bytes, ifstats->tx_bytes);
454
} else if (rta->rta_type == IFLA_STATS64) {
455
ifstats64 = (struct rtnl_link_stats64 *)RTA_DATA(rta);
456
event->rxbytes = ifstats64->rx_bytes;
457
event->txbytes = ifstats64->tx_bytes;
458
have64bitstats = TRUE;
459
if (!(event->type & MMGUI_NETLINK_INTERFACE_EVENT_TYPE_STATS)) {
460
event->type |= MMGUI_NETLINK_INTERFACE_EVENT_TYPE_STATS;
462
g_debug("Tag: Interface Statistics (64bit): RX: %llu, TX: %llu\n", ifstats64->rx_bytes, ifstats64->tx_bytes);
463
} else if (rta->rta_type == IFLA_LINK) {
464
g_debug("Tag: Link type\n");
465
} else if (rta->rta_type == IFLA_ADDRESS) {
466
g_debug("Tag: interface L2 address\n");
467
} else if (rta->rta_type == IFLA_UNSPEC) {
468
g_debug("Tag: unspecified\n");
470
g_debug("Tag: %u\n", rta->rta_type);
472
rta = RTA_NEXT(rta, msgheader->nlmsg_len);
480
gint mmgui_netlink_get_connections_monitoring_socket_fd(mmgui_netlink_t netlink)
482
if (netlink == NULL) return -1;
484
return netlink->connsocketfd;
487
gint mmgui_netlink_get_interfaces_monitoring_socket_fd(mmgui_netlink_t netlink)
489
if (netlink == NULL) return -1;
491
return netlink->intsocketfd;
494
struct sockaddr_nl *mmgui_netlink_get_connections_monitoring_socket_address(mmgui_netlink_t netlink)
496
if (netlink == NULL) return NULL;
498
return &(netlink->connaddr);
501
struct sockaddr_nl *mmgui_netlink_get_interfaces_monitoring_socket_address(mmgui_netlink_t netlink)
503
if (netlink == NULL) return NULL;
505
return &(netlink->intaddr);
508
GHashTable *mmgui_netlink_get_connections_list(mmgui_netlink_t netlink)
510
if (netlink == NULL) return NULL;
512
return netlink->connections;
515
void mmgui_netlink_close(mmgui_netlink_t netlink)
517
if (netlink == NULL) return;
519
if (netlink->connsocketfd != -1) {
520
close(netlink->connsocketfd);
521
g_hash_table_destroy(netlink->connections);
524
if (netlink->intsocketfd != -1) {
525
close(netlink->intsocketfd);
531
mmgui_netlink_t mmgui_netlink_open(void)
533
mmgui_netlink_t netlink;
535
netlink = g_new(struct _mmgui_netlink, 1);
537
#ifndef NETLINK_SOCK_DIAG
538
netlink->connsocketfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG);
540
netlink->connsocketfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
543
if (netlink->connsocketfd != -1) {
544
memset(&netlink->connaddr, 0, sizeof(struct sockaddr_nl));
545
netlink->connaddr.nl_family = AF_NETLINK;
546
netlink->connaddr.nl_pid = getpid();
547
netlink->connaddr.nl_groups = 0;
549
netlink->userid = getuid();
551
netlink->connections = g_hash_table_new_full(g_int_hash, g_int_equal, NULL, (GDestroyNotify)mmgui_netlink_hash_destroy);
553
netlink->connections = NULL;
554
g_debug("Failed to open connections monitoring netlink socket\n");
557
netlink->intsocketfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
559
if (netlink->intsocketfd != -1) {
560
memset(&netlink->intaddr, 0, sizeof(netlink->intaddr));
561
netlink->intaddr.nl_family = AF_NETLINK;
562
netlink->intaddr.nl_groups = RTMGRP_LINK;
563
netlink->intaddr.nl_pid = getpid();
565
if (bind(netlink->intsocketfd, (struct sockaddr *)&(netlink->intaddr), sizeof(netlink->intaddr)) == -1) {
566
g_debug("Failed to bind network interfaces monitoring netlink socket\n");
567
close(netlink->intsocketfd);
568
netlink->intsocketfd = -1;
571
g_debug("Failed to open network interfaces monitoring netlink socket\n");