Skip to content

Commit

Permalink
Add flow filter capability to filter on TCP flags
Browse files Browse the repository at this point in the history
Signed-off-by: Mohamed Mahmoud <mmahmoud@redhat.com>
  • Loading branch information
msherif1234 committed Jul 14, 2024
1 parent 7138dc8 commit a9eafb3
Show file tree
Hide file tree
Showing 20 changed files with 144 additions and 25 deletions.
2 changes: 1 addition & 1 deletion bpf/flows.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ static inline int flow_monitor(struct __sk_buff *skb, u8 direction) {
id.direction = direction;

// check if this packet need to be filtered if filtering feature is enabled
bool skip = check_and_do_flow_filtering(&id);
bool skip = check_and_do_flow_filtering(&id, pkt.flags);
if (skip) {
return TC_ACT_OK;
}
Expand Down
21 changes: 17 additions & 4 deletions bpf/flows_filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ static __always_inline int is_equal_ip(u8 *ip1, u8 *ip2, u8 len) {
}

static __always_inline int do_flow_filter_lookup(flow_id *id, struct filter_key_t *key,
filter_action *action, u8 len, u8 offset) {
filter_action *action, u8 len, u8 offset,
u16 flags) {
int result = 0;

struct filter_value_t *rule = (struct filter_value_t *)bpf_map_lookup_elem(&filter_map, key);
Expand Down Expand Up @@ -115,6 +116,18 @@ static __always_inline int do_flow_filter_lookup(flow_id *id, struct filter_key_
goto end;
}
}
// for TCP only check TCP flags if its set
if (id->transport_protocol == IPPROTO_TCP) {
if (rule->tcpFlags != 0) {
if (rule->tcpFlags == flags) {
BPF_PRINTK("tcpFlags matched\n");
result++;
} else {
result = 0;
goto end;
}
}
}
break;
case IPPROTO_ICMP:
case IPPROTO_ICMPV6:
Expand Down Expand Up @@ -210,7 +223,7 @@ static __always_inline int flow_filter_setup_lookup_key(flow_id *id, struct filt
/*
* check if the flow match filter rule and return >= 1 if the flow is to be dropped
*/
static __always_inline int is_flow_filtered(flow_id *id, filter_action *action) {
static __always_inline int is_flow_filtered(flow_id *id, filter_action *action, u16 flags) {
struct filter_key_t key;
u8 len, offset;
int result = 0;
Expand All @@ -224,7 +237,7 @@ static __always_inline int is_flow_filtered(flow_id *id, filter_action *action)
return result;
}

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

return do_flow_filter_lookup(id, &key, action, len, offset);
return do_flow_filter_lookup(id, &key, action, len, offset, flags);
}

#endif //__FLOWS_FILTER_H__
2 changes: 1 addition & 1 deletion bpf/pca.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ static inline bool validate_pca_filter(struct __sk_buff *skb, direction dir) {
id.direction = dir;

// check if this packet need to be filtered if filtering feature is enabled
bool skip = check_and_do_flow_filtering(&id);
bool skip = check_and_do_flow_filtering(&id, pkt.flags);
if (skip) {
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion bpf/pkt_drops.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ static inline int trace_pkt_drop(void *ctx, u8 state, struct sk_buff *skb,
}

// check if this packet need to be filtered if filtering feature is enabled
bool skip = check_and_do_flow_filtering(&id);
bool skip = check_and_do_flow_filtering(&id, flags);
if (skip) {
return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion bpf/rtt_tracker.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ static inline int calculate_flow_rtt_tcp(struct sock *sk, struct sk_buff *skb) {
rtt *= 1000u;

// check if this packet need to be filtered if filtering feature is enabled
bool skip = check_and_do_flow_filtering(&id);
bool skip = check_and_do_flow_filtering(&id, flags);
if (skip) {
return 0;
}
Expand Down
29 changes: 17 additions & 12 deletions bpf/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,22 @@
#define SUBMIT 0

// Flags according to RFC 9293 & https://www.iana.org/assignments/ipfix/ipfix.xhtml
#define FIN_FLAG 0x01
#define SYN_FLAG 0x02
#define RST_FLAG 0x04
#define PSH_FLAG 0x08
#define ACK_FLAG 0x10
#define URG_FLAG 0x20
#define ECE_FLAG 0x40
#define CWR_FLAG 0x80
// Custom flags exported
#define SYN_ACK_FLAG 0x100
#define FIN_ACK_FLAG 0x200
#define RST_ACK_FLAG 0x400
typedef enum tcp_flags_t {
FIN_FLAG = 0x01,
SYN_FLAG = 0x02,
RST_FLAG = 0x04,
PSH_FLAG = 0x08,
ACK_FLAG = 0x10,
URG_FLAG = 0x20,
ECE_FLAG = 0x40,
CWR_FLAG = 0x80,
// Custom flags exported
SYN_ACK_FLAG = 0x100,
FIN_ACK_FLAG = 0x200,
RST_ACK_FLAG = 0x400,
} tcp_flags;
// Force emitting enum tcp_flags_t into the ELF.
const enum tcp_flags_t *unused10 __attribute__((unused));

#if defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
Expand Down Expand Up @@ -216,6 +220,7 @@ struct filter_value_t {
u8 icmpCode;
direction direction;
filter_action action;
tcp_flags tcpFlags;
u8 ip[IP_MAX_LEN];
} __attribute__((packed));
// Force emitting struct filter_value_t into the ELF.
Expand Down
4 changes: 2 additions & 2 deletions bpf/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -282,13 +282,13 @@ static inline long pkt_drop_lookup_and_update_flow(struct sk_buff *skb, flow_id
/*
* check if flow filter is enabled and if we need to continue processing the packet or not
*/
static inline bool check_and_do_flow_filtering(flow_id *id) {
static inline bool check_and_do_flow_filtering(flow_id *id, u16 flags) {
// check if this packet need to be filtered if filtering feature is enabled
if (enable_flows_filtering || enable_pca) {
filter_action action = ACCEPT;
u32 *filter_counter_p = NULL;
u32 initVal = 1, key = 0;
if (is_flow_filtered(id, &action) != 0 && action != MAX_FILTER_ACTIONS) {
if (is_flow_filtered(id, &action, flags) != 0 && action != MAX_FILTER_ACTIONS) {
// we have matching rules follow through the actions to decide if we should accept or reject the flow
// and update global counter for both cases
u32 reject_key = FILTER_REJECT_KEY, accept_key = FILTER_ACCEPT_KEY;
Expand Down
5 changes: 3 additions & 2 deletions docs/flow_filtering.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@ Rule-base filtering is a method to control the flow of packets cached in the eBP
- `FILTER_SOURCE_PORT_RANGE` - Source port range of the flow filter rule. using "80-100" format.
- `FILTER_DESTINATION_PORT` - Single Destination port of the flow filter rule.
- `FILTER_DESTINATION_PORT_RANGE` - Destination port range of the flow filter rule. using "80-100" format.
- `FILTER_PORT` - Single L4 port of the flow filter rule, can be either source or destination port.
- `FILTER_PORT_RANGE` - L4 port range of the flow filter rule. using "80-100" format can be either source or destination ports range.
- `FILTER_PORT` - Single L4 port of the flow filter rule can be either source or destination port.
- `FILTER_PORT_RANGE` - L4 port range of the flow filter rule. using "80100" format can be either a source or destination ports range.
- `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_TCP_FLAGS` - Filter based on TCP flags Possible values are SYN, SYN-ACK, ACK, FIN, RST, PSH, URG, ECE, CWR, FIN-ACK, RST_ACK

Note:
- for L4 ports configuration, you can use either single port config options or the range but not both.
Expand Down
1 change: 1 addition & 0 deletions pkg/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ func FlowsAgent(cfg *Config) (*Flows, error) {
FilterDestinationPort: ebpf.ConvertFilterPortsToInstr(cfg.FilterDestinationPort, cfg.FilterDestinationPortRange),
FilterSourcePort: ebpf.ConvertFilterPortsToInstr(cfg.FilterSourcePort, cfg.FilterSourcePortRange),
FilterPort: ebpf.ConvertFilterPortsToInstr(cfg.FilterPort, cfg.FilterPortRange),
FilterTCPFLags: cfg.FilterTCPFlags,
},
}

Expand Down
3 changes: 3 additions & 0 deletions pkg/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,9 @@ type Config struct {
// FilterAction is the action to filter flows.
// Possible values are "Accept" or "Reject".
FilterAction string `env:"FILTER_ACTION" envDefault:"Accept"`
// FilterTCPFlags is the TCP flags to filter flows.
// possible values are: SYN, SYN-ACK, ACK, FIN, RST, PSH, URG, ECE, CWR, FIN-ACK, RST-ACK
FilterTCPFlags string `env:"FILTER_TCP_FLAGS"`

/* Deprecated configs are listed below this line
* See manageDeprecatedConfigs function for details
Expand Down
17 changes: 17 additions & 0 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.
17 changes: 17 additions & 0 deletions pkg/ebpf/bpf_powerpc_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_powerpc_bpfel.o
Binary file not shown.
17 changes: 17 additions & 0 deletions pkg/ebpf/bpf_s390_bpfeb.go

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

Binary file modified pkg/ebpf/bpf_s390_bpfeb.o
Binary file not shown.
17 changes: 17 additions & 0 deletions pkg/ebpf/bpf_x86_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_x86_bpfel.o
Binary file not shown.
28 changes: 28 additions & 0 deletions pkg/ebpf/flow_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type FilterConfig struct {
FilterIcmpCode int
FilterPeerIP string
FilterAction string
FilterTCPFLags string
}

type Filter struct {
Expand Down Expand Up @@ -77,6 +78,7 @@ func (f *Filter) getFilterKey(config *FilterConfig) (BpfFilterKeyT, error) {
return key, nil
}

// nolint:cyclop
func (f *Filter) getFilterValue(config *FilterConfig) (BpfFilterValueT, error) {
val := BpfFilterValueT{}

Expand Down Expand Up @@ -132,6 +134,32 @@ func (f *Filter) getFilterValue(config *FilterConfig) (BpfFilterValueT, error) {
copy(val.Ip[:], ip.To16())
}
}

switch config.FilterTCPFLags {
case "SYN":
val.TcpFlags = BpfTcpFlagsTSYN_FLAG
case "SYN-ACK":
val.TcpFlags = BpfTcpFlagsTSYN_ACK_FLAG
case "ACK":
val.TcpFlags = BpfTcpFlagsTACK_FLAG
case "FIN":
val.TcpFlags = BpfTcpFlagsTFIN_FLAG
case "RST":
val.TcpFlags = BpfTcpFlagsTRST_FLAG
case "PUSH":
val.TcpFlags = BpfTcpFlagsTPSH_FLAG
case "URG":
val.TcpFlags = BpfTcpFlagsTURG_FLAG
case "ECE":
val.TcpFlags = BpfTcpFlagsTECE_FLAG
case "CWR":
val.TcpFlags = BpfTcpFlagsTCWR_FLAG
case "FIN-ACK":
val.TcpFlags = BpfTcpFlagsTFIN_ACK_FLAG
case "RST-ACK":
val.TcpFlags = BpfTcpFlagsTRST_ACK_FLAG
}

return val, nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/ebpf/tracer.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
)

// $BPF_CLANG and $BPF_CFLAGS are set by the Makefile.
//go:generate bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS -target amd64,arm64,ppc64le,s390x -type flow_metrics_t -type flow_id_t -type flow_record_t -type pkt_drops_t -type dns_record_t -type global_counters_key_t -type direction_t -type filter_action_t Bpf ../../bpf/flows.c -- -I../../bpf/headers
//go:generate bpf2go -cc $BPF_CLANG -cflags $BPF_CFLAGS -target amd64,arm64,ppc64le,s390x -type flow_metrics_t -type flow_id_t -type flow_record_t -type pkt_drops_t -type dns_record_t -type global_counters_key_t -type direction_t -type filter_action_t -type tcp_flags_t Bpf ../../bpf/flows.c -- -I../../bpf/headers

const (
qdiscType = "clsact"
Expand Down

0 comments on commit a9eafb3

Please sign in to comment.