1- /*
2- * This file contains the code for the identity service
3- *
4-
5- * Functionalities:
6- * 1. Creates a PacketLog structure to track incoming packets
7- * 2. Creates a VethLog structure to track veth creation and veth eletetion events
8- * 3. VethLog Tracking Parameters: NAME,STATE,DEVICE_ADDRESS,EVENT_TYPE,NETNS INUM.
9- * 4. PacketLog Tracking Parameters: SRC_IP.SRC_PORT,DST_IP,DST_PORT,PROTOCOL,PID(HOOK)
10- * 5. Store CONNECTION_ID in a BPF LRU HASHMAP and pass PID to the user space to identify ACTIVE CONNECTIONS
11- */
1+ // docs:
2+ // This file contains the code for the identity service
3+ // Functionalities:
4+ // 1. Creates a PacketLog structure to track incoming packets
5+ // 2. Creates a VethLog structure to track veth creation and veth eletetion events
6+ // 3. VethLog Tracking Parameters: NAME,STATE,DEVICE_ADDRESS,EVENT_TYPE,NETNS INUM.
7+ // 4. PacketLog Tracking Parameters: SRC_IP.SRC_PORT,DST_IP,DST_PORT,PROTOCOL,PID(HOOK)
8+ // 5. Store CONNECTION_ID in a BPF LRU HASHMAP and pass PID to the user space to identify ACTIVE CONNECTIONS
9+ //
1210
1311#![ no_std]
1412#![ no_main]
1917mod bindings;
2018mod data_structures;
2119mod offsets;
22-
23- use core:: net:: Ipv4Addr ;
20+ mod tc;
21+ mod tcp_analyzer;
22+ mod veth_tracer;
2423
2524use aya_ebpf:: {
26- bindings:: { TC_ACT_OK , TC_ACT_SHOT } ,
27- helpers:: { bpf_get_current_pid_tgid, bpf_probe_read_kernel } ,
28- macros:: { classifier, kprobe } ,
29- programs:: { ProbeContext , TcContext } ,
25+ bindings:: { TC_ACT_OK , TC_ACT_SHOT } ,
26+ macros:: { classifier, kprobe} ,
27+ programs:: { ProbeContext , TcContext } ,
3028} ;
31- use aya_log_ebpf:: info;
3229
33- use crate :: bindings:: { net, net_device } ;
34- use crate :: data_structures:: { ConnArray , PacketLog , VethLog } ;
35- use crate :: data_structures:: { EVENTS , VETH_EVENTS , BLOCKLIST } ;
36- use crate :: offsets:: OFFSETS ;
30+ use crate :: tc:: try_identity_classifier;
31+ use crate :: veth_tracer:: try_veth_tracer;
32+ use crate :: tcp_analyzer:: try_tcp_analyzer;
3733
3834#[ kprobe]
3935pub fn veth_creation_trace ( ctx : ProbeContext ) -> u32 {
@@ -50,114 +46,38 @@ pub fn veth_deletion_trace(ctx: ProbeContext) -> u32 {
5046 }
5147}
5248
53- //read linux inner struct. takes a ptr to the structure and an offset
54- fn read_linux_inner_struct < T > ( ptr : * const u8 , offset : usize ) -> Result < * const T , i64 > {
55- if ptr. is_null ( ) {
56- return Err ( 1 ) ;
57- } else {
58- let inner_ptr = unsafe { ( ptr as * const u8 ) . add ( offset) } ;
59-
60- let inner_field: * const T = unsafe {
61- match bpf_probe_read_kernel ( inner_ptr as * const * const T ) {
62- Ok ( inner_field) => inner_field,
63- Err ( e) => {
64- return Err ( e) ;
65- }
66- }
67- } ;
68- Ok ( inner_field)
69- }
70- }
49+ // docs;
50+ // this kprobe retrieves pid data and task id of an incoming packet
7151
72- //T= type of return
73- fn read_linux_inner_value < T : Copy > ( ptr : * const u8 , offset : usize ) -> Result < T , i64 > {
74- if ptr. is_null ( ) {
75- return Err ( 1 ) ;
52+ #[ kprobe]
53+ pub fn tcp_message_tracer ( ctx : ProbeContext ) -> u32 {
54+ match try_tcp_analyzer ( ctx) {
55+ Ok ( ret_val) => ret_val,
56+ Err ( ret_val) => ret_val. try_into ( ) . unwrap_or ( 1 ) ,
7657 }
77-
78- let inner_ptr = unsafe { ( ptr as * const u8 ) . add ( offset) } ;
79-
80- let inner_value = unsafe {
81- match bpf_probe_read_kernel :: < T > ( inner_ptr as * const T ) {
82- Ok ( inner_field) => inner_field,
83- Err ( e) => {
84- return Err ( e) ;
85- }
86- }
87- } ;
88-
89- Ok ( inner_value)
9058}
9159
92- fn extract_netns_inum ( net_device_pointer : * const u8 ) -> Result < u32 , i64 > {
93- let possible_net_t_offset = 280 ;
94-
95- let net = read_linux_inner_struct :: < net > ( net_device_pointer, possible_net_t_offset) ?;
96-
97- let ns_common_offset = 120 ;
98-
99- let inum_offset = 16 ;
100- let inum_ptr = read_linux_inner_value :: < u32 > ( net as * const u8 , ns_common_offset + inum_offset) ?;
101- Ok ( inum_ptr)
102- }
103-
104- //mode selection:
105- //1->veth_creation_tracer
106- //2->veth_deletion_tracer
107- pub fn try_veth_tracer ( ctx : ProbeContext , mode : u8 ) -> Result < u32 , i64 > {
108- let net_device_pointer: * const net_device = ctx. arg ( 0 ) . ok_or ( 1i64 ) ?;
109-
110- // first control: i'm, verifying that the pointer is not null
111- if net_device_pointer. is_null ( ) {
112- return Err ( 1 ) ;
113- }
114-
115- let mut name_buf = [ 0u8 ; 16 ] ;
116- let mut dev_addr_buf = [ 0u32 ; 8 ] ;
117-
118- //name field
119- let name_field_offset = 304 ; // reading the name field offset
120-
121- let name_array: [ u8 ; 16 ] = read_linux_inner_value :: < [ u8 ; 16 ] > (
122- net_device_pointer as * const u8 ,
123- name_field_offset
124- ) ?;
60+ // docs: this classifier acts in the very first step when a packet is logged
61+
62+ // Linux hooks stack:
63+ //
64+ // 6.Socket Layer
65+ // |
66+ // 5.TCP Stack
67+ // |
68+ // 4.Netfilter
69+ // |
70+ // 3.Traffic control (TC)
71+ // |
72+ // 2.XDP
73+ // |
74+ // 1.Network interface
75+ // |
76+ // Incoming Packet
77+
78+ // so we also need to extract the data from a second source in a kprobe context and correlate the data to catch
79+ // most of the value, without losing the ability to block a packet from the very early stages
12580
126- //state field
127- let state_offset = 168 ;
128- let state: u8 = read_linux_inner_value :: < u8 > ( net_device_pointer as * const u8 , state_offset) ?;
129-
130- //dev_addr
131- let dev_addr_offset = 1080 ;
132- let dev_addr_array: [ u32 ; 8 ] = read_linux_inner_value :: < [ u32 ; 8 ] > (
133- net_device_pointer as * const u8 ,
134- dev_addr_offset
135- ) ?;
136-
137- let inum: u32 = extract_netns_inum ( net_device_pointer as * const u8 ) ?;
138- let pid: u32 = bpf_get_current_pid_tgid ( ) as u32 ; //extracting lower 32 bit corresponding to the PID
139-
140- //buffer copying for array types
141- name_buf. copy_from_slice ( & name_array) ;
142- dev_addr_buf. copy_from_slice ( & dev_addr_array) ;
143-
144- //compose the structure
145- let veth_data = VethLog {
146- name : name_buf,
147- state : state. into ( ) ,
148- dev_addr : dev_addr_buf,
149- event_type : mode,
150- netns : inum,
151- pid,
152- } ;
153-
154- //send the data to the userspace
155- unsafe {
156- VETH_EVENTS . output ( & ctx, & veth_data, 0 ) ;
157- }
158-
159- Ok ( 0 )
160- }
16181#[ classifier]
16282pub fn identity_classifier ( ctx : TcContext ) -> i32 {
16383 match try_identity_classifier ( ctx) {
@@ -166,84 +86,11 @@ pub fn identity_classifier(ctx: TcContext) -> i32 {
16686 }
16787}
16888
169- fn try_identity_classifier ( ctx : TcContext ) -> Result < ( ) , i64 > {
170- let eth_proto = u16:: from_be ( ctx. load :: < u16 > ( 12 ) . map_err ( |_| 1 ) ?) ;
171-
172- //only ipv4 protcol allowed
173- if eth_proto != OFFSETS :: IPV4_ETHERTYPE {
174- return Ok ( ( ) ) ;
175- }
176-
177- //read if the packets has Options
178- let first_ipv4_byte = u8:: from_be ( ctx. load :: < u8 > ( OFFSETS :: ETH_STACK_BYTES ) . map_err ( |_| 1 ) ?) ;
179- let ihl = ( first_ipv4_byte &
180- 0x0f ) as usize ; /* 0x0F=00001111 &=AND bit a bit operator to extract the last 4 bit*/
181- let ip_header_len = ihl * 4 ; //returns the header lenght in bytes
182-
183- //get the source ip,destination ip and connection id
184- let src_ip = ctx. load :: < u32 > ( OFFSETS :: SRC_T0TAL_BYTES_OFFSET ) . map_err ( |_| 1 ) ?; // ETH+SOURCE_ADDRESS
185- let src_port = u16:: from_be (
186- ctx
187- . load :: < u16 > (
188- OFFSETS :: ETH_STACK_BYTES + ip_header_len + OFFSETS :: SRC_PORT_OFFSET_FROM_IP_HEADER
189- )
190- . map_err ( |_| 1 ) ?
191- ) ; //14+IHL-Lenght+0
192- let dst_ip = ctx. load :: < u32 > ( OFFSETS :: DST_T0TAL_BYTES_OFFSET ) . map_err ( |_| 1 ) ?; // ETH+ DESTINATION_ADDRESS
193- let dst_port = u16:: from_be (
194- ctx
195- . load :: < u16 > (
196- OFFSETS :: ETH_STACK_BYTES + ip_header_len + OFFSETS :: DST_PORT_OFFSET_FROM_IP_HEADER
197- )
198- . map_err ( |_| 1 ) ?
199- ) ; //14+IHL-Lenght+0
200- let proto = u8:: from_be ( ctx. load :: < u8 > ( OFFSETS :: PROTOCOL_T0TAL_BYTES_OFFSET ) . map_err ( |_| 1 ) ?) ;
201-
202- let pid: u32 = bpf_get_current_pid_tgid ( ) as u32 ;
203-
204- //TODO: do not log internal communications such as minikube dashboard packets or kubectl api packets
205-
206- // check if the address is in the blocklist
207- let src_ip_be_bytes: [ u8 ; 4 ] = src_ip. to_be_bytes ( ) ; //transforming the src_ip in big endian bytes
208-
209- // ** blocklist logic
210- if ( unsafe { BLOCKLIST . get ( & src_ip_be_bytes) . is_some ( ) } ) {
211- info ! (
212- & ctx,
213- "Blocking address: {}. Reason: Address is in a BLOCKLIST" ,
214- Ipv4Addr :: from( src_ip_be_bytes)
215- ) ;
216- return Err ( 1 ) ;
217- } else {
218- let key = ConnArray {
219- src_ip,
220- dst_ip,
221- src_port,
222- dst_port,
223- proto,
224- } ;
225-
226- let log = PacketLog {
227- proto,
228- src_ip,
229- src_port,
230- dst_ip,
231- dst_port,
232- pid,
233- } ;
234- unsafe {
235- EVENTS . output ( & ctx, & log, 0 ) ; //output to userspace
236- }
237- }
238- Ok ( ( ) )
239- }
240-
24189//ref:https://elixir.bootlin.com/linux/v6.15.1/source/include/uapi/linux/ethtool.h#L536
24290//https://elixir.bootlin.com/linux/v6.15.1/source/drivers/net/veth.c#L268
24391//https://eunomia.dev/tutorials/3-fentry-unlink/
24492
24593#[ panic_handler]
24694fn panic ( _info : & core:: panic:: PanicInfo ) -> ! {
247- loop {
248- }
95+ loop { }
24996}
0 commit comments