Skip to content

Commit fb96ef8

Browse files
Apply anonymization to traces before writing in triggertrace (#154)
* Add anonymization support to scamper parsers * Anonymize traces before writing * Update m-lab/go version * Rename Tracetool field * Add test case for WriteFile failure * Correct goveralls path
1 parent 189c0c7 commit fb96ef8

File tree

15 files changed

+397
-76
lines changed

15 files changed

+397
-76
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ go:
33
- 1.18
44

55
before_install:
6-
- go get github.com/mattn/goveralls
6+
- go install github.com/mattn/goveralls@latest
77
# Install dependencies, including test dependencies.
88
- sudo apt-get update && sudo apt-get install -y scamper
99
- go get -v -t ./...
@@ -18,4 +18,4 @@ script:
1818
after_success:
1919
# Coveralls
2020
# Upload coverage information for unit tests.
21-
- $HOME/gopath/bin/goveralls -coverprofile=_coverage.cov -service=travis-ci
21+
- $GOPATH/bin/goveralls -coverprofile=_coverage.cov -service=travis-ci

cmd/trex/main.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -156,22 +156,19 @@ func parseFile(fileName string) *parser.Scamper1 {
156156
nParseErrors++
157157
return nil
158158
}
159-
var scamper1 parser.Scamper1
160-
switch p := parsedData.(type) {
161-
case parser.Scamper1:
162-
scamper1 = p
163-
default:
159+
scamper1, ok := parsedData.(*parser.Scamper1)
160+
if !ok {
164161
// This is an internal error because we instantiated a new MDA
165162
// parser which should return Scamper1 as the concrete type.
166-
fmt.Fprintf(os.Stderr, "%T: unknown datatype (expected scamper1)", p)
163+
fmt.Fprintf(os.Stderr, "%T: unknown datatype (expected scamper1)", parsedData)
167164
os.Exit(1)
168165
}
169166
nFilesParsed++
170167
if len(scamper1.Tracelb.Nodes) == 0 {
171168
nNoTraceroute++
172169
return nil
173170
}
174-
return &scamper1
171+
return scamper1
175172
}
176173

177174
// tracerouteDuration returns the duration of the specified traceroute

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/m-lab/traceroute-caller
33
go 1.18
44

55
require (
6-
github.com/m-lab/go v0.1.47
6+
github.com/m-lab/go v0.1.55
77
github.com/m-lab/tcp-info v1.5.3
88
github.com/m-lab/uuid-annotator v0.4.7
99
github.com/prometheus/client_golang v1.12.2

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,8 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
242242
github.com/m-lab/annotation-service v0.0.0-20210504151333-138bdf572368 h1:cRzgLEJxoMI0iSexWOxTZQR/gNG08ra1IoEEgwJBdgs=
243243
github.com/m-lab/go v0.1.47 h1:yV6RgVpiWm2BnpJcjfy4pbUkB9cz0BvBbVG64UGLiC0=
244244
github.com/m-lab/go v0.1.47/go.mod h1:woT26L9Hf07juZGHe7Z4WveV7MM6NS6vQaaWzRQnab4=
245+
github.com/m-lab/go v0.1.55 h1:cuapp1ZTaBL6QDylni+k/GVnCqf9OUdzbJM+qnkh96Q=
246+
github.com/m-lab/go v0.1.55/go.mod h1:O1D/EoVarJ8lZt9foANcqcKtwxHatBzUxXFFyC87aQQ=
245247
github.com/m-lab/tcp-info v1.5.3 h1:4IspTPcNc8D8LNRvuFnID8gDiz+hxPAtYvpKZaiGGe8=
246248
github.com/m-lab/tcp-info v1.5.3/go.mod h1:bkvI4qbjB6QVC2tsLSHqf5OnIYcmuLEVjo7+8YA56Kg=
247249
github.com/m-lab/uuid-annotator v0.4.1/go.mod h1:f/zvgcc5A3HQ1Y63HWpbBVXNcsJwQ4uRIOqsF/nyto8=

internal/triggertrace/triggertrace.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"sync"
1313
"time"
1414

15+
"github.com/m-lab/go/anonymize"
1516
"github.com/m-lab/tcp-info/inetdiag"
1617
"github.com/m-lab/traceroute-caller/hopannotation"
1718
"github.com/m-lab/traceroute-caller/internal/ipcache"
@@ -50,6 +51,7 @@ type AnnotateAndArchiver interface {
5051
WriteAnnotations(map[string]*annotator.ClientAnnotations, time.Time) []error
5152
}
5253

54+
// TracerWriter provides the interface for issuing traces and writing results.
5355
type TracerWriter interface {
5456
ipcache.Tracer
5557
WriteFile(uuid string, t time.Time, b []byte) error
@@ -63,7 +65,8 @@ type Handler struct {
6365
IPCache FetchTracer
6466
Parser ParseTracer
6567
HopAnnotator AnnotateAndArchiver
66-
Tracer TracerWriter
68+
Tracetool TracerWriter
69+
Anonymizer anonymize.IPAnonymizer
6770
done chan struct{} // For testing.
6871
}
6972

@@ -87,7 +90,8 @@ func NewHandler(ctx context.Context, tracetool TracerWriter, ipcCfg ipcache.Conf
8790
IPCache: ipCache,
8891
Parser: newParser,
8992
HopAnnotator: hopCache,
90-
Tracer: tracetool,
93+
Tracetool: tracetool,
94+
Anonymizer: anonymize.New(anonymize.IPAnonymizationFlag),
9195
}, nil
9296
}
9397

@@ -152,8 +156,14 @@ func (h *Handler) traceAnnotateAndArchive(ctx context.Context, uuid string, dest
152156
log.Printf("context %p: failed to parse traceroute output (error: %v)\n", ctx, err)
153157
return
154158
}
159+
160+
// Anonymize the parsed data in place.
161+
parsedData.Anonymize(h.Anonymizer)
162+
// Remarshal anonymized data for writing.
163+
rawData = parsedData.MarshalJSONL()
164+
155165
traceStartTime := parsedData.StartTime()
156-
err = h.Tracer.WriteFile(uuid, traceStartTime, rawData)
166+
err = h.Tracetool.WriteFile(uuid, traceStartTime, rawData)
157167
if err != nil {
158168
log.Printf("context %p: failed to write trace file for uuid: %s: (error: %v)\n", ctx, uuid, err)
159169
}

internal/triggertrace/triggertrace_test.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"log"
88
"net"
99
"os"
10+
"path"
1011
"sync/atomic"
1112
"testing"
1213
"time"
@@ -23,6 +24,7 @@ var (
2324
forceParseErr = "88.88.88.88" // force a failure parsing a traceroute output
2425
forceExtractErr = "77.77.77.77" // force a failure extracting hops
2526
forceAnnotateErr = "66.66.66.66" // force a failure annotating hops
27+
forceWriteErr = "write-err-8" // force a failure writing trace
2628
)
2729

2830
func init() {
@@ -57,7 +59,12 @@ func (ft *fakeTracer) Trace(remoteIP, uuid string, t time.Time) ([]byte, error)
5759
}
5860

5961
func (ft *fakeTracer) WriteFile(uuid string, t time.Time, data []byte) error {
60-
return nil
62+
switch uuid {
63+
case forceWriteErr:
64+
return errors.New("forced write error")
65+
default:
66+
return nil
67+
}
6168
}
6269

6370
func (ft *fakeTracer) CachedTrace(uuid string, t time.Time, cachedTest []byte) ([]byte, error) {
@@ -99,12 +106,12 @@ func TestNewHandler(t *testing.T) {
99106
defer func() { netInterfaceAddrs = saveNetInterfaceAddrs }()
100107

101108
netInterfaceAddrs = fakeInterfaceAddrsBad
102-
if _, err := newHandler(&fakeTracer{}); err == nil {
109+
if _, err := newHandler(t, &fakeTracer{}); err == nil {
103110
t.Fatalf("NewHandler() = nil, want error")
104111
}
105112

106113
netInterfaceAddrs = fakeInterfaceAddrs
107-
if _, err := newHandler(&fakeTracer{}); err != nil {
114+
if _, err := newHandler(t, &fakeTracer{}); err != nil {
108115
t.Fatalf("NewHandler() = %v, want nil", err)
109116
}
110117
}
@@ -114,7 +121,7 @@ func TestOpen(t *testing.T) {
114121
netInterfaceAddrs = fakeInterfaceAddrs
115122
defer func() { netInterfaceAddrs = saveNetInterfaceAddrs }()
116123

117-
handler, err := newHandler(&fakeTracer{})
124+
handler, err := newHandler(t, &fakeTracer{})
118125
if err != nil {
119126
t.Fatalf("NewHandler() = %v, want nil", err)
120127
}
@@ -164,13 +171,14 @@ func TestClose(t *testing.T) {
164171
{"bad6", "127.0.0.1", forceAnnotateErr, "00005", true, true, 1, 0},
165172
{"good1", "127.0.0.1", "3.4.5.6", "00006", true, true, 1, 0},
166173
{"good2", "4.5.6.7", "127.0.0.1", "00007", true, true, 1, 1},
174+
{"bad7", "127.0.0.1", "192.168.33.2", forceWriteErr, true, true, 1, 0},
167175
}
168176
for _, test := range tests {
169177
test := test
170178
t.Run(test.name, func(t *testing.T) {
171179
t.Parallel()
172180
tracer := &fakeTracer{}
173-
handler, err := newHandler(tracer)
181+
handler, err := newHandler(t, tracer)
174182
if err != nil {
175183
t.Fatalf("NewHandler() = %v, want nil", err)
176184
}
@@ -206,15 +214,15 @@ func TestClose(t *testing.T) {
206214
}
207215
}
208216

209-
func newHandler(tracer *fakeTracer) (*Handler, error) {
217+
func newHandler(t *testing.T, tracer *fakeTracer) (*Handler, error) {
210218
ipcCfg := ipcache.Config{
211219
EntryTimeout: 2 * time.Second,
212220
ScanPeriod: 1 * time.Second,
213221
}
214222
annotator := &fakeAnnotator{}
215223
haCfg := hopannotation.Config{
216224
AnnotatorClient: annotator,
217-
OutputPath: "/tmp/annotation1",
225+
OutputPath: path.Join(t.TempDir(), "annotation1"),
218226
}
219227
newParser, err := parser.New("mda")
220228
if err != nil {

parser/parser.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"errors"
66
"fmt"
77
"time"
8+
9+
"github.com/m-lab/go/anonymize"
810
)
911

1012
// Errors returned by parser.
@@ -51,6 +53,8 @@ type CyclestopLine struct {
5153
type ParsedData interface {
5254
StartTime() time.Time
5355
ExtractHops() []string
56+
MarshalJSONL() []byte
57+
Anonymize(anon anonymize.IPAnonymizer)
5458
}
5559

5660
// TracerouteParser defines the interface for raw traceroute data.

parser/scamper1.go

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net"
88
"time"
99

10+
"github.com/m-lab/go/anonymize"
1011
"github.com/m-lab/traceroute-caller/tracer"
1112
)
1213

@@ -57,10 +58,12 @@ type ScamperNode struct {
5758
}
5859

5960
// Scamper1 encapsulates the four lines of a traceroute:
60-
// {"UUID":...}
61-
// {"type":"cycle-start"...}
62-
// {"type":"tracelb"...}
63-
// {"type":"cycle-stop"...}
61+
//
62+
// {"UUID":...}
63+
// {"type":"cycle-start"...}
64+
// {"type":"tracelb"...}
65+
// {"type":"cycle-stop"...}
66+
//
6467
// Refer to scamper source code files scamper/scamper_list.h and
6568
// scamper/tracelb/scamper_tracelb.h for the definitions of cycle_start,
6669
// tracelb, and cycle_stop lines.
@@ -143,7 +146,7 @@ func (s1 *scamper1Parser) ParseRawData(rawData []byte) (ParsedData, error) {
143146
return nil, fmt.Errorf("%w: %v", ErrCycleStopType, scamper1.CycleStop.Type)
144147
}
145148

146-
return scamper1, nil
149+
return &scamper1, nil
147150
}
148151

149152
// StartTime returns the start time of the traceroute.
@@ -177,3 +180,42 @@ func (s1 Scamper1) ExtractHops() []string {
177180
}
178181
return hopStrings
179182
}
183+
184+
// Anonymize looks for hops that are in the client subnet, and anonymizes them using the given anonymizer.
185+
func (s1 *Scamper1) Anonymize(anon anonymize.IPAnonymizer) {
186+
tracelb := s1.Tracelb
187+
dst := net.ParseIP(tracelb.Dst)
188+
anon.IP(dst)
189+
s1.Tracelb.Dst = dst.String()
190+
191+
for i := range tracelb.Nodes {
192+
node := &tracelb.Nodes[i]
193+
ip := net.ParseIP(node.Addr)
194+
if anon.Contains(dst, ip) {
195+
anon.IP(ip)
196+
node.Addr = ip.String()
197+
}
198+
for j := range node.Links {
199+
links := node.Links[j]
200+
for k := range links {
201+
link := &links[k]
202+
ip = net.ParseIP(link.Addr)
203+
if anon.Contains(dst, ip) {
204+
anon.IP(ip)
205+
link.Addr = ip.String()
206+
}
207+
}
208+
}
209+
}
210+
}
211+
212+
// MarshalJSONL encodes the scamper object as JSONL.
213+
func (s1 Scamper1) MarshalJSONL() []byte {
214+
buff := &bytes.Buffer{}
215+
enc := json.NewEncoder(buff)
216+
enc.Encode(s1.Metadata)
217+
enc.Encode(s1.CycleStart)
218+
enc.Encode(s1.Tracelb)
219+
enc.Encode(s1.CycleStop)
220+
return buff.Bytes()
221+
}

0 commit comments

Comments
 (0)