From f63df67d236ade7ed02bac86cfd94a1cb785393f Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 10 Oct 2022 11:21:45 +0200 Subject: [PATCH] Improve verdict handling and switch to immediate re-evaluation --- firewall/dns.go | 2 +- firewall/inspection/inspection.go | 8 +- firewall/interception.go | 160 +++++++++++++++++++++++------- firewall/tunnel.go | 33 ++++-- nameserver/nameserver.go | 14 +-- netquery/manager.go | 4 +- network/api.go | 2 +- network/api_test.go | 36 +++---- network/connection.go | 59 +++++------ network/dns.go | 6 +- network/metrics.go | 2 +- network/reference/protocols.go | 27 +++++ 12 files changed, 242 insertions(+), 111 deletions(-) diff --git a/firewall/dns.go b/firewall/dns.go index ac4d4865..394e334a 100644 --- a/firewall/dns.go +++ b/firewall/dns.go @@ -187,7 +187,7 @@ func FilterResolvedDNS( // Filter dns records and return if the query is blocked. rrCache = filterDNSResponse(ctx, conn, layeredProfile, rrCache, sysResolver) - if conn.Verdict.Current == network.VerdictBlock { + if conn.Verdict.Active == network.VerdictBlock { return rrCache } diff --git a/firewall/inspection/inspection.go b/firewall/inspection/inspection.go index dfe13d46..9481932c 100644 --- a/firewall/inspection/inspection.go +++ b/firewall/inspection/inspection.go @@ -63,8 +63,8 @@ func RunInspectors(conn *network.Connection, pkt packet.Packet) (network.Verdict continue } - // check if the current verdict is already past the inspection criteria. - if conn.Verdict.Current > inspectVerdicts[key] { + // check if the active verdict is already past the inspection criteria. + if conn.Verdict.Active > inspectVerdicts[key] { activeInspectors[key] = true continue } @@ -86,11 +86,11 @@ func RunInspectors(conn *network.Connection, pkt packet.Packet) (network.Verdict continueInspection = true case BLOCK_CONN: conn.SetVerdict(network.VerdictBlock, "", "", nil) - verdict = conn.Verdict.Current + verdict = conn.Verdict.Active activeInspectors[key] = true case DROP_CONN: conn.SetVerdict(network.VerdictDrop, "", "", nil) - verdict = conn.Verdict.Current + verdict = conn.Verdict.Active activeInspectors[key] = true case STOP_INSPECTING: activeInspectors[key] = true diff --git a/firewall/interception.go b/firewall/interception.go index 98d039b6..edace3e9 100644 --- a/firewall/interception.go +++ b/firewall/interception.go @@ -24,7 +24,7 @@ import ( "github.com/safing/portmaster/network" "github.com/safing/portmaster/network/netutils" "github.com/safing/portmaster/network/packet" - "github.com/safing/spn/captain" + "github.com/safing/portmaster/network/reference" ) var ( @@ -122,21 +122,55 @@ func resetAllConnectionVerdicts() { log.Info("interception: marking all connections for re-evaluation") // reset all connection firewall handlers. This will tell the master to rerun the firewall checks. - for _, conn := range network.GetAllConnections() { - isSPNConnection := captain.IsExcepted(conn.Entity.IP) && conn.Process().Pid == ownPID + // for _, conn := range network.GetAllConnections() { + // isSPNConnection := captain.IsExcepted(conn.Entity.IP) && conn.Process().Pid == ownPID - // mark all non SPN connections to be processed by the firewall. - if !isSPNConnection { + // // mark all non SPN connections to be processed by the firewall. + // if !isSPNConnection { + // conn.Lock() + // conn.SetFirewallHandler(initialHandler) + // // Don't keep the previous tunneled value. + // conn.Tunneled = false + // // Reset entity if it exists. + // if conn.Entity != nil { + // conn.Entity.ResetLists() + // } + // conn.Unlock() + // } + // } + + // Create tracing context. + ctx, tracer := log.AddTracer(context.Background()) + defer tracer.Submit() + + for _, conn := range network.GetAllConnections() { + func() { conn.Lock() - conn.SetFirewallHandler(initialHandler) - // Don't keep the previous tunneled value. - conn.Tunneled = false - // Reset entity if it exists. - if conn.Entity != nil { - conn.Entity.ResetLists() + defer conn.Unlock() + + // Skip internal connections: + // - Pre-authenticated connections from Portmaster + // - Redirected DNS requests + // - SPN Uplink to Home Hub + if conn.Internal { + log.Tracef("skipping internal connection %s", conn) + return } - conn.Unlock() - } + + log.Tracer(ctx).Debugf("filter: re-evaluating verdict of %s", conn) + previousVerdict := conn.Verdict.Firewall + + // Apply privacy filter and check tunneling. + filterConnection(ctx, conn, nil) + + // Save if verdict changed. + if conn.Verdict.Firewall != previousVerdict { + conn.Save() + tracer.Infof("filter: verdict of connection %s changed from %s to %s", conn, previousVerdict.Verb(), conn.VerdictVerb()) + } else { + tracer.Tracef("filter: verdict to connection %s unchanged at %s", conn, conn.VerdictVerb()) + } + }() } err := interception.ResetVerdictOfAllConnections() @@ -418,6 +452,7 @@ func fastTrackedPermit(pkt packet.Packet) (handled bool) { func initialHandler(conn *network.Connection, pkt packet.Packet) { 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): // Approve connection. @@ -444,14 +479,31 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) { conn.Internal = true // End directly, as no other processing is necessary. conn.StopFirewallHandler() + finalizeVerdict(conn) issueVerdict(conn, pkt, 0, true) return + } - case filterEnabled(): - log.Tracer(pkt.Ctx()).Trace("filter: starting decision process") - DecideOnConnection(pkt.Ctx(), conn, pkt) + // Apply privacy filter and check tunneling. + filterConnection(pkt.Ctx(), conn, pkt) + // Decide how to continue handling connection. + switch { + case conn.Inspecting: + log.Tracer(pkt.Ctx()).Trace("filter: start inspecting") + conn.SetFirewallHandler(inspectThenVerdict) + inspectThenVerdict(conn, pkt) default: + conn.StopFirewallHandler() + issueVerdict(conn, pkt, 0, true) + } +} + +func filterConnection(ctx context.Context, conn *network.Connection, pkt packet.Packet) { + if filterEnabled() { + log.Tracer(ctx).Trace("filter: starting decision process") + DecideOnConnection(ctx, conn, pkt) + } else { conn.Accept("privacy filter disabled", noReasonOptionKey) } @@ -472,18 +524,19 @@ func initialHandler(conn *network.Connection, pkt packet.Packet) { } // Check if connection should be tunneled. - checkTunneling(pkt.Ctx(), conn, pkt) + checkTunneling(ctx, conn) + // Handle verdict records and transitions. finalizeVerdict(conn) - switch { - case conn.Inspecting: - log.Tracer(pkt.Ctx()).Trace("filter: start inspecting") - conn.SetFirewallHandler(inspectThenVerdict) - inspectThenVerdict(conn, pkt) - default: - conn.StopFirewallHandler() - issueVerdict(conn, pkt, 0, true) + // Request tunneling if no tunnel is set and connection should be tunneled. + if conn.Verdict.Active == network.VerdictRerouteToTunnel && + conn.TunnelContext == nil { + err := requestTunneling(ctx, conn) + if err != nil { + conn.Failed(fmt.Sprintf("failed to request tunneling: %s", err), "") + finalizeVerdict(conn) + } } } @@ -514,8 +567,8 @@ func issueVerdict(conn *network.Connection, pkt packet.Packet, verdict network.V } // do not allow to circumvent decision: e.g. to ACCEPT packets from a DROP-ed connection - if verdict < conn.Verdict.Current { - verdict = conn.Verdict.Current + if verdict < conn.Verdict.Active { + verdict = conn.Verdict.Active } var err error @@ -561,18 +614,53 @@ func issueVerdict(conn *network.Connection, pkt packet.Packet, verdict network.V } } +// verdictRating rates the privacy and security aspect of verdicts from worst to best. +var verdictRating = []network.Verdict{ + network.VerdictAccept, // Connection allowed in the open. + network.VerdictRerouteToTunnel, // Connection allowed, but protected. + network.VerdictRerouteToNameserver, // Connection allowed, but resolved via Portmaster. + network.VerdictBlock, // Connection blocked, with feedback. + network.VerdictDrop, // Connection blocked, without feedback. + network.VerdictFailed, + network.VerdictUndeterminable, + network.VerdictUndecided, +} + func finalizeVerdict(conn *network.Connection) { - // previously accepted or tunneled connections may need to be blocked - if conn.Verdict.Current == network.VerdictAccept { - switch { - case conn.Verdict.Previous == network.VerdictRerouteToTunnel && !conn.Tunneled: - conn.SetVerdictDirectly(network.VerdictBlock) - case conn.Verdict.Previous == network.VerdictAccept && conn.Tunneled: - conn.SetVerdictDirectly(network.VerdictBlock) - case conn.Tunneled: - conn.SetVerdictDirectly(network.VerdictRerouteToTunnel) + // Update worst verdict. + for _, worstVerdict := range verdictRating { + if conn.Verdict.Firewall == worstVerdict { + conn.Verdict.Worst = worstVerdict } } + + // Check for non-applicable verdicts. + // The earlier and clearer we do this, the better. + switch conn.Verdict.Firewall { //nolint:exhaustive + case network.VerdictUndecided, network.VerdictUndeterminable, network.VerdictFailed: + if conn.Inbound { + conn.Verdict.Active = network.VerdictDrop + } else { + conn.Verdict.Active = network.VerdictBlock + } + return + } + + // Apply firewall verdict to active verdict. + switch { + case conn.Verdict.Active == network.VerdictUndecided: + // Apply first verdict without change. + conn.Verdict.Active = conn.Verdict.Firewall + + case reference.IsPacketProtocol(conn.Entity.Protocol): + // For known packet protocols, apply firewall verdict unchanged. + conn.Verdict.Active = conn.Verdict.Firewall + + case conn.Verdict.Active != conn.Verdict.Firewall: + // For all other protocols (most notably, stream protocols), always block after the first change. + // Block in both directions, as there is a live connection, which we want to actively kill. + conn.Verdict.Active = network.VerdictBlock + } } // func tunnelHandler(pkt packet.Packet) { diff --git a/firewall/tunnel.go b/firewall/tunnel.go index 77908723..232636f6 100644 --- a/firewall/tunnel.go +++ b/firewall/tunnel.go @@ -2,6 +2,7 @@ package firewall import ( "context" + "errors" "github.com/safing/portbase/log" "github.com/safing/portmaster/netenv" @@ -16,7 +17,7 @@ import ( "github.com/safing/spn/sluice" ) -func checkTunneling(ctx context.Context, conn *network.Connection, pkt packet.Packet) { +func checkTunneling(ctx context.Context, conn *network.Connection) { // Check if the connection should be tunneled at all. switch { case !tunnelEnabled(): @@ -28,9 +29,12 @@ func checkTunneling(ctx context.Context, conn *network.Connection, pkt packet.Pa case conn.Inbound: // Can't tunnel incoming connections. return - case conn.Verdict.Current != network.VerdictAccept: + case conn.Verdict.Firewall != network.VerdictAccept: // Connection will be blocked. return + case conn.IPProtocol != packet.TCP && conn.IPProtocol != packet.UDP: + // Unsupported protocol. + return case conn.Process().Pid == ownPID: // Bypass tunneling for certain own connections. switch { @@ -101,11 +105,22 @@ func checkTunneling(ctx context.Context, conn *network.Connection, pkt packet.Pa // Check if ready. if !captain.ClientReady() { // Block connection as SPN is not ready yet. - log.Tracer(pkt.Ctx()).Trace("SPN not ready for tunneling") + log.Tracer(ctx).Trace("SPN not ready for tunneling") conn.Failed("SPN not ready for tunneling", "") return } + conn.SetVerdictDirectly(network.VerdictRerouteToTunnel) + conn.Tunneled = true +} + +func requestTunneling(ctx context.Context, conn *network.Connection) error { + // Get profile. + layeredProfile := conn.Process().Profile() + if layeredProfile == nil { + return errors.New("no profile set") + } + // Set options. conn.TunnelOpts = &navigator.Options{ HubPolicies: layeredProfile.StackedExitHubPolicies(), @@ -150,13 +165,11 @@ func checkTunneling(ctx context.Context, conn *network.Connection, pkt packet.Pa } // Queue request in sluice. - err = sluice.AwaitRequest(conn, crew.HandleSluiceRequest) + err := sluice.AwaitRequest(conn, crew.HandleSluiceRequest) if err != nil { - log.Tracer(pkt.Ctx()).Warningf("failed to request tunneling: %s", err) - conn.Failed("failed to request tunneling", "") - } else { - log.Tracer(pkt.Ctx()).Trace("filter: tunneling requested") - // set the flag so the verdict can be updated - conn.Tunneled = true + return err } + + log.Tracer(ctx).Trace("filter: tunneling requested") + return nil } diff --git a/nameserver/nameserver.go b/nameserver/nameserver.go index 81a36b91..0d000a1e 100644 --- a/nameserver/nameserver.go +++ b/nameserver/nameserver.go @@ -189,7 +189,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg) conn.Resolver = rrCache.Resolver } - switch conn.Verdict.Current { + switch conn.Verdict.Active { // We immediately save blocked, dropped or failed verdicts so // they pop up in the UI. case network.VerdictBlock, network.VerdictDrop, network.VerdictFailed, network.VerdictRerouteToNameserver, network.VerdictRerouteToTunnel: @@ -217,7 +217,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg) case network.VerdictUndeterminable: fallthrough default: - tracer.Warningf("nameserver: unexpected verdict %s for connection %s, not saving", conn.Verdict, conn) + tracer.Warningf("nameserver: unexpected verdict %s for connection %s, not saving", conn.VerdictVerb(), conn) } }() @@ -235,11 +235,11 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg) } // Check if there is a Verdict to act upon. - switch conn.Verdict.Current { //nolint:exhaustive // Only checking for specific values. + switch conn.Verdict.Active { //nolint:exhaustive // Only checking for specific values. case network.VerdictBlock, network.VerdictDrop, network.VerdictFailed: tracer.Infof( "nameserver: returning %s response for %s to %s", - conn.Verdict.Current.Verb(), + conn.VerdictVerb(), q.ID(), conn.Process(), ) @@ -315,11 +315,11 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg) } // Check if there is a Verdict to act upon. - switch conn.Verdict.Current { //nolint:exhaustive // Only checking for specific values. + switch conn.Verdict.Active { //nolint:exhaustive // Only checking for specific values. case network.VerdictBlock, network.VerdictDrop, network.VerdictFailed: tracer.Infof( "nameserver: returning %s response for %s to %s", - conn.Verdict.Current.Verb(), + conn.VerdictVerb(), q.ID(), conn.Process(), ) @@ -338,7 +338,7 @@ func handleRequest(ctx context.Context, w dns.ResponseWriter, request *dns.Msg) } tracer.Infof( "nameserver: returning %s response (%s%s) for %s to %s", - conn.Verdict.Current.Verb(), + conn.VerdictVerb(), dns.RcodeToString[rrCache.RCode], noAnswerIndicator, q.ID(), diff --git a/netquery/manager.go b/netquery/manager.go index 2c9e9fe2..fb244526 100644 --- a/netquery/manager.go +++ b/netquery/manager.go @@ -155,7 +155,7 @@ func convertConnection(conn *network.Connection) (*Conn, error) { IPProtocol: conn.IPProtocol, LocalIP: conn.LocalIP.String(), LocalPort: conn.LocalPort, - Verdict: conn.Verdict.User, + Verdict: conn.Verdict.Firewall, // TODO: Expose both Worst and Firewall verdicts. Started: time.Unix(conn.Started, 0), Tunneled: conn.Tunneled, Encrypted: conn.Encrypted, @@ -177,7 +177,7 @@ func convertConnection(conn *network.Connection) (*Conn, error) { c.Type = "" } - switch conn.Verdict.User { + switch conn.Verdict.Firewall { case network.VerdictAccept, network.VerdictRerouteToNameserver, network.VerdictRerouteToTunnel: accepted := true c.Allowed = &accepted diff --git a/network/api.go b/network/api.go index 00b8ed9c..a9f6d993 100644 --- a/network/api.go +++ b/network/api.go @@ -152,7 +152,7 @@ func AddNetworkDebugData(di *debug.Info, profile, where string) { // Count. total++ - switch conn.Verdict.Current { //nolint:exhaustive + switch conn.Verdict.Firewall { //nolint:exhaustive case VerdictAccept, VerdictRerouteToNameserver, VerdictRerouteToTunnel: diff --git a/network/api_test.go b/network/api_test.go index 0d37f022..0e908c8c 100644 --- a/network/api_test.go +++ b/network/api_test.go @@ -41,13 +41,13 @@ var connectionTestData = []*Connection{ ASN: 0, }, Verdict: struct { - Current Verdict - Previous Verdict - User Verdict + Worst Verdict + Active Verdict + Firewall Verdict }{ - Current: 2, - Previous: 2, - User: 2, + Worst: 2, + Active: 2, + Firewall: 2, }, Reason: Reason{ Msg: "incoming connection blocked by default", @@ -89,13 +89,13 @@ var connectionTestData = []*Connection{ ASN: 16509, }, Verdict: struct { - Current Verdict - Previous Verdict - User Verdict + Worst Verdict + Active Verdict + Firewall Verdict }{ - Current: 2, - Previous: 2, - User: 2, + Worst: 2, + Active: 2, + Firewall: 2, }, Reason: Reason{ Msg: "default permit", @@ -140,13 +140,13 @@ var connectionTestData = []*Connection{ ASN: 15169, }, Verdict: struct { - Current Verdict - Previous Verdict - User Verdict + Worst Verdict + Active Verdict + Firewall Verdict }{ - Current: 2, - Previous: 2, - User: 2, + Worst: 2, + Active: 2, + Firewall: 2, }, Reason: Reason{ Msg: "default permit", diff --git a/network/connection.go b/network/connection.go index dbad5664..e3b45ddf 100644 --- a/network/connection.go +++ b/network/connection.go @@ -111,12 +111,15 @@ type Connection struct { //nolint:maligned // TODO: fix alignment // The verdict may change so any access to it must be guarded by the // connection lock. Verdict struct { - // Current is the current decision that has been made for a connection. - Current Verdict - // Previous holds the previous verdict value, if there wasn't previous it will VerdictUndecided - Previous Verdict - // User holds the verdict that should be displayed in the user interface - User Verdict + // Worst verdict holds the worst verdict that was assigned to this + // connection from a privacy/security perspective. + Worst Verdict + // Active verdict holds the verdict that Portmaster will respond with. + // This is different from the Firewall verdict in order to guarantee proper + // transition between verdicts that need the connection to be re-established. + Active Verdict + // Firewall holsd the last (most recent) decision by the firewall. + Firewall Verdict } // Reason holds information justifying the verdict, as well as additional // information about the reason. @@ -524,35 +527,35 @@ func (conn *Connection) Failed(reason, reasonOptionKey string) { func (conn *Connection) SetVerdict(newVerdict Verdict, reason, reasonOptionKey string, reasonCtx interface{}) (ok bool) { conn.SetVerdictDirectly(newVerdict) - // Only set if it matches the user verdict. For a consistent reason - if newVerdict == conn.Verdict.User { - conn.Reason.Msg = reason - conn.Reason.Context = reasonCtx + 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) - } + 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 true // TODO: remove } -// SetVerdictDirectly sets the new verdict and stores the previous value. +// SetVerdictDirectly sets the firewall verdict. func (conn *Connection) SetVerdictDirectly(newVerdict Verdict) { - if newVerdict == conn.Verdict.Current { - return - } - // Save previous verdict and set new one - conn.Verdict.Previous = conn.Verdict.Current - conn.Verdict.Current = newVerdict + conn.Verdict.Firewall = newVerdict +} - // if a connection is accepted once it should always show as accepted - if conn.Verdict.User != VerdictAccept { - conn.Verdict.User = newVerdict +// VerdictVerb returns the verdict as a verb, while taking any special states +// into account. +func (conn *Connection) VerdictVerb() string { + if conn.Verdict.Firewall == conn.Verdict.Active { + return conn.Verdict.Firewall.Verb() } + return fmt.Sprintf( + "%s (transitioning to %s)", + conn.Verdict.Active.Verb(), + conn.Verdict.Firewall.Verb(), + ) } // Process returns the connection's process. @@ -679,7 +682,7 @@ func packetHandlerHandleConn(conn *Connection, pkt packet.Packet) { } // Log verdict. - log.Tracer(pkt.Ctx()).Infof("filter: connection %s %s: %s", conn, conn.Verdict.Current.Verb(), conn.Reason.Msg) + log.Tracer(pkt.Ctx()).Infof("filter: connection %s %s: %s", conn, conn.VerdictVerb(), conn.Reason.Msg) // Submit trace logs. log.Tracer(pkt.Ctx()).Submit() diff --git a/network/dns.go b/network/dns.go index 6638b5e8..d016783e 100644 --- a/network/dns.go +++ b/network/dns.go @@ -115,7 +115,7 @@ func writeOpenDNSRequestsToDB() { // ReplyWithDNS creates a new reply to the given request with the data from the RRCache, and additional informational records. func (conn *Connection) ReplyWithDNS(ctx context.Context, request *dns.Msg) *dns.Msg { // Select request responder. - switch conn.Verdict.Current { + switch conn.Verdict.Active { case VerdictBlock: return nsutil.BlockIP().ReplyWithDNS(ctx, request) case VerdictDrop: @@ -136,7 +136,7 @@ func (conn *Connection) ReplyWithDNS(ctx context.Context, request *dns.Msg) *dns func (conn *Connection) GetExtraRRs(ctx context.Context, request *dns.Msg) []dns.RR { // Select level to add the verdict record with. var level log.Severity - switch conn.Verdict.Current { + switch conn.Verdict.Active { case VerdictFailed: level = log.ErrorLevel case VerdictUndecided, VerdictUndeterminable, @@ -148,7 +148,7 @@ func (conn *Connection) GetExtraRRs(ctx context.Context, request *dns.Msg) []dns } // Create resource record with verdict and reason. - rr, err := nsutil.MakeMessageRecord(level, fmt.Sprintf("%s: %s", conn.Verdict.Current.Verb(), conn.Reason.Msg)) + rr, err := nsutil.MakeMessageRecord(level, fmt.Sprintf("%s: %s", conn.VerdictVerb(), conn.Reason.Msg)) if err != nil { log.Tracer(ctx).Warningf("filter: failed to add informational record to reply: %s", err) return nil diff --git a/network/metrics.go b/network/metrics.go index 8729b509..db5e701e 100644 --- a/network/metrics.go +++ b/network/metrics.go @@ -119,7 +119,7 @@ func (conn *Connection) addToMetrics() { } // Check the verdict. - switch conn.Verdict.Current { //nolint:exhaustive // Not critical. + switch conn.Verdict.Firewall { //nolint:exhaustive // Not critical. case VerdictBlock, VerdictDrop: blockedOutConnCounter.Inc() conn.addedToMetrics = true diff --git a/network/reference/protocols.go b/network/reference/protocols.go index 8d193817..12202e8d 100644 --- a/network/reference/protocols.go +++ b/network/reference/protocols.go @@ -46,3 +46,30 @@ func GetProtocolNumber(protocol string) (number uint8, ok bool) { } return 0, false } + +// IsPacketProtocol returns whether the given protocol number is a known packet based protocol. +// Note: Not fully complete. Calling IsPacketProtocol() does not equal calling !IsStreamProtocol(). +func IsPacketProtocol(protocol uint8) bool { + switch protocol { + case 1, // ICMP + 17, // UDP + 27, // RDP + 58, // ICMP6 + 33, // DCCP + 136: // UDP-LITE + return true + default: + return false + } +} + +// IsStreamProtocol returns whether the given protocol number is a known stream based protocol. +// Note: Not fully complete. Calling IsPacketProtocol() does not equal calling !IsStreamProtocol(). +func IsStreamProtocol(protocol uint8) bool { + switch protocol { + case 6: // TCP + return true + default: + return false + } +}