75
75
PublicKey = flag .String ("verify.key" , "cf.pub" , "Public key path (PEM file)" )
76
76
77
77
CacheBin = flag .String ("cache" , "https://rpki.cloudflare.com/rpki.json" , "URL of the cached JSON data" )
78
+ Etag = flag .Bool ("etag" , true , "Enable Etag header" )
78
79
UserAgent = flag .String ("useragent" , fmt .Sprintf ("Cloudflare-%v (+https://github.com/cloudflare/gortr)" , AppVersion ), "User-Agent header" )
79
80
RefreshInterval = flag .Int ("refresh" , 600 , "Refresh interval in seconds" )
80
81
MaxConn = flag .Int ("maxconn" , 0 , "Max simultaneous connections (0 to disable limit)" )
@@ -96,10 +97,24 @@ var (
96
97
LastRefresh = prometheus .NewGaugeVec (
97
98
prometheus.GaugeOpts {
98
99
Name : "rpki_refresh" ,
99
- Help : "Last refresh ." ,
100
+ Help : "Last successfull request for the given URL ." ,
100
101
},
101
102
[]string {"path" },
102
103
)
104
+ LastChange = prometheus .NewGaugeVec (
105
+ prometheus.GaugeOpts {
106
+ Name : "rpki_change" ,
107
+ Help : "Last change." ,
108
+ },
109
+ []string {"path" },
110
+ )
111
+ RefreshStatusCode = prometheus .NewCounterVec (
112
+ prometheus.CounterOpts {
113
+ Name : "refresh_requests_total" ,
114
+ Help : "Total number of HTTP requests by status code" ,
115
+ },
116
+ []string {"path" , "code" },
117
+ )
103
118
ClientsMetric = prometheus .NewGaugeVec (
104
119
prometheus.GaugeOpts {
105
120
Name : "rtr_clients" ,
@@ -128,7 +143,9 @@ var (
128
143
129
144
func initMetrics () {
130
145
prometheus .MustRegister (NumberOfROAs )
146
+ prometheus .MustRegister (LastChange )
131
147
prometheus .MustRegister (LastRefresh )
148
+ prometheus .MustRegister (RefreshStatusCode )
132
149
prometheus .MustRegister (ClientsMetric )
133
150
prometheus .MustRegister (PDUsRecv )
134
151
}
@@ -138,7 +155,7 @@ func metricHTTP() {
138
155
log .Fatal (http .ListenAndServe (* MetricsAddr , nil ))
139
156
}
140
157
141
- func fetchFile ( file string , ua string ) ([]byte , error ) {
158
+ func ( s * state ) fetchFile ( file string ) ([]byte , error ) {
142
159
var f io.Reader
143
160
var err error
144
161
if len (file ) > 8 && (file [0 :7 ] == "http://" || file [0 :8 ] == "https://" ) {
@@ -159,13 +176,18 @@ func fetchFile(file string, ua string) ([]byte, error) {
159
176
ProxyConnectHeader : map [string ][]string {},
160
177
}
161
178
// Keep User-Agent in proxy request
162
- tr .ProxyConnectHeader .Set ("User-Agent" , ua )
179
+ tr .ProxyConnectHeader .Set ("User-Agent" , s . userAgent )
163
180
164
181
client := & http.Client {Transport : tr }
165
182
req , err := http .NewRequest ("GET" , file , nil )
166
- req .Header .Set ("User-Agent" , ua )
183
+ req .Header .Set ("User-Agent" , s . userAgent )
167
184
req .Header .Set ("Accept" , "text/json" )
168
185
186
+ etag , ok := s .etags [file ]
187
+ if s .enableEtags && ok {
188
+ req .Header .Set ("If-None-Match" , etag )
189
+ }
190
+
169
191
proxyurl , err := http .ProxyFromEnvironment (req )
170
192
if err != nil {
171
193
return nil , err
@@ -181,16 +203,41 @@ func fetchFile(file string, ua string) ([]byte, error) {
181
203
if err != nil {
182
204
return nil , err
183
205
}
206
+
207
+ RefreshStatusCode .WithLabelValues (file , fmt .Sprintf ("%d" , fhttp .StatusCode )).Inc ()
208
+
209
+ if fhttp .StatusCode == 304 {
210
+ LastRefresh .WithLabelValues (file ).Set (float64 (s .lastts .UnixNano () / 1e9 ))
211
+ return nil , HttpNotModified {
212
+ File : file ,
213
+ }
214
+ } else if fhttp .StatusCode != 200 {
215
+ delete (s .etags , file )
216
+ return nil , fmt .Errorf ("HTTP %s" , fhttp .Status )
217
+ }
218
+ LastRefresh .WithLabelValues (file ).Set (float64 (s .lastts .UnixNano () / 1e9 ))
219
+
184
220
f = fhttp .Body
221
+
222
+ newEtag := fhttp .Header .Get ("ETag" )
223
+
224
+ if ! s .enableEtags || newEtag == "" || newEtag != s .etags [file ] {
225
+ s .etags [file ] = newEtag
226
+ } else {
227
+ return nil , IdenticalEtag {
228
+ File : file ,
229
+ Etag : newEtag ,
230
+ }
231
+ }
185
232
} else {
186
233
f , err = os .Open (file )
187
234
if err != nil {
188
235
return nil , err
189
236
}
190
237
}
191
- data , err2 := ioutil .ReadAll (f )
192
- if err2 != nil {
193
- return nil , err2
238
+ data , err := ioutil .ReadAll (f )
239
+ if err != nil {
240
+ return nil , err
194
241
}
195
242
return data , nil
196
243
}
@@ -236,7 +283,7 @@ func processData(roalistjson []prefixfile.ROAJson) ([]rtr.ROA, int, int, int) {
236
283
countv6 ++
237
284
}
238
285
239
- key := fmt .Sprintf ("%v,%v,%v " , prefix , asn , v .Length )
286
+ key := fmt .Sprintf ("%s,%d,%d " , prefix , asn , v .Length )
240
287
_ , exists := filterDuplicates [key ]
241
288
if ! exists {
242
289
filterDuplicates [key ] = true
@@ -259,14 +306,32 @@ type IdenticalFile struct {
259
306
}
260
307
261
308
func (e IdenticalFile ) Error () string {
262
- return fmt .Sprintf ("File %v is identical to the previous version" , e .File )
309
+ return fmt .Sprintf ("File %s is identical to the previous version" , e .File )
310
+ }
311
+
312
+ type HttpNotModified struct {
313
+ File string
314
+ }
315
+
316
+ func (e HttpNotModified ) Error () string {
317
+ return fmt .Sprintf ("HTTP 304 Not modified for %s" , e .File )
318
+ }
319
+
320
+ type IdenticalEtag struct {
321
+ File string
322
+ Etag string
323
+ }
324
+
325
+ func (e IdenticalEtag ) Error () string {
326
+ return fmt .Sprintf ("File %s is identical according to Etag: %s" , e .File , e .Etag )
263
327
}
264
328
265
329
func (s * state ) updateFile (file string ) error {
266
- log .Debugf ("Refreshing cache from %v" , file )
267
- data , err := fetchFile (file , s .userAgent )
330
+ log .Debugf ("Refreshing cache from %s" , file )
331
+
332
+ s .lastts = time .Now ().UTC ()
333
+ data , err := s .fetchFile (file )
268
334
if err != nil {
269
- log .Error (err )
270
335
return err
271
336
}
272
337
hsum , _ := checkFile (data )
@@ -277,7 +342,7 @@ func (s *state) updateFile(file string) error {
277
342
}
278
343
}
279
344
280
- s .lastts = time .Now ().UTC ()
345
+ s .lastchange = time .Now ().UTC ()
281
346
s .lastdata = data
282
347
283
348
roalistjson , err := decodeJSON (s .lastdata )
@@ -291,7 +356,6 @@ func (s *state) updateFile(file string) error {
291
356
return errors .New (fmt .Sprintf ("File is expired: %v" , validtime ))
292
357
}
293
358
}
294
-
295
359
if s .verify {
296
360
log .Debugf ("Verifying signature in %v" , file )
297
361
if roalistjson .Metadata .SignatureDate == "" || roalistjson .Metadata .Signature == "" {
@@ -342,6 +406,7 @@ func (s *state) updateFile(file string) error {
342
406
if err != nil {
343
407
return err
344
408
}
409
+
345
410
log .Infof ("New update (%v uniques, %v total prefixes). %v bytes. Updating sha256 hash %x -> %x" ,
346
411
len (roas ), count , len (s .lastconverted ), s .lasthash , hsum )
347
412
s .lasthash = hsum
@@ -366,16 +431,15 @@ func (s *state) updateFile(file string) error {
366
431
countv6_dup ++
367
432
}
368
433
}
369
- s .metricsEvent .UpdateMetrics (countv4 , countv6 , countv4_dup , countv6_dup , s .lastts , file )
434
+ s .metricsEvent .UpdateMetrics (countv4 , countv6 , countv4_dup , countv6_dup , s .lastchange , s . lastts , file )
370
435
}
371
436
return nil
372
437
}
373
438
374
439
func (s * state ) updateSlurm (file string ) error {
375
440
log .Debugf ("Refreshing slurm from %v" , file )
376
- data , err := fetchFile (file , s . userAgent )
441
+ data , err := s . fetchFile (file )
377
442
if err != nil {
378
- log .Error (err )
379
443
return err
380
444
}
381
445
@@ -404,12 +468,23 @@ func (s *state) routineUpdate(file string, interval int, slurmFile string) {
404
468
if slurmFile != "" {
405
469
err := s .updateSlurm (slurmFile )
406
470
if err != nil {
407
- log .Errorf ("Slurm: %v" , err )
471
+ switch err .(type ) {
472
+ case HttpNotModified :
473
+ log .Info (err )
474
+ case IdenticalEtag :
475
+ log .Info (err )
476
+ default :
477
+ log .Errorf ("Slurm: %v" , err )
478
+ }
408
479
}
409
480
}
410
481
err := s .updateFile (file )
411
482
if err != nil {
412
483
switch err .(type ) {
484
+ case HttpNotModified :
485
+ log .Info (err )
486
+ case IdenticalEtag :
487
+ log .Info (err )
413
488
case IdenticalFile :
414
489
log .Info (err )
415
490
default :
@@ -431,9 +506,12 @@ type state struct {
431
506
lastdata []byte
432
507
lastconverted []byte
433
508
lasthash []byte
509
+ lastchange time.Time
434
510
lastts time.Time
435
511
sendNotifs bool
436
512
userAgent string
513
+ etags map [string ]string
514
+ enableEtags bool
437
515
438
516
server * rtr.Server
439
517
@@ -471,12 +549,12 @@ func (m *metricsEvent) HandlePDU(c *rtr.Client, pdu rtr.PDU) {
471
549
"_" , - 1 ))).Inc ()
472
550
}
473
551
474
- func (m * metricsEvent ) UpdateMetrics (numIPv4 int , numIPv6 int , numIPv4filtered int , numIPv6filtered int , refreshed time.Time , file string ) {
552
+ func (m * metricsEvent ) UpdateMetrics (numIPv4 int , numIPv6 int , numIPv4filtered int , numIPv6filtered int , changed time. Time , refreshed time.Time , file string ) {
475
553
NumberOfROAs .WithLabelValues ("ipv4" , "filtered" , file ).Set (float64 (numIPv4filtered ))
476
554
NumberOfROAs .WithLabelValues ("ipv4" , "unfiltered" , file ).Set (float64 (numIPv4 ))
477
555
NumberOfROAs .WithLabelValues ("ipv6" , "filtered" , file ).Set (float64 (numIPv6filtered ))
478
556
NumberOfROAs .WithLabelValues ("ipv6" , "unfiltered" , file ).Set (float64 (numIPv6 ))
479
- LastRefresh .WithLabelValues (file ).Set (float64 (refreshed .UnixNano () / 1e9 ))
557
+ LastChange .WithLabelValues (file ).Set (float64 (changed .UnixNano () / 1e9 ))
480
558
}
481
559
482
560
func ReadPublicKey (key []byte , isPem bool ) (* ecdsa.PublicKey , error ) {
@@ -563,6 +641,8 @@ func main() {
563
641
verify : * Verify ,
564
642
checktime : * TimeCheck ,
565
643
userAgent : * UserAgent ,
644
+ etags : make (map [string ]string ),
645
+ enableEtags : * Etag ,
566
646
lockJson : & sync.RWMutex {},
567
647
}
568
648
@@ -708,7 +788,14 @@ func main() {
708
788
if slurmFile != "" {
709
789
err := s .updateSlurm (slurmFile )
710
790
if err != nil {
711
- log .Errorf ("Slurm: %v" , err )
791
+ switch err .(type ) {
792
+ case HttpNotModified :
793
+ log .Info (err )
794
+ case IdenticalEtag :
795
+ log .Info (err )
796
+ default :
797
+ log .Errorf ("Slurm: %v" , err )
798
+ }
712
799
}
713
800
if ! * SlurmRefresh {
714
801
slurmFile = ""
@@ -718,8 +805,12 @@ func main() {
718
805
err := s .updateFile (* CacheBin )
719
806
if err != nil {
720
807
switch err .(type ) {
808
+ case HttpNotModified :
809
+ log .Info (err )
721
810
case IdenticalFile :
722
811
log .Info (err )
812
+ case IdenticalEtag :
813
+ log .Info (err )
723
814
default :
724
815
log .Errorf ("Error updating: %v" , err )
725
816
}
0 commit comments