commit fa756500d3ebc7775b28116626723d88bdc06d69 Author: Alexey Novikov Date: Fri Feb 7 13:32:34 2025 +0300 initial diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e7605c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +pinger_bpf* +cursed-ping +result diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..89fa686 --- /dev/null +++ b/flake.lock @@ -0,0 +1,60 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1738680400, + "narHash": "sha256-ooLh+XW8jfa+91F1nhf9OF7qhuA/y1ChLx6lXDNeY5U=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "799ba5bffed04ced7067a91798353d360788b30d", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-unstable", + "type": "indirect" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..dc668fb --- /dev/null +++ b/flake.nix @@ -0,0 +1,64 @@ +{ + description = "cursed ping"; + + inputs = { + nixpkgs.url = "nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + }; + + outputs = { + self, + nixpkgs, + flake-utils, + ... + }: + { + overlays.default = final: prev: { + inherit (self.packages.${prev.system}) bpf2go; + }; + } // flake-utils.lib.eachDefaultSystem (system: let + pkgs = import nixpkgs { + inherit system; + overlays = [ self.overlays.default ]; + }; + in { + packages = { + bpf2go = pkgs.buildGoModule rec { + pname = "bpf2go"; + version = "0.17.2"; + src = pkgs.fetchFromGitHub { + owner = "cilium"; + repo = "ebpf"; + rev = "v${version}"; + hash = "sha256-lIJsITwdt6PuTMEHXJekBKM2rgCdPJF+i+QHt0jBgp8="; + }; + vendorHash = "sha256-Ygljpp5GVM2EbD51q1ufqA6z5yJnrXVEfVLIPrxfm18="; + subPackages = [ "cmd/bpf2go" ]; + doCheck = false; + }; + pinger = pkgs.buildGoModule rec { + pname = "cursed-ping"; + version = "1"; + src = ./.; + vendorHash = "sha256-RxmMgiMATpO31VP85dlkOXl/nLbVD5W1dfWHhuGKcME="; + deleteVendor = true; + + ldflags = ["-s -w"]; + env.CGO_ENABLED = 0; + nativeBuildInputs = with pkgs; [clang bpftools libllvm linuxHeaders bpf2go glibc_multi libbpf]; + hardeningDisable = [ "all" ]; + buildInputs = nativeBuildInputs; + + postConfigure = '' + go generate ./... + ''; + + meta = with pkgs.lib; { + description = "cursed ping"; + license = licenses.wtfpl; + }; + }; + }; + formatter = pkgs.alejandra; + }); +} diff --git a/gen.go b/gen.go new file mode 100644 index 0000000..cd1a911 --- /dev/null +++ b/gen.go @@ -0,0 +1,3 @@ +package main + +//go:generate bpf2go pinger xdp.c diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8f99509 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/x3lfyn/cursed-ping + +go 1.23.3 + +require ( + github.com/cilium/ebpf v0.17.1 // indirect + golang.org/x/sys v0.26.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7bcd880 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/cilium/ebpf v0.17.1 h1:G8mzU81R2JA1nE5/8SRubzqvBMmAmri2VL8BIZPWvV0= +github.com/cilium/ebpf v0.17.1/go.mod h1:vay2FaYSmIlv3r8dNACd4mW/OCaZLJKJOo+IHBvCIO8= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/main.go b/main.go new file mode 100644 index 0000000..fe7452e --- /dev/null +++ b/main.go @@ -0,0 +1,56 @@ +package main + +import ( + "errors" + "fmt" + "log" + "net" + "os" + "os/signal" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/link" +) + +func main() { + if len(os.Args) < 2 { + log.Fatalf("specify a network interface") + } + + ifaceName := os.Args[1] + iface, err := net.InterfaceByName(ifaceName) + if err != nil { + log.Fatalf("lookup network iface %q: %s", ifaceName, err) + } + + var objs pingerObjects + if err := loadPingerObjects(&objs, nil); err != nil { + var verr *ebpf.VerifierError + if errors.As(err, &verr) { + fmt.Printf("%+v\n", verr) + } + + } + defer objs.Close() + + link, err := link.AttachXDP(link.XDPOptions{ + Program: objs.Pinger, + Interface: iface.Index, + }) + if err != nil { + log.Fatal("attaching XDP:", err) + } + defer link.Close() + + log.Printf("attached to %s", ifaceName) + + stop := make(chan os.Signal, 5) + signal.Notify(stop, os.Interrupt) + for { + select { + case <-stop: + log.Print("received signal, exiting..") + return + } + } +} diff --git a/xdp.c b/xdp.c new file mode 100644 index 0000000..71290ed --- /dev/null +++ b/xdp.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include +#include +#include + +#define bpf_memcpy __builtin_memcpy + +__attribute__((__always_inline__)) static inline __u16 csum_fold_helper( + __u64 csum) { + int i; +#pragma unroll + for (i = 0; i < 4; i++) { + if (csum >> 16) + csum = (csum & 0xffff) + (csum >> 16); + } + return ~csum; +} + +// https://github.com/AirVantage/sbulb/blob/master/sbulb/bpf/checksum.c#L21 +__attribute__((__always_inline__)) +static inline void update_csum(__u64 *csum, __be32 old_addr,__be32 new_addr ) { + // ~HC + *csum = ~*csum; + *csum = *csum & 0xffff; + // + ~m + __u32 tmp; + tmp = ~old_addr; + *csum += tmp; + // + m + *csum += new_addr; + // then fold and complement result ! + *csum = csum_fold_helper(*csum); +} + +__attribute__((__always_inline__)) +static inline void recalc_icmp_csum(struct icmphdr* hdr, __be32 old_value, __be32 new_value) { + __u64 csum = hdr->checksum; + update_csum(&csum, old_value, new_value); + hdr->checksum = csum; +} + +__attribute__((__always_inline__)) +static inline void recalc_ip_csum(struct iphdr* hdr, __be32 old_value, __be32 new_value) { + __u64 csum = hdr->check; + update_csum(&csum, old_value, new_value); + hdr->check = csum; +} + +SEC("xdp") +int pinger(struct xdp_md* ctx) { + void *data_end = (void *)(long)ctx->data_end; + void *data = (void *)(long)ctx->data; + + struct ethhdr *eth = data; + if ((void *)(eth + 1) > data_end) { + return XDP_PASS; + } + + if (bpf_ntohs(eth->h_proto) != ETH_P_IP) { + return XDP_PASS; + } + + struct iphdr *iph = (void*)(eth + 1); + if ((void*)(iph + 1) > data_end) { + return XDP_PASS; + } + + if (iph->protocol != IPPROTO_ICMP) { + return XDP_PASS; + } + + struct icmphdr* icmphdr = (void*)(iph + 1); + if ((void*)(icmphdr + 1) > data_end) { + return XDP_PASS; + } + + if (icmphdr->type != 8) { + return XDP_PASS; + } + + __u8 tmp_mac[ETH_ALEN]; + bpf_memcpy(tmp_mac, eth->h_dest, ETH_ALEN); + bpf_memcpy(eth->h_dest, eth->h_source, ETH_ALEN); + bpf_memcpy(eth->h_source, tmp_mac, ETH_ALEN); + + __u32 tmp_ip = iph->daddr; + iph->daddr = iph->saddr; + iph->saddr = tmp_ip; + + icmphdr->type = 0; + recalc_icmp_csum(icmphdr, 8, icmphdr->type); + + __u64* ts_secs = (void*)(icmphdr + 1); + __u64* ts_nsecs = (void*)(icmphdr + 1) + sizeof(__u64); + + if ((void*)ts_nsecs + sizeof(__u64) <= data_end) { + __u64 old_secs = *ts_secs; + __u64 old_nsecs = *ts_nsecs; + + *ts_secs -= bpf_get_prandom_u32() % 500; + *ts_nsecs -= bpf_get_prandom_u32(); + + recalc_icmp_csum(icmphdr, old_secs, *ts_secs); + recalc_icmp_csum(icmphdr, old_nsecs, *ts_nsecs); + } + + __u8 old_ttl = iph->ttl; + iph->ttl = bpf_get_prandom_u32() % 200 + 40; + recalc_ip_csum(iph, old_ttl, iph->ttl); + + __be16 old_seq = icmphdr->un.echo.sequence; + icmphdr->un.echo.sequence = bpf_htons(bpf_get_prandom_u32() % 1000); + recalc_icmp_csum(icmphdr, old_seq, icmphdr->un.echo.sequence); + + return XDP_TX; +} + +char LICENSE[] SEC("license") = "GPL"; +