nbpf_sys/
lib.rs

1#![allow(non_camel_case_types)]
2#![allow(non_snake_case)]
3#![allow(dead_code)]
4
5/// packet info
6mod packet_info;
7pub use packet_info::{IPversion, PacketInfo, PacketInfoBuilder};
8/// parser and matcher
9mod 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
94// Header qualifiers
95pub const NBPF_Q_OUTER: u8 = 1;
96pub const NBPF_Q_INNER: u8 = 2;
97
98// Protocol qualifiers
99pub 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
108// Direction qualifiers
109pub 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
114// Address qualifiers
115pub 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
130// Common qualifiers
131pub const NBPF_Q_DEFAULT: u8 = 0;
132pub const NBPF_Q_UNDEF: u8 = 255;
133
134// Rel Op
135pub const NBPF_R_EQ: u8 = 0; // ==
136pub const NBPF_R_NE: u8 = 1; // !=
137pub const NBPF_R_LT: u8 = 2; // <
138pub const NBPF_R_LE: u8 = 4; // <=
139pub const NBPF_R_GT: u8 = 3; // >
140pub const NBPF_R_GE: u8 = 5; // >=
141
142// Node types
143pub 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, // tcp, udp, sctp
261    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}