2019-08-15 11:26:14 +02:00
import axios from "axios" ;
import brembo from "brembo" ;
import yaml from "js-yaml" ;
import fs from "fs" ;
2019-09-14 21:00:20 +02:00
const batchPromises = require ( 'batch-promises' ) ;
2019-08-15 11:26:14 +02:00
2019-09-15 20:40:51 +02:00
module . exports = function generatePrefixes ( asns , outputFile ) {
const generateList = { } ;
let someNotValidatedPrefixes = false ;
2019-08-15 11:26:14 +02:00
2019-09-15 20:40:51 +02:00
if ( ! asns ) {
throw new Error ( "One or more comma-separated AS numbers have to be specified" ) ;
}
2019-08-15 11:26:14 +02:00
2019-09-15 20:40:51 +02:00
if ( ! outputFile ) {
throw new Error ( "Output file not specified" ) ;
}
2019-08-15 11:26:14 +02:00
2019-09-15 20:40:51 +02:00
const asnList = asns . split ( "," ) ;
2019-08-15 11:26:14 +02:00
2019-09-15 20:40:51 +02:00
const getAnnouncedMoreSpecifics = ( prefix ) => {
const url = brembo . build ( "https://stat.ripe.net" , {
path : [ "data" , "related-prefixes" , "data.json" ] ,
params : {
resource : prefix
2019-09-14 21:00:20 +02:00
}
2019-09-15 20:40:51 +02:00
} ) ;
2019-09-14 21:00:20 +02:00
2019-09-15 20:40:51 +02:00
return axios ( {
url ,
method : 'GET' ,
responseType : 'json'
2019-09-14 21:00:20 +02:00
} )
2019-09-15 20:40:51 +02:00
. then ( data => {
let prefixes = [ ] ;
if ( data . data && data . data . data && data . data . data . prefixes ) {
prefixes = data . data . data . prefixes
. filter ( i => i . relationship === "Overlap - More Specific" )
. map ( i => {
return {
asn : i . origin _asn ,
prefix : i . prefix
}
} ) ;
}
2019-09-14 21:00:20 +02:00
2019-09-15 20:40:51 +02:00
return prefixes ;
} )
2019-09-14 21:00:20 +02:00
2019-09-15 20:40:51 +02:00
} ;
2019-09-14 21:00:20 +02:00
2019-08-15 11:26:14 +02:00
2019-09-15 20:40:51 +02:00
const generateRule = ( asn , ignoreMorespecifics ) => {
return {
description : "No description provided" ,
asn : parseInt ( asn ) ,
ignoreMorespecifics
2019-08-15 11:26:14 +02:00
}
2019-09-15 20:40:51 +02:00
} ;
2019-08-15 11:26:14 +02:00
2019-09-15 20:40:51 +02:00
const getAnnouncedPrefixes = ( asn ) => {
const url = brembo . build ( "https://stat.ripe.net" , {
path : [ "data" , "announced-prefixes" , "data.json" ] ,
params : {
resource : asn
2019-08-15 11:26:14 +02:00
}
2019-09-15 20:40:51 +02:00
} ) ;
2019-09-14 21:00:20 +02:00
2019-09-15 20:40:51 +02:00
return axios ( {
url ,
method : 'GET' ,
responseType : 'json'
2019-08-15 11:26:14 +02:00
} )
2019-09-15 20:40:51 +02:00
. then ( data => {
if ( data . data && data . data . data && data . data . data . prefixes ) {
return data . data . data . prefixes
. filter ( item => {
const latest = item . timelines
. map ( t => ( t . endtime ) ? new Date ( t . endtime ) : new Date ( ) )
. sort ( ( a , b ) => a - b )
. pop ( ) ;
return latest . getTime ( ) + ( 3600 * 24 * 1000 ) > new Date ( ) . getTime ( ) ;
} )
}
return [ ] ;
} )
. then ( list => list
. map ( i => {
generateList [ i . prefix ] = generateRule ( asn , false ) ;
return i . prefix ;
} ) )
. then ( prefixes => {
return Promise . all ( prefixes . map ( prefix => validatePrefix ( asn , prefix ) ) )
. then ( ( ) => prefixes ) ;
} )
. then ( prefixes => {
prefixes = prefixes . filter ( i => ! generateList [ i ] . prefix ) ;
return batchPromises ( 20 , prefixes , prefix =>
new Promise ( ( resolve , reject ) => {
resolve ( getAnnouncedMoreSpecifics ( prefix )
. then ( items => {
if ( items ) {
for ( let item of items ) {
generateList [ item . prefix ] = generateRule ( item . asn , true ) ;
}
}
} ) ) ;
} ) ) ;
} )
} ;
const validatePrefix = ( asn , prefix ) => {
const url = brembo . build ( "https://stat.ripe.net" , {
path : [ "data" , "rpki-validation" , "data.json" ] ,
params : {
resource : asn ,
prefix
2019-08-15 11:26:14 +02:00
}
2019-09-15 20:40:51 +02:00
} ) ;
2019-09-14 21:00:20 +02:00
2019-09-15 20:40:51 +02:00
return axios ( {
url ,
method : 'GET' ,
responseType : 'json'
2019-08-15 11:26:14 +02:00
} )
2019-09-15 20:40:51 +02:00
. then ( data => {
if ( data . data && data . data . data && data . data . data . validating _roas ) {
return data . data . data . validating _roas . map ( i => i . validity ) . some ( i => i === 'valid' ) ;
}
return false ;
} )
. then ( ( isValid ) => {
if ( isValid ) {
generateList [ prefix ] . description += ' (valid ROA available)' ;
} else {
someNotValidatedPrefixes = true ;
}
} )
} ;
return Promise
. all ( asnList . map ( getAnnouncedPrefixes ) )
. then ( ( data ) => {
const yamlContent = yaml . dump ( generateList ) ;
fs . writeFileSync ( outputFile , yamlContent ) ;
if ( someNotValidatedPrefixes ) {
console . log ( "WARNING: The generated configuration is a snapshot of what is currently announced by " + asns + " but some of the prefixes don't have ROA objects associated. Please, verify the config file by hand!" ) ;
}
console . log ( "Done!" ) ;
} ) ;
2019-08-15 11:26:14 +02:00
2019-09-15 20:40:51 +02:00
}