【问题标题】:ebpf: drop ICMP packet in socket filter program on lo interfaceebpf:在 lo 接口的套接字过滤程序中丢弃 ICMP 数据包
【发布时间】: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.cnet/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


    【解决方案1】:

    反之亦然:如果返回 0,它将丢弃数据包。来自代码:

    *   sk_filter_trim_cap - run a packet through a socket filter
    *   [...]
    *
    * Run the eBPF program and then cut skb->data to correct size returned by
    * the program. If pkt_len is 0 we toss packet. If skb->len is smaller
    * than pkt_len we keep whole skb->data. [...]
    

    【讨论】:

    • 嗯.. BPF_MOV64_IMM(BPF_REG_0, 0) ICMP 仍然通过,即 ping -c1 127.0.0.1 成功。我确信数据包会命中ebpf 代码,因为我在tracelog 中打印数据包的以太网类型。不知何故,ping 收到了数据包。
    • 你是如何加载和附加 BPF 程序的?您如何检查是否仍收到 ICMP 数据包?套接字过滤程序通常在数据包的副本上运行,因此删除副本不会影响实际流量。
    • 我以内核的samples/bpf/sock_example.c 示例程序作为我实验的基础;它通过setsockopt(.., SO_ATTACH_BPF) 附加。我只是 ping -c1 127.0.0.1 并查看 0% packet loss 统计信息。我还跑了tcpdump -i lo,这更接近我的实验,因为 tcpdump 打开 PF_PACKET 原始套接字,如果我理解正确,套接字过滤程序应该立即启动。当您说套接字过滤程序通常在数据包的副本上运行时,是不是意味着有时它们不会在副本上运行?
    • 我用一些发现更新了我的问题。
    【解决方案2】:

    pchaigno 的回答是正确的,内核代码始终是事实的最终来源,但在这种情况下,我也会重定向到套接字的手册页,man 7 socket。这是描述将 BPF 过滤器附加到套接字的选项的地方,它说:

    SO_ATTACH_FILTER (since Linux 2.2), SO_ATTACH_BPF (since Linux
           3.19)
                  Attach a classic BPF (SO_ATTACH_FILTER) or an extended BPF
                  (SO_ATTACH_BPF) program to the socket for use as a filter
                  of incoming packets.  A packet will be dropped if the
                  filter program returns zero.  If the filter program
                  returns a nonzero value which is less than the packet's
                  data length, the packet will be truncated to the length
                  returned.  If the value returned by the filter is greater
                  than or equal to the packet's data length, the packet is
                  allowed to proceed unmodified.
    

    所以:

    • 0 丢弃数据包(“截断为长度 0”)。
    • 小值会截断数据包(到与该值对应的长度)。
    • 大值通过数据包。

    这对 cBPF 和 eBPF 都有效。

    【讨论】:

      【解决方案3】:

      Socket Filter BPFs 接收数据包的副本;因此,此 BPF 过滤器丢弃或截断数据包的副本,而不是原始数据包。原始数据包通过内核不受过滤器影响。

      eBPF 程序的套接字过滤器的返回值实际上只影响链接在自身之后或插入到自身之后的 BPF 过滤器。

      如果你想要 eBPF 功能并且需要丢弃数据包,你应该选择不同的内核挂钩点,例如Netfilter Hooks、流量控制等

      流量控制是入口/出口,过滤器附加到单个网络接口,支持流量控制BPF(tc-eBPF)形式的BPF。 Netfilter Hooks 是 IP 级别的,支持 Iptables、Nftables、Nfqueue,并且易于在内核模块中使用。与在超早期(入口/出口)阶段轻松修改或丢弃数据包的流量控制相比,BPF 支持不如流量控制好。 iptables-extensions BPF (xt-bpf) 可以丢弃数据包,将数据包信息存储在maps中,但是对数据包的修改是有限的。

      但是对于 ICMP 数据包丢弃,单个 Iptables 规则就足够了。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-11-10
        • 2010-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多