1#![allow(non_camel_case_types)]
2#![allow(non_snake_case)]
3#![allow(dead_code)]
4
5mod packet_info;
7pub use packet_info::{IPversion, PacketInfo, PacketInfoBuilder};
8mod pm;
10pub use pm::Tree;
11
12use std::net::{IpAddr, Ipv6Addr};
13
14use libc::c_void;
15#[cfg(not(windows))]
16use libc::{c_char, c_int};
17
18#[cfg(windows)]
19mod windows_types {
20 pub type u_char = u8;
21 pub type u_short = u16;
22 pub type uint = u32;
23 pub type u_long = u32;
24 pub type u_int8_t = u8;
25 pub type u_int16_t = u16;
26 pub type u_int32_t = u32;
27 pub type int32_t = i32;
28 pub type u_int64_t = u64;
29 pub type int64_t = i64;
30}
31
32#[cfg(windows)]
33use windows_types::*;
34
35#[cfg(not(windows))]
36mod unix_types {
37 pub type u_int8_t = libc::c_uchar;
38 pub type u_int16_t = libc::c_ushort;
39 pub type u_int32_t = libc::c_uint;
40 pub type int32_t = libc::c_int;
41 pub type u_int64_t = libc::c_ulong;
42 pub type int64_t = libc::c_long;
43}
44
45#[cfg(not(windows))]
46use unix_types::*;
47
48#[cfg(windows)]
49use winapi::shared::ws2def::WSAData;
50#[cfg(windows)]
51use winapi::um::winsock2::{WSACleanup, WSAStartup, MAKEWORD};
52
53#[repr(C)]
54#[derive(Copy, Clone)]
55pub union nbpf_in6_addr {
56 pub addr8: [u_int8_t; 16],
57 pub addr16: [u_int16_t; 8],
58 pub addr32: [u_int32_t; 4],
59}
60
61impl From<Ipv6Addr> for nbpf_in6_addr {
62 fn from(value: Ipv6Addr) -> Self {
63 Self {
64 addr8: value.octets(),
65 }
66 }
67}
68
69#[repr(C, packed)]
70pub union nbpf_ip_addr {
71 pub v6: nbpf_in6_addr,
72 pub v4: u_int32_t,
73}
74
75impl From<IpAddr> for nbpf_ip_addr {
76 fn from(value: IpAddr) -> Self {
77 match value {
78 IpAddr::V4(v4) => {
79 let mut v4 = v4.octets();
80 nbpf_ip_addr {
81 v4: u32::from_le_bytes(v4),
82 }
83 }
84 IpAddr::V6(v6) => {
85 let mut v6 = v6.octets();
86 nbpf_ip_addr {
87 v6: nbpf_in6_addr { addr8: v6 },
88 }
89 }
90 }
91 }
92}
93
94pub const NBPF_Q_OUTER: u8 = 1;
96pub const NBPF_Q_INNER: u8 = 2;
97
98pub const NBPF_Q_LINK: u8 = 1;
100pub const NBPF_Q_IP: u8 = 2;
101pub const NBPF_Q_SCTP: u8 = 3;
102pub const NBPF_Q_TCP: u8 = 4;
103pub const NBPF_Q_UDP: u8 = 5;
104pub const NBPF_Q_IPV6: u8 = 6;
105pub const NBPF_Q_GTP: u8 = 7;
106pub const NBPF_Q_ICMP: u8 = 8;
107
108pub const NBPF_Q_SRC: u8 = 1;
110pub const NBPF_Q_DST: u8 = 2;
111pub const NBPF_Q_OR: u8 = 3;
112pub const NBPF_Q_AND: u8 = 4;
113
114pub const NBPF_Q_HOST: u8 = 1;
116pub const NBPF_Q_NET: u8 = 2;
117pub const NBPF_Q_PORT: u8 = 3;
118pub const NBPF_Q_PROTO: u8 = 5;
119pub const NBPF_Q_PORTRANGE: u8 = 7;
120pub const NBPF_Q_VLAN: u8 = 8;
121pub const NBPF_Q_MPLS: u8 = 9;
122pub const NBPF_Q_L7PROTO: u8 = 10;
123pub const NBPF_Q_PROTO_REL: u8 = 11;
124pub const NBPF_Q_CUSTOM: u8 = 12;
125pub const NBPF_Q_LOCAL: u8 = 13;
126pub const NBPF_Q_REMOTE: u8 = 14;
127pub const NBPF_Q_DEVICE: u8 = 15;
128pub const NBPF_Q_INTERFACE: u8 = 16;
129
130pub const NBPF_Q_DEFAULT: u8 = 0;
132pub const NBPF_Q_UNDEF: u8 = 255;
133
134pub const NBPF_R_EQ: u8 = 0; pub const NBPF_R_NE: u8 = 1; pub const NBPF_R_LT: u8 = 2; pub const NBPF_R_LE: u8 = 4; pub const NBPF_R_GT: u8 = 3; pub const NBPF_R_GE: u8 = 5; pub const N_EMPTY: u8 = 0;
144pub const N_PRIMITIVE: u8 = 1;
145pub const N_AND: u8 = 2;
146pub const N_OR: u8 = 3;
147
148#[repr(C, packed)]
149pub struct nbpf_qualifiers_t {
150 pub header: u_int8_t,
151 pub protocol: u_int8_t,
152 pub direction: u_int8_t,
153 pub address: u_int8_t,
154}
155
156#[repr(C, packed)]
157pub struct nbpf_arth_t {
158 pub protocol: c_int,
159 pub offset: u_int16_t,
160 pub mask: u_int8_t,
161}
162
163#[repr(C, packed)]
164pub struct nbpf_node_t {
165 pub type_: c_int,
166 pub level: c_int,
167 pub qualifiers: nbpf_qualifiers_t,
168 pub not_rule: u_int8_t,
169 pub device_id: u_int16_t,
170 pub interface_id: u_int16_t,
171 pub vlan_id_defined: u_int8_t,
172 pub mpls_label_defined: u_int8_t,
173 pub __padding: u_int8_t,
174 pub vlan_id: u_int16_t,
175 pub mpls_label: u_int16_t,
176 pub mac: [u_int8_t; 6],
177 pub ip6: [u_int8_t; 16],
178 pub mask6: [u_int8_t; 16],
179 pub ip: u_int32_t,
180 pub mask: u_int32_t,
181 pub port_from: u_int16_t,
182 pub port_to: u_int16_t,
183 pub protocol: u_int16_t,
184 pub l7protocol: u_int16_t,
185 pub byte_match: nbpf_byte_match_t,
186 pub custom_key: *mut c_char,
187 pub custom_value: *mut c_char,
188 pub l: *mut nbpf_node_t,
189 pub r: *mut nbpf_node_t,
190}
191
192#[repr(C, packed)]
193pub struct nbpf_byte_match_t {
194 pub protocol: u_int16_t,
195 pub offset: u_int16_t,
196 pub mask: u_int8_t,
197 pub relop: u_int8_t,
198 pub value: u_int8_t,
199}
200
201pub type nbpf_custom_node_callback = Option<
202 unsafe extern "C" fn(key: *const c_char, value: *const c_char, user: *mut c_void) -> c_int,
203>;
204pub type nbpf_ip_locality_callback = Option<
205 unsafe extern "C" fn(ip: *mut nbpf_ip_addr, ip_version: u_int8_t, user: *mut c_void) -> c_int,
206>;
207
208#[repr(C, packed)]
209pub struct nbpf_tree_t {
210 pub root: *mut nbpf_node_t,
211 pub compatibility_level: c_int,
212 pub default_pass: c_int,
213 pub custom_callback: nbpf_custom_node_callback,
214 pub locality_callback: nbpf_ip_locality_callback,
215}
216
217pub type l7protocol_by_name_func = Option<unsafe extern "C" fn(name: *const c_char) -> c_int>;
218
219#[repr(C, packed)]
220pub struct nbpf_pkt_info_tuple_t {
221 pub eth_type: u_int16_t,
222 pub ip_version: u_int8_t,
223 pub l3_proto: u_int8_t,
224 pub ip_tos: u_int8_t,
225 pub ip_src: nbpf_ip_addr,
226 pub ip_dst: nbpf_ip_addr,
227 pub l4_src_port: u_int16_t,
228 pub l4_dst_port: u_int16_t,
229}
230
231#[repr(C, packed)]
232pub struct nbpf_pkt_info_t {
233 pub device_id: u_int16_t,
234 pub interface_id: u_int16_t,
235 pub dmac: [u_int8_t; 6],
236 pub smac: [u_int8_t; 6],
237 pub vlan_id: u_int16_t,
238 pub vlan_id_qinq: u_int16_t,
239 pub master_l7_proto: u_int16_t,
240 pub l7_proto: u_int16_t,
241 pub tuple: nbpf_pkt_info_tuple_t,
242 pub tunneled_tuple: nbpf_pkt_info_tuple_t,
243}
244
245#[repr(C, packed)]
246pub struct nbpf_rule_core_fields_byte_match_t {
247 pub protocol: u_int16_t,
248 pub offset: u_int16_t,
249 pub mask: u_int8_t,
250 pub relop: u_int8_t,
251 pub value: u_int8_t,
252 pub next: *mut nbpf_rule_core_fields_byte_match_t,
253}
254
255#[repr(C, packed)]
256pub struct nbpf_rule_core_fields_t {
257 pub not_rule: u_int8_t,
258 pub smac: [u_int8_t; 6],
259 pub dmac: [u_int8_t; 6],
260 pub proto: u_int8_t, pub ip_version: u_int8_t,
262 pub gtp: u_int8_t,
263 pub vlan: u_int8_t,
264 pub mpls: u_int8_t,
265 pub vlan_id: u_int16_t,
266 pub l7_proto: u_int16_t,
267 pub mpls_label: u_int16_t,
268 pub shost: nbpf_ip_addr,
269 pub dhost: nbpf_ip_addr,
270 pub shost_mask: nbpf_ip_addr,
271 pub dhost_mask: nbpf_ip_addr,
272 pub sport_low: u_int16_t,
273 pub sport_high: u_int16_t,
274 pub dport_low: u_int16_t,
275 pub dport_high: u_int16_t,
276 pub byte_match: *mut nbpf_rule_core_fields_byte_match_t,
277}
278
279#[repr(C, packed)]
280pub struct nbpf_rule_list_item_t {
281 pub fields: nbpf_rule_core_fields_t,
282 pub bidirectional: c_int,
283 pub next: *mut nbpf_rule_list_item_t,
284}
285
286#[repr(C, packed)]
287pub struct nbpf_rule_block_list_item_t {
288 pub rule_list_head: *mut nbpf_rule_list_item_t,
289 pub next: *mut nbpf_rule_block_list_item_t,
290}
291
292extern "C" {
293 pub fn nbpf_parse(
294 bpf_filter: *const c_char,
295 l7proto_by_name_callback: l7protocol_by_name_func,
296 ) -> *mut nbpf_tree_t;
297 pub fn nbpf_match(tree: *const nbpf_tree_t, h: *const nbpf_pkt_info_t) -> c_int;
298 pub fn nbpf_free(t: *mut nbpf_tree_t);
299
300 pub fn nbpf_generate_rules(tree: *const nbpf_tree_t) -> *mut nbpf_rule_list_item_t;
301 pub fn nbpf_rule_list_free(list: *mut nbpf_rule_list_item_t);
302
303 pub fn nbpf_generate_optimized_rules(
304 tree: *const nbpf_tree_t,
305 ) -> *mut nbpf_rule_block_list_item_t;
306 pub fn nbpf_rule_block_list_free(blocks: *mut nbpf_rule_block_list_item_t);
307}
308
309#[cfg(test)]
310mod test {
311 use libc::c_char;
312 use std::ffi::CString;
313
314 use crate::{nbpf_ip_addr, nbpf_tree_t};
315
316 use super::{nbpf_free, nbpf_match, nbpf_parse, nbpf_pkt_info_t, nbpf_pkt_info_tuple_t};
317
318 #[test]
319 pub fn test_nbpf_base() {
320 unsafe {
321 let filter = "not host 192.168.89.1";
322 let filter = CString::new(filter).unwrap();
323 let tree = nbpf_parse(filter.as_ptr() as *const c_char, None);
324 let l4_src_port: u16 = 34;
325 let l4_dst_port: u16 = 345;
326
327 let pkt_info = nbpf_pkt_info_t {
328 device_id: 0,
329 interface_id: 0,
330 dmac: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
331 smac: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
332 vlan_id: 0,
333 vlan_id_qinq: 0,
334 master_l7_proto: 0,
335 l7_proto: 7,
336 tuple: nbpf_pkt_info_tuple_t {
337 eth_type: 0x0800,
338 ip_version: 4,
339 l3_proto: 17,
340 ip_tos: 0,
341 ip_src: nbpf_ip_addr { v4: 0x0100000A },
342 ip_dst: nbpf_ip_addr { v4: 0x0159A8C1 },
343 l4_src_port: l4_src_port.to_be(),
344 l4_dst_port: l4_dst_port.to_be(),
345 },
346 tunneled_tuple: nbpf_pkt_info_tuple_t {
347 eth_type: 0,
348 ip_version: 0,
349 l3_proto: 0,
350 ip_tos: 0,
351 ip_src: nbpf_ip_addr { v4: 0 },
352 ip_dst: nbpf_ip_addr { v4: 0 },
353 l4_src_port: 0,
354 l4_dst_port: 0,
355 },
356 };
357
358 let matched = nbpf_match(
359 tree as *const nbpf_tree_t,
360 &pkt_info as *const nbpf_pkt_info_t,
361 );
362
363 assert_eq!(matched, 1);
364
365 let pkt_info = nbpf_pkt_info_t {
366 device_id: 0,
367 interface_id: 0,
368 dmac: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
369 smac: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
370 vlan_id: 0,
371 vlan_id_qinq: 0,
372 master_l7_proto: 0,
373 l7_proto: 7,
374 tuple: nbpf_pkt_info_tuple_t {
375 eth_type: 0x0800,
376 ip_version: 4,
377 l3_proto: 17,
378 ip_tos: 0,
379 ip_src: nbpf_ip_addr { v4: 0x0100000A },
380 ip_dst: nbpf_ip_addr { v4: 0x0159A8C0 },
381 l4_src_port: l4_src_port.to_be(),
382 l4_dst_port: l4_dst_port.to_be(),
383 },
384 tunneled_tuple: nbpf_pkt_info_tuple_t {
385 eth_type: 0,
386 ip_version: 0,
387 l3_proto: 0,
388 ip_tos: 0,
389 ip_src: nbpf_ip_addr { v4: 0 },
390 ip_dst: nbpf_ip_addr { v4: 0 },
391 l4_src_port: 0,
392 l4_dst_port: 0,
393 },
394 };
395
396 let matched = nbpf_match(
397 tree as *const nbpf_tree_t,
398 &pkt_info as *const nbpf_pkt_info_t,
399 );
400
401 assert_eq!(matched, 0);
402 nbpf_free(tree);
403 }
404 }
405
406 #[test]
407 pub fn test_nbpf_complex() {
408 unsafe {
409 let filter = "(host 192.168.0.1 and port 3000) or (src host 10.0.0.1 and proto 17)";
410 let filter = CString::new(filter).unwrap();
411 let tree = nbpf_parse(filter.as_ptr() as *const c_char, None);
412 let l4_src_port: u16 = 3000;
413 let l4_dst_port: u16 = 345;
414
415 let pkt_info = nbpf_pkt_info_t {
416 device_id: 0,
417 interface_id: 0,
418 dmac: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
419 smac: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
420 vlan_id: 0,
421 vlan_id_qinq: 0,
422 master_l7_proto: 0,
423 l7_proto: 7,
424 tuple: nbpf_pkt_info_tuple_t {
425 eth_type: 0x0800,
426 ip_version: 4,
427 l3_proto: 17,
428 ip_tos: 0,
429 ip_src: nbpf_ip_addr { v4: 0x0100000A },
430 ip_dst: nbpf_ip_addr { v4: 0x0100A8C0 },
431 l4_src_port: l4_src_port.to_be(),
432 l4_dst_port: l4_dst_port.to_be(),
433 },
434 tunneled_tuple: nbpf_pkt_info_tuple_t {
435 eth_type: 0,
436 ip_version: 0,
437 l3_proto: 0,
438 ip_tos: 0,
439 ip_src: nbpf_ip_addr { v4: 0 },
440 ip_dst: nbpf_ip_addr { v4: 0 },
441 l4_src_port: 0,
442 l4_dst_port: 0,
443 },
444 };
445
446 let matched = nbpf_match(
447 tree as *const nbpf_tree_t,
448 &pkt_info as *const nbpf_pkt_info_t,
449 );
450
451 assert_eq!(matched, 1);
452 nbpf_free(tree);
453 }
454 }
455}