Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .mk/bc.mk
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ define MAPS
"packets_record":"perf_event_array",
"dns_flows":"hash",
"global_counters":"per_cpu_array",
"filter_map":"lpm_trie"
"filter_map":"lpm_trie",
"peer_filter_map":"lpm_trie"
}
endef

Expand Down
120 changes: 52 additions & 68 deletions bpf/flows_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,75 @@
if (trace_messages) \
bpf_printk(fmt, ##args)

static __always_inline int is_zero_ip(u8 *ip, u8 len) {
for (int i = 0; i < len; i++) {
if (ip[i] != 0) {
BPF_PRINTK("ip not zero ip[%d]:%d\n", i, ip[i]);
return 0;
}
}
return 1;
}
static __always_inline int flow_filter_setup_lookup_key(flow_id *id, struct filter_key_t *key,
u8 *len, u8 *offset, bool use_src_ip,
u16 eth_protocol) {

static __always_inline int is_equal_ip(u8 *ip1, u8 *ip2, u8 len) {
for (int i = 0; i < len; i++) {
if (ip1[i] != ip2[i]) {
BPF_PRINTK("ip mismatched ip1[%d]:%d not equal to ip2[%d]:%d\n", i, ip1[i], i, ip2[i]);
return 0;
if (eth_protocol == ETH_P_IP) {
*len = sizeof(u32);
*offset = sizeof(ip4in6);
if (use_src_ip) {
__builtin_memcpy(key->ip_data, id->src_ip + *offset, *len);
} else {
__builtin_memcpy(key->ip_data, id->dst_ip + *offset, *len);
}
key->prefix_len = 32;
} else if (eth_protocol == ETH_P_IPV6) {
*len = IP_MAX_LEN;
*offset = 0;
if (use_src_ip) {
__builtin_memcpy(key->ip_data, id->src_ip + *offset, *len);
} else {
__builtin_memcpy(key->ip_data, id->dst_ip + *offset, *len);
}
key->prefix_len = 128;
} else {
return -1;
}
return 1;
return 0;
}

static __always_inline int do_flow_filter_lookup(flow_id *id, struct filter_key_t *key,
filter_action *action, u8 len, u8 offset,
u16 flags, u32 drop_reason, u32 *sampling,
u8 direction) {
u8 direction, bool use_src_ip, u16 eth_protocol) {
int result = 0;

struct filter_value_t *rule = (struct filter_value_t *)bpf_map_lookup_elem(&filter_map, key);

if (rule) {
BPF_PRINTK("rule found drop_reason %d flags %d\n", drop_reason, flags);
BPF_PRINTK("rule found drop_reason %d flags %d do_peerCIDR_lookup %d\n", drop_reason, flags,
rule->do_peerCIDR_lookup);
result++;
if (rule->action != MAX_FILTER_ACTIONS) {
BPF_PRINTK("action matched: %d\n", rule->action);
*action = rule->action;
result++;
}

if (rule->do_peerCIDR_lookup) {
struct filter_key_t peerKey;
__builtin_memset(&peerKey, 0, sizeof(peerKey));
// PeerCIDR lookup will will target the opposite IP compared to original CIDR lookup
// In other words if cidr is using srcIP then peerCIDR will be the dstIP
if (flow_filter_setup_lookup_key(id, &peerKey, &len, &offset, use_src_ip,
eth_protocol) < 0) {
BPF_PRINTK("peerCIDR failed to setup lookup key\n");
result = 0;
goto end;
}

u8 *peer_result = (u8 *)bpf_map_lookup_elem(&peer_filter_map, &peerKey);
if (peer_result) {
BPF_PRINTK("peerCIDR matched\n");
result++;
} else {
BPF_PRINTK("peerCIDR couldn't find a matching key\n");
result = 0;
goto end;
}
}

if (rule->sample && sampling != NULL) {
BPF_PRINTK("sampling action is set to %d\n", rule->sample);
*sampling = rule->sample;
Expand Down Expand Up @@ -160,27 +193,6 @@ static __always_inline int do_flow_filter_lookup(flow_id *id, struct filter_key_
goto end;
}

if (!is_zero_ip(rule->ip, len)) {
// for Ingress side we can filter using dstIP and for Egress side we can filter using srcIP
if (direction == INGRESS) {
if (is_equal_ip(rule->ip, id->dst_ip + offset, len)) {
BPF_PRINTK("dstIP matched\n");
result++;
} else {
result = 0;
goto end;
}
} else {
if (is_equal_ip(rule->ip, id->src_ip + offset, len)) {
BPF_PRINTK("srcIP matched\n");
result++;
} else {
result = 0;
goto end;
}
}
}

if (rule->direction != MAX_DIRECTION) {
if (rule->direction == direction) {
BPF_PRINTK("direction matched\n");
Expand All @@ -206,34 +218,6 @@ static __always_inline int do_flow_filter_lookup(flow_id *id, struct filter_key_
return result;
}

static __always_inline int flow_filter_setup_lookup_key(flow_id *id, struct filter_key_t *key,
u8 *len, u8 *offset, bool use_src_ip,
u16 eth_protocol) {

if (eth_protocol == ETH_P_IP) {
*len = sizeof(u32);
*offset = sizeof(ip4in6);
if (use_src_ip) {
__builtin_memcpy(key->ip_data, id->src_ip + *offset, *len);
} else {
__builtin_memcpy(key->ip_data, id->dst_ip + *offset, *len);
}
key->prefix_len = 32;
} else if (eth_protocol == ETH_P_IPV6) {
*len = IP_MAX_LEN;
*offset = 0;
if (use_src_ip) {
__builtin_memcpy(key->ip_data, id->src_ip + *offset, *len);
} else {
__builtin_memcpy(key->ip_data, id->dst_ip + *offset, *len);
}
key->prefix_len = 128;
} else {
return -1;
}
return 0;
}

/*
* check if the flow match filter rule and return >= 1 if the flow is to be dropped
*/
Expand All @@ -254,7 +238,7 @@ static __always_inline int is_flow_filtered(flow_id *id, filter_action *action,
}

result = do_flow_filter_lookup(id, &key, action, len, offset, flags, drop_reason, sampling,
direction);
direction, false, eth_protocol);
// we have a match so return
if (result > 0) {
return result;
Expand All @@ -267,7 +251,7 @@ static __always_inline int is_flow_filtered(flow_id *id, filter_action *action,
}

return do_flow_filter_lookup(id, &key, action, len, offset, flags, drop_reason, sampling,
direction);
direction, true, eth_protocol);
}

#endif //__FLOWS_FILTER_H__
12 changes: 11 additions & 1 deletion bpf/maps_definition.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ struct {
__uint(pinning, LIBBPF_PIN_BY_NAME);
} global_counters SEC(".maps");

// LPM trie map used to filter traffic by IP address CIDR and direction
// LPM trie map used to filter traffic by IP address CIDR
struct {
__uint(type, BPF_MAP_TYPE_LPM_TRIE);
__type(key, struct filter_key_t);
Expand All @@ -69,4 +69,14 @@ struct {
__uint(pinning, LIBBPF_PIN_BY_NAME);
} filter_map SEC(".maps");

// LPM trie map used to filter traffic by peer IP address CIDR
struct {
__uint(type, BPF_MAP_TYPE_LPM_TRIE);
__type(key, struct filter_key_t);
__type(value, u8);
__uint(max_entries, MAX_FILTER_ENTRIES);
__uint(map_flags, BPF_F_NO_PREALLOC);
__uint(pinning, LIBBPF_PIN_BY_NAME);
} peer_filter_map SEC(".maps");

#endif //__MAPS_DEFINITION_H__
4 changes: 2 additions & 2 deletions bpf/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@ struct filter_value_t {
tcp_flags tcpFlags;
u8 filter_drops;
u32 sample;
u8 ip[IP_MAX_LEN];
} __attribute__((packed));
u8 do_peerCIDR_lookup;
} filter_value;

// Force emitting enums/structs into the ELF
const struct filter_value_t *unused12 __attribute__((unused));
Expand Down
16 changes: 9 additions & 7 deletions docs/flow_filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Rule-base filtering is a method to control the flow of packets cached in the eBP
- `FILTER_ICMP_TYPE` - ICMP type of the flow filter rule.
- `FILTER_ICMP_CODE` - ICMP code of the flow filter rule.
- `FILTER_PEER_IP` - Specific Peer IP address of the flow filter rule.
- `FILTER_PEER_CIDR` - Specific Peer IP CIDR of the flow filter rule.
- `FILTER_TCP_FLAGS` - Filter based on TCP flags Possible values are SYN, SYN-ACK, ACK, FIN, RST, PSH, URG, ECE, CWR, FIN-ACK, RST_ACK
- `FILTER_DROPS` - Filter flows when packets drop feature is enabled to filter only flows with drop cause not 0.

Expand All @@ -50,17 +51,18 @@ of each packet against a CIDR range specified in the `FILTER_IP_CIDR` parameter.
If the packet's source or destination IP address falls within the specified CIDR range, the filter takes action based on the configured rules.
This action could involve allowing the packet to be cached in an eBPF flow table or blocking it.

### Matching Specific Endpoints with `FILTER_PEER_IP`
### Matching Specific Endpoints with `FILTER_PEER_IP` or `FILTER_PEER_CIDR`

The `FILTER_PEER_IP` parameter specifies the IP address of a specific endpoint.
The `FILTER_PEER_IP` parameter specifies the IP address of a specific endpoint, while
`FILTER_PEER_CIDR` specifies subnet for range of endpoints.
Depending on whether the traffic is ingress (incoming) or egress (outgoing), this IP address is used to further refine
the filtering process:
- In ingress traffic filtering, the `FILTER_PEER_IP` is used to match against the destination IP address of the packet.
- In ingress traffic filtering, the `FILTER_PEER_IP`/`FILTER_PEER_CIDR` is used to match against the destination IP(s) address of the packet.
After the initial CIDR matching, the filter then narrows down the scope to packets destined for a specific endpoint
specified by `FLOW_FILTER_PEER_IP`.
- In egress traffic filtering, the `FILTER_PEER_IP` is used to match against the source IP address of the packet.
After the initial CIDR matching, the filter narrows down the scope to packets originating from a specific endpoint
specified by `FILTER_PEER_IP`.
- In egress traffic filtering, the `FILTER_PEER_IP`/`FILTER_PEER_CIDR` is used to match against the source IP(s) address of the packet.
After the initial CIDR matching, the filter narrows down the scope to packets originating from a specific endpoint(s)
specified by `FILTER_PEER_IP` or `FILTER_PEER_CIDR`.

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

Expand Down Expand Up @@ -130,5 +132,5 @@ for that we can use the following configuration:
FILTER_ACTION=Accept
FILTER_PROTOCOL=TCP
FILTER_PORT=80
FILTER_PEER_IP=1.2.1.10
FILTER_PEER_CIDR=1.2.1.10/32
```
1 change: 1 addition & 0 deletions pkg/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ func FlowsAgent(cfg *Config) (*Flows, error) {
FilterIPCIDR: r.FilterIPCIDR,
FilterProtocol: r.FilterProtocol,
FilterPeerIP: r.FilterPeerIP,
FilterPeerCIDR: r.FilterPeerCIDR,
FilterDestinationPort: tracer.ConvertFilterPortsToInstr(r.FilterDestinationPort, r.FilterDestinationPortRange, r.FilterDestinationPorts),
FilterSourcePort: tracer.ConvertFilterPortsToInstr(r.FilterSourcePort, r.FilterSourcePortRange, r.FilterSourcePorts),
FilterPort: tracer.ConvertFilterPortsToInstr(r.FilterPort, r.FilterPortRange, r.FilterPorts),
Expand Down
3 changes: 3 additions & 0 deletions pkg/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ type FlowFilter struct {
FilterDrops bool `json:"drops,omitempty"`
// FilterSample is the sample rate this matching flow will use
FilterSample uint32 `json:"sample,omitempty"`
// FilterPeerCIDR is the PeerIP CIDR to filter flows.
// Example: 10.10.10.0/24 or 100:100:100:100::/64, default is 0.0.0.0/0
FilterPeerCIDR string `json:"peer_cidr,omitempty"`
}

type Config struct {
Expand Down
1 change: 1 addition & 0 deletions pkg/agent/packets_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ func PacketsAgent(cfg *Config) (*Packets, error) {
FilterIPCIDR: r.FilterIPCIDR,
FilterProtocol: r.FilterProtocol,
FilterPeerIP: r.FilterPeerIP,
FilterPeerCIDR: r.FilterPeerCIDR,
FilterDestinationPort: tracer.ConvertFilterPortsToInstr(r.FilterDestinationPort, r.FilterDestinationPortRange, r.FilterDestinationPorts),
FilterSourcePort: tracer.ConvertFilterPortsToInstr(r.FilterSourcePort, r.FilterSourcePortRange, r.FilterSourcePorts),
FilterPort: tracer.ConvertFilterPortsToInstr(r.FilterPort, r.FilterPortRange, r.FilterPorts),
Expand Down
48 changes: 27 additions & 21 deletions pkg/ebpf/bpf_arm64_bpfel.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file modified pkg/ebpf/bpf_arm64_bpfel.o
Binary file not shown.
Loading
Loading