Skip to content
This repository was archived by the owner on Dec 9, 2025. It is now read-only.

Commit 1eed7bf

Browse files
committed
expose ebpf interface capabilities
It will be useful for some platforms to filter based on the existince of ebpf programs on the interfaces. If the platforms also want to have more granularity on the filters, both the tc filters names and tcx program names are exposed, this can be useful for cilium environments, that attach the bpf programs with a well known prefix https: //github.com/cilium/cilium/blob/2732b5253beef350dd89216813d4e4da1180e8c7/pkg/datapath/loader/tcx.go#L139 Change-Id: I72d10dd04c364ca3d594c5fa2f1168fd2d179ba7
1 parent 0d3db6d commit 1eed7bf

File tree

9 files changed

+143
-1
lines changed

9 files changed

+143
-1
lines changed

.github/workflows/bats.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
env:
3333
BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }}
3434
TERM: xterm
35-
run: bats -o _artifacts tests/
35+
run: bats -o _artifacts --print-output-on-failure tests/
3636

3737
- name: Upload logs
3838
if: always()

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ require (
77
cloud.google.com/go/compute/metadata v0.7.0
88
cloud.google.com/go/container v1.43.0
99
github.com/Mellanox/rdmamap v1.1.0
10+
github.com/cilium/ebpf v0.18.0
1011
github.com/containerd/nri v0.9.0
1112
github.com/google/cel-go v0.25.0
1213
github.com/google/go-cmp v0.7.0

go.sum

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
2020
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
2121
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
2222
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
23+
github.com/cilium/ebpf v0.18.0 h1:OsSwqS4y+gQHxaKgg2U/+Fev834kdnsQbtzRnbVC6Gs=
24+
github.com/cilium/ebpf v0.18.0/go.mod h1:vmsAT73y4lW2b4peE+qcOqw6MxvWQdC+LiU5gd/xyo4=
2325
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
2426
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
2527
github.com/containerd/nri v0.9.0 h1:jribDJs/oQ95vLO4Yn19HKFYriZGWKiG6nKWjl9Y/x4=
@@ -48,6 +50,8 @@ github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF
4850
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
4951
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
5052
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
53+
github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6 h1:teYtXy9B7y5lHTp8V9KPxpYRAVA7dozigQcMiBust1s=
54+
github.com/go-quicktest/qt v1.101.1-0.20240301121107-c6c8733fa1e6/go.mod h1:p4lGIVX+8Wa6ZPNDvqcxq36XpUDLh42FLetFU7odllI=
5155
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
5256
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
5357
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -83,6 +87,9 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
8387
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
8488
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
8589
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
90+
github.com/jsimonetti/rtnetlink v1.3.5 h1:hVlNQNRlLDGZz31gBPicsG7Q53rnlsz1l1Ix/9XlpVA=
91+
github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM=
92+
github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE=
8693
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
8794
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
8895
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=

pkg/inventory/db.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,22 @@ func (db *DB) netdevToDRAdev(link netlink.Link) (*resourceapi.Device, error) {
251251
device.Basic.Attributes["dra.net/alias"] = resourceapi.DeviceAttribute{StringValue: &linkAttrs.Alias}
252252
device.Basic.Attributes["dra.net/type"] = resourceapi.DeviceAttribute{StringValue: &linkType}
253253

254+
// Get eBPF properties from the interface using the legacy tc hooks
255+
isEbpf := false
256+
filterNames, ok := getTcFilters(link)
257+
if ok {
258+
isEbpf = true
259+
device.Basic.Attributes["dra.net/tcFilterNames"] = resourceapi.DeviceAttribute{StringValue: ptr.To(strings.Join(filterNames, ","))}
260+
}
261+
262+
// Get eBPF properties from the interface using the tcx hooks
263+
programNames, ok := getTcxFilters(link)
264+
if ok {
265+
isEbpf = true
266+
device.Basic.Attributes["dra.net/tcxProgramNames"] = resourceapi.DeviceAttribute{StringValue: ptr.To(strings.Join(programNames, ","))}
267+
}
268+
device.Basic.Attributes["dra.net/ebpf"] = resourceapi.DeviceAttribute{BoolValue: &isEbpf}
269+
254270
isRDMA := rdmamap.IsRDmaDeviceForNetdevice(ifName)
255271
device.Basic.Attributes["dra.net/rdma"] = resourceapi.DeviceAttribute{BoolValue: &isRDMA}
256272
// from https://github.com/k8snetworkplumbingwg/sriov-network-device-plugin/blob/ed1c14dd4c313c7dd9fe4730a60358fbeffbfdd4/pkg/netdevice/netDeviceProvider.go#L99
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ limitations under the License.
1717
package inventory
1818

1919
import (
20+
"github.com/cilium/ebpf"
21+
"github.com/cilium/ebpf/link"
2022
"github.com/vishvananda/netlink"
2123

2224
"k8s.io/apimachinery/pkg/util/sets"
@@ -68,3 +70,51 @@ func getDefaultGwInterfaces() sets.Set[string] {
6870
klog.V(4).Infof("Found following interfaces for the default gateway: %v", interfaces.UnsortedList())
6971
return interfaces
7072
}
73+
74+
func getTcFilters(link netlink.Link) ([]string, bool) {
75+
isTcEBPF := false
76+
filterNames := sets.Set[string]{}
77+
for _, parent := range []uint32{netlink.HANDLE_MIN_INGRESS, netlink.HANDLE_MIN_EGRESS} {
78+
filters, err := netlink.FilterList(link, parent)
79+
if err == nil {
80+
for _, f := range filters {
81+
if bpffFilter, ok := f.(*netlink.BpfFilter); ok {
82+
isTcEBPF = true
83+
filterNames.Insert(bpffFilter.Name)
84+
}
85+
}
86+
}
87+
}
88+
return filterNames.UnsortedList(), isTcEBPF
89+
}
90+
91+
// see https://github.com/cilium/ebpf/issues/1117
92+
func getTcxFilters(device netlink.Link) ([]string, bool) {
93+
isTcxEBPF := false
94+
programNames := sets.Set[string]{}
95+
for _, attach := range []ebpf.AttachType{ebpf.AttachTCXIngress, ebpf.AttachTCXEgress} {
96+
result, err := link.QueryPrograms(link.QueryOptions{
97+
Target: int(device.Attrs().Index),
98+
Attach: attach,
99+
})
100+
if err != nil {
101+
continue
102+
}
103+
104+
isTcxEBPF = true
105+
for _, p := range result.Programs {
106+
prog, err := ebpf.NewProgramFromID(p.ID)
107+
if err != nil {
108+
continue
109+
}
110+
defer prog.Close()
111+
112+
pi, err := prog.Info()
113+
if err != nil {
114+
continue
115+
}
116+
programNames.Insert(pi.Name)
117+
}
118+
}
119+
return programNames.UnsortedList(), isTcxEBPF
120+
}

site/content/docs/contributing/developer-guide.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,37 @@ Waiting for daemon set "dranet" rollout to finish: 1 out of 5 new pods have been
5555
updated...
5656
```
5757

58+
## Accesing nodes
59+
60+
When developing it may be also useful to log into the nodes to be able to debug problems. Just obtain the list of nodes with `kubectl get nodes -o wide`
61+
62+
For Kind clusters use `docker exec -it <name of the node> bash`
63+
64+
```sh
65+
kubectl get nodes -o wide
66+
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
67+
gke-cluster-tpu-v6-default-pool-3f96de9d-hr87 Ready <none> 8d v1.33.1-gke.1107000 10.202.0.21 34.162.194.173 Container-Optimized OS from Google 6.6.87+ containerd://2.0.4
68+
gke-cluster-tpu-v6-default-pool-955df71d-zd7b Ready <none> 8d v1.33.1-gke.1107000 10.202.0.15 34.162.239.241 Container-Optimized OS from Google 6.6.87+ containerd://2.0.4
69+
gke-cluster-tpu-v6-default-pool-e1c69e67-k0jw Ready <none> 8d v1.33.1-gke.1107000 10.202.0.20 34.162.209.25 Container-Optimized OS from Google 6.6.87+ containerd://2.0.4
70+
gke-tpu-de8b9feb-kgdj Ready <none> 8d v1.33.1-gke.1107000 10.202.0.26 34.162.163.69 Container-Optimized OS from Google 6.6.87+ containerd://2.0.4
71+
gke-tpu-de8b9feb-prgf Ready <none> 8d v1.33.1-gke.1107000 10.202.0.24 34.162.144.5 Container-Optimized OS from Google 6.6.87+ containerd://2.0.4
72+
gke-tpu-de8b9feb-sjcp Ready <none> 8d v1.33.1-gke.1107000 10.202.0.27 34.162.117.62 Container-Optimized OS from Google 6.6.87+ containerd://2.0.4
73+
gke-tpu-de8b9feb-z8g1 Ready <none> 8d v1.33.1-gke.1107000 10.202.0.25 34.162.239.83 Container-Optimized OS from Google 6.6.87+ containerd://2.0.4
74+
```
75+
76+
For GKE or other clusters, you may have restrictions on ssh, so you can use
77+
78+
```sh
79+
kubectl debug -it node/gke-tpu-de8b9feb-kgdj --image busybox -- chroot /host
80+
--profile=legacy is deprecated and will be removed in the future. It is recommended to explicitly specify a profile, for example "--profile=general".
81+
Creating debugging pod node-debugger-gke-tpu-de8b9feb-kgdj-94xdx with container debugger on node gke-tpu-de8b9feb-kgdj.
82+
If you don't see a command prompt, try pressing enter.
83+
gke-tpu-de8b9feb-kgdj / #
84+
```
85+
86+
If you want to upload some binary, per example `bpftrace` or `pwru` , you can use the streaming capabilities for that:
87+
88+
```
5889
5990
6091
## Troubleshooting

tests/dummy_bpf.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <linux/bpf.h>
2+
#include <linux/pkt_cls.h>
3+
4+
__attribute__((section("classifier"), used))
5+
int handle_ingress(struct __sk_buff *skb) {
6+
return TC_ACT_OK;
7+
}
8+
9+
char __license[] __attribute__((section("license"), used)) = "GPL";

tests/dummy_bpf.o

720 Bytes
Binary file not shown.

tests/e2e.bats

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,31 @@
147147
kubectl delete -f "$BATS_TEST_DIRNAME"/../examples/resourceclaim_bigtcp.yaml
148148
kubectl delete -f "$BATS_TEST_DIRNAME"/../examples/deviceclass.yaml
149149
}
150+
151+
152+
# Test case for validating ebpf attributes are exposed via resource slice.
153+
# TODO use tcx hooks too
154+
@test "validate bpf filter attributes" {
155+
docker cp "$BATS_TEST_DIRNAME"/dummy_bpf.o "$CLUSTER_NAME"-worker2:/dummy_bpf.o
156+
docker exec "$CLUSTER_NAME"-worker2 bash -c "ip link add dummy5 type dummy"
157+
docker exec "$CLUSTER_NAME"-worker2 bash -c "ip link set up dev dummy5"
158+
docker exec "$CLUSTER_NAME"-worker2 bash -c "tc qdisc add dev dummy5 clsact"
159+
docker exec "$CLUSTER_NAME"-worker2 bash -c "tc filter add dev dummy5 ingress bpf direct-action obj dummy_bpf.o sec classifier"
160+
161+
run docker exec "$CLUSTER_NAME"-worker2 bash -c "tc filter show dev dummy5 ingress"
162+
[ "$status" -eq 0 ]
163+
[[ "$output" == *"dummy_bpf.o:[classifier] direct-action"* ]]
164+
165+
# Wait for the interface to be discovered
166+
sleep 5
167+
168+
# Validate bpf attribute is true
169+
run kubectl get resourceslices --field-selector spec.nodeName="$CLUSTER_NAME"-worker2 -o jsonpath='{.items[0].spec.devices[?(@.name=="dummy5")].attributes.dra\.net\/ebpf.bool}'
170+
[ "$status" -eq 0 ]
171+
[[ "$output" == "true" ]]
172+
173+
# Validate bpfName attribute
174+
run kubectl get resourceslices --field-selector spec.nodeName="$CLUSTER_NAME"-worker2 -o jsonpath='{.items[0].spec.devices[?(@.name=="dummy5")].attributes.dra\.net\/tcFilterNames.string}'
175+
[ "$status" -eq 0 ]
176+
[[ "$output" == "dummy_bpf.o:[classifier]" ]]
177+
}

0 commit comments

Comments
 (0)