diff --git a/bpf/flows.c b/bpf/flows.c index 494cba5f61ded6b88d84222230a9c898955ccbe4..6278f9eb6a21dd1136050cbe2f9173bfc269081c 100644 --- a/bpf/flows.c +++ b/bpf/flows.c @@ -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; } diff --git a/bpf/flows_filter.h b/bpf/flows_filter.h index 3f4204148a930b2a8e96ccb4df31e88d2413d296..d2246ea97c81e7ae8eb66964816478aee02a924e 100644 --- a/bpf/flows_filter.h +++ b/bpf/flows_filter.h @@ -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); @@ -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: @@ -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; @@ -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; @@ -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__ diff --git a/bpf/pca.h b/bpf/pca.h index 295f983b168bcf79361e594a2639182caf15d26b..739982e978358f47714484dead235e99b878b4ec 100644 --- a/bpf/pca.h +++ b/bpf/pca.h @@ -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; } diff --git a/bpf/pkt_drops.h b/bpf/pkt_drops.h index 4435fba52eac5e43537721db51e02ad0439f4904..04235344262db5b0c763319fa32966abab8dd90e 100644 --- a/bpf/pkt_drops.h +++ b/bpf/pkt_drops.h @@ -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; } diff --git a/bpf/rtt_tracker.h b/bpf/rtt_tracker.h index 8368772abdd7ebcce5ced98de61f9d88d2502eaa..105ded7f87ba56af01abf2afe217b590dd42bbeb 100644 --- a/bpf/rtt_tracker.h +++ b/bpf/rtt_tracker.h @@ -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; } diff --git a/bpf/types.h b/bpf/types.h index c6df9b1e070724ad13b47b524a8020383eb827b1..730abd8d4fd997ff05a248158d08416b293b7415 100644 --- a/bpf/types.h +++ b/bpf/types.h @@ -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__ @@ -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. diff --git a/bpf/utils.h b/bpf/utils.h index a0ecd48238a4c4016626f1068c860dfed9625d1d..e755c96820d33f44929f843902d8ec631d584443 100644 --- a/bpf/utils.h +++ b/bpf/utils.h @@ -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; diff --git a/docs/flow_filtering.md b/docs/flow_filtering.md index 50ef5821d34297e07869a3e840e7fa57e8f468eb..d5a9319e4ece6c8025fb6796e9fc849e91effded 100644 --- a/docs/flow_filtering.md +++ b/docs/flow_filtering.md @@ -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 "80–100" 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. diff --git a/pkg/agent/agent.go b/pkg/agent/agent.go index 5ec2d2d72c6074858b6b3c750e101f351674c364..4342deb6684f5006249f9e1c736a5fa0b6aca83c 100644 --- a/pkg/agent/agent.go +++ b/pkg/agent/agent.go @@ -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, }, } diff --git a/pkg/agent/config.go b/pkg/agent/config.go index 5c3588e9dbfd1a1e2590d500eb0043cacfa0091a..852acf4a9aaa1944412e42b1661d8784d5bc3171 100644 --- a/pkg/agent/config.go +++ b/pkg/agent/config.go @@ -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 diff --git a/pkg/ebpf/bpf_arm64_bpfel.go b/pkg/ebpf/bpf_arm64_bpfel.go index e13c50b92e20f6df51efd881927df47b2a1d04f8..fca5ed8e5364eb9a842607b71c710533ab9813f9 100644 --- a/pkg/ebpf/bpf_arm64_bpfel.go +++ b/pkg/ebpf/bpf_arm64_bpfel.go @@ -61,6 +61,7 @@ type BpfFilterValueT struct { IcmpCode uint8 Direction BpfDirectionT Action BpfFilterActionT + TcpFlags BpfTcpFlagsT Ip [16]uint8 } @@ -119,6 +120,22 @@ type BpfPktDropsT struct { LatestDropCause uint32 } +type BpfTcpFlagsT uint32 + +const ( + BpfTcpFlagsTFIN_FLAG BpfTcpFlagsT = 1 + BpfTcpFlagsTSYN_FLAG BpfTcpFlagsT = 2 + BpfTcpFlagsTRST_FLAG BpfTcpFlagsT = 4 + BpfTcpFlagsTPSH_FLAG BpfTcpFlagsT = 8 + BpfTcpFlagsTACK_FLAG BpfTcpFlagsT = 16 + BpfTcpFlagsTURG_FLAG BpfTcpFlagsT = 32 + BpfTcpFlagsTECE_FLAG BpfTcpFlagsT = 64 + BpfTcpFlagsTCWR_FLAG BpfTcpFlagsT = 128 + BpfTcpFlagsTSYN_ACK_FLAG BpfTcpFlagsT = 256 + BpfTcpFlagsTFIN_ACK_FLAG BpfTcpFlagsT = 512 + BpfTcpFlagsTRST_ACK_FLAG BpfTcpFlagsT = 1024 +) + // LoadBpf returns the embedded CollectionSpec for Bpf. func LoadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) diff --git a/pkg/ebpf/bpf_arm64_bpfel.o b/pkg/ebpf/bpf_arm64_bpfel.o index 3141e95644865092590d028d5d562f4c2377c7ab..dc5ee13a29b7d539c1f63261680ae53b1741a059 100644 Binary files a/pkg/ebpf/bpf_arm64_bpfel.o and b/pkg/ebpf/bpf_arm64_bpfel.o differ diff --git a/pkg/ebpf/bpf_powerpc_bpfel.go b/pkg/ebpf/bpf_powerpc_bpfel.go index 3535af8fd1d88d307c1556c466f321d71871eef2..26a1023448e8a5c592051fc97a6c977520773503 100644 --- a/pkg/ebpf/bpf_powerpc_bpfel.go +++ b/pkg/ebpf/bpf_powerpc_bpfel.go @@ -61,6 +61,7 @@ type BpfFilterValueT struct { IcmpCode uint8 Direction BpfDirectionT Action BpfFilterActionT + TcpFlags BpfTcpFlagsT Ip [16]uint8 } @@ -119,6 +120,22 @@ type BpfPktDropsT struct { LatestDropCause uint32 } +type BpfTcpFlagsT uint32 + +const ( + BpfTcpFlagsTFIN_FLAG BpfTcpFlagsT = 1 + BpfTcpFlagsTSYN_FLAG BpfTcpFlagsT = 2 + BpfTcpFlagsTRST_FLAG BpfTcpFlagsT = 4 + BpfTcpFlagsTPSH_FLAG BpfTcpFlagsT = 8 + BpfTcpFlagsTACK_FLAG BpfTcpFlagsT = 16 + BpfTcpFlagsTURG_FLAG BpfTcpFlagsT = 32 + BpfTcpFlagsTECE_FLAG BpfTcpFlagsT = 64 + BpfTcpFlagsTCWR_FLAG BpfTcpFlagsT = 128 + BpfTcpFlagsTSYN_ACK_FLAG BpfTcpFlagsT = 256 + BpfTcpFlagsTFIN_ACK_FLAG BpfTcpFlagsT = 512 + BpfTcpFlagsTRST_ACK_FLAG BpfTcpFlagsT = 1024 +) + // LoadBpf returns the embedded CollectionSpec for Bpf. func LoadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) diff --git a/pkg/ebpf/bpf_powerpc_bpfel.o b/pkg/ebpf/bpf_powerpc_bpfel.o index c9f16da10ebe1901b7cdce2166395a331a6a73de..0a95ce4717a1b5b7279346ad8e8cc53abe455504 100644 Binary files a/pkg/ebpf/bpf_powerpc_bpfel.o and b/pkg/ebpf/bpf_powerpc_bpfel.o differ diff --git a/pkg/ebpf/bpf_s390_bpfeb.go b/pkg/ebpf/bpf_s390_bpfeb.go index fef364a8ca7cd9389909f5462b3791233af373de..a99fe5f60f96a95b6011fe1e3052bfe099c74179 100644 --- a/pkg/ebpf/bpf_s390_bpfeb.go +++ b/pkg/ebpf/bpf_s390_bpfeb.go @@ -61,6 +61,7 @@ type BpfFilterValueT struct { IcmpCode uint8 Direction BpfDirectionT Action BpfFilterActionT + TcpFlags BpfTcpFlagsT Ip [16]uint8 } @@ -119,6 +120,22 @@ type BpfPktDropsT struct { LatestDropCause uint32 } +type BpfTcpFlagsT uint32 + +const ( + BpfTcpFlagsTFIN_FLAG BpfTcpFlagsT = 1 + BpfTcpFlagsTSYN_FLAG BpfTcpFlagsT = 2 + BpfTcpFlagsTRST_FLAG BpfTcpFlagsT = 4 + BpfTcpFlagsTPSH_FLAG BpfTcpFlagsT = 8 + BpfTcpFlagsTACK_FLAG BpfTcpFlagsT = 16 + BpfTcpFlagsTURG_FLAG BpfTcpFlagsT = 32 + BpfTcpFlagsTECE_FLAG BpfTcpFlagsT = 64 + BpfTcpFlagsTCWR_FLAG BpfTcpFlagsT = 128 + BpfTcpFlagsTSYN_ACK_FLAG BpfTcpFlagsT = 256 + BpfTcpFlagsTFIN_ACK_FLAG BpfTcpFlagsT = 512 + BpfTcpFlagsTRST_ACK_FLAG BpfTcpFlagsT = 1024 +) + // LoadBpf returns the embedded CollectionSpec for Bpf. func LoadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) diff --git a/pkg/ebpf/bpf_s390_bpfeb.o b/pkg/ebpf/bpf_s390_bpfeb.o index ee345e6374f88487fcd8de8fa1581d7887e7282d..9238fbb03d3620b919547a7724dc635da0a27134 100644 Binary files a/pkg/ebpf/bpf_s390_bpfeb.o and b/pkg/ebpf/bpf_s390_bpfeb.o differ diff --git a/pkg/ebpf/bpf_x86_bpfel.go b/pkg/ebpf/bpf_x86_bpfel.go index 30f26cf989affa546972e2b34cf0df3f22f61ecd..8ce29c3c92a7444156c5bd511b8bf457e4d81a18 100644 --- a/pkg/ebpf/bpf_x86_bpfel.go +++ b/pkg/ebpf/bpf_x86_bpfel.go @@ -61,6 +61,7 @@ type BpfFilterValueT struct { IcmpCode uint8 Direction BpfDirectionT Action BpfFilterActionT + TcpFlags BpfTcpFlagsT Ip [16]uint8 } @@ -119,6 +120,22 @@ type BpfPktDropsT struct { LatestDropCause uint32 } +type BpfTcpFlagsT uint32 + +const ( + BpfTcpFlagsTFIN_FLAG BpfTcpFlagsT = 1 + BpfTcpFlagsTSYN_FLAG BpfTcpFlagsT = 2 + BpfTcpFlagsTRST_FLAG BpfTcpFlagsT = 4 + BpfTcpFlagsTPSH_FLAG BpfTcpFlagsT = 8 + BpfTcpFlagsTACK_FLAG BpfTcpFlagsT = 16 + BpfTcpFlagsTURG_FLAG BpfTcpFlagsT = 32 + BpfTcpFlagsTECE_FLAG BpfTcpFlagsT = 64 + BpfTcpFlagsTCWR_FLAG BpfTcpFlagsT = 128 + BpfTcpFlagsTSYN_ACK_FLAG BpfTcpFlagsT = 256 + BpfTcpFlagsTFIN_ACK_FLAG BpfTcpFlagsT = 512 + BpfTcpFlagsTRST_ACK_FLAG BpfTcpFlagsT = 1024 +) + // LoadBpf returns the embedded CollectionSpec for Bpf. func LoadBpf() (*ebpf.CollectionSpec, error) { reader := bytes.NewReader(_BpfBytes) diff --git a/pkg/ebpf/bpf_x86_bpfel.o b/pkg/ebpf/bpf_x86_bpfel.o index 618488d29577f13039992d61774fc2e62a3845d0..f411fe7cd9e851a0a545d476e9993256964e7286 100644 Binary files a/pkg/ebpf/bpf_x86_bpfel.o and b/pkg/ebpf/bpf_x86_bpfel.o differ diff --git a/pkg/ebpf/flow_filter.go b/pkg/ebpf/flow_filter.go index 598aff5987be1e3b9c1ddd769f766ef615e92415..18fc87e4a9d92acd8cab445ec1e87f8842cd77b7 100644 --- a/pkg/ebpf/flow_filter.go +++ b/pkg/ebpf/flow_filter.go @@ -22,6 +22,7 @@ type FilterConfig struct { FilterIcmpCode int FilterPeerIP string FilterAction string + FilterTCPFLags string } type Filter struct { @@ -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{} @@ -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 } diff --git a/pkg/ebpf/tracer.go b/pkg/ebpf/tracer.go index 74b7bb24877ebad4c32822579ed73b297782cf6a..dc6986295a3f32b7cab9f505e6190b48e38e826d 100644 --- a/pkg/ebpf/tracer.go +++ b/pkg/ebpf/tracer.go @@ -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"