-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathexample_gteq_1.16_test.go
143 lines (128 loc) · 4.11 KB
/
example_gteq_1.16_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//go:build go1.16 && linux
// +build go1.16,linux
package tc_test
import (
"fmt"
"net"
"os"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/asm"
"github.com/florianl/go-tc"
"github.com/florianl/go-tc/core"
"github.com/florianl/go-tc/internal/unix"
"github.com/jsimonetti/rtnetlink"
"github.com/mdlayher/netlink"
)
// This example demonstrate how to attach an eBPF program with TC to an interface.
func Example_eBPF() {
tcIface := "ExampleEBPF"
// For the purpose of testing a dummy network interface is set up for the example.
rtnl, err := setupDummyInterface(tcIface)
if err != nil {
fmt.Fprintf(os.Stderr, "could not setup dummy interface: %v\n", err)
return
}
defer rtnl.Close()
// Get the net.Interface by its name to which the tc/qdisc and tc/filter with
// the eBPF program will be attached on.
devID, err := net.InterfaceByName(tcIface)
if err != nil {
fmt.Fprintf(os.Stderr, "could not get interface ID: %v\n", err)
return
}
defer func(devID uint32, rtnl *rtnetlink.Conn) {
// As a dummy network interface was set up for this test make sure to
// remove this link again once this example finished.
if err := rtnl.Link.Delete(devID); err != nil {
fmt.Fprintf(os.Stderr, "could not delete interface: %v\n", err)
}
}(uint32(devID.Index), rtnl)
// Open a netlink/tc connection to the Linux kernel. This connection is
// used to manage the tc/qdisc and tc/filter to which
// the eBPF program will be attached
tcnl, err := tc.Open(&tc.Config{})
if err != nil {
fmt.Fprintf(os.Stderr, "could not open rtnetlink socket: %v\n", err)
return
}
defer func() {
if err := tcnl.Close(); err != nil {
fmt.Fprintf(os.Stderr, "could not close rtnetlink socket: %v\n", err)
}
}()
// For enhanced error messages from the kernel, it is recommended to set
// option `NETLINK_EXT_ACK`, which is supported since 4.12 kernel.
//
// If not supported, `unix.ENOPROTOOPT` is returned.
if err := tcnl.SetOption(netlink.ExtendedAcknowledge, true); err != nil {
fmt.Fprintf(os.Stderr, "could not set option ExtendedAcknowledge: %v\n", err)
return
}
// Create a qdisc/clsact object that will be attached to the ingress part
// of the networking interface.
qdisc := tc.Object{
Msg: tc.Msg{
Family: unix.AF_UNSPEC,
Ifindex: uint32(devID.Index),
Handle: core.BuildHandle(tc.HandleRoot, 0x0000),
Parent: tc.HandleIngress,
Info: 0,
},
Attribute: tc.Attribute{
Kind: "clsact",
},
}
// Attach the qdisc/clsact to the networking interface.
if err := tcnl.Qdisc().Add(&qdisc); err != nil {
fmt.Fprintf(os.Stderr, "could not assign clsact to %s: %v\n", tcIface, err)
return
}
// When deleting the qdisc, the applied filter will also be gone
defer tcnl.Qdisc().Delete(&qdisc)
// Handcraft an eBPF program of type BPF_PROG_TYPE_SCHED_CLS that will be attached to
// the networking interface via qdisc/clsact.
//
// For eBPF programs of type BPF_PROG_TYPE_SCHED_CLS the returned code defines the action that
// will be applied to the network packet. Returning 0 translates to TC_ACT_OK and will terminate
// the packet processing pipeline within netlink/tc and allows the packet to proceed.
spec := ebpf.ProgramSpec{
Name: "test",
Type: ebpf.SchedCLS,
Instructions: asm.Instructions{
// Set exit code to 0
asm.Mov.Imm(asm.R0, 0),
asm.Return(),
},
License: "GPL",
}
// Load the eBPF program into the kernel.
prog, err := ebpf.NewProgram(&spec)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to load eBPF program: %v\n", err)
return
}
fd := uint32(prog.FD())
flags := uint32(0x1)
// Create a tc/filter object that will attach the eBPF program to the qdisc/clsact.
filter := tc.Object{
tc.Msg{
Family: unix.AF_UNSPEC,
Ifindex: uint32(devID.Index),
Handle: 0,
Parent: core.BuildHandle(tc.HandleRoot, tc.HandleMinIngress),
Info: 0x300,
},
tc.Attribute{
Kind: "bpf",
BPF: &tc.Bpf{
FD: &fd,
Flags: &flags,
},
},
}
// Attach the tc/filter object with the eBPF program to the qdisc/clsact.
if err := tcnl.Filter().Add(&filter); err != nil {
fmt.Fprintf(os.Stderr, "could not attach filter for eBPF program: %v\n", err)
return
}
}