Repository: cilium/ebpf
Pull request #490 (2021-11-17 13:56:46)

link: use sys types and simplify feature test
link: simplify link feature test
    
    Don't load a program to check for the presence of bpf_link, we can
    just call BPF_LINK_CREATE with an invalid fd instead.

link: use sys constants


Patch:
diff --git a/asm/instruction.go b/asm/instruction.go
index 64d717d..9774e5d 100644
--- a/asm/instruction.go
+++ b/asm/instruction.go
@@ -275,6 +275,11 @@ ref:
 	}
 }
 
+// Size returns the amount of bytes ins would occupy in binary form.
+func (ins Instruction) Size() uint64 {
+	return uint64(InstructionSize * ins.OpCode.rawInstructions())
+}
+
 // Instructions is an eBPF program.
 type Instructions []Instruction
 
@@ -282,6 +287,15 @@ func (insns Instructions) String() string {
 	return fmt.Sprint(insns)
 }
 
+// Size returns the amount of bytes insns would occupy in binary form.
+func (insns Instructions) Size() uint64 {
+	var sum uint64
+	for _, ins := range insns {
+		sum += ins.Size()
+	}
+	return sum
+}
+
 // RewriteMapPtr rewrites all loads of a specific map pointer to a new fd.
 //
 // Returns an error if the symbol isn't used, see IsUnreferencedSymbol.
diff --git a/elf_reader.go b/elf_reader.go
index bcc65ff..0e29429 100644
--- a/elf_reader.go
+++ b/elf_reader.go
@@ -990,8 +990,8 @@ func getProgType(sectionName string) (ProgramType, AttachType, uint32, string) {
 		"lwt_seg6local":         {LWTSeg6Local, AttachNone, 0},
 		"sockops":               {SockOps, AttachCGroupSockOps, 0},
 		"sk_skb/stream_parser":  {SkSKB, AttachSkSKBStreamParser, 0},
-		"sk_skb/stream_verdict": {SkSKB, AttachSkSKBStreamParser, 0},
-		"sk_msg":                {SkMsg, AttachSkSKBStreamVerdict, 0},
+		"sk_skb/stream_verdict": {SkSKB, AttachSkSKBStreamVerdict, 0},
+		"sk_msg":                {SkMsg, AttachSkMsgVerdict, 0},
 		"lirc_mode2":            {LircMode2, AttachLircMode2, 0},
 		"flow_dissector":        {FlowDissector, AttachFlowDissector, 0},
 		"iter/":                 {Tracing, AttachTraceIter, 0},
diff --git a/features/prog.go b/features/prog.go
index f178e63..04b1a88 100644
--- a/features/prog.go
+++ b/features/prog.go
@@ -36,7 +36,7 @@ func createProgLoadAttr(pt ebpf.ProgramType) (*sys.ProgLoadAttr, error) {
 		asm.Return(),
 	}
 
-	buf := bytes.NewBuffer(make([]byte, 0, len(insns)*asm.InstructionSize))
+	buf := bytes.NewBuffer(make([]byte, 0, insns.Size()))
 	if err := insns.Marshal(buf, internal.NativeEndian); err != nil {
 		return nil, err
 	}
diff --git a/internal/cmd/gentypes/main.go b/internal/cmd/gentypes/main.go
index 47a3a64..5be21fe 100644
--- a/internal/cmd/gentypes/main.go
+++ b/internal/cmd/gentypes/main.go
@@ -70,6 +70,7 @@ func run(args []string) error {
 
 func generateTypes(spec *btf.Spec) ([]byte, error) {
 	objName := &btf.Array{Nelems: 16, Type: &btf.Int{Encoding: btf.Char, Size: 1}}
+	linkID := &btf.Int{Size: 4}
 	pointer := &btf.Int{Size: 8}
 
 	// Pre-declare handwritten types sys.ObjName and sys.Pointer so that
@@ -77,11 +78,13 @@ func generateTypes(spec *btf.Spec) ([]byte, error) {
 	var (
 		_ sys.Pointer
 		_ sys.ObjName
+		_ sys.LinkID
 	)
 
 	gf := &btf.GoFormatter{
 		Names: map[btf.Type]string{
 			objName: "ObjName",
+			linkID:  "LinkID",
 			pointer: "Pointer",
 		},
 		Identifier: internal.Identifier,
@@ -173,6 +176,7 @@ import (
 			"LinkInfo", "bpf_link_info",
 			[]patch{
 				replace(enumTypes["LinkType"], "type"),
+				replace(linkID, "id"),
 				truncateAfter("prog_id"),
 			},
 		},
diff --git a/internal/sys/syscall.go b/internal/sys/syscall.go
index 2da57ef..dd515f0 100644
--- a/internal/sys/syscall.go
+++ b/internal/sys/syscall.go
@@ -90,6 +90,9 @@ func NewObjName(name string) ObjName {
 	return result
 }
 
+// LinkID uniquely identifies a bpf_link.
+type LinkID uint32
+
 // wrappedErrno wraps syscall.Errno to prevent direct comparisons with
 // syscall.E* or unix.E* constants.
 //
diff --git a/internal/sys/types.go b/internal/sys/types.go
index 67088d4..f9702d4 100644
--- a/internal/sys/types.go
+++ b/internal/sys/types.go
@@ -425,7 +425,7 @@ type LineInfo struct {
 
 type LinkInfo struct {
 	Type   LinkType
-	Id     uint32
+	Id     LinkID
 	ProgId uint32
 }
 
diff --git a/link/link.go b/link/link.go
index 4d962e5..94aeaff 100644
--- a/link/link.go
+++ b/link/link.go
@@ -40,7 +40,7 @@ type Link interface {
 }
 
 // ID uniquely identifies a BPF link.
-type ID uint32
+type ID = sys.LinkID
 
 // RawLinkOptions control the creation of a raw link.
 type RawLinkOptions struct {
@@ -52,6 +52,8 @@ type RawLinkOptions struct {
 	Attach ebpf.AttachType
 	// BTF is the BTF of the attachment target.
 	BTF btf.TypeID
+	// Flags control the attach behaviour.
+	Flags uint32
 }
 
 // RawLinkInfo contains metadata on a link.
@@ -90,6 +92,7 @@ func AttachRawLink(opts RawLinkOptions) (*RawLink, error) {
 		ProgFd:      uint32(progFd),
 		AttachType:  sys.AttachType(opts.Attach),
 		TargetBtfId: uint32(opts.BTF),
+		Flags:       opts.Flags,
 	}
 	fd, err := sys.LinkCreate(&attr)
 	if err != nil {
@@ -212,8 +215,8 @@ func (l *RawLink) Info() (*RawLinkInfo, error) {
 	}
 
 	return &RawLinkInfo{
-		Type(info.Type),
-		ID(info.Id),
+		info.Type,
+		info.Id,
 		ebpf.ProgramID(info.ProgId),
 	}, nil
 }
diff --git a/link/syscalls.go b/link/syscalls.go
index 00b4acf..072dfad 100644
--- a/link/syscalls.go
+++ b/link/syscalls.go
@@ -11,19 +11,17 @@ import (
 )
 
 // Type is the kind of link.
-type Type uint32
+type Type = sys.LinkType
 
 // Valid link types.
-//
-// Equivalent to enum bpf_link_type.
 const (
-	UnspecifiedType Type = iota
-	RawTracepointType
-	TracingType
-	CgroupType
-	IterType
-	NetNsType
-	XDPType
+	UnspecifiedType   = sys.BPF_LINK_TYPE_UNSPEC
+	RawTracepointType = sys.BPF_LINK_TYPE_RAW_TRACEPOINT
+	TracingType       = sys.BPF_LINK_TYPE_TRACING
+	CgroupType        = sys.BPF_LINK_TYPE_CGROUP
+	IterType          = sys.BPF_LINK_TYPE_ITER
+	NetNsType         = sys.BPF_LINK_TYPE_NETNS
+	XDPType           = sys.BPF_LINK_TYPE_XDP
 )
 
 var haveProgAttach = internal.FeatureTest("BPF_PROG_ATTACH", "4.10", func() error {
@@ -88,27 +86,13 @@ var haveProgAttachReplace = internal.FeatureTest("BPF_PROG_ATTACH atomic replace
 })
 
 var haveBPFLink = internal.FeatureTest("bpf_link", "5.7", func() error {
-	prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
-		Type:       ebpf.CGroupSKB,
-		AttachType: ebpf.AttachCGroupInetIngress,
-		License:    "MIT",
-		Instructions: asm.Instructions{
-			asm.Mov.Imm(asm.R0, 0),
-			asm.Return(),
-		},
-	})
-	if err != nil {
-		return internal.ErrNotSupported
-	}
-	defer prog.Close()
-
 	attr := sys.LinkCreateAttr{
 		// This is a hopefully invalid file descriptor, which triggers EBADF.
 		TargetFd:   ^uint32(0),
-		ProgFd:     uint32(prog.FD()),
+		ProgFd:     ^uint32(0),
 		AttachType: sys.AttachType(ebpf.AttachCGroupInetIngress),
 	}
-	_, err = sys.LinkCreate(&attr)
+	_, err := sys.LinkCreate(&attr)
 	if errors.Is(err, unix.EINVAL) {
 		return internal.ErrNotSupported
 	}
diff --git a/link/xdp.go b/link/xdp.go
new file mode 100644
index 0000000..aa8dd3a
--- /dev/null
+++ b/link/xdp.go
@@ -0,0 +1,54 @@
+package link
+
+import (
+	"fmt"
+
+	"github.com/cilium/ebpf"
+)
+
+// XDPAttachFlags represents how XDP program will be attached to interface.
+type XDPAttachFlags uint32
+
+const (
+	// XDPGenericMode (SKB) links XDP BPF program for drivers which do
+	// not yet support native XDP.
+	XDPGenericMode XDPAttachFlags = 1 << (iota + 1)
+	// XDPDriverMode links XDP BPF program into the driver’s receive path.
+	XDPDriverMode
+	// XDPOffloadMode offloads the entire XDP BPF program into hardware.
+	XDPOffloadMode
+)
+
+type XDPOptions struct {
+	// Program must be an XDP BPF program.
+	Program *ebpf.Program
+
+	// Interface is the interface index to attach program to.
+	Interface int
+
+	// Flags is one of XDPAttachFlags (optional).
+	//
+	// Only one XDP mode should be set, without flag defaults
+	// to driver/generic mode (best effort).
+	Flags XDPAttachFlags
+}
+
+// AttachXDP links an XDP BPF program to an XDP hook.
+func AttachXDP(opts XDPOptions) (Link, error) {
+	if t := opts.Program.Type(); t != ebpf.XDP {
+		return nil, fmt.Errorf("invalid program type %s, expected XDP", t)
+	}
+
+	if opts.Interface < 1 {
+		return nil, fmt.Errorf("invalid interface index: %d", opts.Interface)
+	}
+
+	rawLink, err := AttachRawLink(RawLinkOptions{
+		Program: opts.Program,
+		Attach:  ebpf.AttachXDP,
+		Target:  opts.Interface,
+		Flags:   uint32(opts.Flags),
+	})
+
+	return rawLink, err
+}
diff --git a/prog.go b/prog.go
index f538730..aebbfeb 100644
--- a/prog.go
+++ b/prog.go
@@ -237,7 +237,7 @@ func newProgramWithOptions(spec *ProgramSpec, opts ProgramOptions, handles *hand
 		return nil, err
 	}
 
-	buf := bytes.NewBuffer(make([]byte, 0, len(spec.Instructions)*asm.InstructionSize))
+	buf := bytes.NewBuffer(make([]byte, 0, insns.Size()))
 	err = insns.Marshal(buf, internal.NativeEndian)
 	if err != nil {
 		return nil, err
diff --git a/syscalls.go b/syscalls.go
index 8456a60..ccbbe09 100644
--- a/syscalls.go
+++ b/syscalls.go
@@ -226,7 +226,7 @@ var haveProbeReadKernel = internal.FeatureTest("bpf_probe_read_kernel", "5.5", f
 		asm.FnProbeReadKernel.Call(),
 		asm.Return(),
 	}
-	buf := bytes.NewBuffer(make([]byte, 0, len(insns)*asm.InstructionSize))
+	buf := bytes.NewBuffer(make([]byte, 0, insns.Size()))
 	if err := insns.Marshal(buf, internal.NativeEndian); err != nil {
 		return err
 	}


Test patch:
diff --git a/link/xdp_test.go b/link/xdp_test.go
new file mode 100644
index 0000000..ae8006e
--- /dev/null
+++ b/link/xdp_test.go
@@ -0,0 +1,42 @@
+package link
+
+import (
+	"testing"
+
+	"github.com/cilium/ebpf"
+	"github.com/cilium/ebpf/asm"
+	"github.com/cilium/ebpf/internal/testutils"
+)
+
+const IfIndexLO = 1
+
+func TestAttachXDP(t *testing.T) {
+	testutils.SkipOnOldKernel(t, "5.9", "BPF_LINK_TYPE_XDP")
+	prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{
+		Type: ebpf.XDP,
+		Instructions: asm.Instructions{
+			asm.LoadImm(asm.R0, 2, asm.DWord),
+			asm.Return(),
+		},
+		License: "MIT",
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer prog.Close()
+
+	l, err := AttachXDP(XDPOptions{
+		Program:   prog,
+		Interface: IfIndexLO,
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	testLink(t, l, testLinkOptions{
+		prog: prog,
+		loadPinned: func(s string, opts *ebpf.LoadPinOptions) (Link, error) {
+			return LoadPinnedRawLink(s, XDPType, opts)
+		},
+	})
+}