1use socket2::Socket;
2
3#[cfg(target_os = "linux")]
5mod op {
6 pub const LD: u16 = 0x00;
7 pub const LDX: u16 = 0x01;
8 pub const B: u16 = 0x10;
9 pub const H: u16 = 0x08;
10 pub const ABS: u16 = 0x20;
11 pub const IND: u16 = 0x40;
12 pub const MSH: u16 = 0xa0;
13 pub const ALU: u16 = 0x04;
14 pub const AND: u16 = 0x50;
15 pub const RSH: u16 = 0x70;
16 pub const JMP: u16 = 0x05;
17 pub const JEQ: u16 = 0x10;
18 pub const K: u16 = 0x00;
19 pub const RET: u16 = 0x06;
20}
21
22#[cfg(target_os = "linux")]
23const ACCEPT: u32 = 0xffff_ffff; #[cfg(target_os = "linux")]
25const DROP: u32 = 0; #[cfg(target_os = "linux")]
28#[inline]
29fn sf(code: u16, jt: u8, jf: u8, k: u32) -> libc::sock_filter {
30 libc::sock_filter { code, jt, jf, k }
31}
32
33#[cfg(target_os = "linux")]
37fn attach(socket: &Socket, prog: &mut [libc::sock_filter]) -> std::io::Result<()> {
38 use std::os::fd::AsRawFd;
39
40 let fprog = libc::sock_fprog {
41 len: prog.len() as u16,
42 filter: prog.as_mut_ptr(),
43 };
44
45 let ret = unsafe {
46 libc::setsockopt(
48 socket.as_raw_fd(),
49 libc::SOL_SOCKET,
50 libc::SO_ATTACH_FILTER,
51 &fprog as *const _ as *const libc::c_void,
52 std::mem::size_of::<libc::sock_fprog>() as libc::socklen_t,
53 )
54 };
55
56 if ret != 0 {
57 return Err(std::io::Error::last_os_error());
58 }
59 Ok(())
60}
61
62#[cfg(target_os = "linux")]
70pub(crate) fn attach_icmp_filter(
71 socket: &Socket,
72 icmp_id: u16,
73 is_ipv6: bool,
74) -> std::io::Result<()> {
75 use op::*;
76
77 const ICMP_ECHO_REPLY_V4: u32 = 0;
78 const ICMP_ECHO_REPLY_V6: u32 = 129;
79 let id = icmp_id as u32;
80
81 let mut prog: Vec<libc::sock_filter> = if !is_ipv6 {
82 vec![
83 sf(LDX | B | MSH, 0, 0, 0), sf(LD | B | IND, 0, 0, 0), sf(JMP | JEQ | K, 0, 3, ICMP_ECHO_REPLY_V4), sf(LD | H | IND, 0, 0, 4), sf(JMP | JEQ | K, 0, 1, id), sf(RET | K, 0, 0, ACCEPT),
89 sf(RET | K, 0, 0, DROP),
90 ]
91 } else {
92 vec![
93 sf(LD | B | ABS, 0, 0, 0), sf(JMP | JEQ | K, 0, 3, ICMP_ECHO_REPLY_V6), sf(LD | H | ABS, 0, 0, 4), sf(JMP | JEQ | K, 0, 1, id), sf(RET | K, 0, 0, ACCEPT),
98 sf(RET | K, 0, 0, DROP),
99 ]
100 };
101
102 attach(socket, &mut prog)
103}
104
105#[cfg(target_os = "linux")]
114pub(crate) fn attach_traceroute_filter(socket: &Socket, is_ipv6: bool) -> std::io::Result<()> {
115 use op::*;
116
117 const ICMP_ECHO_REPLY_V4: u32 = 0;
118 const ICMP_DEST_UNREACHABLE_V4: u32 = 3;
119 const ICMP_TIME_EXCEEDED_V4: u32 = 11;
120 const ICMP_ECHO_REPLY_V6: u32 = 129;
121 const ICMP_DEST_UNREACHABLE_V6: u32 = 1;
122 const ICMP_TIME_EXCEEDED_V6: u32 = 3;
123
124 let mut prog: Vec<libc::sock_filter> = if !is_ipv6 {
125 vec![
126 sf(LDX | B | MSH, 0, 0, 0), sf(LD | B | IND, 0, 0, 0), sf(JMP | JEQ | K, 2, 0, ICMP_TIME_EXCEEDED_V4), sf(JMP | JEQ | K, 1, 0, ICMP_DEST_UNREACHABLE_V4), sf(JMP | JEQ | K, 0, 1, ICMP_ECHO_REPLY_V4), sf(RET | K, 0, 0, ACCEPT),
132 sf(RET | K, 0, 0, DROP),
133 ]
134 } else {
135 vec![
136 sf(LD | B | ABS, 0, 0, 0), sf(JMP | JEQ | K, 2, 0, ICMP_TIME_EXCEEDED_V6), sf(JMP | JEQ | K, 1, 0, ICMP_DEST_UNREACHABLE_V6), sf(JMP | JEQ | K, 0, 1, ICMP_ECHO_REPLY_V6), sf(RET | K, 0, 0, ACCEPT),
141 sf(RET | K, 0, 0, DROP),
142 ]
143 };
144
145 attach(socket, &mut prog)
146}
147
148#[cfg(target_os = "linux")]
159pub(crate) fn attach_tcp_filter(socket: &Socket, sport: u16, is_ipv6: bool) -> std::io::Result<()> {
160 use op::*;
161
162 const TCP_RST: u32 = 0x04; let dport = sport as u32;
164
165 let mut prog: Vec<libc::sock_filter> = if !is_ipv6 {
166 vec![
167 sf(LDX | B | MSH, 0, 0, 0), sf(LD | H | IND, 0, 0, 2), sf(JMP | JEQ | K, 0, 4, dport), sf(LD | B | IND, 0, 0, 13), sf(ALU | AND | K, 0, 0, TCP_RST), sf(JMP | JEQ | K, 0, 1, TCP_RST), sf(RET | K, 0, 0, ACCEPT),
174 sf(RET | K, 0, 0, DROP),
175 ]
176 } else {
177 vec![
178 sf(LD | H | ABS, 0, 0, 2), sf(JMP | JEQ | K, 0, 4, dport), sf(LD | B | ABS, 0, 0, 13), sf(ALU | AND | K, 0, 0, TCP_RST), sf(JMP | JEQ | K, 0, 1, TCP_RST), sf(RET | K, 0, 0, ACCEPT),
184 sf(RET | K, 0, 0, DROP),
185 ]
186 };
187
188 attach(socket, &mut prog)
189}
190
191#[cfg(target_os = "linux")]
201pub(crate) fn attach_dns_filter(
202 socket: &Socket,
203 sport: u16,
204 dns_identifier: u8,
205 is_ipv6: bool,
206) -> std::io::Result<()> {
207 use op::*;
208
209 let dport = sport as u32;
210 let id = dns_identifier as u32;
211
212 let mut prog: Vec<libc::sock_filter> = if !is_ipv6 {
213 vec![
214 sf(LDX | B | MSH, 0, 0, 0), sf(LD | H | IND, 0, 0, 2), sf(JMP | JEQ | K, 0, 4, dport), sf(LD | B | IND, 0, 0, 8), sf(ALU | RSH | K, 0, 0, 2), sf(JMP | JEQ | K, 0, 1, id), sf(RET | K, 0, 0, ACCEPT),
221 sf(RET | K, 0, 0, DROP),
222 ]
223 } else {
224 vec![
225 sf(LD | H | ABS, 0, 0, 2), sf(JMP | JEQ | K, 0, 4, dport), sf(LD | B | ABS, 0, 0, 8), sf(ALU | RSH | K, 0, 0, 2), sf(JMP | JEQ | K, 0, 1, id), sf(RET | K, 0, 0, ACCEPT),
231 sf(RET | K, 0, 0, DROP),
232 ]
233 };
234
235 attach(socket, &mut prog)
236}
237
238#[cfg(not(target_os = "linux"))]
241pub(crate) fn attach_icmp_filter(
242 _socket: &Socket,
243 _icmp_id: u16,
244 _is_ipv6: bool,
245) -> std::io::Result<()> {
246 Ok(())
247}
248
249#[cfg(not(target_os = "linux"))]
250pub(crate) fn attach_traceroute_filter(_socket: &Socket, _is_ipv6: bool) -> std::io::Result<()> {
251 Ok(())
252}
253
254#[cfg(not(target_os = "linux"))]
255pub(crate) fn attach_tcp_filter(
256 _socket: &Socket,
257 _sport: u16,
258 _is_ipv6: bool,
259) -> std::io::Result<()> {
260 Ok(())
261}
262
263#[cfg(not(target_os = "linux"))]
264pub(crate) fn attach_dns_filter(
265 _socket: &Socket,
266 _sport: u16,
267 _dns_identifier: u8,
268 _is_ipv6: bool,
269) -> std::io::Result<()> {
270 Ok(())
271}