| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright(c) 2023 Huawei Technologies Co., Ltd |
| */ |
| |
| #include "bpf_sockmap.h" |
| |
| #define REDIS_BIND_MAP_SIZE 100 |
| #define BLOCKLIST_SIZE 1000 |
| |
| #define ENABLE_BLOCKLIST 0 |
| #define SHORT_THR 10 |
| #define BLOCK_THR 10000 |
| |
| struct local_ip { |
| __u32 ip4; |
| }; |
| |
| struct ipaddr_port { |
| __u32 ip4; |
| __u32 port; |
| } __attribute__((packed)); |
| |
| #if ENABLE_BLOCKLIST |
| struct { |
| __uint(type, BPF_MAP_TYPE_LRU_HASH); |
| __type(key, struct ipaddr_port); |
| __type(value, int); |
| __uint(max_entries, BLOCKLIST_SIZE); |
| __uint(map_flags, 0); |
| } blocklist_map SEC(".maps"); |
| #endif |
| |
| struct { |
| __uint(type, BPF_MAP_TYPE_HASH); |
| __type(key, struct ipaddr_port); |
| __type(value, int); |
| __uint(max_entries, REDIS_BIND_MAP_SIZE); |
| __uint(map_flags, 0); |
| } redis_bind_map SEC(".maps"); |
| |
| |
| static inline void extract_ipaddrport_from_ops(struct bpf_sock_ops *skops, |
| struct ipaddr_port *key1, struct ipaddr_port *key2) |
| { |
| key1->ip4 = skops->remote_ip4; |
| // remote_port is in network byte order |
| key1->port = bpf_ntohl(skops->remote_port); |
| |
| key2->ip4 = skops->local_ip4; |
| // local_port is in host byte order |
| key2->port = skops->local_port; |
| } |
| |
| static inline int __is_redis_sock(struct ipaddr_port *key) |
| { |
| int *pv = NULL; |
| |
| pv = bpf_map_lookup_elem(&redis_bind_map, key); |
| if (pv) |
| return 1; |
| |
| return 0; |
| } |
| |
| static inline int is_redis_sock(struct ipaddr_port *key1, struct ipaddr_port *key2, |
| struct ipaddr_port *key10, struct ipaddr_port *key20) |
| { |
| net_dbg("is_redis, ip1:0x%x, port1:0x%x\n", key1->ip4, key1->port); |
| net_dbg("is_redis, ip2:0x%x, port2:0x%x\n", key2->ip4, key2->port); |
| |
| if (__is_redis_sock(key1)) |
| return 1; |
| |
| if (__is_redis_sock(key2)) |
| return 1; |
| |
| if (__is_redis_sock(key10)) |
| return 1; |
| |
| if (__is_redis_sock(key20)) |
| return 1; |
| |
| return 0; |
| } |
| |
| static inline int is_localip_sock(struct bpf_sock_ops *skops) |
| { |
| struct local_ip remoteip; |
| |
| net_dbg("is_localip, ip1:0x%x, ip2:0x%x\n", |
| skops->local_ip4, skops->remote_ip4); |
| |
| // skops->local_ip4 must be the local IP address |
| remoteip.ip4 = skops->remote_ip4; |
| |
| if ((remoteip.ip4 & 0xff) == 0x7f) |
| return 1; |
| |
| if (!bpf_is_local_ipaddr(remoteip.ip4)) |
| return 0; |
| |
| return 1; |
| } |
| |
| #if ENABLE_BLOCKLIST |
| static inline int __is_in_block_list(struct ipaddr_port *key) |
| { |
| int *pv = NULL; |
| |
| pv = bpf_map_lookup_elem(&blocklist_map, key); |
| if (pv && *pv > BLOCK_THR) |
| return 1; |
| |
| return 0; |
| } |
| |
| static inline int is_in_block_list(struct ipaddr_port *key1, struct ipaddr_port *key2, |
| struct ipaddr_port *key10, struct ipaddr_port *key20) |
| { |
| |
| if (__is_in_block_list(key1)) |
| return 1; |
| if (__is_in_block_list(key2)) |
| return 1; |
| if (__is_in_block_list(key10)) |
| return 1; |
| if (__is_in_block_list(key20)) |
| return 1; |
| |
| return 0; |
| } |
| |
| static inline int __add_task2block_list(struct ipaddr_port *block) |
| { |
| int *pv = NULL; |
| int value = 1; |
| |
| pv = bpf_map_lookup_elem(&blocklist_map, block); |
| if (pv == NULL) { |
| bpf_map_update_elem(&blocklist_map, block, &value, BPF_NOEXIST); |
| return 0; |
| } |
| |
| if (*pv > BLOCK_THR) |
| return 0; |
| |
| *pv += 1; |
| return 0; |
| } |
| |
| static inline int add_task2block_list(struct bpf_sock_ops *skops) |
| { |
| struct ipaddr_port block1; |
| struct ipaddr_port block2; |
| |
| extract_ipaddrport_from_ops(skops, &block1, &block2); |
| |
| if (__is_redis_sock(&block1)) |
| return __add_task2block_list(&block1); |
| |
| if (__is_redis_sock(&block2)) |
| return __add_task2block_list(&block2); |
| |
| block1.ip4 = 0; |
| if (__is_redis_sock(&block1)) |
| return __add_task2block_list(&block1); |
| |
| block2.ip4 = 0; |
| if (__is_redis_sock(&block2)) |
| return __add_task2block_list(&block2); |
| |
| return 0; |
| } |
| #else |
| static inline int add_task2block_list(struct bpf_sock_ops *skops) |
| { |
| return 0; |
| } |
| static inline int is_in_block_list(struct ipaddr_port *key1, struct ipaddr_port *key2, |
| struct ipaddr_port *key10, struct ipaddr_port *key20) |
| { |
| return 0; |
| } |
| #endif |
| |
| static inline int is_redis_loopback_tcp(struct bpf_sock_ops *skops) |
| { |
| struct ipaddr_port key10; |
| struct ipaddr_port key20; |
| struct ipaddr_port key1; |
| struct ipaddr_port key2; |
| |
| if (!is_localip_sock(skops)) |
| return 0; |
| net_dbg("this is localip\n"); |
| |
| extract_ipaddrport_from_ops(skops, &key1, &key2); |
| key10.ip4 = 0; |
| key10.port = key1.port; |
| key20.ip4 = 0; |
| key20.port = key2.port; |
| |
| if (!is_redis_sock(&key1, &key2, &key10, &key20)) |
| return 0; |
| net_dbg("this is redis sock\n"); |
| |
| if (is_in_block_list(&key1, &key2, &key10, &key20)) |
| return 0; |
| |
| net_dbg("the sock is redis loopback sock\n"); |
| return 1; |
| } |
| |
| static inline int update_redis_info(struct bpf_sock_ops *skops) |
| { |
| struct ipaddr_port key; |
| int value = 1; |
| char comm[16] = {0}; |
| |
| bpf_get_current_comm(comm, sizeof(comm)); |
| if (comm[0] != 'r' || comm[1] != 'e' || comm[2] != 'd' || comm[3] != 'i' || |
| comm[4] != 's' || comm[5] != '-' || comm[6] != 's' || comm[7] != 'e' || |
| comm[8] != 'r' || comm[9] != 'v' || comm[10] != 'e' || comm[11] != 'r') |
| return 0; |
| |
| key.ip4 = skops->local_ip4; |
| key.port = skops->local_port; // host order |
| |
| bpf_map_update_elem(&redis_bind_map, &key, &value, BPF_NOEXIST); |
| net_dbg("%s, update redisinfo: sip:0x%x, sport:%d\n", comm, key.ip4, key.port); |
| return 1; |
| } |
| |
| static inline void clean_redis_info(struct bpf_sock_ops *skops) |
| { |
| struct ipaddr_port key; |
| |
| key.ip4 = skops->local_ip4; |
| key.port = skops->local_port; // host order |
| net_dbg("clean redisinfo, 0x%x:%d\n", key.ip4, key.port); |
| bpf_map_delete_elem(&redis_bind_map, &key); |
| } |
| |
| SEC("sockops") int redis_sockops(struct bpf_sock_ops *skops) |
| { |
| switch (skops->op) { |
| case BPF_SOCK_OPS_ACTIVE_ESTABLISHED_CB: |
| case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB: |
| if (skops->family == 2) {// AF_INET |
| if (is_redis_loopback_tcp(skops)) { |
| net_dbg("bpf_sockops, sockmap, op:%d, sk:%p\n", |
| skops->op, skops->sk); |
| bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_STATE_CB_FLAG); |
| bpf_sockmap_ipv4_insert(skops); |
| } else { |
| bpf_sock_ops_cb_flags_set(skops, 0); |
| } |
| } |
| break; |
| case BPF_SOCK_OPS_STATE_CB: |
| if (skops->family == 2 && skops->args[0] == BPF_TCP_LISTEN && |
| skops->args[1] == BPF_TCP_CLOSE) { |
| clean_redis_info(skops); |
| } else if (skops->family == 2 && (skops->args[1] == BPF_TCP_CLOSE || |
| skops->args[1] == BPF_TCP_CLOSE_WAIT || |
| skops->args[1] == BPF_TCP_FIN_WAIT1)) { |
| __u64 tx_cnt = SHORT_THR; |
| |
| bpf_sockmap_ipv4_cleanup(skops, &tx_cnt); |
| net_dbg("sockops sk:%p, state:%d, tx_cnt:%llu\n", |
| skops->sk, skops->args[1], tx_cnt); |
| if (tx_cnt < SHORT_THR) |
| add_task2block_list(skops); |
| } |
| break; |
| case BPF_SOCK_OPS_TCP_LISTEN_CB: |
| if (skops->family == 2 && update_redis_info(skops)) |
| bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_STATE_CB_FLAG); |
| break; |
| default: |
| break; |
| } |
| return 1; |
| } |
| |
| char _license[] SEC("license") = "GPL"; |
| int _version SEC("version") = 1; |