Compare commits
No commits in common. "9784d4dc7cb42a4aa24bc05ca7482d9c2fce7191" and "181d8550338346f678922eca8ec770cec4a122b5" have entirely different histories.
9784d4dc7c
...
181d855033
|
@ -4,4 +4,3 @@
|
|||
.*.swo
|
||||
db.sqlite3
|
||||
dnmapper/settings.py
|
||||
__pycache__
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
|
@ -1,5 +0,0 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class Apiv2Config(AppConfig):
|
||||
name = 'apiv2'
|
|
@ -1,3 +0,0 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
|
@ -1,32 +0,0 @@
|
|||
import json
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from bgpdata.models import CrawlRun, ASLastSeen
|
||||
|
||||
|
||||
class CrawlRunSerializer(serializers.ModelSerializer):
|
||||
graph = serializers.ReadOnlyField()
|
||||
|
||||
class Meta:
|
||||
model = CrawlRun
|
||||
fields = ('id', 'startTime', 'endTime', 'asCount', 'asOnlineCount', 'asOfflineCount', 'peeringCount', 'graph')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
if "with_graph" not in self.context['request'].query_params:
|
||||
self.fields.pop("graph")
|
||||
|
||||
def to_representation(self, instance):
|
||||
data = super().to_representation(instance)
|
||||
for elem in data:
|
||||
if "graph" in data and isinstance(data['graph'], str):
|
||||
data['graph'] = json.loads(data['graph'])
|
||||
return data
|
||||
|
||||
|
||||
class ASLastSeenSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = ASLastSeen
|
||||
fields = ('id', 'asn', 'directlyCrawled', 'online', 'lastSeen', 'crawlLastSeen')
|
|
@ -1,3 +0,0 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -1,12 +0,0 @@
|
|||
from django.conf.urls import include, url
|
||||
from rest_framework import routers
|
||||
|
||||
from apiv2.views import CrawlRunViewSet, ASLastSeenViewSet
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
router.register('crawlrun', CrawlRunViewSet)
|
||||
router.register('asn', ASLastSeenViewSet)
|
||||
|
||||
urlpatterns = [
|
||||
url('', include(router.urls)),
|
||||
]
|
|
@ -1,30 +0,0 @@
|
|||
from rest_framework import viewsets
|
||||
|
||||
from apiv2.serializers import CrawlRunSerializer, ASLastSeenSerializer
|
||||
from backend import crawler
|
||||
from bgpdata.models import CrawlRun, ASLastSeen
|
||||
|
||||
|
||||
class CrawlRunViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
"""Represents a CrawlRun.
|
||||
|
||||
Graph is shown if with_graph is passed as query arg.
|
||||
With /live/ the current network is shown (internally, a crawl is triggered for each request)"""
|
||||
queryset = CrawlRun.objects.all()
|
||||
serializer_class = CrawlRunSerializer
|
||||
|
||||
def get_object(self):
|
||||
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
|
||||
if self.kwargs.get(lookup_url_kwarg) == "live":
|
||||
net = crawler.get_current_network()
|
||||
obj = crawler.make_crawl_from_net(net)
|
||||
self.check_object_permissions(self.request, obj)
|
||||
else:
|
||||
obj = super().get_object()
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
class ASLastSeenViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = ASLastSeen.objects.all()
|
||||
serializer_class = ASLastSeenSerializer
|
|
@ -1,354 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
# This file is part of dnmapper, an AS--level mapping tool
|
||||
# Licensed under GNU General Public License v3 or later
|
||||
# Written by Sebastian Lohff (seba@someserver.de)
|
||||
from __future__ import print_function
|
||||
|
||||
from collections import OrderedDict
|
||||
import re
|
||||
import socket
|
||||
|
||||
from backend.exceptions import RouterParserException
|
||||
|
||||
|
||||
def err(msg):
|
||||
raise RouterParserException(msg)
|
||||
|
||||
|
||||
def getBGPData(ip, asno):
|
||||
rawData = getDataFromHost(ip)
|
||||
if not rawData:
|
||||
err("Could not get data from host (empty response)")
|
||||
|
||||
router = parseBGPData(rawData, asno)
|
||||
|
||||
router["ip"] = ip
|
||||
|
||||
return router
|
||||
|
||||
|
||||
def getDataFromHost(ip):
|
||||
socket.setdefaulttimeout(5)
|
||||
x = socket.socket()
|
||||
x.connect((ip, 6556))
|
||||
f = x.makefile()
|
||||
data = f.read()
|
||||
x.close()
|
||||
|
||||
return data
|
||||
|
||||
def parseBGPData(raw, asno):
|
||||
d = re.search(r"(?:^|\n)<<<(quagga|bird)>>>\n(.*?)(?:$|<<<[^\n]+>>>)", raw, re.DOTALL)
|
||||
|
||||
if not d:
|
||||
err("Data not found in check mk output")
|
||||
|
||||
# mkify
|
||||
raw = d.group(2).split("\n")
|
||||
arr = filter(lambda _z: _z, map(lambda _y: filter(lambda _x: _x, re.split(r"\s+", _y)), raw))
|
||||
|
||||
# parse for bird/quagga
|
||||
result = None
|
||||
|
||||
if d.group(1) == "quagga":
|
||||
result = parseQuagga(arr, raw, asno)
|
||||
else:
|
||||
result = parseBird(arr, raw, asno)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def parseQuagga(data, raw, asno):
|
||||
status = _quaggaFindCommand(data, "show ip bgp sum")
|
||||
|
||||
if status[0][0:3] == ['IPv4', 'Unicast', 'Summary:']:
|
||||
del(status[0])
|
||||
|
||||
if status[0][0:3] != ['BGP', 'router', 'identifier']:
|
||||
print(status)
|
||||
err("Couldn't find router id in quagga output")
|
||||
|
||||
peers = _quaggaFindNeighbors(data)
|
||||
if asno and int(asno) != int(status[0][7]):
|
||||
err("AS number (%s) does not match as number from quagga (%s)" % (asno, status[0][7]))
|
||||
|
||||
routes = _quaggaFindRoutes(raw)
|
||||
|
||||
return {"local_id": status[0][3].strip(","), "local_as": int(status[0][7]), "peers": peers, "routes": routes}
|
||||
|
||||
def parseBird(data, raw, asno):
|
||||
status = _birdFindTable(data, "show status")
|
||||
if status[2][0] != "1011-Router":
|
||||
err("Couldn't find router id in bird output")
|
||||
peers = filter(lambda _x: _x["type"] == "BGP", _birdMakeProtocols(data))
|
||||
|
||||
if asno == None:
|
||||
err("Host is bird")
|
||||
# FIXME
|
||||
|
||||
routes = _birdFindRoutes(data)
|
||||
|
||||
return {"local_id": status[2][3], "local_as": int(asno), "peers": peers, "routes": routes}
|
||||
|
||||
def _birdFindTable(info, command):
|
||||
""" find command output of a bird command, e.g. "show bgp neighbors" """
|
||||
command = ["bird>"] + command.split(" ")
|
||||
commandInfo = []
|
||||
editNextLine = False
|
||||
for line in info:
|
||||
if not commandInfo:
|
||||
if line == command:
|
||||
commandInfo.append(line)
|
||||
editNextLine = True
|
||||
else:
|
||||
if editNextLine:
|
||||
editNextLine = False
|
||||
commandInfo.append(line[1:])
|
||||
elif line[0] == "bird>":
|
||||
return commandInfo
|
||||
else:
|
||||
commandInfo.append(line)
|
||||
return []
|
||||
|
||||
def _birdFindProtocols(info):
|
||||
""" return a list of tuples (protoname, protoinfo) """
|
||||
protocolTable = _birdFindTable(info, "show protocols all")
|
||||
protocols = OrderedDict()
|
||||
currProto = None
|
||||
for line in protocolTable[2:]:
|
||||
if line[0][0:4] == "1002":
|
||||
currProto = line[0][5:]
|
||||
protocols[currProto] = [[currProto] + line[1:]]
|
||||
elif currProto == None:
|
||||
err("No proto selected, couldn't parse line:", line)
|
||||
else:
|
||||
protocols[currProto].append(line)
|
||||
|
||||
return protocols
|
||||
|
||||
def _birdMakeProtocols(info):
|
||||
""" Parse birds show protocols all output """
|
||||
# proto: name, type, description, state (up/down?), up-since
|
||||
# routes imported, exported, preferred
|
||||
# also: routing stats (
|
||||
# bgp special stuff: state, neighbor (address, as, id) (id not available when down)
|
||||
# state (established, active)
|
||||
# if error, last error is avilable
|
||||
protocols = []
|
||||
for proto, data in _birdFindProtocols(info).iteritems():
|
||||
protoInfo = {
|
||||
"name": proto,
|
||||
"type": data[0][1],
|
||||
"table": data[0][2],
|
||||
"state": data[0][3],
|
||||
"last_change": data[0][4],
|
||||
"info": " ".join(data[0][5:]),
|
||||
"description": " ".join(data[1][2:]),
|
||||
"routes": {
|
||||
"imported": data[5][1],
|
||||
"exported": data[5][3],
|
||||
"preferred": data[5][5],
|
||||
}
|
||||
}
|
||||
if protoInfo["type"] == "BGP":
|
||||
found = False
|
||||
for n, line in enumerate(data):
|
||||
if line[0:2] == ["BGP", "state:"]:
|
||||
found = True
|
||||
protoInfo["BGP"] = {
|
||||
"state": data[n][2],
|
||||
"online": data[n][2] == "Established",
|
||||
"neighbor_address": data[n+1][2],
|
||||
"neighbor_as": int(data[n+2][2]),
|
||||
"neighbor_id": data[n+3][2] if len(data) > n+3 and data[n+3][0:2] == ["Neighbor", "ID:"] else None,
|
||||
"last_error": " ".join(data[n+3][2:]) if len(data) > n+3 and data[n+3][0:2] == ["Last", "error:"] else None,
|
||||
}
|
||||
|
||||
if not found:
|
||||
protoInfo["BGP"] = None
|
||||
|
||||
protocols.append(protoInfo)
|
||||
|
||||
return protocols
|
||||
|
||||
|
||||
def _birdFindRoutes(info):
|
||||
output = _birdFindTable(info, "show route all")
|
||||
if len(output) < 1:
|
||||
# no data found
|
||||
return None
|
||||
|
||||
def handleCandidate(routes, candidate):
|
||||
if candidate:
|
||||
# path, nexthop, network
|
||||
for key in ["path", "nexthop", "network", "iBGP"]:
|
||||
if key not in candidate:
|
||||
return
|
||||
route = {"prefix": candidate["network"], "nexthop": candidate["nexthop"],
|
||||
"path": list(map(int, candidate["path"])), "iBGP": candidate["iBGP"]}
|
||||
routes.append(route)
|
||||
|
||||
routes = []
|
||||
candidate = None
|
||||
lastIP = None
|
||||
for line in output:
|
||||
if line[0].startswith("1007-"):
|
||||
# new route!
|
||||
handleCandidate(routes, candidate)
|
||||
if line[0] != "1007-":
|
||||
# line has a network, use it!
|
||||
lastIP = line[0][5:]
|
||||
candidate = {"network": lastIP, "iBGP": None}
|
||||
|
||||
elif candidate is not None:
|
||||
# search bgp attributes
|
||||
if line[0] == "1012-":
|
||||
pass
|
||||
k, v = line[1], line[2:]
|
||||
else:
|
||||
k, v = line[0], line[1:]
|
||||
|
||||
k = k.rstrip(":")
|
||||
if k == "BGP.next_hop":
|
||||
candidate["nexthop"] = v[0]
|
||||
elif k == "BGP.as_path":
|
||||
candidate["path"] = v
|
||||
|
||||
|
||||
handleCandidate(routes, candidate)
|
||||
|
||||
return routes
|
||||
|
||||
|
||||
def _quaggaFindCommand(info, cmd):
|
||||
# ['core-frunde#', 'show', 'ip', 'bgp', 'sum']
|
||||
# ['core-frunde#', 'show', 'ip', 'bgp', 'neighbors']
|
||||
output = []
|
||||
cmd = cmd.split(" ")
|
||||
prompt = None
|
||||
for line in info:
|
||||
if line[1:] == cmd:
|
||||
prompt = line[0]
|
||||
elif line[0] == prompt:
|
||||
# done
|
||||
return output
|
||||
elif prompt != None:
|
||||
output.append(line)
|
||||
|
||||
err("Could not find command '%s' in output" % " ".join(cmd))
|
||||
|
||||
def _quaggaFindNeighbors(info):
|
||||
#['BGP', 'neighbor', 'is', '10.50.1.2,', 'remote', 'AS', '65001,', 'local', 'AS', '65001,', 'internal', 'link']
|
||||
output = _quaggaFindCommand(info, "show ip bgp neighbors")
|
||||
start = ["BGP", "neighbor", "is"]
|
||||
|
||||
curr = None
|
||||
rawNeighbors = []
|
||||
for line in output:
|
||||
if line[0:3] == start:
|
||||
if curr:
|
||||
rawNeighbors.append(curr)
|
||||
curr = [line]
|
||||
elif curr:
|
||||
curr.append(line)
|
||||
else:
|
||||
err("Could not find start of neighbors")
|
||||
|
||||
if curr:
|
||||
rawNeighbors.append(curr)
|
||||
curr = None
|
||||
|
||||
neighbors = []
|
||||
neighborDict = OrderedDict()
|
||||
for raw in rawNeighbors:
|
||||
descrIdx = 1 if raw[1][0] == "Description:" else 0
|
||||
if raw[descrIdx + 1][0] == "Hostname:":
|
||||
descrIdx += 1
|
||||
|
||||
peerdict = {
|
||||
"neighbor_address": raw[0][3].rstrip(","),
|
||||
"neighbor_as": int(raw[0][6].rstrip(",")),
|
||||
"local_as": int(raw[0][9].rstrip(",")),
|
||||
"description": " ".join(raw[1][1:]) if descrIdx else "No description",
|
||||
"neighbor_id": raw[1+descrIdx][6].strip(","),
|
||||
"state": raw[2+descrIdx][3].strip(","),
|
||||
"routes": {
|
||||
"imported": 0,
|
||||
},
|
||||
"BGP": {
|
||||
"state": raw[2+descrIdx][3].strip(","),
|
||||
"online": raw[2+descrIdx][3].strip(",") == "Established",
|
||||
"neighbor_id": raw[1+descrIdx][6].strip(","),
|
||||
"neighbor_address": raw[0][3].rstrip(","),
|
||||
"neighbor_as": int(raw[0][6].rstrip(",")),
|
||||
"state": raw[2+descrIdx][3].strip(","),
|
||||
},
|
||||
}
|
||||
|
||||
for line in raw:
|
||||
if line[1:3] == ["accepted", "prefixes"]:
|
||||
# woooo
|
||||
peerdict["routes"]["imported"] = int(line[0])
|
||||
break
|
||||
|
||||
neighbors.append(peerdict)
|
||||
neighborDict[peerdict["neighbor_address"]] = peerdict
|
||||
|
||||
return neighbors
|
||||
|
||||
def _quaggaFindRoutes(raw):
|
||||
# from # show ip bgp to Total number of prefixes XX
|
||||
# BGP table version is 0, local router ID is 10.50.0.1
|
||||
# *> 10.3.14.0/27 10.75.0.22 0 65002 65112 i
|
||||
cmdre = re.compile(r"^([^\s#]+#) show ip bgp$")
|
||||
routere = re.compile(r"^(?P<status>.)(?P<status2>.)(?P<origin>.)(?P<network>[0-9./]+)?\s+(?P<nexthop>[0-9./]+)[\s0-9i?]+$")
|
||||
|
||||
# find output
|
||||
output = []
|
||||
prompt = None
|
||||
for line in raw:
|
||||
if not prompt:
|
||||
m = cmdre.match(line)
|
||||
if m:
|
||||
prompt = m.group(1) + " "
|
||||
else:
|
||||
if line.startswith(prompt):
|
||||
break
|
||||
else:
|
||||
output.append(line)
|
||||
|
||||
if len(output) < 1:
|
||||
# no data found
|
||||
return None
|
||||
|
||||
routes = []
|
||||
foundTable = False
|
||||
lastIP = None
|
||||
for line in output:
|
||||
if not foundTable:
|
||||
if line.endswith("Metric LocPrf Weight Path"):
|
||||
foundTable = True
|
||||
else:
|
||||
if line != '':
|
||||
if line.startswith("Total number of prefixes") or line.startswith("Displayed "):
|
||||
break
|
||||
else:
|
||||
# parse one route line
|
||||
#print(line)
|
||||
m = routere.match(line)
|
||||
d = m.groupdict()
|
||||
if d["network"]:
|
||||
lastIP = d["network"]
|
||||
else:
|
||||
d["network"] = lastIP
|
||||
|
||||
# "parse" path (everything after 61 chars, but no i)
|
||||
path = filter(lambda _x: _x not in ('', 'i'), line[61:].split(" "))
|
||||
|
||||
# currently skip incomplete routes
|
||||
if '?' not in path:
|
||||
route = {"prefix": d["network"], "nexthop": d["nexthop"],
|
||||
"path": list(map(int, path)), "iBGP": d["origin"] == "i"}
|
||||
routes.append(route)
|
||||
|
||||
return routes
|
|
@ -1,199 +0,0 @@
|
|||
import json
|
||||
import logging
|
||||
import socket
|
||||
import time
|
||||
|
||||
from django.utils import timezone
|
||||
import networkx as nx
|
||||
|
||||
from backend import gobgp, cmk_parser
|
||||
from backend.exceptions import RouterParserException
|
||||
from bgpdata.models import ConfigHost, ASLastSeen, ASLastSeenNeighbor, CrawlRun
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
FORMAT = '%(asctime)-15s %(message)s'
|
||||
logging.basicConfig(level=logging.INFO, format=FORMAT)
|
||||
log.setLevel(logging.INFO)
|
||||
|
||||
|
||||
def crawl():
|
||||
net = get_current_network()
|
||||
crawl = make_crawl_from_net(net)
|
||||
crawl.save()
|
||||
|
||||
# handle last seen ASNs
|
||||
log.info("Updating last seen info")
|
||||
last_seen = {}
|
||||
for asls in ASLastSeen.objects.all():
|
||||
last_seen[asls.asn] = asls
|
||||
|
||||
for asn, node in net.nodes(data=True):
|
||||
if asn not in last_seen:
|
||||
last_seen[asn] = ASLastSeen(asn=asn)
|
||||
asls = last_seen[asn]
|
||||
asls.online = node['online']
|
||||
if node['online']:
|
||||
asls.directlyCrawled = node['directly_crawled']
|
||||
asls.lastSeen = timezone.now()
|
||||
asls.crawlLastSeen = crawl
|
||||
else:
|
||||
asls.directlyCrawled = False
|
||||
asls.save()
|
||||
|
||||
if asls.online:
|
||||
neighs = net.neighbors(asn)
|
||||
db_neighs = set()
|
||||
for db_neigh in asls.aslastseenneighbor_set.all():
|
||||
if db_neigh.asn in neighs:
|
||||
db_neighs.add(asls.asn)
|
||||
else:
|
||||
db_neigh.delete()
|
||||
for neigh in neighs:
|
||||
if neigh not in db_neighs:
|
||||
asneigh = ASLastSeenNeighbor(asn=neigh, neighbor=asls)
|
||||
asneigh.save()
|
||||
db_neighs.add(neigh)
|
||||
|
||||
crawl.endTime = timezone.now()
|
||||
crawl.save()
|
||||
|
||||
log.info("Automated crawl done")
|
||||
|
||||
|
||||
def make_crawl_from_net(net):
|
||||
"""Create a CrawlRun, but don't save it"""
|
||||
asCount = asOnlineCount = asOfflineCount = 0
|
||||
for asn, node in net.nodes(data=True):
|
||||
asCount += 1
|
||||
if node['online']:
|
||||
asOnlineCount += 1
|
||||
else:
|
||||
asOfflineCount += 1
|
||||
|
||||
crawl = CrawlRun()
|
||||
crawl.startTime = timezone.now()
|
||||
crawl.graph = net_to_json(net)
|
||||
crawl.asCount = asCount
|
||||
crawl.asOnlineCount = asOnlineCount
|
||||
crawl.asOfflineCount = asOfflineCount
|
||||
crawl.peeringCount = len(net.edges)
|
||||
|
||||
return crawl
|
||||
|
||||
|
||||
def get_current_network():
|
||||
net = nx.Graph()
|
||||
|
||||
crawl_start = time.time()
|
||||
log.info("Crawl run started")
|
||||
for host in ConfigHost.objects.all():
|
||||
try:
|
||||
if host.checkMethod == 'CMK':
|
||||
data = cmk_parser.getBGPData(host.ip, host.number)
|
||||
_add_data_to_net(net, data)
|
||||
elif host.checkMethod == 'GOBGP':
|
||||
for entry in gobgp.get_bgp_data(host.ip):
|
||||
_add_data_to_net(net, entry)
|
||||
except (RouterParserException, socket.error):
|
||||
log.exception("Could not get data from host %s method %s", host, host.checkMethod)
|
||||
continue
|
||||
|
||||
# add neighbor count
|
||||
for node, data in net.nodes(data=True):
|
||||
data["neighbors"] = len(list(net.neighbors(node)))
|
||||
|
||||
log.info("Adding last seen neighbor info")
|
||||
for asls in ASLastSeen.objects.all():
|
||||
if asls.asn not in net.nodes:
|
||||
if any(neigh.asn in net.nodes for neigh in asls.aslastseenneighbor_set.all()):
|
||||
_populate_node(net, asls.asn)
|
||||
net.nodes[asls.asn]['online'] = False
|
||||
for neigh in asls.aslastseenneighbor_set.all():
|
||||
if neigh.asn not in net.nodes:
|
||||
_populate_node(net, neigh.asn)
|
||||
net.nodes[asls.asn]['online'] = False
|
||||
|
||||
log.info("Crawl done in %.2fs", time.time() - crawl_start)
|
||||
|
||||
# add id to edges
|
||||
for n, (_, _, data) in enumerate(net.edges(data=True)):
|
||||
data['id'] = n
|
||||
|
||||
# import IPython
|
||||
# IPython.embed()
|
||||
|
||||
return net
|
||||
|
||||
|
||||
def net_to_json(net):
|
||||
"""Dum net to json, will replace all sets from the graph"""
|
||||
# replace all sets with lists for better dumpability
|
||||
for node in net.nodes.values():
|
||||
for key, val in node.items():
|
||||
if isinstance(val, set):
|
||||
node[key] = list(val)
|
||||
|
||||
return json.dumps(nx.readwrite.json_graph.node_link_data(net))
|
||||
|
||||
|
||||
def _populate_node(net, asn):
|
||||
net.add_node(asn)
|
||||
node = net.nodes[asn]
|
||||
node.setdefault("prefixes", set())
|
||||
node.setdefault("router_ids", set())
|
||||
node.setdefault("routing_table", set())
|
||||
node.setdefault("directly_crawled", False)
|
||||
node.setdefault("online", True)
|
||||
node.setdefault("stub", True)
|
||||
return node
|
||||
|
||||
|
||||
def _add_data_to_net(net, data):
|
||||
asn = data['local_as']
|
||||
as_node = _populate_node(net, asn)
|
||||
as_node['router_ids'].add(data['local_id'])
|
||||
as_node['directly_crawled'] = True
|
||||
|
||||
for peer in data['peers']:
|
||||
pass
|
||||
|
||||
for route in data['routes']:
|
||||
as_node['routing_table'].add((route['prefix'], tuple(route['path'])))
|
||||
|
||||
as_path = route['path']
|
||||
if not as_path:
|
||||
continue
|
||||
|
||||
orig_node = _populate_node(net, as_path[0])
|
||||
orig_node['prefixes'].add(route['prefix'])
|
||||
|
||||
for n in range(len(as_path) - 1):
|
||||
if as_path[n] != as_path[n + 1]:
|
||||
if as_path[n + 1] not in net.nodes:
|
||||
_populate_node(net, as_path[n + 1])
|
||||
if as_path[n + 1] not in (as_path[-1], as_path[0]):
|
||||
net.nodes[as_path[n + 1]]['stub'] = False
|
||||
net.add_edge(as_path[n], as_path[n + 1])
|
||||
|
||||
|
||||
def convert_crawl(crawl):
|
||||
net = nx.Graph()
|
||||
|
||||
for asn in crawl.as_set.all():
|
||||
if asn.number not in net.nodes:
|
||||
_populate_node(net, asn.number)
|
||||
d = net.nodes[asn.number]
|
||||
d['online'] = asn.online
|
||||
d['directly_crawled'] = asn.directlyCrawled
|
||||
for br in asn.borderrouter_set.all():
|
||||
d['router_ids'].add(br.routerID)
|
||||
|
||||
for asn in crawl.as_set.all():
|
||||
for peering in asn.getPeerings():
|
||||
net.add_edge(peering.as1.number, peering.as2.number)
|
||||
|
||||
for ann in asn.announcement_set.all():
|
||||
prefix = "{}/{}".format(ann.ip, ann.prefix)
|
||||
path = list(map(int, ann.ASPath.split()))
|
||||
net.nodes[asn.number]['routing_table'].add((prefix, tuple(path)))
|
||||
net.nodes[path[-1]]['prefixes'].add(prefix)
|
|
@ -1,2 +0,0 @@
|
|||
class RouterParserException(Exception):
|
||||
pass
|
|
@ -1,65 +0,0 @@
|
|||
import grpc
|
||||
|
||||
from backend.gobgp_api import gobgp_pb2_grpc, attribute_pb2
|
||||
from backend.gobgp_api.gobgp_pb2 import TableType, Family, ListPeerRequest, ListPathRequest
|
||||
|
||||
|
||||
def get_bgp_data(gobgp_host):
|
||||
_timeout = 10
|
||||
|
||||
channel = grpc.insecure_channel('{}:50051'.format(gobgp_host))
|
||||
stub = gobgp_pb2_grpc.GobgpApiStub(channel)
|
||||
data = []
|
||||
|
||||
peers = stub.ListPeer(ListPeerRequest(), _timeout)
|
||||
for peer in peers:
|
||||
entry = {
|
||||
"local_id": peer.peer.state.router_id,
|
||||
"local_as": peer.peer.conf.peer_as,
|
||||
"peers": [], # we don't export any peers
|
||||
"routes": [],
|
||||
}
|
||||
neigh = peer.peer.conf.neighbor_address
|
||||
|
||||
for af in [Family.Afi.AFI_IP, Family.Afi.AFI_IP6]:
|
||||
req = ListPathRequest(
|
||||
name=neigh,
|
||||
table_type=TableType.ADJ_IN,
|
||||
family=Family(afi=af, safi=Family.Safi.SAFI_UNICAST),
|
||||
sort_type=ListPathRequest.SortType.PREFIX)
|
||||
rib = stub.ListPath(req)
|
||||
for dest in rib:
|
||||
prefix = dest.destination.prefix
|
||||
for path in dest.destination.paths:
|
||||
as_path = []
|
||||
next_hop = "<unknown>" # currently here to prevent error from NOT NULL constraint
|
||||
|
||||
# parse attrs
|
||||
for pattr in path.pattrs:
|
||||
if pattr.type_url == "type.googleapis.com/gobgpapi.NextHopAttribute":
|
||||
nh = attribute_pb2.NextHopAttribute()
|
||||
nh.ParseFromString(pattr.value)
|
||||
next_hop = nh.next_hop
|
||||
elif pattr.type_url == "type.googleapis.com/gobgpapi.AsPathAttribute":
|
||||
asp = attribute_pb2.AsPathAttribute()
|
||||
asp.ParseFromString(pattr.value)
|
||||
for seg in asp.segments:
|
||||
if seg.type == 2:
|
||||
as_path = seg.numbers
|
||||
elif pattr.type_url == "type.googleapis.com/gobgpapi.MpReachNLRIAttribute":
|
||||
mpreach = attribute_pb2.MpReachNLRIAttribute()
|
||||
mpreach.ParseFromString(pattr.value)
|
||||
next_hop = mpreach.next_hops[0]
|
||||
|
||||
if not as_path:
|
||||
continue
|
||||
|
||||
route = {
|
||||
"prefix": prefix,
|
||||
"path": as_path,
|
||||
"nexthop": next_hop,
|
||||
}
|
||||
entry["routes"].append(route)
|
||||
|
||||
data.append(entry)
|
||||
return data
|
File diff suppressed because one or more lines are too long
|
@ -1,3 +0,0 @@
|
|||
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
||||
import grpc
|
||||
|
|
@ -1,692 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
# source: capability.proto
|
||||
|
||||
from google.protobuf.internal import enum_type_wrapper
|
||||
from google.protobuf import descriptor as _descriptor
|
||||
from google.protobuf import message as _message
|
||||
from google.protobuf import reflection as _reflection
|
||||
from google.protobuf import symbol_database as _symbol_database
|
||||
# @@protoc_insertion_point(imports)
|
||||
|
||||
_sym_db = _symbol_database.Default()
|
||||
|
||||
|
||||
import gobgp_pb2 as gobgp__pb2
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor.FileDescriptor(
|
||||
name='capability.proto',
|
||||
package='gobgpapi',
|
||||
syntax='proto3',
|
||||
serialized_options=None,
|
||||
serialized_pb=b'\n\x10\x63\x61pability.proto\x12\x08gobgpapi\x1a\x0bgobgp.proto\";\n\x17MultiProtocolCapability\x12 \n\x06\x66\x61mily\x18\x01 \x01(\x0b\x32\x10.gobgpapi.Family\"\x18\n\x16RouteRefreshCapability\"\x1d\n\x1b\x43\x61rryingLabelInfoCapability\"q\n\x1e\x45xtendedNexthopCapabilityTuple\x12%\n\x0bnlri_family\x18\x01 \x01(\x0b\x32\x10.gobgpapi.Family\x12(\n\x0enexthop_family\x18\x02 \x01(\x0b\x32\x10.gobgpapi.Family\"U\n\x19\x45xtendedNexthopCapability\x12\x38\n\x06tuples\x18\x01 \x03(\x0b\x32(.gobgpapi.ExtendedNexthopCapabilityTuple\"Q\n\x1eGracefulRestartCapabilityTuple\x12 \n\x06\x66\x61mily\x18\x01 \x01(\x0b\x32\x10.gobgpapi.Family\x12\r\n\x05\x66lags\x18\x02 \x01(\r\"r\n\x19GracefulRestartCapability\x12\r\n\x05\x66lags\x18\x01 \x01(\r\x12\x0c\n\x04time\x18\x02 \x01(\r\x12\x38\n\x06tuples\x18\x03 \x03(\x0b\x32(.gobgpapi.GracefulRestartCapabilityTuple\")\n\x1b\x46ourOctetASNumberCapability\x12\n\n\x02\x61s\x18\x01 \x01(\r\"_\n\x16\x41\x64\x64PathCapabilityTuple\x12 \n\x06\x66\x61mily\x18\x01 \x01(\x0b\x32\x10.gobgpapi.Family\x12#\n\x04mode\x18\x02 \x01(\x0e\x32\x15.gobgpapi.AddPathMode\"E\n\x11\x41\x64\x64PathCapability\x12\x30\n\x06tuples\x18\x01 \x03(\x0b\x32 .gobgpapi.AddPathCapabilityTuple\" \n\x1e\x45nhancedRouteRefreshCapability\"h\n\'LongLivedGracefulRestartCapabilityTuple\x12 \n\x06\x66\x61mily\x18\x01 \x01(\x0b\x32\x10.gobgpapi.Family\x12\r\n\x05\x66lags\x18\x02 \x01(\r\x12\x0c\n\x04time\x18\x03 \x01(\r\"g\n\"LongLivedGracefulRestartCapability\x12\x41\n\x06tuples\x18\x01 \x03(\x0b\x32\x31.gobgpapi.LongLivedGracefulRestartCapabilityTuple\"\x1d\n\x1bRouteRefreshCiscoCapability\"0\n\x11UnknownCapability\x12\x0c\n\x04\x63ode\x18\x01 \x01(\r\x12\r\n\x05value\x18\x02 \x01(\x0c*L\n\x0b\x41\x64\x64PathMode\x12\r\n\tMODE_NONE\x10\x00\x12\x10\n\x0cMODE_RECEIVE\x10\x01\x12\r\n\tMODE_SEND\x10\x02\x12\r\n\tMODE_BOTH\x10\x03\x62\x06proto3'
|
||||
,
|
||||
dependencies=[gobgp__pb2.DESCRIPTOR,])
|
||||
|
||||
_ADDPATHMODE = _descriptor.EnumDescriptor(
|
||||
name='AddPathMode',
|
||||
full_name='gobgpapi.AddPathMode',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
values=[
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='MODE_NONE', index=0, number=0,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='MODE_RECEIVE', index=1, number=1,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='MODE_SEND', index=2, number=2,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
_descriptor.EnumValueDescriptor(
|
||||
name='MODE_BOTH', index=3, number=3,
|
||||
serialized_options=None,
|
||||
type=None),
|
||||
],
|
||||
containing_type=None,
|
||||
serialized_options=None,
|
||||
serialized_start=1099,
|
||||
serialized_end=1175,
|
||||
)
|
||||
_sym_db.RegisterEnumDescriptor(_ADDPATHMODE)
|
||||
|
||||
AddPathMode = enum_type_wrapper.EnumTypeWrapper(_ADDPATHMODE)
|
||||
MODE_NONE = 0
|
||||
MODE_RECEIVE = 1
|
||||
MODE_SEND = 2
|
||||
MODE_BOTH = 3
|
||||
|
||||
|
||||
|
||||
_MULTIPROTOCOLCAPABILITY = _descriptor.Descriptor(
|
||||
name='MultiProtocolCapability',
|
||||
full_name='gobgpapi.MultiProtocolCapability',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='family', full_name='gobgpapi.MultiProtocolCapability.family', index=0,
|
||||
number=1, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=43,
|
||||
serialized_end=102,
|
||||
)
|
||||
|
||||
|
||||
_ROUTEREFRESHCAPABILITY = _descriptor.Descriptor(
|
||||
name='RouteRefreshCapability',
|
||||
full_name='gobgpapi.RouteRefreshCapability',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=104,
|
||||
serialized_end=128,
|
||||
)
|
||||
|
||||
|
||||
_CARRYINGLABELINFOCAPABILITY = _descriptor.Descriptor(
|
||||
name='CarryingLabelInfoCapability',
|
||||
full_name='gobgpapi.CarryingLabelInfoCapability',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=130,
|
||||
serialized_end=159,
|
||||
)
|
||||
|
||||
|
||||
_EXTENDEDNEXTHOPCAPABILITYTUPLE = _descriptor.Descriptor(
|
||||
name='ExtendedNexthopCapabilityTuple',
|
||||
full_name='gobgpapi.ExtendedNexthopCapabilityTuple',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='nlri_family', full_name='gobgpapi.ExtendedNexthopCapabilityTuple.nlri_family', index=0,
|
||||
number=1, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='nexthop_family', full_name='gobgpapi.ExtendedNexthopCapabilityTuple.nexthop_family', index=1,
|
||||
number=2, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=161,
|
||||
serialized_end=274,
|
||||
)
|
||||
|
||||
|
||||
_EXTENDEDNEXTHOPCAPABILITY = _descriptor.Descriptor(
|
||||
name='ExtendedNexthopCapability',
|
||||
full_name='gobgpapi.ExtendedNexthopCapability',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='tuples', full_name='gobgpapi.ExtendedNexthopCapability.tuples', index=0,
|
||||
number=1, type=11, cpp_type=10, label=3,
|
||||
has_default_value=False, default_value=[],
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=276,
|
||||
serialized_end=361,
|
||||
)
|
||||
|
||||
|
||||
_GRACEFULRESTARTCAPABILITYTUPLE = _descriptor.Descriptor(
|
||||
name='GracefulRestartCapabilityTuple',
|
||||
full_name='gobgpapi.GracefulRestartCapabilityTuple',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='family', full_name='gobgpapi.GracefulRestartCapabilityTuple.family', index=0,
|
||||
number=1, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='flags', full_name='gobgpapi.GracefulRestartCapabilityTuple.flags', index=1,
|
||||
number=2, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=363,
|
||||
serialized_end=444,
|
||||
)
|
||||
|
||||
|
||||
_GRACEFULRESTARTCAPABILITY = _descriptor.Descriptor(
|
||||
name='GracefulRestartCapability',
|
||||
full_name='gobgpapi.GracefulRestartCapability',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='flags', full_name='gobgpapi.GracefulRestartCapability.flags', index=0,
|
||||
number=1, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='time', full_name='gobgpapi.GracefulRestartCapability.time', index=1,
|
||||
number=2, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='tuples', full_name='gobgpapi.GracefulRestartCapability.tuples', index=2,
|
||||
number=3, type=11, cpp_type=10, label=3,
|
||||
has_default_value=False, default_value=[],
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=446,
|
||||
serialized_end=560,
|
||||
)
|
||||
|
||||
|
||||
_FOUROCTETASNUMBERCAPABILITY = _descriptor.Descriptor(
|
||||
name='FourOctetASNumberCapability',
|
||||
full_name='gobgpapi.FourOctetASNumberCapability',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='as', full_name='gobgpapi.FourOctetASNumberCapability.as', index=0,
|
||||
number=1, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=562,
|
||||
serialized_end=603,
|
||||
)
|
||||
|
||||
|
||||
_ADDPATHCAPABILITYTUPLE = _descriptor.Descriptor(
|
||||
name='AddPathCapabilityTuple',
|
||||
full_name='gobgpapi.AddPathCapabilityTuple',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='family', full_name='gobgpapi.AddPathCapabilityTuple.family', index=0,
|
||||
number=1, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='mode', full_name='gobgpapi.AddPathCapabilityTuple.mode', index=1,
|
||||
number=2, type=14, cpp_type=8, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=605,
|
||||
serialized_end=700,
|
||||
)
|
||||
|
||||
|
||||
_ADDPATHCAPABILITY = _descriptor.Descriptor(
|
||||
name='AddPathCapability',
|
||||
full_name='gobgpapi.AddPathCapability',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='tuples', full_name='gobgpapi.AddPathCapability.tuples', index=0,
|
||||
number=1, type=11, cpp_type=10, label=3,
|
||||
has_default_value=False, default_value=[],
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=702,
|
||||
serialized_end=771,
|
||||
)
|
||||
|
||||
|
||||
_ENHANCEDROUTEREFRESHCAPABILITY = _descriptor.Descriptor(
|
||||
name='EnhancedRouteRefreshCapability',
|
||||
full_name='gobgpapi.EnhancedRouteRefreshCapability',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=773,
|
||||
serialized_end=805,
|
||||
)
|
||||
|
||||
|
||||
_LONGLIVEDGRACEFULRESTARTCAPABILITYTUPLE = _descriptor.Descriptor(
|
||||
name='LongLivedGracefulRestartCapabilityTuple',
|
||||
full_name='gobgpapi.LongLivedGracefulRestartCapabilityTuple',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='family', full_name='gobgpapi.LongLivedGracefulRestartCapabilityTuple.family', index=0,
|
||||
number=1, type=11, cpp_type=10, label=1,
|
||||
has_default_value=False, default_value=None,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='flags', full_name='gobgpapi.LongLivedGracefulRestartCapabilityTuple.flags', index=1,
|
||||
number=2, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='time', full_name='gobgpapi.LongLivedGracefulRestartCapabilityTuple.time', index=2,
|
||||
number=3, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=807,
|
||||
serialized_end=911,
|
||||
)
|
||||
|
||||
|
||||
_LONGLIVEDGRACEFULRESTARTCAPABILITY = _descriptor.Descriptor(
|
||||
name='LongLivedGracefulRestartCapability',
|
||||
full_name='gobgpapi.LongLivedGracefulRestartCapability',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='tuples', full_name='gobgpapi.LongLivedGracefulRestartCapability.tuples', index=0,
|
||||
number=1, type=11, cpp_type=10, label=3,
|
||||
has_default_value=False, default_value=[],
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=913,
|
||||
serialized_end=1016,
|
||||
)
|
||||
|
||||
|
||||
_ROUTEREFRESHCISCOCAPABILITY = _descriptor.Descriptor(
|
||||
name='RouteRefreshCiscoCapability',
|
||||
full_name='gobgpapi.RouteRefreshCiscoCapability',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=1018,
|
||||
serialized_end=1047,
|
||||
)
|
||||
|
||||
|
||||
_UNKNOWNCAPABILITY = _descriptor.Descriptor(
|
||||
name='UnknownCapability',
|
||||
full_name='gobgpapi.UnknownCapability',
|
||||
filename=None,
|
||||
file=DESCRIPTOR,
|
||||
containing_type=None,
|
||||
fields=[
|
||||
_descriptor.FieldDescriptor(
|
||||
name='code', full_name='gobgpapi.UnknownCapability.code', index=0,
|
||||
number=1, type=13, cpp_type=3, label=1,
|
||||
has_default_value=False, default_value=0,
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
_descriptor.FieldDescriptor(
|
||||
name='value', full_name='gobgpapi.UnknownCapability.value', index=1,
|
||||
number=2, type=12, cpp_type=9, label=1,
|
||||
has_default_value=False, default_value=b"",
|
||||
message_type=None, enum_type=None, containing_type=None,
|
||||
is_extension=False, extension_scope=None,
|
||||
serialized_options=None, file=DESCRIPTOR),
|
||||
],
|
||||
extensions=[
|
||||
],
|
||||
nested_types=[],
|
||||
enum_types=[
|
||||
],
|
||||
serialized_options=None,
|
||||
is_extendable=False,
|
||||
syntax='proto3',
|
||||
extension_ranges=[],
|
||||
oneofs=[
|
||||
],
|
||||
serialized_start=1049,
|
||||
serialized_end=1097,
|
||||
)
|
||||
|
||||
_MULTIPROTOCOLCAPABILITY.fields_by_name['family'].message_type = gobgp__pb2._FAMILY
|
||||
_EXTENDEDNEXTHOPCAPABILITYTUPLE.fields_by_name['nlri_family'].message_type = gobgp__pb2._FAMILY
|
||||
_EXTENDEDNEXTHOPCAPABILITYTUPLE.fields_by_name['nexthop_family'].message_type = gobgp__pb2._FAMILY
|
||||
_EXTENDEDNEXTHOPCAPABILITY.fields_by_name['tuples'].message_type = _EXTENDEDNEXTHOPCAPABILITYTUPLE
|
||||
_GRACEFULRESTARTCAPABILITYTUPLE.fields_by_name['family'].message_type = gobgp__pb2._FAMILY
|
||||
_GRACEFULRESTARTCAPABILITY.fields_by_name['tuples'].message_type = _GRACEFULRESTARTCAPABILITYTUPLE
|
||||
_ADDPATHCAPABILITYTUPLE.fields_by_name['family'].message_type = gobgp__pb2._FAMILY
|
||||
_ADDPATHCAPABILITYTUPLE.fields_by_name['mode'].enum_type = _ADDPATHMODE
|
||||
_ADDPATHCAPABILITY.fields_by_name['tuples'].message_type = _ADDPATHCAPABILITYTUPLE
|
||||
_LONGLIVEDGRACEFULRESTARTCAPABILITYTUPLE.fields_by_name['family'].message_type = gobgp__pb2._FAMILY
|
||||
_LONGLIVEDGRACEFULRESTARTCAPABILITY.fields_by_name['tuples'].message_type = _LONGLIVEDGRACEFULRESTARTCAPABILITYTUPLE
|
||||
DESCRIPTOR.message_types_by_name['MultiProtocolCapability'] = _MULTIPROTOCOLCAPABILITY
|
||||
DESCRIPTOR.message_types_by_name['RouteRefreshCapability'] = _ROUTEREFRESHCAPABILITY
|
||||
DESCRIPTOR.message_types_by_name['CarryingLabelInfoCapability'] = _CARRYINGLABELINFOCAPABILITY
|
||||
DESCRIPTOR.message_types_by_name['ExtendedNexthopCapabilityTuple'] = _EXTENDEDNEXTHOPCAPABILITYTUPLE
|
||||
DESCRIPTOR.message_types_by_name['ExtendedNexthopCapability'] = _EXTENDEDNEXTHOPCAPABILITY
|
||||
DESCRIPTOR.message_types_by_name['GracefulRestartCapabilityTuple'] = _GRACEFULRESTARTCAPABILITYTUPLE
|
||||
DESCRIPTOR.message_types_by_name['GracefulRestartCapability'] = _GRACEFULRESTARTCAPABILITY
|
||||
DESCRIPTOR.message_types_by_name['FourOctetASNumberCapability'] = _FOUROCTETASNUMBERCAPABILITY
|
||||
DESCRIPTOR.message_types_by_name['AddPathCapabilityTuple'] = _ADDPATHCAPABILITYTUPLE
|
||||
DESCRIPTOR.message_types_by_name['AddPathCapability'] = _ADDPATHCAPABILITY
|
||||
DESCRIPTOR.message_types_by_name['EnhancedRouteRefreshCapability'] = _ENHANCEDROUTEREFRESHCAPABILITY
|
||||
DESCRIPTOR.message_types_by_name['LongLivedGracefulRestartCapabilityTuple'] = _LONGLIVEDGRACEFULRESTARTCAPABILITYTUPLE
|
||||
DESCRIPTOR.message_types_by_name['LongLivedGracefulRestartCapability'] = _LONGLIVEDGRACEFULRESTARTCAPABILITY
|
||||
DESCRIPTOR.message_types_by_name['RouteRefreshCiscoCapability'] = _ROUTEREFRESHCISCOCAPABILITY
|
||||
DESCRIPTOR.message_types_by_name['UnknownCapability'] = _UNKNOWNCAPABILITY
|
||||
DESCRIPTOR.enum_types_by_name['AddPathMode'] = _ADDPATHMODE
|
||||
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
|
||||
|
||||
MultiProtocolCapability = _reflection.GeneratedProtocolMessageType('MultiProtocolCapability', (_message.Message,), {
|
||||
'DESCRIPTOR' : _MULTIPROTOCOLCAPABILITY,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.MultiProtocolCapability)
|
||||
})
|
||||
_sym_db.RegisterMessage(MultiProtocolCapability)
|
||||
|
||||
RouteRefreshCapability = _reflection.GeneratedProtocolMessageType('RouteRefreshCapability', (_message.Message,), {
|
||||
'DESCRIPTOR' : _ROUTEREFRESHCAPABILITY,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.RouteRefreshCapability)
|
||||
})
|
||||
_sym_db.RegisterMessage(RouteRefreshCapability)
|
||||
|
||||
CarryingLabelInfoCapability = _reflection.GeneratedProtocolMessageType('CarryingLabelInfoCapability', (_message.Message,), {
|
||||
'DESCRIPTOR' : _CARRYINGLABELINFOCAPABILITY,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.CarryingLabelInfoCapability)
|
||||
})
|
||||
_sym_db.RegisterMessage(CarryingLabelInfoCapability)
|
||||
|
||||
ExtendedNexthopCapabilityTuple = _reflection.GeneratedProtocolMessageType('ExtendedNexthopCapabilityTuple', (_message.Message,), {
|
||||
'DESCRIPTOR' : _EXTENDEDNEXTHOPCAPABILITYTUPLE,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.ExtendedNexthopCapabilityTuple)
|
||||
})
|
||||
_sym_db.RegisterMessage(ExtendedNexthopCapabilityTuple)
|
||||
|
||||
ExtendedNexthopCapability = _reflection.GeneratedProtocolMessageType('ExtendedNexthopCapability', (_message.Message,), {
|
||||
'DESCRIPTOR' : _EXTENDEDNEXTHOPCAPABILITY,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.ExtendedNexthopCapability)
|
||||
})
|
||||
_sym_db.RegisterMessage(ExtendedNexthopCapability)
|
||||
|
||||
GracefulRestartCapabilityTuple = _reflection.GeneratedProtocolMessageType('GracefulRestartCapabilityTuple', (_message.Message,), {
|
||||
'DESCRIPTOR' : _GRACEFULRESTARTCAPABILITYTUPLE,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.GracefulRestartCapabilityTuple)
|
||||
})
|
||||
_sym_db.RegisterMessage(GracefulRestartCapabilityTuple)
|
||||
|
||||
GracefulRestartCapability = _reflection.GeneratedProtocolMessageType('GracefulRestartCapability', (_message.Message,), {
|
||||
'DESCRIPTOR' : _GRACEFULRESTARTCAPABILITY,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.GracefulRestartCapability)
|
||||
})
|
||||
_sym_db.RegisterMessage(GracefulRestartCapability)
|
||||
|
||||
FourOctetASNumberCapability = _reflection.GeneratedProtocolMessageType('FourOctetASNumberCapability', (_message.Message,), {
|
||||
'DESCRIPTOR' : _FOUROCTETASNUMBERCAPABILITY,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.FourOctetASNumberCapability)
|
||||
})
|
||||
_sym_db.RegisterMessage(FourOctetASNumberCapability)
|
||||
|
||||
AddPathCapabilityTuple = _reflection.GeneratedProtocolMessageType('AddPathCapabilityTuple', (_message.Message,), {
|
||||
'DESCRIPTOR' : _ADDPATHCAPABILITYTUPLE,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.AddPathCapabilityTuple)
|
||||
})
|
||||
_sym_db.RegisterMessage(AddPathCapabilityTuple)
|
||||
|
||||
AddPathCapability = _reflection.GeneratedProtocolMessageType('AddPathCapability', (_message.Message,), {
|
||||
'DESCRIPTOR' : _ADDPATHCAPABILITY,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.AddPathCapability)
|
||||
})
|
||||
_sym_db.RegisterMessage(AddPathCapability)
|
||||
|
||||
EnhancedRouteRefreshCapability = _reflection.GeneratedProtocolMessageType('EnhancedRouteRefreshCapability', (_message.Message,), {
|
||||
'DESCRIPTOR' : _ENHANCEDROUTEREFRESHCAPABILITY,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.EnhancedRouteRefreshCapability)
|
||||
})
|
||||
_sym_db.RegisterMessage(EnhancedRouteRefreshCapability)
|
||||
|
||||
LongLivedGracefulRestartCapabilityTuple = _reflection.GeneratedProtocolMessageType('LongLivedGracefulRestartCapabilityTuple', (_message.Message,), {
|
||||
'DESCRIPTOR' : _LONGLIVEDGRACEFULRESTARTCAPABILITYTUPLE,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.LongLivedGracefulRestartCapabilityTuple)
|
||||
})
|
||||
_sym_db.RegisterMessage(LongLivedGracefulRestartCapabilityTuple)
|
||||
|
||||
LongLivedGracefulRestartCapability = _reflection.GeneratedProtocolMessageType('LongLivedGracefulRestartCapability', (_message.Message,), {
|
||||
'DESCRIPTOR' : _LONGLIVEDGRACEFULRESTARTCAPABILITY,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.LongLivedGracefulRestartCapability)
|
||||
})
|
||||
_sym_db.RegisterMessage(LongLivedGracefulRestartCapability)
|
||||
|
||||
RouteRefreshCiscoCapability = _reflection.GeneratedProtocolMessageType('RouteRefreshCiscoCapability', (_message.Message,), {
|
||||
'DESCRIPTOR' : _ROUTEREFRESHCISCOCAPABILITY,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.RouteRefreshCiscoCapability)
|
||||
})
|
||||
_sym_db.RegisterMessage(RouteRefreshCiscoCapability)
|
||||
|
||||
UnknownCapability = _reflection.GeneratedProtocolMessageType('UnknownCapability', (_message.Message,), {
|
||||
'DESCRIPTOR' : _UNKNOWNCAPABILITY,
|
||||
'__module__' : 'capability_pb2'
|
||||
# @@protoc_insertion_point(class_scope:gobgpapi.UnknownCapability)
|
||||
})
|
||||
_sym_db.RegisterMessage(UnknownCapability)
|
||||
|
||||
|
||||
# @@protoc_insertion_point(module_scope)
|
|
@ -1,3 +0,0 @@
|
|||
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
||||
import grpc
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -3,7 +3,7 @@
|
|||
# Written by Sebastian Lohff (seba@someserver.de)
|
||||
|
||||
from django.contrib import admin
|
||||
from bgpdata.models import ConfigHost, CrawlRun, CrawlLog, AS, BorderRouter, Announcement, Peering, BorderRouterPair, ASLastSeen, ASLastSeenNeighbor
|
||||
from bgpdata.models import ConfigHost, CrawlRun, CrawlLog, AS, BorderRouter, Announcement, Peering, BorderRouterPair
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(ConfigHost)
|
||||
|
@ -14,5 +14,3 @@ admin.site.register(BorderRouter)
|
|||
admin.site.register(Announcement)
|
||||
admin.site.register(Peering)
|
||||
admin.site.register(BorderRouterPair)
|
||||
admin.site.register(ASLastSeen)
|
||||
admin.site.register(ASLastSeenNeighbor)
|
||||
|
|
|
@ -6,10 +6,8 @@ from tastypie.resources import ModelResource, ALL_WITH_RELATIONS, ALL
|
|||
from tastypie import fields
|
||||
from bgpdata.models import AS, CrawlRun, Announcement, BorderRouter
|
||||
|
||||
|
||||
class ASResource(ModelResource):
|
||||
crawl = fields.ForeignKey("bgpdata.api.CrawlResource", "crawl")
|
||||
|
||||
class Meta:
|
||||
list_allowed_methods = ['get']
|
||||
detail_allowed_methods = ['get']
|
||||
|
@ -18,23 +16,13 @@ class ASResource(ModelResource):
|
|||
queryset = AS.objects.all()
|
||||
resource_name = "as"
|
||||
|
||||
|
||||
class CrawlResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = CrawlRun.objects.all()
|
||||
resource_name = "crawl"
|
||||
excludes = ["graph"]
|
||||
|
||||
|
||||
class CrawlGraphResource(ModelResource):
|
||||
class Meta:
|
||||
queryset = CrawlRun.objects.all()
|
||||
resource_name = "crawl_graph"
|
||||
|
||||
|
||||
class BorderRouterResource(ModelResource):
|
||||
AS = fields.ForeignKey("bgpdata.api.ASResource", "AS")
|
||||
|
||||
class Meta:
|
||||
list_allowed_methods = ['get']
|
||||
detail_allowed_methods = ['get']
|
||||
|
@ -43,10 +31,8 @@ class BorderRouterResource(ModelResource):
|
|||
queryset = BorderRouter.objects.all()
|
||||
resource_name = "borderrouter"
|
||||
|
||||
|
||||
class AnnouncementResource(ModelResource):
|
||||
router = fields.ForeignKey("bgpdata.api.BorderRouterResource", "router")
|
||||
|
||||
class Meta:
|
||||
list_allowed_methods = ['get']
|
||||
detail_allowed_methods = ['get']
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
# This file is part of dnmapper, an AS--level mapping tool
|
||||
# Licensed under GNU General Public License v3 or later
|
||||
# Written by Sebastian Lohff (seba@someserver.de)
|
||||
from django.conf.urls import url, include
|
||||
|
||||
from bgpdata.api import ASResource, CrawlResource, CrawlGraphResource, BorderRouterResource, AnnouncementResource
|
||||
|
||||
asResource = ASResource()
|
||||
crawlResource = CrawlResource()
|
||||
crawlGraphResource = CrawlGraphResource()
|
||||
borderRouterResource = BorderRouterResource()
|
||||
announcementResource = AnnouncementResource()
|
||||
|
||||
|
||||
urlpatterns = (
|
||||
url('', include(asResource.urls)),
|
||||
url('', include(crawlResource.urls)),
|
||||
url('', include(crawlGraphResource.urls)),
|
||||
url('', include(borderRouterResource.urls)),
|
||||
url('', include(announcementResource.urls)),
|
||||
)
|
|
@ -39,7 +39,7 @@ class Migration(migrations.Migration):
|
|||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('pingable', models.BooleanField(default=False)),
|
||||
('reachable', models.BooleanField(default=False)),
|
||||
('AS', models.ForeignKey(to='bgpdata.AS', on_delete=models.CASCADE)),
|
||||
('AS', models.ForeignKey(to='bgpdata.AS')),
|
||||
],
|
||||
options={
|
||||
},
|
||||
|
@ -73,10 +73,10 @@ class Migration(migrations.Migration):
|
|||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('origin', models.CharField(max_length=10, choices=[(b'path', b'BGP Path'), (b'direct', b'Direct Connection')])),
|
||||
('as1', models.ForeignKey(related_name='peering1', to='bgpdata.AS', on_delete=models.CASCADE)),
|
||||
('as2', models.ForeignKey(related_name='peering2', to='bgpdata.AS', on_delete=models.CASCADE)),
|
||||
('router1', models.ForeignKey(related_name='peering1', default=None, to='bgpdata.BorderRouter', null=True, on_delete=models.CASCADE)),
|
||||
('router2', models.ForeignKey(related_name='peering2', default=None, to='bgpdata.BorderRouter', null=True, on_delete=models.CASCADE)),
|
||||
('as1', models.ForeignKey(related_name='peering1', to='bgpdata.AS')),
|
||||
('as2', models.ForeignKey(related_name='peering2', to='bgpdata.AS')),
|
||||
('router1', models.ForeignKey(related_name='peering1', default=None, to='bgpdata.BorderRouter', null=True)),
|
||||
('router2', models.ForeignKey(related_name='peering2', default=None, to='bgpdata.BorderRouter', null=True)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
|
@ -85,13 +85,13 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='as',
|
||||
name='crawl',
|
||||
field=models.ForeignKey(to='bgpdata.CrawlRun', on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(to='bgpdata.CrawlRun'),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='announcement',
|
||||
name='originAS',
|
||||
field=models.ForeignKey(to='bgpdata.AS', on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(to='bgpdata.AS'),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -18,8 +18,8 @@ class Migration(migrations.Migration):
|
|||
('logtime', models.DateTimeField(auto_now_add=True)),
|
||||
('severity', models.CharField(max_length=10, choices=[(b'INFO', b'info'), (b'ERROR', b'error'), (b'DEBUG', b'debug'), (b'WARN', b'warning')])),
|
||||
('message', models.TextField()),
|
||||
('crawl', models.ForeignKey(to='bgpdata.CrawlRun', on_delete=models.CASCADE)),
|
||||
('host', models.ForeignKey(to='bgpdata.ConfigHost', on_delete=models.SET_NULL)),
|
||||
('crawl', models.ForeignKey(to='bgpdata.CrawlRun')),
|
||||
('host', models.ForeignKey(to='bgpdata.ConfigHost')),
|
||||
],
|
||||
options={
|
||||
},
|
||||
|
|
|
@ -14,7 +14,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='crawllog',
|
||||
name='host',
|
||||
field=models.ForeignKey(to='bgpdata.ConfigHost', null=True, on_delete=models.SET_NULL),
|
||||
field=models.ForeignKey(to='bgpdata.ConfigHost', null=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -20,7 +20,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='crawllog',
|
||||
name='host',
|
||||
field=models.ForeignKey(blank=True, to='bgpdata.ConfigHost', null=True, on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(blank=True, to='bgpdata.ConfigHost', null=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -15,8 +15,8 @@ class Migration(migrations.Migration):
|
|||
name='BorderRouterPair',
|
||||
fields=[
|
||||
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
|
||||
('router1', models.ForeignKey(related_name='routerpair1', default=None, blank=True, to='bgpdata.BorderRouter', null=True, on_delete=models.CASCADE)),
|
||||
('router2', models.ForeignKey(related_name='routerpair2', default=None, blank=True, to='bgpdata.BorderRouter', null=True, on_delete=models.CASCADE)),
|
||||
('router1', models.ForeignKey(related_name='routerpair1', default=None, blank=True, to='bgpdata.BorderRouter', null=True)),
|
||||
('router2', models.ForeignKey(related_name='routerpair2', default=None, blank=True, to='bgpdata.BorderRouter', null=True)),
|
||||
],
|
||||
options={
|
||||
},
|
||||
|
@ -33,7 +33,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='announcement',
|
||||
name='router',
|
||||
field=models.ForeignKey(default=None, to='bgpdata.BorderRouter', on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(default=None, to='bgpdata.BorderRouter'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
|
|
|
@ -18,7 +18,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='borderrouterpair',
|
||||
name='peering',
|
||||
field=models.ForeignKey(default=None, to='bgpdata.Peering', on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(default=None, to='bgpdata.Peering'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -14,7 +14,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='as',
|
||||
name='lastSeen',
|
||||
field=models.ForeignKey(related_name='as_lastseen', default=None, blank=True, to='bgpdata.CrawlRun', null=True, on_delete=models.SET_NULL),
|
||||
field=models.ForeignKey(related_name='as_lastseen', default=None, blank=True, to='bgpdata.CrawlRun', null=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
|
|
|
@ -14,7 +14,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='announcement',
|
||||
name='crawlAS',
|
||||
field=models.ForeignKey(related_name='crawl_as', to='bgpdata.AS', null=True, on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(related_name='crawl_as', to='bgpdata.AS', null=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
|
|
|
@ -14,7 +14,7 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='announcement',
|
||||
name='originAS',
|
||||
field=models.ForeignKey(to='bgpdata.AS', null=True, on_delete=models.CASCADE),
|
||||
field=models.ForeignKey(to='bgpdata.AS', null=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.25 on 2020-05-31 00:00
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bgpdata', '0016_auto_20170117_0103'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='confighost',
|
||||
name='checkMethod',
|
||||
field=models.CharField(choices=[('CMK', 'Check MK'), ('PLAIN', 'Plain'), ('GOBGP', 'GoBGP')], max_length=10),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='crawllog',
|
||||
name='severity',
|
||||
field=models.CharField(choices=[('INFO', 'info'), ('ERROR', 'error'), ('DEBUG', 'debug'), ('WARN', 'warning')], max_length=10),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='peering',
|
||||
name='origin',
|
||||
field=models.CharField(choices=[('path', 'BGP Path'), ('direct', 'Direct Connection')], max_length=10),
|
||||
),
|
||||
]
|
|
@ -1,45 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.25 on 2020-06-03 22:45
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bgpdata', '0017_auto_20200531_0200'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ASLastSeen',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('asn', models.IntegerField(db_index=True, unique=True)),
|
||||
('directlyCrawled', models.BooleanField(default=False)),
|
||||
('online', models.BooleanField()),
|
||||
('lastSeen', models.DateTimeField(blank=True, null=True)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ASLastSeenNeighbor',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('asn', models.IntegerField(unique=True)),
|
||||
('neighbor', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='bgpdata.ASLastSeen')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='crawlrun',
|
||||
name='graph',
|
||||
field=models.TextField(default=''),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='aslastseen',
|
||||
name='crawlLastSeen',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='bgpdata.CrawlRun'),
|
||||
),
|
||||
]
|
|
@ -1,20 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.25 on 2020-06-03 23:45
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bgpdata', '0018_auto_20200604_0045'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='aslastseenneighbor',
|
||||
name='asn',
|
||||
field=models.IntegerField(),
|
||||
),
|
||||
]
|
|
@ -1,26 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.29 on 2020-06-06 18:40
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('bgpdata', '0019_auto_20200604_0145'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='as',
|
||||
name='lastSeen',
|
||||
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='as_lastseen', to='bgpdata.CrawlRun'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='aslastseen',
|
||||
name='crawlLastSeen',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='bgpdata.CrawlRun'),
|
||||
),
|
||||
]
|
|
@ -11,47 +11,42 @@ class ConfigHost(models.Model):
|
|||
CHECK_CHOICES = (
|
||||
('CMK', "Check MK"),
|
||||
('PLAIN', "Plain"),
|
||||
('GOBGP', "GoBGP"),
|
||||
)
|
||||
|
||||
# asno, ip, check method,
|
||||
name = models.CharField(max_length=50)
|
||||
number = models.IntegerField()
|
||||
ip = models.GenericIPAddressField()
|
||||
checkMethod = models.CharField(max_length=10, choices=CHECK_CHOICES)
|
||||
|
||||
def __str__(self):
|
||||
return "%s (%s / %s)" % (self.name, self.number, self.ip)
|
||||
checkMethod = models.CharField(max_length=4, choices=CHECK_CHOICES)
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s (%s / %s)" % (self.name, self.number, self.ip)
|
||||
|
||||
class CrawlRun(models.Model):
|
||||
# time start, time end,
|
||||
startTime = models.DateTimeField()
|
||||
endTime = models.DateTimeField(null=True, blank=True)
|
||||
|
||||
hostsCrawled = models.ManyToManyField(ConfigHost, blank=True)
|
||||
graph = models.TextField()
|
||||
hostsCrawled = models.ManyToManyField(ConfigHost, null=True, blank=True)
|
||||
|
||||
asCount = models.IntegerField(default=0)
|
||||
asOnlineCount = models.IntegerField(default=0)
|
||||
asOfflineCount = models.IntegerField(default=0)
|
||||
peeringCount = models.IntegerField(default=0)
|
||||
|
||||
def __str__(self):
|
||||
return "Run %d - %s to %s" % (self.pk, self.startTime, self.endTime if self.endTime else "?")
|
||||
def __unicode__(self):
|
||||
return u"Run %d - %s to %s" % (self.pk, self.startTime, self.endTime if self.endTime else "?")
|
||||
|
||||
def countAS(self):
|
||||
return self.asCount
|
||||
|
||||
def countASOnline(self):
|
||||
return self.asOnlineCount
|
||||
|
||||
def countASOffline(self):
|
||||
return self.asOfflineCount
|
||||
|
||||
#return self.as_set.filter(online=False).count()
|
||||
def countPeerings(self):
|
||||
return self.peeringCount
|
||||
|
||||
#return Peering.objects.filter(Q(as1__crawl=self)|Q(as2__crawl=self)).count()
|
||||
|
||||
class CrawlLog(models.Model):
|
||||
INFO = 'INFO'
|
||||
|
@ -65,7 +60,7 @@ class CrawlLog(models.Model):
|
|||
(WARN, 'warning'),
|
||||
)
|
||||
|
||||
crawl = models.ForeignKey(CrawlRun, on_delete=models.CASCADE)
|
||||
crawl = models.ForeignKey(CrawlRun)
|
||||
host = models.ForeignKey(ConfigHost, null=True, blank=True, on_delete=models.SET_NULL)
|
||||
logtime = models.DateTimeField(auto_now_add=True)
|
||||
severity = models.CharField(max_length=10, choices=SEVERITY)
|
||||
|
@ -83,20 +78,18 @@ class CrawlLog(models.Model):
|
|||
log.host = host
|
||||
log.save()
|
||||
|
||||
def __str__(self):
|
||||
def __unicode__(self):
|
||||
host = "host %s - " % self.host.name if self.host else ""
|
||||
return "Log %s %s: %s%s" % (self.get_severity_display(), self.logtime, host, self.message)
|
||||
|
||||
return u"Log %s %s: %s%s" % (self.get_severity_display(), self.logtime, host, self.message)
|
||||
|
||||
class AS(models.Model):
|
||||
# asno
|
||||
crawl = models.ForeignKey(CrawlRun, on_delete=models.CASCADE)
|
||||
crawl = models.ForeignKey(CrawlRun)
|
||||
number = models.IntegerField(db_index=True)
|
||||
|
||||
directlyCrawled = models.BooleanField(default=False)
|
||||
online = models.BooleanField(default=True, db_index=True)
|
||||
lastSeen = models.ForeignKey(CrawlRun, blank=True, null=True, default=None, related_name='as_lastseen',
|
||||
on_delete=models.CASCADE)
|
||||
lastSeen = models.ForeignKey(CrawlRun, blank=True, null=True, default=None, related_name='as_lastseen')
|
||||
|
||||
class Meta:
|
||||
unique_together = (('crawl', 'number'),)
|
||||
|
@ -104,8 +97,8 @@ class AS(models.Model):
|
|||
('crawl', 'number'),
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return "AS %s (crawl %d)" % (self.number, self.crawl.pk)
|
||||
def __unicode__(self):
|
||||
return u"AS %s (crawl %d)" % (self.number, self.crawl.pk)
|
||||
|
||||
def setOnline(self):
|
||||
if not self.online:
|
||||
|
@ -123,24 +116,22 @@ class AS(models.Model):
|
|||
if self.lastSeen:
|
||||
return self.lastSeen.startTime.strftime("%d.%m.%Y %H:%I")
|
||||
|
||||
|
||||
class BorderRouter(models.Model):
|
||||
# as id, ip, check method, pingable, reachable
|
||||
# unique: (crawl_id, asno, as id)
|
||||
AS = models.ForeignKey(AS, on_delete=models.CASCADE)
|
||||
AS = models.ForeignKey(AS)
|
||||
routerID = models.GenericIPAddressField()
|
||||
|
||||
pingable = models.BooleanField(default=False)
|
||||
reachable = models.BooleanField(default=False)
|
||||
|
||||
def __str__(self):
|
||||
def __unicode__(self):
|
||||
p = "p" if self.pingable else "!p"
|
||||
r = "r" if self.reachable else "!r"
|
||||
return "Router %s (AS %s, %s%s)" % (self.routerID, self.AS.number, p, r)
|
||||
|
||||
return u"Router %s (AS %s, %s%s)" % (self.routerID, self.AS.number, p, r)
|
||||
|
||||
class Announcement(models.Model):
|
||||
router = models.ForeignKey(BorderRouter, on_delete=models.CASCADE)
|
||||
router = models.ForeignKey(BorderRouter)
|
||||
|
||||
ip = models.GenericIPAddressField()
|
||||
prefix = models.IntegerField()
|
||||
|
@ -148,12 +139,11 @@ class Announcement(models.Model):
|
|||
# NOTE: increase length for longer pathes (currently supports a length of ~85)
|
||||
ASPath = models.CharField(max_length=512)
|
||||
nextHop = models.GenericIPAddressField()
|
||||
originAS = models.ForeignKey(AS, null=True, on_delete=models.CASCADE)
|
||||
crawlAS = models.ForeignKey(AS, related_name='crawl_as', null=True, on_delete=models.CASCADE)
|
||||
|
||||
def __str__(self):
|
||||
return "%s/%s via %s (crawl %s)" % (self.ip, self.prefix, self.ASPath, self.router.AS.crawl.pk)
|
||||
originAS = models.ForeignKey(AS, null=True)
|
||||
crawlAS = models.ForeignKey(AS, related_name='crawl_as', null=True)
|
||||
|
||||
def __unicode__(self):
|
||||
return u"%s/%s via %s (crawl %s)" % (self.ip, self.prefix, self.ASPath, self.router.AS.crawl.pk)
|
||||
|
||||
class Peering(models.Model):
|
||||
DIRECT = 'direct'
|
||||
|
@ -168,13 +158,12 @@ class Peering(models.Model):
|
|||
('as1', 'as2'),
|
||||
)
|
||||
|
||||
as1 = models.ForeignKey(AS, related_name='peering1', on_delete=models.CASCADE)
|
||||
as2 = models.ForeignKey(AS, related_name='peering2', on_delete=models.CASCADE)
|
||||
as1 = models.ForeignKey(AS, related_name='peering1')
|
||||
as2 = models.ForeignKey(AS, related_name='peering2')
|
||||
origin = models.CharField(max_length=10, choices=ORIGIN)
|
||||
|
||||
def __str__(self):
|
||||
return "AS %s <--> AS %s (%s, crawl %s)" % (self.as1.number, self.as2.number,
|
||||
self.get_origin_display(), self.as1.crawl.pk)
|
||||
def __unicode__(self):
|
||||
return u"AS %s <--> AS %s (%s, crawl %s)" % (self.as1.number, self.as2.number, self.get_origin_display(), self.as1.crawl.pk)
|
||||
|
||||
def containsAS(self, AS):
|
||||
return AS in (self.as1, self.as2)
|
||||
|
@ -187,16 +176,13 @@ class Peering(models.Model):
|
|||
except Peering.DoesNotExist:
|
||||
return Peering.objects.get(as1=as2, as2=as1)
|
||||
|
||||
|
||||
class BorderRouterPair(models.Model):
|
||||
peering = models.ForeignKey(Peering, on_delete=models.CASCADE)
|
||||
router1 = models.ForeignKey(BorderRouter, default=None, blank=True, null=True, related_name='routerpair1',
|
||||
on_delete=models.CASCADE)
|
||||
router2 = models.ForeignKey(BorderRouter, default=None, blank=True, null=True, related_name='routerpair2',
|
||||
on_delete=models.CASCADE)
|
||||
peering = models.ForeignKey(Peering)
|
||||
router1 = models.ForeignKey(BorderRouter, default=None, blank=True, null=True, related_name='routerpair1')
|
||||
router2 = models.ForeignKey(BorderRouter, default=None, blank=True, null=True, related_name='routerpair2')
|
||||
|
||||
def __str__(self):
|
||||
return "%s <--> %s (crawl %d)" % (self.router1, self.router2, self.router1.AS.crawl.pk)
|
||||
def __unicode__(self):
|
||||
return u"%s <--> %s (crawl %d)" % (self.router1, self.router2, self.router1.AS.crawl.pk)
|
||||
|
||||
@staticmethod
|
||||
def getPairing(peering, router1, router2):
|
||||
|
@ -204,20 +190,3 @@ class BorderRouterPair(models.Model):
|
|||
return BorderRouterPair.objects.get(peering=peering, router1=router1, router2=router2)
|
||||
except BorderRouterPair.DoesNotExist:
|
||||
return BorderRouterPair.objects.get(peering=peering, router1=router2, router2=router1)
|
||||
|
||||
|
||||
class ASLastSeen(models.Model):
|
||||
asn = models.IntegerField(db_index=True, unique=True)
|
||||
directlyCrawled = models.BooleanField(default=False)
|
||||
online = models.BooleanField()
|
||||
lastSeen = models.DateTimeField(blank=True, null=True)
|
||||
crawlLastSeen = models.ForeignKey(CrawlRun, null=True, on_delete=models.SET_NULL)
|
||||
|
||||
def __str__(self):
|
||||
return ("AS{} {}, last seen {} (crawl {})"
|
||||
.format(self.asn, "online" if self.online else "offline", self.lastSeen, self.crawlLastSeen.pk))
|
||||
|
||||
|
||||
class ASLastSeenNeighbor(models.Model):
|
||||
asn = models.IntegerField()
|
||||
neighbor = models.ForeignKey(ASLastSeen, on_delete=models.CASCADE)
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% block body %}
|
||||
<h3>ASN Last Seen</h3>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Status</th>
|
||||
<th>Directly Crawled</th>
|
||||
<th>Last Seen</th>
|
||||
<th>Last Seen Crawl</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for asn in last_seen %}
|
||||
<tr class="{% if asn.online %}success{% else %}danger{% endif %}">
|
||||
<td>{{ asn.asn }}</td>
|
||||
<td>{%if not asn.online %}Offline{% endif %}</td>
|
||||
<td>{% if asn.directlyCrawled %}Yes{% endif %}</td>
|
||||
<td>{% if not asn.online %}{{ asn.lastSeen }}{% endif %}</td>
|
||||
<td><a href="/map/{{ asn.crawlLastSeen.id }}/">Crawl {{ asn.crawlLastSeen.id }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
|
@ -238,12 +238,12 @@ function click(d) {
|
|||
//});
|
||||
|
||||
if(d.crawled) {
|
||||
$.ajax({url: "/api/v1/borderrouter/?AS__crawl={{crawl.pk}}&AS__number=" + d.asnumber, success: function(result) {
|
||||
$.ajax({url: "/map/api/borderrouter/?AS__crawl={{crawl.pk}}&AS__number=" + d.asnumber, success: function(result) {
|
||||
$("#infowin").html('');
|
||||
$("#infowin").fadeIn('fast', function() {});
|
||||
for(var i=0; i<result.objects.length; i++) {
|
||||
(function(currRouter) {
|
||||
$.ajax({url:"/api/v1/announcement/?router="+currRouter.id, success: function(result2) {
|
||||
$.ajax({url:"/map/api/announcement/?router="+currRouter.id, success: function(result2) {
|
||||
astable = 'Table for AS'+d.asnumber+' router ID ' + currRouter.routerID;
|
||||
astable += '<table class="table table-bordered">';
|
||||
astable += '<tr><th>Network</th><th>Next Hop</th><th>AS Path</th></tr>';
|
||||
|
|
|
@ -1,215 +0,0 @@
|
|||
{% extends "base.html" %}
|
||||
{% block container_class %}container-fluid{% endblock %}
|
||||
{% block head %}
|
||||
{% load static from staticfiles %}
|
||||
<!-- <script src="{% static "js/d3.js" %}" charset="utf-8"></script> -->
|
||||
<script src="https://d3js.org/d3.v5.min.js"></script>
|
||||
<style>
|
||||
.node {
|
||||
stroke: #fff;
|
||||
stroke-width: 1.5px;
|
||||
}
|
||||
|
||||
.link {
|
||||
stroke: #999;
|
||||
stroke-opacity: .6;
|
||||
}
|
||||
|
||||
#plotwin {
|
||||
height: 75vh;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<div class="page-header">
|
||||
<h3>{% if crawl.pk %}Crawl run {{crawl.pk}}{% else %}Live Crawl {% endif %} from {{crawl.startTime|date:"d.m.Y H:i"}}</h3>
|
||||
</div>
|
||||
|
||||
<div id="plotwin" class="container-fluid"></div>
|
||||
<!-- <div id="infowin"><div class="alert alert-info" role="alert">Click on a node for more information</div></div> -->
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
|
||||
const margin = {
|
||||
top: 40,
|
||||
bottom: 10,
|
||||
left: 20,
|
||||
right: 20,
|
||||
};
|
||||
let plotwin = document.getElementById("plotwin")
|
||||
const width = plotwin.offsetWidth - margin.left - margin.right;
|
||||
const height = plotwin.offsetHeight - margin.top - margin.bottom;
|
||||
|
||||
let drag = d3.drag()
|
||||
.on("drag", dragged);
|
||||
|
||||
function dragged() {
|
||||
let current = d3.select(this);
|
||||
current
|
||||
.attr('cx', d3.event.x)
|
||||
.attr('cy', d3.event.y);
|
||||
console.log(`${d3.event.x}, ${d3.event.y}`);
|
||||
}
|
||||
|
||||
function dragmove(d) {
|
||||
let x = d3.event.x;
|
||||
let y = d3.event.y;
|
||||
d3.select(this).attr("transform", "translate(" + x + "," + y + ")");
|
||||
}
|
||||
|
||||
const parent_svg = d3
|
||||
.select("#plotwin")
|
||||
.append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
//.attr("viewBox", [width / 2, height / 2, width, height])
|
||||
.attr("viewBox", [0, 0, width, height])
|
||||
const svg = parent_svg.append("g")
|
||||
// .append("g")
|
||||
// .attr("transform", `translate(${margin.left}, ${margin.top})`);
|
||||
|
||||
parent_svg.call(
|
||||
d3.zoom()
|
||||
.scaleExtent([.1, 4])
|
||||
.on("zoom", function() { svg.attr("transform", d3.event.transform); })
|
||||
);
|
||||
|
||||
//const simulation = d3
|
||||
// .forceSimulation()
|
||||
// .force(
|
||||
// "link",
|
||||
// d3.forceLink().id((d) => d.id)
|
||||
// )
|
||||
// .force("charge", d3.forceManyBody().distanceMin(50).strength(-300))
|
||||
// .force("center", d3.forceCenter(width / 2, height / 2));
|
||||
|
||||
const color = d3.scaleOrdinal(d3.schemeCategory10);
|
||||
|
||||
drag = simulation => {
|
||||
|
||||
function dragstarted(d) {
|
||||
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
|
||||
d.fx = d.x;
|
||||
d.fy = d.y;
|
||||
}
|
||||
|
||||
function dragged(d) {
|
||||
d.fx = d3.event.x;
|
||||
d.fy = d3.event.y;
|
||||
}
|
||||
|
||||
function dragended(d) {
|
||||
if (!d3.event.active) simulation.alphaTarget(0);
|
||||
d.fx = null;
|
||||
d.fy = null;
|
||||
}
|
||||
|
||||
return d3.drag()
|
||||
.on("start", dragstarted)
|
||||
.on("drag", dragged)
|
||||
.on("end", dragended);
|
||||
}
|
||||
|
||||
|
||||
d3.json("/api/v2/crawlrun/live/?with_graph").then((data) => {
|
||||
// Links data join
|
||||
data = data.graph
|
||||
|
||||
const simulation = d3.forceSimulation(data.nodes)
|
||||
//.force("link", d3.forceLink(data.links).id(d => d.id).distance(150).strength(1))
|
||||
//.force("link", d3.forceLink().id(function(d) { return d.id; }))
|
||||
.force("link", d3.forceLink().id(d => d.id)
|
||||
.distance(l => {
|
||||
neighs = Math.min(l.source.neighbors, l.target.neighbors);
|
||||
switch(neighs) {
|
||||
case 0: return 40;
|
||||
case 1: return 40;
|
||||
case 2: return 120;
|
||||
case 3:
|
||||
case 4: return 200;
|
||||
default: return 300;
|
||||
}
|
||||
})
|
||||
.strength(l => {
|
||||
neighs = Math.min(l.source.neighbors, l.target.neighbors);
|
||||
return 1 / (neighs);
|
||||
|
||||
}))
|
||||
.force("charge", d3.forceManyBody()
|
||||
.strength(-200)
|
||||
.theta(1.1))
|
||||
//.force("collision", d3.forceCollide(50).strength(0.2).iterations(100))
|
||||
//.force("collision", d3.forceCollide(40).strength(0.2))
|
||||
.force("collision", d3.forceCollide(40).strength(0.2))
|
||||
//.force("x", d3.forceX())
|
||||
//.force("y", d3.forceY())
|
||||
.force("center", d3.forceCenter(width / 2, height / 2));
|
||||
//.force("center", d3.forceRadial(100, width / 2, height / 2));
|
||||
|
||||
|
||||
|
||||
|
||||
const link = svg
|
||||
.selectAll(".link")
|
||||
.data(data.links)
|
||||
.join((enter) =>
|
||||
enter.append("line")
|
||||
.attr("class", "link"));
|
||||
|
||||
// Nodes data join
|
||||
|
||||
|
||||
let node = svg.selectAll('.node')
|
||||
.data(data.nodes)
|
||||
.enter()
|
||||
.append("g")
|
||||
.attr("id", d => "node-" + d.id)
|
||||
.call(drag(simulation));
|
||||
|
||||
node.append("ellipse")
|
||||
//.attr("rx", d => d.stub ? 35 : 40)
|
||||
//.attr("ry", d => d.stub ? 14 : 20)
|
||||
.attr("rx", d => d.stub ? 34 : (40 + (d.neighbors > 5 ? 5 : 0)))
|
||||
.attr("ry", d => d.stub ? 12 : (20 + (d.neighbors > 5 ? 2 : 0)))
|
||||
.attr("fill", function(d) {
|
||||
if(d.directly_crawled)
|
||||
return "#94FF70";
|
||||
else if(!d.online)
|
||||
return "#FFCCCC";
|
||||
// return "#F0FFEB";
|
||||
else if(d.stub)
|
||||
return "#3291A8"
|
||||
else
|
||||
return "#D1FFC2";
|
||||
})
|
||||
.attr("stroke", "black")
|
||||
.attr("stroke-width", "1px");
|
||||
|
||||
node.append('text')
|
||||
.attr("fill", "black")
|
||||
.attr("font-family", "sans-serif")
|
||||
.attr("font-size", "13px")
|
||||
.attr("font-weight", "bold")
|
||||
.attr("dy", "4")
|
||||
.attr("text-anchor", "middle")
|
||||
.text(d => d.id)
|
||||
|
||||
|
||||
|
||||
|
||||
simulation.nodes(data.nodes).force("link").links(data.links);
|
||||
|
||||
simulation.on("tick", (e) => {
|
||||
link
|
||||
.attr("x1", (d) => d.source.x)
|
||||
.attr("y1", (d) => d.source.y)
|
||||
.attr("x2", (d) => d.target.x)
|
||||
.attr("y2", (d) => d.target.y);
|
||||
|
||||
node.attr("transform", d => "translate(" + d.x + "," + d.y + ")");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
|
@ -1,14 +1,26 @@
|
|||
# This file is part of dnmapper, an AS--level mapping tool
|
||||
# Licensed under GNU General Public License v3 or later
|
||||
# Written by Sebastian Lohff (seba@someserver.de)
|
||||
from django.conf.urls import url
|
||||
|
||||
from django.conf.urls import url, include
|
||||
|
||||
from api import ASResource, CrawlResource, BorderRouterResource, AnnouncementResource
|
||||
from bgpdata import views as bgpdata_views
|
||||
|
||||
asResource = ASResource()
|
||||
crawlResource = CrawlResource()
|
||||
borderRouterResource = BorderRouterResource()
|
||||
announcementResource = AnnouncementResource()
|
||||
|
||||
urlpatterns = (
|
||||
url(r'^$', bgpdata_views.overview, name='overview'),
|
||||
url(r'^([0-9]+)/$', bgpdata_views.showMap, name='show_map'),
|
||||
url(r'^new/([0-9]+)/$', bgpdata_views.show_new_map, name='show_map'),
|
||||
url(r'^new/(?P<crawl_id>live)/$', bgpdata_views.show_new_map, name='show_live_map'),
|
||||
url(r'^lastseen/$', bgpdata_views.show_asn_last_seen, name='lastseen'),
|
||||
url(r'^$', bgpdata_views.overview),
|
||||
url(r'^([0-9]+)/$', bgpdata_views.showMap),
|
||||
|
||||
#url(r'^api/crawl/(?P<crawlID>\d+)/asses/$', 'bgpdata.api.asses'),
|
||||
#(r'^api/', include(asResource.urls)),
|
||||
url(r'^api/', include(asResource.urls)),
|
||||
url(r'^api/', include(crawlResource.urls)),
|
||||
url(r'^api/', include(borderRouterResource.urls)),
|
||||
url(r'^api/', include(announcementResource.urls)),
|
||||
)
|
||||
|
||||
|
|
|
@ -1,19 +1,16 @@
|
|||
# This file is part of dnmapper, an AS--level mapping tool
|
||||
# Licensed under GNU General Public License v3 or later
|
||||
# Written by Sebastian Lohff (seba@someserver.de)
|
||||
|
||||
from django.shortcuts import render
|
||||
from bgpdata.models import CrawlRun, AS, Peering
|
||||
from django.core.paginator import Paginator
|
||||
|
||||
from bgpdata.models import CrawlRun, AS, Peering, ASLastSeen
|
||||
from backend import crawler
|
||||
|
||||
|
||||
def overview(request):
|
||||
crawls = CrawlRun.objects.order_by("-startTime")
|
||||
crawlsPage = Paginator(crawls, 200)
|
||||
return render(request, 'bgpdata/overview.html', {"crawls": crawlsPage.page(1)})
|
||||
|
||||
|
||||
def showMap(request, crawlId):
|
||||
crawl = None
|
||||
try:
|
||||
|
@ -25,21 +22,3 @@ def showMap(request, crawlId):
|
|||
peerings = Peering.objects.filter(as1__crawl=crawl)
|
||||
|
||||
return render(request, 'bgpdata/map.html', {"crawl": crawl, 'ASses': ASses, 'peerings': peerings})
|
||||
|
||||
|
||||
def show_new_map(request, crawl_id):
|
||||
crawl = None
|
||||
if crawl_id == 'live':
|
||||
net = crawler.get_current_network()
|
||||
crawl = crawler.make_crawl_from_net(net)
|
||||
else:
|
||||
try:
|
||||
crawl = CrawlRun.objects.get(id=crawl_id)
|
||||
except CrawlRun.DoesNotExist:
|
||||
return render(request, "bgpdata/no-map-found.html", {"crawl_id": crawl_id})
|
||||
|
||||
return render(request, 'bgpdata/new_new_map.html', {"crawl": crawl})
|
||||
|
||||
|
||||
def show_asn_last_seen(request):
|
||||
return render(request, 'bgpdata/asn_last_seen.html', {'last_seen': ASLastSeen.objects.order_by("asn")})
|
||||
|
|
28
bin/conv.py
28
bin/conv.py
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
sys.path.append("..")
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dnmapper.settings")
|
||||
import django
|
||||
django.setup()
|
||||
|
||||
from backend.crawler import convert_crawl
|
||||
from bgpdata.models import CrawlRun
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-c", "--crawl-id", type=int)
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
crawl = CrawlRun.objects.get(pk=args.crawl_id)
|
||||
except CrawlRun.DoesNotExist:
|
||||
parser.error("CrawlRun with id {} does not exist".format(args.crawl_id))
|
||||
|
||||
convert_crawl(crawl)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
173
bin/crawl.py
173
bin/crawl.py
|
@ -1,10 +1,13 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python2
|
||||
# This file is part of dnmapper, an AS--level mapping tool
|
||||
# Licensed under GNU General Public License v3 or later
|
||||
# Written by Sebastian Lohff (seba@someserver.de)
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
# config
|
||||
LAST_SEEN_DAYS = 7
|
||||
|
||||
# prepare environment
|
||||
import sys
|
||||
sys.path.append("..")
|
||||
|
@ -19,10 +22,7 @@ from django.utils import timezone
|
|||
from django.db.models import Q, Max
|
||||
|
||||
from bgpdata.models import ConfigHost, CrawlRun, CrawlLog, AS, BorderRouter, Announcement, Peering, BorderRouterPair
|
||||
from backend import gobgp, cmk_parser
|
||||
|
||||
# config
|
||||
LAST_SEEN_DAYS = 7
|
||||
from routerparsers import getBGPData, RouterParserException
|
||||
|
||||
|
||||
def getOrCreateAS(crawl, number, online=True):
|
||||
|
@ -52,13 +52,11 @@ def main():
|
|||
print(" -- Getting data for host %s" % host)
|
||||
try:
|
||||
if host.checkMethod == 'CMK':
|
||||
data = [cmk_parser.getBGPData(host.ip, host.number)]
|
||||
elif host.checkMethod == 'GOBGP':
|
||||
data = gobgp.get_bgp_data(host.ip)
|
||||
data = getBGPData(host.ip, host.number)
|
||||
else:
|
||||
CrawlLog.log(crawl, "Method %s is not currently supported, skipping host" % host.checkMethod, host=host, severity=CrawlLog.ERROR)
|
||||
continue
|
||||
except cmk_parser.RouterParserException as e:
|
||||
except RouterParserException as e:
|
||||
msg = "Could not parse data for host: %s" % str(e)
|
||||
print("%s: %s" % (host, msg))
|
||||
CrawlLog.log(crawl, msg, host=host, severity=CrawlLog.ERROR)
|
||||
|
@ -69,82 +67,6 @@ def main():
|
|||
CrawlLog.log(crawl, msg, host=host, severity=CrawlLog.ERROR)
|
||||
continue
|
||||
|
||||
for elem in data:
|
||||
parseData(crawl, host, elem)
|
||||
|
||||
# 3. calculate missing data
|
||||
print(" -- Adding extra data from announcements...")
|
||||
# 3.1. use announcement data to find hidden peerings
|
||||
for announcement in Announcement.objects.filter(router__AS__crawl=crawl):
|
||||
path = announcement.ASPath.split(" ")
|
||||
if len(path) > 1:
|
||||
firstASno = path.pop(0)
|
||||
firstAS = getOrCreateAS(crawl, firstASno)
|
||||
while len(path) > 0:
|
||||
secondASno = path.pop(0)
|
||||
secondAS = getOrCreateAS(crawl, secondASno)
|
||||
|
||||
try:
|
||||
Peering.getPeering(firstAS, secondAS)
|
||||
except Peering.DoesNotExist:
|
||||
peering = Peering(as1=firstAS, as2=secondAS, origin=Peering.PATH)
|
||||
peering.save()
|
||||
|
||||
firstAS = secondAS
|
||||
|
||||
# 3.2 add ASses, routers and peerings from old crawlruns (last should suffice)
|
||||
# find
|
||||
print(" --> copy old ASses")
|
||||
timerangeStart = crawl.startTime - datetime.timedelta(LAST_SEEN_DAYS)
|
||||
oldASses = AS.objects.filter(online=True, crawl__startTime__gte=timerangeStart) \
|
||||
.values("number").annotate(lastSeen=Max('crawl_id')).filter(~Q(lastSeen=crawl.pk))
|
||||
|
||||
# 3.2.1. copy old asses
|
||||
print(" ----> create ASses")
|
||||
for oldASdata in oldASses:
|
||||
print(" ------> AS", oldASdata["number"])
|
||||
oldAS = AS.objects.get(number=oldASdata["number"], crawl=oldASdata["lastSeen"])
|
||||
|
||||
try:
|
||||
newAS = AS.objects.get(number=oldAS.number, crawl=crawl)
|
||||
if not newAS.online and not newAS.lastSeen:
|
||||
newAS.lastSeen = oldAS.crawl
|
||||
newAS.save()
|
||||
except Exception:
|
||||
newAS = AS(number=oldAS.number, crawl=crawl, lastSeen=oldAS.crawl, directlyCrawled=False, online=False)
|
||||
newAS.save()
|
||||
|
||||
# 3.2.2 copy peerings between old asses
|
||||
print(" ----> copy peerings")
|
||||
for oldASdata in oldASses:
|
||||
print(" ------> AS", oldASdata["number"])
|
||||
oldAS = AS.objects.get(number=oldASdata["number"], crawl=oldASdata["lastSeen"])
|
||||
for peering in oldAS.getPeerings():
|
||||
print(" --------> Peering %s <--> %s" % (peering.as1.number, peering.as2.number))
|
||||
peering = Peering(
|
||||
as1=AS.objects.get(number=peering.as1.number, crawl=crawl),
|
||||
as2=AS.objects.get(number=peering.as2.number, crawl=crawl),
|
||||
origin=peering.origin)
|
||||
peering.save()
|
||||
|
||||
# 3.3 FIXME: do we also want to have old peerings which do not exist anymore?
|
||||
|
||||
# 4. end crawl run
|
||||
crawl.endTime = timezone.now()
|
||||
crawl.save()
|
||||
|
||||
# additional data
|
||||
crawl.asCount = crawl.as_set.count()
|
||||
crawl.asOnlineCount = crawl.as_set.filter(online=True).count()
|
||||
crawl.asOfflineCount = crawl.as_set.filter(online=False).count()
|
||||
crawl.peeringCount = Peering.objects.filter(Q(as1__crawl=crawl) | Q(as2__crawl=crawl)).count()
|
||||
crawl.save()
|
||||
|
||||
print(" !! Done")
|
||||
CrawlLog.log(crawl, "Crawl completed", severity=CrawlLog.INFO)
|
||||
|
||||
|
||||
def parseData(crawl, host, data):
|
||||
print(" -- parsing...")
|
||||
|
||||
currASno = int(data["local_as"])
|
||||
|
@ -185,17 +107,15 @@ def parseData(crawl, host, data):
|
|||
try:
|
||||
neighRouter = BorderRouter.objects.get(AS=neighAS, routerID=peer["BGP"]["neighbor_id"])
|
||||
except BorderRouter.DoesNotExist:
|
||||
neighRouter = BorderRouter(AS=neighAS, routerID=peer["BGP"]["neighbor_id"],
|
||||
pingable=False, reachable=False)
|
||||
neighRouter = BorderRouter(AS=neighAS, routerID=peer["BGP"]["neighbor_id"], pingable=False, reachable=False)
|
||||
neighRouter.save()
|
||||
try:
|
||||
BorderRouterPair.getPairing(peering, currRouter, neighRouter)
|
||||
except BorderRouterPair.DoesNotExist:
|
||||
pairs = BorderRouterPair.objects.filter(Q(peering=peering) & (Q(router1=neighRouter, router2=None) |
|
||||
Q(router1=None, router2=neighRouter)))
|
||||
pairs = BorderRouterPair.objects.filter(Q(peering=peering) & (Q(router1=neighRouter, router2=None)|Q(router1=None, router2=neighRouter)))
|
||||
if pairs.count() > 0:
|
||||
pair = pairs[0]
|
||||
if pair.router1 is None:
|
||||
if pair.router1 == None:
|
||||
pair.router1 = currRouter
|
||||
else:
|
||||
pair.router2 = currRouter
|
||||
|
@ -213,7 +133,6 @@ def parseData(crawl, host, data):
|
|||
|
||||
crawlAS = currAS
|
||||
if len(route["path"]) > 0:
|
||||
route["path"] = list(map(str, route["path"]))
|
||||
crawlAS = getOrCreateAS(crawl, route["path"][0])
|
||||
originAS = getOrCreateAS(crawl, route["path"][-1])
|
||||
ip, prefix = route["prefix"].split("/")
|
||||
|
@ -223,9 +142,77 @@ def parseData(crawl, host, data):
|
|||
a.save()
|
||||
else:
|
||||
print(" !! No routes found in host output")
|
||||
CrawlLog.log(crawl, "No routes found in host output (no bgp feed included?)",
|
||||
host=host, severity=CrawlLog.WARN)
|
||||
CrawlLog.log(crawl, "No routes found in host output (no bgp feed included?)", host=host, severity=CrawlLog.WARN)
|
||||
|
||||
# 3. calculate missing data
|
||||
print(" -- Adding extra data from announcements...")
|
||||
# 3.1. use announcement data to find hidden peerings
|
||||
for announcement in Announcement.objects.filter(router__AS__crawl=crawl):
|
||||
path = announcement.ASPath.split(" ")
|
||||
if len(path) > 1:
|
||||
firstASno = path.pop(0)
|
||||
firstAS = getOrCreateAS(crawl, firstASno)
|
||||
while len(path) > 0:
|
||||
secondASno = path.pop(0)
|
||||
secondAS = getOrCreateAS(crawl, secondASno)
|
||||
|
||||
try:
|
||||
Peering.getPeering(firstAS, secondAS)
|
||||
except Peering.DoesNotExist:
|
||||
peering = Peering(as1=firstAS, as2=secondAS, origin=Peering.PATH)
|
||||
peering.save()
|
||||
|
||||
firstAS = secondAS
|
||||
|
||||
# 3.2 add ASses, routers and peerings from old crawlruns (last should suffice)
|
||||
# find
|
||||
print(" --> copy old ASses")
|
||||
timerangeStart = crawl.startTime - datetime.timedelta(LAST_SEEN_DAYS)
|
||||
oldASses = AS.objects.filter(online=True, crawl__startTime__gte=timerangeStart).values("number").annotate(lastSeen=Max('crawl_id')).filter(~Q(lastSeen=crawl.pk))
|
||||
|
||||
# 3.2.1. copy old asses
|
||||
print(" ----> create ASses")
|
||||
for oldASdata in oldASses:
|
||||
print(" ------> AS", oldASdata["number"])
|
||||
oldAS = AS.objects.get(number=oldASdata["number"], crawl=oldASdata["lastSeen"])
|
||||
|
||||
try:
|
||||
newAS = AS.objects.get(number=oldAS.number, crawl=crawl)
|
||||
if not newAS.online and not newAS.lastSeen:
|
||||
newAS.lastSeen = oldAS.crawl
|
||||
newAS.save()
|
||||
except:
|
||||
newAS = AS(number=oldAS.number, crawl=crawl, lastSeen=oldAS.crawl, directlyCrawled=False, online=False)
|
||||
newAS.save()
|
||||
|
||||
# 3.2.2 copy peerings between old asses
|
||||
print(" ----> copy peerings")
|
||||
for oldASdata in oldASses:
|
||||
print(" ------> AS", oldASdata["number"])
|
||||
oldAS = AS.objects.get(number=oldASdata["number"], crawl=oldASdata["lastSeen"])
|
||||
for peering in oldAS.getPeerings():
|
||||
print(" --------> Peering %s <--> %s" % (peering.as1.number, peering.as2.number))
|
||||
peering = Peering(
|
||||
as1=AS.objects.get(number=peering.as1.number, crawl=crawl),
|
||||
as2=AS.objects.get(number=peering.as2.number, crawl=crawl),
|
||||
origin=peering.origin)
|
||||
peering.save()
|
||||
|
||||
# 3.3 FIXME: do we also want to have old peerings which do not exist anymore?
|
||||
|
||||
# 4. end crawl run
|
||||
crawl.endTime = timezone.now()
|
||||
crawl.save()
|
||||
|
||||
# additional data
|
||||
crawl.asCount = crawl.as_set.count()
|
||||
crawl.asOnlineCount = crawl.as_set.filter(online=True).count()
|
||||
crawl.asOfflineCount = crawl.as_set.filter(online=False).count()
|
||||
crawl.peeringCount = Peering.objects.filter(Q(as1__crawl=crawl)|Q(as2__crawl=crawl)).count()
|
||||
crawl.save()
|
||||
|
||||
print(" !! Done")
|
||||
CrawlLog.log(crawl, "Crawl completed", severity=CrawlLog.INFO)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
sys.path.append("..")
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "dnmapper.settings")
|
||||
import django
|
||||
django.setup()
|
||||
|
||||
from backend.crawler import crawl
|
||||
|
||||
|
||||
def main():
|
||||
crawl()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,353 @@
|
|||
#!/usr/bin/env python
|
||||
# This file is part of dnmapper, an AS--level mapping tool
|
||||
# Licensed under GNU General Public License v3 or later
|
||||
# Written by Sebastian Lohff (seba@someserver.de)
|
||||
from __future__ import print_function
|
||||
|
||||
import re
|
||||
import socket
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
class RouterParserException(Exception):
|
||||
pass
|
||||
|
||||
def err(msg):
|
||||
raise RouterParserException(msg)
|
||||
|
||||
|
||||
def getBGPData(ip, asno):
|
||||
rawData = getDataFromHost(ip)
|
||||
if not rawData:
|
||||
err("Could not get data from host (empty response)")
|
||||
|
||||
router = parseBGPData(rawData, asno)
|
||||
|
||||
router["ip"] = ip
|
||||
|
||||
return router
|
||||
|
||||
def getDataFromHost(ip):
|
||||
socket.setdefaulttimeout(5)
|
||||
x = socket.socket()
|
||||
x.connect((ip, 6556))
|
||||
f = x.makefile()
|
||||
data = f.read()
|
||||
x.close()
|
||||
|
||||
return data
|
||||
|
||||
def parseBGPData(raw, asno):
|
||||
d = re.search(r"(?:^|\n)<<<(quagga|bird)>>>\n(.*?)(?:$|<<<[^\n]+>>>)", raw, re.DOTALL)
|
||||
|
||||
if not d:
|
||||
err("Data not found in check mk output")
|
||||
|
||||
# mkify
|
||||
raw = d.group(2).split("\n")
|
||||
arr = filter(lambda _z: _z, map(lambda _y: filter(lambda _x: _x, re.split(r"\s+", _y)), raw))
|
||||
|
||||
# parse for bird/quagga
|
||||
result = None
|
||||
|
||||
if d.group(1) == "quagga":
|
||||
result = parseQuagga(arr, raw, asno)
|
||||
else:
|
||||
result = parseBird(arr, raw, asno)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def parseQuagga(data, raw, asno):
|
||||
status = _quaggaFindCommand(data, "show ip bgp sum")
|
||||
|
||||
if status[0][0:3] == ['IPv4', 'Unicast', 'Summary:']:
|
||||
del(status[0])
|
||||
|
||||
if status[0][0:3] != ['BGP', 'router', 'identifier']:
|
||||
print(status)
|
||||
err("Couldn't find router id in quagga output")
|
||||
|
||||
peers = _quaggaFindNeighbors(data)
|
||||
if asno and int(asno) != int(status[0][7]):
|
||||
err("AS number (%s) does not match as number from quagga (%s)" % (asno, status[0][7]))
|
||||
|
||||
routes = _quaggaFindRoutes(raw)
|
||||
|
||||
return {"local_id": status[0][3].strip(","), "local_as": int(status[0][7]), "peers": peers, "routes": routes}
|
||||
|
||||
def parseBird(data, raw, asno):
|
||||
status = _birdFindTable(data, "show status")
|
||||
if status[2][0] != "1011-Router":
|
||||
err("Couldn't find router id in bird output")
|
||||
peers = filter(lambda _x: _x["type"] == "BGP", _birdMakeProtocols(data))
|
||||
|
||||
if asno == None:
|
||||
err("Host is bird")
|
||||
# FIXME
|
||||
|
||||
routes = _birdFindRoutes(data)
|
||||
|
||||
return {"local_id": status[2][3], "local_as": int(asno), "peers": peers, "routes": routes}
|
||||
|
||||
def _birdFindTable(info, command):
|
||||
""" find command output of a bird command, e.g. "show bgp neighbors" """
|
||||
command = ["bird>"] + command.split(" ")
|
||||
commandInfo = []
|
||||
editNextLine = False
|
||||
for line in info:
|
||||
if not commandInfo:
|
||||
if line == command:
|
||||
commandInfo.append(line)
|
||||
editNextLine = True
|
||||
else:
|
||||
if editNextLine:
|
||||
editNextLine = False
|
||||
commandInfo.append(line[1:])
|
||||
elif line[0] == "bird>":
|
||||
return commandInfo
|
||||
else:
|
||||
commandInfo.append(line)
|
||||
return []
|
||||
|
||||
def _birdFindProtocols(info):
|
||||
""" return a list of tuples (protoname, protoinfo) """
|
||||
protocolTable = _birdFindTable(info, "show protocols all")
|
||||
protocols = OrderedDict()
|
||||
currProto = None
|
||||
for line in protocolTable[2:]:
|
||||
if line[0][0:4] == "1002":
|
||||
currProto = line[0][5:]
|
||||
protocols[currProto] = [[currProto] + line[1:]]
|
||||
elif currProto == None:
|
||||
err("No proto selected, couldn't parse line:", line)
|
||||
else:
|
||||
protocols[currProto].append(line)
|
||||
|
||||
return protocols
|
||||
|
||||
def _birdMakeProtocols(info):
|
||||
""" Parse birds show protocols all output """
|
||||
# proto: name, type, description, state (up/down?), up-since
|
||||
# routes imported, exported, preferred
|
||||
# also: routing stats (
|
||||
# bgp special stuff: state, neighbor (address, as, id) (id not available when down)
|
||||
# state (established, active)
|
||||
# if error, last error is avilable
|
||||
protocols = []
|
||||
for proto, data in _birdFindProtocols(info).iteritems():
|
||||
protoInfo = {
|
||||
"name": proto,
|
||||
"type": data[0][1],
|
||||
"table": data[0][2],
|
||||
"state": data[0][3],
|
||||
"last_change": data[0][4],
|
||||
"info": " ".join(data[0][5:]),
|
||||
"description": " ".join(data[1][2:]),
|
||||
"routes": {
|
||||
"imported": data[5][1],
|
||||
"exported": data[5][3],
|
||||
"preferred": data[5][5],
|
||||
}
|
||||
}
|
||||
if protoInfo["type"] == "BGP":
|
||||
found = False
|
||||
for n, line in enumerate(data):
|
||||
if line[0:2] == ["BGP", "state:"]:
|
||||
found = True
|
||||
protoInfo["BGP"] = {
|
||||
"state": data[n][2],
|
||||
"online": data[n][2] == "Established",
|
||||
"neighbor_address": data[n+1][2],
|
||||
"neighbor_as": int(data[n+2][2]),
|
||||
"neighbor_id": data[n+3][2] if len(data) > n+3 and data[n+3][0:2] == ["Neighbor", "ID:"] else None,
|
||||
"last_error": " ".join(data[n+3][2:]) if len(data) > n+3 and data[n+3][0:2] == ["Last", "error:"] else None,
|
||||
}
|
||||
|
||||
if not found:
|
||||
protoInfo["BGP"] = None
|
||||
|
||||
protocols.append(protoInfo)
|
||||
|
||||
return protocols
|
||||
|
||||
|
||||
def _birdFindRoutes(info):
|
||||
output = _birdFindTable(info, "show route all")
|
||||
if len(output) < 1:
|
||||
# no data found
|
||||
return None
|
||||
|
||||
def handleCandidate(routes, candidate):
|
||||
if candidate:
|
||||
# path, nexthop, network
|
||||
for key in ["path", "nexthop", "network", "iBGP"]:
|
||||
if key not in candidate:
|
||||
return
|
||||
route = {"prefix": candidate["network"], "nexthop": candidate["nexthop"], "path": candidate["path"], "iBGP": candidate["iBGP"]}
|
||||
routes.append(route)
|
||||
pass
|
||||
|
||||
routes = []
|
||||
candidate = None
|
||||
lastIP = None
|
||||
for line in output:
|
||||
if line[0].startswith("1007-"):
|
||||
# new route!
|
||||
handleCandidate(routes, candidate)
|
||||
if line[0] != "1007-":
|
||||
# line has a network, use it!
|
||||
lastIP = line[0][5:]
|
||||
candidate = {"network": lastIP, "iBGP": None}
|
||||
|
||||
elif candidate is not None:
|
||||
# search bgp attributes
|
||||
if line[0] == "1012-":
|
||||
pass
|
||||
k, v = line[1], line[2:]
|
||||
else:
|
||||
k, v = line[0], line[1:]
|
||||
|
||||
k = k.rstrip(":")
|
||||
if k == "BGP.next_hop":
|
||||
candidate["nexthop"] = v[0]
|
||||
elif k == "BGP.as_path":
|
||||
candidate["path"] = v
|
||||
|
||||
|
||||
handleCandidate(routes, candidate)
|
||||
|
||||
return routes
|
||||
|
||||
|
||||
def _quaggaFindCommand(info, cmd):
|
||||
# ['core-frunde#', 'show', 'ip', 'bgp', 'sum']
|
||||
# ['core-frunde#', 'show', 'ip', 'bgp', 'neighbors']
|
||||
output = []
|
||||
cmd = cmd.split(" ")
|
||||
prompt = None
|
||||
for line in info:
|
||||
if line[1:] == cmd:
|
||||
prompt = line[0]
|
||||
elif line[0] == prompt:
|
||||
# done
|
||||
return output
|
||||
elif prompt != None:
|
||||
output.append(line)
|
||||
|
||||
err("Could not find command '%s' in output" % " ".join(cmd))
|
||||
|
||||
def _quaggaFindNeighbors(info):
|
||||
#['BGP', 'neighbor', 'is', '10.50.1.2,', 'remote', 'AS', '65001,', 'local', 'AS', '65001,', 'internal', 'link']
|
||||
output = _quaggaFindCommand(info, "show ip bgp neighbors")
|
||||
start = ["BGP", "neighbor", "is"]
|
||||
|
||||
curr = None
|
||||
rawNeighbors = []
|
||||
for line in output:
|
||||
if line[0:3] == start:
|
||||
if curr:
|
||||
rawNeighbors.append(curr)
|
||||
curr = [line]
|
||||
elif curr:
|
||||
curr.append(line)
|
||||
else:
|
||||
err("Could not find start of neighbors")
|
||||
|
||||
if curr:
|
||||
rawNeighbors.append(curr)
|
||||
curr = None
|
||||
|
||||
neighbors = []
|
||||
neighborDict = OrderedDict()
|
||||
for raw in rawNeighbors:
|
||||
descrIdx = 1 if raw[1][0] == "Description:" else 0
|
||||
if raw[descrIdx + 1][0] == "Hostname:":
|
||||
descrIdx += 1
|
||||
|
||||
peerdict = {
|
||||
"neighbor_address": raw[0][3].rstrip(","),
|
||||
"neighbor_as": int(raw[0][6].rstrip(",")),
|
||||
"local_as": int(raw[0][9].rstrip(",")),
|
||||
"description": " ".join(raw[1][1:]) if descrIdx else "No description",
|
||||
"neighbor_id": raw[1+descrIdx][6].strip(","),
|
||||
"state": raw[2+descrIdx][3].strip(","),
|
||||
"routes": {
|
||||
"imported": 0,
|
||||
},
|
||||
"BGP": {
|
||||
"state": raw[2+descrIdx][3].strip(","),
|
||||
"online": raw[2+descrIdx][3].strip(",") == "Established",
|
||||
"neighbor_id": raw[1+descrIdx][6].strip(","),
|
||||
"neighbor_address": raw[0][3].rstrip(","),
|
||||
"neighbor_as": int(raw[0][6].rstrip(",")),
|
||||
"state": raw[2+descrIdx][3].strip(","),
|
||||
},
|
||||
}
|
||||
|
||||
for line in raw:
|
||||
if line[1:3] == ["accepted", "prefixes"]:
|
||||
# woooo
|
||||
peerdict["routes"]["imported"] = int(line[0])
|
||||
break
|
||||
|
||||
neighbors.append(peerdict)
|
||||
neighborDict[peerdict["neighbor_address"]] = peerdict
|
||||
|
||||
return neighbors
|
||||
|
||||
def _quaggaFindRoutes(raw):
|
||||
# from # show ip bgp to Total number of prefixes XX
|
||||
# BGP table version is 0, local router ID is 10.50.0.1
|
||||
# *> 10.3.14.0/27 10.75.0.22 0 65002 65112 i
|
||||
cmdre = re.compile(r"^([^\s#]+#) show ip bgp$")
|
||||
routere = re.compile(r"^(?P<status>.)(?P<status2>.)(?P<origin>.)(?P<network>[0-9./]+)?\s+(?P<nexthop>[0-9./]+)[\s0-9i?]+$")
|
||||
|
||||
# find output
|
||||
output = []
|
||||
prompt = None
|
||||
for line in raw:
|
||||
if not prompt:
|
||||
m = cmdre.match(line)
|
||||
if m:
|
||||
prompt = m.group(1) + " "
|
||||
else:
|
||||
if line.startswith(prompt):
|
||||
break
|
||||
else:
|
||||
output.append(line)
|
||||
|
||||
if len(output) < 1:
|
||||
# no data found
|
||||
return None
|
||||
|
||||
routes = []
|
||||
foundTable = False
|
||||
lastIP = None
|
||||
for line in output:
|
||||
if not foundTable:
|
||||
if line.endswith("Metric LocPrf Weight Path"):
|
||||
foundTable = True
|
||||
else:
|
||||
if line != '':
|
||||
if line.startswith("Total number of prefixes") or line.startswith("Displayed "):
|
||||
break
|
||||
else:
|
||||
# parse one route line
|
||||
#print(line)
|
||||
m = routere.match(line)
|
||||
d = m.groupdict()
|
||||
if d["network"]:
|
||||
lastIP = d["network"]
|
||||
else:
|
||||
d["network"] = lastIP
|
||||
|
||||
# "parse" path (everything after 61 chars, but no i)
|
||||
path = filter(lambda _x: _x not in ('', 'i'), line[61:].split(" "))
|
||||
|
||||
# currently skip incomplete routes
|
||||
if '?' not in path:
|
||||
route = {"prefix": d["network"], "nexthop": d["nexthop"], "path": path, "iBGP": d["origin"] == "i"}
|
||||
routes.append(route)
|
||||
|
||||
return routes
|
|
@ -41,18 +41,18 @@ INSTALLED_APPS = (
|
|||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'rest_framework',
|
||||
'bgpdata',
|
||||
'tastypie',
|
||||
)
|
||||
|
||||
API_LIMIT_PER_PAGE = 100
|
||||
|
||||
MIDDLEWARE = (
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
)
|
||||
|
|
|
@ -5,10 +5,7 @@
|
|||
from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
from django.views.generic import RedirectView
|
||||
|
||||
import bgpdata.urls
|
||||
import bgpdata.api_urls
|
||||
import apiv2.urls
|
||||
|
||||
urlpatterns = (
|
||||
# Examples:
|
||||
|
@ -16,8 +13,6 @@ urlpatterns = (
|
|||
# url(r'^blog/', include('blog.urls')),
|
||||
url(r'^$', RedirectView.as_view(url='/map/')),
|
||||
url(r'^map/', include(bgpdata.urls)),
|
||||
url(r'^api/v1/', include(bgpdata.api_urls)),
|
||||
url(r'^api/v2/', include(apiv2.urls)),
|
||||
|
||||
url(r'^admin/', admin.site.urls),
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
)
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
django<3
|
||||
django==1.11.25
|
||||
django-tastypie
|
||||
django-rest-framework
|
||||
django-filter
|
||||
networkx
|
||||
grpcio
|
||||
|
||||
|
|
|
@ -26,15 +26,29 @@
|
|||
</div>
|
||||
<div id="navbar" class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li{% if request.resolver_match.url_name == "show_map" or request.resolver_match.url_name == "overview" %} class="active"{%endif%}><a href="/">Crawl Data</a></li>
|
||||
<li{% if request.resolver_match.url_name == "show_live_map" %} class="active"{%endif%}><a href="{% url "show_live_map" crawl_id="live" %}">Live View</a></li>
|
||||
<li{% if request.resolver_match.url_name == 'lastseen' %} class="active"{%endif%}><a href="{% url "lastseen" %}">AS Status</a></li>
|
||||
<li class="active"><a href="/">Crawl Data</a></li>
|
||||
<!--
|
||||
<li><a href="#about">About</a></li>
|
||||
<li><a href="#contact">Contact</a></li>
|
||||
<li class="dropdown">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">Dropdown <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li><a href="#">Action</a></li>
|
||||
<li><a href="#">Another action</a></li>
|
||||
<li><a href="#">Something else here</a></li>
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-header">Nav header</li>
|
||||
<li><a href="#">Separated link</a></li>
|
||||
<li><a href="#">One more separated link</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
-->
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="{% block container_class %}container{% endblock %}">
|
||||
<div class="container">
|
||||
{% block body %}{% endblock %}
|
||||
</div>
|
||||
</body>
|
||||
|
|
Loading…
Reference in New Issue