Skip to content

Commit 8a6d8ce

Browse files
authored
NETOBSERV-1617: reuse flow filter capability with pcap feature (#359)
* NETOBSERV-1617: reuse flow filter capability with pcap feature rename all flow filters to just filter to allow reuse the same configs modify pca to use filter config update userspace, examples and doc Signed-off-by: Mohamed Mahmoud <[email protected]> * Allow sampling configs to be applied for PCA Signed-off-by: Mohamed Mahmoud <[email protected]> --------- Signed-off-by: Mohamed Mahmoud <[email protected]>
1 parent 58e5d37 commit 8a6d8ce

21 files changed

+265
-283
lines changed

bpf/configs.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
volatile const u32 sampling = 0;
77
volatile const u8 trace_messages = 0;
88
volatile const u8 enable_rtt = 0;
9-
volatile const u16 pca_port = 0;
10-
volatile const u8 pca_proto = 0;
9+
volatile const u8 enable_pca = 0;
1110
volatile const u8 enable_dns_tracking = 0;
1211
volatile const u8 enable_flows_filtering = 0;
1312
#endif //__CONFIGS_H__

bpf/pca.h

Lines changed: 37 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -36,93 +36,72 @@ static int attach_packet_payload(void *data, void *data_end, struct __sk_buff *s
3636
return TC_ACT_UNSPEC;
3737
}
3838

39-
static inline bool validate_pca_filter(u8 ipproto, void *ipheaderend, void *data_end) {
40-
// If filters: pca_proto and pca_port are not specified, export packet
41-
if (pca_proto == 0 && pca_port == 0)
42-
return true;
43-
44-
//Only export packets with protocol set by ENV var PCA_FILTER
45-
u16 sourcePort, destPort;
46-
if (ipproto != pca_proto) {
47-
return false;
48-
}
39+
static inline bool validate_pca_filter(struct __sk_buff *skb, direction dir) {
40+
pkt_info pkt;
41+
__builtin_memset(&pkt, 0, sizeof(pkt));
42+
flow_id id;
43+
__builtin_memset(&id, 0, sizeof(id));
4944

50-
if (ipproto == IPPROTO_TCP) {
51-
struct tcphdr *tcp_header = ipheaderend;
52-
if ((void *)tcp_header + sizeof(*tcp_header) > data_end) {
53-
return false;
54-
}
55-
sourcePort = tcp_header->source;
56-
destPort = tcp_header->dest;
57-
} else if (ipproto == IPPROTO_UDP) {
58-
struct udphdr *udp_header = ipheaderend;
59-
if ((void *)udp_header + sizeof(*udp_header) > data_end) {
60-
return false;
61-
}
62-
sourcePort = udp_header->source;
63-
destPort = udp_header->dest;
64-
} else if (ipproto == IPPROTO_SCTP) {
65-
struct sctphdr *sctp_header = ipheaderend;
66-
if ((void *)sctp_header + sizeof(*sctp_header) > data_end) {
67-
return false;
68-
}
69-
sourcePort = sctp_header->source;
70-
destPort = sctp_header->dest;
71-
} else {
72-
return false;
73-
}
74-
u16 pca_port_end = bpf_htons(pca_port);
75-
if (sourcePort == pca_port_end || destPort == pca_port_end) {
76-
return true;
77-
}
78-
return false;
79-
}
45+
pkt.id = &id;
8046

81-
static inline int export_packet_payload(struct __sk_buff *skb) {
8247
void *data_end = (void *)(long)skb->data_end;
8348
void *data = (void *)(long)skb->data;
84-
struct ethhdr *eth = data;
85-
struct iphdr *ip;
49+
struct ethhdr *eth = (struct ethhdr *)data;
8650

87-
if ((void *)eth + sizeof(*eth) > data_end) {
88-
return TC_ACT_UNSPEC;
51+
if (fill_ethhdr(eth, data_end, &pkt) == DISCARD) {
52+
return false;
8953
}
9054

91-
// Only IPv4 and IPv6 packets captured
92-
u16 ethType = bpf_ntohs(eth->h_proto);
93-
if (ethType != ETH_P_IP && ethType != ETH_P_IPV6) {
94-
return TC_ACT_UNSPEC;
55+
//Set extra fields
56+
id.if_index = skb->ifindex;
57+
id.direction = dir;
58+
59+
// check if this packet need to be filtered if filtering feature is enabled
60+
bool skip = check_and_do_flow_filtering(&id);
61+
if (skip) {
62+
return false;
9563
}
9664

97-
ip = data + sizeof(*eth);
98-
if ((void *)ip + sizeof(*ip) > data_end) {
99-
return TC_ACT_UNSPEC;
65+
return true;
66+
}
67+
68+
static inline int export_packet_payload(struct __sk_buff *skb, direction dir) {
69+
// If sampling is defined, will only parse 1 out of "sampling" flows
70+
if (sampling > 1 && (bpf_get_prandom_u32() % sampling) != 0) {
71+
return 0;
10072
}
10173

102-
if (validate_pca_filter(ip->protocol, (void *)ip + sizeof(*ip), data_end)) {
74+
void *data_end = (void *)(long)skb->data_end;
75+
void *data = (void *)(long)skb->data;
76+
77+
if (validate_pca_filter(skb, dir)) {
10378
return attach_packet_payload(data, data_end, skb);
10479
}
105-
return TC_ACT_UNSPEC;
80+
return 0;
10681
}
10782

10883
SEC("tc_pca_ingress")
10984
int tc_ingress_pca_parse(struct __sk_buff *skb) {
110-
return export_packet_payload(skb);
85+
export_packet_payload(skb, INGRESS);
86+
return TC_ACT_OK;
11187
}
11288

11389
SEC("tc_pca_egress")
11490
int tc_egress_pca_parse(struct __sk_buff *skb) {
115-
return export_packet_payload(skb);
91+
export_packet_payload(skb, EGRESS);
92+
return TC_ACT_OK;
11693
}
11794

11895
SEC("tcx_pca_ingress")
11996
int tcx_ingress_pca_parse(struct __sk_buff *skb) {
120-
return export_packet_payload(skb);
97+
export_packet_payload(skb, INGRESS);
98+
return TCX_NEXT;
12199
}
122100

123101
SEC("tcx_pca_egress")
124102
int tcx_egress_pca_parse(struct __sk_buff *skb) {
125-
return export_packet_payload(skb);
103+
export_packet_payload(skb, EGRESS);
104+
return TCX_NEXT;
126105
}
127106

128107
#endif /* __PCA_H__ */

bpf/types.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,9 @@ typedef struct dns_flow_id_t {
177177
// Enum to define global counters keys and share it with userspace
178178
typedef enum global_counters_key_t {
179179
HASHMAP_FLOWS_DROPPED_KEY = 0,
180-
FILTER_FLOWS_REJECT_KEY = 1,
181-
FILTER_FLOWS_ACCEPT_KEY = 2,
182-
FILTER_FLOWS_NOMATCH_KEY = 3,
180+
FILTER_REJECT_KEY = 1,
181+
FILTER_ACCEPT_KEY = 2,
182+
FILTER_NOMATCH_KEY = 3,
183183
MAX_DROPPED_FLOWS_KEY = 4,
184184
} global_counters_key;
185185

bpf/utils.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -284,14 +284,14 @@ static inline long pkt_drop_lookup_and_update_flow(struct sk_buff *skb, flow_id
284284
*/
285285
static inline bool check_and_do_flow_filtering(flow_id *id) {
286286
// check if this packet need to be filtered if filtering feature is enabled
287-
if (enable_flows_filtering) {
287+
if (enable_flows_filtering || enable_pca) {
288288
filter_action action = ACCEPT;
289289
u32 *filter_counter_p = NULL;
290290
u32 initVal = 1, key = 0;
291291
if (is_flow_filtered(id, &action) != 0 && action != MAX_FILTER_ACTIONS) {
292292
// we have matching rules follow through the actions to decide if we should accept or reject the flow
293293
// and update global counter for both cases
294-
u32 reject_key = FILTER_FLOWS_REJECT_KEY, accept_key = FILTER_FLOWS_ACCEPT_KEY;
294+
u32 reject_key = FILTER_REJECT_KEY, accept_key = FILTER_ACCEPT_KEY;
295295
bool skip = false;
296296

297297
switch (action) {
@@ -319,7 +319,7 @@ static inline bool check_and_do_flow_filtering(flow_id *id) {
319319
}
320320
} else {
321321
// we have no matching rules so we update global counter for flows that are not matched by any rule
322-
key = FILTER_FLOWS_NOMATCH_KEY;
322+
key = FILTER_NOMATCH_KEY;
323323
filter_counter_p = bpf_map_lookup_elem(&global_counters, &key);
324324
if (!filter_counter_p) {
325325
bpf_map_update_elem(&global_counters, &key, &initVal, BPF_ANY);

cmd/netobserv-ebpf-agent.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,6 @@ func main() {
5454
logrus.WithField("configuration", fmt.Sprintf("%#v", config)).Debugf("configuration loaded")
5555

5656
if config.EnablePCA {
57-
if config.PCAFilters == "" {
58-
logrus.Info("[PCA] NetObserv eBPF Agent instantiated without filters to identify packets. All packets will be captured. This might cause reduced performance.")
59-
}
6057
packetsAgent, err := agent.PacketsAgent(&config)
6158
if err != nil {
6259
logrus.WithError(err).Fatal("[PCA] can't instantiate NetObserv eBPF Agent")

docs/flow_filtering.md

Lines changed: 60 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,80 @@
1-
# eBPF Flow Rule Based Filtering
1+
# eBPF Rule Based Filtering
22

33
## Introduction
44

5-
Flow rule-base filtering is a method to control the flow of packets cached in the eBPF flows table based on certain configuration
5+
Rule-base filtering is a method to control the flow of packets cached in the eBPF flows table based on certain configuration
66

7-
## Flow filter rule configuration
7+
## Filter rule configuration
88

9-
The Flow filter rule consists of two parts mandatory and optional parameters.
9+
The filter rule consists of two parts mandatory and optional parameters.
1010

1111
### Mandatory parameters
1212

13-
- `FLOW_FILTER_IP_CIDR` - IP address and CIDR mask for the flow filter rule, supports IPv4 and IPv6 address format.
13+
- `FILTER_IP_CIDR` - IP address and CIDR mask for the flow filter rule, supports IPv4 and IPv6 address format.
1414
If wanted to match against any IP, user can use `0.0.0.0/0` or `::/0` for IPv4 and IPv6 respectively.
15-
- `FLOW_FILTER_ACTION` - Action to be taken for the flow filter rule. Possible values are `Accept` and `Reject`.
16-
- For the matching rule with `Accept` action this flow will be allowed to be cached in eBPF table, with updated global metric `FlowFilterAcceptCounter`.
17-
- For the matching rule with `Reject` action this flow will not be cached in eBPF table, with updated global metric `FlowFilterRejectCounter`.
15+
- `FILTER_ACTION` - Action to be taken for the flow filter rule. Possible values are `Accept` and `Reject`.
16+
- For the matching rule with `Accept` action this flow will be allowed to be cached in eBPF table, with updated global metric `FilterAcceptCounter`.
17+
- For the matching rule with `Reject` action this flow will not be cached in eBPF table, with updated global metric `FilterRejectCounter`.
1818
- If the rule is not matched, based on the configured action if its `Accept` the flow will not be cached in eBPF table,
19-
if the action is `Reject` then the flow will be cached in the eBPF table and a global metric `FlowFilterNoMatchCounter` will be updated.
19+
if the action is `Reject` then the flow will be cached in the eBPF table and a global metric `FilterNoMatchCounter` will be updated.
2020

2121
### Optional parameters
2222

23-
- `FLOW_FILTER_DIRECTION` - Direction of the flow filter rule. Possible values are `Ingress` and `Egress`.
24-
- `FLOW_FILTER_PROTOCOL` - Protocol of the flow filter rule. Possible values are `TCP`, `UDP`, `SCTP`, `ICMP`, `ICMPv6`.
25-
- `FLOW_FILTER_SOURCE_PORT` - Single Source port of the flow filter rule.
26-
- `FLOW_FILTER_SOURCE_PORT_RANGE` - Source port range of the flow filter rule. using "80-100" format.
27-
- `FLOW_FILTER_DESTINATION_PORT` - Single Destination port of the flow filter rule.
28-
- `FLOW_FILTER_DESTINATION_PORT_RANGE` - Destination port range of the flow filter rule. using "80-100" format.
29-
- `FLOW_FILTER_PORT` - Single L4 port of the flow filter rule, can be either source or destination port.
30-
- `FLOW_FILTER_PORT_RANGE` - L4 port range of the flow filter rule. using "80-100" format can be either source or destination ports range.
31-
- `FLOW_FILTER_ICMP_TYPE` - ICMP type of the flow filter rule.
32-
- `FLOW_FILTER_ICMP_CODE` - ICMP code of the flow filter rule.
33-
- `FLOW_FILTER_PEER_IP` - Specific Peer IP address of the flow filter rule.
23+
- `FILTER_DIRECTION` - Direction of the flow filter rule. Possible values are `Ingress` and `Egress`.
24+
- `FILTER_PROTOCOL` - Protocol of the flow filter rule. Possible values are `TCP`, `UDP`, `SCTP`, `ICMP`, `ICMPv6`.
25+
- `FILTER_SOURCE_PORT` - Single Source port of the flow filter rule.
26+
- `FILTER_SOURCE_PORT_RANGE` - Source port range of the flow filter rule. using "80-100" format.
27+
- `FILTER_DESTINATION_PORT` - Single Destination port of the flow filter rule.
28+
- `FILTER_DESTINATION_PORT_RANGE` - Destination port range of the flow filter rule. using "80-100" format.
29+
- `FILTER_PORT` - Single L4 port of the flow filter rule, can be either source or destination port.
30+
- `FILTER_PORT_RANGE` - L4 port range of the flow filter rule. using "80-100" format can be either source or destination ports range.
31+
- `FILTER_ICMP_TYPE` - ICMP type of the flow filter rule.
32+
- `FILTER_ICMP_CODE` - ICMP code of the flow filter rule.
33+
- `FILTER_PEER_IP` - Specific Peer IP address of the flow filter rule.
3434

3535
Note:
36-
- for L4 ports configuration you can use either single port config options or the range but not both.
37-
- use either specific src and/or dst ports or the generic port config that works for both direction.
36+
- for L4 ports configuration, you can use either single port config options or the range but not both.
37+
- use either specific src and/or dst ports or the generic port config that works for both directions.
3838

3939
## How does Flow Filtering work
4040

41-
### Flow Filter and CIDR Matching
41+
### Filter and CIDR Matching
4242

4343
The flow filter examines incoming or outgoing packets and attempts to match the source IP address or the destination IP address
44-
of each packet against a CIDR range specified in the `FLOW_FILTER_IP_CIDR` parameter.
44+
of each packet against a CIDR range specified in the `FILTER_IP_CIDR` parameter.
4545
If the packet's source or destination IP address falls within the specified CIDR range, the filter takes action based on the configured rules.
4646
This action could involve allowing the packet to be cached in an eBPF flow table or blocking it.
4747

48-
### Matching Specific Endpoints with `FLOW_FILTER_PEER_IP`
48+
### Matching Specific Endpoints with `FILTER_PEER_IP`
4949

50-
The `FLOW_FILTER_PEER_IP` parameter specifies the IP address of a specific endpoint.
50+
The `FILTER_PEER_IP` parameter specifies the IP address of a specific endpoint.
5151
Depending on whether the traffic is ingress (incoming) or egress (outgoing), this IP address is used to further refine
5252
the filtering process:
53-
- In ingress traffic filtering, the `FLOW_FILTER_PEER_IP` is used to match against the destination IP address of the packet.
53+
- In ingress traffic filtering, the `FILTER_PEER_IP` is used to match against the destination IP address of the packet.
5454
After the initial CIDR matching, the filter then narrows down the scope to packets destined for a specific endpoint
5555
specified by `FLOW_FILTER_PEER_IP`.
56-
- In egress traffic filtering, the `FLOW_FILTER_PEER_IP` is used to match against the source IP address of the packet.
56+
- In egress traffic filtering, the `FILTER_PEER_IP` is used to match against the source IP address of the packet.
5757
After the initial CIDR matching, the filter narrows down the scope to packets originating from a specific endpoint
58-
specified by `FLOW_FILTER_PEER_IP`.
58+
specified by `FILTER_PEER_IP`.
5959

60-
### How to fine tune the flow filter rule configuration?
60+
### How to fine-tune the flow filter rule configuration?
6161

6262
We have many configuration options available for the flow filter rule configuration, but we can use them in combination to achieve the desired
63-
flow filter rule configuration. Let's use some examples to understand how to fine tune the flow filter rule configuration.
63+
flow filter rule configuration. Let's use some examples to understand how to fine-tune the flow filter rule configuration.
6464

6565
#### Use-case 1:
6666

6767
Filter k8s service traffic to specific POD IP endpoint.
68-
For example if we wanted to filter in incoming k8s service traffic coming from source `172.210.150.100` for `SCTP` protocol,
69-
on specific dport range 80-100, and targeting specific POD IP endpoint at `10.10.10.10` we can use the following configuration:
68+
For example, if we wanted to filter in incoming k8s service traffic coming from source `172.210.150.100` for `SCTP` protocol,
69+
on specific dport range 80100, and targeting specific POD IP endpoint at `10.10.10.10` we can use the following configuration:
7070

7171
```shell
72-
FLOW_FILTER_IP_CIDR=172.210.150.1/24
73-
FLOW_FILTER_ACTION=Accept
74-
FLOW_FILTER_PROTOCOL=SCTP
75-
FLOW_FILTER_DIRECTION=Ingress
76-
FLOW_FILTER_DESTINATION_PORT_RANGE=80-100
77-
FLOW_FILTER_PEER_IP=10.10.10.10
72+
FILTER_IP_CIDR=172.210.150.1/24
73+
FILTER_ACTION=Accept
74+
FILTER_PROTOCOL=SCTP
75+
FILTER_DIRECTION=Ingress
76+
FILTER_DESTINATION_PORT_RANGE=80-100
77+
FILTER_PEER_IP=10.10.10.10
7878
```
7979

8080
#### Use-case 2:
@@ -83,12 +83,12 @@ Users wanted to see flows after EgressIP feature is configured with EgressIP `19
8383
to any cluster's outside addresses (destinations is unknown or don't care), so they can use the following configuration:
8484

8585
```shell
86-
FLOW_FILTER_IP_CIDR=0.0.0.0/0
87-
FLOW_FILTER_ACTION=Accept
88-
FLOW_FILTER_PROTOCOL=TCP
89-
FLOW_FILTER_DIRECTION=Egress
90-
FLOW_FILTER_SOURCE_PORT=100
91-
FLOW_FILTER_PEER_IP=192.168.127.12
86+
FILTER_IP_CIDR=0.0.0.0/0
87+
FILTER_ACTION=Accept
88+
FILTER_PROTOCOL=TCP
89+
FILTER_DIRECTION=Egress
90+
FILTER_SOURCE_PORT=100
91+
FILTER_PEER_IP=192.168.127.12
9292
```
9393

9494
#### Use-case 3:
@@ -97,22 +97,22 @@ OpenShift ovn kubernetes CNI uses `169.254.169.1-4` as masquerade addresses when
9797
I am not interested in capturing any those packets, so I can use the following configuration:
9898

9999
```shell
100-
FLOW_FILTER_IP_CIDR=169.254.169.1/24
101-
FLOW_FILTER_ACTION=Reject
102-
FLOW_FILTER_DIRECTION=Ingress
100+
FILTER_IP_CIDR=169.254.169.1/24
101+
FILTER_ACTION=Reject
102+
FILTER_DIRECTION=Ingress
103103
```
104104

105105
#### Use-case 4:
106106

107-
We have case where ping traffic is going between PODA `1.1.1.10` to PODB in different node `1.2.1.10` for that we can use the following configuration:
107+
We have a case where ping traffic is going between PODA `1.1.1.10` to PODB in different node `1.2.1.10` for that we can use the following configuration:
108108

109109
```shell
110-
FLOW_FILTER_IP_CIDR=1.1.1.10/32
111-
FLOW_FILTER_ACTION=Accept
112-
FLOW_FILTER_DIRECTION=Ingress
113-
FLOW_FILTER_PROTOCOL=ICMP
114-
FLOW_FILTER_PEER_IP=1.2.1.10
115-
FLOW_FILTER_ICMP_TYPE=8
110+
FILTER_IP_CIDR=1.1.1.10/32
111+
FILTER_ACTION=Accept
112+
FILTER_DIRECTION=Ingress
113+
FILTER_PROTOCOL=ICMP
114+
FILTER_PEER_IP=1.2.1.10
115+
FILTER_ICMP_TYPE=8
116116
```
117117

118118
#### Use-case 5:
@@ -121,9 +121,9 @@ We wanted to filter in `curl` request and response for TCP flow going from PODA
121121
for that we can use the following configuration:
122122

123123
```shell
124-
FLOW_FILTER_IP_CIDR=1.1.1.10/32
125-
FLOW_FILTER_ACTION=Accept
126-
FLOW_FILTER_PROTOCOL=TCP
127-
FLOW_FILTER_PORT=80
128-
FLOW_FILTER_PEER_IP=1.2.1.10
124+
FILTER_IP_CIDR=1.1.1.10/32
125+
FILTER_ACTION=Accept
126+
FILTER_PROTOCOL=TCP
127+
FILTER_PORT=80
128+
FILTER_PEER_IP=1.2.1.10
129129
```

examples/packetcapture-dump/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Start the packetcapture-client using: (in a secondary shell)
1919

2020
Start the agent using:
2121
```bash
22-
sudo TARGET_HOST=localhost TARGET_PORT=9990 ENABLE_PCA=true PCA_FILTER=tcp,22 ./bin/netobserv-ebpf-agent
22+
sudo TARGET_HOST=localhost TARGET_PORT=9990 ENABLE_PCA="true" FILTER_IP_CIDR="0.0.0.0/0" FILTER_PROTOCOL="TCP" FILTER_PORT=22 FILTER_ACTION="Accept" ./bin/netobserv-ebpf-agent
2323
```
2424

2525
You should see output such as:

0 commit comments

Comments
 (0)