blob: b23df1aa3e6c6e80d3555a90d84e3087d8a623c3 [file] [log] [blame]
// 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;