In the previous article, we used eBPF Map in XDP to exchange data with user space.
In the article before that, we wrote a program using XDP that drops all packets.
This time, we will write a program that responds with ICMP Echo Reply to ICMP Echo Request.
How to Respond with ICMP Echo Reply
To respond with ICMP Echo Reply to an ICMP Echo Request, the following steps are necessary:
- Receive an ICMP Echo Request packet
- Change the flag to ICMP Echo Reply
- Swap the source and destination IP addresses
- Swap the source and destination MAC addresses in the Ethernet header
- Send the packet
Dropping Only ICMP Echo Requests
First, we want to handle ICMP Echo Requests, so we will write a program that only drops ICMP Echo Requests.
- xdp_echo_request_drop.c
#include <linux/bpf.h> #include <bpf/bpf_helpers.h> #include <linux/if_ether.h> #include <linux/ip.h> #include <linux/icmp.h> #include <arpa/inet.h> SEC("xdp") int xdp_drop_icmp(struct xdp_md *ctx) { void *data_end = (void *)(unsigned long)ctx->data_end; void *data = (void *)(unsigned long)ctx->data; // Get pointer to Ethernet header struct ethhdr *eth = data; if ((void *)eth + sizeof(*eth) > data_end) return XDP_PASS; // Check if the Ethernet frame contains an IP packet if (eth->h_proto != htons(ETH_P_IP)) return XDP_PASS; // Get pointer to IP header struct iphdr *ip = data + sizeof(*eth); if ((void *)ip + sizeof(*ip) > data_end) return XDP_PASS; // Check if the protocol is ICMP if (ip->protocol != IPPROTO_ICMP) return XDP_PASS; // Get pointer to ICMP header struct icmphdr *icmp = (void *)ip + sizeof(*ip); if ((void *)icmp + sizeof(*icmp) > data_end) return XDP_PASS; // Pass through packets that are not ICMP Echo Requests if (icmp->type != ICMP_ECHO) return XDP_PASS; // Drop the ICMP Echo Request packet return XDP_DROP; } char _license[] SEC("license") = "MIT";
This program is almost identical to the one created in Quick Start Guide to XDP: Part 1.
// Pass through packets that are not ICMP Echo Requests
if (icmp->type != ICMP_ECHO)
return XDP_PASS;
In this part, we make sure that packets other than ICMP Echo Requests can pass through.
Replying to ICMP Echo Requests
Next, we will write a program to respond with ICMP Echo Reply to ICMP Echo Requests.
- Change the flag to ICMP Echo Reply
// Change the flag to ICMP Echo Reply
icmp->type = ICMP_ECHOREPLY;
We change the type from ICMP_ECHO (8) to ICMP_ECHOREPLY (0).
- Swap the source and destination IP addresses
// Swap the source and destination IP addresses
__u32 tmp = ip->saddr;
ip->saddr = ip->daddr;
ip->daddr = tmp;
- Swap the source and destination MAC addresses in the Ethernet header
// Swap source and destination of the Ethernet header
unsigned char tmp_mac[ETH_ALEN];
__builtin_memcpy(tmp_mac, eth->h_dest, ETH_ALEN);
__builtin_memcpy(eth->h_dest, eth->h_source, ETH_ALEN);
__builtin_memcpy(eth->h_source, tmp_mac, ETH_ALEN);
- Send the packet with XDP_TX
// Send the packet
return XDP_TX;
Now we have a program that returns ICMP Echo Replies.
SEC("xdp")
int xdp_echo_reply(struct xdp_md *ctx)
{
void *data_end = (void *)(unsigned long)ctx->data_end;
void *data = (void *)(unsigned long)ctx->data;
// Get pointer to Ethernet header
struct ethhdr *eth = data;
if ((void *)eth + sizeof(*eth) > data_end)
return XDP_PASS;
// Check if the Ethernet frame contains an IP packet
if (eth->h_proto != htons(ETH_P_IP))
return XDP_PASS;
// Get pointer to IP header
struct iphdr *ip = data + sizeof(*eth);
if ((void *)ip + sizeof(*ip) > data_end)
return XDP_PASS;
// Check if the protocol is ICMP
if (ip->protocol != IPPROTO_ICMP)
return XDP_PASS;
// Get pointer to ICMP header
struct icmphdr *icmp = (void *)ip + sizeof(*ip);
if ((void *)icmp + sizeof(*icmp) > data_end)
return XDP_PASS;
// Pass through packets that are not ICMP Echo Requests
if (icmp->type != ICMP_ECHO)
return XDP_PASS;
// Change the flag to ICMP Echo Reply
icmp->type = ICMP_ECHOREPLY;
// Swap the source and destination IP addresses
__u32 tmp = ip->saddr;
ip->saddr = ip->daddr;
ip->daddr = tmp;
// Swap source and destination of the Ethernet header
unsigned char tmp_mac[ETH_ALEN];
__builtin_memcpy(tmp_mac, eth->h_dest, ETH_ALEN);
__builtin_memcpy(eth->h_dest, eth->h_source, ETH_ALEN);
__builtin_memcpy(eth->h_source, tmp_mac, ETH_ALEN);
// Drop the ICMP Echo Request packet
// return XDP_DROP;
return XDP_TX;
}
Loading the Program to the NIC
Compile the program and load it to the NIC.
$ clang -O2 -target bpf -c xdp_echo_reply.c -o xdp_echo_reply.o
$ sudo ip link set dev eth0 xdp obj xdp_echo_reply.o
Verifying Operation
Send a ping from the client
$ ping 192.168.XXX.XXX
Run tcpdump on the server to confirm that ICMP Requests are not being received. Since XDP processes packets directly, it cannot be confirmed via tcpdump, and it shows that the Linux kernel is not returning the Echo Reply.
$ sudo tcpdump -i eth0 icmp
When you send a ping from the client, confirm that ICMP Echo Reply is returned.
$ sudo tcpdump -i eth0 icmp -vvv XX:XX:XX.160507 IP (tos 0x0, ttl 64, id 53521, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.XXX.1 > 192.168.XXX.2: ICMP echo request, id 58371, seq 684, length 64 XX:XX:XX.161394 IP (tos 0x0, ttl 64, id 53521, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.XXX.2 > 192.168.XXX.1: ICMP echo reply, id 58371, seq 684, length 64 (wrong icmp cksum 767b (->7e7b)!)
We could confirm the receipt of ICMP Echo Reply.
However, since it displays wrong icmp cksum
, it indicates that the ICMP header checksum is not calculated correctly.
If I feel motivated enough to write an article about how to correctly compute the ICMP checksum and return it, I will do so.
Summary
- We implemented an XDP program that responds to ICMP Echo Requests with ICMP Echo Replies using XDP.
- The procedure includes detecting ICMP Echo Requests, generating the reply packet, swapping the MAC addresses and IP addresses of source and destination, and replying directly with XDP_TX without passing through the kernel.
- Ultimately, we encountered a
wrong icmp cksum
error, indicating that recalculation of the ICMP checksum is necessary.
Next Steps (Updated: 2025/03/06)
Quick Start Guide: Super XDP Introduction Part 3 (ICMP Echo Reply - Part 2)