| /* |
| * Copyright (C) 2009 Red Hat Inc. |
| * |
| * Arnaldo Carvalho de Melo <acme@redhat.com> |
| * |
| * Using code from the iproute2 package which was written mostly by: |
| * |
| * Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> |
| * |
| * This application is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the Free |
| * Software Foundation; version 2. |
| * |
| * This application 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. |
| */ |
| #include <Python.h> |
| |
| #include <errno.h> |
| #include <sys/socket.h> |
| #include <linux/types.h> |
| #include <linux/netlink.h> |
| #include <linux/rtnetlink.h> |
| #include <arpa/inet.h> |
| #include <netinet/tcp.h> |
| #include <linux/inet_diag.h> |
| #include <linux/sock_diag.h> |
| |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <string.h> |
| #include <netdb.h> |
| #include <dirent.h> |
| #include <fnmatch.h> |
| #include <fcntl.h> |
| #include <sys/ioctl.h> |
| |
| #include "diag_filter.h" |
| #ifndef __unused |
| #define __unused __attribute__ ((unused)) |
| #endif |
| #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) |
| |
| #ifndef NETLINK_INET_DIAG |
| #define NETLINK_INET_DIAG 4 |
| #endif |
| |
| /* From libnetlink */ |
| static int parse_rtattr(struct rtattr *tb[], int max, |
| struct rtattr *rta, int len) |
| { |
| memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); |
| while (RTA_OK(rta, len)) { |
| if (rta->rta_type <= max) |
| tb[rta->rta_type] = rta; |
| rta = RTA_NEXT(rta,len); |
| } |
| if (len) |
| fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len); |
| return 0; |
| } |
| |
| enum { |
| SS_UNKNOWN, |
| SS_ESTABLISHED, |
| SS_SYN_SENT, |
| SS_SYN_RECV, |
| SS_FIN_WAIT1, |
| SS_FIN_WAIT2, |
| SS_TIME_WAIT, |
| SS_CLOSE, |
| SS_CLOSE_WAIT, |
| SS_LAST_ACK, |
| SS_LISTEN, |
| SS_CLOSING, |
| SS_MAX |
| }; |
| |
| static const char *sstate_name[] = { |
| "UNKNOWN", |
| [SS_ESTABLISHED] = "ESTAB", |
| [SS_SYN_SENT] = "SYN-SENT", |
| [SS_SYN_RECV] = "SYN-RECV", |
| [SS_FIN_WAIT1] = "FIN-WAIT-1", |
| [SS_FIN_WAIT2] = "FIN-WAIT-2", |
| [SS_TIME_WAIT] = "TIME-WAIT", |
| [SS_CLOSE] = "UNCONN", |
| [SS_CLOSE_WAIT] = "CLOSE-WAIT", |
| [SS_LAST_ACK] = "LAST-ACK", |
| [SS_LISTEN] = "LISTEN", |
| [SS_CLOSING] = "CLOSING", |
| }; |
| |
| #define SS_ALL ((1 << SS_MAX) - 1) |
| |
| static const int default_states = SS_ALL & ~((1 << SS_LISTEN) | |
| (1 << SS_CLOSE) | |
| (1 << SS_TIME_WAIT) | |
| (1 << SS_SYN_RECV)); |
| |
| static const int listen_states = (1<<SS_LISTEN) | (1<<SS_CLOSE); |
| |
| static const char *tmr_name[] = { |
| "off", |
| "on", |
| "keepalive", |
| "timewait", |
| "persist", |
| "unknown" |
| }; |
| |
| struct inet_socket { |
| PyObject_HEAD |
| struct inet_diag_msg msg; |
| struct inet_diag_meminfo *ext_memory; |
| struct tcp_info *ext_protocol; |
| struct user_ent *proc; |
| char *ext_congestion; |
| }; |
| |
| /* destructor */ |
| static void inet_socket__dealloc(struct inet_socket *self) |
| { |
| free(self->ext_memory); |
| free(self->ext_protocol); |
| free(self->proc); |
| free(self->ext_congestion); |
| PyObject_Del(self); |
| } |
| |
| /* prints query object in human readable format */ |
| static int inet_socket__print(struct inet_socket *self, FILE *fp, |
| int flags __unused) |
| { |
| char bufsaddr[1024]; |
| char bufdaddr[1024]; |
| inet_ntop(self->msg.idiag_family, &self->msg.id.idiag_src, |
| bufsaddr, sizeof(bufsaddr)); |
| inet_ntop(self->msg.idiag_family, &self->msg.id.idiag_dst, |
| bufdaddr, sizeof(bufdaddr)); |
| fprintf(fp, "<inet_socket state=%s rqueue=%d wqueue=%d " |
| "saddr=%s sport=%d " |
| "daddr=%s dport=%d>", |
| sstate_name[self->msg.idiag_state], |
| self->msg.idiag_rqueue, |
| self->msg.idiag_wqueue, |
| bufsaddr, ntohs(self->msg.id.idiag_sport), |
| bufdaddr, ntohs(self->msg.id.idiag_dport)); |
| return 0; |
| } |
| |
| /* process and user lookup */ |
| struct user_ent { |
| struct user_ent *next; |
| unsigned int ino; |
| int pid; |
| int fd; |
| char process[4096]; |
| }; |
| |
| #define USER_ENT_HASH_SIZE 256 |
| struct user_ent *user_ent_hash[USER_ENT_HASH_SIZE] = {0}; |
| |
| int show_users = 0; |
| |
| static int user_ent_hashfn(unsigned int ino) |
| { |
| int val = (ino >> 24) ^ (ino >> 16) ^ (ino >> 8) ^ ino; |
| |
| return val & (USER_ENT_HASH_SIZE - 1); |
| } |
| |
| static void user_ent_add(unsigned int ino, int pid, int fd) |
| { |
| struct user_ent *p, **pp; |
| |
| p = malloc(sizeof(struct user_ent)); |
| if (!p) |
| abort(); |
| p->next = NULL; |
| p->ino = ino; |
| p->pid = pid; |
| p->fd = fd; |
| |
| pp = &user_ent_hash[user_ent_hashfn(ino)]; |
| p->next = *pp; |
| *pp = p; |
| } |
| |
| static void user_ent_hash_build(void) |
| { |
| const char *root = getenv("PROC_ROOT") ? : "/proc/"; |
| struct dirent *d; |
| char name[1024]; |
| int nameoff; |
| DIR *dir; |
| |
| strcpy(name, root); |
| if (strlen(name) == 0 || name[strlen(name)-1] != '/') |
| strcat(name, "/"); |
| |
| nameoff = strlen(name); |
| |
| dir = opendir(name); |
| if (!dir) |
| return; |
| |
| while ((d = readdir(dir)) != NULL) { |
| struct dirent *d1; |
| int pid, pos; |
| DIR *dir1; |
| char crap; |
| |
| if (sscanf(d->d_name, "%d%c", &pid, &crap) != 1) |
| continue; |
| |
| sprintf(name + nameoff, "%d/fd/", pid); |
| pos = strlen(name); |
| if ((dir1 = opendir(name)) == NULL) |
| continue; |
| |
| while ((d1 = readdir(dir1)) != NULL) { |
| const char *pattern = "socket:["; |
| unsigned int ino; |
| char lnk[64]; |
| int fd; |
| ssize_t link_len; |
| |
| if (sscanf(d1->d_name, "%d%c", &fd, &crap) != 1) |
| continue; |
| |
| sprintf(name+pos, "%d", fd); |
| |
| link_len = readlink(name, lnk, sizeof(lnk)-1); |
| if (link_len == -1) |
| continue; |
| lnk[link_len] = '\0'; |
| |
| if (strncmp(lnk, pattern, strlen(pattern))) |
| continue; |
| |
| sscanf(lnk, "socket:[%u]", &ino); |
| |
| user_ent_add(ino, pid, fd); |
| } |
| closedir(dir1); |
| } |
| closedir(dir); |
| } |
| |
| static int find_users(unsigned ino, struct user_ent *found) |
| { |
| struct user_ent *p; |
| int cnt = 0; |
| |
| if (!ino) |
| return 0; |
| |
| p = user_ent_hash[user_ent_hashfn(ino)]; |
| while (p) { |
| if (p->ino != ino) |
| goto next; |
| |
| found->ino = p->ino; |
| found->fd = p->fd; |
| found->pid = p->pid; |
| |
| cnt++; |
| next: |
| p = p->next; |
| } |
| |
| //get the full process path |
| char tmp[4096]; |
| const char *root = getenv("PROC_ROOT") ? : "/proc/"; |
| |
| snprintf(tmp, sizeof(tmp), "%s/%d/exe", root, found->pid); |
| char *bin_path = canonicalize_file_name(tmp); |
| if ( bin_path != NULL ) { |
| strncpy(found->process, bin_path, 4096); |
| free(bin_path); |
| } |
| |
| return cnt; |
| } |
| |
| static void clear_users(void) |
| { |
| struct user_ent *p; |
| struct user_ent *temp; |
| int i; |
| |
| for (i = 0; i < USER_ENT_HASH_SIZE; i++) { |
| if ( user_ent_hash[i] != 0 ) { |
| p = user_ent_hash[i]; |
| while ( p != NULL ) { |
| temp = p; |
| p = p->next; |
| free(temp); |
| } |
| } |
| user_ent_hash[i] = 0; |
| } |
| } |
| |
| static char inet_socket__daddr_doc__[] = |
| "daddr() -- get internet socket destination address"; |
| static PyObject *inet_socket__daddr(struct inet_socket *self, |
| PyObject *args __unused) |
| { |
| char buf[1024]; |
| inet_ntop(self->msg.idiag_family, &self->msg.id.idiag_dst, |
| buf, sizeof(buf)); |
| return PyString_FromString(buf); |
| } |
| |
| static char inet_socket__saddr_doc__[] = |
| "saddr() -- get internet socket source address"; |
| static PyObject *inet_socket__saddr(struct inet_socket *self, |
| PyObject *args __unused) |
| { |
| char buf[1024]; |
| inet_ntop(self->msg.idiag_family, &self->msg.id.idiag_src, |
| buf, sizeof(buf)); |
| return PyString_FromString(buf); |
| } |
| |
| static char inet_socket__sock_doc__[] = |
| "sock() -- get internet socket pointer"; |
| static PyObject *inet_socket__sock(struct inet_socket *self, |
| PyObject *args __unused) |
| { |
| return Py_BuildValue("l", (((unsigned long)self->msg.id.idiag_cookie[0]) << 32) | |
| self->msg.id.idiag_cookie[1]); |
| } |
| |
| static char inet_socket__congestion_algorithm_doc__[] = |
| "congestion_algorithm() -- get internet socket congestion algorithm being used"; |
| static PyObject *inet_socket__congestion_algorithm(struct inet_socket *self, |
| PyObject *args __unused) |
| { |
| if (self->ext_congestion == NULL) { |
| PyErr_SetString(PyExc_OSError, |
| "no congestion algorithm on this socket"); |
| return NULL; |
| } |
| return PyString_FromString(self->ext_congestion); |
| } |
| |
| static char inet_socket__process_doc__[] = |
| "process() -- get name of process"; |
| static PyObject *inet_socket__process(struct inet_socket *self, |
| PyObject *args __unused) |
| { |
| if (self->proc == NULL) { |
| PyErr_SetString(PyExc_OSError, |
| "no process found or proc not specified"); |
| return NULL; |
| } |
| return PyString_FromString(self->proc->process); |
| } |
| |
| #define INET_SOCK__STR_METHOD(name, field, table, doc) \ |
| static char inet_socket__##name##_doc__[] = #name "() -- " doc; \ |
| static PyObject *inet_socket__##name(struct inet_socket *self, \ |
| PyObject *args __unused) \ |
| { return PyString_FromString(table[self->msg.field]); } |
| |
| #define INET_SOCK__INT_METHOD(name, field, doc) \ |
| static char inet_socket__##name##_doc__[] = #name "() -- " doc; \ |
| static PyObject *inet_socket__##name(struct inet_socket *self, \ |
| PyObject *args __unused) \ |
| { return Py_BuildValue("i", self->msg.field); } |
| |
| #define INET_SOCK__NET_INT_METHOD(name, field, doc) \ |
| static char inet_socket__##name##_doc__[] = #name "() -- " doc; \ |
| static PyObject *inet_socket__##name(struct inet_socket *self, \ |
| PyObject *args __unused) \ |
| { return Py_BuildValue("i", ntohs(self->msg.field)); } |
| |
| #define INET_SOCK__EXT_INT_METHOD(name, ext, field, doc) \ |
| static char inet_socket__##name##_doc__[] = #name "() -- " doc; \ |
| static PyObject *inet_socket__##name(struct inet_socket *self, \ |
| PyObject *args __unused) \ |
| { \ |
| if (self->ext_##ext == NULL) { \ |
| PyErr_SetString(PyExc_OSError, \ |
| "extension not requested"); \ |
| return NULL; \ |
| } \ |
| return Py_BuildValue("l", self->ext_##ext->field); \ |
| } |
| |
| #define INET_SOCK__PROC_INT_METHOD(name, field, doc) \ |
| static char inet_socket__##name##_doc__[] = #name "() -- " doc; \ |
| static PyObject *inet_socket__##name(struct inet_socket *self, \ |
| PyObject *args __unused) \ |
| { return Py_BuildValue("i", self->proc->field); } |
| |
| INET_SOCK__NET_INT_METHOD(dport, id.idiag_dport, |
| "get internet socket destination port"); |
| INET_SOCK__NET_INT_METHOD(sport, id.idiag_sport, |
| "get internet socket source port"); |
| INET_SOCK__INT_METHOD(bound_iface, id.idiag_if, |
| "get interface this socket is bound to"); |
| INET_SOCK__INT_METHOD(family, idiag_family, |
| "get address family of this socket"); |
| INET_SOCK__INT_METHOD(receive_queue, idiag_rqueue, |
| "get internet socket receive queue length"); |
| INET_SOCK__INT_METHOD(write_queue, idiag_wqueue, |
| "get internet socket write queue length"); |
| INET_SOCK__INT_METHOD(inode, idiag_inode, |
| "get internet socket associated inode"); |
| INET_SOCK__STR_METHOD(state, idiag_state, sstate_name, |
| "get internet socket state"); |
| INET_SOCK__STR_METHOD(timer, idiag_timer, tmr_name, |
| "get internet socket running timer"); |
| INET_SOCK__INT_METHOD(timer_expiration, idiag_expires, |
| "get expiration time (in ms) for running timer"); |
| INET_SOCK__INT_METHOD(retransmissions, idiag_retrans, |
| "get connection retransmissions timer"); |
| INET_SOCK__INT_METHOD(uid, idiag_uid, |
| "get connection owner user id"); |
| INET_SOCK__EXT_INT_METHOD(receive_queue_memory, memory, idiag_rmem, |
| "get memory in bytes allocated for socket receive queue"); |
| INET_SOCK__EXT_INT_METHOD(write_queue_used_memory, memory, idiag_wmem, |
| "get number of bytes queued from socket write queue"); |
| INET_SOCK__EXT_INT_METHOD(write_queue_memory, memory, idiag_tmem, |
| "get number of bytes allocated for the socket write queue"); |
| INET_SOCK__EXT_INT_METHOD(forward_alloc, memory, idiag_fmem, |
| "memory in bytes a socket can allocate before the " |
| "socket buffer autotuning routines enter memory pressure"); |
| INET_SOCK__EXT_INT_METHOD(congestion_state, protocol, tcpi_ca_state, |
| "get socket congestion state"); |
| INET_SOCK__EXT_INT_METHOD(windows_probes_out, protocol, tcpi_probes, |
| "get number of unanswered 0 window probes"); |
| INET_SOCK__EXT_INT_METHOD(protocol_options, protocol, tcpi_options, |
| "get protocol specific options being used in the socket"); |
| INET_SOCK__EXT_INT_METHOD(receive_window_scale_shift, protocol, tcpi_rcv_wscale, |
| "get receive window scale shift used in this socket"); |
| INET_SOCK__EXT_INT_METHOD(send_window_scale_shift, protocol, tcpi_snd_wscale, |
| "get send window scale shift used in this socket"); |
| INET_SOCK__EXT_INT_METHOD(rto, protocol, tcpi_rto, |
| "get retransmission timeout used on this socket"); |
| INET_SOCK__EXT_INT_METHOD(rtt, protocol, tcpi_rtt, |
| "get round trip time calculated on this socket"); |
| INET_SOCK__EXT_INT_METHOD(rttvar, protocol, tcpi_rttvar, |
| "get round trip time variation calculated on this socket"); |
| INET_SOCK__EXT_INT_METHOD(ato, protocol, tcpi_ato, |
| "get socket ack timeout value"); |
| INET_SOCK__EXT_INT_METHOD(cwnd, protocol, tcpi_snd_cwnd, |
| "get socket congestion window"); |
| INET_SOCK__EXT_INT_METHOD(ssthresh, protocol, tcpi_snd_ssthresh, |
| "get socket slow start threshold"); |
| INET_SOCK__PROC_INT_METHOD(pid, pid, |
| "get process id"); |
| INET_SOCK__PROC_INT_METHOD(fd, fd, |
| "get file descriptor"); |
| |
| #define INET_SOCK__METHOD(name) { \ |
| .ml_name = #name, \ |
| .ml_meth = (PyCFunction)inet_socket__##name, \ |
| .ml_doc = inet_socket__##name##_doc__, \ |
| } |
| |
| static struct PyMethodDef inet_socket__methods[] = { |
| INET_SOCK__METHOD(bound_iface), |
| INET_SOCK__METHOD(daddr), |
| INET_SOCK__METHOD(saddr), |
| INET_SOCK__METHOD(dport), |
| INET_SOCK__METHOD(sport), |
| INET_SOCK__METHOD(sock), |
| INET_SOCK__METHOD(family), |
| INET_SOCK__METHOD(receive_queue), |
| INET_SOCK__METHOD(write_queue), |
| INET_SOCK__METHOD(inode), |
| INET_SOCK__METHOD(state), |
| INET_SOCK__METHOD(timer), |
| INET_SOCK__METHOD(timer_expiration), |
| INET_SOCK__METHOD(retransmissions), |
| INET_SOCK__METHOD(uid), |
| INET_SOCK__METHOD(receive_queue_memory), |
| INET_SOCK__METHOD(write_queue_used_memory), |
| INET_SOCK__METHOD(write_queue_memory), |
| INET_SOCK__METHOD(forward_alloc), |
| INET_SOCK__METHOD(congestion_state), |
| INET_SOCK__METHOD(windows_probes_out), |
| INET_SOCK__METHOD(protocol_options), |
| INET_SOCK__METHOD(receive_window_scale_shift), |
| INET_SOCK__METHOD(send_window_scale_shift), |
| INET_SOCK__METHOD(congestion_algorithm), |
| INET_SOCK__METHOD(rto), |
| INET_SOCK__METHOD(rtt), |
| INET_SOCK__METHOD(rttvar), |
| INET_SOCK__METHOD(ato), |
| INET_SOCK__METHOD(cwnd), |
| INET_SOCK__METHOD(ssthresh), |
| INET_SOCK__METHOD(process), |
| INET_SOCK__METHOD(pid), |
| INET_SOCK__METHOD(fd), |
| { .ml_name = NULL, } |
| }; |
| |
| static PyObject *inet_socket__getattr(struct inet_socket *self, char *name) |
| { |
| /* module name */ |
| if (!strcmp(name, "__module__")) |
| return PyString_FromString("_inet_socket"); |
| |
| /* class name */ |
| if (!strcmp(name, "__class__")) |
| return PyString_FromString("inet_socket"); |
| |
| /* seeks name in methods (fallback) */ |
| return Py_FindMethod(inet_socket__methods, (PyObject *)self, name); |
| } |
| |
| static PyTypeObject inet_socket__type = { |
| PyObject_HEAD_INIT(NULL) |
| .tp_name = "inet_socket", |
| .tp_basicsize = sizeof(struct inet_socket), |
| .tp_dealloc = (destructor)inet_socket__dealloc, |
| .tp_print = (printfunc)inet_socket__print, |
| .tp_getattr = (getattrfunc)inet_socket__getattr, |
| }; |
| |
| /* constructor */ |
| static PyObject *inet_socket__new(struct inet_diag_msg *r, int nlmsg_len, struct user_ent *proc) |
| { |
| struct inet_socket *self; |
| |
| self = PyObject_NEW(struct inet_socket, &inet_socket__type); |
| if (self == NULL) |
| return NULL; |
| |
| self->msg = *r; |
| if (self->msg.idiag_timer >= ARRAY_SIZE(tmr_name) - 1) { |
| /* Unknown timer */ |
| self->msg.idiag_timer = ARRAY_SIZE(tmr_name) - 1; |
| } |
| |
| self->ext_memory = NULL; |
| self->ext_protocol = NULL; |
| self->ext_congestion = NULL; |
| self->proc = NULL; |
| |
| if (nlmsg_len) { |
| struct rtattr *tb[INET_DIAG_MAX + 1]; |
| |
| parse_rtattr(tb, INET_DIAG_MAX, (struct rtattr *)(r + 1), |
| nlmsg_len); |
| |
| if (tb[INET_DIAG_MEMINFO]) { |
| struct inet_diag_meminfo *minfo = RTA_DATA(tb[INET_DIAG_MEMINFO]); |
| |
| self->ext_memory = malloc(sizeof(*self->ext_memory)); |
| if (self->ext_memory == NULL) |
| goto out_err; |
| |
| *self->ext_memory = *minfo; |
| } |
| |
| if (tb[INET_DIAG_INFO]) { |
| struct tcp_info *info = RTA_DATA(tb[INET_DIAG_INFO]); |
| size_t len = RTA_PAYLOAD(tb[INET_DIAG_INFO]); |
| |
| self->ext_protocol = malloc(sizeof(*self->ext_protocol)); |
| if (self->ext_protocol == NULL) |
| goto out_err; |
| |
| /* workaround for older kernels with less fields */ |
| if (len < sizeof(*info)) |
| memset(self->ext_protocol + len, 0, |
| sizeof(*info) - len); |
| |
| *self->ext_protocol = *info; |
| } |
| |
| if (tb[INET_DIAG_CONG]) { |
| self->ext_congestion = strdup(RTA_DATA(tb[INET_DIAG_CONG])); |
| if (self->ext_congestion == NULL) |
| goto out_err; |
| } |
| |
| if( proc != NULL) { |
| self->proc = malloc(sizeof(*self->proc)); |
| if (self->proc == NULL) |
| goto out_err; |
| self->proc->ino = proc->ino; |
| self->proc->pid = proc->pid; |
| self->proc->fd = proc->fd; |
| strcpy(self->proc->process, proc->process); |
| } |
| } |
| |
| free(proc); |
| return (PyObject *)self; |
| out_err: |
| PyErr_SetNone(PyExc_MemoryError); |
| Py_XDECREF(self); |
| return NULL; |
| } |
| |
| struct inet_diag { |
| PyObject_HEAD |
| int socket; /* NETLINK socket */ |
| char *bytecode; /* NETLINK filter */ |
| struct diag_filter *filter; /* NETLINK filter */ |
| char buf[8192]; |
| struct nlmsghdr *h; |
| size_t len; |
| }; |
| |
| /* destructor */ |
| static void inet_diag__dealloc(struct inet_diag *self) |
| { |
| close(self->socket); |
| clear_users(); |
| free(self->bytecode); |
| PyObject_Del(self); |
| } |
| |
| /* prints query object in human readable format */ |
| static int inet_diag__print(struct inet_diag *self __unused, |
| FILE *fp, int flags __unused) |
| { |
| if (self->socket >= 0) |
| fprintf(fp, "<Opened inet_diag socket>"); |
| else |
| fprintf(fp, "<Closed large object>"); |
| |
| return 0; |
| } |
| |
| static char inet_diag__get_doc__[] = |
| "read() -- read from inet_diag_sock. "; |
| static PyObject *inet_diag__get(struct inet_diag *self, PyObject *args __unused) |
| { |
| try_again_nlmsg_ok: |
| if (!NLMSG_OK(self->h, self->len)) { |
| struct sockaddr_nl nladdr; |
| try_again_recvmsg: |
| { |
| struct iovec iov[1] = { |
| [0] = { |
| .iov_base = self->buf, |
| .iov_len = sizeof(self->buf), |
| }, |
| }; |
| struct msghdr msg = { |
| .msg_name = &nladdr, |
| .msg_namelen = sizeof(nladdr), |
| .msg_iov = iov, |
| .msg_iovlen = 1, |
| }; |
| int len = recvmsg(self->socket, &msg, 0); |
| |
| if (len < 0) { |
| if (errno == EINTR) |
| goto try_again_recvmsg; |
| out_eof: |
| close(self->socket); |
| PyErr_SetString(PyExc_OSError, strerror(errno)); |
| return NULL; |
| } |
| |
| if (len == 0) { /* EOF, how to signal properly? */ |
| close(self->socket); |
| PyErr_SetNone(PyExc_EOFError); |
| return NULL; |
| } |
| |
| self->len = len; |
| self->h = (void *)self->buf; |
| } |
| } |
| |
| if (self->h->nlmsg_seq != 123456) { |
| self->h = NLMSG_NEXT(self->h, self->len); |
| goto try_again_nlmsg_ok; |
| } |
| |
| if (self->h->nlmsg_type == NLMSG_DONE) |
| goto out_eof; |
| |
| if (self->h->nlmsg_type == NLMSG_ERROR) { |
| struct nlmsgerr *err = NLMSG_DATA(self->h); |
| if (self->h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) { |
| PyErr_SetString(PyExc_OSError, "message truncated"); |
| close(self->socket); |
| } else { |
| errno = -err->error; |
| PyErr_SetString(PyExc_OSError, strerror(errno)); |
| |
| } |
| return NULL; |
| } |
| |
| struct inet_diag_msg *r = NLMSG_DATA(self->h); |
| const int nlmsg_len = self->h->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); |
| self->h = NLMSG_NEXT(self->h, self->len); |
| |
| struct user_ent *found; |
| if (!(found=malloc(sizeof(struct user_ent)))) abort(); |
| if ( show_users > 0 ) { |
| find_users(r->idiag_inode, found); |
| } |
| |
| return inet_socket__new(r, nlmsg_len, found); |
| } |
| |
| static char inet_diag_clear__doc__[] = |
| "clear()\n\n\ |
| Explicitly clear memory used by inet_diag object."; |
| static void inet_diag__clear(PyObject *mself __unused, PyObject *args, |
| PyObject *keywds) |
| { |
| //clear_users(); |
| } |
| |
| |
| static struct PyMethodDef inet_diag__methods[] = { |
| { |
| .ml_name = "get", |
| .ml_meth = (PyCFunction)inet_diag__get, |
| .ml_flags = METH_VARARGS, |
| .ml_doc = inet_diag__get_doc__, |
| }, |
| { |
| .ml_name = "clear", |
| .ml_meth = (PyCFunction)inet_diag__clear, |
| .ml_flags = METH_VARARGS, |
| .ml_doc = inet_diag_clear__doc__, |
| }, |
| { |
| .ml_name = NULL, |
| } |
| }; |
| |
| static PyObject *inet_diag__getattr(struct inet_diag *self, char *name) |
| { |
| /* module name */ |
| if (!strcmp(name, "__module__")) |
| return PyString_FromString("_inet_diag"); |
| |
| /* class name */ |
| if (!strcmp(name, "__class__")) |
| return PyString_FromString("inet_diag"); |
| |
| /* seeks name in methods (fallback) */ |
| return Py_FindMethod(inet_diag__methods, (PyObject *)self, name); |
| } |
| |
| static PyTypeObject inet_diag_type = { |
| PyObject_HEAD_INIT(NULL) |
| .tp_name = "inet_diag", |
| .tp_basicsize = sizeof(struct inet_diag), |
| .tp_dealloc = (destructor)inet_diag__dealloc, |
| .tp_print = (printfunc)inet_diag__print, |
| .tp_getattr = (getattrfunc)inet_diag__getattr, |
| }; |
| |
| struct aafilter |
| { |
| int family; |
| int mask; |
| unsigned long int prefix; |
| int port; |
| struct aafilter *next; |
| }; |
| |
| static void filter_patch(char *a, int len, int reloc) |
| { |
| while (len > 0) { |
| struct inet_diag_bc_op *op = (struct inet_diag_bc_op*)a; |
| if (op->no == len+4) |
| op->no += reloc; |
| len -= op->yes; |
| a += op->yes; |
| } |
| if (len < 0) |
| abort(); |
| } |
| |
| static int filter_bytecompile(struct diag_filter *f, char **bytecode) |
| { |
| switch (f->type) { |
| case DIAG_BC_AUTO: |
| { |
| if (!(*bytecode=malloc(4))) abort(); |
| ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_AUTO, 4, 8 }; |
| return 4; |
| } |
| case DIAG_BC_D_COND: |
| case DIAG_BC_S_COND: |
| { |
| struct aafilter *a = (void*)f->pred; |
| struct aafilter *b; |
| char *ptr; |
| int code = (f->type == DIAG_BC_D_COND ? INET_DIAG_BC_D_COND : INET_DIAG_BC_S_COND); |
| int len = 0; |
| |
| for (b=a; b; b=b->next) { |
| len += 4 + sizeof(struct inet_diag_hostcond); |
| if (a->family == AF_INET6) |
| len += 16; |
| else |
| len += 4; |
| if (b->next) |
| len += 4; |
| } |
| if (!(ptr = malloc(len))) abort(); |
| *bytecode = ptr; |
| for (b=a; b; b=b->next) { |
| struct inet_diag_bc_op *op = (struct inet_diag_bc_op *)ptr; |
| int alen = (a->family == AF_INET6 ? 16 : 4); |
| int oplen = alen + 4 + sizeof(struct inet_diag_hostcond); |
| struct inet_diag_hostcond *cond = (struct inet_diag_hostcond*)(ptr+4); |
| |
| *op = (struct inet_diag_bc_op){ code, oplen, oplen+4 }; |
| cond->family = a->family; |
| cond->port = a->port; |
| cond->prefix_len = a->mask; |
| memcpy(cond->addr, &a->prefix, alen); |
| ptr += oplen; |
| if (b->next) { |
| op = (struct inet_diag_bc_op *)ptr; |
| *op = (struct inet_diag_bc_op){ INET_DIAG_BC_JMP, 4, len - (ptr-*bytecode)}; |
| ptr += 4; |
| } |
| } |
| return ptr - *bytecode; |
| } |
| case DIAG_BC_D_GE: |
| { |
| struct aafilter *x = (void*)f->pred; |
| if (!(*bytecode=malloc(8))) abort(); |
| ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_D_GE, 8, 12 }; |
| ((struct inet_diag_bc_op*)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port }; |
| return 8; |
| } |
| case DIAG_BC_D_LE: |
| { |
| struct aafilter *x = (void*)f->pred; |
| if (!(*bytecode=malloc(8))) abort(); |
| ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_D_LE, 8, 12 }; |
| ((struct inet_diag_bc_op*)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port }; |
| return 8; |
| } |
| case DIAG_BC_S_GE: |
| { |
| struct aafilter *x = (void*)f->pred; |
| if (!(*bytecode=malloc(8))) abort(); |
| ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_S_GE, 8, 12 }; |
| ((struct inet_diag_bc_op*)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port }; |
| return 8; |
| } |
| case DIAG_BC_S_LE: |
| { |
| struct aafilter *x = (void*)f->pred; |
| if (!(*bytecode=malloc(8))) abort(); |
| ((struct inet_diag_bc_op*)*bytecode)[0] = (struct inet_diag_bc_op){ INET_DIAG_BC_S_LE, 8, 12 }; |
| ((struct inet_diag_bc_op*)*bytecode)[1] = (struct inet_diag_bc_op){ 0, 0, x->port }; |
| return 8; |
| } |
| |
| case DIAG_FILTER_AND: |
| { |
| char *a1, *a2, *a, l1, l2; |
| l1 = filter_bytecompile(f->pred, &a1); |
| l2 = filter_bytecompile(f->post, &a2); |
| if (!(a = malloc(l1+l2))) abort(); |
| memcpy(a, a1, l1); |
| memcpy(a+l1, a2, l2); |
| free(a1); free(a2); |
| filter_patch(a, l1, l2); |
| *bytecode = a; |
| return l1+l2; |
| } |
| case DIAG_FILTER_OR: |
| { |
| char *a1, *a2, *a, l1, l2; |
| l1 = filter_bytecompile(f->pred, &a1); |
| l2 = filter_bytecompile(f->post, &a2); |
| if (!(a = malloc(l1+l2+4))) abort(); |
| memcpy(a, a1, l1); |
| memcpy(a+l1+4, a2, l2); |
| free(a1); free(a2); |
| *(struct inet_diag_bc_op*)(a+l1) = (struct inet_diag_bc_op){ INET_DIAG_BC_JMP, 4, l2+4 }; |
| *bytecode = a; |
| return l1+l2+4; |
| } |
| case DIAG_FILTER_NOT: |
| { |
| char *a1, *a, l1; |
| l1 = filter_bytecompile(f->pred, &a1); |
| if (!(a = malloc(l1+4))) abort(); |
| memcpy(a, a1, l1); |
| free(a1); |
| *(struct inet_diag_bc_op*)(a+l1) = (struct inet_diag_bc_op){ INET_DIAG_BC_JMP, 4, 8 }; |
| *bytecode = a; |
| return l1+4; |
| } |
| default: |
| abort(); |
| } |
| } |
| |
| /* constructor */ |
| static char inet_diag_create__doc__[] = |
| "create([states, extensions, socktype, src, sport, dst, dport, le_spt, le_dpt, ge_spt, ge_dpt, join=DIAG_FILTER_AND])\n\n\ |
| Creates a new inet_diag socket object. Filters include:\n\ |
| - socket states (SENT, RECV, etc.)\n\ |
| - source addr and source port (must be used together)\n\ |
| - dest addr and dest port (must be used together)\n\ |
| - <=, >= source and dest port\n\n\ |
| All specified source and dest filters can either be joined with DIAG_FILTER_AND or DIAG_FILTER_OR."; |
| static PyObject *inet_diag__create(PyObject *mself __unused, PyObject *args, |
| PyObject *keywds) |
| { |
| int states = default_states; |
| int extensions = INET_DIAG_NONE; |
| int socktype = IPPROTO_TCP; |
| const char *src; |
| const char *dst; |
| int sport = -1; |
| int dport = -1; |
| int le_spt = -1; |
| int le_dpt = -1; |
| int ge_spt = -1; |
| int ge_dpt = -1; |
| int proc = 0; |
| int join = DIAG_FILTER_AND; |
| static char *kwlist[] = { "states", "extensions", "socktype", "src", "dst", "sport", "dport", "le_spt", "le_dpt", "ge_spt", "ge_dpt", "join", "proc" }; |
| struct inet_diag *self = PyObject_NEW(struct inet_diag, |
| &inet_diag_type); |
| if (self == NULL) |
| return NULL; |
| |
| if (!PyArg_ParseTupleAndKeywords(args, keywds, "|iiissiiiiiiii", kwlist, |
| &states, &extensions, &socktype, &src, &dst, &sport, &dport, &le_spt, &le_dpt, &ge_spt, &ge_dpt, &join, &proc)) |
| goto out_err; |
| |
| /* TODO: have different levels of process identification */ |
| if ( proc > 0 ) { |
| show_users++; |
| user_ent_hash_build(); |
| } |
| |
| self->socket = socket(AF_NETLINK, SOCK_RAW, NETLINK_INET_DIAG); |
| if (self->socket < 0) |
| goto out_err; |
| |
| struct sockaddr_nl nladdr = { |
| .nl_family = AF_NETLINK, |
| }; |
| |
| struct { |
| struct nlmsghdr nlh; |
| struct inet_diag_req_v2 r; |
| } req = { |
| .nlh = { |
| .nlmsg_len = sizeof(req), |
| .nlmsg_type = SOCK_DIAG_BY_FAMILY, |
| .nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST, |
| .nlmsg_seq = 123456, |
| }, |
| .r = { |
| .sdiag_family = AF_INET, |
| .sdiag_protocol = socktype, |
| .idiag_states = states, |
| .idiag_ext = extensions, |
| }, |
| }; |
| |
| // filter preparation |
| struct diag_filter *filter; |
| int f_exists = 0; |
| if ( src != NULL && sport != -1 ) { |
| struct in_addr in_src = {}; |
| inet_aton(src, &in_src); |
| |
| filter = &(struct diag_filter){ |
| .type = DIAG_BC_S_COND, |
| .pred = (void*)&(struct aafilter){ |
| .family = AF_INET, |
| .mask = 32, |
| .prefix = in_src.s_addr, |
| .port = sport, |
| .next = NULL, |
| }, |
| .post = NULL |
| }; |
| f_exists = 1; |
| } |
| |
| if ( dst != NULL && dport != -1 ) { |
| struct in_addr in_dst = {}; |
| inet_aton(dst, &in_dst); |
| |
| struct diag_filter tmp_dst = (struct diag_filter){ |
| .type = DIAG_BC_D_COND, |
| .pred = (void*)&(struct aafilter){ |
| .family = AF_INET, |
| .mask = 32, |
| .prefix = in_dst.s_addr, |
| .port = dport, |
| .next = NULL, |
| }, |
| .post = NULL |
| }; |
| |
| if ( f_exists != 0 ) { |
| filter = &(struct diag_filter){ |
| .type = join, |
| .pred = filter, |
| .post = &tmp_dst, |
| }; |
| } else { |
| filter = &tmp_dst; |
| f_exists = 1; |
| } |
| } |
| |
| if ( le_spt != -1 ) { |
| struct diag_filter tmp_le_spt = { |
| .type = DIAG_BC_S_LE, |
| .pred = (void*)&(struct aafilter){ |
| .port = le_spt, |
| .next = NULL, |
| }, |
| .post = NULL |
| }; |
| |
| if ( f_exists != 0 ) { |
| filter = &(struct diag_filter){ |
| .type = join, |
| .pred = filter, |
| .post = &tmp_le_spt, |
| }; |
| } else { |
| filter = &tmp_le_spt; |
| f_exists = 1; |
| } |
| } |
| |
| if ( le_dpt != -1 ) { |
| struct diag_filter tmp_le_dpt = { |
| .type = DIAG_BC_D_LE, |
| .pred = (void*)&(struct aafilter){ |
| .port = le_dpt, |
| .next = NULL, |
| }, |
| .post = NULL |
| }; |
| |
| if ( f_exists != 0 ) { |
| filter = &(struct diag_filter){ |
| .type = join, |
| .pred = filter, |
| .post = &tmp_le_dpt, |
| }; |
| } else { |
| filter = &tmp_le_dpt; |
| f_exists = 1; |
| } |
| } |
| |
| if ( ge_spt != -1 ) { |
| struct diag_filter tmp_ge_spt = { |
| .type = DIAG_BC_S_GE, |
| .pred = (void*)&(struct aafilter){ |
| .port = ge_spt, |
| .next = NULL, |
| }, |
| .post = NULL |
| }; |
| |
| if ( f_exists != 0 ) { |
| filter = &(struct diag_filter){ |
| .type = join, |
| .pred = filter, |
| .post = &tmp_ge_spt, |
| }; |
| } else { |
| filter = &tmp_ge_spt; |
| f_exists = 1; |
| } |
| } |
| |
| if ( ge_dpt != -1 ) { |
| struct diag_filter tmp_ge_dpt = { |
| .type = DIAG_BC_D_GE, |
| .pred = (void*)&(struct aafilter){ |
| .port = ge_dpt, |
| .next = NULL, |
| }, |
| .post = NULL |
| }; |
| |
| if ( f_exists != 0 ) { |
| filter = &(struct diag_filter){ |
| .type = join, |
| .pred = filter, |
| .post = &tmp_ge_dpt, |
| }; |
| } else { |
| filter = &tmp_ge_dpt; |
| f_exists = 1; |
| } |
| } |
| |
| struct iovec iov[3]; |
| iov[0] = (struct iovec){ |
| .iov_base = &req, |
| .iov_len = sizeof(req), |
| }; |
| |
| // append the filter |
| struct rtattr rta; |
| |
| int filter_len = 0; |
| if ( f_exists != 0 ) { |
| filter_len = filter_bytecompile(filter, &(self->bytecode)); |
| rta.rta_type = INET_DIAG_REQ_BYTECODE; |
| rta.rta_len = RTA_LENGTH(filter_len); |
| |
| iov[1] = (struct iovec){ &rta, sizeof(rta) }; |
| iov[2] = (struct iovec){ self->bytecode, filter_len }; |
| |
| req.nlh.nlmsg_len += RTA_LENGTH(filter_len); |
| } |
| |
| struct msghdr msg = { |
| .msg_name = &nladdr, |
| .msg_namelen = sizeof(nladdr), |
| .msg_iov = iov, |
| .msg_iovlen = ( f_exists != 0 ? 3 : 1 ), |
| }; |
| if (sendmsg(self->socket, &msg, 0) < 0) |
| goto out_err; |
| |
| self->len = 0; |
| return (PyObject *)self; |
| out_err: |
| PyErr_SetString(PyExc_OSError, strerror(errno)); |
| Py_XDECREF(self); |
| return NULL; |
| } |
| |
| static struct PyMethodDef python_inet_diag__methods[] = { |
| { |
| .ml_name = "create", |
| .ml_meth = (PyCFunction)inet_diag__create, |
| .ml_flags = METH_VARARGS | METH_KEYWORDS, |
| .ml_doc = inet_diag_create__doc__ |
| }, |
| { .ml_name = NULL, }, |
| }; |
| |
| PyMODINIT_FUNC initinet_diag(void) |
| { |
| PyObject *m; |
| m = Py_InitModule3("inet_diag", python_inet_diag__methods, "Example:\n\n\ |
| > import inet_diag\n\ |
| > from socket import IPPROTO_TCP\n\ |
| > idiag = inet_diag.create(states = inet_diag.default_states, extensions = inet_diag.EXT_MEMORY, socktype = IPPROTO_TCP, le_dpt = 500)\n\ |
| > while True:\n\ |
| > try:\n\ |
| > s = idiag.get()\n\ |
| > except:\n\ |
| > break\n\ |
| > print s"); |
| PyModule_AddIntConstant(m, "SS_ESTABLISHED", SS_ESTABLISHED); |
| PyModule_AddIntConstant(m, "SS_SYN_SENT", SS_SYN_SENT); |
| PyModule_AddIntConstant(m, "SS_SYN_RECV", SS_SYN_RECV); |
| PyModule_AddIntConstant(m, "SS_FIN_WAIT1", SS_FIN_WAIT1); |
| PyModule_AddIntConstant(m, "SS_FIN_WAIT2", SS_FIN_WAIT2); |
| PyModule_AddIntConstant(m, "SS_TIME_WAIT", SS_TIME_WAIT); |
| PyModule_AddIntConstant(m, "SS_CLOSE", SS_CLOSE); |
| PyModule_AddIntConstant(m, "SS_CLOSE_WAIT", SS_CLOSE_WAIT); |
| PyModule_AddIntConstant(m, "SS_LAST_ACK", SS_LAST_ACK); |
| PyModule_AddIntConstant(m, "SS_LISTEN", SS_LISTEN); |
| PyModule_AddIntConstant(m, "SS_CLOSING", SS_CLOSING); |
| PyModule_AddIntConstant(m, "SS_ALL", SS_ALL); |
| PyModule_AddIntConstant(m, "default_states", default_states); |
| PyModule_AddIntConstant(m, "EXT_MEMORY", 1 << (INET_DIAG_MEMINFO - 1)); |
| PyModule_AddIntConstant(m, "EXT_PROTOCOL", 1 << (INET_DIAG_INFO - 1)); |
| PyModule_AddIntConstant(m, "EXT_TCP_VEGAS", 1 << (INET_DIAG_VEGASINFO - 1)); |
| PyModule_AddIntConstant(m, "EXT_CONGESTION", 1 << (INET_DIAG_CONG - 1)); |
| PyModule_AddIntConstant(m, "PROTO_OPT_TIMESTAMPS", TCPI_OPT_TIMESTAMPS); |
| PyModule_AddIntConstant(m, "PROTO_OPT_SACK", TCPI_OPT_SACK); |
| PyModule_AddIntConstant(m, "PROTO_OPT_WSCALE", TCPI_OPT_WSCALE); |
| PyModule_AddIntConstant(m, "PROTO_OPT_ECN", TCPI_OPT_ECN); |
| PyModule_AddIntConstant(m, "TCPDIAG_GETSOCK", TCPDIAG_GETSOCK); |
| PyModule_AddIntConstant(m, "DCCPDIAG_GETSOCK", DCCPDIAG_GETSOCK); |
| PyModule_AddIntConstant(m, "DIAG_FILTER_AND", DIAG_FILTER_AND); |
| PyModule_AddIntConstant(m, "DIAG_FILTER_OR", DIAG_FILTER_OR); |
| } |