forked from kristrev/inet-diag-example
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathminimal.c
129 lines (116 loc) · 4.31 KB
/
minimal.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
/* We keep this file for aesthetic enjoyment.
* Contributions are welcome if they improve the clarity of the code.
* Feel free to add insightful comments.
*/
#include <asm/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h> // RTA_* macros for struct rtattr (optinoal attributes after the nlmsghdr)
#include <linux/netlink.h>
#include <netinet/in.h>
#include <linux/tcp.h>
#include <linux/sock_diag.h>
#include <linux/inet_diag.h>
#include <arpa/inet.h>
#include <pwd.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
//inspired by libnml git://git.netfilter.org/libmnl
#define SOCKET_BUFFER_SIZE ( sysconf(_SC_PAGESIZE) < 8192L ? sysconf(_SC_PAGESIZE) : 8192L )
//Kernel TCP states. /include/net/tcp_states.h
enum{
TCP_ESTABLISHED = 1,
TCP_SYN_SENT,
TCP_SYN_RECV,
TCP_FIN_WAIT1,
TCP_FIN_WAIT2,
TCP_TIME_WAIT,
TCP_CLOSE,
TCP_CLOSE_WAIT,
TCP_LAST_ACK,
TCP_LISTEN,
TCP_CLOSING
};
static const char* tcp_states_map[]={ // array of pointers
[TCP_ESTABLISHED] = "ESTABLISHED",
[TCP_SYN_SENT] = "SYN-SENT",
[TCP_SYN_RECV] = "SYN-RECV",
[TCP_FIN_WAIT1] = "FIN-WAIT-1",
[TCP_FIN_WAIT2] = "FIN-WAIT-2",
[TCP_TIME_WAIT] = "TIME-WAIT",
[TCP_CLOSE] = "CLOSE",
[TCP_CLOSE_WAIT] = "CLOSE-WAIT",
[TCP_LAST_ACK] = "LAST-ACK",
[TCP_LISTEN] = "LISTEN",
[TCP_CLOSING] = "CLOSING"
};
#define TCPF_ALL 0xFFF
int main() { // takes no args for now, replace with (int argc, char *argv[]) later
//printf("%d\n", netlink_socket);
struct iovec iov[2] = {0};
struct nlmsghdr nlhdr = {
.nlmsg_len = NLMSG_LENGTH(sizeof(struct inet_diag_req_v2)),
.nlmsg_type = SOCK_DIAG_BY_FAMILY, //sock_diag.c: if nlh->nlmsg_type == SOCK_DIAG_BY_FAMILY .. dump(skb, nlh)
.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST, // try NLM_F_ROOT instead of dump
.nlmsg_seq = 420, // the kernel doesn't care about these
.nlmsg_pid = 69,
};
iov[0].iov_base = (void *) &nlhdr; //nlmsghdr is encapsulated in the msghdr
iov[0].iov_len = sizeof(nlhdr);
struct inet_diag_req_v2 nlreq = {
.sdiag_family = AF_INET,
.sdiag_protocol = IPPROTO_TCP,
.idiag_ext = (1 << (INET_DIAG_INFO - 1)), // see the "if (tcp_info)" line in "net/ipv4/inet_diag.c
.idiag_states = 0xFFF, // TCPF_ALL
};
iov[1].iov_base = (void *) &nlreq; //inet_diag_req_v2 is encapsulated in the msghdr
iov[1].iov_len = sizeof(nlreq);
struct sockaddr_nl sa = {
.nl_family = AF_NETLINK,
.nl_pid = 0, // usually the pid of the destination process, but 0 means kernel
.nl_groups = 0, // multicast group mask
};
struct msghdr msg = {
.msg_name = (void *) &sa,
.msg_namelen = sizeof(sa),
.msg_iov = iov, // pointer to array of "io vector" structs
.msg_iovlen = 2, // number of elements in the array
};
int netlink_socket = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_SOCK_DIAG); // see man 7 netlink
uint8_t buf[SOCKET_BUFFER_SIZE]; // = {0}; ERROR variable-sized object may not be initialized
sendmsg(netlink_socket, &msg, 0);
int i = 0;
while(1) {
ssize_t msglen = recv(netlink_socket, buf, sizeof(buf), 0);
struct nlmsghdr *recvnlh = (struct nlmsghdr *) buf;
while(NLMSG_OK(recvnlh, msglen)) {
if(recvnlh->nlmsg_type == NLMSG_DONE) {
printf(" Done\n");
return EXIT_SUCCESS;
} else if(recvnlh->nlmsg_type == NLMSG_ERROR) { //Would NLMSG_OK equal 1 if there was an error?
printf("Error\n");
return EXIT_FAILURE;
}
struct inet_diag_msg *diag_msg = (struct inet_diag_msg *) NLMSG_DATA(recvnlh);
char src_addr_buf[INET_ADDRSTRLEN];
printf("src:%-16s ", inet_ntop(AF_INET, &diag_msg->id.idiag_src, src_addr_buf, sizeof(buf)));
struct rtattr *attr = (struct rtattr *) (diag_msg + 1);
unsigned int rtattrlen = recvnlh->nlmsg_len - NLMSG_LENGTH(sizeof(*diag_msg));
while (RTA_OK(attr, rtattrlen)) {
if (attr->rta_type == INET_DIAG_INFO) {
struct tcp_info *tcpi = (struct tcp_info *) RTA_DATA(attr);
//https://elixir.bootlin.com/linux/v5.3.8/source/include/uapi/linux/tcp.h#L206
fprintf(stdout, "state:%-11s snd_mss:%-6d rcv_mss:%-6d pmtu:%-6d advmss:%-6d\n",
tcp_states_map[tcpi->tcpi_state],
tcpi->tcpi_snd_mss,
tcpi->tcpi_rcv_mss,
tcpi->tcpi_pmtu,
tcpi->tcpi_advmss);
}
attr = RTA_NEXT(attr, rtattrlen);
}
recvnlh = NLMSG_NEXT(recvnlh, msglen);
}
}
}