mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-08 22:49:10 +02:00
ebpf test
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/cilium/ebpf"
|
||||
"net"
|
||||
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// Filter represents a classic BPF filter program that can be applied to a socket
|
||||
type Filter struct {
|
||||
*ebpf.ProgramSpec
|
||||
}
|
||||
|
||||
// ApplyTo applies the current filter onto the provided UDPConn
|
||||
func (filter Filter) ApplyTo(conn *net.UDPConn) error {
|
||||
|
||||
file, err := conn.File()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p, err := ebpf.NewProgramWithOptions(filter.ProgramSpec, ebpf.ProgramOptions{
|
||||
LogLevel: 6,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := syscall.SetsockoptInt(int(file.Fd()), syscall.SOL_SOCKET, SO_ATTACH_BPF, p.FD()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/asm"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
SO_ATTACH_BPF int = 50
|
||||
StunMagicCookie uint32 = 0x2112A442
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// open a raw socket
|
||||
/*fd, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_RAW, syscall.IPPROTO_UDP)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Print(fd)*/
|
||||
addr := net.UDPAddr{
|
||||
IP: net.IPv4zero,
|
||||
Port: 12345,
|
||||
}
|
||||
conn, err := net.ListenUDP("udp4", &addr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
filter := &Filter{
|
||||
ProgramSpec: &ebpf.ProgramSpec{
|
||||
Type: ebpf.SocketFilter,
|
||||
License: "GPL",
|
||||
Instructions: asm.Instructions{
|
||||
asm.Mov.Reg(asm.R6, asm.R1), // LDABS requires ctx in R6
|
||||
asm.LoadAbs(-0x100000+22, asm.Half),
|
||||
asm.JNE.Imm(asm.R0, int32(addr.Port), "skip"),
|
||||
/* asm.LoadAbs(-0x100000+32, asm.Word),
|
||||
asm.JNE.Imm(asm.R0, int32(StunMagicCookie), "skip"),
|
||||
asm.Mov.Imm(asm.R0, -1).Sym("exit"),*/
|
||||
/*asm.Return(),*/
|
||||
asm.Mov.Imm(asm.R0, 0).Sym("skip"),
|
||||
asm.Return(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err = filter.ApplyTo(conn)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("start")
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
n, ra, err := conn.ReadFrom(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Printf("Bytes read: %d\n", n)
|
||||
fmt.Printf("Remote Addr: %+v\n", ra)
|
||||
fmt.Printf("Bytes HEX: %s\n", hex.EncodeToString(buf[:n]))
|
||||
fmt.Printf("Bytes String: %s\n", string(buf[:n]))
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package main
|
||||
|
||||
// This code is derived from https://github.com/cloudflare/cloudflare-blog/tree/master/2018-03-ebpf
|
||||
//
|
||||
// Copyright (c) 2015-2017 Cloudflare, Inc. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of the Cloudflare, Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/asm"
|
||||
)
|
||||
|
||||
// ExampleExtractDistance shows how to attach an eBPF socket filter to
|
||||
// extract the network distance of an IP host.
|
||||
func main() {
|
||||
filter, TTLs, err := newDistanceFilter()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer filter.Close()
|
||||
defer TTLs.Close()
|
||||
|
||||
// Attach filter before the call to connect()
|
||||
dialer := net.Dialer{
|
||||
Control: func(network, address string, c syscall.RawConn) (err error) {
|
||||
const SO_ATTACH_BPF = 50
|
||||
|
||||
err = c.Control(func(fd uintptr) {
|
||||
err = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, SO_ATTACH_BPF, filter.FD())
|
||||
})
|
||||
return err
|
||||
},
|
||||
}
|
||||
|
||||
conn, err := dialer.Dial("tcp", "1.1.1.1:53")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
conn.Close()
|
||||
|
||||
minDist, err := minDistance(TTLs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("1.1.1.1:53 is", minDist, "hops away")
|
||||
}
|
||||
|
||||
func newDistanceFilter() (*ebpf.Program, *ebpf.Map, error) {
|
||||
const ETH_P_IPV6 uint16 = 0x86DD
|
||||
|
||||
ttls, err := ebpf.NewMap(&ebpf.MapSpec{
|
||||
Type: ebpf.Hash,
|
||||
KeySize: 4,
|
||||
ValueSize: 8,
|
||||
MaxEntries: 4,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
insns := asm.Instructions{
|
||||
// r1 has ctx
|
||||
// r0 = ctx[16] (aka protocol)
|
||||
asm.LoadMem(asm.R0, asm.R1, 16, asm.Word),
|
||||
|
||||
// Perhaps ipv6
|
||||
asm.LoadImm(asm.R2, int64(ETH_P_IPV6), asm.DWord),
|
||||
asm.HostTo(asm.BE, asm.R2, asm.Half),
|
||||
asm.JEq.Reg(asm.R0, asm.R2, "ipv6"),
|
||||
|
||||
// otherwise assume ipv4
|
||||
// 8th byte in IPv4 is TTL
|
||||
// LDABS requires ctx in R6
|
||||
asm.Mov.Reg(asm.R6, asm.R1),
|
||||
asm.LoadAbs(-0x100000+8, asm.Byte),
|
||||
asm.Ja.Label("store-ttl"),
|
||||
|
||||
// 7th byte in IPv6 is Hop count
|
||||
// LDABS requires ctx in R6
|
||||
asm.Mov.Reg(asm.R6, asm.R1).Sym("ipv6"),
|
||||
asm.LoadAbs(-0x100000+7, asm.Byte),
|
||||
|
||||
// stash the load result into FP[-4]
|
||||
asm.StoreMem(asm.RFP, -4, asm.R0, asm.Word).Sym("store-ttl"),
|
||||
// stash the &FP[-4] into r2
|
||||
asm.Mov.Reg(asm.R2, asm.RFP),
|
||||
asm.Add.Imm(asm.R2, -4),
|
||||
|
||||
// r1 must point to map
|
||||
asm.LoadMapPtr(asm.R1, ttls.FD()),
|
||||
asm.FnMapLookupElem.Call(),
|
||||
|
||||
// load ok? inc. Otherwise? jmp to mapupdate
|
||||
asm.JEq.Imm(asm.R0, 0, "update-map"),
|
||||
asm.Mov.Imm(asm.R1, 1),
|
||||
asm.StoreXAdd(asm.R0, asm.R1, asm.DWord),
|
||||
asm.Ja.Label("exit"),
|
||||
|
||||
// MapUpdate
|
||||
// r1 has map ptr
|
||||
asm.LoadMapPtr(asm.R1, ttls.FD()).Sym("update-map"),
|
||||
// r2 has key -> &FP[-4]
|
||||
asm.Mov.Reg(asm.R2, asm.RFP),
|
||||
asm.Add.Imm(asm.R2, -4),
|
||||
// r3 has value -> &FP[-16] , aka 1
|
||||
asm.StoreImm(asm.RFP, -16, 1, asm.DWord),
|
||||
asm.Mov.Reg(asm.R3, asm.RFP),
|
||||
asm.Add.Imm(asm.R3, -16),
|
||||
// r4 has flags, 0
|
||||
asm.Mov.Imm(asm.R4, 0),
|
||||
asm.FnMapUpdateElem.Call(),
|
||||
|
||||
// set exit code to -1, don't trunc packet
|
||||
asm.Mov.Imm(asm.R0, -1).Sym("exit"),
|
||||
asm.Return(),
|
||||
}
|
||||
|
||||
prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
|
||||
Name: "distance_filter",
|
||||
Type: ebpf.SocketFilter,
|
||||
License: "GPL",
|
||||
Instructions: insns,
|
||||
})
|
||||
if err != nil {
|
||||
ttls.Close()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return prog, ttls, nil
|
||||
}
|
||||
|
||||
func minDistance(TTLs *ebpf.Map) (int, error) {
|
||||
var (
|
||||
entries = TTLs.Iterate()
|
||||
ttl uint32
|
||||
minDist uint32 = 255
|
||||
count uint64
|
||||
)
|
||||
for entries.Next(&ttl, &count) {
|
||||
var dist uint32
|
||||
switch {
|
||||
case ttl > 128:
|
||||
dist = 255 - ttl
|
||||
case ttl > 64:
|
||||
dist = 128 - ttl
|
||||
case ttl > 32:
|
||||
dist = 64 - ttl
|
||||
default:
|
||||
dist = 32 - ttl
|
||||
}
|
||||
if minDist > dist {
|
||||
minDist = dist
|
||||
}
|
||||
}
|
||||
return int(minDist), entries.Err()
|
||||
}
|
||||
Reference in New Issue
Block a user