inspektor-gadget
129 строк · 3.7 Кб
1// Copyright 2023 The Inspektor Gadget authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package containerhook
16
17import (
18"errors"
19"fmt"
20"os"
21"syscall"
22
23"github.com/cilium/ebpf"
24"github.com/cilium/ebpf/link"
25"golang.org/x/sys/unix"
26
27"github.com/inspektor-gadget/inspektor-gadget/pkg/btfgen"
28"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"
29)
30
31//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET -cc clang -cflags ${CFLAGS} -no-global-types privatedata ./bpf/privatedata.bpf.c -- -I./bpf/
32
33// readPrivateDataFromFd use ebpf to read the private_data pointer from the
34// kernel "struct file" associated with the given fd.
35//
36// It can then be used in other ebpf programs.
37//
38// This is done:
39// - without using bpf iterators in order to work on old kernels.
40// - without comparing pids from userspace and ebpf in order to work from
41// different pid namespaces.
42func readPrivateDataFromFd(fd int) (uint64, error) {
43var objs privatedataObjects
44var links []link.Link
45var err error
46sock := [2]int{-1, -1}
47
48defer func() {
49for i := 0; i < 2; i++ {
50if sock[i] != -1 {
51unix.Close(sock[i])
52}
53}
54for _, l := range links {
55gadgets.CloseLink(l)
56}
57objs.Close()
58}()
59
60// Create a socket pair
61sock, err = unix.Socketpair(unix.AF_UNIX, unix.SOCK_DGRAM, 0)
62if err != nil {
63return 0, fmt.Errorf("creating socket pair: %w", err)
64}
65
66// Find the inode of the socket
67fdFileInfo, err := os.Stat(fmt.Sprintf("/proc/self/fd/%d", sock[0]))
68if err != nil {
69return 0, fmt.Errorf("reading file info from fd %d: %w", fd, err)
70}
71fdStat, ok := fdFileInfo.Sys().(*syscall.Stat_t)
72if !ok {
73return 0, errors.New("not a syscall.Stat_t")
74}
75fdIno := fdStat.Ino
76
77// Load ebpf program configured with the socket inode
78spec, err := loadPrivatedata()
79if err != nil {
80return 0, fmt.Errorf("load ebpf program for container-hook: %w", err)
81}
82consts := map[string]interface{}{
83"socket_ino": uint64(fdIno),
84}
85if err := spec.RewriteConstants(consts); err != nil {
86return 0, fmt.Errorf("RewriteConstants: %w", err)
87}
88
89opts := ebpf.CollectionOptions{
90Programs: ebpf.ProgramOptions{
91KernelTypes: btfgen.GetBTFSpec(),
92},
93}
94if err := spec.LoadAndAssign(&objs, &opts); err != nil {
95return 0, fmt.Errorf("loading maps and programs: %w", err)
96}
97
98// Attach ebpf programs
99l, err := link.Kprobe("__scm_send", objs.IgScmSndE, nil)
100if err != nil {
101return 0, fmt.Errorf("attaching kprobe __scm_send: %w", err)
102}
103links = append(links, l)
104
105l, err = link.Kretprobe("fget_raw", objs.IgFgetX, nil)
106if err != nil {
107return 0, fmt.Errorf("attaching kretprobe fget_raw: %w", err)
108}
109links = append(links, l)
110
111// Send the fd through the socket with SCM_RIGHTS.
112// This will trigger the __scm_send kprobe and fget_raw kretprobe
113buf := make([]byte, 1)
114err = unix.Sendmsg(sock[0], buf, unix.UnixRights(fd), nil, 0)
115if err != nil {
116return 0, fmt.Errorf("sending fd: %w", err)
117}
118
119// Read private_data from objs
120privateData := uint64(0)
121err = objs.IgPrivateData.Lookup(uint32(0), &privateData)
122if err != nil {
123return 0, fmt.Errorf("reading private_data: %w", err)
124}
125if privateData == 0 {
126return 0, fmt.Errorf("private_data is 0")
127}
128return privateData, nil
129}
130