aboutsummaryrefslogtreecommitdiffstats
path: root/src/fnettrace-icmp/main.c
diff options
context:
space:
mode:
authorLibravatar netblue30 <netblue30@protonmail.com>2022-10-24 08:35:01 -0400
committerLibravatar netblue30 <netblue30@protonmail.com>2022-10-24 08:35:01 -0400
commit729b1251cd1783a0bc72a96ebc5aba455ccb375f (patch)
tree4abb82b3883dc4d1ce5261815f7e53fd8bc4cc3c /src/fnettrace-icmp/main.c
parentMerge pull request #5431 from netblue30/musl_warnings (diff)
downloadfirejail-729b1251cd1783a0bc72a96ebc5aba455ccb375f.tar.gz
firejail-729b1251cd1783a0bc72a96ebc5aba455ccb375f.tar.zst
firejail-729b1251cd1783a0bc72a96ebc5aba455ccb375f.zip
--icmptrace
Diffstat (limited to 'src/fnettrace-icmp/main.c')
-rw-r--r--src/fnettrace-icmp/main.c215
1 files changed, 215 insertions, 0 deletions
diff --git a/src/fnettrace-icmp/main.c b/src/fnettrace-icmp/main.c
new file mode 100644
index 000000000..47d61a326
--- /dev/null
+++ b/src/fnettrace-icmp/main.c
@@ -0,0 +1,215 @@
1/*
2 * Copyright (C) 2014-2022 Firejail Authors
3 *
4 * This file is part of firejail project
5 *
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 2 of the License, or
9 * (at your option) any later version.
10 *
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.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19*/
20#include "fnettrace_icmp.h"
21#include <sys/ioctl.h>
22#include <time.h>
23#include <linux/filter.h>
24#include <linux/if_ether.h>
25#define MAX_BUF_SIZE (64 * 1024)
26
27char *type_description[19] = {
28 "Echo reply",
29 "unassigned",
30 "unassigned",
31 "Destination unreachable",
32 "Source quench",
33 "Redirect message",
34 "unassigned",
35 "unassigned",
36 "Echo request",
37 "Router advertisement",
38 "Router solicitation"
39 "Time exceeded",
40 "Bad IP header",
41 "Timestamp",
42 "Timestamp replay",
43 "Information request",
44 "Information reply",
45 "Address mask request",
46 "Address mask reply"
47};
48
49char *code_dest_unreachable[16] = {
50 "Network unreachable",
51 "Host unreachable",
52 "Protocol unreachable",
53 "Port unreachable",
54 "Fragmentation required, and DF flag set",
55 "Source route failed",
56 "Network unknown",
57 "Host unknown",
58 "Source host isolated",
59 "Network administratively prohibited",
60 "Host administratively prohibited",
61 "Network unreachable for ToS",
62 "Host unreachable for ToS",
63 "Communication administratively prohibited",
64 "Host Precedence Violation",
65 "Precedence cutoff in effect "
66};
67
68char *code_redirect_message[4] = {
69 "Datagram for the Network",
70 "Datagram for the Host",
71 "Datagram for the ToS & network",
72 "Datagram for the ToS & host "
73};
74
75char *code_time_exceeded[2] = {
76 "TTL expired in transit",
77 "Fragment reassembly time exceeded "
78};
79
80char *code_bad_ip_header[3] = {
81 "Pointer indicates the error",
82 "Missing a required option",
83 "Bad length"
84};
85
86static void print_icmp(uint32_t ip_dest, uint32_t ip_src, uint8_t type, uint8_t code, unsigned icmp_bytes) {
87 char type_number[10];
88 char *type_ptr = type_number;
89 if (type < 19)
90 type_ptr = type_description[type];
91 else
92 sprintf(type_number, "%u", type);
93
94 char code_number[10];
95 char *code_ptr = code_number;
96 if (type ==3 && code < 16)
97 code_ptr = code_dest_unreachable[code];
98 else if (type == 5 && code < 4)
99 code_ptr = code_redirect_message[code];
100 else if (type == 11 && code < 2)
101 code_ptr = code_time_exceeded[code];
102 else if (type == 12 && code < 3)
103 code_ptr = code_bad_ip_header[code];
104 else
105 sprintf(code_number, "%u", code);
106
107 time_t seconds = time(NULL);
108 struct tm *t = localtime(&seconds);
109 printf("%02d:%02d:%02d %d.%d.%d.%d -> %d.%d.%d.%d - %u bytes - %s/%s\n",
110 t->tm_hour, t->tm_min, t->tm_sec,
111 PRINT_IP(ip_src),
112 PRINT_IP(ip_dest),
113 icmp_bytes,
114 type_ptr,
115 code_ptr);
116}
117
118// https://www.kernel.org/doc/html/latest/networking/filter.html
119static void custom_bpf(int sock) {
120 struct sock_filter code[] = {
121 // sudo tcpdump "icmp" -dd
122 { 0x28, 0, 0, 0x0000000c },
123 { 0x15, 0, 3, 0x00000800 },
124 { 0x30, 0, 0, 0x00000017 },
125 { 0x15, 0, 1, 0x00000001 },
126 { 0x6, 0, 0, 0x00040000 },
127 { 0x6, 0, 0, 0x00000000 },
128 };
129
130 struct sock_fprog bpf = {
131 .len = (unsigned short) sizeof(code) / sizeof(code[0]),
132 .filter = code,
133 };
134
135 int rv = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &bpf, sizeof(bpf));
136 if (rv < 0) {
137 fprintf(stderr, "Error: cannot attach BPF filter\n");
138 exit(1);
139 }
140}
141
142static void run_trace(void) {
143 // grab all Ethernet packets and use a custom BPF filter to get TLS/SNI packets
144 int s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
145 if (s < 0)
146 errExit("socket");
147 custom_bpf(s);
148
149 unsigned char buf[MAX_BUF_SIZE];
150 while (1) {
151 fd_set rfds;
152 FD_ZERO(&rfds);
153 FD_SET(s, &rfds);
154 struct timeval tv;
155 tv.tv_sec = 1;
156 tv.tv_usec = 0;
157 int rv = select(s + 1, &rfds, NULL, NULL, &tv);
158 if (rv < 0)
159 errExit("select");
160 else if (rv == 0)
161 continue;
162 unsigned bytes = recvfrom(s, buf, MAX_BUF_SIZE, 0, NULL, NULL);
163
164 if (bytes >= (14 + 20 + 2)) { // size of MAC + IP + ICMP code and type fields
165 uint8_t ip_hlen = (buf[14] & 0x0f) * 4;
166 uint8_t type = *(buf + 14 +ip_hlen);
167 uint8_t code = *(buf + 14 + ip_hlen + 1);
168
169 uint32_t ip_dest;
170 memcpy(&ip_dest, buf + 14 + 16, 4);
171 ip_dest = ntohl(ip_dest);
172 uint32_t ip_src;
173 memcpy(&ip_src, buf + 14 + 12, 4);
174 ip_src = ntohl(ip_src);
175
176 print_icmp(ip_dest, ip_src, type, code, bytes);
177 }
178 }
179
180 close(s);
181}
182
183
184static void usage(void) {
185 printf("Usage: fnettrace-icmp [OPTIONS]\n");
186 printf("Options:\n");
187 printf(" --help, -? - this help screen\n");
188 printf("\n");
189}
190
191int main(int argc, char **argv) {
192 int i;
193
194 for (i = 1; i < argc; i++) {
195 if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-?") == 0) {
196 usage();
197 return 0;
198 }
199 else {
200 fprintf(stderr, "Error: invalid argument\n");
201 return 1;
202 }
203 }
204
205 if (getuid() != 0) {
206 fprintf(stderr, "Error: you need to be root to run this program\n");
207 return 1;
208 }
209
210 time_t now = time(NULL);
211 printf("ICMP trace for %s\n", ctime(&now));
212 run_trace();
213
214 return 0;
215}