eBPF 代答arp请求

eBPF代答veth口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

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  王景迁  阅读(19)  评论(0编辑  收藏  举报

导航