Merge pull request #1234 from safing/fix/process-detection
Improve process detection
This commit is contained in:
@@ -7,6 +7,7 @@ linters:
|
||||
- containedctx
|
||||
- contextcheck
|
||||
- cyclop
|
||||
- depguard
|
||||
- exhaustivestruct
|
||||
- exhaustruct
|
||||
- forbidigo
|
||||
|
||||
@@ -104,6 +104,7 @@ func apiAuthenticator(r *http.Request, s *http.Server) (token *api.AuthToken, er
|
||||
SrcPort: remotePort, // source as in the process we are looking for
|
||||
Dst: localIP,
|
||||
DstPort: localPort,
|
||||
PID: process.UndefinedProcessID,
|
||||
},
|
||||
)
|
||||
if !retry {
|
||||
@@ -141,7 +142,11 @@ func authenticateAPIRequest(ctx context.Context, pktInfo *packet.Info) (retry bo
|
||||
authenticatedPath += string(filepath.Separator)
|
||||
|
||||
// Get process of request.
|
||||
proc, _, err := process.GetProcessByConnection(ctx, pktInfo)
|
||||
pid, _, _ := process.GetPidOfConnection(ctx, pktInfo)
|
||||
if pid < 0 {
|
||||
return false, fmt.Errorf(deniedMsgUnidentified, api.ErrAPIAccessDeniedMessage) //nolint:stylecheck // message for user
|
||||
}
|
||||
proc, err := process.GetOrFindProcess(ctx, pid)
|
||||
if err != nil {
|
||||
log.Tracer(ctx).Debugf("filter: failed to get process of api request: %s", err)
|
||||
originalPid = process.UnidentifiedProcessID
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portbase/modules/subsystems"
|
||||
_ "github.com/safing/portmaster/core"
|
||||
)
|
||||
|
||||
var filterModule *modules.Module
|
||||
|
||||
func init() {
|
||||
filterModule = modules.Register("filter", nil, nil, nil, "core", "interception", "intel")
|
||||
subsystems.Register(
|
||||
"filter",
|
||||
"Privacy Filter",
|
||||
"DNS and Network Filter",
|
||||
filterModule,
|
||||
"config:filter/",
|
||||
&config.Option{
|
||||
Name: "Privacy Filter Module",
|
||||
Key: CfgOptionEnableFilterKey,
|
||||
Description: "Start the Privacy Filter module. If turned off, all privacy filter protections are fully disabled on this device.",
|
||||
OptType: config.OptTypeBool,
|
||||
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
||||
ReleaseLevel: config.ReleaseLevelStable,
|
||||
DefaultValue: true,
|
||||
Annotations: config.Annotations{
|
||||
config.CategoryAnnotation: "General",
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
148
firewall/interception/ebpf/bandwidth/bpf_bpfeb.go
Normal file
148
firewall/interception/ebpf/bandwidth/bpf_bpfeb.go
Normal file
@@ -0,0 +1,148 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||
// +build arm64be armbe mips mips64 mips64p32 ppc64 s390 s390x sparc sparc64
|
||||
|
||||
package ebpf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfSkInfo struct {
|
||||
Rx uint64
|
||||
Tx uint64
|
||||
Reported uint64
|
||||
}
|
||||
|
||||
type bpfSkKey struct {
|
||||
SrcIp [4]uint32
|
||||
DstIp [4]uint32
|
||||
SrcPort uint16
|
||||
DstPort uint16
|
||||
Protocol uint8
|
||||
Ipv6 uint8
|
||||
_ [2]byte
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
SocketOperations *ebpf.ProgramSpec `ebpf:"socket_operations"`
|
||||
UdpRecvmsg *ebpf.ProgramSpec `ebpf:"udp_recvmsg"`
|
||||
UdpSendmsg *ebpf.ProgramSpec `ebpf:"udp_sendmsg"`
|
||||
Udpv6Recvmsg *ebpf.ProgramSpec `ebpf:"udpv6_recvmsg"`
|
||||
Udpv6Sendmsg *ebpf.ProgramSpec `ebpf:"udpv6_sendmsg"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
PmBandwidthMap *ebpf.MapSpec `ebpf:"pm_bandwidth_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
PmBandwidthMap *ebpf.Map `ebpf:"pm_bandwidth_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.PmBandwidthMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
SocketOperations *ebpf.Program `ebpf:"socket_operations"`
|
||||
UdpRecvmsg *ebpf.Program `ebpf:"udp_recvmsg"`
|
||||
UdpSendmsg *ebpf.Program `ebpf:"udp_sendmsg"`
|
||||
Udpv6Recvmsg *ebpf.Program `ebpf:"udpv6_recvmsg"`
|
||||
Udpv6Sendmsg *ebpf.Program `ebpf:"udpv6_sendmsg"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.SocketOperations,
|
||||
p.UdpRecvmsg,
|
||||
p.UdpSendmsg,
|
||||
p.Udpv6Recvmsg,
|
||||
p.Udpv6Sendmsg,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfeb.o
|
||||
var _BpfBytes []byte
|
||||
BIN
firewall/interception/ebpf/bandwidth/bpf_bpfeb.o
Normal file
BIN
firewall/interception/ebpf/bandwidth/bpf_bpfeb.o
Normal file
Binary file not shown.
148
firewall/interception/ebpf/bandwidth/bpf_bpfel.go
Normal file
148
firewall/interception/ebpf/bandwidth/bpf_bpfel.go
Normal file
@@ -0,0 +1,148 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
||||
// +build 386 amd64 amd64p32 arm arm64 mips64le mips64p32le mipsle ppc64le riscv64
|
||||
|
||||
package ebpf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfSkInfo struct {
|
||||
Rx uint64
|
||||
Tx uint64
|
||||
Reported uint64
|
||||
}
|
||||
|
||||
type bpfSkKey struct {
|
||||
SrcIp [4]uint32
|
||||
DstIp [4]uint32
|
||||
SrcPort uint16
|
||||
DstPort uint16
|
||||
Protocol uint8
|
||||
Ipv6 uint8
|
||||
_ [2]byte
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
SocketOperations *ebpf.ProgramSpec `ebpf:"socket_operations"`
|
||||
UdpRecvmsg *ebpf.ProgramSpec `ebpf:"udp_recvmsg"`
|
||||
UdpSendmsg *ebpf.ProgramSpec `ebpf:"udp_sendmsg"`
|
||||
Udpv6Recvmsg *ebpf.ProgramSpec `ebpf:"udpv6_recvmsg"`
|
||||
Udpv6Sendmsg *ebpf.ProgramSpec `ebpf:"udpv6_sendmsg"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
PmBandwidthMap *ebpf.MapSpec `ebpf:"pm_bandwidth_map"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
PmBandwidthMap *ebpf.Map `ebpf:"pm_bandwidth_map"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.PmBandwidthMap,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
SocketOperations *ebpf.Program `ebpf:"socket_operations"`
|
||||
UdpRecvmsg *ebpf.Program `ebpf:"udp_recvmsg"`
|
||||
UdpSendmsg *ebpf.Program `ebpf:"udp_sendmsg"`
|
||||
Udpv6Recvmsg *ebpf.Program `ebpf:"udpv6_recvmsg"`
|
||||
Udpv6Sendmsg *ebpf.Program `ebpf:"udpv6_sendmsg"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.SocketOperations,
|
||||
p.UdpRecvmsg,
|
||||
p.UdpSendmsg,
|
||||
p.Udpv6Recvmsg,
|
||||
p.Udpv6Sendmsg,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfel.o
|
||||
var _BpfBytes []byte
|
||||
BIN
firewall/interception/ebpf/bandwidth/bpf_bpfel.o
Normal file
BIN
firewall/interception/ebpf/bandwidth/bpf_bpfel.o
Normal file
Binary file not shown.
178
firewall/interception/ebpf/bandwidth/interface.go
Normal file
178
firewall/interception/ebpf/bandwidth/interface.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package ebpf
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
"golang.org/x/sys/unix"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/network/packet"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" bpf ../programs/bandwidth.c
|
||||
|
||||
var ebpfLoadingFailed atomic.Uint32
|
||||
|
||||
// BandwidthStatsWorker monitors connection bandwidth using ebpf.
|
||||
func BandwidthStatsWorker(ctx context.Context, collectInterval time.Duration, bandwidthUpdates chan *packet.BandwidthUpdate) error {
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
err := rlimit.RemoveMemlock()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ebpf: failed to remove memlock: %w", err)
|
||||
}
|
||||
|
||||
// Load pre-compiled programs and maps into the kernel.
|
||||
objs := bpfObjects{}
|
||||
if err := loadBpfObjects(&objs, nil); err != nil {
|
||||
if ebpfLoadingFailed.Add(1) >= 5 {
|
||||
log.Warningf("ebpf: failed to load ebpf object 5 times, giving up with error %s", err)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("ebpf: failed to load ebpf object: %w", err)
|
||||
}
|
||||
defer objs.Close() //nolint:errcheck
|
||||
|
||||
// Find the cgroup path
|
||||
path, err := findCgroupPath()
|
||||
if err != nil {
|
||||
return fmt.Errorf("ebpf: failed to find cgroup paths: %w", err)
|
||||
}
|
||||
|
||||
// Attach socket options for monitoring connections
|
||||
sockOptionsLink, err := link.AttachCgroup(link.CgroupOptions{
|
||||
Path: path,
|
||||
Program: objs.bpfPrograms.SocketOperations,
|
||||
Attach: ebpf.AttachCGroupSockOps,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("ebpf: failed to open module sockops: %w", err)
|
||||
}
|
||||
defer sockOptionsLink.Close() //nolint:errcheck
|
||||
|
||||
// Attach Udp Ipv4 recive message tracing
|
||||
udpv4RMLink, err := link.AttachTracing(link.TracingOptions{
|
||||
Program: objs.bpfPrograms.UdpRecvmsg,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("ebpf: failed to open trace Udp IPv4 recvmsg: %w", err)
|
||||
}
|
||||
defer udpv4RMLink.Close() //nolint:errcheck
|
||||
|
||||
// Attach UDP IPv4 send message tracing
|
||||
udpv4SMLink, err := link.AttachTracing(link.TracingOptions{
|
||||
Program: objs.bpfPrograms.UdpSendmsg,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("ebpf: failed to open trace Udp IPv4 sendmsg: %w", err)
|
||||
}
|
||||
defer udpv4SMLink.Close() //nolint:errcheck
|
||||
|
||||
// Attach UDP IPv6 receive message tracing
|
||||
udpv6RMLink, err := link.AttachTracing(link.TracingOptions{
|
||||
Program: objs.bpfPrograms.Udpv6Recvmsg,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("ebpf: failed to open trace Udp IPv6 recvmsg: %w", err)
|
||||
}
|
||||
defer udpv6RMLink.Close() //nolint:errcheck
|
||||
|
||||
// Attach UDP IPv6 send message tracing
|
||||
udpv6SMLink, err := link.AttachTracing(link.TracingOptions{
|
||||
Program: objs.bpfPrograms.Udpv6Sendmsg,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("ebpf: failed to open trace Udp IPv6 sendmsg: %w", err)
|
||||
}
|
||||
defer udpv6SMLink.Close() //nolint:errcheck
|
||||
|
||||
// Setup ticker.
|
||||
ticker := time.NewTicker(collectInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
// Collect bandwidth at every tick.
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
reportBandwidth(ctx, objs, bandwidthUpdates)
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reportBandwidth reports the bandwidth to the given updates channel.
|
||||
func reportBandwidth(ctx context.Context, objs bpfObjects, bandwidthUpdates chan *packet.BandwidthUpdate) {
|
||||
iter := objs.bpfMaps.PmBandwidthMap.Iterate()
|
||||
var skKey bpfSkKey
|
||||
var skInfo bpfSkInfo
|
||||
for iter.Next(&skKey, &skInfo) {
|
||||
// Check if already reported.
|
||||
if skInfo.Reported >= 1 {
|
||||
continue
|
||||
}
|
||||
// Mark as reported and update the map.
|
||||
skInfo.Reported = 1
|
||||
if err := objs.bpfMaps.PmBandwidthMap.Put(&skKey, &skInfo); err != nil {
|
||||
log.Debugf("ebpf: failed to update map: %s", err)
|
||||
}
|
||||
|
||||
connID := packet.CreateConnectionID(
|
||||
packet.IPProtocol(skKey.Protocol),
|
||||
convertArrayToIP(skKey.SrcIp, skKey.Ipv6 == 1), skKey.SrcPort,
|
||||
convertArrayToIP(skKey.DstIp, skKey.Ipv6 == 1), skKey.DstPort,
|
||||
false,
|
||||
)
|
||||
update := &packet.BandwidthUpdate{
|
||||
ConnID: connID,
|
||||
RecvBytes: skInfo.Rx,
|
||||
SentBytes: skInfo.Tx,
|
||||
Method: packet.Absolute,
|
||||
}
|
||||
select {
|
||||
case bandwidthUpdates <- update:
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// findCgroupPath returns the default unified path of the cgroup.
|
||||
func findCgroupPath() (string, error) {
|
||||
cgroupPath := "/sys/fs/cgroup"
|
||||
|
||||
var st syscall.Statfs_t
|
||||
err := syscall.Statfs(cgroupPath, &st)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
isCgroupV2Enabled := st.Type == unix.CGROUP2_SUPER_MAGIC
|
||||
if !isCgroupV2Enabled {
|
||||
cgroupPath = filepath.Join(cgroupPath, "unified")
|
||||
}
|
||||
return cgroupPath, nil
|
||||
}
|
||||
|
||||
// convertArrayToIP converts an array of uint32 values to a net.IP address.
|
||||
func convertArrayToIP(input [4]uint32, ipv6 bool) net.IP {
|
||||
if !ipv6 {
|
||||
addressBuf := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(addressBuf, input[0])
|
||||
return net.IP(addressBuf)
|
||||
}
|
||||
|
||||
addressBuf := make([]byte, 16)
|
||||
for i := 0; i < 4; i++ {
|
||||
binary.LittleEndian.PutUint32(addressBuf[i*4:i*4+4], input[i])
|
||||
}
|
||||
return net.IP(addressBuf)
|
||||
}
|
||||
138
firewall/interception/ebpf/connection_listener/bpf_bpfeb.go
Normal file
138
firewall/interception/ebpf/connection_listener/bpf_bpfeb.go
Normal file
@@ -0,0 +1,138 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build arm64be || armbe || mips || mips64 || mips64p32 || ppc64 || s390 || s390x || sparc || sparc64
|
||||
// +build arm64be armbe mips mips64 mips64p32 ppc64 s390 s390x sparc sparc64
|
||||
|
||||
package ebpf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfEvent struct {
|
||||
Saddr [4]uint32
|
||||
Daddr [4]uint32
|
||||
Sport uint16
|
||||
Dport uint16
|
||||
Pid uint32
|
||||
IpVersion uint8
|
||||
Protocol uint8
|
||||
Direction uint8
|
||||
_ [1]byte
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
TcpConnect *ebpf.ProgramSpec `ebpf:"tcp_connect"`
|
||||
UdpV4Connect *ebpf.ProgramSpec `ebpf:"udp_v4_connect"`
|
||||
UdpV6Connect *ebpf.ProgramSpec `ebpf:"udp_v6_connect"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
PmConnectionEvents *ebpf.MapSpec `ebpf:"pm_connection_events"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
PmConnectionEvents *ebpf.Map `ebpf:"pm_connection_events"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.PmConnectionEvents,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
TcpConnect *ebpf.Program `ebpf:"tcp_connect"`
|
||||
UdpV4Connect *ebpf.Program `ebpf:"udp_v4_connect"`
|
||||
UdpV6Connect *ebpf.Program `ebpf:"udp_v6_connect"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.TcpConnect,
|
||||
p.UdpV4Connect,
|
||||
p.UdpV6Connect,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfeb.o
|
||||
var _BpfBytes []byte
|
||||
BIN
firewall/interception/ebpf/connection_listener/bpf_bpfeb.o
Normal file
BIN
firewall/interception/ebpf/connection_listener/bpf_bpfeb.o
Normal file
Binary file not shown.
138
firewall/interception/ebpf/connection_listener/bpf_bpfel.go
Normal file
138
firewall/interception/ebpf/connection_listener/bpf_bpfel.go
Normal file
@@ -0,0 +1,138 @@
|
||||
// Code generated by bpf2go; DO NOT EDIT.
|
||||
//go:build 386 || amd64 || amd64p32 || arm || arm64 || mips64le || mips64p32le || mipsle || ppc64le || riscv64
|
||||
// +build 386 amd64 amd64p32 arm arm64 mips64le mips64p32le mipsle ppc64le riscv64
|
||||
|
||||
package ebpf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/cilium/ebpf"
|
||||
)
|
||||
|
||||
type bpfEvent struct {
|
||||
Saddr [4]uint32
|
||||
Daddr [4]uint32
|
||||
Sport uint16
|
||||
Dport uint16
|
||||
Pid uint32
|
||||
IpVersion uint8
|
||||
Protocol uint8
|
||||
Direction uint8
|
||||
_ [1]byte
|
||||
}
|
||||
|
||||
// loadBpf returns the embedded CollectionSpec for bpf.
|
||||
func loadBpf() (*ebpf.CollectionSpec, error) {
|
||||
reader := bytes.NewReader(_BpfBytes)
|
||||
spec, err := ebpf.LoadCollectionSpecFromReader(reader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can't load bpf: %w", err)
|
||||
}
|
||||
|
||||
return spec, err
|
||||
}
|
||||
|
||||
// loadBpfObjects loads bpf and converts it into a struct.
|
||||
//
|
||||
// The following types are suitable as obj argument:
|
||||
//
|
||||
// *bpfObjects
|
||||
// *bpfPrograms
|
||||
// *bpfMaps
|
||||
//
|
||||
// See ebpf.CollectionSpec.LoadAndAssign documentation for details.
|
||||
func loadBpfObjects(obj interface{}, opts *ebpf.CollectionOptions) error {
|
||||
spec, err := loadBpf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return spec.LoadAndAssign(obj, opts)
|
||||
}
|
||||
|
||||
// bpfSpecs contains maps and programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfSpecs struct {
|
||||
bpfProgramSpecs
|
||||
bpfMapSpecs
|
||||
}
|
||||
|
||||
// bpfSpecs contains programs before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfProgramSpecs struct {
|
||||
TcpConnect *ebpf.ProgramSpec `ebpf:"tcp_connect"`
|
||||
UdpV4Connect *ebpf.ProgramSpec `ebpf:"udp_v4_connect"`
|
||||
UdpV6Connect *ebpf.ProgramSpec `ebpf:"udp_v6_connect"`
|
||||
}
|
||||
|
||||
// bpfMapSpecs contains maps before they are loaded into the kernel.
|
||||
//
|
||||
// It can be passed ebpf.CollectionSpec.Assign.
|
||||
type bpfMapSpecs struct {
|
||||
PmConnectionEvents *ebpf.MapSpec `ebpf:"pm_connection_events"`
|
||||
}
|
||||
|
||||
// bpfObjects contains all objects after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfObjects struct {
|
||||
bpfPrograms
|
||||
bpfMaps
|
||||
}
|
||||
|
||||
func (o *bpfObjects) Close() error {
|
||||
return _BpfClose(
|
||||
&o.bpfPrograms,
|
||||
&o.bpfMaps,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfMaps contains all maps after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfMaps struct {
|
||||
PmConnectionEvents *ebpf.Map `ebpf:"pm_connection_events"`
|
||||
}
|
||||
|
||||
func (m *bpfMaps) Close() error {
|
||||
return _BpfClose(
|
||||
m.PmConnectionEvents,
|
||||
)
|
||||
}
|
||||
|
||||
// bpfPrograms contains all programs after they have been loaded into the kernel.
|
||||
//
|
||||
// It can be passed to loadBpfObjects or ebpf.CollectionSpec.LoadAndAssign.
|
||||
type bpfPrograms struct {
|
||||
TcpConnect *ebpf.Program `ebpf:"tcp_connect"`
|
||||
UdpV4Connect *ebpf.Program `ebpf:"udp_v4_connect"`
|
||||
UdpV6Connect *ebpf.Program `ebpf:"udp_v6_connect"`
|
||||
}
|
||||
|
||||
func (p *bpfPrograms) Close() error {
|
||||
return _BpfClose(
|
||||
p.TcpConnect,
|
||||
p.UdpV4Connect,
|
||||
p.UdpV6Connect,
|
||||
)
|
||||
}
|
||||
|
||||
func _BpfClose(closers ...io.Closer) error {
|
||||
for _, closer := range closers {
|
||||
if err := closer.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Do not access this directly.
|
||||
//
|
||||
//go:embed bpf_bpfel.o
|
||||
var _BpfBytes []byte
|
||||
BIN
firewall/interception/ebpf/connection_listener/bpf_bpfel.o
Normal file
BIN
firewall/interception/ebpf/connection_listener/bpf_bpfel.o
Normal file
Binary file not shown.
169
firewall/interception/ebpf/connection_listener/worker.go
Normal file
169
firewall/interception/ebpf/connection_listener/worker.go
Normal file
@@ -0,0 +1,169 @@
|
||||
package ebpf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/cilium/ebpf/link"
|
||||
"github.com/cilium/ebpf/ringbuf"
|
||||
"github.com/cilium/ebpf/rlimit"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/network/packet"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/cilium/ebpf/cmd/bpf2go -cc clang -cflags "-O2 -g -Wall -Werror" -type Event bpf ../programs/monitor.c
|
||||
|
||||
var ebpfLoadingFailed atomic.Uint32
|
||||
|
||||
// ConnectionListenerWorker listens to new connections using ebpf.
|
||||
func ConnectionListenerWorker(ctx context.Context, packets chan packet.Packet) error {
|
||||
// Allow the current process to lock memory for eBPF resources.
|
||||
if err := rlimit.RemoveMemlock(); err != nil {
|
||||
return fmt.Errorf("ebpf: failed to remove ebpf memlock: %w", err)
|
||||
}
|
||||
|
||||
// Load pre-compiled programs and maps into the kernel.
|
||||
objs := bpfObjects{}
|
||||
if err := loadBpfObjects(&objs, nil); err != nil {
|
||||
if ebpfLoadingFailed.Add(1) >= 5 {
|
||||
log.Warningf("ebpf: failed to load ebpf object 5 times, giving up with error %s", err)
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("ebpf: failed to load ebpf object: %w", err)
|
||||
}
|
||||
defer objs.Close() //nolint:errcheck
|
||||
|
||||
// Create a link to the tcp_connect program.
|
||||
linkTCPConnect, err := link.AttachTracing(link.TracingOptions{
|
||||
Program: objs.bpfPrograms.TcpConnect,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("ebpf: failed to attach to tcp_v4_connect: %w", err)
|
||||
}
|
||||
defer linkTCPConnect.Close() //nolint:errcheck
|
||||
|
||||
// Create a link to the udp_v4_connect program.
|
||||
linkUDPV4, err := link.AttachTracing(link.TracingOptions{
|
||||
Program: objs.bpfPrograms.UdpV4Connect,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("ebpf: failed to attach to udp_v4_connect: %w", err)
|
||||
}
|
||||
defer linkUDPV4.Close() //nolint:errcheck
|
||||
|
||||
// Create a link to the udp_v6_connect program.
|
||||
linkUDPV6, err := link.AttachTracing(link.TracingOptions{
|
||||
Program: objs.bpfPrograms.UdpV6Connect,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("ebpf: failed to attach to udp_v6_connect: %w", err)
|
||||
}
|
||||
defer linkUDPV6.Close() //nolint:errcheck
|
||||
|
||||
// Create new reader to read events.
|
||||
rd, err := ringbuf.NewReader(objs.bpfMaps.PmConnectionEvents)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ebpf: failed to open ring buffer: %w", err)
|
||||
}
|
||||
defer rd.Close() //nolint:errcheck
|
||||
|
||||
// Start watcher to close the reader when the context is canceled.
|
||||
// TODO: Can we put this into a worker?
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
|
||||
if err := rd.Close(); err != nil {
|
||||
log.Errorf("ebpf: failed closing ringbuf reader: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
// Read next event
|
||||
record, err := rd.Read()
|
||||
if err != nil {
|
||||
if errors.Is(err, ringbuf.ErrClosed) {
|
||||
// Normal return
|
||||
return nil
|
||||
}
|
||||
log.Errorf("ebpf: failed to read from ring buffer: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var event bpfEvent
|
||||
// Parse the ringbuf event entry into a bpfEvent structure.
|
||||
if err := binary.Read(bytes.NewBuffer(record.RawSample), binary.BigEndian, &event); err != nil {
|
||||
log.Errorf("ebpf: failed to parse ringbuf event: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
pkt := packet.NewInfoPacket(packet.Info{
|
||||
Inbound: event.Direction == 1,
|
||||
InTunnel: false,
|
||||
Version: packet.IPVersion(event.IpVersion),
|
||||
Protocol: packet.IPProtocol(event.Protocol),
|
||||
SrcPort: event.Sport,
|
||||
DstPort: event.Dport,
|
||||
Src: convertArrayToIPv4(event.Saddr, packet.IPVersion(event.IpVersion)),
|
||||
Dst: convertArrayToIPv4(event.Daddr, packet.IPVersion(event.IpVersion)),
|
||||
PID: int(event.Pid),
|
||||
})
|
||||
if isEventValid(event) {
|
||||
log.Debugf("ebpf: received valid connect event: PID: %d Conn: %s", pkt.Info().PID, pkt)
|
||||
packets <- pkt
|
||||
} else {
|
||||
log.Warningf("ebpf: received invalid connect event: PID: %d Conn: %s", pkt.Info().PID, pkt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isEventValid checks whether the given bpfEvent is valid or not.
|
||||
// It returns true if the event is valid, otherwise false.
|
||||
func isEventValid(event bpfEvent) bool {
|
||||
// Check if the destination port is 0
|
||||
if event.Dport == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the source port is 0
|
||||
if event.Sport == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the process ID is 0
|
||||
if event.Pid == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// If the IP version is IPv4
|
||||
if event.IpVersion == 4 {
|
||||
if event.Saddr[0] == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if event.Daddr[0] == 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// convertArrayToIPv4 converts an array of uint32 values to an IPv4 net.IP address.
|
||||
func convertArrayToIPv4(input [4]uint32, ipVersion packet.IPVersion) net.IP {
|
||||
if ipVersion == packet.IPv4 {
|
||||
addressBuf := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(addressBuf, input[0])
|
||||
return net.IP(addressBuf)
|
||||
}
|
||||
|
||||
addressBuf := make([]byte, 16)
|
||||
for i := 0; i < 4; i++ {
|
||||
binary.LittleEndian.PutUint32(addressBuf[i*4:i*4+4], input[i])
|
||||
}
|
||||
return net.IP(addressBuf)
|
||||
}
|
||||
218
firewall/interception/ebpf/programs/bandwidth.c
Normal file
218
firewall/interception/ebpf/programs/bandwidth.c
Normal file
@@ -0,0 +1,218 @@
|
||||
#include "vmlinux-x86.h"
|
||||
#include "bpf/bpf_helpers.h"
|
||||
#include "bpf/bpf_tracing.h"
|
||||
#include "bpf/bpf_core_read.h"
|
||||
|
||||
#define AF_INET 2
|
||||
#define AF_INET6 10
|
||||
|
||||
#define PROTOCOL_TCP 6
|
||||
#define PROTOCOL_UDP 17
|
||||
|
||||
char __license[] SEC("license") = "GPL";
|
||||
|
||||
struct sk_key {
|
||||
u32 src_ip[4];
|
||||
u32 dst_ip[4];
|
||||
u16 src_port;
|
||||
u16 dst_port;
|
||||
u8 protocol;
|
||||
u8 ipv6;
|
||||
};
|
||||
|
||||
struct sk_info {
|
||||
u64 rx;
|
||||
u64 tx;
|
||||
u64 reported;
|
||||
};
|
||||
|
||||
// Max number of connections that will be kept. Increse the number if it's not enough.
|
||||
#define SOCKOPS_MAP_SIZE 5000
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_LRU_HASH);
|
||||
__uint(max_entries, SOCKOPS_MAP_SIZE);
|
||||
__type(key, struct sk_key);
|
||||
__type(value, struct sk_info);
|
||||
} pm_bandwidth_map SEC(".maps");
|
||||
|
||||
SEC("sockops")
|
||||
int socket_operations(struct bpf_sock_ops *skops) {
|
||||
switch (skops->op) {
|
||||
case BPF_SOCK_OPS_TCP_CONNECT_CB: // Outgoing connections
|
||||
// Set flag so any modification on the socket, will trigger this function.
|
||||
bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_ALL_CB_FLAGS);
|
||||
return 0;
|
||||
case BPF_SOCK_OPS_TCP_LISTEN_CB: // Listening ports
|
||||
bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_ALL_CB_FLAGS);
|
||||
// No rx tx data for this socket object.
|
||||
return 0;
|
||||
case BPF_SOCK_OPS_PASSIVE_ESTABLISHED_CB: // Incoming connections
|
||||
// Set flag so any modification on the socket, will trigger this function.
|
||||
bpf_sock_ops_cb_flags_set(skops, BPF_SOCK_OPS_ALL_CB_FLAGS);
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
struct bpf_sock *sk = skops->sk;
|
||||
if (sk == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sk_key key = {0};
|
||||
key.protocol = PROTOCOL_TCP;
|
||||
if(sk->family == AF_INET) {
|
||||
// Generate key for IPv4
|
||||
key.src_ip[0] = sk->src_ip4;
|
||||
key.src_port = sk->src_port;
|
||||
key.dst_ip[0] = sk->dst_ip4;
|
||||
key.dst_port = __builtin_bswap16(sk->dst_port);
|
||||
key.ipv6 = 0;
|
||||
|
||||
struct sk_info newInfo = {0};
|
||||
newInfo.rx = skops->bytes_received;
|
||||
newInfo.tx = skops->bytes_acked;
|
||||
|
||||
bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY);
|
||||
} else if(sk->family == AF_INET6){
|
||||
// Generate key for IPv6
|
||||
for(int i = 0; i < 4; i++) {
|
||||
key.src_ip[i] = sk->src_ip6[i];
|
||||
}
|
||||
key.src_port = sk->src_port;
|
||||
for(int i = 0; i < 4; i++) {
|
||||
key.dst_ip[i] = sk->dst_ip6[i];
|
||||
}
|
||||
key.dst_port = __builtin_bswap16(sk->dst_port);
|
||||
key.ipv6 = 1;
|
||||
|
||||
// FIXME: This should be added here too, but loading the ebpf module fails if we add it.
|
||||
// This 100% the same thing as above. No clue, man.
|
||||
|
||||
// struct sk_info newInfo = {0};
|
||||
// newInfo.rx = skops->bytes_received;
|
||||
// newInfo.tx = skops->bytes_acked;
|
||||
|
||||
// bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// udp_sendmsg hookes to the respective kernel function and saves the bandwidth data
|
||||
SEC("fentry/udp_sendmsg")
|
||||
int BPF_PROG(udp_sendmsg, struct sock *sk, struct msghdr *msg, size_t len) {
|
||||
struct sock_common *skc = &sk->__sk_common;
|
||||
|
||||
// Create a key for the map and set all the nececery information.
|
||||
struct sk_key key = {0};
|
||||
key.protocol = PROTOCOL_UDP;
|
||||
key.src_ip[0] = skc->skc_rcv_saddr;
|
||||
key.dst_ip[0] = skc->skc_daddr;
|
||||
key.src_port = skc->skc_num;
|
||||
key.dst_port = __builtin_bswap16(skc->skc_dport);
|
||||
key.ipv6 = 0;
|
||||
|
||||
// Update the map with the new information
|
||||
struct sk_info *info = bpf_map_lookup_elem(&pm_bandwidth_map, &key);
|
||||
if (info != NULL) {
|
||||
__sync_fetch_and_add(&info->tx, len); // TODO: Use atomic instead.
|
||||
__sync_fetch_and_and(&info->reported, 0); // TODO: Use atomic instead.
|
||||
} else {
|
||||
struct sk_info newInfo = {0};
|
||||
|
||||
newInfo.tx = len;
|
||||
bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY);
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
// udp_recvmsg hookes to the respective kernel function and saves the bandwidth data
|
||||
SEC("fentry/udp_recvmsg")
|
||||
int BPF_PROG(udp_recvmsg, struct sock *sk, struct msghdr *msg, size_t len, int flags, int *addr_len) {
|
||||
struct sock_common *skc = &sk->__sk_common;
|
||||
|
||||
// Create a key for the map and set all the nececery information.
|
||||
struct sk_key key = {0};
|
||||
key.protocol = PROTOCOL_UDP;
|
||||
key.src_ip[0] = skc->skc_rcv_saddr;
|
||||
key.dst_ip[0] = skc->skc_daddr;
|
||||
key.src_port = skc->skc_num;
|
||||
key.dst_port = __builtin_bswap16(skc->skc_dport);
|
||||
key.ipv6 = 0;
|
||||
|
||||
// Update the map with the new information
|
||||
struct sk_info *info = bpf_map_lookup_elem(&pm_bandwidth_map, &key);
|
||||
if (info != NULL) {
|
||||
__sync_fetch_and_add(&info->rx, len); // TODO: Use atomic instead.
|
||||
__sync_fetch_and_and(&info->reported, 0); // TODO: Use atomic instead.
|
||||
} else {
|
||||
struct sk_info newInfo = {0};
|
||||
|
||||
newInfo.rx = len;
|
||||
bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY);
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
// udpv6_sendmsg hookes to the respective kernel function and saves the bandwidth data
|
||||
SEC("fentry/udpv6_sendmsg")
|
||||
int BPF_PROG(udpv6_sendmsg, struct sock *sk, struct msghdr *msg, size_t len) {
|
||||
struct sock_common *skc = &sk->__sk_common;
|
||||
|
||||
// Create a key for the map and set all the nececery information.
|
||||
struct sk_key key = {0};
|
||||
key.protocol = PROTOCOL_UDP;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
key.src_ip[i] = skc->skc_v6_rcv_saddr.in6_u.u6_addr32[i];
|
||||
key.dst_ip[i] = skc->skc_v6_rcv_saddr.in6_u.u6_addr32[i];
|
||||
}
|
||||
key.src_port = skc->skc_num;
|
||||
key.dst_port = __builtin_bswap16(skc->skc_dport);
|
||||
key.ipv6 = 1;
|
||||
|
||||
// Update the map with the new information
|
||||
struct sk_info *info = bpf_map_lookup_elem(&pm_bandwidth_map, &key);
|
||||
if (info != NULL) {
|
||||
__sync_fetch_and_add(&info->tx, len); // TODO: Use atomic instead.
|
||||
__sync_fetch_and_and(&info->reported, 0); // TODO: Use atomic instead.
|
||||
} else {
|
||||
struct sk_info newInfo = {0};
|
||||
newInfo.tx = len;
|
||||
bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// udpv6_recvmsg hookes to the respective kernel function and saves the bandwidth data
|
||||
SEC("fentry/udpv6_recvmsg")
|
||||
int BPF_PROG(udpv6_recvmsg, struct sock *sk, struct msghdr *msg, size_t len, int flags, int *addr_len) {
|
||||
struct sock_common *skc = &sk->__sk_common;
|
||||
|
||||
// Create a key for the map and set all the nececery information.
|
||||
struct sk_key key = {0};
|
||||
key.protocol = PROTOCOL_UDP;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
key.src_ip[i] = skc->skc_v6_rcv_saddr.in6_u.u6_addr32[i];
|
||||
key.dst_ip[i] = skc->skc_v6_rcv_saddr.in6_u.u6_addr32[i];
|
||||
}
|
||||
key.src_port = skc->skc_num;
|
||||
key.dst_port = __builtin_bswap16(skc->skc_dport);
|
||||
key.ipv6 = 1;
|
||||
|
||||
// Update the map with the new information
|
||||
struct sk_info *info = bpf_map_lookup_elem(&pm_bandwidth_map, &key);
|
||||
if (info != NULL) {
|
||||
__sync_fetch_and_add(&info->rx, len); // TODO: Use atomic instead.
|
||||
__sync_fetch_and_and(&info->reported, 0); // TODO: Use atomic instead.
|
||||
} else {
|
||||
struct sk_info newInfo = {0};
|
||||
newInfo.rx = len;
|
||||
bpf_map_update_elem(&pm_bandwidth_map, &key, &newInfo, BPF_ANY);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
484
firewall/interception/ebpf/programs/bpf/bpf_core_read.h
Normal file
484
firewall/interception/ebpf/programs/bpf/bpf_core_read.h
Normal file
@@ -0,0 +1,484 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __BPF_CORE_READ_H__
|
||||
#define __BPF_CORE_READ_H__
|
||||
|
||||
/*
|
||||
* enum bpf_field_info_kind is passed as a second argument into
|
||||
* __builtin_preserve_field_info() built-in to get a specific aspect of
|
||||
* a field, captured as a first argument. __builtin_preserve_field_info(field,
|
||||
* info_kind) returns __u32 integer and produces BTF field relocation, which
|
||||
* is understood and processed by libbpf during BPF object loading. See
|
||||
* selftests/bpf for examples.
|
||||
*/
|
||||
enum bpf_field_info_kind {
|
||||
BPF_FIELD_BYTE_OFFSET = 0, /* field byte offset */
|
||||
BPF_FIELD_BYTE_SIZE = 1,
|
||||
BPF_FIELD_EXISTS = 2, /* field existence in target kernel */
|
||||
BPF_FIELD_SIGNED = 3,
|
||||
BPF_FIELD_LSHIFT_U64 = 4,
|
||||
BPF_FIELD_RSHIFT_U64 = 5,
|
||||
};
|
||||
|
||||
/* second argument to __builtin_btf_type_id() built-in */
|
||||
enum bpf_type_id_kind {
|
||||
BPF_TYPE_ID_LOCAL = 0, /* BTF type ID in local program */
|
||||
BPF_TYPE_ID_TARGET = 1, /* BTF type ID in target kernel */
|
||||
};
|
||||
|
||||
/* second argument to __builtin_preserve_type_info() built-in */
|
||||
enum bpf_type_info_kind {
|
||||
BPF_TYPE_EXISTS = 0, /* type existence in target kernel */
|
||||
BPF_TYPE_SIZE = 1, /* type size in target kernel */
|
||||
BPF_TYPE_MATCHES = 2, /* type match in target kernel */
|
||||
};
|
||||
|
||||
/* second argument to __builtin_preserve_enum_value() built-in */
|
||||
enum bpf_enum_value_kind {
|
||||
BPF_ENUMVAL_EXISTS = 0, /* enum value existence in kernel */
|
||||
BPF_ENUMVAL_VALUE = 1, /* enum value value relocation */
|
||||
};
|
||||
|
||||
#define __CORE_RELO(src, field, info) \
|
||||
__builtin_preserve_field_info((src)->field, BPF_FIELD_##info)
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \
|
||||
bpf_probe_read_kernel( \
|
||||
(void *)dst, \
|
||||
__CORE_RELO(src, fld, BYTE_SIZE), \
|
||||
(const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET))
|
||||
#else
|
||||
/* semantics of LSHIFT_64 assumes loading values into low-ordered bytes, so
|
||||
* for big-endian we need to adjust destination pointer accordingly, based on
|
||||
* field byte size
|
||||
*/
|
||||
#define __CORE_BITFIELD_PROBE_READ(dst, src, fld) \
|
||||
bpf_probe_read_kernel( \
|
||||
(void *)dst + (8 - __CORE_RELO(src, fld, BYTE_SIZE)), \
|
||||
__CORE_RELO(src, fld, BYTE_SIZE), \
|
||||
(const void *)src + __CORE_RELO(src, fld, BYTE_OFFSET))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Extract bitfield, identified by s->field, and return its value as u64.
|
||||
* All this is done in relocatable manner, so bitfield changes such as
|
||||
* signedness, bit size, offset changes, this will be handled automatically.
|
||||
* This version of macro is using bpf_probe_read_kernel() to read underlying
|
||||
* integer storage. Macro functions as an expression and its return type is
|
||||
* bpf_probe_read_kernel()'s return value: 0, on success, <0 on error.
|
||||
*/
|
||||
#define BPF_CORE_READ_BITFIELD_PROBED(s, field) ({ \
|
||||
unsigned long long val = 0; \
|
||||
\
|
||||
__CORE_BITFIELD_PROBE_READ(&val, s, field); \
|
||||
val <<= __CORE_RELO(s, field, LSHIFT_U64); \
|
||||
if (__CORE_RELO(s, field, SIGNED)) \
|
||||
val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \
|
||||
else \
|
||||
val = val >> __CORE_RELO(s, field, RSHIFT_U64); \
|
||||
val; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Extract bitfield, identified by s->field, and return its value as u64.
|
||||
* This version of macro is using direct memory reads and should be used from
|
||||
* BPF program types that support such functionality (e.g., typed raw
|
||||
* tracepoints).
|
||||
*/
|
||||
#define BPF_CORE_READ_BITFIELD(s, field) ({ \
|
||||
const void *p = (const void *)s + __CORE_RELO(s, field, BYTE_OFFSET); \
|
||||
unsigned long long val; \
|
||||
\
|
||||
/* This is a so-called barrier_var() operation that makes specified \
|
||||
* variable "a black box" for optimizing compiler. \
|
||||
* It forces compiler to perform BYTE_OFFSET relocation on p and use \
|
||||
* its calculated value in the switch below, instead of applying \
|
||||
* the same relocation 4 times for each individual memory load. \
|
||||
*/ \
|
||||
asm volatile("" : "=r"(p) : "0"(p)); \
|
||||
\
|
||||
switch (__CORE_RELO(s, field, BYTE_SIZE)) { \
|
||||
case 1: val = *(const unsigned char *)p; break; \
|
||||
case 2: val = *(const unsigned short *)p; break; \
|
||||
case 4: val = *(const unsigned int *)p; break; \
|
||||
case 8: val = *(const unsigned long long *)p; break; \
|
||||
} \
|
||||
val <<= __CORE_RELO(s, field, LSHIFT_U64); \
|
||||
if (__CORE_RELO(s, field, SIGNED)) \
|
||||
val = ((long long)val) >> __CORE_RELO(s, field, RSHIFT_U64); \
|
||||
else \
|
||||
val = val >> __CORE_RELO(s, field, RSHIFT_U64); \
|
||||
val; \
|
||||
})
|
||||
|
||||
#define ___bpf_field_ref1(field) (field)
|
||||
#define ___bpf_field_ref2(type, field) (((typeof(type) *)0)->field)
|
||||
#define ___bpf_field_ref(args...) \
|
||||
___bpf_apply(___bpf_field_ref, ___bpf_narg(args))(args)
|
||||
|
||||
/*
|
||||
* Convenience macro to check that field actually exists in target kernel's.
|
||||
* Returns:
|
||||
* 1, if matching field is present in target kernel;
|
||||
* 0, if no matching field found.
|
||||
*
|
||||
* Supports two forms:
|
||||
* - field reference through variable access:
|
||||
* bpf_core_field_exists(p->my_field);
|
||||
* - field reference through type and field names:
|
||||
* bpf_core_field_exists(struct my_type, my_field).
|
||||
*/
|
||||
#define bpf_core_field_exists(field...) \
|
||||
__builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_EXISTS)
|
||||
|
||||
/*
|
||||
* Convenience macro to get the byte size of a field. Works for integers,
|
||||
* struct/unions, pointers, arrays, and enums.
|
||||
*
|
||||
* Supports two forms:
|
||||
* - field reference through variable access:
|
||||
* bpf_core_field_size(p->my_field);
|
||||
* - field reference through type and field names:
|
||||
* bpf_core_field_size(struct my_type, my_field).
|
||||
*/
|
||||
#define bpf_core_field_size(field...) \
|
||||
__builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_BYTE_SIZE)
|
||||
|
||||
/*
|
||||
* Convenience macro to get field's byte offset.
|
||||
*
|
||||
* Supports two forms:
|
||||
* - field reference through variable access:
|
||||
* bpf_core_field_offset(p->my_field);
|
||||
* - field reference through type and field names:
|
||||
* bpf_core_field_offset(struct my_type, my_field).
|
||||
*/
|
||||
#define bpf_core_field_offset(field...) \
|
||||
__builtin_preserve_field_info(___bpf_field_ref(field), BPF_FIELD_BYTE_OFFSET)
|
||||
|
||||
/*
|
||||
* Convenience macro to get BTF type ID of a specified type, using a local BTF
|
||||
* information. Return 32-bit unsigned integer with type ID from program's own
|
||||
* BTF. Always succeeds.
|
||||
*/
|
||||
#define bpf_core_type_id_local(type) \
|
||||
__builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_LOCAL)
|
||||
|
||||
/*
|
||||
* Convenience macro to get BTF type ID of a target kernel's type that matches
|
||||
* specified local type.
|
||||
* Returns:
|
||||
* - valid 32-bit unsigned type ID in kernel BTF;
|
||||
* - 0, if no matching type was found in a target kernel BTF.
|
||||
*/
|
||||
#define bpf_core_type_id_kernel(type) \
|
||||
__builtin_btf_type_id(*(typeof(type) *)0, BPF_TYPE_ID_TARGET)
|
||||
|
||||
/*
|
||||
* Convenience macro to check that provided named type
|
||||
* (struct/union/enum/typedef) exists in a target kernel.
|
||||
* Returns:
|
||||
* 1, if such type is present in target kernel's BTF;
|
||||
* 0, if no matching type is found.
|
||||
*/
|
||||
#define bpf_core_type_exists(type) \
|
||||
__builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_EXISTS)
|
||||
|
||||
/*
|
||||
* Convenience macro to check that provided named type
|
||||
* (struct/union/enum/typedef) "matches" that in a target kernel.
|
||||
* Returns:
|
||||
* 1, if the type matches in the target kernel's BTF;
|
||||
* 0, if the type does not match any in the target kernel
|
||||
*/
|
||||
#define bpf_core_type_matches(type) \
|
||||
__builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_MATCHES)
|
||||
|
||||
/*
|
||||
* Convenience macro to get the byte size of a provided named type
|
||||
* (struct/union/enum/typedef) in a target kernel.
|
||||
* Returns:
|
||||
* >= 0 size (in bytes), if type is present in target kernel's BTF;
|
||||
* 0, if no matching type is found.
|
||||
*/
|
||||
#define bpf_core_type_size(type) \
|
||||
__builtin_preserve_type_info(*(typeof(type) *)0, BPF_TYPE_SIZE)
|
||||
|
||||
/*
|
||||
* Convenience macro to check that provided enumerator value is defined in
|
||||
* a target kernel.
|
||||
* Returns:
|
||||
* 1, if specified enum type and its enumerator value are present in target
|
||||
* kernel's BTF;
|
||||
* 0, if no matching enum and/or enum value within that enum is found.
|
||||
*/
|
||||
#define bpf_core_enum_value_exists(enum_type, enum_value) \
|
||||
__builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_EXISTS)
|
||||
|
||||
/*
|
||||
* Convenience macro to get the integer value of an enumerator value in
|
||||
* a target kernel.
|
||||
* Returns:
|
||||
* 64-bit value, if specified enum type and its enumerator value are
|
||||
* present in target kernel's BTF;
|
||||
* 0, if no matching enum and/or enum value within that enum is found.
|
||||
*/
|
||||
#define bpf_core_enum_value(enum_type, enum_value) \
|
||||
__builtin_preserve_enum_value(*(typeof(enum_type) *)enum_value, BPF_ENUMVAL_VALUE)
|
||||
|
||||
/*
|
||||
* bpf_core_read() abstracts away bpf_probe_read_kernel() call and captures
|
||||
* offset relocation for source address using __builtin_preserve_access_index()
|
||||
* built-in, provided by Clang.
|
||||
*
|
||||
* __builtin_preserve_access_index() takes as an argument an expression of
|
||||
* taking an address of a field within struct/union. It makes compiler emit
|
||||
* a relocation, which records BTF type ID describing root struct/union and an
|
||||
* accessor string which describes exact embedded field that was used to take
|
||||
* an address. See detailed description of this relocation format and
|
||||
* semantics in comments to struct bpf_field_reloc in libbpf_internal.h.
|
||||
*
|
||||
* This relocation allows libbpf to adjust BPF instruction to use correct
|
||||
* actual field offset, based on target kernel BTF type that matches original
|
||||
* (local) BTF, used to record relocation.
|
||||
*/
|
||||
#define bpf_core_read(dst, sz, src) \
|
||||
bpf_probe_read_kernel(dst, sz, (const void *)__builtin_preserve_access_index(src))
|
||||
|
||||
/* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */
|
||||
#define bpf_core_read_user(dst, sz, src) \
|
||||
bpf_probe_read_user(dst, sz, (const void *)__builtin_preserve_access_index(src))
|
||||
/*
|
||||
* bpf_core_read_str() is a thin wrapper around bpf_probe_read_str()
|
||||
* additionally emitting BPF CO-RE field relocation for specified source
|
||||
* argument.
|
||||
*/
|
||||
#define bpf_core_read_str(dst, sz, src) \
|
||||
bpf_probe_read_kernel_str(dst, sz, (const void *)__builtin_preserve_access_index(src))
|
||||
|
||||
/* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use. */
|
||||
#define bpf_core_read_user_str(dst, sz, src) \
|
||||
bpf_probe_read_user_str(dst, sz, (const void *)__builtin_preserve_access_index(src))
|
||||
|
||||
#define ___concat(a, b) a ## b
|
||||
#define ___apply(fn, n) ___concat(fn, n)
|
||||
#define ___nth(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, __11, N, ...) N
|
||||
|
||||
/*
|
||||
* return number of provided arguments; used for switch-based variadic macro
|
||||
* definitions (see ___last, ___arrow, etc below)
|
||||
*/
|
||||
#define ___narg(...) ___nth(_, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
/*
|
||||
* return 0 if no arguments are passed, N - otherwise; used for
|
||||
* recursively-defined macros to specify termination (0) case, and generic
|
||||
* (N) case (e.g., ___read_ptrs, ___core_read)
|
||||
*/
|
||||
#define ___empty(...) ___nth(_, ##__VA_ARGS__, N, N, N, N, N, N, N, N, N, N, 0)
|
||||
|
||||
#define ___last1(x) x
|
||||
#define ___last2(a, x) x
|
||||
#define ___last3(a, b, x) x
|
||||
#define ___last4(a, b, c, x) x
|
||||
#define ___last5(a, b, c, d, x) x
|
||||
#define ___last6(a, b, c, d, e, x) x
|
||||
#define ___last7(a, b, c, d, e, f, x) x
|
||||
#define ___last8(a, b, c, d, e, f, g, x) x
|
||||
#define ___last9(a, b, c, d, e, f, g, h, x) x
|
||||
#define ___last10(a, b, c, d, e, f, g, h, i, x) x
|
||||
#define ___last(...) ___apply(___last, ___narg(__VA_ARGS__))(__VA_ARGS__)
|
||||
|
||||
#define ___nolast2(a, _) a
|
||||
#define ___nolast3(a, b, _) a, b
|
||||
#define ___nolast4(a, b, c, _) a, b, c
|
||||
#define ___nolast5(a, b, c, d, _) a, b, c, d
|
||||
#define ___nolast6(a, b, c, d, e, _) a, b, c, d, e
|
||||
#define ___nolast7(a, b, c, d, e, f, _) a, b, c, d, e, f
|
||||
#define ___nolast8(a, b, c, d, e, f, g, _) a, b, c, d, e, f, g
|
||||
#define ___nolast9(a, b, c, d, e, f, g, h, _) a, b, c, d, e, f, g, h
|
||||
#define ___nolast10(a, b, c, d, e, f, g, h, i, _) a, b, c, d, e, f, g, h, i
|
||||
#define ___nolast(...) ___apply(___nolast, ___narg(__VA_ARGS__))(__VA_ARGS__)
|
||||
|
||||
#define ___arrow1(a) a
|
||||
#define ___arrow2(a, b) a->b
|
||||
#define ___arrow3(a, b, c) a->b->c
|
||||
#define ___arrow4(a, b, c, d) a->b->c->d
|
||||
#define ___arrow5(a, b, c, d, e) a->b->c->d->e
|
||||
#define ___arrow6(a, b, c, d, e, f) a->b->c->d->e->f
|
||||
#define ___arrow7(a, b, c, d, e, f, g) a->b->c->d->e->f->g
|
||||
#define ___arrow8(a, b, c, d, e, f, g, h) a->b->c->d->e->f->g->h
|
||||
#define ___arrow9(a, b, c, d, e, f, g, h, i) a->b->c->d->e->f->g->h->i
|
||||
#define ___arrow10(a, b, c, d, e, f, g, h, i, j) a->b->c->d->e->f->g->h->i->j
|
||||
#define ___arrow(...) ___apply(___arrow, ___narg(__VA_ARGS__))(__VA_ARGS__)
|
||||
|
||||
#define ___type(...) typeof(___arrow(__VA_ARGS__))
|
||||
|
||||
#define ___read(read_fn, dst, src_type, src, accessor) \
|
||||
read_fn((void *)(dst), sizeof(*(dst)), &((src_type)(src))->accessor)
|
||||
|
||||
/* "recursively" read a sequence of inner pointers using local __t var */
|
||||
#define ___rd_first(fn, src, a) ___read(fn, &__t, ___type(src), src, a);
|
||||
#define ___rd_last(fn, ...) \
|
||||
___read(fn, &__t, ___type(___nolast(__VA_ARGS__)), __t, ___last(__VA_ARGS__));
|
||||
#define ___rd_p1(fn, ...) const void *__t; ___rd_first(fn, __VA_ARGS__)
|
||||
#define ___rd_p2(fn, ...) ___rd_p1(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
|
||||
#define ___rd_p3(fn, ...) ___rd_p2(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
|
||||
#define ___rd_p4(fn, ...) ___rd_p3(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
|
||||
#define ___rd_p5(fn, ...) ___rd_p4(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
|
||||
#define ___rd_p6(fn, ...) ___rd_p5(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
|
||||
#define ___rd_p7(fn, ...) ___rd_p6(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
|
||||
#define ___rd_p8(fn, ...) ___rd_p7(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
|
||||
#define ___rd_p9(fn, ...) ___rd_p8(fn, ___nolast(__VA_ARGS__)) ___rd_last(fn, __VA_ARGS__)
|
||||
#define ___read_ptrs(fn, src, ...) \
|
||||
___apply(___rd_p, ___narg(__VA_ARGS__))(fn, src, __VA_ARGS__)
|
||||
|
||||
#define ___core_read0(fn, fn_ptr, dst, src, a) \
|
||||
___read(fn, dst, ___type(src), src, a);
|
||||
#define ___core_readN(fn, fn_ptr, dst, src, ...) \
|
||||
___read_ptrs(fn_ptr, src, ___nolast(__VA_ARGS__)) \
|
||||
___read(fn, dst, ___type(src, ___nolast(__VA_ARGS__)), __t, \
|
||||
___last(__VA_ARGS__));
|
||||
#define ___core_read(fn, fn_ptr, dst, src, a, ...) \
|
||||
___apply(___core_read, ___empty(__VA_ARGS__))(fn, fn_ptr, dst, \
|
||||
src, a, ##__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* BPF_CORE_READ_INTO() is a more performance-conscious variant of
|
||||
* BPF_CORE_READ(), in which final field is read into user-provided storage.
|
||||
* See BPF_CORE_READ() below for more details on general usage.
|
||||
*/
|
||||
#define BPF_CORE_READ_INTO(dst, src, a, ...) ({ \
|
||||
___core_read(bpf_core_read, bpf_core_read, \
|
||||
dst, (src), a, ##__VA_ARGS__) \
|
||||
})
|
||||
|
||||
/*
|
||||
* Variant of BPF_CORE_READ_INTO() for reading from user-space memory.
|
||||
*
|
||||
* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use.
|
||||
*/
|
||||
#define BPF_CORE_READ_USER_INTO(dst, src, a, ...) ({ \
|
||||
___core_read(bpf_core_read_user, bpf_core_read_user, \
|
||||
dst, (src), a, ##__VA_ARGS__) \
|
||||
})
|
||||
|
||||
/* Non-CO-RE variant of BPF_CORE_READ_INTO() */
|
||||
#define BPF_PROBE_READ_INTO(dst, src, a, ...) ({ \
|
||||
___core_read(bpf_probe_read_kernel, bpf_probe_read_kernel, \
|
||||
dst, (src), a, ##__VA_ARGS__) \
|
||||
})
|
||||
|
||||
/* Non-CO-RE variant of BPF_CORE_READ_USER_INTO().
|
||||
*
|
||||
* As no CO-RE relocations are emitted, source types can be arbitrary and are
|
||||
* not restricted to kernel types only.
|
||||
*/
|
||||
#define BPF_PROBE_READ_USER_INTO(dst, src, a, ...) ({ \
|
||||
___core_read(bpf_probe_read_user, bpf_probe_read_user, \
|
||||
dst, (src), a, ##__VA_ARGS__) \
|
||||
})
|
||||
|
||||
/*
|
||||
* BPF_CORE_READ_STR_INTO() does same "pointer chasing" as
|
||||
* BPF_CORE_READ() for intermediate pointers, but then executes (and returns
|
||||
* corresponding error code) bpf_core_read_str() for final string read.
|
||||
*/
|
||||
#define BPF_CORE_READ_STR_INTO(dst, src, a, ...) ({ \
|
||||
___core_read(bpf_core_read_str, bpf_core_read, \
|
||||
dst, (src), a, ##__VA_ARGS__) \
|
||||
})
|
||||
|
||||
/*
|
||||
* Variant of BPF_CORE_READ_STR_INTO() for reading from user-space memory.
|
||||
*
|
||||
* NOTE: see comments for BPF_CORE_READ_USER() about the proper types use.
|
||||
*/
|
||||
#define BPF_CORE_READ_USER_STR_INTO(dst, src, a, ...) ({ \
|
||||
___core_read(bpf_core_read_user_str, bpf_core_read_user, \
|
||||
dst, (src), a, ##__VA_ARGS__) \
|
||||
})
|
||||
|
||||
/* Non-CO-RE variant of BPF_CORE_READ_STR_INTO() */
|
||||
#define BPF_PROBE_READ_STR_INTO(dst, src, a, ...) ({ \
|
||||
___core_read(bpf_probe_read_kernel_str, bpf_probe_read_kernel, \
|
||||
dst, (src), a, ##__VA_ARGS__) \
|
||||
})
|
||||
|
||||
/*
|
||||
* Non-CO-RE variant of BPF_CORE_READ_USER_STR_INTO().
|
||||
*
|
||||
* As no CO-RE relocations are emitted, source types can be arbitrary and are
|
||||
* not restricted to kernel types only.
|
||||
*/
|
||||
#define BPF_PROBE_READ_USER_STR_INTO(dst, src, a, ...) ({ \
|
||||
___core_read(bpf_probe_read_user_str, bpf_probe_read_user, \
|
||||
dst, (src), a, ##__VA_ARGS__) \
|
||||
})
|
||||
|
||||
/*
|
||||
* BPF_CORE_READ() is used to simplify BPF CO-RE relocatable read, especially
|
||||
* when there are few pointer chasing steps.
|
||||
* E.g., what in non-BPF world (or in BPF w/ BCC) would be something like:
|
||||
* int x = s->a.b.c->d.e->f->g;
|
||||
* can be succinctly achieved using BPF_CORE_READ as:
|
||||
* int x = BPF_CORE_READ(s, a.b.c, d.e, f, g);
|
||||
*
|
||||
* BPF_CORE_READ will decompose above statement into 4 bpf_core_read (BPF
|
||||
* CO-RE relocatable bpf_probe_read_kernel() wrapper) calls, logically
|
||||
* equivalent to:
|
||||
* 1. const void *__t = s->a.b.c;
|
||||
* 2. __t = __t->d.e;
|
||||
* 3. __t = __t->f;
|
||||
* 4. return __t->g;
|
||||
*
|
||||
* Equivalence is logical, because there is a heavy type casting/preservation
|
||||
* involved, as well as all the reads are happening through
|
||||
* bpf_probe_read_kernel() calls using __builtin_preserve_access_index() to
|
||||
* emit CO-RE relocations.
|
||||
*
|
||||
* N.B. Only up to 9 "field accessors" are supported, which should be more
|
||||
* than enough for any practical purpose.
|
||||
*/
|
||||
#define BPF_CORE_READ(src, a, ...) ({ \
|
||||
___type((src), a, ##__VA_ARGS__) __r; \
|
||||
BPF_CORE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \
|
||||
__r; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Variant of BPF_CORE_READ() for reading from user-space memory.
|
||||
*
|
||||
* NOTE: all the source types involved are still *kernel types* and need to
|
||||
* exist in kernel (or kernel module) BTF, otherwise CO-RE relocation will
|
||||
* fail. Custom user types are not relocatable with CO-RE.
|
||||
* The typical situation in which BPF_CORE_READ_USER() might be used is to
|
||||
* read kernel UAPI types from the user-space memory passed in as a syscall
|
||||
* input argument.
|
||||
*/
|
||||
#define BPF_CORE_READ_USER(src, a, ...) ({ \
|
||||
___type((src), a, ##__VA_ARGS__) __r; \
|
||||
BPF_CORE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \
|
||||
__r; \
|
||||
})
|
||||
|
||||
/* Non-CO-RE variant of BPF_CORE_READ() */
|
||||
#define BPF_PROBE_READ(src, a, ...) ({ \
|
||||
___type((src), a, ##__VA_ARGS__) __r; \
|
||||
BPF_PROBE_READ_INTO(&__r, (src), a, ##__VA_ARGS__); \
|
||||
__r; \
|
||||
})
|
||||
|
||||
/*
|
||||
* Non-CO-RE variant of BPF_CORE_READ_USER().
|
||||
*
|
||||
* As no CO-RE relocations are emitted, source types can be arbitrary and are
|
||||
* not restricted to kernel types only.
|
||||
*/
|
||||
#define BPF_PROBE_READ_USER(src, a, ...) ({ \
|
||||
___type((src), a, ##__VA_ARGS__) __r; \
|
||||
BPF_PROBE_READ_USER_INTO(&__r, (src), a, ##__VA_ARGS__); \
|
||||
__r; \
|
||||
})
|
||||
|
||||
#endif
|
||||
|
||||
4748
firewall/interception/ebpf/programs/bpf/bpf_helper_defs.h
Normal file
4748
firewall/interception/ebpf/programs/bpf/bpf_helper_defs.h
Normal file
File diff suppressed because it is too large
Load Diff
397
firewall/interception/ebpf/programs/bpf/bpf_helpers.h
Normal file
397
firewall/interception/ebpf/programs/bpf/bpf_helpers.h
Normal file
@@ -0,0 +1,397 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __BPF_HELPERS__
|
||||
#define __BPF_HELPERS__
|
||||
|
||||
/*
|
||||
* Note that bpf programs need to include either
|
||||
* vmlinux.h (auto-generated from BTF) or linux/types.h
|
||||
* in advance since bpf_helper_defs.h uses such types
|
||||
* as __u64.
|
||||
*/
|
||||
#include "bpf_helper_defs.h"
|
||||
|
||||
#define __uint(name, val) int (*name)[val]
|
||||
#define __type(name, val) typeof(val) *name
|
||||
#define __array(name, val) typeof(val) *name[]
|
||||
|
||||
/*
|
||||
* Helper macro to place programs, maps, license in
|
||||
* different sections in elf_bpf file. Section names
|
||||
* are interpreted by libbpf depending on the context (BPF programs, BPF maps,
|
||||
* extern variables, etc).
|
||||
* To allow use of SEC() with externs (e.g., for extern .maps declarations),
|
||||
* make sure __attribute__((unused)) doesn't trigger compilation warning.
|
||||
*/
|
||||
#if __GNUC__ && !__clang__
|
||||
|
||||
/*
|
||||
* Pragma macros are broken on GCC
|
||||
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55578
|
||||
* https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90400
|
||||
*/
|
||||
#define SEC(name) __attribute__((section(name), used))
|
||||
|
||||
#else
|
||||
|
||||
#define SEC(name) \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wignored-attributes\"") \
|
||||
__attribute__((section(name), used)) \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
|
||||
#endif
|
||||
|
||||
/* Avoid 'linux/stddef.h' definition of '__always_inline'. */
|
||||
#undef __always_inline
|
||||
#define __always_inline inline __attribute__((always_inline))
|
||||
|
||||
#ifndef __noinline
|
||||
#define __noinline __attribute__((noinline))
|
||||
#endif
|
||||
#ifndef __weak
|
||||
#define __weak __attribute__((weak))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Use __hidden attribute to mark a non-static BPF subprogram effectively
|
||||
* static for BPF verifier's verification algorithm purposes, allowing more
|
||||
* extensive and permissive BPF verification process, taking into account
|
||||
* subprogram's caller context.
|
||||
*/
|
||||
#define __hidden __attribute__((visibility("hidden")))
|
||||
|
||||
/* When utilizing vmlinux.h with BPF CO-RE, user BPF programs can't include
|
||||
* any system-level headers (such as stddef.h, linux/version.h, etc), and
|
||||
* commonly-used macros like NULL and KERNEL_VERSION aren't available through
|
||||
* vmlinux.h. This just adds unnecessary hurdles and forces users to re-define
|
||||
* them on their own. So as a convenience, provide such definitions here.
|
||||
*/
|
||||
#ifndef NULL
|
||||
#define NULL ((void *)0)
|
||||
#endif
|
||||
|
||||
#ifndef KERNEL_VERSION
|
||||
#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + ((c) > 255 ? 255 : (c)))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper macros to manipulate data structures
|
||||
*/
|
||||
#ifndef offsetof
|
||||
#define offsetof(TYPE, MEMBER) ((unsigned long)&((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
#ifndef container_of
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
void *__mptr = (void *)(ptr); \
|
||||
((type *)(__mptr - offsetof(type, member))); \
|
||||
})
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Compiler (optimization) barrier.
|
||||
*/
|
||||
#ifndef barrier
|
||||
#define barrier() asm volatile("" ::: "memory")
|
||||
#endif
|
||||
|
||||
/* Variable-specific compiler (optimization) barrier. It's a no-op which makes
|
||||
* compiler believe that there is some black box modification of a given
|
||||
* variable and thus prevents compiler from making extra assumption about its
|
||||
* value and potential simplifications and optimizations on this variable.
|
||||
*
|
||||
* E.g., compiler might often delay or even omit 32-bit to 64-bit casting of
|
||||
* a variable, making some code patterns unverifiable. Putting barrier_var()
|
||||
* in place will ensure that cast is performed before the barrier_var()
|
||||
* invocation, because compiler has to pessimistically assume that embedded
|
||||
* asm section might perform some extra operations on that variable.
|
||||
*
|
||||
* This is a variable-specific variant of more global barrier().
|
||||
*/
|
||||
#ifndef barrier_var
|
||||
#define barrier_var(var) asm volatile("" : "+r"(var))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper macro to throw a compilation error if __bpf_unreachable() gets
|
||||
* built into the resulting code. This works given BPF back end does not
|
||||
* implement __builtin_trap(). This is useful to assert that certain paths
|
||||
* of the program code are never used and hence eliminated by the compiler.
|
||||
*
|
||||
* For example, consider a switch statement that covers known cases used by
|
||||
* the program. __bpf_unreachable() can then reside in the default case. If
|
||||
* the program gets extended such that a case is not covered in the switch
|
||||
* statement, then it will throw a build error due to the default case not
|
||||
* being compiled out.
|
||||
*/
|
||||
#ifndef __bpf_unreachable
|
||||
# define __bpf_unreachable() __builtin_trap()
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper function to perform a tail call with a constant/immediate map slot.
|
||||
*/
|
||||
#if __clang_major__ >= 8 && defined(__bpf__)
|
||||
static __always_inline void
|
||||
bpf_tail_call_static(void *ctx, const void *map, const __u32 slot)
|
||||
{
|
||||
if (!__builtin_constant_p(slot))
|
||||
__bpf_unreachable();
|
||||
|
||||
/*
|
||||
* Provide a hard guarantee that LLVM won't optimize setting r2 (map
|
||||
* pointer) and r3 (constant map index) from _different paths_ ending
|
||||
* up at the _same_ call insn as otherwise we won't be able to use the
|
||||
* jmpq/nopl retpoline-free patching by the x86-64 JIT in the kernel
|
||||
* given they mismatch. See also d2e4c1e6c294 ("bpf: Constant map key
|
||||
* tracking for prog array pokes") for details on verifier tracking.
|
||||
*
|
||||
* Note on clobber list: we need to stay in-line with BPF calling
|
||||
* convention, so even if we don't end up using r0, r4, r5, we need
|
||||
* to mark them as clobber so that LLVM doesn't end up using them
|
||||
* before / after the call.
|
||||
*/
|
||||
asm volatile("r1 = %[ctx]\n\t"
|
||||
"r2 = %[map]\n\t"
|
||||
"r3 = %[slot]\n\t"
|
||||
"call 12"
|
||||
:: [ctx]"r"(ctx), [map]"r"(map), [slot]"i"(slot)
|
||||
: "r0", "r1", "r2", "r3", "r4", "r5");
|
||||
}
|
||||
#endif
|
||||
|
||||
enum libbpf_pin_type {
|
||||
LIBBPF_PIN_NONE,
|
||||
/* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */
|
||||
LIBBPF_PIN_BY_NAME,
|
||||
};
|
||||
|
||||
enum libbpf_tristate {
|
||||
TRI_NO = 0,
|
||||
TRI_YES = 1,
|
||||
TRI_MODULE = 2,
|
||||
};
|
||||
|
||||
#define __kconfig __attribute__((section(".kconfig")))
|
||||
#define __ksym __attribute__((section(".ksyms")))
|
||||
#define __kptr_untrusted __attribute__((btf_type_tag("kptr_untrusted")))
|
||||
#define __kptr __attribute__((btf_type_tag("kptr")))
|
||||
|
||||
#define bpf_ksym_exists(sym) ({ \
|
||||
_Static_assert(!__builtin_constant_p(!!sym), #sym " should be marked as __weak"); \
|
||||
!!sym; \
|
||||
})
|
||||
|
||||
#ifndef ___bpf_concat
|
||||
#define ___bpf_concat(a, b) a ## b
|
||||
#endif
|
||||
#ifndef ___bpf_apply
|
||||
#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)
|
||||
#endif
|
||||
#ifndef ___bpf_nth
|
||||
#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N
|
||||
#endif
|
||||
#ifndef ___bpf_narg
|
||||
#define ___bpf_narg(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
#endif
|
||||
|
||||
#define ___bpf_fill0(arr, p, x) do {} while (0)
|
||||
#define ___bpf_fill1(arr, p, x) arr[p] = x
|
||||
#define ___bpf_fill2(arr, p, x, args...) arr[p] = x; ___bpf_fill1(arr, p + 1, args)
|
||||
#define ___bpf_fill3(arr, p, x, args...) arr[p] = x; ___bpf_fill2(arr, p + 1, args)
|
||||
#define ___bpf_fill4(arr, p, x, args...) arr[p] = x; ___bpf_fill3(arr, p + 1, args)
|
||||
#define ___bpf_fill5(arr, p, x, args...) arr[p] = x; ___bpf_fill4(arr, p + 1, args)
|
||||
#define ___bpf_fill6(arr, p, x, args...) arr[p] = x; ___bpf_fill5(arr, p + 1, args)
|
||||
#define ___bpf_fill7(arr, p, x, args...) arr[p] = x; ___bpf_fill6(arr, p + 1, args)
|
||||
#define ___bpf_fill8(arr, p, x, args...) arr[p] = x; ___bpf_fill7(arr, p + 1, args)
|
||||
#define ___bpf_fill9(arr, p, x, args...) arr[p] = x; ___bpf_fill8(arr, p + 1, args)
|
||||
#define ___bpf_fill10(arr, p, x, args...) arr[p] = x; ___bpf_fill9(arr, p + 1, args)
|
||||
#define ___bpf_fill11(arr, p, x, args...) arr[p] = x; ___bpf_fill10(arr, p + 1, args)
|
||||
#define ___bpf_fill12(arr, p, x, args...) arr[p] = x; ___bpf_fill11(arr, p + 1, args)
|
||||
#define ___bpf_fill(arr, args...) \
|
||||
___bpf_apply(___bpf_fill, ___bpf_narg(args))(arr, 0, args)
|
||||
|
||||
/*
|
||||
* BPF_SEQ_PRINTF to wrap bpf_seq_printf to-be-printed values
|
||||
* in a structure.
|
||||
*/
|
||||
#define BPF_SEQ_PRINTF(seq, fmt, args...) \
|
||||
({ \
|
||||
static const char ___fmt[] = fmt; \
|
||||
unsigned long long ___param[___bpf_narg(args)]; \
|
||||
\
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
___bpf_fill(___param, args); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
\
|
||||
bpf_seq_printf(seq, ___fmt, sizeof(___fmt), \
|
||||
___param, sizeof(___param)); \
|
||||
})
|
||||
|
||||
/*
|
||||
* BPF_SNPRINTF wraps the bpf_snprintf helper with variadic arguments instead of
|
||||
* an array of u64.
|
||||
*/
|
||||
#define BPF_SNPRINTF(out, out_size, fmt, args...) \
|
||||
({ \
|
||||
static const char ___fmt[] = fmt; \
|
||||
unsigned long long ___param[___bpf_narg(args)]; \
|
||||
\
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
___bpf_fill(___param, args); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
\
|
||||
bpf_snprintf(out, out_size, ___fmt, \
|
||||
___param, sizeof(___param)); \
|
||||
})
|
||||
|
||||
#ifdef BPF_NO_GLOBAL_DATA
|
||||
#define BPF_PRINTK_FMT_MOD
|
||||
#else
|
||||
#define BPF_PRINTK_FMT_MOD static const
|
||||
#endif
|
||||
|
||||
#define __bpf_printk(fmt, ...) \
|
||||
({ \
|
||||
BPF_PRINTK_FMT_MOD char ____fmt[] = fmt; \
|
||||
bpf_trace_printk(____fmt, sizeof(____fmt), \
|
||||
##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
/*
|
||||
* __bpf_vprintk wraps the bpf_trace_vprintk helper with variadic arguments
|
||||
* instead of an array of u64.
|
||||
*/
|
||||
#define __bpf_vprintk(fmt, args...) \
|
||||
({ \
|
||||
static const char ___fmt[] = fmt; \
|
||||
unsigned long long ___param[___bpf_narg(args)]; \
|
||||
\
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
___bpf_fill(___param, args); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
\
|
||||
bpf_trace_vprintk(___fmt, sizeof(___fmt), \
|
||||
___param, sizeof(___param)); \
|
||||
})
|
||||
|
||||
/* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args
|
||||
* Otherwise use __bpf_vprintk
|
||||
*/
|
||||
#define ___bpf_pick_printk(...) \
|
||||
___bpf_nth(_, ##__VA_ARGS__, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \
|
||||
__bpf_vprintk, __bpf_vprintk, __bpf_vprintk, __bpf_vprintk, \
|
||||
__bpf_vprintk, __bpf_vprintk, __bpf_printk /*3*/, __bpf_printk /*2*/,\
|
||||
__bpf_printk /*1*/, __bpf_printk /*0*/)
|
||||
|
||||
/* Helper macro to print out debug messages */
|
||||
#define bpf_printk(fmt, args...) ___bpf_pick_printk(args)(fmt, ##args)
|
||||
|
||||
struct bpf_iter_num;
|
||||
|
||||
extern int bpf_iter_num_new(struct bpf_iter_num *it, int start, int end) __weak __ksym;
|
||||
extern int *bpf_iter_num_next(struct bpf_iter_num *it) __weak __ksym;
|
||||
extern void bpf_iter_num_destroy(struct bpf_iter_num *it) __weak __ksym;
|
||||
|
||||
#ifndef bpf_for_each
|
||||
/* bpf_for_each(iter_type, cur_elem, args...) provides generic construct for
|
||||
* using BPF open-coded iterators without having to write mundane explicit
|
||||
* low-level loop logic. Instead, it provides for()-like generic construct
|
||||
* that can be used pretty naturally. E.g., for some hypothetical cgroup
|
||||
* iterator, you'd write:
|
||||
*
|
||||
* struct cgroup *cg, *parent_cg = <...>;
|
||||
*
|
||||
* bpf_for_each(cgroup, cg, parent_cg, CG_ITER_CHILDREN) {
|
||||
* bpf_printk("Child cgroup id = %d", cg->cgroup_id);
|
||||
* if (cg->cgroup_id == 123)
|
||||
* break;
|
||||
* }
|
||||
*
|
||||
* I.e., it looks almost like high-level for each loop in other languages,
|
||||
* supports continue/break, and is verifiable by BPF verifier.
|
||||
*
|
||||
* For iterating integers, the difference betwen bpf_for_each(num, i, N, M)
|
||||
* and bpf_for(i, N, M) is in that bpf_for() provides additional proof to
|
||||
* verifier that i is in [N, M) range, and in bpf_for_each() case i is `int
|
||||
* *`, not just `int`. So for integers bpf_for() is more convenient.
|
||||
*
|
||||
* Note: this macro relies on C99 feature of allowing to declare variables
|
||||
* inside for() loop, bound to for() loop lifetime. It also utilizes GCC
|
||||
* extension: __attribute__((cleanup(<func>))), supported by both GCC and
|
||||
* Clang.
|
||||
*/
|
||||
#define bpf_for_each(type, cur, args...) for ( \
|
||||
/* initialize and define destructor */ \
|
||||
struct bpf_iter_##type ___it __attribute__((aligned(8), /* enforce, just in case */, \
|
||||
cleanup(bpf_iter_##type##_destroy))), \
|
||||
/* ___p pointer is just to call bpf_iter_##type##_new() *once* to init ___it */ \
|
||||
*___p __attribute__((unused)) = ( \
|
||||
bpf_iter_##type##_new(&___it, ##args), \
|
||||
/* this is a workaround for Clang bug: it currently doesn't emit BTF */ \
|
||||
/* for bpf_iter_##type##_destroy() when used from cleanup() attribute */ \
|
||||
(void)bpf_iter_##type##_destroy, (void *)0); \
|
||||
/* iteration and termination check */ \
|
||||
(((cur) = bpf_iter_##type##_next(&___it))); \
|
||||
)
|
||||
#endif /* bpf_for_each */
|
||||
|
||||
#ifndef bpf_for
|
||||
/* bpf_for(i, start, end) implements a for()-like looping construct that sets
|
||||
* provided integer variable *i* to values starting from *start* through,
|
||||
* but not including, *end*. It also proves to BPF verifier that *i* belongs
|
||||
* to range [start, end), so this can be used for accessing arrays without
|
||||
* extra checks.
|
||||
*
|
||||
* Note: *start* and *end* are assumed to be expressions with no side effects
|
||||
* and whose values do not change throughout bpf_for() loop execution. They do
|
||||
* not have to be statically known or constant, though.
|
||||
*
|
||||
* Note: similarly to bpf_for_each(), it relies on C99 feature of declaring for()
|
||||
* loop bound variables and cleanup attribute, supported by GCC and Clang.
|
||||
*/
|
||||
#define bpf_for(i, start, end) for ( \
|
||||
/* initialize and define destructor */ \
|
||||
struct bpf_iter_num ___it __attribute__((aligned(8), /* enforce, just in case */ \
|
||||
cleanup(bpf_iter_num_destroy))), \
|
||||
/* ___p pointer is necessary to call bpf_iter_num_new() *once* to init ___it */ \
|
||||
*___p __attribute__((unused)) = ( \
|
||||
bpf_iter_num_new(&___it, (start), (end)), \
|
||||
/* this is a workaround for Clang bug: it currently doesn't emit BTF */ \
|
||||
/* for bpf_iter_num_destroy() when used from cleanup() attribute */ \
|
||||
(void)bpf_iter_num_destroy, (void *)0); \
|
||||
({ \
|
||||
/* iteration step */ \
|
||||
int *___t = bpf_iter_num_next(&___it); \
|
||||
/* termination and bounds check */ \
|
||||
(___t && ((i) = *___t, (i) >= (start) && (i) < (end))); \
|
||||
}); \
|
||||
)
|
||||
#endif /* bpf_for */
|
||||
|
||||
#ifndef bpf_repeat
|
||||
/* bpf_repeat(N) performs N iterations without exposing iteration number
|
||||
*
|
||||
* Note: similarly to bpf_for_each(), it relies on C99 feature of declaring for()
|
||||
* loop bound variables and cleanup attribute, supported by GCC and Clang.
|
||||
*/
|
||||
#define bpf_repeat(N) for ( \
|
||||
/* initialize and define destructor */ \
|
||||
struct bpf_iter_num ___it __attribute__((aligned(8), /* enforce, just in case */ \
|
||||
cleanup(bpf_iter_num_destroy))), \
|
||||
/* ___p pointer is necessary to call bpf_iter_num_new() *once* to init ___it */ \
|
||||
*___p __attribute__((unused)) = ( \
|
||||
bpf_iter_num_new(&___it, 0, (N)), \
|
||||
/* this is a workaround for Clang bug: it currently doesn't emit BTF */ \
|
||||
/* for bpf_iter_num_destroy() when used from cleanup() attribute */ \
|
||||
(void)bpf_iter_num_destroy, (void *)0); \
|
||||
bpf_iter_num_next(&___it); \
|
||||
/* nothing here */ \
|
||||
)
|
||||
#endif /* bpf_repeat */
|
||||
|
||||
#endif
|
||||
924
firewall/interception/ebpf/programs/bpf/bpf_tracing.h
Normal file
924
firewall/interception/ebpf/programs/bpf/bpf_tracing.h
Normal file
@@ -0,0 +1,924 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __BPF_TRACING_H__
|
||||
#define __BPF_TRACING_H__
|
||||
|
||||
// #include <bpf/bpf_helpers.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
/* Scan the ARCH passed in from ARCH env variable (see Makefile) */
|
||||
#if defined(__TARGET_ARCH_x86)
|
||||
#define bpf_target_x86
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_s390)
|
||||
#define bpf_target_s390
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_arm)
|
||||
#define bpf_target_arm
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_arm64)
|
||||
#define bpf_target_arm64
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_mips)
|
||||
#define bpf_target_mips
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_powerpc)
|
||||
#define bpf_target_powerpc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_sparc)
|
||||
#define bpf_target_sparc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_riscv)
|
||||
#define bpf_target_riscv
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_arc)
|
||||
#define bpf_target_arc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__TARGET_ARCH_loongarch)
|
||||
#define bpf_target_loongarch
|
||||
#define bpf_target_defined
|
||||
#else
|
||||
|
||||
/* Fall back to what the compiler says */
|
||||
#if defined(__x86_64__)
|
||||
#define bpf_target_x86
|
||||
#define bpf_target_defined
|
||||
#elif defined(__s390__)
|
||||
#define bpf_target_s390
|
||||
#define bpf_target_defined
|
||||
#elif defined(__arm__)
|
||||
#define bpf_target_arm
|
||||
#define bpf_target_defined
|
||||
#elif defined(__aarch64__)
|
||||
#define bpf_target_arm64
|
||||
#define bpf_target_defined
|
||||
#elif defined(__mips__)
|
||||
#define bpf_target_mips
|
||||
#define bpf_target_defined
|
||||
#elif defined(__powerpc__)
|
||||
#define bpf_target_powerpc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__sparc__)
|
||||
#define bpf_target_sparc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__riscv) && __riscv_xlen == 64
|
||||
#define bpf_target_riscv
|
||||
#define bpf_target_defined
|
||||
#elif defined(__arc__)
|
||||
#define bpf_target_arc
|
||||
#define bpf_target_defined
|
||||
#elif defined(__loongarch__)
|
||||
#define bpf_target_loongarch
|
||||
#define bpf_target_defined
|
||||
#endif /* no compiler target */
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef __BPF_TARGET_MISSING
|
||||
#define __BPF_TARGET_MISSING "GCC error \"Must specify a BPF target arch via __TARGET_ARCH_xxx\""
|
||||
#endif
|
||||
|
||||
#if defined(bpf_target_x86)
|
||||
|
||||
/*
|
||||
* https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI
|
||||
*/
|
||||
|
||||
#if defined(__KERNEL__) || defined(__VMLINUX_H__)
|
||||
|
||||
#define __PT_PARM1_REG di
|
||||
#define __PT_PARM2_REG si
|
||||
#define __PT_PARM3_REG dx
|
||||
#define __PT_PARM4_REG cx
|
||||
#define __PT_PARM5_REG r8
|
||||
#define __PT_PARM6_REG r9
|
||||
/*
|
||||
* Syscall uses r10 for PARM4. See arch/x86/entry/entry_64.S:entry_SYSCALL_64
|
||||
* comments in Linux sources. And refer to syscall(2) manpage.
|
||||
*/
|
||||
#define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG
|
||||
#define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG
|
||||
#define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG
|
||||
#define __PT_PARM4_SYSCALL_REG r10
|
||||
#define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG
|
||||
#define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG
|
||||
|
||||
#define __PT_RET_REG sp
|
||||
#define __PT_FP_REG bp
|
||||
#define __PT_RC_REG ax
|
||||
#define __PT_SP_REG sp
|
||||
#define __PT_IP_REG ip
|
||||
|
||||
#else
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
/* i386 kernel is built with -mregparm=3 */
|
||||
#define __PT_PARM1_REG eax
|
||||
#define __PT_PARM2_REG edx
|
||||
#define __PT_PARM3_REG ecx
|
||||
/* i386 syscall ABI is very different, refer to syscall(2) manpage */
|
||||
#define __PT_PARM1_SYSCALL_REG ebx
|
||||
#define __PT_PARM2_SYSCALL_REG ecx
|
||||
#define __PT_PARM3_SYSCALL_REG edx
|
||||
#define __PT_PARM4_SYSCALL_REG esi
|
||||
#define __PT_PARM5_SYSCALL_REG edi
|
||||
#define __PT_PARM6_SYSCALL_REG ebp
|
||||
|
||||
#define __PT_RET_REG esp
|
||||
#define __PT_FP_REG ebp
|
||||
#define __PT_RC_REG eax
|
||||
#define __PT_SP_REG esp
|
||||
#define __PT_IP_REG eip
|
||||
|
||||
#else /* __i386__ */
|
||||
|
||||
#define __PT_PARM1_REG rdi
|
||||
#define __PT_PARM2_REG rsi
|
||||
#define __PT_PARM3_REG rdx
|
||||
#define __PT_PARM4_REG rcx
|
||||
#define __PT_PARM5_REG r8
|
||||
#define __PT_PARM6_REG r9
|
||||
|
||||
#define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG
|
||||
#define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG
|
||||
#define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG
|
||||
#define __PT_PARM4_SYSCALL_REG r10
|
||||
#define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG
|
||||
#define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG
|
||||
|
||||
#define __PT_RET_REG rsp
|
||||
#define __PT_FP_REG rbp
|
||||
#define __PT_RC_REG rax
|
||||
#define __PT_SP_REG rsp
|
||||
#define __PT_IP_REG rip
|
||||
|
||||
#endif /* __i386__ */
|
||||
|
||||
#endif /* __KERNEL__ || __VMLINUX_H__ */
|
||||
|
||||
#elif defined(bpf_target_s390)
|
||||
|
||||
/*
|
||||
* https://github.com/IBM/s390x-abi/releases/download/v1.6/lzsabi_s390x.pdf
|
||||
*/
|
||||
|
||||
struct pt_regs___s390 {
|
||||
unsigned long orig_gpr2;
|
||||
};
|
||||
|
||||
/* s390 provides user_pt_regs instead of struct pt_regs to userspace */
|
||||
#define __PT_REGS_CAST(x) ((const user_pt_regs *)(x))
|
||||
#define __PT_PARM1_REG gprs[2]
|
||||
#define __PT_PARM2_REG gprs[3]
|
||||
#define __PT_PARM3_REG gprs[4]
|
||||
#define __PT_PARM4_REG gprs[5]
|
||||
#define __PT_PARM5_REG gprs[6]
|
||||
|
||||
#define __PT_PARM1_SYSCALL_REG orig_gpr2
|
||||
#define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG
|
||||
#define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG
|
||||
#define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG
|
||||
#define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG
|
||||
#define __PT_PARM6_SYSCALL_REG gprs[7]
|
||||
#define PT_REGS_PARM1_SYSCALL(x) PT_REGS_PARM1_CORE_SYSCALL(x)
|
||||
#define PT_REGS_PARM1_CORE_SYSCALL(x) \
|
||||
BPF_CORE_READ((const struct pt_regs___s390 *)(x), __PT_PARM1_SYSCALL_REG)
|
||||
|
||||
#define __PT_RET_REG gprs[14]
|
||||
#define __PT_FP_REG gprs[11] /* Works only with CONFIG_FRAME_POINTER */
|
||||
#define __PT_RC_REG gprs[2]
|
||||
#define __PT_SP_REG gprs[15]
|
||||
#define __PT_IP_REG psw.addr
|
||||
|
||||
#elif defined(bpf_target_arm)
|
||||
|
||||
/*
|
||||
* https://github.com/ARM-software/abi-aa/blob/main/aapcs32/aapcs32.rst#machine-registers
|
||||
*/
|
||||
|
||||
#define __PT_PARM1_REG uregs[0]
|
||||
#define __PT_PARM2_REG uregs[1]
|
||||
#define __PT_PARM3_REG uregs[2]
|
||||
#define __PT_PARM4_REG uregs[3]
|
||||
|
||||
#define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG
|
||||
#define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG
|
||||
#define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG
|
||||
#define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG
|
||||
#define __PT_PARM5_SYSCALL_REG uregs[4]
|
||||
#define __PT_PARM6_SYSCALL_REG uregs[5]
|
||||
#define __PT_PARM7_SYSCALL_REG uregs[6]
|
||||
|
||||
#define __PT_RET_REG uregs[14]
|
||||
#define __PT_FP_REG uregs[11] /* Works only with CONFIG_FRAME_POINTER */
|
||||
#define __PT_RC_REG uregs[0]
|
||||
#define __PT_SP_REG uregs[13]
|
||||
#define __PT_IP_REG uregs[12]
|
||||
|
||||
#elif defined(bpf_target_arm64)
|
||||
|
||||
/*
|
||||
* https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#machine-registers
|
||||
*/
|
||||
|
||||
struct pt_regs___arm64 {
|
||||
unsigned long orig_x0;
|
||||
};
|
||||
|
||||
/* arm64 provides struct user_pt_regs instead of struct pt_regs to userspace */
|
||||
#define __PT_REGS_CAST(x) ((const struct user_pt_regs *)(x))
|
||||
#define __PT_PARM1_REG regs[0]
|
||||
#define __PT_PARM2_REG regs[1]
|
||||
#define __PT_PARM3_REG regs[2]
|
||||
#define __PT_PARM4_REG regs[3]
|
||||
#define __PT_PARM5_REG regs[4]
|
||||
#define __PT_PARM6_REG regs[5]
|
||||
#define __PT_PARM7_REG regs[6]
|
||||
#define __PT_PARM8_REG regs[7]
|
||||
|
||||
#define __PT_PARM1_SYSCALL_REG orig_x0
|
||||
#define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG
|
||||
#define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG
|
||||
#define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG
|
||||
#define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG
|
||||
#define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG
|
||||
#define PT_REGS_PARM1_SYSCALL(x) PT_REGS_PARM1_CORE_SYSCALL(x)
|
||||
#define PT_REGS_PARM1_CORE_SYSCALL(x) \
|
||||
BPF_CORE_READ((const struct pt_regs___arm64 *)(x), __PT_PARM1_SYSCALL_REG)
|
||||
|
||||
#define __PT_RET_REG regs[30]
|
||||
#define __PT_FP_REG regs[29] /* Works only with CONFIG_FRAME_POINTER */
|
||||
#define __PT_RC_REG regs[0]
|
||||
#define __PT_SP_REG sp
|
||||
#define __PT_IP_REG pc
|
||||
|
||||
#elif defined(bpf_target_mips)
|
||||
|
||||
/*
|
||||
* N64 ABI is assumed right now.
|
||||
* https://en.wikipedia.org/wiki/MIPS_architecture#Calling_conventions
|
||||
*/
|
||||
|
||||
#define __PT_PARM1_REG regs[4]
|
||||
#define __PT_PARM2_REG regs[5]
|
||||
#define __PT_PARM3_REG regs[6]
|
||||
#define __PT_PARM4_REG regs[7]
|
||||
#define __PT_PARM5_REG regs[8]
|
||||
#define __PT_PARM6_REG regs[9]
|
||||
#define __PT_PARM7_REG regs[10]
|
||||
#define __PT_PARM8_REG regs[11]
|
||||
|
||||
#define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG
|
||||
#define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG
|
||||
#define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG
|
||||
#define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG
|
||||
#define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG /* only N32/N64 */
|
||||
#define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG /* only N32/N64 */
|
||||
|
||||
#define __PT_RET_REG regs[31]
|
||||
#define __PT_FP_REG regs[30] /* Works only with CONFIG_FRAME_POINTER */
|
||||
#define __PT_RC_REG regs[2]
|
||||
#define __PT_SP_REG regs[29]
|
||||
#define __PT_IP_REG cp0_epc
|
||||
|
||||
#elif defined(bpf_target_powerpc)
|
||||
|
||||
/*
|
||||
* http://refspecs.linux-foundation.org/elf/elfspec_ppc.pdf (page 3-14,
|
||||
* section "Function Calling Sequence")
|
||||
*/
|
||||
|
||||
#define __PT_PARM1_REG gpr[3]
|
||||
#define __PT_PARM2_REG gpr[4]
|
||||
#define __PT_PARM3_REG gpr[5]
|
||||
#define __PT_PARM4_REG gpr[6]
|
||||
#define __PT_PARM5_REG gpr[7]
|
||||
#define __PT_PARM6_REG gpr[8]
|
||||
#define __PT_PARM7_REG gpr[9]
|
||||
#define __PT_PARM8_REG gpr[10]
|
||||
|
||||
/* powerpc does not select ARCH_HAS_SYSCALL_WRAPPER. */
|
||||
#define PT_REGS_SYSCALL_REGS(ctx) ctx
|
||||
#define __PT_PARM1_SYSCALL_REG orig_gpr3
|
||||
#define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG
|
||||
#define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG
|
||||
#define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG
|
||||
#define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG
|
||||
#define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG
|
||||
#if !defined(__arch64__)
|
||||
#define __PT_PARM7_SYSCALL_REG __PT_PARM7_REG /* only powerpc (not powerpc64) */
|
||||
#endif
|
||||
|
||||
#define __PT_RET_REG regs[31]
|
||||
#define __PT_FP_REG __unsupported__
|
||||
#define __PT_RC_REG gpr[3]
|
||||
#define __PT_SP_REG sp
|
||||
#define __PT_IP_REG nip
|
||||
|
||||
#elif defined(bpf_target_sparc)
|
||||
|
||||
/*
|
||||
* https://en.wikipedia.org/wiki/Calling_convention#SPARC
|
||||
*/
|
||||
|
||||
#define __PT_PARM1_REG u_regs[UREG_I0]
|
||||
#define __PT_PARM2_REG u_regs[UREG_I1]
|
||||
#define __PT_PARM3_REG u_regs[UREG_I2]
|
||||
#define __PT_PARM4_REG u_regs[UREG_I3]
|
||||
#define __PT_PARM5_REG u_regs[UREG_I4]
|
||||
#define __PT_PARM6_REG u_regs[UREG_I5]
|
||||
|
||||
#define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG
|
||||
#define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG
|
||||
#define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG
|
||||
#define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG
|
||||
#define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG
|
||||
#define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG
|
||||
|
||||
#define __PT_RET_REG u_regs[UREG_I7]
|
||||
#define __PT_FP_REG __unsupported__
|
||||
#define __PT_RC_REG u_regs[UREG_I0]
|
||||
#define __PT_SP_REG u_regs[UREG_FP]
|
||||
/* Should this also be a bpf_target check for the sparc case? */
|
||||
#if defined(__arch64__)
|
||||
#define __PT_IP_REG tpc
|
||||
#else
|
||||
#define __PT_IP_REG pc
|
||||
#endif
|
||||
|
||||
#elif defined(bpf_target_riscv)
|
||||
|
||||
/*
|
||||
* https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#risc-v-calling-conventions
|
||||
*/
|
||||
|
||||
#define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x))
|
||||
#define __PT_PARM1_REG a0
|
||||
#define __PT_PARM2_REG a1
|
||||
#define __PT_PARM3_REG a2
|
||||
#define __PT_PARM4_REG a3
|
||||
#define __PT_PARM5_REG a4
|
||||
#define __PT_PARM6_REG a5
|
||||
#define __PT_PARM7_REG a6
|
||||
#define __PT_PARM8_REG a7
|
||||
|
||||
/* riscv does not select ARCH_HAS_SYSCALL_WRAPPER. */
|
||||
#define PT_REGS_SYSCALL_REGS(ctx) ctx
|
||||
#define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG
|
||||
#define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG
|
||||
#define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG
|
||||
#define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG
|
||||
#define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG
|
||||
#define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG
|
||||
|
||||
#define __PT_RET_REG ra
|
||||
#define __PT_FP_REG s0
|
||||
#define __PT_RC_REG a0
|
||||
#define __PT_SP_REG sp
|
||||
#define __PT_IP_REG pc
|
||||
|
||||
#elif defined(bpf_target_arc)
|
||||
|
||||
/*
|
||||
* Section "Function Calling Sequence" (page 24):
|
||||
* https://raw.githubusercontent.com/wiki/foss-for-synopsys-dwc-arc-processors/toolchain/files/ARCv2_ABI.pdf
|
||||
*/
|
||||
|
||||
/* arc provides struct user_pt_regs instead of struct pt_regs to userspace */
|
||||
#define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x))
|
||||
#define __PT_PARM1_REG scratch.r0
|
||||
#define __PT_PARM2_REG scratch.r1
|
||||
#define __PT_PARM3_REG scratch.r2
|
||||
#define __PT_PARM4_REG scratch.r3
|
||||
#define __PT_PARM5_REG scratch.r4
|
||||
#define __PT_PARM6_REG scratch.r5
|
||||
#define __PT_PARM7_REG scratch.r6
|
||||
#define __PT_PARM8_REG scratch.r7
|
||||
|
||||
/* arc does not select ARCH_HAS_SYSCALL_WRAPPER. */
|
||||
#define PT_REGS_SYSCALL_REGS(ctx) ctx
|
||||
#define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG
|
||||
#define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG
|
||||
#define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG
|
||||
#define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG
|
||||
#define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG
|
||||
#define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG
|
||||
|
||||
#define __PT_RET_REG scratch.blink
|
||||
#define __PT_FP_REG scratch.fp
|
||||
#define __PT_RC_REG scratch.r0
|
||||
#define __PT_SP_REG scratch.sp
|
||||
#define __PT_IP_REG scratch.ret
|
||||
|
||||
#elif defined(bpf_target_loongarch)
|
||||
|
||||
/*
|
||||
* https://docs.kernel.org/loongarch/introduction.html
|
||||
* https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html
|
||||
*/
|
||||
|
||||
/* loongarch provides struct user_pt_regs instead of struct pt_regs to userspace */
|
||||
#define __PT_REGS_CAST(x) ((const struct user_pt_regs *)(x))
|
||||
#define __PT_PARM1_REG regs[4]
|
||||
#define __PT_PARM2_REG regs[5]
|
||||
#define __PT_PARM3_REG regs[6]
|
||||
#define __PT_PARM4_REG regs[7]
|
||||
#define __PT_PARM5_REG regs[8]
|
||||
#define __PT_PARM6_REG regs[9]
|
||||
#define __PT_PARM7_REG regs[10]
|
||||
#define __PT_PARM8_REG regs[11]
|
||||
|
||||
/* loongarch does not select ARCH_HAS_SYSCALL_WRAPPER. */
|
||||
#define PT_REGS_SYSCALL_REGS(ctx) ctx
|
||||
#define __PT_PARM1_SYSCALL_REG __PT_PARM1_REG
|
||||
#define __PT_PARM2_SYSCALL_REG __PT_PARM2_REG
|
||||
#define __PT_PARM3_SYSCALL_REG __PT_PARM3_REG
|
||||
#define __PT_PARM4_SYSCALL_REG __PT_PARM4_REG
|
||||
#define __PT_PARM5_SYSCALL_REG __PT_PARM5_REG
|
||||
#define __PT_PARM6_SYSCALL_REG __PT_PARM6_REG
|
||||
|
||||
#define __PT_RET_REG regs[1]
|
||||
#define __PT_FP_REG regs[22]
|
||||
#define __PT_RC_REG regs[4]
|
||||
#define __PT_SP_REG regs[3]
|
||||
#define __PT_IP_REG csr_era
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(bpf_target_defined)
|
||||
|
||||
struct pt_regs;
|
||||
|
||||
/* allow some architectures to override `struct pt_regs` */
|
||||
#ifndef __PT_REGS_CAST
|
||||
#define __PT_REGS_CAST(x) (x)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Different architectures support different number of arguments passed
|
||||
* through registers. i386 supports just 3, some arches support up to 8.
|
||||
*/
|
||||
#ifndef __PT_PARM4_REG
|
||||
#define __PT_PARM4_REG __unsupported__
|
||||
#endif
|
||||
#ifndef __PT_PARM5_REG
|
||||
#define __PT_PARM5_REG __unsupported__
|
||||
#endif
|
||||
#ifndef __PT_PARM6_REG
|
||||
#define __PT_PARM6_REG __unsupported__
|
||||
#endif
|
||||
#ifndef __PT_PARM7_REG
|
||||
#define __PT_PARM7_REG __unsupported__
|
||||
#endif
|
||||
#ifndef __PT_PARM8_REG
|
||||
#define __PT_PARM8_REG __unsupported__
|
||||
#endif
|
||||
/*
|
||||
* Similarly, syscall-specific conventions might differ between function call
|
||||
* conventions within each architecutre. All supported architectures pass
|
||||
* either 6 or 7 syscall arguments in registers.
|
||||
*
|
||||
* See syscall(2) manpage for succinct table with information on each arch.
|
||||
*/
|
||||
#ifndef __PT_PARM7_SYSCALL_REG
|
||||
#define __PT_PARM7_SYSCALL_REG __unsupported__
|
||||
#endif
|
||||
|
||||
#define PT_REGS_PARM1(x) (__PT_REGS_CAST(x)->__PT_PARM1_REG)
|
||||
#define PT_REGS_PARM2(x) (__PT_REGS_CAST(x)->__PT_PARM2_REG)
|
||||
#define PT_REGS_PARM3(x) (__PT_REGS_CAST(x)->__PT_PARM3_REG)
|
||||
#define PT_REGS_PARM4(x) (__PT_REGS_CAST(x)->__PT_PARM4_REG)
|
||||
#define PT_REGS_PARM5(x) (__PT_REGS_CAST(x)->__PT_PARM5_REG)
|
||||
#define PT_REGS_PARM6(x) (__PT_REGS_CAST(x)->__PT_PARM6_REG)
|
||||
#define PT_REGS_PARM7(x) (__PT_REGS_CAST(x)->__PT_PARM7_REG)
|
||||
#define PT_REGS_PARM8(x) (__PT_REGS_CAST(x)->__PT_PARM8_REG)
|
||||
#define PT_REGS_RET(x) (__PT_REGS_CAST(x)->__PT_RET_REG)
|
||||
#define PT_REGS_FP(x) (__PT_REGS_CAST(x)->__PT_FP_REG)
|
||||
#define PT_REGS_RC(x) (__PT_REGS_CAST(x)->__PT_RC_REG)
|
||||
#define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG)
|
||||
#define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG)
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM1_REG)
|
||||
#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM2_REG)
|
||||
#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM3_REG)
|
||||
#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM4_REG)
|
||||
#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM5_REG)
|
||||
#define PT_REGS_PARM6_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM6_REG)
|
||||
#define PT_REGS_PARM7_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM7_REG)
|
||||
#define PT_REGS_PARM8_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM8_REG)
|
||||
#define PT_REGS_RET_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_RET_REG)
|
||||
#define PT_REGS_FP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_FP_REG)
|
||||
#define PT_REGS_RC_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_RC_REG)
|
||||
#define PT_REGS_SP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_SP_REG)
|
||||
#define PT_REGS_IP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_IP_REG)
|
||||
|
||||
#if defined(bpf_target_powerpc)
|
||||
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; })
|
||||
#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
|
||||
|
||||
#elif defined(bpf_target_sparc)
|
||||
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); })
|
||||
#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP
|
||||
|
||||
#else
|
||||
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) \
|
||||
({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); })
|
||||
#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) \
|
||||
({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)(PT_REGS_FP(ctx) + sizeof(ip))); })
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef PT_REGS_PARM1_SYSCALL
|
||||
#define PT_REGS_PARM1_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM1_SYSCALL_REG)
|
||||
#define PT_REGS_PARM1_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM1_SYSCALL_REG)
|
||||
#endif
|
||||
#ifndef PT_REGS_PARM2_SYSCALL
|
||||
#define PT_REGS_PARM2_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM2_SYSCALL_REG)
|
||||
#define PT_REGS_PARM2_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM2_SYSCALL_REG)
|
||||
#endif
|
||||
#ifndef PT_REGS_PARM3_SYSCALL
|
||||
#define PT_REGS_PARM3_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM3_SYSCALL_REG)
|
||||
#define PT_REGS_PARM3_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM3_SYSCALL_REG)
|
||||
#endif
|
||||
#ifndef PT_REGS_PARM4_SYSCALL
|
||||
#define PT_REGS_PARM4_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM4_SYSCALL_REG)
|
||||
#define PT_REGS_PARM4_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM4_SYSCALL_REG)
|
||||
#endif
|
||||
#ifndef PT_REGS_PARM5_SYSCALL
|
||||
#define PT_REGS_PARM5_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM5_SYSCALL_REG)
|
||||
#define PT_REGS_PARM5_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM5_SYSCALL_REG)
|
||||
#endif
|
||||
#ifndef PT_REGS_PARM6_SYSCALL
|
||||
#define PT_REGS_PARM6_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM6_SYSCALL_REG)
|
||||
#define PT_REGS_PARM6_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM6_SYSCALL_REG)
|
||||
#endif
|
||||
#ifndef PT_REGS_PARM7_SYSCALL
|
||||
#define PT_REGS_PARM7_SYSCALL(x) (__PT_REGS_CAST(x)->__PT_PARM7_SYSCALL_REG)
|
||||
#define PT_REGS_PARM7_CORE_SYSCALL(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM7_SYSCALL_REG)
|
||||
#endif
|
||||
|
||||
#else /* defined(bpf_target_defined) */
|
||||
|
||||
#define PT_REGS_PARM1(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM2(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM3(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM4(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM5(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM6(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM7(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM8(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_RET(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_FP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_RC(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_SP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_IP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
|
||||
#define PT_REGS_PARM1_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM2_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM3_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM4_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM5_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM6_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM7_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM8_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_RET_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_FP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_RC_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_SP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_IP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
|
||||
#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
|
||||
#define PT_REGS_PARM1_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM2_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM3_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM4_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM5_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM6_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM7_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
|
||||
#define PT_REGS_PARM1_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM2_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM3_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM4_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM5_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM6_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
#define PT_REGS_PARM7_CORE_SYSCALL(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
|
||||
|
||||
#endif /* defined(bpf_target_defined) */
|
||||
|
||||
/*
|
||||
* When invoked from a syscall handler kprobe, returns a pointer to a
|
||||
* struct pt_regs containing syscall arguments and suitable for passing to
|
||||
* PT_REGS_PARMn_SYSCALL() and PT_REGS_PARMn_CORE_SYSCALL().
|
||||
*/
|
||||
#ifndef PT_REGS_SYSCALL_REGS
|
||||
/* By default, assume that the arch selects ARCH_HAS_SYSCALL_WRAPPER. */
|
||||
#define PT_REGS_SYSCALL_REGS(ctx) ((struct pt_regs *)PT_REGS_PARM1(ctx))
|
||||
#endif
|
||||
|
||||
#ifndef ___bpf_concat
|
||||
#define ___bpf_concat(a, b) a ## b
|
||||
#endif
|
||||
#ifndef ___bpf_apply
|
||||
#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)
|
||||
#endif
|
||||
#ifndef ___bpf_nth
|
||||
#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N
|
||||
#endif
|
||||
#ifndef ___bpf_narg
|
||||
#define ___bpf_narg(...) ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
|
||||
#endif
|
||||
|
||||
#define ___bpf_ctx_cast0() ctx
|
||||
#define ___bpf_ctx_cast1(x) ___bpf_ctx_cast0(), (void *)ctx[0]
|
||||
#define ___bpf_ctx_cast2(x, args...) ___bpf_ctx_cast1(args), (void *)ctx[1]
|
||||
#define ___bpf_ctx_cast3(x, args...) ___bpf_ctx_cast2(args), (void *)ctx[2]
|
||||
#define ___bpf_ctx_cast4(x, args...) ___bpf_ctx_cast3(args), (void *)ctx[3]
|
||||
#define ___bpf_ctx_cast5(x, args...) ___bpf_ctx_cast4(args), (void *)ctx[4]
|
||||
#define ___bpf_ctx_cast6(x, args...) ___bpf_ctx_cast5(args), (void *)ctx[5]
|
||||
#define ___bpf_ctx_cast7(x, args...) ___bpf_ctx_cast6(args), (void *)ctx[6]
|
||||
#define ___bpf_ctx_cast8(x, args...) ___bpf_ctx_cast7(args), (void *)ctx[7]
|
||||
#define ___bpf_ctx_cast9(x, args...) ___bpf_ctx_cast8(args), (void *)ctx[8]
|
||||
#define ___bpf_ctx_cast10(x, args...) ___bpf_ctx_cast9(args), (void *)ctx[9]
|
||||
#define ___bpf_ctx_cast11(x, args...) ___bpf_ctx_cast10(args), (void *)ctx[10]
|
||||
#define ___bpf_ctx_cast12(x, args...) ___bpf_ctx_cast11(args), (void *)ctx[11]
|
||||
#define ___bpf_ctx_cast(args...) ___bpf_apply(___bpf_ctx_cast, ___bpf_narg(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_PROG is a convenience wrapper for generic tp_btf/fentry/fexit and
|
||||
* similar kinds of BPF programs, that accept input arguments as a single
|
||||
* pointer to untyped u64 array, where each u64 can actually be a typed
|
||||
* pointer or integer of different size. Instead of requring user to write
|
||||
* manual casts and work with array elements by index, BPF_PROG macro
|
||||
* allows user to declare a list of named and typed input arguments in the
|
||||
* same syntax as for normal C function. All the casting is hidden and
|
||||
* performed transparently, while user code can just assume working with
|
||||
* function arguments of specified type and name.
|
||||
*
|
||||
* Original raw context argument is preserved as well as 'ctx' argument.
|
||||
* This is useful when using BPF helpers that expect original context
|
||||
* as one of the parameters (e.g., for bpf_perf_event_output()).
|
||||
*/
|
||||
#define BPF_PROG(name, args...) \
|
||||
name(unsigned long long *ctx); \
|
||||
static __always_inline typeof(name(0)) \
|
||||
____##name(unsigned long long *ctx, ##args); \
|
||||
typeof(name(0)) name(unsigned long long *ctx) \
|
||||
{ \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
return ____##name(___bpf_ctx_cast(args)); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
} \
|
||||
static __always_inline typeof(name(0)) \
|
||||
____##name(unsigned long long *ctx, ##args)
|
||||
|
||||
#ifndef ___bpf_nth2
|
||||
#define ___bpf_nth2(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, \
|
||||
_14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, N, ...) N
|
||||
#endif
|
||||
#ifndef ___bpf_narg2
|
||||
#define ___bpf_narg2(...) \
|
||||
___bpf_nth2(_, ##__VA_ARGS__, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, \
|
||||
6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0)
|
||||
#endif
|
||||
|
||||
#define ___bpf_treg_cnt(t) \
|
||||
__builtin_choose_expr(sizeof(t) == 1, 1, \
|
||||
__builtin_choose_expr(sizeof(t) == 2, 1, \
|
||||
__builtin_choose_expr(sizeof(t) == 4, 1, \
|
||||
__builtin_choose_expr(sizeof(t) == 8, 1, \
|
||||
__builtin_choose_expr(sizeof(t) == 16, 2, \
|
||||
(void)0)))))
|
||||
|
||||
#define ___bpf_reg_cnt0() (0)
|
||||
#define ___bpf_reg_cnt1(t, x) (___bpf_reg_cnt0() + ___bpf_treg_cnt(t))
|
||||
#define ___bpf_reg_cnt2(t, x, args...) (___bpf_reg_cnt1(args) + ___bpf_treg_cnt(t))
|
||||
#define ___bpf_reg_cnt3(t, x, args...) (___bpf_reg_cnt2(args) + ___bpf_treg_cnt(t))
|
||||
#define ___bpf_reg_cnt4(t, x, args...) (___bpf_reg_cnt3(args) + ___bpf_treg_cnt(t))
|
||||
#define ___bpf_reg_cnt5(t, x, args...) (___bpf_reg_cnt4(args) + ___bpf_treg_cnt(t))
|
||||
#define ___bpf_reg_cnt6(t, x, args...) (___bpf_reg_cnt5(args) + ___bpf_treg_cnt(t))
|
||||
#define ___bpf_reg_cnt7(t, x, args...) (___bpf_reg_cnt6(args) + ___bpf_treg_cnt(t))
|
||||
#define ___bpf_reg_cnt8(t, x, args...) (___bpf_reg_cnt7(args) + ___bpf_treg_cnt(t))
|
||||
#define ___bpf_reg_cnt9(t, x, args...) (___bpf_reg_cnt8(args) + ___bpf_treg_cnt(t))
|
||||
#define ___bpf_reg_cnt10(t, x, args...) (___bpf_reg_cnt9(args) + ___bpf_treg_cnt(t))
|
||||
#define ___bpf_reg_cnt11(t, x, args...) (___bpf_reg_cnt10(args) + ___bpf_treg_cnt(t))
|
||||
#define ___bpf_reg_cnt12(t, x, args...) (___bpf_reg_cnt11(args) + ___bpf_treg_cnt(t))
|
||||
#define ___bpf_reg_cnt(args...) ___bpf_apply(___bpf_reg_cnt, ___bpf_narg2(args))(args)
|
||||
|
||||
#define ___bpf_union_arg(t, x, n) \
|
||||
__builtin_choose_expr(sizeof(t) == 1, ({ union { __u8 z[1]; t x; } ___t = { .z = {ctx[n]}}; ___t.x; }), \
|
||||
__builtin_choose_expr(sizeof(t) == 2, ({ union { __u16 z[1]; t x; } ___t = { .z = {ctx[n]} }; ___t.x; }), \
|
||||
__builtin_choose_expr(sizeof(t) == 4, ({ union { __u32 z[1]; t x; } ___t = { .z = {ctx[n]} }; ___t.x; }), \
|
||||
__builtin_choose_expr(sizeof(t) == 8, ({ union { __u64 z[1]; t x; } ___t = {.z = {ctx[n]} }; ___t.x; }), \
|
||||
__builtin_choose_expr(sizeof(t) == 16, ({ union { __u64 z[2]; t x; } ___t = {.z = {ctx[n], ctx[n + 1]} }; ___t.x; }), \
|
||||
(void)0)))))
|
||||
|
||||
#define ___bpf_ctx_arg0(n, args...)
|
||||
#define ___bpf_ctx_arg1(n, t, x) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt1(t, x))
|
||||
#define ___bpf_ctx_arg2(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt2(t, x, args)) ___bpf_ctx_arg1(n, args)
|
||||
#define ___bpf_ctx_arg3(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt3(t, x, args)) ___bpf_ctx_arg2(n, args)
|
||||
#define ___bpf_ctx_arg4(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt4(t, x, args)) ___bpf_ctx_arg3(n, args)
|
||||
#define ___bpf_ctx_arg5(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt5(t, x, args)) ___bpf_ctx_arg4(n, args)
|
||||
#define ___bpf_ctx_arg6(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt6(t, x, args)) ___bpf_ctx_arg5(n, args)
|
||||
#define ___bpf_ctx_arg7(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt7(t, x, args)) ___bpf_ctx_arg6(n, args)
|
||||
#define ___bpf_ctx_arg8(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt8(t, x, args)) ___bpf_ctx_arg7(n, args)
|
||||
#define ___bpf_ctx_arg9(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt9(t, x, args)) ___bpf_ctx_arg8(n, args)
|
||||
#define ___bpf_ctx_arg10(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt10(t, x, args)) ___bpf_ctx_arg9(n, args)
|
||||
#define ___bpf_ctx_arg11(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt11(t, x, args)) ___bpf_ctx_arg10(n, args)
|
||||
#define ___bpf_ctx_arg12(n, t, x, args...) , ___bpf_union_arg(t, x, n - ___bpf_reg_cnt12(t, x, args)) ___bpf_ctx_arg11(n, args)
|
||||
#define ___bpf_ctx_arg(args...) ___bpf_apply(___bpf_ctx_arg, ___bpf_narg2(args))(___bpf_reg_cnt(args), args)
|
||||
|
||||
#define ___bpf_ctx_decl0()
|
||||
#define ___bpf_ctx_decl1(t, x) , t x
|
||||
#define ___bpf_ctx_decl2(t, x, args...) , t x ___bpf_ctx_decl1(args)
|
||||
#define ___bpf_ctx_decl3(t, x, args...) , t x ___bpf_ctx_decl2(args)
|
||||
#define ___bpf_ctx_decl4(t, x, args...) , t x ___bpf_ctx_decl3(args)
|
||||
#define ___bpf_ctx_decl5(t, x, args...) , t x ___bpf_ctx_decl4(args)
|
||||
#define ___bpf_ctx_decl6(t, x, args...) , t x ___bpf_ctx_decl5(args)
|
||||
#define ___bpf_ctx_decl7(t, x, args...) , t x ___bpf_ctx_decl6(args)
|
||||
#define ___bpf_ctx_decl8(t, x, args...) , t x ___bpf_ctx_decl7(args)
|
||||
#define ___bpf_ctx_decl9(t, x, args...) , t x ___bpf_ctx_decl8(args)
|
||||
#define ___bpf_ctx_decl10(t, x, args...) , t x ___bpf_ctx_decl9(args)
|
||||
#define ___bpf_ctx_decl11(t, x, args...) , t x ___bpf_ctx_decl10(args)
|
||||
#define ___bpf_ctx_decl12(t, x, args...) , t x ___bpf_ctx_decl11(args)
|
||||
#define ___bpf_ctx_decl(args...) ___bpf_apply(___bpf_ctx_decl, ___bpf_narg2(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_PROG2 is an enhanced version of BPF_PROG in order to handle struct
|
||||
* arguments. Since each struct argument might take one or two u64 values
|
||||
* in the trampoline stack, argument type size is needed to place proper number
|
||||
* of u64 values for each argument. Therefore, BPF_PROG2 has different
|
||||
* syntax from BPF_PROG. For example, for the following BPF_PROG syntax:
|
||||
*
|
||||
* int BPF_PROG(test2, int a, int b) { ... }
|
||||
*
|
||||
* the corresponding BPF_PROG2 syntax is:
|
||||
*
|
||||
* int BPF_PROG2(test2, int, a, int, b) { ... }
|
||||
*
|
||||
* where type and the corresponding argument name are separated by comma.
|
||||
*
|
||||
* Use BPF_PROG2 macro if one of the arguments might be a struct/union larger
|
||||
* than 8 bytes:
|
||||
*
|
||||
* int BPF_PROG2(test_struct_arg, struct bpf_testmod_struct_arg_1, a, int, b,
|
||||
* int, c, int, d, struct bpf_testmod_struct_arg_2, e, int, ret)
|
||||
* {
|
||||
* // access a, b, c, d, e, and ret directly
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
#define BPF_PROG2(name, args...) \
|
||||
name(unsigned long long *ctx); \
|
||||
static __always_inline typeof(name(0)) \
|
||||
____##name(unsigned long long *ctx ___bpf_ctx_decl(args)); \
|
||||
typeof(name(0)) name(unsigned long long *ctx) \
|
||||
{ \
|
||||
return ____##name(ctx ___bpf_ctx_arg(args)); \
|
||||
} \
|
||||
static __always_inline typeof(name(0)) \
|
||||
____##name(unsigned long long *ctx ___bpf_ctx_decl(args))
|
||||
|
||||
struct pt_regs;
|
||||
|
||||
#define ___bpf_kprobe_args0() ctx
|
||||
#define ___bpf_kprobe_args1(x) ___bpf_kprobe_args0(), (void *)PT_REGS_PARM1(ctx)
|
||||
#define ___bpf_kprobe_args2(x, args...) ___bpf_kprobe_args1(args), (void *)PT_REGS_PARM2(ctx)
|
||||
#define ___bpf_kprobe_args3(x, args...) ___bpf_kprobe_args2(args), (void *)PT_REGS_PARM3(ctx)
|
||||
#define ___bpf_kprobe_args4(x, args...) ___bpf_kprobe_args3(args), (void *)PT_REGS_PARM4(ctx)
|
||||
#define ___bpf_kprobe_args5(x, args...) ___bpf_kprobe_args4(args), (void *)PT_REGS_PARM5(ctx)
|
||||
#define ___bpf_kprobe_args6(x, args...) ___bpf_kprobe_args5(args), (void *)PT_REGS_PARM6(ctx)
|
||||
#define ___bpf_kprobe_args7(x, args...) ___bpf_kprobe_args6(args), (void *)PT_REGS_PARM7(ctx)
|
||||
#define ___bpf_kprobe_args8(x, args...) ___bpf_kprobe_args7(args), (void *)PT_REGS_PARM8(ctx)
|
||||
#define ___bpf_kprobe_args(args...) ___bpf_apply(___bpf_kprobe_args, ___bpf_narg(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_KPROBE serves the same purpose for kprobes as BPF_PROG for
|
||||
* tp_btf/fentry/fexit BPF programs. It hides the underlying platform-specific
|
||||
* low-level way of getting kprobe input arguments from struct pt_regs, and
|
||||
* provides a familiar typed and named function arguments syntax and
|
||||
* semantics of accessing kprobe input paremeters.
|
||||
*
|
||||
* Original struct pt_regs* context is preserved as 'ctx' argument. This might
|
||||
* be necessary when using BPF helpers like bpf_perf_event_output().
|
||||
*/
|
||||
#define BPF_KPROBE(name, args...) \
|
||||
name(struct pt_regs *ctx); \
|
||||
static __always_inline typeof(name(0)) \
|
||||
____##name(struct pt_regs *ctx, ##args); \
|
||||
typeof(name(0)) name(struct pt_regs *ctx) \
|
||||
{ \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
return ____##name(___bpf_kprobe_args(args)); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
} \
|
||||
static __always_inline typeof(name(0)) \
|
||||
____##name(struct pt_regs *ctx, ##args)
|
||||
|
||||
#define ___bpf_kretprobe_args0() ctx
|
||||
#define ___bpf_kretprobe_args1(x) ___bpf_kretprobe_args0(), (void *)PT_REGS_RC(ctx)
|
||||
#define ___bpf_kretprobe_args(args...) ___bpf_apply(___bpf_kretprobe_args, ___bpf_narg(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_KRETPROBE is similar to BPF_KPROBE, except, it only provides optional
|
||||
* return value (in addition to `struct pt_regs *ctx`), but no input
|
||||
* arguments, because they will be clobbered by the time probed function
|
||||
* returns.
|
||||
*/
|
||||
#define BPF_KRETPROBE(name, args...) \
|
||||
name(struct pt_regs *ctx); \
|
||||
static __always_inline typeof(name(0)) \
|
||||
____##name(struct pt_regs *ctx, ##args); \
|
||||
typeof(name(0)) name(struct pt_regs *ctx) \
|
||||
{ \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
return ____##name(___bpf_kretprobe_args(args)); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
} \
|
||||
static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args)
|
||||
|
||||
/* If kernel has CONFIG_ARCH_HAS_SYSCALL_WRAPPER, read pt_regs directly */
|
||||
#define ___bpf_syscall_args0() ctx
|
||||
#define ___bpf_syscall_args1(x) ___bpf_syscall_args0(), (void *)PT_REGS_PARM1_SYSCALL(regs)
|
||||
#define ___bpf_syscall_args2(x, args...) ___bpf_syscall_args1(args), (void *)PT_REGS_PARM2_SYSCALL(regs)
|
||||
#define ___bpf_syscall_args3(x, args...) ___bpf_syscall_args2(args), (void *)PT_REGS_PARM3_SYSCALL(regs)
|
||||
#define ___bpf_syscall_args4(x, args...) ___bpf_syscall_args3(args), (void *)PT_REGS_PARM4_SYSCALL(regs)
|
||||
#define ___bpf_syscall_args5(x, args...) ___bpf_syscall_args4(args), (void *)PT_REGS_PARM5_SYSCALL(regs)
|
||||
#define ___bpf_syscall_args6(x, args...) ___bpf_syscall_args5(args), (void *)PT_REGS_PARM6_SYSCALL(regs)
|
||||
#define ___bpf_syscall_args7(x, args...) ___bpf_syscall_args6(args), (void *)PT_REGS_PARM7_SYSCALL(regs)
|
||||
#define ___bpf_syscall_args(args...) ___bpf_apply(___bpf_syscall_args, ___bpf_narg(args))(args)
|
||||
|
||||
/* If kernel doesn't have CONFIG_ARCH_HAS_SYSCALL_WRAPPER, we have to BPF_CORE_READ from pt_regs */
|
||||
#define ___bpf_syswrap_args0() ctx
|
||||
#define ___bpf_syswrap_args1(x) ___bpf_syswrap_args0(), (void *)PT_REGS_PARM1_CORE_SYSCALL(regs)
|
||||
#define ___bpf_syswrap_args2(x, args...) ___bpf_syswrap_args1(args), (void *)PT_REGS_PARM2_CORE_SYSCALL(regs)
|
||||
#define ___bpf_syswrap_args3(x, args...) ___bpf_syswrap_args2(args), (void *)PT_REGS_PARM3_CORE_SYSCALL(regs)
|
||||
#define ___bpf_syswrap_args4(x, args...) ___bpf_syswrap_args3(args), (void *)PT_REGS_PARM4_CORE_SYSCALL(regs)
|
||||
#define ___bpf_syswrap_args5(x, args...) ___bpf_syswrap_args4(args), (void *)PT_REGS_PARM5_CORE_SYSCALL(regs)
|
||||
#define ___bpf_syswrap_args6(x, args...) ___bpf_syswrap_args5(args), (void *)PT_REGS_PARM6_CORE_SYSCALL(regs)
|
||||
#define ___bpf_syswrap_args7(x, args...) ___bpf_syswrap_args6(args), (void *)PT_REGS_PARM7_CORE_SYSCALL(regs)
|
||||
#define ___bpf_syswrap_args(args...) ___bpf_apply(___bpf_syswrap_args, ___bpf_narg(args))(args)
|
||||
|
||||
/*
|
||||
* BPF_KSYSCALL is a variant of BPF_KPROBE, which is intended for
|
||||
* tracing syscall functions, like __x64_sys_close. It hides the underlying
|
||||
* platform-specific low-level way of getting syscall input arguments from
|
||||
* struct pt_regs, and provides a familiar typed and named function arguments
|
||||
* syntax and semantics of accessing syscall input parameters.
|
||||
*
|
||||
* Original struct pt_regs * context is preserved as 'ctx' argument. This might
|
||||
* be necessary when using BPF helpers like bpf_perf_event_output().
|
||||
*
|
||||
* At the moment BPF_KSYSCALL does not transparently handle all the calling
|
||||
* convention quirks for the following syscalls:
|
||||
*
|
||||
* - mmap(): __ARCH_WANT_SYS_OLD_MMAP.
|
||||
* - clone(): CONFIG_CLONE_BACKWARDS, CONFIG_CLONE_BACKWARDS2 and
|
||||
* CONFIG_CLONE_BACKWARDS3.
|
||||
* - socket-related syscalls: __ARCH_WANT_SYS_SOCKETCALL.
|
||||
* - compat syscalls.
|
||||
*
|
||||
* This may or may not change in the future. User needs to take extra measures
|
||||
* to handle such quirks explicitly, if necessary.
|
||||
*
|
||||
* This macro relies on BPF CO-RE support and virtual __kconfig externs.
|
||||
*/
|
||||
#define BPF_KSYSCALL(name, args...) \
|
||||
name(struct pt_regs *ctx); \
|
||||
extern _Bool LINUX_HAS_SYSCALL_WRAPPER __kconfig; \
|
||||
static __always_inline typeof(name(0)) \
|
||||
____##name(struct pt_regs *ctx, ##args); \
|
||||
typeof(name(0)) name(struct pt_regs *ctx) \
|
||||
{ \
|
||||
struct pt_regs *regs = LINUX_HAS_SYSCALL_WRAPPER \
|
||||
? (struct pt_regs *)PT_REGS_PARM1(ctx) \
|
||||
: ctx; \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wint-conversion\"") \
|
||||
if (LINUX_HAS_SYSCALL_WRAPPER) \
|
||||
return ____##name(___bpf_syswrap_args(args)); \
|
||||
else \
|
||||
return ____##name(___bpf_syscall_args(args)); \
|
||||
_Pragma("GCC diagnostic pop") \
|
||||
} \
|
||||
static __always_inline typeof(name(0)) \
|
||||
____##name(struct pt_regs *ctx, ##args)
|
||||
|
||||
#define BPF_KPROBE_SYSCALL BPF_KSYSCALL
|
||||
|
||||
/* BPF_UPROBE and BPF_URETPROBE are identical to BPF_KPROBE and BPF_KRETPROBE,
|
||||
* but are named way less confusingly for SEC("uprobe") and SEC("uretprobe")
|
||||
* use cases.
|
||||
*/
|
||||
#define BPF_UPROBE(name, args...) BPF_KPROBE(name, ##args)
|
||||
#define BPF_URETPROBE(name, args...) BPF_KRETPROBE(name, ##args)
|
||||
|
||||
#endif
|
||||
182
firewall/interception/ebpf/programs/monitor.c
Normal file
182
firewall/interception/ebpf/programs/monitor.c
Normal file
@@ -0,0 +1,182 @@
|
||||
#include "vmlinux-x86.h"
|
||||
#include "bpf/bpf_helpers.h"
|
||||
#include "bpf/bpf_tracing.h"
|
||||
|
||||
// IP Version
|
||||
#define AF_INET 2
|
||||
#define AF_INET6 10
|
||||
|
||||
// Protocols
|
||||
#define TCP 6
|
||||
#define UDP 17
|
||||
#define UDPLite 136
|
||||
|
||||
#define OUTBOUND 0
|
||||
#define INBOUND 1
|
||||
|
||||
char __license[] SEC("license") = "GPL";
|
||||
|
||||
// Ring buffer for all connection events
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
||||
__uint(max_entries, 1 << 24);
|
||||
} pm_connection_events SEC(".maps");
|
||||
|
||||
// Event struct that will be sent to Go on each new connection. (The name should be the same as the go generate command)
|
||||
struct Event {
|
||||
u32 saddr[4];
|
||||
u32 daddr[4];
|
||||
u16 sport;
|
||||
u16 dport;
|
||||
u32 pid;
|
||||
u8 ipVersion;
|
||||
u8 protocol;
|
||||
u8 direction;
|
||||
};
|
||||
struct Event *unused __attribute__((unused));
|
||||
|
||||
// Fentry of tcp_connect will be executed when equivalent kernel function is called.
|
||||
// In the kernel all IP address and ports should be set before tcp_connect is called. [this-function] -> tcp_connect
|
||||
SEC("fentry/tcp_connect")
|
||||
int BPF_PROG(tcp_connect, struct sock *sk) {
|
||||
// Alloc space for the event
|
||||
struct Event *tcp_info;
|
||||
tcp_info = bpf_ringbuf_reserve(&pm_connection_events, sizeof(struct Event), 0);
|
||||
if (!tcp_info) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read PID
|
||||
tcp_info->pid = __builtin_bswap32((u32)bpf_get_current_pid_tgid());
|
||||
|
||||
// Set protocol
|
||||
tcp_info->protocol = TCP;
|
||||
|
||||
// Set direction
|
||||
tcp_info->direction = OUTBOUND;
|
||||
|
||||
// Set src and dist ports
|
||||
tcp_info->sport = __builtin_bswap16(sk->__sk_common.skc_num);
|
||||
tcp_info->dport = sk->__sk_common.skc_dport;
|
||||
|
||||
// Set src and dist IPs
|
||||
if (sk->__sk_common.skc_family == AF_INET) {
|
||||
tcp_info->saddr[0] = __builtin_bswap32(sk->__sk_common.skc_rcv_saddr);
|
||||
tcp_info->daddr[0] = __builtin_bswap32(sk->__sk_common.skc_daddr);
|
||||
// Set IP version
|
||||
tcp_info->ipVersion = 4;
|
||||
} else if (sk->__sk_common.skc_family == AF_INET6) {
|
||||
for(int i = 0; i < 4; i++) {
|
||||
tcp_info->saddr[i] = __builtin_bswap32(sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[i]);
|
||||
}
|
||||
for(int i = 0; i < 4; i++) {
|
||||
tcp_info->daddr[i] = __builtin_bswap32(sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32[i]);
|
||||
}
|
||||
// Set IP version
|
||||
tcp_info->ipVersion = 6;
|
||||
}
|
||||
|
||||
// Send event
|
||||
bpf_ringbuf_submit(tcp_info, 0);
|
||||
return 0;
|
||||
};
|
||||
|
||||
// Fexit(function exit) of udp_v4_connect will be executed after the ip4_datagram_connect kernel function is called.
|
||||
// ip4_datagram_connect -> udp_v4_connect
|
||||
SEC("fexit/ip4_datagram_connect")
|
||||
int BPF_PROG(udp_v4_connect, struct sock *sk) {
|
||||
// Ignore everything else then IPv4
|
||||
if (sk->__sk_common.skc_family != AF_INET) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ip4_datagram_connect return error
|
||||
if (sk->__sk_common.skc_dport == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate space for the event.
|
||||
struct Event *udp_info;
|
||||
udp_info = bpf_ringbuf_reserve(&pm_connection_events, sizeof(struct Event), 0);
|
||||
if (!udp_info) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read PID
|
||||
udp_info->pid = __builtin_bswap32((u32)bpf_get_current_pid_tgid());
|
||||
|
||||
// Set src and dist ports
|
||||
udp_info->sport = __builtin_bswap16(sk->__sk_common.skc_num);
|
||||
udp_info->dport = sk->__sk_common.skc_dport;
|
||||
|
||||
// Set src and dist IPs
|
||||
udp_info->saddr[0] = __builtin_bswap32(sk->__sk_common.skc_rcv_saddr);
|
||||
udp_info->daddr[0] = __builtin_bswap32(sk->__sk_common.skc_daddr);
|
||||
|
||||
// Set IP version
|
||||
udp_info->ipVersion = 4;
|
||||
|
||||
// Set protocol. No way to detect udplite for ipv4
|
||||
udp_info->protocol = UDP;
|
||||
|
||||
// Send event
|
||||
bpf_ringbuf_submit(udp_info, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fentry(function enter) of udp_v6_connect will be executed after the ip6_datagram_connect kernel function is called.
|
||||
// ip6_datagram_connect -> udp_v6_connect
|
||||
SEC("fexit/ip6_datagram_connect")
|
||||
int BPF_PROG(udp_v6_connect, struct sock *sk) {
|
||||
// Ignore everything else then IPv6
|
||||
if (sk->__sk_common.skc_family != AF_INET6) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ip6_datagram_connect return error
|
||||
if (sk->__sk_common.skc_dport == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Make sure its udp6 socket
|
||||
struct udp6_sock *us = bpf_skc_to_udp6_sock(sk);
|
||||
if (!us) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate space for the event.
|
||||
struct Event *udp_info;
|
||||
udp_info = bpf_ringbuf_reserve(&pm_connection_events, sizeof(struct Event), 0);
|
||||
if (!udp_info) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read PID
|
||||
udp_info->pid = __builtin_bswap32((u32)bpf_get_current_pid_tgid());
|
||||
|
||||
// Set src and dist ports
|
||||
udp_info->sport = __builtin_bswap16(sk->__sk_common.skc_num);
|
||||
udp_info->dport = sk->__sk_common.skc_dport;
|
||||
|
||||
// Set src and dist IPs
|
||||
for(int i = 0; i < 4; i++) {
|
||||
udp_info->saddr[i] = __builtin_bswap32(sk->__sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32[i]);
|
||||
}
|
||||
for(int i = 0; i < 4; i++) {
|
||||
udp_info->daddr[i] = __builtin_bswap32(sk->__sk_common.skc_v6_daddr.in6_u.u6_addr32[i]);
|
||||
}
|
||||
|
||||
// IP version
|
||||
udp_info->ipVersion = 6;
|
||||
|
||||
// Set protocol for UDPLite
|
||||
if(us->udp.pcflag == 0) {
|
||||
udp_info->protocol = UDP;
|
||||
} else {
|
||||
udp_info->protocol = UDPLite;
|
||||
}
|
||||
|
||||
// Send event
|
||||
bpf_ringbuf_submit(udp_info, 0);
|
||||
return 0;
|
||||
}
|
||||
17
firewall/interception/ebpf/programs/update.sh
Normal file
17
firewall/interception/ebpf/programs/update.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Version of libbpf to fetch headers from
|
||||
LIBBPF_VERSION=1.2.0
|
||||
|
||||
# The headers we want
|
||||
prefix=libbpf-"$LIBBPF_VERSION"
|
||||
headers=(
|
||||
"$prefix"/src/bpf_core_read.h
|
||||
"$prefix"/src/bpf_helper_defs.h
|
||||
"$prefix"/src/bpf_helpers.h
|
||||
"$prefix"/src/bpf_tracing.h
|
||||
)
|
||||
|
||||
# Fetch libbpf release and extract the desired headers
|
||||
curl -sL "https://github.com/libbpf/libbpf/archive/refs/tags/v${LIBBPF_VERSION}.tar.gz" | \
|
||||
tar -xz --xform='s#.*/#bpf/#' "${headers[@]}"
|
||||
120348
firewall/interception/ebpf/programs/vmlinux-x86.h
Normal file
120348
firewall/interception/ebpf/programs/vmlinux-x86.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -9,13 +9,13 @@ import (
|
||||
)
|
||||
|
||||
// start starts the interception.
|
||||
func start(_ chan packet.Packet) error {
|
||||
func startInterception(_ chan packet.Packet) error {
|
||||
log.Critical("interception: this platform has no support for packet interception - a lot of functionality will be broken")
|
||||
return nil
|
||||
}
|
||||
|
||||
// stop starts the interception.
|
||||
func stop() error {
|
||||
func stopInterception() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,39 @@
|
||||
package interception
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
bandwidth "github.com/safing/portmaster/firewall/interception/ebpf/bandwidth"
|
||||
conn_listener "github.com/safing/portmaster/firewall/interception/ebpf/connection_listener"
|
||||
"github.com/safing/portmaster/firewall/interception/nfq"
|
||||
"github.com/safing/portmaster/network"
|
||||
"github.com/safing/portmaster/network/packet"
|
||||
)
|
||||
|
||||
// start starts the interception.
|
||||
func start(ch chan packet.Packet) error {
|
||||
return StartNfqueueInterception(ch)
|
||||
func startInterception(packets chan packet.Packet) error {
|
||||
// Start packet interception via nfqueue.
|
||||
err := StartNfqueueInterception(packets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Start ebpf new connection listener.
|
||||
module.StartServiceWorker("ebpf connection listener", 0, func(ctx context.Context) error {
|
||||
return conn_listener.ConnectionListenerWorker(ctx, packets)
|
||||
})
|
||||
|
||||
// Start ebpf bandwidth stats monitor.
|
||||
module.StartServiceWorker("ebpf bandwidth stats monitor", 0, func(ctx context.Context) error {
|
||||
return bandwidth.BandwidthStatsWorker(ctx, 1*time.Second, BandwidthUpdates)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// stop starts the interception.
|
||||
func stop() error {
|
||||
func stopInterception() error {
|
||||
return StopNfqueueInterception()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package interception
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portmaster/firewall/interception/windowskext"
|
||||
"github.com/safing/portmaster/network"
|
||||
@@ -10,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// start starts the interception.
|
||||
func start(ch chan packet.Packet) error {
|
||||
func startInterception(packets chan packet.Packet) error {
|
||||
kextFile, err := updates.GetPlatformFile("kext/portmaster-kext.sys")
|
||||
if err != nil {
|
||||
return fmt.Errorf("interception: could not get kext sys: %s", err)
|
||||
@@ -26,13 +28,22 @@ func start(ch chan packet.Packet) error {
|
||||
return fmt.Errorf("interception: could not start windows kext: %s", err)
|
||||
}
|
||||
|
||||
go windowskext.Handler(ch)
|
||||
// Start packet handler.
|
||||
module.StartServiceWorker("kext packet handler", 0, func(ctx context.Context) error {
|
||||
windowskext.Handler(ctx, packets)
|
||||
return nil
|
||||
})
|
||||
|
||||
// Start bandwidth stats monitor.
|
||||
module.StartServiceWorker("kext bandwidth stats monitor", 0, func(ctx context.Context) error {
|
||||
return windowskext.BandwidthStatsWorker(ctx, 1*time.Second, BandwidthUpdates)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// stop starts the interception.
|
||||
func stop() error {
|
||||
func stopInterception() error {
|
||||
return windowskext.Stop()
|
||||
}
|
||||
|
||||
|
||||
@@ -4,22 +4,34 @@ import (
|
||||
"flag"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/network/packet"
|
||||
)
|
||||
|
||||
var (
|
||||
// Packets channel for feeding the firewall.
|
||||
module *modules.Module
|
||||
|
||||
// Packets is a stream of interception network packest.
|
||||
Packets = make(chan packet.Packet, 1000)
|
||||
|
||||
// BandwidthUpdates is a stream of bandwidth usage update for connections.
|
||||
BandwidthUpdates = make(chan *packet.BandwidthUpdate, 1000)
|
||||
|
||||
disableInterception bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.BoolVar(&disableInterception, "disable-interception", false, "disable packet interception; this breaks a lot of functionality")
|
||||
|
||||
module = modules.Register("interception", prep, start, stop, "base", "updates", "network", "notifications", "profiles")
|
||||
}
|
||||
|
||||
func prep() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start starts the interception.
|
||||
func Start() error {
|
||||
func start() error {
|
||||
if disableInterception {
|
||||
log.Warning("interception: packet interception is disabled via flag - this breaks a lot of functionality")
|
||||
return nil
|
||||
@@ -36,16 +48,16 @@ func Start() error {
|
||||
}()
|
||||
}
|
||||
|
||||
return start(inputPackets)
|
||||
return startInterception(inputPackets)
|
||||
}
|
||||
|
||||
// Stop starts the interception.
|
||||
func Stop() error {
|
||||
func stop() error {
|
||||
if disableInterception {
|
||||
return nil
|
||||
}
|
||||
|
||||
close(metrics.done)
|
||||
|
||||
return stop()
|
||||
return stopInterception()
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
pmpacket "github.com/safing/portmaster/network/packet"
|
||||
"github.com/safing/portmaster/process"
|
||||
)
|
||||
|
||||
// Queue wraps a nfqueue.
|
||||
@@ -175,10 +176,11 @@ func (q *Queue) packetHandler(ctx context.Context) func(nfqueue.Attribute) int {
|
||||
pkt := &packet{
|
||||
pktID: *attrs.PacketID,
|
||||
queue: q,
|
||||
received: time.Now(),
|
||||
verdictSet: make(chan struct{}),
|
||||
verdictPending: abool.New(),
|
||||
}
|
||||
pkt.Info().PID = process.UndefinedProcessID
|
||||
pkt.Info().SeenAt = time.Now()
|
||||
|
||||
if attrs.Payload == nil {
|
||||
// There is not payload.
|
||||
@@ -194,11 +196,11 @@ func (q *Queue) packetHandler(ctx context.Context) func(nfqueue.Attribute) int {
|
||||
|
||||
select {
|
||||
case q.packets <- pkt:
|
||||
log.Tracef("nfqueue: queued packet %s (%s -> %s) after %s", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.received))
|
||||
log.Tracef("nfqueue: queued packet %s (%s -> %s) after %s", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.Info().SeenAt))
|
||||
case <-ctx.Done():
|
||||
return 0
|
||||
case <-time.After(time.Second):
|
||||
log.Warningf("nfqueue: failed to queue packet (%s since it was handed over by the kernel)", time.Since(pkt.received))
|
||||
log.Warningf("nfqueue: failed to queue packet (%s since it was handed over by the kernel)", time.Since(pkt.Info().SeenAt))
|
||||
}
|
||||
|
||||
go func() {
|
||||
@@ -206,7 +208,7 @@ func (q *Queue) packetHandler(ctx context.Context) func(nfqueue.Attribute) int {
|
||||
case <-pkt.verdictSet:
|
||||
|
||||
case <-time.After(20 * time.Second):
|
||||
log.Warningf("nfqueue: no verdict set for packet %s (%s -> %s) after %s, dropping", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.received))
|
||||
log.Warningf("nfqueue: no verdict set for packet %s (%s -> %s) after %s, dropping", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, time.Since(pkt.Info().SeenAt))
|
||||
if err := pkt.Drop(); err != nil {
|
||||
log.Warningf("nfqueue: failed to apply default-drop to unveridcted packet %s (%s -> %s)", pkt.ID(), pkt.Info().Src, pkt.Info().Dst)
|
||||
}
|
||||
|
||||
@@ -55,7 +55,6 @@ func markToString(mark int) string {
|
||||
type packet struct {
|
||||
pmpacket.Base
|
||||
pktID uint32
|
||||
received time.Time
|
||||
queue *Queue
|
||||
verdictSet chan struct{}
|
||||
verdictPending *abool.AtomicBool
|
||||
@@ -118,7 +117,7 @@ func (pkt *packet) setMark(mark int) error {
|
||||
}
|
||||
break
|
||||
}
|
||||
log.Tracer(pkt.Ctx()).Tracef("nfqueue: marking packet %s (%s -> %s) on queue %d with %s after %s", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, pkt.queue.id, markToString(mark), time.Since(pkt.received))
|
||||
log.Tracer(pkt.Ctx()).Tracef("nfqueue: marking packet %s (%s -> %s) on queue %d with %s after %s", pkt.ID(), pkt.Info().Src, pkt.Info().Dst, pkt.queue.id, markToString(mark), time.Since(pkt.Info().SeenAt))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package interception
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"sort"
|
||||
@@ -257,30 +258,30 @@ func StartNfqueueInterception(packets chan<- packet.Packet) (err error) {
|
||||
|
||||
err = activateNfqueueFirewall()
|
||||
if err != nil {
|
||||
_ = Stop()
|
||||
_ = StopNfqueueInterception()
|
||||
return fmt.Errorf("could not initialize nfqueue: %w", err)
|
||||
}
|
||||
|
||||
out4Queue, err = nfq.New(17040, false)
|
||||
if err != nil {
|
||||
_ = Stop()
|
||||
_ = StopNfqueueInterception()
|
||||
return fmt.Errorf("nfqueue(IPv4, out): %w", err)
|
||||
}
|
||||
in4Queue, err = nfq.New(17140, false)
|
||||
if err != nil {
|
||||
_ = Stop()
|
||||
_ = StopNfqueueInterception()
|
||||
return fmt.Errorf("nfqueue(IPv4, in): %w", err)
|
||||
}
|
||||
|
||||
if netenv.IPv6Enabled() {
|
||||
out6Queue, err = nfq.New(17060, true)
|
||||
if err != nil {
|
||||
_ = Stop()
|
||||
_ = StopNfqueueInterception()
|
||||
return fmt.Errorf("nfqueue(IPv6, out): %w", err)
|
||||
}
|
||||
in6Queue, err = nfq.New(17160, true)
|
||||
if err != nil {
|
||||
_ = Stop()
|
||||
_ = StopNfqueueInterception()
|
||||
return fmt.Errorf("nfqueue(IPv6, in): %w", err)
|
||||
}
|
||||
} else {
|
||||
@@ -289,7 +290,9 @@ func StartNfqueueInterception(packets chan<- packet.Packet) (err error) {
|
||||
in6Queue = &disabledNfQueue{}
|
||||
}
|
||||
|
||||
go handleInterception(packets)
|
||||
module.StartServiceWorker("nfqueue packet handler", 0, func(_ context.Context) error {
|
||||
return handleInterception(packets)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -318,12 +321,12 @@ func StopNfqueueInterception() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handleInterception(packets chan<- packet.Packet) {
|
||||
func handleInterception(packets chan<- packet.Packet) error {
|
||||
for {
|
||||
var pkt packet.Packet
|
||||
select {
|
||||
case <-shutdownSignal:
|
||||
return
|
||||
return nil
|
||||
case pkt = <-out4Queue.PacketChannel():
|
||||
pkt.SetOutbound()
|
||||
case pkt = <-in4Queue.PacketChannel():
|
||||
@@ -337,7 +340,7 @@ func handleInterception(packets chan<- packet.Packet) {
|
||||
select {
|
||||
case packets <- pkt:
|
||||
case <-shutdownSignal:
|
||||
return
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
129
firewall/interception/windowskext/bandwidth_stats.go
Normal file
129
firewall/interception/windowskext/bandwidth_stats.go
Normal file
@@ -0,0 +1,129 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package windowskext
|
||||
|
||||
// This file contains example code how to read bandwidth stats from the kext. Its not ment to be used in production.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/network/packet"
|
||||
)
|
||||
|
||||
type Rxtxdata struct {
|
||||
rx uint64
|
||||
tx uint64
|
||||
}
|
||||
|
||||
type Key struct {
|
||||
localIP [4]uint32
|
||||
remoteIP [4]uint32
|
||||
localPort uint16
|
||||
remotePort uint16
|
||||
ipv6 bool
|
||||
protocol uint8
|
||||
}
|
||||
|
||||
var m = make(map[Key]Rxtxdata)
|
||||
|
||||
func BandwidthStatsWorker(ctx context.Context, collectInterval time.Duration, bandwidthUpdates chan *packet.BandwidthUpdate) error {
|
||||
// Setup ticker.
|
||||
ticker := time.NewTicker(collectInterval)
|
||||
defer ticker.Stop()
|
||||
|
||||
// Collect bandwidth at every tick.
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
err := reportBandwidth(ctx, bandwidthUpdates)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func reportBandwidth(ctx context.Context, bandwidthUpdates chan *packet.BandwidthUpdate) error {
|
||||
stats, err := GetConnectionsStats()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Report all statistics.
|
||||
for _, stat := range stats {
|
||||
connID := packet.CreateConnectionID(
|
||||
packet.IPProtocol(stat.protocol),
|
||||
convertArrayToIP(stat.localIP, stat.ipV6 == 1), stat.localPort,
|
||||
convertArrayToIP(stat.remoteIP, stat.ipV6 == 1), stat.remotePort,
|
||||
false,
|
||||
)
|
||||
update := &packet.BandwidthUpdate{
|
||||
ConnID: connID,
|
||||
RecvBytes: stat.receivedBytes,
|
||||
SentBytes: stat.transmittedBytes,
|
||||
Method: packet.Additive,
|
||||
}
|
||||
select {
|
||||
case bandwidthUpdates <- update:
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartBandwithConsoleLogger() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
conns, err := GetConnectionsStats()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, conn := range conns {
|
||||
if conn.receivedBytes == 0 && conn.transmittedBytes == 0 {
|
||||
continue
|
||||
}
|
||||
key := Key{
|
||||
localIP: conn.localIP,
|
||||
remoteIP: conn.remoteIP,
|
||||
localPort: conn.localPort,
|
||||
remotePort: conn.remotePort,
|
||||
ipv6: conn.ipV6 == 1,
|
||||
protocol: conn.protocol,
|
||||
}
|
||||
|
||||
// First we get a "copy" of the entry
|
||||
if entry, ok := m[key]; ok {
|
||||
// Then we modify the copy
|
||||
entry.rx += conn.receivedBytes
|
||||
entry.tx += conn.transmittedBytes
|
||||
|
||||
// Then we reassign map entry
|
||||
m[key] = entry
|
||||
} else {
|
||||
m[key] = Rxtxdata{
|
||||
rx: conn.receivedBytes,
|
||||
tx: conn.transmittedBytes,
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Debug("----------------------------------")
|
||||
for key, value := range m {
|
||||
log.Debugf(
|
||||
"Conn: %d %s:%d %s:%d rx:%d tx:%d", key.protocol,
|
||||
convertArrayToIP(key.localIP, key.ipv6), key.localPort,
|
||||
convertArrayToIP(key.remoteIP, key.ipv6), key.remotePort,
|
||||
value.rx, value.tx,
|
||||
)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -4,12 +4,16 @@
|
||||
package windowskext
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/safing/portmaster/process"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
@@ -27,13 +31,18 @@ const (
|
||||
// connection that was intercepted on an ALE layer instead of in the network
|
||||
// stack itself. Thus, no packet data is available.
|
||||
VerdictRequestFlagSocketAuth = 2
|
||||
|
||||
// VerdictRequestFlagExpectSocketAuth indicates that the next verdict
|
||||
// requests is expected to be an informational socket auth request from
|
||||
// the ALE layer.
|
||||
VerdictRequestFlagExpectSocketAuth = 4
|
||||
)
|
||||
|
||||
// Do not change the order of the members! The structure is used to communicate with the kernel extension.
|
||||
// VerdictRequest is the request structure from the Kext.
|
||||
type VerdictRequest struct {
|
||||
id uint32 // ID from RegisterPacket
|
||||
_ uint64 // Process ID - does not yet work
|
||||
pid uint64 // Process ID - info only packets
|
||||
direction uint8
|
||||
ipV6 uint8 // True: IPv6, False: IPv4
|
||||
protocol uint8 // Protocol
|
||||
@@ -65,6 +74,17 @@ type VerdictUpdateInfo struct {
|
||||
verdict uint8 // New verdict
|
||||
}
|
||||
|
||||
type ConnectionStat struct {
|
||||
localIP [4]uint32 //Source Address, only srcIP[0] if IPv4
|
||||
remoteIP [4]uint32 //Destination Address
|
||||
localPort uint16 //Source Port
|
||||
remotePort uint16 //Destination port
|
||||
receivedBytes uint64 //Number of bytes recived on this connection
|
||||
transmittedBytes uint64 //Number of bytes transsmited from this connection
|
||||
ipV6 uint8 //True: IPv6, False: IPv4
|
||||
protocol uint8 //Protocol (UDP, TCP, ...)
|
||||
}
|
||||
|
||||
type VersionInfo struct {
|
||||
major uint8
|
||||
minor uint8
|
||||
@@ -77,9 +97,7 @@ func (v *VersionInfo) String() string {
|
||||
}
|
||||
|
||||
// Handler transforms received packets to the Packet interface.
|
||||
func Handler(packets chan packet.Packet) {
|
||||
defer close(packets)
|
||||
|
||||
func Handler(ctx context.Context, packets chan packet.Packet) {
|
||||
for {
|
||||
packetInfo, err := RecvVerdictRequest()
|
||||
if err != nil {
|
||||
@@ -103,45 +121,39 @@ func Handler(packets chan packet.Packet) {
|
||||
verdictRequest: packetInfo,
|
||||
verdictSet: abool.NewBool(false),
|
||||
}
|
||||
|
||||
info := new.Info()
|
||||
info.Inbound = packetInfo.direction > 0
|
||||
info.InTunnel = false
|
||||
info.Protocol = packet.IPProtocol(packetInfo.protocol)
|
||||
info.PID = int(packetInfo.pid)
|
||||
info.SeenAt = time.Now()
|
||||
|
||||
// IP version
|
||||
// Check PID
|
||||
if info.PID == 0 {
|
||||
// Windows does not have zero PIDs.
|
||||
// Set to UndefinedProcessID.
|
||||
info.PID = process.UndefinedProcessID
|
||||
}
|
||||
|
||||
// Set IP version
|
||||
if packetInfo.ipV6 == 1 {
|
||||
info.Version = packet.IPv6
|
||||
} else {
|
||||
info.Version = packet.IPv4
|
||||
}
|
||||
|
||||
// IPs
|
||||
if info.Version == packet.IPv4 {
|
||||
// IPv4
|
||||
if info.Inbound {
|
||||
// Inbound
|
||||
info.Src = convertIPv4(packetInfo.remoteIP)
|
||||
info.Dst = convertIPv4(packetInfo.localIP)
|
||||
} else {
|
||||
// Outbound
|
||||
info.Src = convertIPv4(packetInfo.localIP)
|
||||
info.Dst = convertIPv4(packetInfo.remoteIP)
|
||||
}
|
||||
// Set IPs
|
||||
if info.Inbound {
|
||||
// Inbound
|
||||
info.Src = convertArrayToIP(packetInfo.remoteIP, info.Version == packet.IPv6)
|
||||
info.Dst = convertArrayToIP(packetInfo.localIP, info.Version == packet.IPv6)
|
||||
} else {
|
||||
// IPv6
|
||||
if info.Inbound {
|
||||
// Inbound
|
||||
info.Src = convertIPv6(packetInfo.remoteIP)
|
||||
info.Dst = convertIPv6(packetInfo.localIP)
|
||||
} else {
|
||||
// Outbound
|
||||
info.Src = convertIPv6(packetInfo.localIP)
|
||||
info.Dst = convertIPv6(packetInfo.remoteIP)
|
||||
}
|
||||
// Outbound
|
||||
info.Src = convertArrayToIP(packetInfo.localIP, info.Version == packet.IPv6)
|
||||
info.Dst = convertArrayToIP(packetInfo.remoteIP, info.Version == packet.IPv6)
|
||||
}
|
||||
|
||||
// Ports
|
||||
// Set Ports
|
||||
if info.Inbound {
|
||||
// Inbound
|
||||
info.SrcPort = packetInfo.remotePort
|
||||
@@ -156,14 +168,14 @@ func Handler(packets chan packet.Packet) {
|
||||
}
|
||||
}
|
||||
|
||||
// convertIPv4 as needed for data from the kernel
|
||||
func convertIPv4(input [4]uint32) net.IP {
|
||||
addressBuf := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(addressBuf, input[0])
|
||||
return net.IP(addressBuf)
|
||||
}
|
||||
// convertArrayToIP converts an array of uint32 values to a net.IP address.
|
||||
func convertArrayToIP(input [4]uint32, ipv6 bool) net.IP {
|
||||
if !ipv6 {
|
||||
addressBuf := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(addressBuf, input[0])
|
||||
return net.IP(addressBuf)
|
||||
}
|
||||
|
||||
func convertIPv6(input [4]uint32) net.IP {
|
||||
addressBuf := make([]byte, 16)
|
||||
for i := 0; i < 4; i++ {
|
||||
binary.BigEndian.PutUint32(addressBuf[i*4:i*4+4], input[i])
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
@@ -127,8 +126,10 @@ func RecvVerdictRequest() (*VerdictRequest, error) {
|
||||
return nil, ErrKextNotReady
|
||||
}
|
||||
|
||||
timestamp := time.Now()
|
||||
defer log.Tracef("winkext: getting verdict request took %s", time.Since(timestamp))
|
||||
// DEBUG:
|
||||
// timestamp := time.Now()
|
||||
// defer log.Tracef("winkext: getting verdict request took %s", time.Since(timestamp))
|
||||
|
||||
// Initialize struct for the output data
|
||||
var new VerdictRequest
|
||||
|
||||
@@ -147,6 +148,9 @@ func RecvVerdictRequest() (*VerdictRequest, error) {
|
||||
|
||||
// SetVerdict sets the verdict for a packet and/or connection.
|
||||
func SetVerdict(pkt *Packet, verdict network.Verdict) error {
|
||||
if pkt.verdictRequest.pid != 0 {
|
||||
return nil // Ignore info only packets
|
||||
}
|
||||
if pkt.verdictRequest.id == 0 {
|
||||
log.Tracer(pkt.Ctx()).Errorf("kext: failed to set verdict %s: no packet ID", verdict)
|
||||
return ErrNoPacketID
|
||||
@@ -285,6 +289,29 @@ func GetVersion() (*VersionInfo, error) {
|
||||
return version, nil
|
||||
}
|
||||
|
||||
var sizeOfConnectionStat = uint32(unsafe.Sizeof(ConnectionStat{}))
|
||||
|
||||
func GetConnectionsStats() ([]ConnectionStat, error) {
|
||||
kextLock.RLock()
|
||||
defer kextLock.RUnlock()
|
||||
|
||||
// Check if driver is initialized
|
||||
if kextHandle == winInvalidHandleValue {
|
||||
log.Error("kext: failed to clear the cache: kext not ready")
|
||||
return nil, ErrKextNotReady
|
||||
}
|
||||
|
||||
var data [100]ConnectionStat
|
||||
size := len(data)
|
||||
bytesReturned, err := deviceIOControl(kextHandle, IOCTL_GET_CONNECTIONS_STAT, asByteArray(&size), asByteArray(&data))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data[:bytesReturned/sizeOfConnectionStat], nil
|
||||
}
|
||||
|
||||
func openDriver(filename string) (windows.Handle, error) {
|
||||
u16filename, err := syscall.UTF16FromString(filename)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package windowskext
|
||||
@@ -29,6 +30,17 @@ func (pkt *Packet) FastTrackedByIntegration() bool {
|
||||
return pkt.verdictRequest.flags&VerdictRequestFlagFastTrackPermitted > 0
|
||||
}
|
||||
|
||||
// InfoOnly returns whether the packet is informational only and does not
|
||||
// represent an actual packet.
|
||||
func (pkt *Packet) InfoOnly() bool {
|
||||
return pkt.verdictRequest.flags&VerdictRequestFlagSocketAuth > 0
|
||||
}
|
||||
|
||||
// ExpectInfo returns whether the next packet is expected to be informational only.
|
||||
func (pkt *Packet) ExpectInfo() bool {
|
||||
return pkt.verdictRequest.flags&VerdictRequestFlagExpectSocketAuth > 0
|
||||
}
|
||||
|
||||
// GetPayload returns the full raw packet.
|
||||
func (pkt *Packet) LoadPacketData() error {
|
||||
pkt.lock.Lock()
|
||||
|
||||
@@ -13,46 +13,47 @@ const (
|
||||
|
||||
SIOCTL_TYPE = 40000
|
||||
)
|
||||
|
||||
var (
|
||||
IOCTL_VERSION = ctlCode(SIOCTL_TYPE, 0x800, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
IOCTL_SHUTDOWN_REQUEST = ctlCode(SIOCTL_TYPE, 0x801, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
IOCTL_RECV_VERDICT_REQ = ctlCode(SIOCTL_TYPE, 0x802, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
IOCTL_SET_VERDICT = ctlCode(SIOCTL_TYPE, 0x803, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
IOCTL_GET_PAYLOAD = ctlCode(SIOCTL_TYPE, 0x804, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
IOCTL_CLEAR_CACHE = ctlCode(SIOCTL_TYPE, 0x805, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
IOCTL_UPDATE_VERDICT = ctlCode(SIOCTL_TYPE, 0x806, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
)
|
||||
|
||||
func ctlCode(device_type, function, method, access uint32) uint32 {
|
||||
return (device_type << 16) | (access << 14) | (function << 2) | method
|
||||
}
|
||||
|
||||
func deviceIOControlAsync(handle windows.Handle, code uint32, inData []byte, outData []byte) (*windows.Overlapped, error) {
|
||||
var inDataPtr *byte = nil
|
||||
var inDataSize uint32 = 0
|
||||
if inData != nil {
|
||||
inDataPtr = &inData[0]
|
||||
inDataSize = uint32(len(inData))
|
||||
}
|
||||
|
||||
var outDataPtr *byte = nil
|
||||
var outDataSize uint32 = 0
|
||||
if outData != nil {
|
||||
outDataPtr = &outData[0]
|
||||
outDataSize = uint32(len(outData))
|
||||
}
|
||||
|
||||
overlapped := &windows.Overlapped{}
|
||||
err := windows.DeviceIoControl(handle,
|
||||
code,
|
||||
inDataPtr, inDataSize,
|
||||
outDataPtr, outDataSize,
|
||||
nil, overlapped)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
IOCTL_VERSION = ctlCode(SIOCTL_TYPE, 0x800, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
IOCTL_SHUTDOWN_REQUEST = ctlCode(SIOCTL_TYPE, 0x801, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
IOCTL_RECV_VERDICT_REQ = ctlCode(SIOCTL_TYPE, 0x802, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
IOCTL_SET_VERDICT = ctlCode(SIOCTL_TYPE, 0x803, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
IOCTL_GET_PAYLOAD = ctlCode(SIOCTL_TYPE, 0x804, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
IOCTL_CLEAR_CACHE = ctlCode(SIOCTL_TYPE, 0x805, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
IOCTL_UPDATE_VERDICT = ctlCode(SIOCTL_TYPE, 0x806, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
IOCTL_GET_CONNECTIONS_STAT = ctlCode(SIOCTL_TYPE, 0x807, METHOD_BUFFERED, windows.FILE_READ_DATA|windows.FILE_WRITE_DATA)
|
||||
)
|
||||
|
||||
func ctlCode(device_type, function, method, access uint32) uint32 {
|
||||
return (device_type << 16) | (access << 14) | (function << 2) | method
|
||||
}
|
||||
|
||||
func deviceIOControlAsync(handle windows.Handle, code uint32, inData []byte, outData []byte) (*windows.Overlapped, error) {
|
||||
var inDataPtr *byte = nil
|
||||
var inDataSize uint32 = 0
|
||||
if inData != nil {
|
||||
inDataPtr = &inData[0]
|
||||
inDataSize = uint32(len(inData))
|
||||
}
|
||||
|
||||
var outDataPtr *byte = nil
|
||||
var outDataSize uint32 = 0
|
||||
if outData != nil {
|
||||
outDataPtr = &outData[0]
|
||||
outDataSize = uint32(len(outData))
|
||||
}
|
||||
|
||||
overlapped := &windows.Overlapped{}
|
||||
err := windows.DeviceIoControl(handle,
|
||||
code,
|
||||
inDataPtr, inDataSize,
|
||||
outDataPtr, outDataSize,
|
||||
nil, overlapped)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return overlapped, nil
|
||||
|
||||
|
||||
@@ -25,19 +25,6 @@ import (
|
||||
"github.com/safing/portmaster/profile/endpoints"
|
||||
)
|
||||
|
||||
// Call order:
|
||||
//
|
||||
// DNS Query:
|
||||
// 1. DecideOnConnection
|
||||
// is called when a DNS query is made, may set verdict to Undeterminable to permit a DNS reply.
|
||||
// is called with a nil packet.
|
||||
// 2. DecideOnResolvedDNS
|
||||
// is called to (possibly) filter out A/AAAA records that the filter would deny later.
|
||||
//
|
||||
// Network Connection:
|
||||
// 3. DecideOnConnection
|
||||
// is called with the first packet of a network connection.
|
||||
|
||||
const noReasonOptionKey = ""
|
||||
|
||||
type deciderFn func(context.Context, *network.Connection, *profile.LayeredProfile, packet.Packet) bool
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"github.com/safing/portbase/api"
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/metrics"
|
||||
)
|
||||
|
||||
var packetHandlingHistogram *metrics.Histogram
|
||||
|
||||
func registerMetrics() (err error) {
|
||||
packetHandlingHistogram, err = metrics.NewHistogram(
|
||||
"firewall/handling/duration/seconds",
|
||||
nil,
|
||||
&metrics.Options{
|
||||
Permission: api.PermitUser,
|
||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
116
firewall/module.go
Normal file
116
firewall/module.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package firewall
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/safing/portbase/config"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portbase/modules/subsystems"
|
||||
_ "github.com/safing/portmaster/core"
|
||||
"github.com/safing/portmaster/network"
|
||||
)
|
||||
|
||||
var module *modules.Module
|
||||
|
||||
func init() {
|
||||
module = modules.Register("filter", prep, start, stop, "core", "interception", "intel")
|
||||
subsystems.Register(
|
||||
"filter",
|
||||
"Privacy Filter",
|
||||
"DNS and Network Filter",
|
||||
module,
|
||||
"config:filter/",
|
||||
&config.Option{
|
||||
Name: "Privacy Filter Module",
|
||||
Key: CfgOptionEnableFilterKey,
|
||||
Description: "Start the Privacy Filter module. If turned off, all privacy filter protections are fully disabled on this device.",
|
||||
OptType: config.OptTypeBool,
|
||||
ExpertiseLevel: config.ExpertiseLevelDeveloper,
|
||||
ReleaseLevel: config.ReleaseLevelStable,
|
||||
DefaultValue: true,
|
||||
Annotations: config.Annotations{
|
||||
config.CategoryAnnotation: "General",
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const (
|
||||
configChangeEvent = "config change"
|
||||
profileConfigChangeEvent = "profile config change"
|
||||
onSPNConnectEvent = "spn connect"
|
||||
)
|
||||
|
||||
func prep() error {
|
||||
network.SetDefaultFirewallHandler(verdictHandler)
|
||||
|
||||
// Reset connections every time configuration changes
|
||||
// this will be triggered on spn enable/disable
|
||||
err := module.RegisterEventHook(
|
||||
"config",
|
||||
configChangeEvent,
|
||||
"reset connection verdicts",
|
||||
func(ctx context.Context, _ interface{}) error {
|
||||
resetAllConnectionVerdicts()
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("interception: failed registering event hook: %s", err)
|
||||
}
|
||||
|
||||
// Reset connections every time profile changes
|
||||
err = module.RegisterEventHook(
|
||||
"profiles",
|
||||
profileConfigChangeEvent,
|
||||
"reset connection verdicts",
|
||||
func(ctx context.Context, _ interface{}) error {
|
||||
resetAllConnectionVerdicts()
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("failed registering event hook: %s", err)
|
||||
}
|
||||
|
||||
// Reset connections when spn is connected
|
||||
// connect and disconnecting is triggered on config change event but connecting takеs more time
|
||||
err = module.RegisterEventHook(
|
||||
"captain",
|
||||
onSPNConnectEvent,
|
||||
"reset connection verdicts",
|
||||
func(ctx context.Context, _ interface{}) error {
|
||||
resetAllConnectionVerdicts()
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("failed registering event hook: %s", err)
|
||||
}
|
||||
|
||||
if err := registerConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return prepAPIAuth()
|
||||
}
|
||||
|
||||
func start() error {
|
||||
getConfig()
|
||||
startAPIAuth()
|
||||
|
||||
module.StartServiceWorker("packet handler", 0, packetHandler)
|
||||
module.StartServiceWorker("bandwidth update handler", 0, bandwidthUpdateHandler)
|
||||
|
||||
// Start stat logger if logging is set to trace.
|
||||
if log.GetLogLevel() == log.TraceLevel {
|
||||
module.StartServiceWorker("stat logger", 0, statLogger)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func stop() error {
|
||||
return nil
|
||||
}
|
||||
@@ -11,10 +11,8 @@ import (
|
||||
|
||||
"github.com/google/gopacket/layers"
|
||||
"github.com/tevino/abool"
|
||||
"golang.org/x/sync/singleflight"
|
||||
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portbase/modules"
|
||||
"github.com/safing/portmaster/compat"
|
||||
_ "github.com/safing/portmaster/core/base"
|
||||
"github.com/safing/portmaster/firewall/inspection"
|
||||
@@ -27,8 +25,6 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
interceptionModule *modules.Module
|
||||
|
||||
nameserverIPMatcher func(ip net.IP) bool
|
||||
nameserverIPMatcherSet = abool.New()
|
||||
nameserverIPMatcherReady = abool.New()
|
||||
@@ -44,71 +40,6 @@ var (
|
||||
ownPID = os.Getpid()
|
||||
)
|
||||
|
||||
const (
|
||||
configChangeEvent = "config change"
|
||||
profileConfigChangeEvent = "profile config change"
|
||||
onSPNConnectEvent = "spn connect"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// TODO: Move interception module to own package (dir).
|
||||
interceptionModule = modules.Register("interception", interceptionPrep, interceptionStart, interceptionStop, "base", "updates", "network", "notifications", "profiles")
|
||||
|
||||
network.SetDefaultFirewallHandler(defaultHandler)
|
||||
}
|
||||
|
||||
func interceptionPrep() error {
|
||||
// Reset connections every time configuration changes
|
||||
// this will be triggered on spn enable/disable
|
||||
err := interceptionModule.RegisterEventHook(
|
||||
"config",
|
||||
configChangeEvent,
|
||||
"reset connection verdicts",
|
||||
func(ctx context.Context, _ interface{}) error {
|
||||
resetAllConnectionVerdicts()
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("interception: failed registering event hook: %s", err)
|
||||
}
|
||||
|
||||
// Reset connections every time profile changes
|
||||
err = interceptionModule.RegisterEventHook(
|
||||
"profiles",
|
||||
profileConfigChangeEvent,
|
||||
"reset connection verdicts",
|
||||
func(ctx context.Context, _ interface{}) error {
|
||||
resetAllConnectionVerdicts()
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("failed registering event hook: %s", err)
|
||||
}
|
||||
|
||||
// Reset connections when spn is connected
|
||||
// connect and disconnecting is triggered on config change event but connecting takеs more time
|
||||
err = interceptionModule.RegisterEventHook(
|
||||
"captain",
|
||||
onSPNConnectEvent,
|
||||
"reset connection verdicts",
|
||||
func(ctx context.Context, _ interface{}) error {
|
||||
resetAllConnectionVerdicts()
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Errorf("failed registering event hook: %s", err)
|
||||
}
|
||||
|
||||
if err := registerConfig(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return prepAPIAuth()
|
||||
}
|
||||
|
||||
func resetAllConnectionVerdicts() {
|
||||
// Resetting will force all the connection to be evaluated by the firewall again
|
||||
// this will set new verdicts if configuration was update or spn has been disabled or enabled.
|
||||
@@ -120,6 +51,11 @@ func resetAllConnectionVerdicts() {
|
||||
// Re-evaluate all connections.
|
||||
var changedVerdicts int
|
||||
for _, conn := range network.GetAllConnections() {
|
||||
// Skip incomplete connections.
|
||||
if !conn.DataIsComplete() {
|
||||
continue
|
||||
}
|
||||
|
||||
func() {
|
||||
conn.Lock()
|
||||
defer conn.Unlock()
|
||||
@@ -165,25 +101,6 @@ func resetAllConnectionVerdicts() {
|
||||
tracer.Submit()
|
||||
}
|
||||
|
||||
func interceptionStart() error {
|
||||
getConfig()
|
||||
|
||||
if err := registerMetrics(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
startAPIAuth()
|
||||
|
||||
interceptionModule.StartWorker("stat logger", statLogger)
|
||||
interceptionModule.StartWorker("packet handler", packetHandler)
|
||||
|
||||
return interception.Start()
|
||||
}
|
||||
|
||||
func interceptionStop() error {
|
||||
return interception.Stop()
|
||||
}
|
||||
|
||||
// SetNameserverIPMatcher sets a function that is used to match the internal
|
||||
// nameserver IP(s). Can only bet set once.
|
||||
func SetNameserverIPMatcher(fn func(ip net.IP) bool) error {
|
||||
@@ -196,92 +113,38 @@ func SetNameserverIPMatcher(fn func(ip net.IP) bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func handlePacket(ctx context.Context, pkt packet.Packet) {
|
||||
// Record metrics.
|
||||
startTime := time.Now()
|
||||
defer packetHandlingHistogram.UpdateDuration(startTime)
|
||||
|
||||
if fastTrackedPermit(pkt) {
|
||||
func handlePacket(pkt packet.Packet) {
|
||||
// First, check for an existing connection.
|
||||
conn, ok := network.GetConnection(pkt.GetConnectionID())
|
||||
if ok {
|
||||
// Add packet to connection handler queue or apply verdict directly.
|
||||
conn.HandlePacket(pkt)
|
||||
return
|
||||
}
|
||||
|
||||
// Add context tracer and set context on packet.
|
||||
traceCtx, tracer := log.AddTracer(ctx)
|
||||
if tracer != nil {
|
||||
// The trace is submitted in `network.Connection.packetHandler()`.
|
||||
tracer.Tracef("filter: handling packet: %s", pkt)
|
||||
}
|
||||
pkt.SetCtx(traceCtx)
|
||||
// Else create new incomplete connection from the packet and start the new handler.
|
||||
conn = network.NewIncompleteConnection(pkt)
|
||||
conn.Lock()
|
||||
defer conn.Unlock()
|
||||
conn.SetFirewallHandler(fastTrackHandler)
|
||||
|
||||
// Get connection of packet.
|
||||
conn, err := getConnection(pkt)
|
||||
if err != nil {
|
||||
tracer.Errorf("filter: packet %s dropped: %s", pkt, err)
|
||||
_ = pkt.Drop()
|
||||
return
|
||||
}
|
||||
|
||||
// handle packet
|
||||
// Let the new connection handler worker handle the packet.
|
||||
conn.HandlePacket(pkt)
|
||||
}
|
||||
|
||||
var getConnectionSingleInflight singleflight.Group
|
||||
|
||||
func getConnection(pkt packet.Packet) (*network.Connection, error) {
|
||||
created := false
|
||||
|
||||
// Create or get connection in single inflight lock in order to prevent duplicates.
|
||||
newConn, err, shared := getConnectionSingleInflight.Do(pkt.GetConnectionID(), func() (interface{}, error) {
|
||||
// First, check for an existing connection.
|
||||
conn, ok := network.GetConnection(pkt.GetConnectionID())
|
||||
if ok {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// Else create new one from the packet.
|
||||
conn = network.NewConnectionFromFirstPacket(pkt)
|
||||
conn.Lock()
|
||||
defer conn.Unlock()
|
||||
conn.SetFirewallHandler(initialHandler)
|
||||
created = true
|
||||
return conn, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get connection: %w", err)
|
||||
}
|
||||
if newConn == nil {
|
||||
return nil, errors.New("connection getter returned nil")
|
||||
}
|
||||
|
||||
// Transform and log result.
|
||||
conn := newConn.(*network.Connection) //nolint:forcetypeassert // Can only be a *network.Connection.
|
||||
sharedIndicator := ""
|
||||
if shared {
|
||||
sharedIndicator = " (shared)"
|
||||
}
|
||||
if created {
|
||||
log.Tracer(pkt.Ctx()).Tracef("filter: created new connection %s%s", conn.ID, sharedIndicator)
|
||||
} else {
|
||||
log.Tracer(pkt.Ctx()).Tracef("filter: assigned connection %s%s", conn.ID, sharedIndicator)
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// fastTrackedPermit quickly permits certain network critical or internal connections.
|
||||
func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
||||
func fastTrackedPermit(pkt packet.Packet) (verdict network.Verdict, permanent bool) {
|
||||
meta := pkt.Info()
|
||||
|
||||
// Check if packed was already fast-tracked by the OS integration.
|
||||
if pkt.FastTrackedByIntegration() {
|
||||
log.Debugf("filter: fast-tracked by OS integration: %s", pkt)
|
||||
return true
|
||||
return network.VerdictAccept, true
|
||||
}
|
||||
|
||||
// Check if connection was already blocked.
|
||||
if meta.Dst.Equal(blockedIPv4) || meta.Dst.Equal(blockedIPv6) {
|
||||
_ = pkt.PermanentBlock()
|
||||
return true
|
||||
return network.VerdictBlock, true
|
||||
}
|
||||
|
||||
// Some programs do a network self-check where they connects to the same
|
||||
@@ -290,8 +153,8 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
||||
if meta.SrcPort == meta.DstPort &&
|
||||
meta.Src.Equal(meta.Dst) {
|
||||
log.Debugf("filter: fast-track network self-check: %s", pkt)
|
||||
_ = pkt.PermanentAccept()
|
||||
return true
|
||||
return network.VerdictAccept, true
|
||||
|
||||
}
|
||||
|
||||
switch meta.Protocol { //nolint:exhaustive // Checking for specific values only.
|
||||
@@ -300,8 +163,7 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
||||
err := pkt.LoadPacketData()
|
||||
if err != nil {
|
||||
log.Debugf("filter: failed to load ICMP packet data: %s", err)
|
||||
_ = pkt.PermanentAccept()
|
||||
return true
|
||||
return network.VerdictAccept, true
|
||||
}
|
||||
|
||||
// Submit to ICMP listener.
|
||||
@@ -311,8 +173,7 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
||||
// permanent accept, because then we won't see any future packets of that
|
||||
// connection and thus cannot continue to submit them.
|
||||
log.Debugf("filter: fast-track tracing ICMP/v6: %s", pkt)
|
||||
_ = pkt.Accept()
|
||||
return true
|
||||
return network.VerdictAccept, false
|
||||
}
|
||||
|
||||
// Handle echo request and replies regularly.
|
||||
@@ -323,20 +184,19 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
||||
switch icmpLayer.TypeCode.Type() {
|
||||
case layers.ICMPv4TypeEchoRequest,
|
||||
layers.ICMPv4TypeEchoReply:
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
}
|
||||
case *layers.ICMPv6:
|
||||
switch icmpLayer.TypeCode.Type() {
|
||||
case layers.ICMPv6TypeEchoRequest,
|
||||
layers.ICMPv6TypeEchoReply:
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
}
|
||||
}
|
||||
|
||||
// Permit all ICMP/v6 packets that are not echo requests or replies.
|
||||
log.Debugf("filter: fast-track accepting ICMP/v6: %s", pkt)
|
||||
_ = pkt.PermanentAccept()
|
||||
return true
|
||||
return network.VerdictAccept, true
|
||||
|
||||
case packet.UDP, packet.TCP:
|
||||
switch meta.DstPort {
|
||||
@@ -346,37 +206,36 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
||||
|
||||
// DHCP and DHCPv6 must be UDP.
|
||||
if meta.Protocol != packet.UDP {
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
}
|
||||
|
||||
// DHCP is only valid in local network scopes.
|
||||
switch netutils.ClassifyIP(meta.Dst) { //nolint:exhaustive // Checking for specific values only.
|
||||
case netutils.HostLocal, netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
||||
default:
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
}
|
||||
|
||||
// Log and permit.
|
||||
log.Debugf("filter: fast-track accepting DHCP: %s", pkt)
|
||||
_ = pkt.PermanentAccept()
|
||||
return true
|
||||
return network.VerdictAccept, true
|
||||
|
||||
case apiPort:
|
||||
// Always allow direct access to the Portmaster API.
|
||||
|
||||
// Portmaster API is TCP only.
|
||||
if meta.Protocol != packet.TCP {
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
}
|
||||
|
||||
// Check if the api port is even set.
|
||||
if !apiPortSet {
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
}
|
||||
|
||||
// Must be destined for the API IP.
|
||||
if !meta.Dst.Equal(apiIP) {
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
}
|
||||
|
||||
// Only fast-track local requests.
|
||||
@@ -384,15 +243,14 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
||||
switch {
|
||||
case err != nil:
|
||||
log.Debugf("filter: failed to check if %s is own IP for fast-track: %s", meta.Src, err)
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
case !isMe:
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
}
|
||||
|
||||
// Log and permit.
|
||||
log.Debugf("filter: fast-track accepting api connection: %s", pkt)
|
||||
_ = pkt.PermanentAccept()
|
||||
return true
|
||||
return network.VerdictAccept, true
|
||||
|
||||
case 53:
|
||||
// Always allow direct access to the Portmaster Nameserver.
|
||||
@@ -400,12 +258,12 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
||||
|
||||
// Check if a nameserver IP matcher is set.
|
||||
if !nameserverIPMatcherReady.IsSet() {
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
}
|
||||
|
||||
// Check if packet is destined for a nameserver IP.
|
||||
if !nameserverIPMatcher(meta.Dst) {
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
}
|
||||
|
||||
// Only fast-track local requests.
|
||||
@@ -413,32 +271,76 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) {
|
||||
switch {
|
||||
case err != nil:
|
||||
log.Debugf("filter: failed to check if %s is own IP for fast-track: %s", meta.Src, err)
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
case !isMe:
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
}
|
||||
|
||||
// Log and permit.
|
||||
log.Debugf("filter: fast-track accepting local dns: %s", pkt)
|
||||
_ = pkt.PermanentAccept()
|
||||
return true
|
||||
return network.VerdictAccept, true
|
||||
}
|
||||
|
||||
case compat.SystemIntegrationCheckProtocol:
|
||||
if pkt.Info().Dst.Equal(compat.SystemIntegrationCheckDstIP) {
|
||||
compat.SubmitSystemIntegrationCheckPacket(pkt)
|
||||
_ = pkt.Drop()
|
||||
return true
|
||||
return network.VerdictDrop, false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return network.VerdictUndecided, false
|
||||
}
|
||||
|
||||
func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
||||
func fastTrackHandler(conn *network.Connection, pkt packet.Packet) {
|
||||
fastTrackedVerdict, permanent := fastTrackedPermit(pkt)
|
||||
if fastTrackedVerdict != network.VerdictUndecided {
|
||||
// Set verdict on connection.
|
||||
conn.Verdict.Active = fastTrackedVerdict
|
||||
conn.Verdict.Firewall = fastTrackedVerdict
|
||||
// Apply verdict to (real) packet.
|
||||
if !pkt.InfoOnly() {
|
||||
issueVerdict(conn, pkt, fastTrackedVerdict, permanent)
|
||||
}
|
||||
// Stop handler if permanent.
|
||||
if permanent {
|
||||
conn.SetVerdict(fastTrackedVerdict, "fast-tracked", "", nil)
|
||||
conn.Verdict.Worst = fastTrackedVerdict
|
||||
// Do not finalize verdict, as we are missing necessary data.
|
||||
conn.StopFirewallHandler()
|
||||
}
|
||||
|
||||
// Do not continue to next handler.
|
||||
return
|
||||
}
|
||||
|
||||
// If packet is not fast-tracked, continue with gathering more information.
|
||||
conn.UpdateFirewallHandler(gatherDataHandler)
|
||||
gatherDataHandler(conn, pkt)
|
||||
}
|
||||
|
||||
func gatherDataHandler(conn *network.Connection, pkt packet.Packet) {
|
||||
// Get process info
|
||||
_ = conn.GatherConnectionInfo(pkt)
|
||||
// Errors are informational and are logged to the context.
|
||||
|
||||
// Run this handler again if data is not yet complete.
|
||||
if !conn.DataIsComplete() {
|
||||
return
|
||||
}
|
||||
|
||||
// Continue to filter handler, when connection data is complete.
|
||||
conn.UpdateFirewallHandler(filterHandler)
|
||||
filterHandler(conn, pkt)
|
||||
}
|
||||
|
||||
func filterHandler(conn *network.Connection, pkt packet.Packet) {
|
||||
// Skip if data is not complete.
|
||||
if !conn.DataIsComplete() {
|
||||
return
|
||||
}
|
||||
|
||||
filterConnection := true
|
||||
|
||||
log.Tracer(pkt.Ctx()).Trace("filter: handing over to connection-based handler")
|
||||
// Check for special (internal) connection cases.
|
||||
switch {
|
||||
case !conn.Inbound && localPortIsPreAuthenticated(conn.Entity.Protocol, conn.LocalPort):
|
||||
@@ -480,8 +382,8 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
||||
switch {
|
||||
case conn.Inspecting:
|
||||
log.Tracer(pkt.Ctx()).Trace("filter: start inspecting")
|
||||
conn.SetFirewallHandler(inspectThenVerdict)
|
||||
inspectThenVerdict(conn, pkt)
|
||||
conn.SetFirewallHandler(inspectAndVerdictHandler)
|
||||
inspectAndVerdictHandler(conn, pkt)
|
||||
default:
|
||||
conn.StopFirewallHandler()
|
||||
issueVerdict(conn, pkt, 0, true)
|
||||
@@ -490,6 +392,11 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
||||
|
||||
// FilterConnection runs all the filtering (and tunneling) procedures.
|
||||
func FilterConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet, checkFilter, checkTunnel bool) {
|
||||
// Skip if data is not complete.
|
||||
if !conn.DataIsComplete() {
|
||||
return
|
||||
}
|
||||
|
||||
if checkFilter {
|
||||
if filterEnabled() {
|
||||
log.Tracer(ctx).Trace("filter: starting decision process")
|
||||
@@ -537,12 +444,11 @@ func FilterConnection(ctx context.Context, conn *network.Connection, pkt packet.
|
||||
}
|
||||
}
|
||||
|
||||
func defaultHandler(conn *network.Connection, pkt packet.Packet) {
|
||||
// TODO: `pkt` has an active trace log, which we currently don't submit.
|
||||
func verdictHandler(conn *network.Connection, pkt packet.Packet) {
|
||||
issueVerdict(conn, pkt, 0, true)
|
||||
}
|
||||
|
||||
func inspectThenVerdict(conn *network.Connection, pkt packet.Packet) {
|
||||
func inspectAndVerdictHandler(conn *network.Connection, pkt packet.Packet) {
|
||||
pktVerdict, continueInspection := inspection.RunInspectors(conn, pkt)
|
||||
if continueInspection {
|
||||
issueVerdict(conn, pkt, pktVerdict, false)
|
||||
@@ -555,6 +461,11 @@ func inspectThenVerdict(conn *network.Connection, pkt packet.Packet) {
|
||||
}
|
||||
|
||||
func issueVerdict(conn *network.Connection, pkt packet.Packet, verdict network.Verdict, allowPermanent bool) {
|
||||
// Check if packed was already fast-tracked by the OS integration.
|
||||
if pkt.FastTrackedByIntegration() {
|
||||
return
|
||||
}
|
||||
|
||||
// enable permanent verdict
|
||||
if allowPermanent && !conn.VerdictPermanent {
|
||||
conn.VerdictPermanent = permanentVerdicts()
|
||||
@@ -689,14 +600,66 @@ func packetHandler(ctx context.Context) error {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case pkt := <-interception.Packets:
|
||||
interceptionModule.StartWorker("initial packet handler", func(workerCtx context.Context) error {
|
||||
handlePacket(workerCtx, pkt)
|
||||
return nil
|
||||
})
|
||||
if pkt != nil {
|
||||
handlePacket(pkt)
|
||||
} else {
|
||||
return errors.New("received nil packet from interception")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func bandwidthUpdateHandler(ctx context.Context) error {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case bwUpdate := <-interception.BandwidthUpdates:
|
||||
if bwUpdate != nil {
|
||||
updateBandwidth(bwUpdate)
|
||||
// DEBUG:
|
||||
// log.Debugf("filter: bandwidth update: %s", bwUpdate)
|
||||
} else {
|
||||
return errors.New("received nil bandwidth update from interception")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func updateBandwidth(bwUpdate *packet.BandwidthUpdate) {
|
||||
// Check if update makes sense.
|
||||
if bwUpdate.RecvBytes == 0 && bwUpdate.SentBytes == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Get connection.
|
||||
conn, ok := network.GetConnection(bwUpdate.ConnID)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Do not wait for connections that are locked.
|
||||
// TODO: Use atomic operations for updating bandwidth stats.
|
||||
if !conn.TryLock() {
|
||||
return
|
||||
}
|
||||
defer conn.Unlock()
|
||||
|
||||
// Update stats according to method.
|
||||
switch bwUpdate.Method {
|
||||
case packet.Absolute:
|
||||
conn.RecvBytes = bwUpdate.RecvBytes
|
||||
conn.SentBytes = bwUpdate.SentBytes
|
||||
case packet.Additive:
|
||||
conn.RecvBytes += bwUpdate.RecvBytes
|
||||
conn.SentBytes += bwUpdate.SentBytes
|
||||
default:
|
||||
log.Warningf("filter: unsupported bandwidth update method: %d", bwUpdate.Method)
|
||||
}
|
||||
|
||||
// TODO: Send update.
|
||||
}
|
||||
|
||||
func statLogger(ctx context.Context) error {
|
||||
for {
|
||||
select {
|
||||
41
go.mod
41
go.mod
@@ -4,6 +4,7 @@ go 1.19
|
||||
|
||||
require (
|
||||
github.com/agext/levenshtein v1.2.3
|
||||
github.com/cilium/ebpf v0.11.0
|
||||
github.com/coreos/go-iptables v0.6.0
|
||||
github.com/florianl/go-conntrack v0.4.0
|
||||
github.com/florianl/go-nfqueue v1.3.1
|
||||
@@ -13,23 +14,23 @@ require (
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/jackc/puddle/v2 v2.2.0
|
||||
github.com/miekg/dns v1.1.54
|
||||
github.com/oschwald/maxminddb-golang v1.10.0
|
||||
github.com/jackc/puddle/v2 v2.2.1
|
||||
github.com/miekg/dns v1.1.55
|
||||
github.com/oschwald/maxminddb-golang v1.11.0
|
||||
github.com/safing/jess v0.3.1
|
||||
github.com/safing/portbase v0.16.6
|
||||
github.com/safing/portmaster-android/go v0.0.0-20230414104013-be2efcb51d9d
|
||||
github.com/safing/portbase v0.17.0
|
||||
github.com/safing/portmaster-android/go v0.0.0-20230605085256-6abf4c495626
|
||||
github.com/safing/spn v0.6.8
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/spkg/zipfs v0.7.1
|
||||
github.com/stretchr/testify v1.8.1
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/tannerryan/ring v1.1.2
|
||||
github.com/tevino/abool v1.2.0
|
||||
github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26
|
||||
golang.org/x/net v0.10.0
|
||||
golang.org/x/sync v0.2.0
|
||||
golang.org/x/sys v0.8.0
|
||||
golang.org/x/net v0.12.0
|
||||
golang.org/x/sync v0.3.0
|
||||
golang.org/x/sys v0.10.0
|
||||
zombiezen.com/go/sqlite v0.13.0
|
||||
)
|
||||
|
||||
@@ -37,7 +38,7 @@ require (
|
||||
github.com/VictoriaMetrics/metrics v1.24.0 // indirect
|
||||
github.com/aead/ecdh v0.2.0 // indirect
|
||||
github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6 // indirect
|
||||
github.com/alessio/shellescape v1.4.1 // indirect
|
||||
github.com/alessio/shellescape v1.4.2 // indirect
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/awalterschulze/gographviz v2.0.3+incompatible // indirect
|
||||
github.com/bluele/gcache v0.0.2 // indirect
|
||||
@@ -65,7 +66,7 @@ require (
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rot256/pblind v0.0.0-20211117203330-22455f90b565 // indirect
|
||||
github.com/rot256/pblind v0.0.0-20230622102829-4dc2c6e4b857 // indirect
|
||||
github.com/satori/go.uuid v1.2.0 // indirect
|
||||
github.com/seehuhn/fortuna v1.0.1 // indirect
|
||||
github.com/seehuhn/sha256d v1.0.0 // indirect
|
||||
@@ -75,7 +76,7 @@ require (
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.11 // indirect
|
||||
github.com/tklauser/numcpus v0.6.0 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/valyala/fastrand v1.1.0 // indirect
|
||||
github.com/valyala/histogram v1.2.0 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
||||
@@ -84,16 +85,16 @@ require (
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
github.com/zalando/go-keyring v0.2.3 // indirect
|
||||
go.etcd.io/bbolt v1.3.7 // indirect
|
||||
golang.org/x/crypto v0.9.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/crypto v0.11.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/tools v0.9.1 // indirect
|
||||
golang.org/x/tools v0.11.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5 // indirect
|
||||
modernc.org/libc v1.22.6 // indirect
|
||||
modernc.org/mathutil v1.5.0 // indirect
|
||||
modernc.org/memory v1.5.0 // indirect
|
||||
modernc.org/sqlite v1.22.1 // indirect
|
||||
modernc.org/libc v1.24.1 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.6.0 // indirect
|
||||
modernc.org/sqlite v1.24.0 // indirect
|
||||
)
|
||||
|
||||
89
go.sum
89
go.sum
@@ -14,8 +14,9 @@ github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6 h1:5L8Mj9Co9sJVgW3TpY
|
||||
github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6/go.mod h1:3HgLJ9d18kXMLQlJvIY3+FszZYMxCz8WfE2MQ7hDY0w=
|
||||
github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
|
||||
github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
|
||||
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
|
||||
github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0=
|
||||
github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
@@ -31,6 +32,8 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cilium/ebpf v0.5.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||
github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y=
|
||||
github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk=
|
||||
@@ -60,6 +63,7 @@ github.com/florianl/go-conntrack v0.4.0/go.mod h1:iPDx4oIats2T7X7Jm3PFyRCJM1GfZh
|
||||
github.com/florianl/go-nfqueue v1.3.1 h1:khQ9fYCrjbu5CF8dZF55G2RTIEIQRI0Aj5k3msJR6Gw=
|
||||
github.com/florianl/go-nfqueue v1.3.1/go.mod h1:aHWbgkhryJxF5XxYvJ3oRZpdD4JP74Zu/hP1zuhja+M=
|
||||
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fxamacker/cbor v1.5.1 h1:XjQWBgdmQyqimslUh5r4tUGmoqzHmBFQOImkWGi2awg=
|
||||
github.com/fxamacker/cbor v1.5.1/go.mod h1:3aPGItF174ni7dDzd6JZ206H8cmr4GDNBGpPa971zsU=
|
||||
@@ -118,8 +122,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jackc/puddle/v2 v2.2.0 h1:RdcDk92EJBuBS55nQMMYFXTxwstHug4jkhT5pq8VxPk=
|
||||
github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
|
||||
@@ -135,11 +139,11 @@ github.com/jsimonetti/rtnetlink v0.0.0-20210525051524-4cc836578190/go.mod h1:NmK
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20211022192332-93da33804786/go.mod h1:v4hqbTdfQngbVSZJVWUhGE/lbTFf9jb+ygmNUDQMuOs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
@@ -172,8 +176,8 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U
|
||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
|
||||
github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/miekg/dns v1.1.55 h1:GoQ4hpsj0nFLYe+bWiCToyrBEJXkQfOOIvFGFy0lEgo=
|
||||
github.com/miekg/dns v1.1.55/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
@@ -182,19 +186,19 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
|
||||
github.com/oschwald/maxminddb-golang v1.11.0 h1:aSXMqYR/EPNjGE8epgqwDay+P30hCBZIveY0WZbAWh0=
|
||||
github.com/oschwald/maxminddb-golang v1.11.0/go.mod h1:YmVI+H0zh3ySFR3w+oz8PCfglAFj3PuCmui13+P9zDg=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rot256/pblind v0.0.0-20211117203330-22455f90b565 h1:jVOT0WWSrjQx6pYq48qSJCrlQ3XU1BHvEI6PsRsE9Bc=
|
||||
github.com/rot256/pblind v0.0.0-20211117203330-22455f90b565/go.mod h1:SI9+Ls7HSJkgYArhh8oBPhXiNL7tJltkU1H6Pm2o8Zo=
|
||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||
github.com/rot256/pblind v0.0.0-20230622102829-4dc2c6e4b857 h1:0FEd4UaKVIMBc18S6zAHWtvqpUPcLVhGU2lweJwJhuA=
|
||||
github.com/rot256/pblind v0.0.0-20230622102829-4dc2c6e4b857/go.mod h1:SI9+Ls7HSJkgYArhh8oBPhXiNL7tJltkU1H6Pm2o8Zo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/safing/jess v0.3.0/go.mod h1:JbYsPk5iJZx0OXDZeMcjS9qEdkGVUg+DCA8Fw2LdN9s=
|
||||
@@ -202,10 +206,10 @@ github.com/safing/jess v0.3.1 h1:cMZVhi2whW/YdD98MPLeLIWJndQ7o2QVt2HefQ/ByFA=
|
||||
github.com/safing/jess v0.3.1/go.mod h1:aj73Eot1zm2ETkJuw9hJlIO8bRom52uBbsCHemvlZmA=
|
||||
github.com/safing/portbase v0.15.2/go.mod h1:5bHi99fz7Hh/wOsZUOI631WF9ePSHk57c4fdlOMS91Y=
|
||||
github.com/safing/portbase v0.16.2/go.mod h1:mzNCWqPbO7vIYbbK5PElGbudwd2vx4YPNawymL8Aro8=
|
||||
github.com/safing/portbase v0.16.6 h1:6MbK8VGusSKswmbZ031Dher1yijYGxUQ36nNuEIP9P8=
|
||||
github.com/safing/portbase v0.16.6/go.mod h1:CJSY+0pgbgh00UcM9myS/l+Bcgqgc6afLJX2BqzD+Ek=
|
||||
github.com/safing/portmaster-android/go v0.0.0-20230414104013-be2efcb51d9d h1:HUPeI54LUyq42YRtd1GdystaQioBJrCY1jPJY36U2nE=
|
||||
github.com/safing/portmaster-android/go v0.0.0-20230414104013-be2efcb51d9d/go.mod h1:E3MFiTwsHxsPfzI+CGhpE8BOGQYS/V7tkXttMTFeyuc=
|
||||
github.com/safing/portbase v0.17.0 h1:RsDzbCGxRIbgaArri3y7MZskfxytEvvkzJpiboDUERQ=
|
||||
github.com/safing/portbase v0.17.0/go.mod h1:eKCRqsfMFLVhNpd2sY/fKvnbuk+LrIYnQEZCg1i86Ho=
|
||||
github.com/safing/portmaster-android/go v0.0.0-20230605085256-6abf4c495626 h1:olc/REnUdpJN/Gmz8B030OxLpMYxyPDTrDILNEw0eKs=
|
||||
github.com/safing/portmaster-android/go v0.0.0-20230605085256-6abf4c495626/go.mod h1:abwyAQrZGemWbSh/aCD9nnkp0SvFFf/mGWkAbOwPnFE=
|
||||
github.com/safing/spn v0.6.8 h1:2obvyMzyw5X3CIYedLBE88kNBBrJumF84q1qtQSFqkc=
|
||||
github.com/safing/spn v0.6.8/go.mod h1:Mh9bmkqFhO/dHNi9RWXzoXjQij893I4Lj8Wn4tQ0KZA=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
@@ -234,7 +238,6 @@ github.com/spkg/zipfs v0.7.1/go.mod h1:48LW+/Rh1G7aAav1ew1PdlYn52T+LM+ARmSHfDNJv
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
@@ -243,8 +246,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/tannerryan/ring v1.1.2 h1:iXayOjqHQOLzuy9GwSKuG3nhWfzQkldMlQivcgIr7gQ=
|
||||
github.com/tannerryan/ring v1.1.2/go.mod h1:DkELJEjbZhJBtFKR9Xziwj3HKZnb/knRgljNqp65vH4=
|
||||
github.com/tevino/abool v1.2.0 h1:heAkClL8H6w+mK5md9dzsuohKeXHUpY7Vw0ZCKW+huA=
|
||||
@@ -267,8 +270,9 @@ github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7Am
|
||||
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/tklauser/numcpus v0.5.0/go.mod h1:OGzpTxpcIMNGYQdit2BYL1pvk/dSOaJWjKoflh+RQjo=
|
||||
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
|
||||
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
|
||||
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
|
||||
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26 h1:UFHFmFfixpmfRBcxuu+LA9l8MdURWVdVNUHxO5n1d2w=
|
||||
github.com/umahmood/haversine v0.0.0-20151105152445-808ab04add26/go.mod h1:IGhd0qMDsUa9acVjsbsT7bu3ktadtGOHI79+idTew/M=
|
||||
@@ -301,17 +305,17 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
|
||||
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
|
||||
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -334,14 +338,14 @@ golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20220107192237-5cfca573fb4d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
|
||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -386,8 +390,9 @@ golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -403,8 +408,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
|
||||
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
golang.org/x/tools v0.11.0 h1:EMCa6U9S2LtZXLAMoWiR/R8dAQFRqbAitmbJ2UKhoi8=
|
||||
golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -413,7 +418,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
|
||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -427,13 +432,13 @@ gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5 h1:cv/zaNV0nr1mJzaeo4S5mHIm
|
||||
gvisor.dev/gvisor v0.0.0-20220817001344-846276b3dbc5/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
|
||||
honnef.co/go/tools v0.2.1/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
|
||||
honnef.co/go/tools v0.2.2/go.mod h1:lPVVZ2BS5TfnjLyizF7o7hv7j9/L+8cZY2hLyjP9cGY=
|
||||
modernc.org/libc v1.22.6 h1:cbXU8R+A6aOjRuhsFh3nbDWXO/Hs4ClJRXYB11KmPDo=
|
||||
modernc.org/libc v1.22.6/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.22.1 h1:P2+Dhp5FR1RlVRkQ3dDfCiv3Ok8XPxqpe70IjYVA9oE=
|
||||
modernc.org/sqlite v1.22.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
|
||||
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o=
|
||||
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||
modernc.org/sqlite v1.24.0 h1:EsClRIWHGhLTCX44p+Ri/JLD+vFGo0QGjasg2/F9TlI=
|
||||
modernc.org/sqlite v1.24.0/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
||||
zombiezen.com/go/sqlite v0.13.0 h1:iEeyVqcm3fk5PCA8OQBhBxPnqrP4yYuVJBF+XZpSnOE=
|
||||
zombiezen.com/go/sqlite v0.13.0/go.mod h1:Ht/5Rg3Ae2hoyh1I7gbWtWAl89CNocfqeb/aAMTkJr4=
|
||||
|
||||
@@ -87,6 +87,9 @@ func (mng *Manager) HandleFeed(ctx context.Context, feed <-chan *network.Connect
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if !conn.DataIsComplete() {
|
||||
continue
|
||||
}
|
||||
|
||||
model, err := convertConnection(conn)
|
||||
if err != nil {
|
||||
|
||||
@@ -51,6 +51,11 @@ func cleanConnections() (activePIDs map[int]struct{}) {
|
||||
|
||||
// delete inactive connections
|
||||
switch {
|
||||
case !conn.DataIsComplete():
|
||||
// Step 0: delete old incomplete connections
|
||||
if conn.Started < deleteOlderThan {
|
||||
conn.delete()
|
||||
}
|
||||
case conn.Ended == 0:
|
||||
// Step 1: check if still active
|
||||
exists := state.Exists(&packet.Info{
|
||||
@@ -61,6 +66,7 @@ func cleanConnections() (activePIDs map[int]struct{}) {
|
||||
SrcPort: conn.LocalPort,
|
||||
Dst: conn.Entity.IP,
|
||||
DstPort: conn.Entity.Port,
|
||||
PID: process.UndefinedProcessID,
|
||||
}, now)
|
||||
|
||||
activePIDs[conn.process.Pid] = struct{}{}
|
||||
|
||||
@@ -4,9 +4,12 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/tevino/abool"
|
||||
|
||||
"github.com/safing/portbase/database/record"
|
||||
"github.com/safing/portbase/log"
|
||||
"github.com/safing/portmaster/intel"
|
||||
@@ -102,6 +105,8 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
|
||||
// set for connections created from DNS requests. LocalPort is
|
||||
// considered immutable once a connection object has been created.
|
||||
LocalPort uint16
|
||||
// PID holds the PID of the owning process.
|
||||
PID int
|
||||
// Entity describes the remote entity that the connection has been
|
||||
// established to. The entity might be changed or information might
|
||||
// be added to it during the livetime of a connection. Access to
|
||||
@@ -168,6 +173,22 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
|
||||
StopTunnel() error
|
||||
}
|
||||
|
||||
RecvBytes uint64
|
||||
SentBytes uint64
|
||||
|
||||
// pkgQueue is used to serialize packet handling for a single
|
||||
// connection and is served by the connections packetHandler.
|
||||
pktQueue chan packet.Packet
|
||||
// pktQueueActive signifies whether the packet queue is active and may be written to.
|
||||
pktQueueActive bool
|
||||
// pktQueueLock locks access to pktQueueActive and writing to pktQueue.
|
||||
pktQueueLock sync.Mutex
|
||||
|
||||
// dataComplete signifies that all information about the connection is
|
||||
// available and an actual packet has been seen.
|
||||
// As long as this flag is not set, the connection may not be evaluated for
|
||||
// a verdict and may not be sent to the UI.
|
||||
dataComplete *abool.AtomicBool
|
||||
// Internal is set to true if the connection is attributed as an
|
||||
// Portmaster internal connection. Internal may be set at different
|
||||
// points and access to it must be guarded by the connection lock.
|
||||
@@ -175,9 +196,6 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
|
||||
// process holds a reference to the actor process. That is, the
|
||||
// process instance that initiated the connection.
|
||||
process *process.Process
|
||||
// pkgQueue is used to serialize packet handling for a single
|
||||
// connection and is served by the connections packetHandler.
|
||||
pktQueue chan packet.Packet
|
||||
// firewallHandler is the firewall handler that is called for
|
||||
// each packet sent to pktQueue.
|
||||
firewallHandler FirewallHandler
|
||||
@@ -250,29 +268,50 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri
|
||||
ipVersion = packet.IPv4
|
||||
}
|
||||
|
||||
// get Process
|
||||
proc, _, err := process.GetProcessByConnection(
|
||||
ctx,
|
||||
&packet.Info{
|
||||
Inbound: false, // outbound as we are looking for the process of the source address
|
||||
Version: ipVersion,
|
||||
Protocol: packet.UDP,
|
||||
Src: localIP, // source as in the process we are looking for
|
||||
SrcPort: localPort, // source as in the process we are looking for
|
||||
Dst: nil, // do not record direction
|
||||
DstPort: 0, // do not record direction
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
log.Tracer(ctx).Debugf("network: failed to find process of dns request for %s: %s", fqdn, err)
|
||||
proc = process.GetUnidentifiedProcess(ctx)
|
||||
// Create packet info for dns request connection.
|
||||
pi := &packet.Info{
|
||||
Inbound: false, // outbound as we are looking for the process of the source address
|
||||
Version: ipVersion,
|
||||
Protocol: packet.UDP,
|
||||
Src: localIP, // source as in the process we are looking for
|
||||
SrcPort: localPort, // source as in the process we are looking for
|
||||
Dst: nil, // do not record direction
|
||||
DstPort: 0, // do not record direction
|
||||
PID: process.UndefinedProcessID,
|
||||
}
|
||||
|
||||
// Check if the dns request connection was reported with process info.
|
||||
dnsRequestConnID := pi.CreateConnectionID()
|
||||
// Cut the destination, as the dns request may have been redirected and we
|
||||
// don't know the original destination.
|
||||
dnsRequestConnIDPrefix, ok := strings.CutSuffix(dnsRequestConnID, "<nil>-0")
|
||||
if !ok {
|
||||
log.Tracer(ctx).Warningf("network: unexpected connection ID for finding dns requests connection: %s", dnsRequestConnID)
|
||||
}
|
||||
// Find matching dns request connection.
|
||||
dnsRequestConn, ok := conns.findByPrefix(dnsRequestConnIDPrefix)
|
||||
if ok && dnsRequestConn.PID != process.UndefinedProcessID {
|
||||
log.Tracer(ctx).Debugf("network: found matching dns request connection %s", dnsRequestConn)
|
||||
pi.PID = dnsRequestConn.PID
|
||||
}
|
||||
|
||||
// Find process by remote IP/Port.
|
||||
if pi.PID == process.UndefinedProcessID {
|
||||
pi.PID, _, _ = process.GetPidOfConnection(
|
||||
ctx,
|
||||
pi,
|
||||
)
|
||||
}
|
||||
|
||||
// Get process and profile with PID.
|
||||
proc, _ := process.GetProcessWithProfile(ctx, pi.PID)
|
||||
|
||||
timestamp := time.Now().Unix()
|
||||
dnsConn := &Connection{
|
||||
ID: connID,
|
||||
Type: DNSRequest,
|
||||
Scope: fqdn,
|
||||
PID: proc.Pid,
|
||||
Entity: &intel.Entity{
|
||||
Domain: fqdn,
|
||||
CNAME: cnames,
|
||||
@@ -281,6 +320,7 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri
|
||||
ProcessContext: getProcessContext(ctx, proc),
|
||||
Started: timestamp,
|
||||
Ended: timestamp,
|
||||
dataComplete: abool.NewBool(true),
|
||||
}
|
||||
|
||||
// Inherit internal status of profile.
|
||||
@@ -292,6 +332,7 @@ func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []stri
|
||||
// query. Blocked requests are saved immediately, accepted ones are only
|
||||
// saved if they are not "used" by a connection.
|
||||
|
||||
dnsConn.UpdateMeta()
|
||||
return dnsConn
|
||||
}
|
||||
|
||||
@@ -308,6 +349,7 @@ func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cname
|
||||
Type: DNSRequest,
|
||||
External: true,
|
||||
Scope: fqdn,
|
||||
PID: process.NetworkHostProcessID,
|
||||
Entity: &intel.Entity{
|
||||
Domain: fqdn,
|
||||
CNAME: cnames,
|
||||
@@ -316,6 +358,7 @@ func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cname
|
||||
ProcessContext: getProcessContext(ctx, remoteHost),
|
||||
Started: timestamp,
|
||||
Ended: timestamp,
|
||||
dataComplete: abool.NewBool(true),
|
||||
}
|
||||
|
||||
// Inherit internal status of profile.
|
||||
@@ -327,52 +370,116 @@ func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cname
|
||||
// query. Blocked requests are saved immediately, accepted ones are only
|
||||
// saved if they are not "used" by a connection.
|
||||
|
||||
dnsConn.UpdateMeta()
|
||||
return dnsConn, nil
|
||||
}
|
||||
|
||||
// NewConnectionFromFirstPacket returns a new connection based on the given packet.
|
||||
func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
|
||||
// get Process
|
||||
proc, inbound, err := process.GetProcessByConnection(pkt.Ctx(), pkt.Info())
|
||||
if err != nil {
|
||||
log.Tracer(pkt.Ctx()).Debugf("network: failed to find process of packet %s: %s", pkt, err)
|
||||
if inbound && !netutils.ClassifyIP(pkt.Info().Dst).IsLocalhost() {
|
||||
proc = process.GetUnsolicitedProcess(pkt.Ctx())
|
||||
// NewIncompleteConnection creates a new incomplete connection with only minimal information.
|
||||
func NewIncompleteConnection(pkt packet.Packet) *Connection {
|
||||
info := pkt.Info()
|
||||
|
||||
// Create new connection object.
|
||||
// We do not yet know the direction of the connection for sure, so we can only set minimal information.
|
||||
conn := &Connection{
|
||||
ID: pkt.GetConnectionID(),
|
||||
Type: IPConnection,
|
||||
IPVersion: info.Version,
|
||||
IPProtocol: info.Protocol,
|
||||
Started: info.SeenAt.Unix(),
|
||||
PID: info.PID,
|
||||
dataComplete: abool.NewBool(false),
|
||||
}
|
||||
|
||||
// Save connection to internal state in order to mitigate creation of
|
||||
// duplicates. Do not propagate yet, as data is not yet complete.
|
||||
conn.UpdateMeta()
|
||||
conns.add(conn)
|
||||
|
||||
return conn
|
||||
}
|
||||
|
||||
// GatherConnectionInfo gathers information on the process and remote entity.
|
||||
func (conn *Connection) GatherConnectionInfo(pkt packet.Packet) (err error) {
|
||||
// Get PID if not yet available.
|
||||
if conn.PID == process.UndefinedProcessID {
|
||||
// Get process by looking at the system state tables.
|
||||
// Apply direction as reported from the state tables.
|
||||
conn.PID, conn.Inbound, _ = process.GetPidOfConnection(pkt.Ctx(), pkt.Info())
|
||||
// Errors are informational and are logged to the context.
|
||||
}
|
||||
|
||||
// Get Process and Profile.
|
||||
if conn.process == nil {
|
||||
// We got connection from the system.
|
||||
conn.process, err = process.GetProcessWithProfile(pkt.Ctx(), conn.PID)
|
||||
if err == nil {
|
||||
// Add process/profile metadata for connection.
|
||||
conn.ProcessContext = getProcessContext(pkt.Ctx(), conn.process)
|
||||
conn.ProfileRevisionCounter = conn.process.Profile().RevisionCnt()
|
||||
|
||||
// Inherit internal status of profile.
|
||||
if localProfile := conn.process.Profile().LocalProfile(); localProfile != nil {
|
||||
conn.Internal = localProfile.Internal
|
||||
}
|
||||
} else {
|
||||
proc = process.GetUnidentifiedProcess(pkt.Ctx())
|
||||
conn.process = nil
|
||||
if pkt.InfoOnly() {
|
||||
log.Tracer(pkt.Ctx()).Debugf("network: failed to get process and profile of PID %d: %s", conn.PID, err)
|
||||
} else {
|
||||
log.Tracer(pkt.Ctx()).Warningf("network: failed to get process and profile of PID %d: %s", conn.PID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the (remote) entity.
|
||||
entity := &intel.Entity{
|
||||
Protocol: uint8(pkt.Info().Protocol),
|
||||
Port: pkt.Info().RemotePort(),
|
||||
}
|
||||
entity.SetIP(pkt.Info().RemoteIP())
|
||||
entity.SetDstPort(pkt.Info().DstPort)
|
||||
|
||||
var scope string
|
||||
var resolverInfo *resolver.ResolverInfo
|
||||
var dnsContext *resolver.DNSRequestContext
|
||||
|
||||
if inbound {
|
||||
switch entity.IPScope {
|
||||
case netutils.HostLocal:
|
||||
scope = IncomingHost
|
||||
case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
||||
scope = IncomingLAN
|
||||
case netutils.Global, netutils.GlobalMulticast:
|
||||
scope = IncomingInternet
|
||||
|
||||
case netutils.Undefined, netutils.Invalid:
|
||||
fallthrough
|
||||
default:
|
||||
scope = IncomingInvalid
|
||||
// Create remote entity.
|
||||
if conn.Entity == nil {
|
||||
// Remote
|
||||
conn.Entity = &intel.Entity{
|
||||
Protocol: uint8(pkt.Info().Protocol),
|
||||
Port: pkt.Info().RemotePort(),
|
||||
}
|
||||
} else {
|
||||
conn.Entity.SetIP(pkt.Info().RemoteIP())
|
||||
conn.Entity.SetDstPort(pkt.Info().DstPort)
|
||||
// Local
|
||||
conn.SetLocalIP(pkt.Info().LocalIP())
|
||||
conn.LocalPort = pkt.Info().LocalPort()
|
||||
|
||||
if conn.Inbound {
|
||||
switch conn.Entity.IPScope {
|
||||
case netutils.HostLocal:
|
||||
conn.Scope = IncomingHost
|
||||
case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
||||
conn.Scope = IncomingLAN
|
||||
case netutils.Global, netutils.GlobalMulticast:
|
||||
conn.Scope = IncomingInternet
|
||||
|
||||
case netutils.Undefined, netutils.Invalid:
|
||||
fallthrough
|
||||
default:
|
||||
conn.Scope = IncomingInvalid
|
||||
}
|
||||
} else {
|
||||
// Outbound direct (possibly P2P) connection.
|
||||
switch conn.Entity.IPScope {
|
||||
case netutils.HostLocal:
|
||||
conn.Scope = PeerHost
|
||||
case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
||||
conn.Scope = PeerLAN
|
||||
case netutils.Global, netutils.GlobalMulticast:
|
||||
conn.Scope = PeerInternet
|
||||
|
||||
case netutils.Undefined, netutils.Invalid:
|
||||
fallthrough
|
||||
default:
|
||||
conn.Scope = PeerInvalid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find domain and DNS context of entity.
|
||||
if conn.Entity.Domain == "" && conn.process.Profile() != nil {
|
||||
// check if we can find a domain for that IP
|
||||
ipinfo, err := resolver.GetIPInfo(proc.Profile().LocalProfile().ID, pkt.Info().RemoteIP().String())
|
||||
ipinfo, err := resolver.GetIPInfo(conn.process.Profile().LocalProfile().ID, pkt.Info().RemoteIP().String())
|
||||
if err != nil {
|
||||
// Try again with the global scope, in case DNS went through the system resolver.
|
||||
ipinfo, err = resolver.GetIPInfo(resolver.IPInfoProfileScopeGlobal, pkt.Info().RemoteIP().String())
|
||||
@@ -380,78 +487,38 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
|
||||
if err == nil {
|
||||
lastResolvedDomain := ipinfo.MostRecentDomain()
|
||||
if lastResolvedDomain != nil {
|
||||
scope = lastResolvedDomain.Domain
|
||||
entity.Domain = lastResolvedDomain.Domain
|
||||
entity.CNAME = lastResolvedDomain.CNAMEs
|
||||
dnsContext = lastResolvedDomain.DNSRequestContext
|
||||
resolverInfo = lastResolvedDomain.Resolver
|
||||
removeOpenDNSRequest(proc.Pid, lastResolvedDomain.Domain)
|
||||
conn.Scope = lastResolvedDomain.Domain
|
||||
conn.Entity.Domain = lastResolvedDomain.Domain
|
||||
conn.Entity.CNAME = lastResolvedDomain.CNAMEs
|
||||
conn.DNSContext = lastResolvedDomain.DNSRequestContext
|
||||
conn.Resolver = lastResolvedDomain.Resolver
|
||||
removeOpenDNSRequest(conn.process.Pid, lastResolvedDomain.Domain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if destination IP is the captive portal's IP
|
||||
// Check if destination IP is the captive portal's IP.
|
||||
if conn.Entity.Domain == "" {
|
||||
portal := netenv.GetCaptivePortal()
|
||||
if pkt.Info().RemoteIP().Equal(portal.IP) {
|
||||
scope = portal.Domain
|
||||
entity.Domain = portal.Domain
|
||||
}
|
||||
|
||||
if scope == "" {
|
||||
// outbound direct (possibly P2P) connection
|
||||
switch entity.IPScope {
|
||||
case netutils.HostLocal:
|
||||
scope = PeerHost
|
||||
case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
||||
scope = PeerLAN
|
||||
case netutils.Global, netutils.GlobalMulticast:
|
||||
scope = PeerInternet
|
||||
|
||||
case netutils.Undefined, netutils.Invalid:
|
||||
fallthrough
|
||||
default:
|
||||
scope = PeerInvalid
|
||||
}
|
||||
conn.Scope = portal.Domain
|
||||
conn.Entity.Domain = portal.Domain
|
||||
}
|
||||
}
|
||||
|
||||
// Create new connection object.
|
||||
newConn := &Connection{
|
||||
ID: pkt.GetConnectionID(),
|
||||
Type: IPConnection,
|
||||
Scope: scope,
|
||||
IPVersion: pkt.Info().Version,
|
||||
Inbound: inbound,
|
||||
// local endpoint
|
||||
IPProtocol: pkt.Info().Protocol,
|
||||
LocalPort: pkt.Info().LocalPort(),
|
||||
ProcessContext: getProcessContext(pkt.Ctx(), proc),
|
||||
DNSContext: dnsContext,
|
||||
process: proc,
|
||||
// remote endpoint
|
||||
Entity: entity,
|
||||
// resolver used to resolve dns request
|
||||
Resolver: resolverInfo,
|
||||
// meta
|
||||
Started: time.Now().Unix(),
|
||||
ProfileRevisionCounter: proc.Profile().RevisionCnt(),
|
||||
}
|
||||
newConn.SetLocalIP(pkt.Info().LocalIP())
|
||||
|
||||
// Inherit internal status of profile.
|
||||
if localProfile := proc.Profile().LocalProfile(); localProfile != nil {
|
||||
newConn.Internal = localProfile.Internal
|
||||
// Data collection is only complete with a packet.
|
||||
if pkt.InfoOnly() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Save connection to internal state in order to mitigate creation of
|
||||
// duplicates. Do not propagate yet, as there is no verdict yet.
|
||||
conns.add(newConn)
|
||||
|
||||
return newConn
|
||||
// If we have all data and have seen an actual packet, the connection data is complete.
|
||||
conn.dataComplete.Set()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetConnection fetches a Connection from the database.
|
||||
func GetConnection(id string) (*Connection, bool) {
|
||||
return conns.get(id)
|
||||
func GetConnection(connID string) (*Connection, bool) {
|
||||
return conns.get(connID)
|
||||
}
|
||||
|
||||
// GetAllConnections Gets all connection.
|
||||
@@ -563,6 +630,14 @@ func (conn *Connection) VerdictVerb() string {
|
||||
)
|
||||
}
|
||||
|
||||
// DataIsComplete returns whether all information about the connection is
|
||||
// available and an actual packet has been seen.
|
||||
// As long as this flag is not set, the connection may not be evaluated for
|
||||
// a verdict and may not be sent to the UI.
|
||||
func (conn *Connection) DataIsComplete() bool {
|
||||
return conn.dataComplete.IsSet()
|
||||
}
|
||||
|
||||
// Process returns the connection's process.
|
||||
func (conn *Connection) Process() *process.Process {
|
||||
return conn.process
|
||||
@@ -579,9 +654,13 @@ func (conn *Connection) SaveWhenFinished() {
|
||||
// Callers must make sure to lock the connection itself before calling
|
||||
// Save().
|
||||
func (conn *Connection) Save() {
|
||||
conn.addToMetrics()
|
||||
conn.UpdateMeta()
|
||||
|
||||
// Do not save/update until data is complete.
|
||||
if !conn.DataIsComplete() {
|
||||
return
|
||||
}
|
||||
|
||||
if !conn.KeyIsSet() {
|
||||
if conn.Type == DNSRequest {
|
||||
conn.SetKey(makeKey(conn.process.Pid, dbScopeDNS, conn.ID))
|
||||
@@ -592,6 +671,8 @@ func (conn *Connection) Save() {
|
||||
}
|
||||
}
|
||||
|
||||
conn.addToMetrics()
|
||||
|
||||
// notify database controller
|
||||
dbController.PushUpdate(conn)
|
||||
}
|
||||
@@ -610,29 +691,61 @@ func (conn *Connection) delete() {
|
||||
}
|
||||
|
||||
conn.Meta().Delete()
|
||||
dbController.PushUpdate(conn)
|
||||
|
||||
// Notify database controller if data is complete and thus connection was previously exposed.
|
||||
if conn.DataIsComplete() {
|
||||
dbController.PushUpdate(conn)
|
||||
}
|
||||
}
|
||||
|
||||
// SetFirewallHandler sets the firewall handler for this link, and starts a
|
||||
// worker to handle the packets.
|
||||
// The caller needs to hold a lock on the connection.
|
||||
// Cannot be called with "nil" handler. Call StopFirewallHandler() instead.
|
||||
func (conn *Connection) SetFirewallHandler(handler FirewallHandler) {
|
||||
if conn.firewallHandler == nil {
|
||||
conn.pktQueue = make(chan packet.Packet, 100)
|
||||
if handler == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Start packet handler worker when first handler is set.
|
||||
if conn.firewallHandler == nil {
|
||||
// start handling
|
||||
module.StartWorker("packet handler", conn.packetHandlerWorker)
|
||||
}
|
||||
|
||||
// Set new handler.
|
||||
conn.firewallHandler = handler
|
||||
|
||||
// Initialize packet queue, if needed.
|
||||
conn.pktQueueLock.Lock()
|
||||
defer conn.pktQueueLock.Unlock()
|
||||
if !conn.pktQueueActive {
|
||||
conn.pktQueue = make(chan packet.Packet, 100)
|
||||
conn.pktQueueActive = true
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateFirewallHandler sets the firewall handler if it already set and the
|
||||
// given handler is not nil.
|
||||
// The caller needs to hold a lock on the connection.
|
||||
func (conn *Connection) UpdateFirewallHandler(handler FirewallHandler) {
|
||||
if handler != nil && conn.firewallHandler != nil {
|
||||
conn.firewallHandler = handler
|
||||
}
|
||||
}
|
||||
|
||||
// StopFirewallHandler unsets the firewall handler and stops the handler worker.
|
||||
// The caller needs to hold a lock on the connection.
|
||||
func (conn *Connection) StopFirewallHandler() {
|
||||
conn.pktQueueLock.Lock()
|
||||
defer conn.pktQueueLock.Unlock()
|
||||
|
||||
// Unset the firewall handler to revert to the default handler.
|
||||
conn.firewallHandler = nil
|
||||
|
||||
// Signal the packet handler worker that it can stop.
|
||||
close(conn.pktQueue)
|
||||
conn.pktQueueActive = false
|
||||
|
||||
// Unset the packet queue so that it can be freed.
|
||||
conn.pktQueue = nil
|
||||
@@ -640,15 +753,25 @@ func (conn *Connection) StopFirewallHandler() {
|
||||
|
||||
// HandlePacket queues packet of Link for handling.
|
||||
func (conn *Connection) HandlePacket(pkt packet.Packet) {
|
||||
conn.Lock()
|
||||
defer conn.Unlock()
|
||||
conn.pktQueueLock.Lock()
|
||||
defer conn.pktQueueLock.Unlock()
|
||||
|
||||
// execute handler or verdict
|
||||
if conn.firewallHandler != nil {
|
||||
conn.pktQueue <- pkt
|
||||
// TODO: drop if overflowing?
|
||||
if conn.pktQueueActive {
|
||||
select {
|
||||
case conn.pktQueue <- pkt:
|
||||
default:
|
||||
log.Debugf(
|
||||
"filter: dropping packet %s, as there is no space in the connection's handling queue",
|
||||
pkt,
|
||||
)
|
||||
_ = pkt.Drop()
|
||||
}
|
||||
} else {
|
||||
defaultFirewallHandler(conn, pkt)
|
||||
|
||||
// Record metrics.
|
||||
packetHandlingHistogram.UpdateDuration(pkt.Info().SeenAt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,7 +779,15 @@ func (conn *Connection) HandlePacket(pkt packet.Packet) {
|
||||
func (conn *Connection) packetHandlerWorker(ctx context.Context) error {
|
||||
// Copy packet queue, so we can remove the reference from the connection
|
||||
// when we stop the firewall handler.
|
||||
pktQueue := conn.pktQueue
|
||||
var pktQueue chan packet.Packet
|
||||
func() {
|
||||
conn.pktQueueLock.Lock()
|
||||
defer conn.pktQueueLock.Unlock()
|
||||
pktQueue = conn.pktQueue
|
||||
}()
|
||||
|
||||
// pktSeq counts the seen packets.
|
||||
var pktSeq int
|
||||
|
||||
for {
|
||||
select {
|
||||
@@ -664,21 +795,58 @@ func (conn *Connection) packetHandlerWorker(ctx context.Context) error {
|
||||
if pkt == nil {
|
||||
return nil
|
||||
}
|
||||
packetHandlerHandleConn(conn, pkt)
|
||||
pktSeq++
|
||||
|
||||
// Check if we should expect an(other) info only packet.
|
||||
// Only wait if this is the first packet and is not an info packet itself.
|
||||
if pktSeq == 1 && pkt.ExpectInfo() && !pkt.InfoOnly() {
|
||||
// Debug: FIXME
|
||||
// log.Debugf("filter: waiting for info only packet in order to pull forward: %s", pkt)
|
||||
select {
|
||||
case infoPkt := <-pktQueue:
|
||||
if infoPkt != nil {
|
||||
// Debug: FIXME
|
||||
// log.Debugf("filter: packet #%d [pulled forward] info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt)
|
||||
packetHandlerHandleConn(ctx, conn, pkt)
|
||||
pktSeq++
|
||||
}
|
||||
case <-time.After(5 * time.Millisecond):
|
||||
}
|
||||
}
|
||||
|
||||
// Debug: FIXME
|
||||
// switch {
|
||||
// case pkt.Info().Inbound:
|
||||
// log.Debugf("filter: packet #%d info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt)
|
||||
// case pktSeq == 1 && !pkt.InfoOnly():
|
||||
// log.Warningf("filter: packet #%d [should be info only!] info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt)
|
||||
// case pktSeq >= 2 && pkt.InfoOnly():
|
||||
// log.Errorf("filter: packet #%d [should not be info only!] info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt)
|
||||
// default:
|
||||
// log.Debugf("filter: packet #%d info=%v PID=%d packet: %s", pktSeq, pkt.InfoOnly(), pkt.Info().PID, pkt)
|
||||
// }
|
||||
|
||||
packetHandlerHandleConn(ctx, conn, pkt)
|
||||
|
||||
case <-ctx.Done():
|
||||
conn.Lock()
|
||||
defer conn.Unlock()
|
||||
conn.firewallHandler = nil
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func packetHandlerHandleConn(conn *Connection, pkt packet.Packet) {
|
||||
func packetHandlerHandleConn(ctx context.Context, conn *Connection, pkt packet.Packet) {
|
||||
conn.Lock()
|
||||
defer conn.Unlock()
|
||||
|
||||
// Create tracing context.
|
||||
// Add context tracer and set context on packet.
|
||||
traceCtx, tracer := log.AddTracer(ctx)
|
||||
if tracer != nil {
|
||||
// The trace is submitted in `network.Connection.packetHandler()`.
|
||||
tracer.Tracef("filter: handling packet: %s", pkt)
|
||||
}
|
||||
pkt.SetCtx(traceCtx)
|
||||
|
||||
// Handle packet with appropriate handler.
|
||||
if conn.firewallHandler != nil {
|
||||
conn.firewallHandler(conn, pkt)
|
||||
@@ -686,13 +854,22 @@ func packetHandlerHandleConn(conn *Connection, pkt packet.Packet) {
|
||||
defaultFirewallHandler(conn, pkt)
|
||||
}
|
||||
|
||||
// Log verdict.
|
||||
log.Tracer(pkt.Ctx()).Infof("filter: connection %s %s: %s", conn, conn.VerdictVerb(), conn.Reason.Msg)
|
||||
// Submit trace logs.
|
||||
log.Tracer(pkt.Ctx()).Submit()
|
||||
// Record metrics.
|
||||
packetHandlingHistogram.UpdateDuration(pkt.Info().SeenAt)
|
||||
|
||||
// Save() itself does not touch any changing data.
|
||||
// Must not be locked - would deadlock with cleaner functions.
|
||||
// Log result and submit trace.
|
||||
switch {
|
||||
case conn.DataIsComplete():
|
||||
tracer.Infof("filter: connection %s %s: %s", conn, conn.VerdictVerb(), conn.Reason.Msg)
|
||||
case conn.Verdict.Firewall != VerdictUndecided:
|
||||
tracer.Debugf("filter: connection %s fast-tracked", pkt)
|
||||
default:
|
||||
tracer.Debugf("filter: gathered data on connection %s", conn)
|
||||
}
|
||||
// Submit trace logs.
|
||||
tracer.Submit()
|
||||
|
||||
// Push changes, if there are any.
|
||||
if conn.saveWhenFinished {
|
||||
conn.saveWhenFinished = false
|
||||
conn.Save()
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/safing/portmaster/network/packet"
|
||||
"github.com/safing/portmaster/process"
|
||||
"github.com/safing/spn/navigator"
|
||||
"github.com/tevino/abool"
|
||||
)
|
||||
|
||||
// NewDefaultConnection creates a new connection with default values except local and remote IPs and protocols.
|
||||
@@ -25,6 +26,7 @@ func NewDefaultConnection(localIP net.IP, localPort uint16, remoteIP net.IP, rem
|
||||
LocalIP: localIP,
|
||||
LocalIPScope: netutils.Global,
|
||||
LocalPort: localPort,
|
||||
PID: process.UnidentifiedProcessID,
|
||||
Entity: &intel.Entity{
|
||||
Protocol: uint8(protocol),
|
||||
IP: remoteIP,
|
||||
@@ -35,6 +37,7 @@ func NewDefaultConnection(localIP net.IP, localPort uint16, remoteIP net.IP, rem
|
||||
VerdictPermanent: false,
|
||||
Tunneled: true,
|
||||
Encrypted: false,
|
||||
DataComplete: abool.NewBool(true),
|
||||
Internal: false,
|
||||
addedToMetrics: true, // Metrics are not needed for now. This will mark the Connection to be ignored.
|
||||
process: process.GetUnidentifiedProcess(context.Background()),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@@ -37,6 +38,21 @@ func (cs *connectionStore) get(id string) (*Connection, bool) {
|
||||
return conn, ok
|
||||
}
|
||||
|
||||
// findByPrefix returns the first connection where the key matches the given prefix.
|
||||
// If the prefix matches multiple entries, the result is not deterministic.
|
||||
func (cs *connectionStore) findByPrefix(prefix string) (*Connection, bool) {
|
||||
cs.rw.RLock()
|
||||
defer cs.rw.RUnlock()
|
||||
|
||||
for key, conn := range cs.items {
|
||||
if strings.HasPrefix(key, prefix) {
|
||||
return conn, true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (cs *connectionStore) clone() map[string]*Connection {
|
||||
cs.rw.RLock()
|
||||
defer cs.rw.RUnlock()
|
||||
|
||||
@@ -90,12 +90,12 @@ func (s *StorageInterface) Get(key string) (record.Record, error) {
|
||||
|
||||
switch scope {
|
||||
case dbScopeDNS:
|
||||
if r, ok := dnsConns.get(id); ok {
|
||||
return r, nil
|
||||
if c, ok := dnsConns.get(id); ok && c.DataIsComplete() {
|
||||
return c, nil
|
||||
}
|
||||
case dbScopeIP:
|
||||
if r, ok := conns.get(id); ok {
|
||||
return r, nil
|
||||
if c, ok := conns.get(id); ok && c.DataIsComplete() {
|
||||
return c, nil
|
||||
}
|
||||
case dbScopeNone:
|
||||
if proc, ok := process.GetProcessFromStorage(pid); ok {
|
||||
@@ -143,11 +143,16 @@ func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) {
|
||||
if scope == dbScopeNone || scope == dbScopeDNS {
|
||||
// dns scopes only
|
||||
for _, dnsConn := range dnsConns.clone() {
|
||||
if !dnsConn.DataIsComplete() {
|
||||
continue
|
||||
}
|
||||
|
||||
func() {
|
||||
dnsConn.Lock()
|
||||
defer dnsConn.Unlock()
|
||||
matches = q.Matches(dnsConn)
|
||||
}()
|
||||
|
||||
if matches {
|
||||
it.Next <- dnsConn
|
||||
}
|
||||
@@ -157,11 +162,16 @@ func (s *StorageInterface) processQuery(q *query.Query, it *iterator.Iterator) {
|
||||
if scope == dbScopeNone || scope == dbScopeIP {
|
||||
// connections
|
||||
for _, conn := range conns.clone() {
|
||||
if !conn.DataIsComplete() {
|
||||
continue
|
||||
}
|
||||
|
||||
func() {
|
||||
conn.Lock()
|
||||
defer conn.Unlock()
|
||||
matches = q.Matches(conn)
|
||||
}()
|
||||
|
||||
if matches {
|
||||
it.Next <- conn
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
packetHandlingHistogram *metrics.Histogram
|
||||
blockedOutConnCounter *metrics.Counter
|
||||
encryptedAndTunneledOutConnCounter *metrics.Counter
|
||||
encryptedOutConnCounter *metrics.Counter
|
||||
@@ -15,8 +16,21 @@ var (
|
||||
outConnCounter *metrics.Counter
|
||||
)
|
||||
|
||||
func registerMetrics() error {
|
||||
_, err := metrics.NewGauge(
|
||||
func registerMetrics() (err error) {
|
||||
// This needed to be moved here, because every packet is now handled by the
|
||||
// connection handler worker.
|
||||
packetHandlingHistogram, err = metrics.NewHistogram(
|
||||
"firewall/handling/duration/seconds",
|
||||
nil,
|
||||
&metrics.Options{
|
||||
Permission: api.PermitUser,
|
||||
ExpertiseLevel: config.ExpertiseLevelExpert,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = metrics.NewGauge(
|
||||
"network/connections/active/total",
|
||||
nil,
|
||||
func() float64 {
|
||||
|
||||
@@ -15,6 +15,8 @@ func GetMulticastRequestConn(responseConn *Connection, responseFromNet *net.IPNe
|
||||
// Find requesting multicast/broadcast connection.
|
||||
for _, conn := range conns.clone() {
|
||||
switch {
|
||||
case !conn.DataIsComplete():
|
||||
// Ignore connection with incomplete data.
|
||||
case conn.Inbound:
|
||||
// Ignore incoming connections.
|
||||
case conn.Ended != 0:
|
||||
|
||||
35
network/packet/bandwidth.go
Normal file
35
network/packet/bandwidth.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package packet
|
||||
|
||||
import "fmt"
|
||||
|
||||
// BandwidthUpdate holds an update to the seen bandwidth of a connection.
|
||||
type BandwidthUpdate struct {
|
||||
ConnID string
|
||||
RecvBytes uint64
|
||||
SentBytes uint64
|
||||
Method BandwidthUpdateMethod
|
||||
}
|
||||
|
||||
// BandwidthUpdateMethod defines how the bandwidth data of a bandwidth update should be interpreted.
|
||||
type BandwidthUpdateMethod uint8
|
||||
|
||||
// Bandwidth Update Methods.
|
||||
const (
|
||||
Absolute BandwidthUpdateMethod = iota
|
||||
Additive
|
||||
)
|
||||
|
||||
func (bu *BandwidthUpdate) String() string {
|
||||
return fmt.Sprintf("%s: %dB recv | %dB sent [%s]", bu.ConnID, bu.RecvBytes, bu.SentBytes, bu.Method)
|
||||
}
|
||||
|
||||
func (bum BandwidthUpdateMethod) String() string {
|
||||
switch bum {
|
||||
case Absolute:
|
||||
return "absolute"
|
||||
case Additive:
|
||||
return "additive"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
77
network/packet/info_only.go
Normal file
77
network/packet/info_only.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// InfoPacket does not represent an actual packet, but only holds metadata.
|
||||
// Implements the packet.Packet interface.
|
||||
type InfoPacket struct {
|
||||
Base
|
||||
}
|
||||
|
||||
// NewInfoPacket returns a new InfoPacket with the given info.
|
||||
func NewInfoPacket(info Info) *InfoPacket {
|
||||
return &InfoPacket{
|
||||
Base{
|
||||
info: info,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// InfoOnly returns whether the packet is informational only and does not
|
||||
// represent an actual packet.
|
||||
func (pkt *InfoPacket) InfoOnly() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// LoadPacketData does nothing on Linux, as data is always fully parsed.
|
||||
func (pkt *InfoPacket) LoadPacketData() error {
|
||||
return fmt.Errorf("%w: info-only packet", ErrFailedToLoadPayload)
|
||||
}
|
||||
|
||||
// ErrInfoOnlyPacket is returned for unsupported operations on an info-only packet.
|
||||
var ErrInfoOnlyPacket = errors.New("info-only packet")
|
||||
|
||||
// Accept does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) Accept() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// Block does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) Block() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// Drop does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) Drop() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// PermanentAccept does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) PermanentAccept() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// PermanentBlock does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) PermanentBlock() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// PermanentDrop does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) PermanentDrop() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// RerouteToNameserver does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) RerouteToNameserver() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
// RerouteToTunnel does nothing on an info-only packet.
|
||||
func (pkt *InfoPacket) RerouteToTunnel() error {
|
||||
return ErrInfoOnlyPacket
|
||||
}
|
||||
|
||||
var _ Packet = &InfoPacket{}
|
||||
@@ -24,6 +24,17 @@ func (pkt *Base) FastTrackedByIntegration() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// InfoOnly returns whether the packet is informational only and does not
|
||||
// represent an actual packet.
|
||||
func (pkt *Base) InfoOnly() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ExpectInfo returns whether the next packet is expected to be informational only.
|
||||
func (pkt *Base) ExpectInfo() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetCtx sets the packet context.
|
||||
func (pkt *Base) SetCtx(ctx context.Context) {
|
||||
pkt.ctx = ctx
|
||||
@@ -101,27 +112,11 @@ func (pkt *Base) Payload() []byte {
|
||||
// GetConnectionID returns the link ID for this packet.
|
||||
func (pkt *Base) GetConnectionID() string {
|
||||
if pkt.connID == "" {
|
||||
pkt.createConnectionID()
|
||||
pkt.connID = pkt.info.CreateConnectionID()
|
||||
}
|
||||
return pkt.connID
|
||||
}
|
||||
|
||||
func (pkt *Base) createConnectionID() {
|
||||
if pkt.info.Protocol == TCP || pkt.info.Protocol == UDP {
|
||||
if pkt.info.Inbound {
|
||||
pkt.connID = fmt.Sprintf("%d-%s-%d-%s-%d", pkt.info.Protocol, pkt.info.Dst, pkt.info.DstPort, pkt.info.Src, pkt.info.SrcPort)
|
||||
} else {
|
||||
pkt.connID = fmt.Sprintf("%d-%s-%d-%s-%d", pkt.info.Protocol, pkt.info.Src, pkt.info.SrcPort, pkt.info.Dst, pkt.info.DstPort)
|
||||
}
|
||||
} else {
|
||||
if pkt.info.Inbound {
|
||||
pkt.connID = fmt.Sprintf("%d-%s-%s", pkt.info.Protocol, pkt.info.Dst, pkt.info.Src)
|
||||
} else {
|
||||
pkt.connID = fmt.Sprintf("%d-%s-%s", pkt.info.Protocol, pkt.info.Src, pkt.info.Dst)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MatchesAddress checks if a the packet matches a given endpoint (remote or local) in protocol, network and port.
|
||||
//
|
||||
// Comparison matrix:
|
||||
@@ -236,6 +231,8 @@ type Packet interface {
|
||||
RerouteToNameserver() error
|
||||
RerouteToTunnel() error
|
||||
FastTrackedByIntegration() bool
|
||||
InfoOnly() bool
|
||||
ExpectInfo() bool
|
||||
|
||||
// Info.
|
||||
SetCtx(context.Context)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package packet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Info holds IP and TCP/UDP header information.
|
||||
@@ -13,6 +15,9 @@ type Info struct {
|
||||
Protocol IPProtocol
|
||||
SrcPort, DstPort uint16
|
||||
Src, Dst net.IP
|
||||
|
||||
PID int
|
||||
SeenAt time.Time
|
||||
}
|
||||
|
||||
// LocalIP returns the local IP of the packet.
|
||||
@@ -46,3 +51,26 @@ func (pi *Info) RemotePort() uint16 {
|
||||
}
|
||||
return pi.DstPort
|
||||
}
|
||||
|
||||
// CreateConnectionID creates a connection ID.
|
||||
// In most circumstances, this method should not be used directly, but
|
||||
// packet.GetConnectionID() should be called instead.
|
||||
func (pi *Info) CreateConnectionID() string {
|
||||
return CreateConnectionID(pi.Protocol, pi.Src, pi.SrcPort, pi.Dst, pi.DstPort, pi.Inbound)
|
||||
}
|
||||
|
||||
// CreateConnectionID creates a connection ID.
|
||||
func CreateConnectionID(protocol IPProtocol, src net.IP, srcPort uint16, dst net.IP, dstPort uint16, inbound bool) string {
|
||||
// TODO: make this ID not depend on the packet direction for better support for forwarded packets.
|
||||
if protocol == TCP || protocol == UDP {
|
||||
if inbound {
|
||||
return fmt.Sprintf("%d-%s-%d-%s-%d", protocol, dst, dstPort, src, srcPort)
|
||||
}
|
||||
return fmt.Sprintf("%d-%s-%d-%s-%d", protocol, src, srcPort, dst, dstPort)
|
||||
}
|
||||
|
||||
if inbound {
|
||||
return fmt.Sprintf("%d-%s-%s", protocol, dst, src)
|
||||
}
|
||||
return fmt.Sprintf("%d-%s-%s", protocol, src, dst)
|
||||
}
|
||||
|
||||
@@ -28,13 +28,18 @@ nextPort:
|
||||
// Check if the generated port is unused.
|
||||
nextConnection:
|
||||
for _, conn := range allConns {
|
||||
// Skip connection if the protocol does not match the protocol of interest.
|
||||
if conn.Entity.Protocol != protocol {
|
||||
switch {
|
||||
case !conn.DataIsComplete():
|
||||
// Skip connection if the data is not complete.
|
||||
continue nextConnection
|
||||
}
|
||||
// Skip port if the local port is in dangerous proximity.
|
||||
// Consecutive port numbers are very common.
|
||||
if conn.LocalPort <= port && conn.LocalPort >= portRangeStart {
|
||||
|
||||
case conn.Entity.Protocol != protocol:
|
||||
// Skip connection if the protocol does not match the protocol of interest.
|
||||
continue nextConnection
|
||||
|
||||
case conn.LocalPort <= port && conn.LocalPort >= portRangeStart:
|
||||
// Skip port if the local port is in dangerous proximity.
|
||||
// Consecutive port numbers are very common.
|
||||
continue nextPort
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,38 +14,19 @@ import (
|
||||
"github.com/safing/portmaster/profile"
|
||||
)
|
||||
|
||||
// GetProcessByConnection returns the process that owns the described connection.
|
||||
func GetProcessByConnection(ctx context.Context, pktInfo *packet.Info) (process *Process, connInbound bool, err error) {
|
||||
// GetProcessWithProfile returns the process, including the profile.
|
||||
// Always returns valid data.
|
||||
// Errors are logged and returned for information or special handling purposes.
|
||||
func GetProcessWithProfile(ctx context.Context, pid int) (process *Process, err error) {
|
||||
if !enableProcessDetection() {
|
||||
log.Tracer(ctx).Tracef("process: process detection disabled")
|
||||
return GetUnidentifiedProcess(ctx), pktInfo.Inbound, nil
|
||||
}
|
||||
|
||||
// Use fast search for inbound packets, as the listening socket should
|
||||
// already be there for a while now.
|
||||
fastSearch := pktInfo.Inbound
|
||||
|
||||
log.Tracer(ctx).Tracef("process: getting pid from system network state")
|
||||
var pid int
|
||||
pid, connInbound, err = state.Lookup(pktInfo, fastSearch)
|
||||
if err != nil {
|
||||
log.Tracer(ctx).Tracef("process: failed to find PID of connection: %s", err)
|
||||
return nil, pktInfo.Inbound, err
|
||||
}
|
||||
|
||||
// Fallback to special profiles if PID could not be found.
|
||||
if pid == UndefinedProcessID {
|
||||
if connInbound {
|
||||
pid = UnsolicitedProcessID
|
||||
} else {
|
||||
pid = UnidentifiedProcessID
|
||||
}
|
||||
return GetUnidentifiedProcess(ctx), nil
|
||||
}
|
||||
|
||||
process, err = GetOrFindProcess(ctx, pid)
|
||||
if err != nil {
|
||||
log.Tracer(ctx).Debugf("process: failed to find (primary) process with PID: %s", err)
|
||||
return nil, connInbound, err
|
||||
log.Tracer(ctx).Debugf("process: failed to find process with PID: %s", err)
|
||||
return GetUnidentifiedProcess(ctx), err
|
||||
}
|
||||
|
||||
changed, err := process.GetProfile(ctx)
|
||||
@@ -57,7 +38,46 @@ func GetProcessByConnection(ctx context.Context, pktInfo *packet.Info) (process
|
||||
process.Save()
|
||||
}
|
||||
|
||||
return process, connInbound, nil
|
||||
return process, nil
|
||||
}
|
||||
|
||||
// GetPidOfConnection returns the PID of the process that owns the described connection.
|
||||
// Always returns valid data.
|
||||
// Errors are logged and returned for information or special handling purposes.
|
||||
func GetPidOfConnection(ctx context.Context, pktInfo *packet.Info) (pid int, connInbound bool, err error) {
|
||||
if !enableProcessDetection() {
|
||||
return UnidentifiedProcessID, pktInfo.Inbound, nil
|
||||
}
|
||||
|
||||
// Use fast search for inbound packets, as the listening socket should
|
||||
// already be there for a while now.
|
||||
fastSearch := pktInfo.Inbound
|
||||
connInbound = pktInfo.Inbound
|
||||
|
||||
// Check if we need to get the PID.
|
||||
if pktInfo.PID == UndefinedProcessID {
|
||||
log.Tracer(ctx).Tracef("process: getting pid from system network state")
|
||||
pid, connInbound, err = state.Lookup(pktInfo, fastSearch)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to find PID of connection: %w", err)
|
||||
log.Tracer(ctx).Tracef("process: %s", err)
|
||||
pid = UndefinedProcessID
|
||||
}
|
||||
} else {
|
||||
log.Tracer(ctx).Tracef("process: pid already set in packet (by ebpf or kext)")
|
||||
pid = pktInfo.PID
|
||||
}
|
||||
|
||||
// Fallback to special profiles if PID could not be found.
|
||||
if pid == UndefinedProcessID {
|
||||
if connInbound && !netutils.ClassifyIP(pktInfo.Dst).IsLocalhost() {
|
||||
pid = UnsolicitedProcessID
|
||||
} else {
|
||||
pid = UnidentifiedProcessID
|
||||
}
|
||||
}
|
||||
|
||||
return pid, connInbound, err
|
||||
}
|
||||
|
||||
// GetNetworkHost returns a *Process that represents a host on the network.
|
||||
@@ -106,9 +126,15 @@ func GetProcessByRequestOrigin(ar *api.Request) (*Process, error) {
|
||||
Protocol: packet.TCP,
|
||||
Src: remoteIP, // source as in the process we are looking for
|
||||
SrcPort: remotePort, // source as in the process we are looking for
|
||||
PID: UndefinedProcessID,
|
||||
}
|
||||
|
||||
proc, _, err := GetProcessByConnection(ar.Context(), pkt)
|
||||
pid, _, err := GetPidOfConnection(ar.Context(), pkt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proc, err := GetProcessWithProfile(ar.Context(), pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ func loadProcess(ctx context.Context, key string, pInfo *processInfo.Process) (*
|
||||
}
|
||||
|
||||
// UID
|
||||
// net yet implemented for windows
|
||||
// TODO: implemented for windows
|
||||
if onLinux {
|
||||
var uids []int32
|
||||
uids, err = pInfo.UidsWithContext(ctx)
|
||||
|
||||
@@ -62,6 +62,9 @@ func (h *SVCHostTagHandler) AddTags(p *process.Process) {
|
||||
p.Name += fmt.Sprintf(" (%s)", strings.Join(svcNames, ", "))
|
||||
// Add services as tags.
|
||||
for _, svcName := range svcNames {
|
||||
// Remove tags from service names, such as "CDPUserSvc_1bf5729".
|
||||
svcName, _, _ := strings.Cut(svcName, "_")
|
||||
// Add service as tag.
|
||||
p.Tags = append(p.Tags, profile.Tag{
|
||||
Key: svchostTagKey,
|
||||
Value: svcName,
|
||||
|
||||
@@ -63,6 +63,7 @@ var (
|
||||
cfgOptionNoAssignedNameserversOrder = 1
|
||||
|
||||
CfgOptionUseStaleCacheKey = "dns/useStaleCache"
|
||||
useStaleCacheConfigOption *config.Option
|
||||
useStaleCache config.BoolOption
|
||||
cfgOptionUseStaleCacheOrder = 2
|
||||
|
||||
@@ -205,7 +206,7 @@ The format is: "protocol://ip:port?parameter=value¶meter=value"
|
||||
}
|
||||
noAssignedNameservers = status.SecurityLevelOption(CfgOptionNoAssignedNameserversKey)
|
||||
|
||||
err = config.Register(&config.Option{
|
||||
useStaleCacheConfigOption = &config.Option{
|
||||
Name: "Always Use DNS Cache",
|
||||
Key: CfgOptionUseStaleCacheKey,
|
||||
Description: "Always use the DNS cache, even if entries have expired. Expired entries are refreshed afterwards in the background. This can improve DNS resolving performance a lot, but may lead to occasional connection errors due to outdated DNS records.",
|
||||
@@ -217,7 +218,8 @@ The format is: "protocol://ip:port?parameter=value¶meter=value"
|
||||
config.DisplayOrderAnnotation: cfgOptionUseStaleCacheOrder,
|
||||
config.CategoryAnnotation: "Resolving",
|
||||
},
|
||||
})
|
||||
}
|
||||
err = config.Register(useStaleCacheConfigOption)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ var suggestUsingStaleCacheNotification *notifications.Notification
|
||||
|
||||
func suggestUsingStaleCacheTask(ctx context.Context, t *modules.Task) error {
|
||||
switch {
|
||||
case useStaleCache():
|
||||
case useStaleCache() || useStaleCacheConfigOption.IsSetByUser():
|
||||
// If setting is already active, disable task repeating.
|
||||
t.Repeat(0)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user