Skip to content

Commit fe6d3c3

Browse files
Assign server annotations when IP is in server netblocks (#21)
* Add additional siteannotator test cases * Clarify error when hostname not found * Change siteinfo data and check server IP * Add v4 and v6 connection tests when networks are empty
1 parent c62affd commit fe6d3c3

File tree

4 files changed

+262
-37
lines changed

4 files changed

+262
-37
lines changed

annotator/annotator.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ type ServerAnnotations struct {
101101
type Annotations struct {
102102
UUID string
103103
Timestamp time.Time
104-
Server ServerAnnotations `bigquery:"server"` // Use Standard Top-Level Column names.
105-
Client ClientAnnotations `bigquery:"client"` // Use Standard Top-Level Column names.
104+
Server ServerAnnotations `json:",omitempty" bigquery:"server"` // Use Standard Top-Level Column names.
105+
Client ClientAnnotations `json:",omitempty" bigquery:"client"` // Use Standard Top-Level Column names.
106106
}
107107

108108
// Annotator is the interface that all systems that want to add metadata should implement.

siteannotator/server.go

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ type siteAnnotator struct {
2222
siteinfoSource rawfile.Provider
2323
hostname string
2424
server *annotator.ServerAnnotations
25+
v4 net.IPNet
26+
v6 net.IPNet
2527
}
2628

27-
// ErrNotFound is generated when the given hostname cannot be found in the
29+
// ErrHostnameNotFound is generated when the given hostname cannot be found in the
2830
// downloaded siteinfo annotations.
29-
var ErrNotFound = errors.New("Not Found")
31+
var ErrHostnameNotFound = errors.New("Hostname Not Found")
3032

3133
// New makes a new server Annotator using metadata from siteinfo JSON.
3234
func New(ctx context.Context, hostname string, js rawfile.Provider, localIPs []net.IP) annotator.Annotator {
@@ -61,8 +63,39 @@ func (g *siteAnnotator) Annotate(ID *inetdiag.SockID, annotations *annotator.Ann
6163
}
6264

6365
func (g *siteAnnotator) annotate(src string, server *annotator.ServerAnnotations) {
64-
// TODO: verify that the given IP actually matches the public server IP block.
65-
*server = *g.server
66+
n := net.ParseIP(src)
67+
if g.v4.Contains(n) || g.v6.Contains(n) {
68+
// NOTE: this will not annotate private IP addrs.
69+
*server = *g.server
70+
}
71+
}
72+
73+
type siteinfoAnnotation struct {
74+
Name string
75+
Network struct {
76+
IPv4 string
77+
IPv6 string
78+
}
79+
Annotation annotator.ServerAnnotations
80+
}
81+
82+
func parseCIDR(v4, v6 string) (net.IPNet, net.IPNet, error) {
83+
var v4ret, v6ret net.IPNet
84+
_, v4net, err := net.ParseCIDR(v4)
85+
if err != nil && v4 != "" {
86+
return v4ret, v6ret, err
87+
}
88+
if v4 != "" {
89+
v4ret = *v4net
90+
}
91+
_, v6net, err := net.ParseCIDR(v6)
92+
if err != nil && v6 != "" {
93+
return v4ret, v6ret, err
94+
}
95+
if v6 != "" {
96+
v6ret = *v6net
97+
}
98+
return v4ret, v6ret, nil
6699
}
67100

68101
// load unconditionally loads siteinfo dataset and returns them.
@@ -71,23 +104,27 @@ func (g *siteAnnotator) load(ctx context.Context) (*annotator.ServerAnnotations,
71104
if err != nil {
72105
return nil, err
73106
}
74-
var s []annotator.ServerAnnotations
107+
var s []siteinfoAnnotation
75108
var result annotator.ServerAnnotations
76109
err = json.Unmarshal(js, &s)
77110
if err != nil {
78111
return nil, err
79112
}
80113
f := strings.Split(g.hostname, ".")
81114
if len(f) < 2 {
82-
return nil, ErrNotFound
115+
return nil, ErrHostnameNotFound
83116
}
84117
site := f[1]
85118
for i := range s {
86-
if s[i].Site == site {
87-
s[i].Machine = f[0]
88-
result = s[i] // Copy out of array.
119+
if s[i].Name == site {
120+
result = s[i].Annotation // Copy out of array.
121+
result.Machine = f[0]
122+
g.v4, g.v6, err = parseCIDR(s[i].Network.IPv4, s[i].Network.IPv6)
123+
if err != nil {
124+
return nil, err
125+
}
89126
return &result, nil
90127
}
91128
}
92-
return nil, ErrNotFound
129+
return nil, ErrHostnameNotFound
93130
}

siteannotator/server_test.go

Lines changed: 125 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
)
1818

1919
func init() {
20-
log.SetFlags(log.Lshortfile | log.LstdFlags)
20+
log.SetFlags(0)
2121
}
2222

2323
type badProvider struct {
@@ -34,12 +34,44 @@ func TestNew(t *testing.T) {
3434
localRawfile, err := rawfile.FromURL(context.Background(), u)
3535
rtx.Must(err, "Could not create rawfile.Provider")
3636

37+
minimalServerAnn := func(site string) annotator.ServerAnnotations {
38+
return annotator.ServerAnnotations{
39+
Site: site,
40+
Machine: "mlab1",
41+
Geo: &annotator.Geolocation{
42+
City: "New York",
43+
},
44+
Network: &annotator.Network{
45+
ASName: "TATA COMMUNICATIONS (AMERICA) INC",
46+
},
47+
}
48+
}
49+
defaultServerAnn := annotator.ServerAnnotations{
50+
Machine: "mlab1",
51+
Site: "lga03",
52+
Geo: &annotator.Geolocation{
53+
ContinentCode: "NA",
54+
CountryCode: "US",
55+
City: "New York",
56+
Latitude: 40.7667,
57+
Longitude: -73.8667,
58+
},
59+
Network: &annotator.Network{
60+
ASNumber: 6453,
61+
ASName: "TATA COMMUNICATIONS (AMERICA) INC",
62+
Systems: []annotator.System{
63+
{ASNs: []uint32{6453}},
64+
},
65+
},
66+
}
67+
3768
tests := []struct {
3869
name string
3970
localIPs []net.IP
4071
provider rawfile.Provider
4172
hostname string
4273
ID *inetdiag.SockID
74+
want annotator.Annotations
4375
wantErr bool
4476
}{
4577
{
@@ -53,6 +85,9 @@ func TestNew(t *testing.T) {
5385
DPort: 2,
5486
DstIP: "64.86.148.137",
5587
},
88+
want: annotator.Annotations{
89+
Server: defaultServerAnn,
90+
},
5691
},
5792
{
5893
name: "success-dest",
@@ -65,6 +100,65 @@ func TestNew(t *testing.T) {
65100
DPort: 2,
66101
DstIP: "1.0.0.1",
67102
},
103+
want: annotator.Annotations{
104+
Server: defaultServerAnn,
105+
},
106+
},
107+
{
108+
name: "success-empty-ipv4-with-ipv6-connection",
109+
localIPs: []net.IP{net.ParseIP("2001:5a0:4300::2")},
110+
provider: localRawfile,
111+
hostname: "mlab1.four0.measurement-lab.org",
112+
ID: &inetdiag.SockID{
113+
SPort: 1,
114+
SrcIP: "2001:5a0:4300::2",
115+
DPort: 2,
116+
DstIP: "2600::1",
117+
},
118+
want: annotator.Annotations{
119+
Server: minimalServerAnn("four0"),
120+
},
121+
},
122+
{
123+
name: "success-empty-ipv4-with-ipv4-connection",
124+
localIPs: []net.IP{net.ParseIP("64.86.148.137")},
125+
provider: localRawfile,
126+
hostname: "mlab1.four0.measurement-lab.org",
127+
ID: &inetdiag.SockID{
128+
SPort: 1,
129+
SrcIP: "64.86.148.137",
130+
DPort: 2,
131+
DstIP: "1.0.0.1",
132+
},
133+
want: annotator.Annotations{},
134+
},
135+
{
136+
name: "success-empty-ipv6-with-ipv4-connection",
137+
localIPs: []net.IP{net.ParseIP("64.86.148.130")},
138+
provider: localRawfile,
139+
hostname: "mlab1.six01.measurement-lab.org",
140+
ID: &inetdiag.SockID{
141+
SPort: 1,
142+
SrcIP: "64.86.148.130",
143+
DPort: 2,
144+
DstIP: "1.0.0.1",
145+
},
146+
want: annotator.Annotations{
147+
Server: minimalServerAnn("six01"),
148+
},
149+
},
150+
{
151+
name: "success-empty-ipv6-with-ipv6-connection",
152+
localIPs: []net.IP{net.ParseIP("2001:5a0:4300::2")},
153+
provider: localRawfile,
154+
hostname: "mlab1.six01.measurement-lab.org",
155+
ID: &inetdiag.SockID{
156+
SPort: 1,
157+
SrcIP: "2001:5a0:4300::2",
158+
DPort: 2,
159+
DstIP: "2600::1",
160+
},
161+
want: annotator.Annotations{},
68162
},
69163
{
70164
name: "error-neither-ips-are-server",
@@ -88,6 +182,9 @@ func TestNew(t *testing.T) {
88182
if err := g.Annotate(tt.ID, &ann); (err != nil) != tt.wantErr {
89183
t.Errorf("srvannotator.Annotate() error = %v, wantErr %v", err, tt.wantErr)
90184
}
185+
if diff := deep.Equal(ann, tt.want); diff != nil {
186+
t.Errorf("Annotate() failed; %s", strings.Join(diff, "\n"))
187+
}
91188
})
92189
}
93190
}
@@ -104,7 +201,6 @@ func Test_srvannotator_load(t *testing.T) {
104201

105202
tests := []struct {
106203
name string
107-
localIPs []net.IP
108204
provider rawfile.Provider
109205
hostname string
110206
ID *inetdiag.SockID
@@ -113,7 +209,6 @@ func Test_srvannotator_load(t *testing.T) {
113209
}{
114210
{
115211
name: "success",
116-
localIPs: []net.IP{net.ParseIP("64.86.148.137")},
117212
provider: localRawfile,
118213
hostname: "mlab1.lga03.measurement-lab.org",
119214
want: &annotator.ServerAnnotations{
@@ -135,30 +230,53 @@ func Test_srvannotator_load(t *testing.T) {
135230
},
136231
},
137232
},
233+
{
234+
name: "success-no-six",
235+
provider: localRawfile,
236+
hostname: "mlab1.six01.measurement-lab.org",
237+
want: &annotator.ServerAnnotations{
238+
Site: "six01",
239+
Machine: "mlab1",
240+
Geo: &annotator.Geolocation{
241+
City: "New York",
242+
},
243+
Network: &annotator.Network{
244+
ASName: "TATA COMMUNICATIONS (AMERICA) INC",
245+
},
246+
},
247+
},
248+
{
249+
name: "error-bad-ipv4",
250+
provider: localRawfile,
251+
hostname: "mlab1.bad04.measurement-lab.org",
252+
wantErr: true,
253+
},
254+
{
255+
name: "error-bad-ipv6",
256+
provider: localRawfile,
257+
hostname: "mlab1.bad06.measurement-lab.org",
258+
wantErr: true,
259+
},
138260
{
139261
name: "error-loading-provider",
140-
localIPs: []net.IP{net.ParseIP("64.86.148.137")},
141262
provider: &badProvider{fmt.Errorf("Fake load error")},
142263
hostname: "mlab1.lga03.measurement-lab.org",
143264
wantErr: true,
144265
},
145266
{
146267
name: "error-corrupt-json",
147-
localIPs: []net.IP{net.ParseIP("64.86.148.137")},
148268
provider: corruptFile,
149269
hostname: "mlab1.lga03.measurement-lab.org",
150270
wantErr: true,
151271
},
152272
{
153273
name: "error-bad-hostname",
154-
localIPs: []net.IP{net.ParseIP("64.86.148.137")},
155274
provider: localRawfile,
156275
hostname: "this-is-not-a-hostname",
157276
wantErr: true,
158277
},
159278
{
160279
name: "error-hostname-not-in-annotations",
161-
localIPs: []net.IP{net.ParseIP("64.86.148.137")},
162280
provider: localRawfile,
163281
hostname: "mlab1.none0.measurement-lab.org",
164282
wantErr: true,
@@ -169,7 +287,6 @@ func Test_srvannotator_load(t *testing.T) {
169287
g := &siteAnnotator{
170288
siteinfoSource: tt.provider,
171289
hostname: tt.hostname,
172-
localIPs: tt.localIPs,
173290
}
174291
ctx := context.Background()
175292
an, err := g.load(ctx)

0 commit comments

Comments
 (0)