eBPF 代答arp请求

eBPF代答veth口arp请求

在cilium中,eBPF tc ingress负责代答容器网卡的arp请求,回复的mac地址是容器网卡对端veth口mac地址,与当前逻辑类似,这里只是回复了假mac。
在kube-ovn中,ovs br-int网桥上xxx_h负责响应容器网卡的arp请求,没有geneve arp请求场景。

Ubuntu 23.10/6.5.0-44-generic

1. 创建包含容器网卡的veth对

ip netns add ns
ip link add veth0 type veth peer name veth1
ip link set veth1 netns ns
ip netns exec ns ip addr add 1.1.1.1/24 dev veth1
ip netns exec ns ip link set dev veth1 up
ip link set dev veth0 up

2. 加载eBPF程序到veth口

clang -c arp/arp-resp.c -o arp/arp-resp.o -target bpf -O2 -g
tc qdisc del dev veth0 clsact
tc qdisc add dev veth0 clsact
tc filter add dev veth0 ingress bpf da obj arp/arp-resp.o sec tc

clsact是假qdisc,只用于执行加载的filter eBPF程序。
tc上加上da表示tc eBPF程序根据TC_ACT_OK等返回值决定转发逻辑,而不是由action关键字来决定。

failed to resolve CO-RE relocation <byte_off> [24] struct arphdr.arp_data.arp_tha.addr_bytes (0:5:2:0 @ offset 20)

原因是参考的dpdk-stable-20.11.1中arp数据结构与内核不匹配。
dpdk-stable-20.11.1 lib/librte_net/rte_arp.h

参考cilium,提取关键部分

headers/arp.h

#include "vmlinux.h"
#define ETH_ALEN	6

#ifndef __packed
# define __packed		__attribute__((packed))
#endif

struct arp_eth {
	unsigned char		ar_sha[ETH_ALEN];
	__be32                  ar_sip;
	unsigned char		ar_tha[ETH_ALEN];
	__be32                  ar_tip;
} __packed;

arp/arp-resp.c

#include "../headers/arp.h"
#include "../headers/bpf_endian.h"
#include "../headers/bpf_helpers.h"

#define TC_ACT_SHOT 2
#define TC_ACT_OK 0

#define ETH_P_IP 0x0800
#define ETH_P_ARP 0x0806
#define ETH_ALEN 6
#define ARPOP_REQUEST 1
#define ARPOP_REPLY 2

#ifndef memcpy
#define memcpy(dest, src, n)   __builtin_memcpy((dest), (src), (n))
#endif

char __license[] SEC("license") = "Dual MIT/GPL";

SEC("tc")
int tc_resp_arp(struct __sk_buff *skb)
{
    void *data_end = (void *)(long)skb->data_end;
    void *data = (void *)(long)skb->data;

    struct ethhdr *eth_hdr = data;
    if ((void *)eth_hdr + sizeof(*eth_hdr) > data_end)
    {
        return TC_ACT_OK;
    }
    if (eth_hdr->h_proto != bpf_htons(ETH_P_ARP))
    {
        return TC_ACT_OK;
    }

    struct arphdr *arp_hdr = (void *)eth_hdr + sizeof(*eth_hdr);
    if ((void *)arp_hdr + sizeof(*arp_hdr) > data_end)
    {
        return TC_ACT_OK;
    }

    if (arp_hdr->ar_op != bpf_htons(ARPOP_REQUEST))
    {
        return TC_ACT_OK;
    }

    struct arp_eth *arpe = (void *)arp_hdr + sizeof(*arp_hdr);
    if ((void *)arpe + sizeof(*arpe) > data_end)
    {
        return TC_ACT_OK;
    }
    
    // 请求改成响应
    arp_hdr->ar_op = bpf_htons(ARPOP_REPLY);

    // 交换源ip和目的ip
    __u32 tmp = arpe->ar_sip;
    arpe->ar_sip = arpe->ar_tip;
    arpe->ar_tip = tmp;
    
    // 更新以太网头和arp头mac
    unsigned char mac[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xee};
    for (int i = 0; i < ETH_ALEN; i++)
    {
        eth_hdr->h_dest[i] = eth_hdr->h_source[i];
        eth_hdr->h_source[i] = mac[i];
        arpe->ar_tha[i] = eth_hdr->h_source[i];
        arpe->ar_sha[i] = mac[i];
    }

    // skb发回veth口
    return bpf_redirect(skb->ifindex, BPF_ANY);
}

arp请求报文

3. 测试arp请求和响应

ip netns exec ns arping 1.1.1.2

posted on 2024-08-13 06:59  王景迁  阅读(124)  评论(0)    收藏  举报

导航