roaapi.go 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. //////////////////////////////////////////////////////////////////////////
  2. // DN42 Registry API Server
  3. //////////////////////////////////////////////////////////////////////////
  4. package main
  5. //////////////////////////////////////////////////////////////////////////
  6. import (
  7. "fmt"
  8. "github.com/gorilla/mux"
  9. log "github.com/sirupsen/logrus"
  10. // "math/big"
  11. "bufio"
  12. "net"
  13. "net/http"
  14. "os"
  15. "sort"
  16. "strconv"
  17. "strings"
  18. "time"
  19. )
  20. //////////////////////////////////////////////////////////////////////////
  21. // register the api
  22. func init() {
  23. EventBus.Listen("APIEndpoint", InitROAAPI)
  24. EventBus.Listen("RegistryUpdate", ROAUpdate)
  25. }
  26. //////////////////////////////////////////////////////////////////////////
  27. // data model
  28. type PrefixROA struct {
  29. Prefix string `json:"prefix"`
  30. MaxLen uint8 `json:"maxLength"`
  31. ASN string `json:"asn"`
  32. }
  33. type ROAFilter struct {
  34. Number uint
  35. Action string
  36. Prefix string
  37. MinLen uint8
  38. MaxLen uint8
  39. Network *net.IPNet
  40. }
  41. type ROA struct {
  42. CTime time.Time
  43. Commit string
  44. Filters []*ROAFilter
  45. IPv4 []*PrefixROA
  46. IPv6 []*PrefixROA
  47. }
  48. var ROAData *ROA
  49. // set validity period for one week
  50. // this might appear to be a long time, but is intended to provide
  51. // enough time to prevent expiry of the data between real registry
  52. // updates (which may only happen infrequently)
  53. const ROA_JSON_VALIDITY_PERIOD = (7 * 24)
  54. type ROAMetaData struct {
  55. Counts uint `json:"counts"`
  56. Generated uint32 `json:"generated"`
  57. Valid uint32 `json:"valid"`
  58. Signature string `json:"signature,omitempty"`
  59. SignatureDate string `json:"signatureDate,omitempty"`
  60. }
  61. type ROAJSON struct {
  62. MetaData ROAMetaData `json:"metadata"`
  63. Roas []*PrefixROA `json:"roas"`
  64. }
  65. var ROAJSONResponse *ROAJSON
  66. //////////////////////////////////////////////////////////////////////////
  67. // called from main to initialise the API routing
  68. func InitROAAPI(params ...interface{}) {
  69. router := params[0].(*mux.Router)
  70. s := router.
  71. Methods("GET").
  72. PathPrefix("/roa").
  73. Subrouter()
  74. s.HandleFunc("/json", roaJSONHandler)
  75. s.HandleFunc("/bird/{birdv}/{ipv}", roaBirdHandler)
  76. log.Info("ROA API installed")
  77. }
  78. //////////////////////////////////////////////////////////////////////////
  79. // api handlers
  80. // return JSON formatted ROA data suitable for use with GoRTR
  81. func roaJSONHandler(w http.ResponseWriter, r *http.Request) {
  82. // check validity period of returned data
  83. tnow := uint32(time.Now().Unix())
  84. valid := ROAJSONResponse.MetaData.Valid
  85. // check if validity period is close to expiry
  86. if (tnow > valid) ||
  87. ((valid - tnow) < (ROA_JSON_VALIDITY_PERIOD / 4)) {
  88. // if so extend the validity period
  89. ROAJSONResponse.MetaData.Valid += (ROA_JSON_VALIDITY_PERIOD * 3600)
  90. }
  91. ResponseJSON(w, ROAJSONResponse)
  92. }
  93. // return the roa in bird format
  94. func roaBirdHandler(w http.ResponseWriter, r *http.Request) {
  95. vars := mux.Vars(r)
  96. birdv := vars["birdv"]
  97. ipv := vars["ipv"]
  98. // bird 1 or bird 2 format
  99. birdf := "roa %s max %d as %s;\n"
  100. if birdv == "2" {
  101. birdf = "route %s max %d as %s;\n"
  102. }
  103. var roa []*PrefixROA
  104. if strings.ContainsRune(ipv, '4') {
  105. roa = append(roa, ROAData.IPv4...)
  106. }
  107. if strings.ContainsRune(ipv, '6') {
  108. roa = append(roa, ROAData.IPv6...)
  109. }
  110. w.Header().Set("Content-Type", "text/plain")
  111. w.Header().Set("Access-Control-Allow-Origin", "*")
  112. fmt.Fprintf(w, "#\n# dn42regsrv ROA Generator\n# Last Updated: %s\n"+
  113. "# Commit: %s\n#\n", ROAData.CTime.String(), ROAData.Commit)
  114. for _, r := range roa {
  115. fmt.Fprintf(w, birdf, r.Prefix, r.MaxLen, r.ASN[2:])
  116. }
  117. }
  118. //////////////////////////////////////////////////////////////////////////
  119. // called whenever the registry is updated
  120. func ROAUpdate(params ...interface{}) {
  121. registry := params[0].(*Registry)
  122. path := params[1].(string)
  123. // initiate new ROA data
  124. roa := &ROA{
  125. CTime: time.Now(),
  126. Commit: registry.Commit,
  127. }
  128. // load filter{,6}.txt files
  129. if roa.loadFilter(path+"/filter.txt") != nil {
  130. // error loading IPv4 filter, don't update
  131. return
  132. }
  133. if roa.loadFilter(path+"/filter6.txt") != nil {
  134. // error loading IPv6 filter, don't update
  135. return
  136. }
  137. // compile ROA prefixes
  138. roa.IPv4 = roa.CompileROA(registry, "route")
  139. roa.IPv6 = roa.CompileROA(registry, "route6")
  140. // swap in the new data
  141. ROAData = roa
  142. log.WithFields(log.Fields{
  143. "ipv4": len(roa.IPv4),
  144. "ipv6": len(roa.IPv6),
  145. }).Debug("ROA data updated")
  146. // pre-compute the JSON return struct
  147. utime := uint32(roa.CTime.Unix())
  148. response := &ROAJSON{
  149. MetaData: ROAMetaData{
  150. Generated: utime,
  151. Valid: utime + (ROA_JSON_VALIDITY_PERIOD * 3600),
  152. },
  153. }
  154. response.Roas = append(roa.IPv4, roa.IPv6...)
  155. response.MetaData.Counts = uint(len(response.Roas))
  156. ROAJSONResponse = response
  157. }
  158. //////////////////////////////////////////////////////////////////////////
  159. // load network filter definitions from a filter file
  160. func (roa *ROA) loadFilter(path string) error {
  161. // open the file for reading
  162. file, err := os.Open(path)
  163. if err != nil {
  164. log.WithFields(log.Fields{
  165. "path": path,
  166. "error": err,
  167. }).Error("Unable to open filter file")
  168. return err
  169. }
  170. defer file.Close()
  171. // helper closure to convert strings to numbers
  172. var cerr error
  173. convert := func(s string) int {
  174. if cerr != nil {
  175. return 0
  176. }
  177. val, cerr := strconv.Atoi(s)
  178. if cerr != nil {
  179. log.WithFields(log.Fields{
  180. "number": s,
  181. "error": err,
  182. }).Error("Unable to parse number in filter file")
  183. return 0
  184. }
  185. return val
  186. }
  187. filters := make([]*ROAFilter, 0)
  188. // read the file line by line
  189. scanner := bufio.NewScanner(file)
  190. for scanner.Scan() {
  191. line := strings.TrimSpace(scanner.Text())
  192. // remove any comments
  193. if ix := strings.IndexRune(line, '#'); ix != -1 {
  194. line = line[:ix]
  195. }
  196. fields := strings.Fields(line)
  197. if len(fields) >= 5 {
  198. // parse the prefix in to a NetIP structure
  199. prefix := fields[2]
  200. _, network, err := net.ParseCIDR(prefix)
  201. if err != nil {
  202. log.WithFields(log.Fields{
  203. "path": path,
  204. "prefix": prefix,
  205. "error": err,
  206. }).Error("Unable to parse CIDR in filter file")
  207. } else {
  208. // construct the filter object
  209. roaf := &ROAFilter{
  210. Number: uint(convert(fields[0])),
  211. Action: fields[1],
  212. Prefix: prefix,
  213. MinLen: uint8(convert(fields[3])),
  214. MaxLen: uint8(convert(fields[4])),
  215. Network: network,
  216. }
  217. // add to list if no strconv error
  218. if cerr == nil {
  219. filters = append(filters, roaf)
  220. }
  221. }
  222. }
  223. }
  224. // did something go wrong ?
  225. if err := scanner.Err(); err != nil {
  226. log.WithFields(log.Fields{
  227. "path": path,
  228. "error": err,
  229. }).Error("Scanner error reading filter file")
  230. return err
  231. }
  232. // sort the filters based on prefix length (largest first)
  233. sort.Slice(filters, func(i, j int) bool {
  234. leni, _ := filters[i].Network.Mask.Size()
  235. lenj, _ := filters[j].Network.Mask.Size()
  236. return leni > lenj
  237. })
  238. // add to the roa object
  239. roa.Filters = append(roa.Filters, filters...)
  240. return nil
  241. }
  242. //////////////////////////////////////////////////////////////////////////
  243. // return the filter object that matches an IP address
  244. func (roa *ROA) MatchFilter(ip net.IP) *ROAFilter {
  245. for _, filter := range roa.Filters {
  246. if filter.Network.Contains(ip) {
  247. return filter
  248. }
  249. }
  250. log.WithFields(log.Fields{
  251. "IP": ip,
  252. }).Error("Couldn't match address to filter !")
  253. return nil
  254. }
  255. //////////////////////////////////////////////////////////////////////////
  256. // compile ROA data
  257. func (roa *ROA) CompileROA(registry *Registry,
  258. tname string) []*PrefixROA {
  259. // prepare indices to the route object keys
  260. stype := registry.Schema[tname]
  261. routeIX := stype.KeyIndex[tname]
  262. originIX := stype.KeyIndex["origin"]
  263. mlenIX := stype.KeyIndex["max-length"]
  264. roalist := make([]*PrefixROA, 0, len(routeIX.Objects))
  265. // for each object that has a route key
  266. for object, rattribs := range routeIX.Objects {
  267. if len(rattribs) > 1 {
  268. log.WithFields(log.Fields{
  269. "object": object.Ref,
  270. }).Warn("Found object with multiple route attributes")
  271. }
  272. // extract the prefix
  273. prefix := rattribs[0].RawValue
  274. _, pnet, err := net.ParseCIDR(prefix)
  275. if err != nil {
  276. log.WithFields(log.Fields{
  277. "object": object.Ref,
  278. "prefix": prefix,
  279. "error": err,
  280. }).Error("Unable to parse CIDR in ROA")
  281. continue
  282. }
  283. // match the prefix to the prefix filters
  284. filter := roa.MatchFilter(pnet.IP)
  285. if filter == nil {
  286. continue
  287. }
  288. // don't allow routes that are denied in the filter rules
  289. if filter.Action == "deny" {
  290. log.WithFields(log.Fields{
  291. "object": object.Ref,
  292. "prefix": prefix,
  293. "filter": filter.Prefix,
  294. }).Warn("Denied ROA through filter rule")
  295. continue
  296. }
  297. mlen := filter.MaxLen
  298. // if the prefix is greater than the filter.MaxLen
  299. // then don't emit an ROA route (making the route invalid)
  300. if ones, _ := pnet.Mask.Size(); ones > int(mlen) {
  301. log.WithFields(log.Fields{
  302. "object": object.Ref,
  303. "prefix": prefix,
  304. "filter": filter.Prefix,
  305. }).Debug("Defined ROA: Prefix > filter MaxLen")
  306. continue
  307. }
  308. // calculate the max-length for this object
  309. // check if the attribute has max-length defined
  310. mattrib := mlenIX.Objects[object]
  311. if mattrib != nil {
  312. // use the local max-length value
  313. tmp, err := strconv.ParseUint(mattrib[0].RawValue, 10, 8)
  314. if err != nil {
  315. log.WithFields(log.Fields{
  316. "object": object.Ref,
  317. "max-length": mattrib[0].RawValue,
  318. "error": err,
  319. }).Warn("Unable to convert max-length attribute")
  320. } else {
  321. // filter rules still have precedence over local values
  322. if (uint8(tmp) < mlen) && (uint8(tmp) > filter.MinLen) {
  323. mlen = uint8(tmp)
  324. }
  325. }
  326. }
  327. // look up the origin key for this object
  328. oattribs := originIX.Objects[object]
  329. if oattribs == nil {
  330. log.WithFields(log.Fields{
  331. "object": object.Ref,
  332. }).Warn("Route Object without Origin")
  333. } else {
  334. // then for origin that can announce this prefix
  335. for _, oattrib := range oattribs {
  336. // add the ROA
  337. roalist = append(roalist, &PrefixROA{
  338. Prefix: prefix,
  339. MaxLen: mlen,
  340. ASN: oattrib.RawValue,
  341. })
  342. }
  343. }
  344. }
  345. return roalist
  346. }
  347. //////////////////////////////////////////////////////////////////////////
  348. // end of code