mirror of
https://github.com/safing/portmaster
synced 2025-09-04 19:49:15 +00:00
Add IP scoping data to entity.Entity and network.Connection
This commit is contained in:
parent
eb22636c8e
commit
43cfba8445
10 changed files with 92 additions and 63 deletions
|
@ -286,7 +286,7 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) {
|
||||||
// TODO: add implementation for forced tunneling
|
// TODO: add implementation for forced tunneling
|
||||||
if pkt.IsOutbound() &&
|
if pkt.IsOutbound() &&
|
||||||
captain.ClientReady() &&
|
captain.ClientReady() &&
|
||||||
netutils.IPIsGlobal(conn.Entity.IP) &&
|
conn.Entity.IPScope.IsGlobal() &&
|
||||||
conn.Verdict == network.VerdictAccept {
|
conn.Verdict == network.VerdictAccept {
|
||||||
// try to tunnel
|
// try to tunnel
|
||||||
err := sluice.AwaitRequest(pkt.Info(), conn.Entity.Domain)
|
err := sluice.AwaitRequest(pkt.Info(), conn.Entity.Domain)
|
||||||
|
|
|
@ -58,6 +58,9 @@ type Entity struct {
|
||||||
// set, IP has been resolved by following all CNAMEs.
|
// set, IP has been resolved by following all CNAMEs.
|
||||||
IP net.IP
|
IP net.IP
|
||||||
|
|
||||||
|
// IPScope holds the network scope of the IP.
|
||||||
|
IPScope netutils.IPScope
|
||||||
|
|
||||||
// Country holds the country the IP address (ASN) is
|
// Country holds the country the IP address (ASN) is
|
||||||
// located in.
|
// located in.
|
||||||
Country string
|
Country string
|
||||||
|
@ -65,6 +68,9 @@ type Entity struct {
|
||||||
// ASN holds the autonomous system number of the IP.
|
// ASN holds the autonomous system number of the IP.
|
||||||
ASN uint
|
ASN uint
|
||||||
|
|
||||||
|
// ASOrg holds the owner's name of the autonomous system.
|
||||||
|
ASOrg string
|
||||||
|
|
||||||
location *geoip.Location
|
location *geoip.Location
|
||||||
|
|
||||||
// BlockedByLists holds list source IDs that
|
// BlockedByLists holds list source IDs that
|
||||||
|
@ -95,6 +101,12 @@ func (e *Entity) Init() *Entity {
|
||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetIP sets the IP address together with its network scope.
|
||||||
|
func (e *Entity) SetIP(ip net.IP) {
|
||||||
|
e.IP = ip
|
||||||
|
e.IPScope = netutils.GetIPScope(ip)
|
||||||
|
}
|
||||||
|
|
||||||
// SetDstPort sets the destination port.
|
// SetDstPort sets the destination port.
|
||||||
func (e *Entity) SetDstPort(dstPort uint16) {
|
func (e *Entity) SetDstPort(dstPort uint16) {
|
||||||
e.dstPort = dstPort
|
e.dstPort = dstPort
|
||||||
|
@ -229,6 +241,7 @@ func (e *Entity) getLocation(ctx context.Context) {
|
||||||
e.location = loc
|
e.location = loc
|
||||||
e.Country = loc.Country.ISOCode
|
e.Country = loc.Country.ISOCode
|
||||||
e.ASN = loc.AutonomousSystemNumber
|
e.ASN = loc.AutonomousSystemNumber
|
||||||
|
e.ASOrg = loc.AutonomousSystemOrganization
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,7 +435,7 @@ func (e *Entity) getIPLists(ctx context.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// only load lists for IP addresses that are classified as global.
|
// only load lists for IP addresses that are classified as global.
|
||||||
if netutils.ClassifyIP(ip) != netutils.Global {
|
if !e.IPScope.IsGlobal() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,12 +38,12 @@ func GetAssignedGlobalAddresses() (ipv4 []net.IP, ipv6 []net.IP, err error) {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
for _, ip4 := range allv4 {
|
for _, ip4 := range allv4 {
|
||||||
if netutils.IPIsGlobal(ip4) {
|
if netutils.GetIPScope(ip4).IsGlobal() {
|
||||||
ipv4 = append(ipv4, ip4)
|
ipv4 = append(ipv4, ip4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, ip6 := range allv6 {
|
for _, ip6 := range allv6 {
|
||||||
if netutils.IPIsGlobal(ip6) {
|
if netutils.GetIPScope(ip6).IsGlobal() {
|
||||||
ipv6 = append(ipv6, ip6)
|
ipv6 = append(ipv6, ip6)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ var (
|
||||||
// Broadcast or multicast addresses will never match, even if valid in in use.
|
// Broadcast or multicast addresses will never match, even if valid in in use.
|
||||||
func IsMyIP(ip net.IP) (yes bool, err error) {
|
func IsMyIP(ip net.IP) (yes bool, err error) {
|
||||||
// Check for IPs that don't need extra checks.
|
// Check for IPs that don't need extra checks.
|
||||||
switch netutils.ClassifyIP(ip) {
|
switch netutils.GetIPScope(ip) {
|
||||||
case netutils.HostLocal:
|
case netutils.HostLocal:
|
||||||
return true, nil
|
return true, nil
|
||||||
case netutils.LocalMulticast, netutils.GlobalMulticast:
|
case netutils.LocalMulticast, netutils.GlobalMulticast:
|
||||||
|
|
|
@ -130,7 +130,7 @@ next:
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we received something from a global IP address, we have succeeded and can return immediately.
|
// If we received something from a global IP address, we have succeeded and can return immediately.
|
||||||
if netutils.IPIsGlobal(addr.IP) {
|
if netutils.GetIPScope(addr.IP).IsGlobal() {
|
||||||
return addr.IP, nil
|
return addr.IP, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -356,7 +356,7 @@ func checkOnlineStatus(ctx context.Context) {
|
||||||
} else {
|
} else {
|
||||||
var lan bool
|
var lan bool
|
||||||
for _, ip := range ipv4 {
|
for _, ip := range ipv4 {
|
||||||
switch netutils.ClassifyIP(ip) {
|
switch netutils.GetIPScope(ip) {
|
||||||
case netutils.SiteLocal:
|
case netutils.SiteLocal:
|
||||||
lan = true
|
lan = true
|
||||||
case netutils.Global:
|
case netutils.Global:
|
||||||
|
@ -366,7 +366,7 @@ func checkOnlineStatus(ctx context.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, ip := range ipv6 {
|
for _, ip := range ipv6 {
|
||||||
switch netutils.ClassifyIP(ip) {
|
switch netutils.GetIPScope(ip) {
|
||||||
case netutils.SiteLocal, netutils.Global:
|
case netutils.SiteLocal, netutils.Global:
|
||||||
// IPv6 global addresses are also used in local networks
|
// IPv6 global addresses are also used in local networks
|
||||||
lan = true
|
lan = true
|
||||||
|
|
|
@ -74,6 +74,8 @@ type Connection struct { //nolint:maligned // TODO: fix alignment
|
||||||
// set for connections created from DNS requests. LocalIP is
|
// set for connections created from DNS requests. LocalIP is
|
||||||
// considered immutable once a connection object has been created.
|
// considered immutable once a connection object has been created.
|
||||||
LocalIP net.IP
|
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
|
// LocalPort holds the local port of the connection. It is not
|
||||||
// set for connections created from DNS requests. LocalPort is
|
// set for connections created from DNS requests. LocalPort is
|
||||||
// considered immutable once a connection object has been created.
|
// considered immutable once a connection object has been created.
|
||||||
|
@ -279,7 +281,14 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
|
||||||
if inbound {
|
if inbound {
|
||||||
|
|
||||||
// inbound connection
|
// inbound connection
|
||||||
switch netutils.ClassifyIP(pkt.Info().Src) {
|
entity = &intel.Entity{
|
||||||
|
Protocol: uint8(pkt.Info().Protocol),
|
||||||
|
Port: pkt.Info().SrcPort,
|
||||||
|
}
|
||||||
|
entity.SetIP(pkt.Info().Src)
|
||||||
|
entity.SetDstPort(pkt.Info().DstPort)
|
||||||
|
|
||||||
|
switch entity.IPScope {
|
||||||
case netutils.HostLocal:
|
case netutils.HostLocal:
|
||||||
scope = IncomingHost
|
scope = IncomingHost
|
||||||
case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
||||||
|
@ -292,21 +301,15 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
|
||||||
default:
|
default:
|
||||||
scope = IncomingInvalid
|
scope = IncomingInvalid
|
||||||
}
|
}
|
||||||
entity = &intel.Entity{
|
|
||||||
IP: pkt.Info().Src,
|
|
||||||
Protocol: uint8(pkt.Info().Protocol),
|
|
||||||
Port: pkt.Info().SrcPort,
|
|
||||||
}
|
|
||||||
entity.SetDstPort(pkt.Info().DstPort)
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// outbound connection
|
// outbound connection
|
||||||
entity = &intel.Entity{
|
entity = &intel.Entity{
|
||||||
IP: pkt.Info().Dst,
|
|
||||||
Protocol: uint8(pkt.Info().Protocol),
|
Protocol: uint8(pkt.Info().Protocol),
|
||||||
Port: pkt.Info().DstPort,
|
Port: pkt.Info().DstPort,
|
||||||
}
|
}
|
||||||
|
entity.SetIP(pkt.Info().Dst)
|
||||||
entity.SetDstPort(entity.Port)
|
entity.SetDstPort(entity.Port)
|
||||||
|
|
||||||
// check if we can find a domain for that IP
|
// check if we can find a domain for that IP
|
||||||
|
@ -331,7 +334,7 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
|
||||||
if scope == "" {
|
if scope == "" {
|
||||||
|
|
||||||
// outbound direct (possibly P2P) connection
|
// outbound direct (possibly P2P) connection
|
||||||
switch netutils.ClassifyIP(pkt.Info().Dst) {
|
switch entity.IPScope {
|
||||||
case netutils.HostLocal:
|
case netutils.HostLocal:
|
||||||
scope = PeerHost
|
scope = PeerHost
|
||||||
case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
case netutils.LinkLocal, netutils.SiteLocal, netutils.LocalMulticast:
|
||||||
|
@ -356,7 +359,6 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
|
||||||
Inbound: inbound,
|
Inbound: inbound,
|
||||||
// local endpoint
|
// local endpoint
|
||||||
IPProtocol: pkt.Info().Protocol,
|
IPProtocol: pkt.Info().Protocol,
|
||||||
LocalIP: pkt.Info().LocalIP(),
|
|
||||||
LocalPort: pkt.Info().LocalPort(),
|
LocalPort: pkt.Info().LocalPort(),
|
||||||
ProcessContext: getProcessContext(pkt.Ctx(), proc),
|
ProcessContext: getProcessContext(pkt.Ctx(), proc),
|
||||||
process: proc,
|
process: proc,
|
||||||
|
@ -366,6 +368,7 @@ func NewConnectionFromFirstPacket(pkt packet.Packet) *Connection {
|
||||||
Started: time.Now().Unix(),
|
Started: time.Now().Unix(),
|
||||||
ProfileRevisionCounter: proc.Profile().RevisionCnt(),
|
ProfileRevisionCounter: proc.Profile().RevisionCnt(),
|
||||||
}
|
}
|
||||||
|
newConn.SetLocalIP(pkt.Info().LocalIP())
|
||||||
|
|
||||||
// Inherit internal status of profile.
|
// Inherit internal status of profile.
|
||||||
if localProfile := proc.Profile().LocalProfile(); localProfile != nil {
|
if localProfile := proc.Profile().LocalProfile(); localProfile != nil {
|
||||||
|
@ -380,6 +383,13 @@ func GetConnection(id string) (*Connection, bool) {
|
||||||
return conns.get(id)
|
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.
|
// AcceptWithContext accepts the connection.
|
||||||
func (conn *Connection) AcceptWithContext(reason, reasonOptionKey string, ctx interface{}) {
|
func (conn *Connection) AcceptWithContext(reason, reasonOptionKey string, ctx interface{}) {
|
||||||
if !conn.SetVerdict(VerdictAccept, reason, reasonOptionKey, ctx) {
|
if !conn.SetVerdict(VerdictAccept, reason, reasonOptionKey, ctx) {
|
||||||
|
|
|
@ -2,19 +2,29 @@ package netutils
|
||||||
|
|
||||||
import "net"
|
import "net"
|
||||||
|
|
||||||
// IP classifications
|
// IPScope is the scope of the IP address.
|
||||||
|
type IPScope int8
|
||||||
|
|
||||||
|
// Defined IP Scopes.
|
||||||
const (
|
const (
|
||||||
HostLocal int8 = iota
|
Invalid IPScope = iota - 1
|
||||||
|
Undefined
|
||||||
|
HostLocal
|
||||||
LinkLocal
|
LinkLocal
|
||||||
SiteLocal
|
SiteLocal
|
||||||
Global
|
Global
|
||||||
LocalMulticast
|
LocalMulticast
|
||||||
GlobalMulticast
|
GlobalMulticast
|
||||||
Invalid int8 = -1
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClassifyIP returns the classification for the given IP address.
|
// ClassifyIP returns the network scope of the given IP address.
|
||||||
func ClassifyIP(ip net.IP) int8 { //nolint:gocognit
|
// Deprecated: Please use the new GetIPScope instead.
|
||||||
|
func ClassifyIP(ip net.IP) IPScope {
|
||||||
|
return GetIPScope(ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIPScope returns the network scope of the given IP address.
|
||||||
|
func GetIPScope(ip net.IP) IPScope { //nolint:gocognit
|
||||||
if ip4 := ip.To4(); ip4 != nil {
|
if ip4 := ip.To4(); ip4 != nil {
|
||||||
// IPv4
|
// IPv4
|
||||||
switch {
|
switch {
|
||||||
|
@ -76,32 +86,27 @@ func ClassifyIP(ip net.IP) int8 { //nolint:gocognit
|
||||||
return Invalid
|
return Invalid
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPIsLocalhost returns whether the IP refers to the host itself.
|
// IsLocalhost returns whether the IP refers to the host itself.
|
||||||
func IPIsLocalhost(ip net.IP) bool {
|
func (scope IPScope) IsLocalhost() bool {
|
||||||
return ClassifyIP(ip) == HostLocal
|
return scope == HostLocal
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPIsLAN returns true if the given IP is a site-local or link-local address.
|
// IsLAN returns true if the scope is site-local or link-local.
|
||||||
func IPIsLAN(ip net.IP) bool {
|
func (scope IPScope) IsLAN() bool {
|
||||||
switch ClassifyIP(ip) {
|
switch scope {
|
||||||
case SiteLocal, LinkLocal:
|
case SiteLocal, LinkLocal, LocalMulticast:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// IPIsGlobal returns true if the given IP is a global address.
|
// IsGlobal returns true if the scope is global.
|
||||||
func IPIsGlobal(ip net.IP) bool {
|
func (scope IPScope) IsGlobal() bool {
|
||||||
return ClassifyIP(ip) == Global
|
switch scope {
|
||||||
}
|
case Global, GlobalMulticast:
|
||||||
|
return true
|
||||||
// IPIsLinkLocal returns true if the given IP is a link-local address.
|
default:
|
||||||
func IPIsLinkLocal(ip net.IP) bool {
|
return false
|
||||||
return ClassifyIP(ip) == LinkLocal
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// IPIsSiteLocal returns true if the given IP is a site-local address.
|
|
||||||
func IPIsSiteLocal(ip net.IP) bool {
|
|
||||||
return ClassifyIP(ip) == SiteLocal
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,26 +5,30 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIPClassification(t *testing.T) {
|
func TestIPScope(t *testing.T) {
|
||||||
testClassification(t, net.IPv4(71, 87, 113, 211), Global)
|
testScope(t, net.IPv4(71, 87, 113, 211), Global)
|
||||||
testClassification(t, net.IPv4(127, 0, 0, 1), HostLocal)
|
testScope(t, net.IPv4(127, 0, 0, 1), HostLocal)
|
||||||
testClassification(t, net.IPv4(127, 255, 255, 1), HostLocal)
|
testScope(t, net.IPv4(127, 255, 255, 1), HostLocal)
|
||||||
testClassification(t, net.IPv4(192, 168, 172, 24), SiteLocal)
|
testScope(t, net.IPv4(192, 168, 172, 24), SiteLocal)
|
||||||
testClassification(t, net.IPv4(172, 15, 1, 1), Global)
|
testScope(t, net.IPv4(172, 15, 1, 1), Global)
|
||||||
testClassification(t, net.IPv4(172, 16, 1, 1), SiteLocal)
|
testScope(t, net.IPv4(172, 16, 1, 1), SiteLocal)
|
||||||
testClassification(t, net.IPv4(172, 31, 1, 1), SiteLocal)
|
testScope(t, net.IPv4(172, 31, 1, 1), SiteLocal)
|
||||||
testClassification(t, net.IPv4(172, 32, 1, 1), Global)
|
testScope(t, net.IPv4(172, 32, 1, 1), Global)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testClassification(t *testing.T, ip net.IP, expectedClassification int8) {
|
func testScope(t *testing.T, ip net.IP, expectedScope IPScope) {
|
||||||
c := ClassifyIP(ip)
|
c := GetIPScope(ip)
|
||||||
if c != expectedClassification {
|
if c != expectedScope {
|
||||||
t.Errorf("%s is %s, expected %s", ip, classificationString(c), classificationString(expectedClassification))
|
t.Errorf("%s is %s, expected %s", ip, scopeName(c), scopeName(expectedScope))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func classificationString(c int8) string {
|
func scopeName(c IPScope) string {
|
||||||
switch c {
|
switch c {
|
||||||
|
case Invalid:
|
||||||
|
return "invalid"
|
||||||
|
case Undefined:
|
||||||
|
return "undefined"
|
||||||
case HostLocal:
|
case HostLocal:
|
||||||
return "hostLocal"
|
return "hostLocal"
|
||||||
case LinkLocal:
|
case LinkLocal:
|
||||||
|
@ -37,9 +41,7 @@ func classificationString(c int8) string {
|
||||||
return "localMulticast"
|
return "localMulticast"
|
||||||
case GlobalMulticast:
|
case GlobalMulticast:
|
||||||
return "globalMulticast"
|
return "globalMulticast"
|
||||||
case Invalid:
|
|
||||||
return "invalid"
|
|
||||||
default:
|
default:
|
||||||
return "unknown"
|
return "undefined"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,7 +151,7 @@ func (table *udpTable) lookup(pktInfo *packet.Info, fast bool) (
|
||||||
// attribute an incoming broadcast/multicast packet to the wrong process if
|
// attribute an incoming broadcast/multicast packet to the wrong process if
|
||||||
// there are multiple processes listening on the same local port, but
|
// there are multiple processes listening on the same local port, but
|
||||||
// binding to different addresses. This highly unusual for clients.
|
// binding to different addresses. This highly unusual for clients.
|
||||||
isInboundMulticast := pktInfo.Inbound && netutils.ClassifyIP(pktInfo.LocalIP()) == netutils.LocalMulticast
|
isInboundMulticast := pktInfo.Inbound && netutils.GetIPScope(pktInfo.LocalIP()) == netutils.LocalMulticast
|
||||||
|
|
||||||
// Search for the socket until found.
|
// Search for the socket until found.
|
||||||
for i := 1; i <= lookupRetries; i++ {
|
for i := 1; i <= lookupRetries; i++ {
|
||||||
|
|
|
@ -36,9 +36,8 @@ func (ep *EndpointScope) Matches(_ context.Context, entity *intel.Entity) (EPRes
|
||||||
return Undeterminable, nil
|
return Undeterminable, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
classification := netutils.ClassifyIP(entity.IP)
|
|
||||||
var scope uint8
|
var scope uint8
|
||||||
switch classification {
|
switch entity.IPScope {
|
||||||
case netutils.HostLocal:
|
case netutils.HostLocal:
|
||||||
scope = scopeLocalhost
|
scope = scopeLocalhost
|
||||||
case netutils.LinkLocal:
|
case netutils.LinkLocal:
|
||||||
|
|
Loading…
Add table
Reference in a new issue