mirror of
https://github.com/safing/portmaster
synced 2025-09-01 18:19:12 +00:00
676 lines
22 KiB
Go
676 lines
22 KiB
Go
package network
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/safing/portbase/database/record"
|
|
"github.com/safing/portbase/log"
|
|
"github.com/safing/portmaster/intel"
|
|
"github.com/safing/portmaster/netenv"
|
|
"github.com/safing/portmaster/network/netutils"
|
|
"github.com/safing/portmaster/network/packet"
|
|
"github.com/safing/portmaster/process"
|
|
"github.com/safing/portmaster/resolver"
|
|
"github.com/safing/spn/navigator"
|
|
)
|
|
|
|
// FirewallHandler defines the function signature for a firewall
|
|
// handle function. A firewall handler is responsible for finding
|
|
// a reasonable verdict for the connection conn. The connection is
|
|
// locked before the firewall handler is called.
|
|
type FirewallHandler func(conn *Connection, pkt packet.Packet)
|
|
|
|
// ProcessContext holds additional information about the process
|
|
// that iniated a connection.
|
|
type ProcessContext struct {
|
|
// ProcessName is the name of the process.
|
|
ProcessName string
|
|
// ProfileName is the name of the profile.
|
|
ProfileName string
|
|
// BinaryPath is the path to the process binary.
|
|
BinaryPath string
|
|
// CmdLine holds the execution parameters.
|
|
CmdLine string
|
|
// PID is the process identifier.
|
|
PID int
|
|
// Profile is the ID of the main profile that
|
|
// is applied to the process.
|
|
Profile string
|
|
// Source is the source of the profile.
|
|
Source string
|
|
}
|
|
|
|
type ConnectionType int8
|
|
|
|
const (
|
|
Undefined ConnectionType = iota
|
|
IPConnection
|
|
DNSRequest
|
|
// ProxyRequest
|
|
)
|
|
|
|
// Connection describes a distinct physical network connection
|
|
// identified by the IP/Port pair.
|
|
type Connection struct { //nolint:maligned // TODO: fix alignment
|
|
record.Base
|
|
sync.Mutex
|
|
|
|
// ID holds a unique request/connection id and is considered immutable after
|
|
// creation.
|
|
ID string
|
|
// Type defines the connection type.
|
|
Type ConnectionType
|
|
// External defines if the connection represents an external request or
|
|
// connection.
|
|
External bool
|
|
// Scope defines the scope of a connection. For DNS requests, the
|
|
// scope is always set to the domain name. For direct packet
|
|
// connections the scope consists of the involved network environment
|
|
// and the packet direction. Once a connection object is created,
|
|
// Scope is considered immutable.
|
|
// Deprecated: This field holds duplicate information, which is accessible
|
|
// clearer through other attributes. Please use conn.Type, conn.Inbound
|
|
// and conn.Entity.Domain instead.
|
|
Scope string
|
|
// IPVersion is set to the packet IP version. It is not set (0) for
|
|
// connections created from a DNS request.
|
|
IPVersion packet.IPVersion
|
|
// Inbound is set to true if the connection is incoming. Inbound is
|
|
// only set when a connection object is created and is considered
|
|
// immutable afterwards.
|
|
Inbound bool
|
|
// IPProtocol is set to the transport protocol used by the connection.
|
|
// Is is considered immutable once a connection object has been
|
|
// created. IPProtocol is not set for connections that have been
|
|
// created from a DNS request.
|
|
IPProtocol packet.IPProtocol
|
|
// LocalIP holds the local IP address of the connection. It is not
|
|
// set for connections created from DNS requests. LocalIP is
|
|
// considered immutable once a connection object has been created.
|
|
LocalIP net.IP
|
|
// LocalIPScope holds the network scope of the local IP.
|
|
LocalIPScope netutils.IPScope
|
|
// LocalPort holds the local port of the connection. It is not
|
|
// set for connections created from DNS requests. LocalPort is
|
|
// considered immutable once a connection object has been created.
|
|
LocalPort uint16
|
|
// 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
|
|
// entity must be guarded by the connection lock.
|
|
Entity *intel.Entity
|
|
// Resolver holds information about the resolver used to resolve
|
|
// Entity.Domain.
|
|
Resolver *resolver.ResolverInfo
|
|
// Verdict is the final decision that has been made for a connection.
|
|
// The verdict may change so any access to it must be guarded by the
|
|
// connection lock.
|
|
Verdict Verdict
|
|
// Reason holds information justifying the verdict, as well as additional
|
|
// information about the reason.
|
|
// Access to Reason must be guarded by the connection lock.
|
|
Reason Reason
|
|
// Started holds the number of seconds in UNIX epoch time at which
|
|
// the connection has been initated and first seen by the portmaster.
|
|
// Started is only ever set when creating a new connection object
|
|
// and is considered immutable afterwards.
|
|
Started int64
|
|
// Ended is set to the number of seconds in UNIX epoch time at which
|
|
// the connection is considered terminated. Ended may be set at any
|
|
// time so access must be guarded by the connection lock.
|
|
Ended int64
|
|
// VerdictPermanent is set to true if the final verdict is permanent
|
|
// and the connection has been (or will be) handed back to the kernel.
|
|
// VerdictPermanent may be changed together with the Verdict and Reason
|
|
// properties and must be guarded using the connection lock.
|
|
VerdictPermanent bool
|
|
// Inspecting is set to true if the connection is being inspected
|
|
// by one or more of the registered inspectors. This property may
|
|
// be changed during the lifetime of a connection and must be guarded
|
|
// using the connection lock.
|
|
Inspecting bool
|
|
// Tunneled is currently unused and MUST be ignored.
|
|
Tunneled bool
|
|
// Encrypted is currently unused and MUST be ignored.
|
|
Encrypted bool
|
|
// TunnelOpts holds options for tunneling the connection.
|
|
TunnelOpts *navigator.Options
|
|
// ProcessContext holds additional information about the process
|
|
// that iniated the connection. It is set once when the connection
|
|
// object is created and is considered immutable afterwards.
|
|
ProcessContext ProcessContext
|
|
// DNSContext holds additional information about the DNS request that was
|
|
// probably used to resolve the IP of this connection.
|
|
DNSContext *resolver.DNSRequestContext
|
|
// TunnelContext holds additional information about the tunnel that this
|
|
// connection is using.
|
|
TunnelContext interface{}
|
|
// 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.
|
|
Internal bool
|
|
// process holds a reference to the actor process. That is, the
|
|
// process instance that initated 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
|
|
// saveWhenFinished can be set to drue during the life-time of
|
|
// a connection and signals the firewallHandler that a Save()
|
|
// should be issued after processing the connection.
|
|
saveWhenFinished bool
|
|
// activeInspectors is a slice of booleans where each entry
|
|
// maps to the index of an available inspector. If the value
|
|
// is true the inspector is currently active. False indicates
|
|
// that the inspector has finished and should be skipped.
|
|
activeInspectors []bool
|
|
// inspectorData holds additional meta data for the inspectors.
|
|
// using the inspectors index as a map key.
|
|
inspectorData map[uint8]interface{}
|
|
// ProfileRevisionCounter is used to track changes to the process
|
|
// profile and required for correct re-evaluation of a connections
|
|
// verdict.
|
|
ProfileRevisionCounter uint64
|
|
// addedToMetrics signifies if the connection has already been counted in
|
|
// the metrics.
|
|
addedToMetrics bool
|
|
}
|
|
|
|
// Reason holds information justifying a verdict, as well as additional
|
|
// information about the reason.
|
|
type Reason struct {
|
|
// Msg is a human readable description of the reason.
|
|
Msg string
|
|
// OptionKey is the configuration option key of the setting that
|
|
// was responsible for the verdict.
|
|
OptionKey string
|
|
// Profile is the database key of the profile that held the setting
|
|
// that was responsible for the verdict.
|
|
Profile string
|
|
// ReasonContext may hold additional reason-specific information and
|
|
// any access must be guarded by the connection lock.
|
|
Context interface{}
|
|
}
|
|
|
|
func getProcessContext(ctx context.Context, proc *process.Process) ProcessContext {
|
|
// Gather process information.
|
|
pCtx := ProcessContext{
|
|
ProcessName: proc.Name,
|
|
BinaryPath: proc.Path,
|
|
CmdLine: proc.CmdLine,
|
|
PID: proc.Pid,
|
|
}
|
|
|
|
// Get local profile.
|
|
localProfile := proc.Profile().LocalProfile()
|
|
if localProfile == nil {
|
|
log.Tracer(ctx).Warningf("network: process %s has no profile", proc)
|
|
return pCtx
|
|
}
|
|
|
|
// Add profile information and return.
|
|
pCtx.ProfileName = localProfile.Name
|
|
pCtx.Profile = localProfile.ID
|
|
pCtx.Source = string(localProfile.Source)
|
|
return pCtx
|
|
}
|
|
|
|
// NewConnectionFromDNSRequest returns a new connection based on the given dns request.
|
|
func NewConnectionFromDNSRequest(ctx context.Context, fqdn string, cnames []string, connID string, localIP net.IP, localPort uint16) *Connection {
|
|
// Determine IP version.
|
|
ipVersion := packet.IPv6
|
|
if localIP.To4() != nil {
|
|
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)
|
|
}
|
|
|
|
timestamp := time.Now().Unix()
|
|
dnsConn := &Connection{
|
|
ID: connID,
|
|
Type: DNSRequest,
|
|
Scope: fqdn,
|
|
Entity: &intel.Entity{
|
|
Domain: fqdn,
|
|
CNAME: cnames,
|
|
},
|
|
process: proc,
|
|
ProcessContext: getProcessContext(ctx, proc),
|
|
Started: timestamp,
|
|
Ended: timestamp,
|
|
}
|
|
|
|
// Inherit internal status of profile.
|
|
if localProfile := proc.Profile().LocalProfile(); localProfile != nil {
|
|
dnsConn.Internal = localProfile.Internal
|
|
}
|
|
|
|
// Always mark dns queries from the system resolver as internal.
|
|
if proc.IsSystemResolver() {
|
|
dnsConn.Internal = true
|
|
}
|
|
|
|
// DNS Requests are saved by the nameserver depending on the result of the
|
|
// query. Blocked requests are saved immediately, accepted ones are only
|
|
// saved if they are not "used" by a connection.
|
|
|
|
return dnsConn
|
|
}
|
|
|
|
func NewConnectionFromExternalDNSRequest(ctx context.Context, fqdn string, cnames []string, connID string, remoteIP net.IP) (*Connection, error) {
|
|
remoteHost, err := process.GetNetworkHost(ctx, remoteIP)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
timestamp := time.Now().Unix()
|
|
dnsConn := &Connection{
|
|
ID: connID,
|
|
Type: DNSRequest,
|
|
External: true,
|
|
Scope: fqdn,
|
|
Entity: &intel.Entity{
|
|
Domain: fqdn,
|
|
CNAME: cnames,
|
|
},
|
|
process: remoteHost,
|
|
ProcessContext: getProcessContext(ctx, remoteHost),
|
|
Started: timestamp,
|
|
Ended: timestamp,
|
|
}
|
|
|
|
// Inherit internal status of profile.
|
|
if localProfile := remoteHost.Profile().LocalProfile(); localProfile != nil {
|
|
dnsConn.Internal = localProfile.Internal
|
|
}
|
|
|
|
// DNS Requests are saved by the nameserver depending on the result of the
|
|
// query. Blocked requests are saved immediately, accepted ones are only
|
|
// saved if they are not "used" by a connection.
|
|
|
|
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)
|
|
proc = process.GetUnidentifiedProcess(pkt.Ctx())
|
|
}
|
|
|
|
// 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.Invalid:
|
|
fallthrough
|
|
default:
|
|
scope = IncomingInvalid
|
|
}
|
|
|
|
} else {
|
|
|
|
// check if we can find a domain for that IP
|
|
ipinfo, err := resolver.GetIPInfo(proc.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())
|
|
}
|
|
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)
|
|
}
|
|
}
|
|
|
|
// check if destination IP is the captive portal's IP
|
|
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.Invalid:
|
|
fallthrough
|
|
default:
|
|
scope = PeerInvalid
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// GetConnection fetches a Connection from the database.
|
|
func GetConnection(id string) (*Connection, bool) {
|
|
return conns.get(id)
|
|
}
|
|
|
|
// SetLocalIP sets the local IP address together with its network scope. The
|
|
// connection is not locked for this.
|
|
func (conn *Connection) SetLocalIP(ip net.IP) {
|
|
conn.LocalIP = ip
|
|
conn.LocalIPScope = netutils.GetIPScope(ip)
|
|
}
|
|
|
|
// AcceptWithContext accepts the connection.
|
|
func (conn *Connection) AcceptWithContext(reason, reasonOptionKey string, ctx interface{}) {
|
|
if !conn.SetVerdict(VerdictAccept, reason, reasonOptionKey, ctx) {
|
|
log.Warningf("filter: tried to accept %s, but current verdict is %s", conn, conn.Verdict)
|
|
}
|
|
}
|
|
|
|
// Accept is like AcceptWithContext but only accepts a reason.
|
|
func (conn *Connection) Accept(reason, reasonOptionKey string) {
|
|
conn.AcceptWithContext(reason, reasonOptionKey, nil)
|
|
}
|
|
|
|
// BlockWithContext blocks the connection.
|
|
func (conn *Connection) BlockWithContext(reason, reasonOptionKey string, ctx interface{}) {
|
|
if !conn.SetVerdict(VerdictBlock, reason, reasonOptionKey, ctx) {
|
|
log.Warningf("filter: tried to block %s, but current verdict is %s", conn, conn.Verdict)
|
|
}
|
|
}
|
|
|
|
// Block is like BlockWithContext but does only accepts a reason.
|
|
func (conn *Connection) Block(reason, reasonOptionKey string) {
|
|
conn.BlockWithContext(reason, reasonOptionKey, nil)
|
|
}
|
|
|
|
// DropWithContext drops the connection.
|
|
func (conn *Connection) DropWithContext(reason, reasonOptionKey string, ctx interface{}) {
|
|
if !conn.SetVerdict(VerdictDrop, reason, reasonOptionKey, ctx) {
|
|
log.Warningf("filter: tried to drop %s, but current verdict is %s", conn, conn.Verdict)
|
|
}
|
|
}
|
|
|
|
// Drop is like DropWithContext but does only accepts a reason.
|
|
func (conn *Connection) Drop(reason, reasonOptionKey string) {
|
|
conn.DropWithContext(reason, reasonOptionKey, nil)
|
|
}
|
|
|
|
// DenyWithContext blocks or drops the link depending on the connection direction.
|
|
func (conn *Connection) DenyWithContext(reason, reasonOptionKey string, ctx interface{}) {
|
|
if conn.Inbound {
|
|
conn.DropWithContext(reason, reasonOptionKey, ctx)
|
|
} else {
|
|
conn.BlockWithContext(reason, reasonOptionKey, ctx)
|
|
}
|
|
}
|
|
|
|
// Deny is like DenyWithContext but only accepts a reason.
|
|
func (conn *Connection) Deny(reason, reasonOptionKey string) {
|
|
conn.DenyWithContext(reason, reasonOptionKey, nil)
|
|
}
|
|
|
|
// FailedWithContext marks the connection with VerdictFailed and stores the reason.
|
|
func (conn *Connection) FailedWithContext(reason, reasonOptionKey string, ctx interface{}) {
|
|
if !conn.SetVerdict(VerdictFailed, reason, reasonOptionKey, ctx) {
|
|
log.Warningf("filter: tried to drop %s due to error but current verdict is %s", conn, conn.Verdict)
|
|
}
|
|
}
|
|
|
|
// Failed is like FailedWithContext but only accepts a string.
|
|
func (conn *Connection) Failed(reason, reasonOptionKey string) {
|
|
conn.FailedWithContext(reason, reasonOptionKey, nil)
|
|
}
|
|
|
|
// SetVerdict sets a new verdict for the connection, making sure it does not interfere with previous verdicts.
|
|
func (conn *Connection) SetVerdict(newVerdict Verdict, reason, reasonOptionKey string, reasonCtx interface{}) (ok bool) {
|
|
if newVerdict >= conn.Verdict {
|
|
conn.Verdict = newVerdict
|
|
conn.Reason.Msg = reason
|
|
conn.Reason.Context = reasonCtx
|
|
|
|
conn.Reason.OptionKey = ""
|
|
conn.Reason.Profile = ""
|
|
if reasonOptionKey != "" && conn.Process() != nil {
|
|
conn.Reason.OptionKey = reasonOptionKey
|
|
conn.Reason.Profile = conn.Process().Profile().GetProfileSource(conn.Reason.OptionKey)
|
|
}
|
|
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Process returns the connection's process.
|
|
func (conn *Connection) Process() *process.Process {
|
|
return conn.process
|
|
}
|
|
|
|
// SaveWhenFinished marks the connection for saving it after the firewall handler.
|
|
func (conn *Connection) SaveWhenFinished() {
|
|
conn.saveWhenFinished = true
|
|
}
|
|
|
|
// Save saves the connection in the storage and propagates the change
|
|
// through the database system. Save may lock dnsConnsLock or connsLock
|
|
// in if Save() is called the first time.
|
|
// Callers must make sure to lock the connection itself before calling
|
|
// Save().
|
|
func (conn *Connection) Save() {
|
|
conn.addToMetrics()
|
|
conn.UpdateMeta()
|
|
|
|
if !conn.KeyIsSet() {
|
|
if conn.Type == DNSRequest {
|
|
conn.SetKey(makeKey(conn.process.Pid, "dns", conn.ID))
|
|
dnsConns.add(conn)
|
|
} else {
|
|
conn.SetKey(makeKey(conn.process.Pid, "ip", conn.ID))
|
|
conns.add(conn)
|
|
}
|
|
}
|
|
|
|
// notify database controller
|
|
dbController.PushUpdate(conn)
|
|
}
|
|
|
|
// delete deletes a link from the storage and propagates the change.
|
|
// delete may lock either the dnsConnsLock or connsLock. Callers
|
|
// must still make sure to lock the connection itself.
|
|
func (conn *Connection) delete() {
|
|
// A connection without an ID has been created from
|
|
// a DNS request rather than a packet. Choose the correct
|
|
// connection store here.
|
|
if conn.Type == IPConnection {
|
|
conns.delete(conn)
|
|
} else {
|
|
dnsConns.delete(conn)
|
|
}
|
|
|
|
conn.Meta().Delete()
|
|
dbController.PushUpdate(conn)
|
|
}
|
|
|
|
// SetFirewallHandler sets the firewall handler for this link, and starts a
|
|
// worker to handle the packets.
|
|
func (conn *Connection) SetFirewallHandler(handler FirewallHandler) {
|
|
if conn.firewallHandler == nil {
|
|
conn.pktQueue = make(chan packet.Packet, 1000)
|
|
|
|
// start handling
|
|
module.StartWorker("packet handler", func(ctx context.Context) error {
|
|
conn.packetHandler()
|
|
return nil
|
|
})
|
|
}
|
|
conn.firewallHandler = handler
|
|
}
|
|
|
|
// StopFirewallHandler unsets the firewall handler and stops the handler worker.
|
|
func (conn *Connection) StopFirewallHandler() {
|
|
conn.firewallHandler = nil
|
|
conn.pktQueue <- nil
|
|
}
|
|
|
|
// HandlePacket queues packet of Link for handling
|
|
func (conn *Connection) HandlePacket(pkt packet.Packet) {
|
|
conn.Lock()
|
|
defer conn.Unlock()
|
|
|
|
// execute handler or verdict
|
|
if conn.firewallHandler != nil {
|
|
conn.pktQueue <- pkt
|
|
// TODO: drop if overflowing?
|
|
} else {
|
|
defaultFirewallHandler(conn, pkt)
|
|
}
|
|
}
|
|
|
|
// packetHandler sequentially handles queued packets
|
|
func (conn *Connection) packetHandler() {
|
|
for pkt := range conn.pktQueue {
|
|
if pkt == nil {
|
|
return
|
|
}
|
|
// get handler
|
|
conn.Lock()
|
|
|
|
// execute handler or verdict
|
|
if conn.firewallHandler != nil {
|
|
conn.firewallHandler(conn, pkt)
|
|
} else {
|
|
defaultFirewallHandler(conn, pkt)
|
|
}
|
|
|
|
// log verdict
|
|
log.Tracer(pkt.Ctx()).Infof("filter: connection %s %s: %s", conn, conn.Verdict.Verb(), conn.Reason.Msg)
|
|
// submit trace logs
|
|
log.Tracer(pkt.Ctx()).Submit()
|
|
|
|
// save does not touch any changing data
|
|
// must not be locked, will deadlock with cleaner functions
|
|
if conn.saveWhenFinished {
|
|
conn.saveWhenFinished = false
|
|
conn.Save()
|
|
}
|
|
|
|
conn.Unlock()
|
|
}
|
|
}
|
|
|
|
// GetActiveInspectors returns the list of active inspectors.
|
|
func (conn *Connection) GetActiveInspectors() []bool {
|
|
return conn.activeInspectors
|
|
}
|
|
|
|
// SetActiveInspectors sets the list of active inspectors.
|
|
func (conn *Connection) SetActiveInspectors(new []bool) {
|
|
conn.activeInspectors = new
|
|
}
|
|
|
|
// GetInspectorData returns the list of inspector data.
|
|
func (conn *Connection) GetInspectorData() map[uint8]interface{} {
|
|
return conn.inspectorData
|
|
}
|
|
|
|
// SetInspectorData set the list of inspector data.
|
|
func (conn *Connection) SetInspectorData(new map[uint8]interface{}) {
|
|
conn.inspectorData = new
|
|
}
|
|
|
|
// String returns a string representation of conn.
|
|
func (conn *Connection) String() string {
|
|
switch {
|
|
case conn.Inbound:
|
|
return fmt.Sprintf("%s <- %s", conn.process, conn.Entity.IP)
|
|
case conn.Entity.Domain != "":
|
|
return fmt.Sprintf("%s to %s (%s)", conn.process, conn.Entity.Domain, conn.Entity.IP)
|
|
default:
|
|
return fmt.Sprintf("%s -> %s", conn.process, conn.Entity.IP)
|
|
}
|
|
}
|