inspektor-gadget
247 строк · 6.5 Кб
1// Copyright 2022 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 socketenricher16
17import (18"fmt"19"sync"20"time"21
22"github.com/cilium/ebpf"23"github.com/cilium/ebpf/link"24log "github.com/sirupsen/logrus"25
26"github.com/inspektor-gadget/inspektor-gadget/pkg/btfgen"27"github.com/inspektor-gadget/inspektor-gadget/pkg/gadgets"28"github.com/inspektor-gadget/inspektor-gadget/pkg/kallsyms"29bpfiterns "github.com/inspektor-gadget/inspektor-gadget/pkg/utils/bpf-iter-ns"30)
31
32//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET -cc clang -cflags ${CFLAGS} socketenricher ./bpf/socket-enricher.bpf.c -- -I./bpf/
33
34//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -target $TARGET -cc clang -cflags ${CFLAGS} socketsiter ./bpf/sockets-iter.bpf.c -- -I./bpf/
35
36const (37SocketsMapName = "gadget_sockets"38)
39
40// SocketEnricher creates a map exposing processes owning each socket.
41//
42// This makes it possible for network gadgets to access that information and
43// display it directly from the BPF code. Example of such code in the dns and
44// sni gadgets.
45type SocketEnricher struct {46objs socketenricherObjects
47objsIter socketsiterObjects
48links []link.Link49
50closeOnce sync.Once51done chan bool52}
53
54func (se *SocketEnricher) SocketsMap() *ebpf.Map {55return se.objs.GadgetSockets56}
57
58func NewSocketEnricher() (*SocketEnricher, error) {59se := &SocketEnricher{}60
61if err := se.start(); err != nil {62se.Close()63return nil, err64}65
66return se, nil67}
68
69func (se *SocketEnricher) start() error {70specIter, err := loadSocketsiter()71if err != nil {72return fmt.Errorf("loading socketsiter asset: %w", err)73}74
75err = kallsyms.SpecUpdateAddresses(specIter, []string{"socket_file_ops"})76if err != nil {77// Being unable to access to /proc/kallsyms can be caused by not having78// CAP_SYSLOG.79log.Warnf("updating socket_file_ops address with ksyms: %v\nEither you cannot access /proc/kallsyms or this file does not contain socket_file_ops", err)80}81
82opts := ebpf.CollectionOptions{83Programs: ebpf.ProgramOptions{84KernelTypes: btfgen.GetBTFSpec(),85},86}87
88disableBPFIterators := false89if err := specIter.LoadAndAssign(&se.objsIter, nil); err != nil {90disableBPFIterators = true91log.Warnf("Socket enricher: skip loading iterators: %v", err)92}93
94spec, err := loadSocketenricher()95if err != nil {96return fmt.Errorf("loading socket enricher asset: %w", err)97}98
99if disableBPFIterators {100spec.RewriteConstants(map[string]interface{}{101"disable_bpf_iterators": true,102})103} else {104opts.MapReplacements = map[string]*ebpf.Map{105SocketsMapName: se.objsIter.GadgetSockets,106}107}108
109if err := spec.LoadAndAssign(&se.objs, &opts); err != nil {110return fmt.Errorf("loading ebpf program: %w", err)111}112
113var l link.Link114
115// bind116l, err = link.Kprobe("inet_bind", se.objs.IgBindIpv4E, nil)117if err != nil {118return fmt.Errorf("attaching ipv4 kprobe: %w", err)119}120se.links = append(se.links, l)121
122l, err = link.Kretprobe("inet_bind", se.objs.IgBindIpv4X, nil)123if err != nil {124return fmt.Errorf("attaching ipv4 kretprobe: %w", err)125}126se.links = append(se.links, l)127
128l, err = link.Kprobe("inet6_bind", se.objs.IgBindIpv6E, nil)129if err != nil {130return fmt.Errorf("attaching ipv6 kprobe: %w", err)131}132se.links = append(se.links, l)133
134l, err = link.Kretprobe("inet6_bind", se.objs.IgBindIpv6X, nil)135if err != nil {136return fmt.Errorf("attaching ipv6 kretprobe: %w", err)137}138se.links = append(se.links, l)139
140// connect141l, err = link.Kprobe("tcp_connect", se.objs.IgTcpCoE, nil)142if err != nil {143return fmt.Errorf("attaching connect kprobe: %w", err)144}145se.links = append(se.links, l)146
147l, err = link.Kretprobe("tcp_connect", se.objs.IgTcpCoX, nil)148if err != nil {149return fmt.Errorf("attaching connect kretprobe: %w", err)150}151se.links = append(se.links, l)152
153// udp_sendmsg154l, err = link.Kprobe("udp_sendmsg", se.objs.IgUdpSendmsg, nil)155if err != nil {156return fmt.Errorf("attaching udp_sendmsg ipv4 kprobe: %w", err)157}158se.links = append(se.links, l)159
160l, err = link.Kprobe("udpv6_sendmsg", se.objs.IgUdp6Sendmsg, nil)161if err != nil {162return fmt.Errorf("attaching udpv6_sendmsg ipv6 kprobe: %w", err)163}164se.links = append(se.links, l)165
166// release167l, err = link.Kprobe("inet_release", se.objs.IgFreeIpv4E, nil)168if err != nil {169return fmt.Errorf("attaching ipv4 release kprobe: %w", err)170}171se.links = append(se.links, l)172
173l, err = link.Kprobe("inet6_release", se.objs.IgFreeIpv6E, nil)174if err != nil {175return fmt.Errorf("attaching ipv6 release kprobe: %w", err)176}177se.links = append(se.links, l)178
179if !disableBPFIterators {180// get initial sockets181socketsIter, err := link.AttachIter(link.IterOptions{182Program: se.objsIter.IgSocketsIt,183})184if err != nil {185return fmt.Errorf("attach BPF iterator: %w", err)186}187defer socketsIter.Close()188
189_, err = bpfiterns.Read(socketsIter)190if err != nil {191return fmt.Errorf("read BPF iterator: %w", err)192}193
194// Schedule socket cleanup195cleanupIter, err := link.AttachIter(link.IterOptions{196Program: se.objsIter.IgSkCleanup,197Map: se.objsIter.GadgetSockets,198})199if err != nil {200return fmt.Errorf("attach BPF iterator for cleanups: %w", err)201}202se.links = append(se.links, cleanupIter)203
204se.done = make(chan bool)205go se.cleanupDeletedSockets(cleanupIter)206}207
208return nil209}
210
211func (se *SocketEnricher) cleanupDeletedSockets(cleanupIter *link.Iter) {212ticker := time.NewTicker(2 * time.Second)213defer ticker.Stop()214for {215select {216case <-se.done:217return218case <-ticker.C:219err := se.cleanupDeletedSocketsNow(cleanupIter)220if err != nil {221fmt.Printf("socket enricher: %v\n", err)222}223}224}225}
226
227func (se *SocketEnricher) cleanupDeletedSocketsNow(cleanupIter *link.Iter) error {228// No need to change pidns for this iterator because cleanupIter is an229// iterator on a map, not on tasks.230_, err := bpfiterns.ReadOnCurrentPidNs(cleanupIter)231return err232}
233
234func (se *SocketEnricher) Close() {235se.closeOnce.Do(func() {236if se.done != nil {237close(se.done)238}239})240
241for _, l := range se.links {242gadgets.CloseLink(l)243}244se.links = nil245se.objs.Close()246se.objsIter.Close()247}
248