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