【发布时间】:2021-05-25 19:47:39
【问题描述】:
考虑一个非常简单的ebpf 代码BPF_PROG_TYPE_SOCKET_FILTER 类型:
struct bpf_insn prog[] = {
BPF_MOV64_IMM(BPF_REG_0, -1),
BPF_EXIT_INSN(),
};
net/core/filter.c 和 net/core/sock/c 的代码 sn-ps 显示了如何调用过滤器:
static inline int pskb_trim(struct sk_buff *skb, unsigned int len)
{
return (len < skb->len) ? __pskb_trim(skb, len) : 0;
}
...
int sk_filter_trim_cap(struct sock *sk, struct sk_buff *skb, unsigned int cap)
{
int err;
...
if (filter) {
pkt_len = bpf_prog_run_save_cb(filter->prog, skb);
skb->sk = save_sk;
err = pkt_len ? pskb_trim(skb, max(cap, pkt_len)) : -EPERM;
}
...
return err;
}
...
static inline int sk_filter(struct sock *sk, struct sk_buff *skb)
{
return sk_filter_trim_cap(sk, skb, 1);
}
最终sk_filter() 将被sock_queue_rcv_skb() 调用,即如果sk_filter() 返回0,则过滤器将处理到达套接字的数据包并排队。
如果我正确理解此代码,在这种情况下(返回代码 0xffffffff)将导致数据包被丢弃。但是,当附加到AF_PACKET 原始套接字(绑定到lo 接口)时,我的简单ebpf 代码不会丢弃通过环回接口发送的icmp 数据包。它与eBPF或ICMP在环回接口上的行为有什么关系吗?
更新
正如pchaigno 所指出的,套接字过滤程序处理副本 数据包。在我的例子中,ebpf 应用程序基本上创建了一个tap 套接字(AF_PACKET 原始套接字),它会在传入数据包被传递到协议层之前。我对内核代码做了一些调查,发现收到的数据包最终会到达__netif_receive_skb_core()函数,它执行以下操作:
list_for_each_entry_rcu(ptype, &skb->dev->ptype_all, list) {
if (pt_prev)
ret = deliver_skb(skb, pt_prev, orig_dev);
pt_prev = ptype;
}
这会将数据包传递给 AF_PACKET 处理程序,该处理程序最终将运行 ebpf 过滤器。
作为一种确认 ebpf 过滤器实际上过滤原始 AF_PACKET 套接字的方法,可以按如下方式转储统计信息:
struct tpacket_stats stats;
...
len = sizeof(stats);
err = getsockopt(sock, SOL_PACKET, PACKET_STATISTICS, &stats, &len);
此统计信息将指示过滤器行为。
【问题讨论】:
标签: networking linux-kernel bpf ebpf