Last active
December 19, 2023 08:38
-
-
Save teknoraver/b66115e3518bb1b7f3e79f52aa2c3424 to your computer and use it in GitHub Desktop.
Sample XDP program to mangle ICMP echo replies
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* xdp_manglepong.c - sample XDP program | |
* Copyright (C) 2019 Matteo Croce <[email protected]> | |
* | |
* 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