【eBPF】BCC实现tcp防火墙

admin 2024年2月22日17:47:09评论6 views字数 2450阅读8分10秒阅读模式

免责声明

本文仅用于技术讨论与学习,利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,文章作者及本公众号不为此承担任何责任。

TCP防火墙

BCC作为一个流行的ebpf开发方案,提供了很多案例供开发者学习,其中/examples/networking/tcp_mon_block/是BCC官方提供的TCP防火墙的案例。过滤TCP请求对于入侵检测和防火墙很有帮助,值得学习。

【eBPF】BCC实现tcp防火墙

【eBPF】BCC实现tcp防火墙

内核态初始化

【eBPF】BCC实现tcp防火墙
  1. 首先定义必要的结构体,包括:完整的网络数据包信息、输出事件、连接关系
typedef struct{    u32 src_ip;    u16 src_port;    u32 dst_ip;    u16 dst_port;    u32 pid;    u8 tcp_flags;    char comm[TASK_COMM_LEN];} full_packet;



typedef struct{    u8 state;    u32 src_ip;    u16 src_port;    u32 dst_ip;    u16 dst_port;    u32 pid;    char comm[TASK_COMM_LEN];} verbose_event;



typedef struct{    u32 src_ip;    u16 src_port;    u32 dst_ip;    u16 dst_port;} key_hash;

2. 定义必要的散列表和性能事件输出:

BPF_HASH(monitored_connections, key_hash, full_packet);BPF_HASH(allow_list, u32, u32);BPF_HASH(pid_list, u32, u32);BPF_PERF_OUTPUT(blocked_events);BPF_PERF_OUTPUT(verbose_events);

3. 定义检查TCP头是否越界的函数:

static __always_inline int tcp_header_bound_check(struct tcphdr* tcp, void* data_end){    if ((void *)tcp + sizeof(*tcp) > data_end)    {        return -1;    }

    return 0;}

4. 定义构建详细事件的函数:

static void make_verbose_event(verbose_event *v, u32 src_ip, u32 dst_ip, u16 src_port, u16 dst_port, u32 pid, u8 state){    v->src_ip = src_ip;    v->src_port = src_port;    v->dst_ip = dst_ip;    v->dst_port = dst_port;    v->pid = pid;    v->state = state;    bpf_get_current_comm(&v->comm, sizeof(v->comm));}
【eBPF】BCC实现tcp防火墙

检测出站流量

【eBPF】BCC实现tcp防火墙

1. 定义检测网卡出站数据包流量,首先定义变量:

int handle_egress(struct __sk_buff *ctx){    void* data_end = (void*)(long)ctx->data_end;    void* data = (void*)(long)ctx->data;    struct ethhdr *eth = data;    struct iphdr *ip = data + sizeof(*eth);    struct tcphdr *tcp;    key_hash key = {};

其中:

  • data_end:代表网络数据包的边界,用于确保访问数据包内容时不越界。
  • data:捕获到的以太网帧。
  • eth:指向以太网头部的结构体指针。
  • ip:指向IP头部的结构体指针,它紧跟在以太网头后面。
  • tcp:用于稍后存储指向TCP头部的指针。
  • key:类型为 key_hash 的变量,用于作为查找监控连接散列表的键。

2. 检查数据包长度是否正确:

/* length check */if (data + sizeof(*eth) + sizeof(*ip) > data_end){    return TC_ACT_OK;}

3. 接下来检查是否为TCP协议:

if (eth->h_proto != htons(ETH_P_IP)){    return TC_ACT_OK;}

if (ip->protocol != IPPROTO_TCP){    return TC_ACT_OK;}

4. 然后可以提取出源和目的的ip和port:

tcp = (void *)ip + sizeof(*ip);if (tcp_header_bound_check(tcp, data_end)){    return TC_ACT_OK;}

u8 tcpflags = ((u_int8_t *)tcp)[13];u16 src_port = bpf_ntohs(tcp->source);u16 dst_port = bpf_ntohs(tcp->dest);

key.src_ip = ip->saddr;key.src_port = src_port;key.dst_ip = ip->daddr;key.dst_port = dst_port;

5. 使用前面创建的key在monitored_connections散列表中查找。如果找到对应条目,说明此连接被监控:

full_packet *packet_value;packet_value = monitored_connections.lookup(&key);

6. 如果找到相应条目,即packet_value不为0,则更新条目内的TCP标志,并使用blocked_events.perf_submit()函数向用户空间发送数据包相关信息的事件。随后,返回 TC_ACT_SHOT 告诉内核丢弃该数据包:

if (packet_value != 0){    packet_value->tcp_flags = tcpflags;    blocked_events.perf_submit(ctx, packet_value, sizeof(full_packet));    return TC_ACT_SHOT;}

(未完待续)

原文始发于微信公众号(赛博安全狗):【eBPF】BCC实现tcp防火墙

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年2月22日17:47:09
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【eBPF】BCC实现tcp防火墙http://cn-sec.com/archives/2515210.html

发表评论

匿名网友 填写信息