Skip to content

Commit

Permalink
refactor(webconnectivity@v0.5): improve logging clarity
Browse files Browse the repository at this point in the history
We're bumping the experiment's version number because we changed the
name of the field used to contain late/duplicate DNS responses.

We have also changed the algorithm to determine #dnsDiff, however, the
change should only impact how we log this information.

Overall, here the idea is to provide users with a reasonably clear
explanation of how the probe maps observations to blocking and accessible.

Part of ooni/probe#2237
  • Loading branch information
bassosimone committed Sep 15, 2022
1 parent d6a362d commit 25c62c7
Show file tree
Hide file tree
Showing 9 changed files with 88 additions and 33 deletions.
10 changes: 5 additions & 5 deletions internal/experiment/webconnectivity/analysiscore.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,38 +109,38 @@ func (tk *TestKeys) analysisToplevel(logger model.Logger) {
tk.Blocking = "dns"
tk.Accessible = false
logger.Warnf(
"ANOMALY: flags=%d accessible=%+v, blocking=%+v",
"ANOMALY: flags=%d, accessible=%+v, blocking=%+v",
tk.BlockingFlags, tk.Accessible, tk.Blocking,
)

case (tk.BlockingFlags & analysisFlagTCPIPBlocking) != 0:
tk.Blocking = "tcp_ip"
tk.Accessible = false
logger.Warnf(
"ANOMALY: flags=%d accessible=%+v, blocking=%+v",
"ANOMALY: flags=%d, accessible=%+v, blocking=%+v",
tk.BlockingFlags, tk.Accessible, tk.Blocking,
)

case (tk.BlockingFlags & (analysisFlagTLSBlocking | analysisFlagHTTPBlocking)) != 0:
tk.Blocking = "http-failure"
tk.Accessible = false
logger.Warnf("ANOMALY: flags=%d accessible=%+v, blocking=%+v",
logger.Warnf("ANOMALY: flags=%d, accessible=%+v, blocking=%+v",
tk.BlockingFlags, tk.Accessible, tk.Blocking,
)

case (tk.BlockingFlags & analysisFlagHTTPDiff) != 0:
tk.Blocking = "http-diff"
tk.Accessible = false
logger.Warnf(
"ANOMALY: flags=%d accessible=%+v, blocking=%+v",
"ANOMALY: flags=%d, accessible=%+v, blocking=%+v",
tk.BlockingFlags, tk.Accessible, tk.Blocking,
)

case tk.BlockingFlags == analysisFlagSuccess:
tk.Blocking = false
tk.Accessible = true
logger.Infof(
"ACCESSIBLE: flags=%d accessible=%+v, blocking=%+v",
"ACCESSIBLE: flags=%d, accessible=%+v, blocking=%+v",
tk.BlockingFlags, tk.Accessible, tk.Blocking,
)

Expand Down
81 changes: 67 additions & 14 deletions internal/experiment/webconnectivity/analysisdns.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ const (
func (tk *TestKeys) analysisDNSToplevel(logger model.Logger) {
tk.analysisDNSExperimentFailure()
tk.analysisDNSBogon(logger)
tk.analysisDNSDuplicateResponses(logger)
tk.analysisDNSUnexpectedFailure(logger)
tk.analysisDNSUnexpectedAddrs(logger)
if tk.DNSFlags != 0 {
Expand All @@ -70,6 +71,15 @@ func (tk *TestKeys) analysisDNSToplevel(logger model.Logger) {
}
}

// analysisDNSDuplicateResponses checks whether we received duplicate
// replies for DNS-over-UDP queries, which is very unexpected.
func (tk *TestKeys) analysisDNSDuplicateResponses(logger model.Logger) {
if length := len(tk.DNSDuplicateResponses); length > 0 {
// TODO(bassosimone): write algorithm to analyze this
logger.Warnf("DNS: got %d unexpected late/duplicate DNS responses", length)
}
}

// analysisDNSExperimentFailure sets the legacy DNSExperimentFailure field.
func (tk *TestKeys) analysisDNSExperimentFailure() {
for _, query := range tk.Queries {
Expand Down Expand Up @@ -106,13 +116,23 @@ func (tk *TestKeys) analysisDNSBogon(logger model.Logger) {
switch answer.AnswerType {
case "A":
if net.ParseIP(answer.IPv4) != nil && netxlite.IsBogon(answer.IPv4) {
logger.Warnf("DNS: BOGON %s in #%d", answer.IPv4, query.TransactionID)
logger.Warnf(
"DNS: got A BOGON answer %s for domain %s (see #%d)",
answer.IPv4,
query.Hostname,
query.TransactionID,
)
tk.DNSFlags |= AnalysisDNSBogon
// continue processing so we print all the bogons we have
}
case "AAAA":
if net.ParseIP(answer.IPv6) != nil && netxlite.IsBogon(answer.IPv6) {
logger.Warnf("DNS: BOGON %s in #%d", answer.IPv6, query.TransactionID)
logger.Warnf(
"DNS: got AAAA BOGON answer %s for %s (see #%d)",
answer.IPv6,
query.Hostname,
query.TransactionID,
)
tk.DNSFlags |= AnalysisDNSBogon
// continue processing so we print all the bogons we have
}
Expand Down Expand Up @@ -255,7 +275,7 @@ func (tk *TestKeys) analysisDNSUnexpectedAddrs(logger model.Logger) {
// if the probe has not collected any addr for the same domain, it's
// definitely suspicious and counts as a difference
if len(probeAddrs) <= 0 {
logger.Warnf("DNS: no IP address resolved by the probe")
logger.Warnf("DNS: the probe did not resolve any IP address")
tk.DNSFlags |= AnalysisDNSUnexpectedAddrs
return
}
Expand All @@ -266,26 +286,34 @@ func (tk *TestKeys) analysisDNSUnexpectedAddrs(logger model.Logger) {
if len(differentAddrs) <= 0 {
return
}
for _, addr := range differentAddrs {
logger.Infof("DNS: address %s: not resolved by TH", addr)
}

// now, let's exclude the differentAddrs for which we successfully
// completed a TLS handshake: those should be good addrs
withoutHandshake := tk.findAddrsWithoutTLSHandshake(domain, differentAddrs)
if len(withoutHandshake) <= 0 {
return
}
for _, addr := range withoutHandshake {
logger.Infof("DNS: address %s: cannot confirm using TLS handshake", addr)
}

// as a last resort, accept the addresses without an handshake whose
// ASN overlaps with ASNs resolved by the TH
differentASNs := tk.analysisDNSDiffASN(withoutHandshake, thAddrs)
differentASNs := tk.analysisDNSDiffASN(logger, withoutHandshake, thAddrs)
if len(differentASNs) <= 0 {
return
}

// otherwise, conclude we have unexpected probe addrs
logger.Warnf(
"DNSDiff: differentAddrs: %+v; withoutHandshake: %+v; differentASNs: %+v",
differentAddrs, withoutHandshake, differentASNs,
)
for addr, asn := range differentASNs {
logger.Warnf(
"DNS: address %s has unexpected AS%d and we cannot use TLS to confirm it",
addr, asn,
)
}
tk.DNSFlags |= AnalysisDNSUnexpectedAddrs
}

Expand All @@ -298,6 +326,10 @@ func (tk *TestKeys) analysisDNSDiffAddrs(probeAddrs, thAddrs []string) (diff []s
)
mapping := make(map[string]int)
for _, addr := range probeAddrs {
if net.ParseIP(addr) != nil && netxlite.IsBogon(addr) {
// we can exclude bogons from the analysis because we already analyzed them
continue
}
mapping[addr] |= inProbe
}
for _, addr := range thAddrs {
Expand All @@ -313,23 +345,44 @@ func (tk *TestKeys) analysisDNSDiffAddrs(probeAddrs, thAddrs []string) (diff []s

// analysisDNSDiffASN returns whether there are IP addresses in the probe's
// list with different ASNs from the ones in the TH's list.
func (tk *TestKeys) analysisDNSDiffASN(probeAddrs, thAddrs []string) (asns []uint) {
func (tk *TestKeys) analysisDNSDiffASN(
logger model.Logger,
probeAddrs,
thAddrs []string,
) (result map[string]uint) {
const (
inProbe = 1 << iota
inTH
)
mapping := make(map[uint]int)
logger.Debugf("DNS: probeAddrs %+v, thAddrs %+v", probeAddrs, thAddrs)
uniqueAddrs := make(map[string]uint)
asnToFlags := make(map[uint]int)
for _, addr := range probeAddrs {
asn, _, _ := geoipx.LookupASN(addr)
mapping[asn] |= inProbe // including the zero ASN that means unknown
asnToFlags[asn] |= inProbe // including the zero ASN that means unknown
uniqueAddrs[addr] = asn
}
for _, addr := range thAddrs {
asn, _, _ := geoipx.LookupASN(addr)
mapping[asn] |= inTH // including the zero ASN that means unknown
asnToFlags[asn] |= inTH // including the zero ASN that means unknown
uniqueAddrs[addr] = asn
}
for addr, asn := range uniqueAddrs {
logger.Infof("DNS: addr %s has AS%d", addr, asn)
}
for asn, where := range mapping {
probeOnlyASNs := make(map[uint]bool)
for asn, where := range asnToFlags {
if (where & inTH) == 0 {
asns = append(asns, asn)
probeOnlyASNs[asn] = true
}
}
for asn := range probeOnlyASNs {
logger.Infof("DNS: AS%d: only seen by probe", asn)
}
result = make(map[string]uint)
for addr, asn := range uniqueAddrs {
if probeOnlyASNs[asn] {
result[addr] = asn
}
}
return
Expand Down
4 changes: 2 additions & 2 deletions internal/experiment/webconnectivity/analysishttpcore.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,10 @@ func (tk *TestKeys) analysisHTTPToplevel(logger model.Logger) {
netxlite.FailureEOFError:
tk.BlockingFlags |= analysisFlagHTTPBlocking
logger.Warnf(
"HTTP: endpoint %s is blocked (see #%d): %s",
"HTTP: unexpected failure %s for %s (see #%d)",
*failure,
finalRequest.Address,
finalRequest.TransactionID,
*failure,
)
default:
// leave this case for ooni/pipeline
Expand Down
5 changes: 5 additions & 0 deletions internal/experiment/webconnectivity/analysishttpdiff.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ func (tk *TestKeys) analysisHTTPDiff(logger model.Logger,
tk.BlockingFlags |= analysisFlagSuccess
return
}
logger.Info("HTTP: body length: MISMATCH")
if tk.HeadersMatch != nil && *tk.HeadersMatch {
logger.Infof(
"HTTP: statusCodeMatch && headersMatch => #%d is successful",
Expand All @@ -61,6 +62,7 @@ func (tk *TestKeys) analysisHTTPDiff(logger model.Logger,
tk.BlockingFlags |= analysisFlagSuccess
return
}
logger.Info("HTTP: headers: MISMATCH")
if tk.TitleMatch != nil && *tk.TitleMatch {
logger.Infof(
"HTTP: statusCodeMatch && titleMatch => #%d is successful",
Expand All @@ -69,6 +71,9 @@ func (tk *TestKeys) analysisHTTPDiff(logger model.Logger,
tk.BlockingFlags |= analysisFlagSuccess
return
}
logger.Info("HTTP: title: MISMATCH")
} else {
logger.Infof("HTTP: status code: MISMATCH")
}

tk.BlockingFlags |= analysisFlagHTTPDiff
Expand Down
4 changes: 2 additions & 2 deletions internal/experiment/webconnectivity/analysistcpip.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,10 @@ func (tk *TestKeys) analysisTCPIPToplevel(logger model.Logger) {
continue
}
logger.Warnf(
"TCP/IP: endpoint %s is blocked (see #%d): %s",
"TCP/IP: unexpected failure %s for %s (see #%d)",
*failure,
epnt,
entry.TransactionID,
*failure,
)
entry.Status.Blocked = &istrue
tk.BlockingFlags |= analysisFlagTCPIPBlocking
Expand Down
4 changes: 2 additions & 2 deletions internal/experiment/webconnectivity/analysistls.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ func (tk *TestKeys) analysisTLSToplevel(logger model.Logger) {
continue
}
logger.Warnf(
"TLS: endpoint %s is blocked (see #%d): %s",
"TLS: unexpected failure %s for %s (see #%d)",
*failure,
epnt,
entry.TransactionID,
*failure,
)
tk.BlockingFlags |= analysisFlagTLSBlocking
}
Expand Down
3 changes: 0 additions & 3 deletions internal/experiment/webconnectivity/dnsresolvers.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,6 @@ func (t *DNSResolvers) waitForLateReplies(parentCtx context.Context, trace *meas
defer t.WaitGroup.Done()
const lateTimeout = 500 * time.Millisecond
events := trace.DelayedDNSResponseWithTimeout(parentCtx, lateTimeout)
if length := len(events); length > 0 {
t.Logger.Warnf("got %d late DNS replies", length)
}
t.TestKeys.AppendDNSLateReplies(events...)
}

Expand Down
2 changes: 1 addition & 1 deletion internal/experiment/webconnectivity/measurer.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func (m *Measurer) ExperimentName() string {

// ExperimentVersion implements model.ExperimentMeasurer.
func (m *Measurer) ExperimentVersion() string {
return "0.5.16"
return "0.5.17"
}

// Run implements model.ExperimentMeasurer.
Expand Down
8 changes: 4 additions & 4 deletions internal/experiment/webconnectivity/testkeys.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ type TestKeys struct {
// Do53 contains ancillary observations collected by Do53 resolvers.
Do53 *TestKeysDo53 `json:"x_do53"`

// DNSLateReplies contains late replies we didn't expect to receive from
// DNSDuplicateResponses contains late/duplicate responses we didn't expect to receive from
// a resolver (which may raise eyebrows if they're different).
DNSLateReplies []*model.ArchivalDNSLookupResult `json:"x_dns_late_replies"`
DNSDuplicateResponses []*model.ArchivalDNSLookupResult `json:"x_dns_duplicate_responses"`

// Queries contains DNS queries.
Queries []*model.ArchivalDNSLookupResult `json:"queries"`
Expand Down Expand Up @@ -202,7 +202,7 @@ func (tk *TestKeys) AppendNetworkEvents(v ...*model.ArchivalNetworkEvent) {
// AppendDNSLateReplies appends to DNSLateReplies.
func (tk *TestKeys) AppendDNSLateReplies(v ...*model.ArchivalDNSLookupResult) {
tk.mu.Lock()
tk.DNSLateReplies = append(tk.DNSLateReplies, v...)
tk.DNSDuplicateResponses = append(tk.DNSDuplicateResponses, v...)
tk.mu.Unlock()
}

Expand Down Expand Up @@ -325,7 +325,7 @@ func NewTestKeys() *TestKeys {
NetworkEvents: []*model.ArchivalNetworkEvent{},
Queries: []*model.ArchivalDNSLookupResult{},
},
DNSLateReplies: []*model.ArchivalDNSLookupResult{},
DNSDuplicateResponses: []*model.ArchivalDNSLookupResult{},
Queries: []*model.ArchivalDNSLookupResult{},
Requests: []*model.ArchivalHTTPRequestResult{},
TCPConnect: []*model.ArchivalTCPConnectResult{},
Expand Down

0 comments on commit 25c62c7

Please sign in to comment.