diff --git a/internal/cmd/gentypes/main.go b/internal/cmd/gentypes/main.go index 4b31d30df..d2d96ad05 100644 --- a/internal/cmd/gentypes/main.go +++ b/internal/cmd/gentypes/main.go @@ -430,6 +430,17 @@ import ( rename("cnt", "count"), }, }, + { + "LinkCreateNetfilter", retFd, "link_create", "BPF_LINK_CREATE", + []patch{ + chooseNth(4, 5), + replace(enumTypes["AttachType"], "attach_type"), + modify(func(m *btf.Member) error { + return rename("flags", "netfilter_flags")(m.Type.(*btf.Struct)) + }, "netfilter"), + flattenAnon, + }, + }, { "LinkCreateTracing", retFd, "link_create", "BPF_LINK_CREATE", []patch{ @@ -564,6 +575,7 @@ import ( {"TcxLinkInfo", "tcx", []patch{ replace(enumTypes["AttachType"], "attach_type"), }}, + {"NetfilterLinkInfo", "netfilter", nil}, } sort.Slice(linkInfoExtraTypes, func(i, j int) bool { diff --git a/internal/sys/types.go b/internal/sys/types.go index d1ba720d2..db7baa022 100644 --- a/internal/sys/types.go +++ b/internal/sys/types.go @@ -720,6 +720,26 @@ func LinkCreateKprobeMulti(attr *LinkCreateKprobeMultiAttr) (*FD, error) { return NewFD(int(fd)) } +type LinkCreateNetfilterAttr struct { + ProgFd uint32 + TargetFd uint32 + AttachType AttachType + Flags uint32 + Pf uint32 + Hooknum uint32 + Priority int32 + NetfilterFlags uint32 + _ [32]byte +} + +func LinkCreateNetfilter(attr *LinkCreateNetfilterAttr) (*FD, error) { + fd, err := BPF(BPF_LINK_CREATE, unsafe.Pointer(attr), unsafe.Sizeof(*attr)) + if err != nil { + return nil, err + } + return NewFD(int(fd)) +} + type LinkCreatePerfEventAttr struct { ProgFd uint32 TargetFd uint32 @@ -1201,6 +1221,13 @@ type NetNsLinkInfo struct { AttachType AttachType } +type NetfilterLinkInfo struct { + Pf uint32 + Hooknum uint32 + Priority int32 + Flags uint32 +} + type RawTracepointLinkInfo struct { TpName Pointer TpNameLen uint32 diff --git a/link/link.go b/link/link.go index 16e7e06d9..03368a201 100644 --- a/link/link.go +++ b/link/link.go @@ -102,6 +102,8 @@ func wrapRawLink(raw *RawLink) (_ Link, err error) { return nil, fmt.Errorf("recovering perf event fd: %w", ErrNotSupported) case TCXType: return &tcxLink{*raw}, nil + case NetfilterType: + return &netfilterLink{*raw}, nil default: return raw, nil } @@ -137,6 +139,7 @@ type CgroupInfo sys.CgroupLinkInfo type NetNsInfo sys.NetNsLinkInfo type XDPInfo sys.XDPLinkInfo type TCXInfo sys.TcxLinkInfo +type NetfilterInfo sys.NetfilterLinkInfo // Tracing returns tracing type-specific link info. // @@ -178,6 +181,14 @@ func (r Info) TCX() *TCXInfo { return e } +// Netfilter returns netfilter type-specific link info. +// +// Returns nil if the type-specific link info isn't available. +func (r Info) Netfilter() *NetfilterInfo { + e, _ := r.extra.(*NetfilterInfo) + return e +} + // RawLink is the low-level API to bpf_link. // // You should consider using the higher level interfaces in this @@ -330,6 +341,8 @@ func (l *RawLink) Info() (*Info, error) { // Extra metadata not supported. case TCXType: extra = &TCXInfo{} + case NetfilterType: + extra = &NetfilterInfo{} default: return nil, fmt.Errorf("unknown link info type: %d", info.Type) } diff --git a/link/link_test.go b/link/link_test.go index 785c63972..baf65c27f 100644 --- a/link/link_test.go +++ b/link/link_test.go @@ -209,6 +209,11 @@ func testLink(t *testing.T, link Link, prog *ebpf.Program) { if tcx.Ifindex == 0 { t.Fatalf("Failed to get link TCX extra info") } + case sys.BPF_LINK_TYPE_NETFILTER: + nf := info.Netfilter() + if nf.Priority == 0 { + t.Fatalf("Failed to get link Netfilter extra info") + } } }) diff --git a/link/netfilter.go b/link/netfilter.go new file mode 100644 index 000000000..250c87677 --- /dev/null +++ b/link/netfilter.go @@ -0,0 +1,70 @@ +package link + +import ( + "fmt" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/internal/sys" +) + +const NetfilterIPDefrag NetfilterAttachFlags = 0 // Enable IP packet defragmentation + +type NetfilterAttachFlags uint32 + +type NetfilterOptions struct { + // Program must be a netfilter BPF program. + Program *ebpf.Program + // The protocol family. + ProtocolFamily uint32 + // The number of the hook you are interested in. + HookNumber uint32 + // Priority within hook + Priority int32 + // Extra link flags + Flags uint32 + // Netfilter flags + NetfilterFlags NetfilterAttachFlags +} + +type netfilterLink struct { + RawLink +} + +// AttachNetfilter links a netfilter BPF program to a netfilter hook. +func AttachNetfilter(opts NetfilterOptions) (Link, error) { + if opts.Program == nil { + return nil, fmt.Errorf("netfilter program is nil") + } + + if t := opts.Program.Type(); t != ebpf.Netfilter { + return nil, fmt.Errorf("invalid program type %s, expected netfilter", t) + } + + progFd := opts.Program.FD() + if progFd < 0 { + return nil, fmt.Errorf("invalid program: %s", sys.ErrClosedFd) + } + + attr := sys.LinkCreateNetfilterAttr{ + ProgFd: uint32(opts.Program.FD()), + AttachType: sys.BPF_NETFILTER, + Flags: opts.Flags, + Pf: uint32(opts.ProtocolFamily), + Hooknum: uint32(opts.HookNumber), + Priority: opts.Priority, + NetfilterFlags: uint32(opts.NetfilterFlags), + } + + fd, err := sys.LinkCreateNetfilter(&attr) + if err != nil { + return nil, fmt.Errorf("attach netfilter link: %w", err) + } + + return &netfilterLink{RawLink{fd, ""}}, nil +} + +func (*netfilterLink) Update(new *ebpf.Program) error { + return fmt.Errorf("netfilter update: %w", ErrNotSupported) +} + +var _ Link = (*netfilterLink)(nil) diff --git a/link/netfilter_test.go b/link/netfilter_test.go new file mode 100644 index 000000000..1674ce6a1 --- /dev/null +++ b/link/netfilter_test.go @@ -0,0 +1,31 @@ +package link + +import ( + "testing" + + "github.com/cilium/ebpf" + "github.com/cilium/ebpf/internal/testutils" +) + +const ( + NFPROTO_IPV4 = 0x2 + NF_INET_LOCAL_OUT = 0x3 +) + +func TestAttachNetfilter(t *testing.T) { + testutils.SkipOnOldKernel(t, "6.4", "BPF_LINK_TYPE_NETFILTER") + + prog := mustLoadProgram(t, ebpf.Netfilter, ebpf.AttachNetfilter, "") + + l, err := AttachNetfilter(NetfilterOptions{ + Program: prog, + ProtocolFamily: NFPROTO_IPV4, + HookNumber: NF_INET_LOCAL_OUT, + Priority: -128, + }) + if err != nil { + t.Fatal(err) + } + + testLink(t, l, prog) +} diff --git a/link/syscalls.go b/link/syscalls.go index 8c1aec55c..53476c925 100644 --- a/link/syscalls.go +++ b/link/syscalls.go @@ -26,6 +26,7 @@ const ( KprobeMultiType = sys.BPF_LINK_TYPE_KPROBE_MULTI TCXType = sys.BPF_LINK_TYPE_TCX UprobeMultiType = sys.BPF_LINK_TYPE_UPROBE_MULTI + NetfilterType = sys.BPF_LINK_TYPE_NETFILTER ) var haveProgAttach = internal.NewFeatureTest("BPF_PROG_ATTACH", "4.10", func() error {