-
-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
388 additions
and
273 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/cilium/ebpf/rlimit" | ||
"github.com/mozillazg/ptcpdump/bpf" | ||
"github.com/mozillazg/ptcpdump/internal/dev" | ||
) | ||
|
||
func attachHooks(opts Options) (map[int]dev.Device, *bpf.BPF, error) { | ||
devices, err := dev.GetDevices(opts.iface) | ||
if err != nil { | ||
return nil, nil, err | ||
} | ||
if err := rlimit.RemoveMemlock(); err != nil { | ||
return devices, nil, err | ||
} | ||
bf, err := bpf.NewBPF() | ||
if err != nil { | ||
return devices, nil, err | ||
} | ||
if err := bf.Load(bpf.NewOptions(opts.pid, opts.comm, opts.followForks, opts.pcapFilter)); err != nil { | ||
return devices, nil, err | ||
} | ||
|
||
if err := bf.AttachKprobes(); err != nil { | ||
return devices, bf, err | ||
} | ||
if err := bf.AttachTracepoints(); err != nil { | ||
return devices, bf, err | ||
} | ||
for _, iface := range devices { | ||
if err := bf.AttachTcHooks(iface.Ifindex); err != nil { | ||
return devices, bf, err | ||
} | ||
} | ||
|
||
return devices, bf, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package cmd | ||
|
||
import ( | ||
"fmt" | ||
"github.com/mozillazg/ptcpdump/internal/dev" | ||
"sort" | ||
"strings" | ||
) | ||
|
||
func listInterfaces() error { | ||
devices, err := dev.GetDevices("any") | ||
if err != nil { | ||
return err | ||
} | ||
outputs := []string{} | ||
for _, d := range devices { | ||
outputs = append(outputs, fmt.Sprintf("%d.%s", d.Ifindex, d.Name)) | ||
} | ||
sort.Strings(outputs) | ||
fmt.Printf("%s\n", strings.Join(outputs, "\n")) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package cmd | ||
|
||
import ( | ||
"errors" | ||
"github.com/cilium/ebpf" | ||
"log" | ||
) | ||
|
||
func logErr(err error) { | ||
var ve *ebpf.VerifierError | ||
if errors.As(err, &ve) { | ||
// Using %+v will print the whole verifier error, not just the last | ||
// few lines. | ||
log.Printf("Verifier error: %+v", ve) | ||
} | ||
log.Printf("%+v", err) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
package cmd | ||
|
||
type Options struct { | ||
iface string | ||
pid uint | ||
comm string | ||
followForks bool | ||
writeFilePath string | ||
pcapFilter string | ||
listInterfaces bool | ||
} | ||
|
||
func (o Options) WritePath() string { | ||
if o.writeFilePath == "" || o.writeFilePath == "-" { | ||
return "" | ||
} | ||
return o.writeFilePath | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
package cmd | ||
|
||
import ( | ||
"context" | ||
"github.com/mozillazg/ptcpdump/internal/consumer" | ||
"github.com/mozillazg/ptcpdump/internal/metadata" | ||
"github.com/spf13/cobra" | ||
"log" | ||
"os/signal" | ||
"syscall" | ||
) | ||
|
||
var opts = Options{} | ||
|
||
var rootCmd = &cobra.Command{ | ||
Use: "ptcpdump", | ||
Short: "XXX", | ||
Long: `XXX.`, | ||
Run: func(cmd *cobra.Command, args []string) { | ||
err := run(cmd, args) | ||
if err != nil { | ||
logErr(err) | ||
} | ||
}, | ||
} | ||
|
||
func init() { | ||
rootCmd.Flags().StringVarP(&opts.writeFilePath, "write-file", "w", "", | ||
"Write the raw packets to file rather than parsing and printing them out. e.g. ptcpdump.pcapng") | ||
rootCmd.Flags().StringVarP(&opts.iface, "interface", "i", "lo", "") | ||
rootCmd.Flags().UintVar(&opts.pid, "pid", 0, "") | ||
rootCmd.Flags().StringVar(&opts.comm, "pname", "", "") | ||
rootCmd.Flags().BoolVarP(&opts.followForks, "follow-forks", "f", false, | ||
"Include child processes when filter by process") | ||
rootCmd.Flags().BoolVar(&opts.listInterfaces, "list-interfaces", false, | ||
"Print the list of the network interfaces available on the system") | ||
} | ||
|
||
func Execute() error { | ||
return rootCmd.Execute() | ||
} | ||
|
||
func run(cmd *cobra.Command, args []string) error { | ||
if opts.listInterfaces { | ||
return listInterfaces() | ||
} | ||
|
||
pcache := metadata.NewProcessCache() | ||
writers, err := getWriters(opts, pcache) | ||
if err != nil { | ||
return err | ||
} | ||
go pcache.Start() | ||
|
||
devices, bf, err := attachHooks(opts) | ||
if err != nil { | ||
return err | ||
} | ||
defer bf.Close() | ||
|
||
packetEventReader, err := bf.NewPacketEventReader() | ||
if err != nil { | ||
return err | ||
} | ||
defer packetEventReader.Close() | ||
execEventReader, err := bf.NewExecEventReader() | ||
if err != nil { | ||
return err | ||
} | ||
defer execEventReader.Close() | ||
|
||
ctx, stop := signal.NotifyContext( | ||
context.Background(), syscall.SIGINT, syscall.SIGTERM, | ||
) | ||
defer stop() | ||
|
||
execConsumer := consumer.NewExecEventConsumer(pcache) | ||
go execConsumer.Start(ctx, execEventReader) | ||
packetConsumer := consumer.NewPacketEventConsumer(writers, devices) | ||
go packetConsumer.Start(ctx, packetEventReader) | ||
|
||
log.Println("tracing...") | ||
<-ctx.Done() | ||
log.Println("bye bye") | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package cmd | ||
|
||
import ( | ||
"github.com/gopacket/gopacket/layers" | ||
"github.com/gopacket/gopacket/pcapgo" | ||
"github.com/mozillazg/ptcpdump/internal/dev" | ||
"github.com/mozillazg/ptcpdump/internal/metadata" | ||
"github.com/mozillazg/ptcpdump/internal/writer" | ||
"golang.org/x/xerrors" | ||
"io" | ||
"math" | ||
"os" | ||
) | ||
|
||
func getWriters(opts Options, pcache *metadata.ProcessCache) ([]writer.PacketWriter, error) { | ||
var writers []writer.PacketWriter | ||
|
||
if opts.WritePath() != "" { | ||
pcapFile, err := os.Create(opts.WritePath()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
pcapWriter, err := newPcapWriter(pcapFile, pcache) | ||
if err != nil { | ||
return nil, err | ||
} | ||
writers = append(writers, pcapWriter) | ||
} else { | ||
stdoutWriter := writer.NewStdoutWriter(os.Stdout, pcache) | ||
writers = append(writers, stdoutWriter) | ||
} | ||
|
||
return writers, nil | ||
} | ||
|
||
func newPcapWriter(w io.Writer, pcache *metadata.ProcessCache) (*writer.PcapNGWriter, error) { | ||
devices, err := dev.GetDevices("any") | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var interfaces []pcapgo.NgInterface | ||
for _, dev := range devices { | ||
interfaces = append(interfaces, pcapgo.NgInterface{ | ||
Name: dev.Name, | ||
Comment: "ptcpdump interface name", | ||
LinkType: layers.LinkTypeEthernet, | ||
SnapLength: uint32(math.MaxUint16), | ||
}) | ||
} | ||
|
||
pcapWriter, err := pcapgo.NewNgWriterInterface(w, interfaces[0], pcapgo.NgWriterOptions{}) | ||
if err != nil { | ||
return nil, xerrors.Errorf(": %w", err) | ||
} | ||
for _, ifc := range interfaces[1:] { | ||
_, err := pcapWriter.AddInterface(ifc) | ||
if err != nil { | ||
return nil, xerrors.Errorf(": %w", err) | ||
} | ||
} | ||
|
||
if err := pcapWriter.Flush(); err != nil { | ||
return nil, xerrors.Errorf("writing pcap header: %w", err) | ||
} | ||
|
||
return writer.NewPcapNGWriter(pcapWriter, pcache), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package consumer | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"github.com/cilium/ebpf/perf" | ||
"github.com/cilium/ebpf/ringbuf" | ||
"github.com/mozillazg/ptcpdump/internal/event" | ||
"github.com/mozillazg/ptcpdump/internal/metadata" | ||
"log" | ||
) | ||
|
||
type ExecEventConsumer struct { | ||
pcache *metadata.ProcessCache | ||
} | ||
|
||
func NewExecEventConsumer(pcache *metadata.ProcessCache) *ExecEventConsumer { | ||
return &ExecEventConsumer{ | ||
pcache: pcache, | ||
} | ||
} | ||
|
||
func (c *ExecEventConsumer) Start(ctx context.Context, reader *ringbuf.Reader) { | ||
for { | ||
select { | ||
case <-ctx.Done(): | ||
return | ||
default: | ||
} | ||
|
||
record, err := reader.Read() | ||
if err != nil { | ||
if errors.Is(err, perf.ErrClosed) { | ||
log.Println("[ExecEventConsumer] Received signal, exiting...") | ||
return | ||
} | ||
log.Printf("[ExecEventConsumer] read event failed: %s", err) | ||
continue | ||
} | ||
c.parseExecEvent(record.RawSample) | ||
} | ||
} | ||
|
||
func (c *ExecEventConsumer) parseExecEvent(rawSample []byte) { | ||
e, err := event.ParseProcessExecEvent(rawSample) | ||
if err != nil { | ||
log.Printf("[ExecEventConsumer] parse event failed: %s", err) | ||
return | ||
} | ||
c.pcache.AddItem(*e) | ||
} | ||
|
||
func (c *ExecEventConsumer) Stop() { | ||
|
||
} |
Oops, something went wrong.