Skip to content

Instantly share code, notes, and snippets.

@teknoraver
Last active December 19, 2023 08:38
Show Gist options
  • Save teknoraver/b66115e3518bb1b7f3e79f52aa2c3424 to your computer and use it in GitHub Desktop.
Save teknoraver/b66115e3518bb1b7f3e79f52aa2c3424 to your computer and use it in GitHub Desktop.
Sample XDP program to mangle ICMP echo replies
/*
* xdp_manglepong.c - sample XDP program
* Copyright (C) 2019 Matteo Croce <mcroce@redhat.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/*
* xdp_manglepong drops even ping replies and changes the TTL of the odd.
* compile it with:
* clang -O2 -Wall -ggdb3 -c xdp_manglepong.c -o - -emit-llvm | \
* llc - -o xdp_manglepong.o -march=bpf -filetype=obj
* attach it to a device with:
* ip link set dev lo xdp object xdp_manglepong.o
* when attached to a device, eg. loopback, the ping output is like:
* $ ping -c 10 127.0.0.1
* PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
* 64 bytes from 127.0.0.1: icmp_seq=1 ttl=180 time=0.065 ms
* 64 bytes from 127.0.0.1: icmp_seq=3 ttl=140 time=0.057 ms
* 64 bytes from 127.0.0.1: icmp_seq=5 ttl=84 time=0.062 ms
* 64 bytes from 127.0.0.1: icmp_seq=7 ttl=169 time=0.062 ms
* 64 bytes from 127.0.0.1: icmp_seq=9 ttl=249 time=0.083 ms
*
* --- 127.0.0.1 ping statistics ---
* 10 packets transmitted, 5 received, 50% packet loss, time 9204ms
* rtt min/avg/max/mdev = 0.057/0.065/0.083/0.009 ms
*/
#include <stdint.h>
#include <arpa/inet.h>
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/icmp.h>
#define SEC(NAME) __attribute__((section(NAME), used))
#define OVER(x, d) (x + 1 > (typeof(x))d)
/* from bpf_helpers.h */
static unsigned long long (*bpf_get_prandom_u32)(void) =
(void *) BPF_FUNC_get_prandom_u32;
static inline void csum_replace2(uint16_t *sum, uint16_t old, uint16_t new)
{
uint16_t csum = ~*sum;
csum += ~old;
csum += csum < (uint16_t)~old;
csum += new;
csum += csum < (uint16_t)new;
*sum = ~csum;
}
SEC("prog")
int xdp_main(struct xdp_md *ctx)
{
void *data_end = (void *)(uintptr_t)ctx->data_end;
void *data = (void *)(uintptr_t)ctx->data;
uint8_t old_ttl;
struct ethhdr *eth = data;
struct iphdr *iph = (struct iphdr *)(eth + 1);
struct icmphdr *icmph = (struct icmphdr *)(iph + 1);
/* sanity check needed by the eBPF verifier */
if (OVER(eth, data_end))
return XDP_DROP;
if (eth->h_proto != ntohs(ETH_P_IP))
return XDP_PASS;
/* sanity check needed by the eBPF verifier */
if (OVER(iph, data_end))
return XDP_DROP;
if (iph->protocol != IPPROTO_ICMP)
return XDP_PASS;
/* sanity check needed by the eBPF verifier */
if (OVER(icmph, data_end))
return XDP_DROP;
/* let other ICMP pass */
if (icmph->type != ICMP_ECHOREPLY)
return XDP_PASS;
/* drop even packets */
if (!(icmph->un.echo.sequence & ntohs(1)))
return XDP_DROP;
/* set the TTL to a pseudorandom number 1..255 */
old_ttl = iph->ttl;
iph->ttl = bpf_get_prandom_u32() & 0xff ?: 1;
/* recalculate the checksum, otherwise the IP stack wil drop it */
csum_replace2(&iph->check, htons(old_ttl << 8), htons(iph->ttl << 8));
return XDP_PASS;
}
char _license[] SEC("license") = "GPL";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment