# 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.db import models from django.db.models import Q # Create your models here. 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) 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() 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 countAS(self): return self.asCount def countASOnline(self): return self.asOnlineCount def countASOffline(self): return self.asOfflineCount def countPeerings(self): return self.peeringCount class CrawlLog(models.Model): INFO = 'INFO' ERROR = 'ERROR' DEBUG = 'DEBUG' WARN = 'WARN' SEVERITY = ( (INFO, 'info'), (ERROR, 'error'), (DEBUG, 'debug'), (WARN, 'warning'), ) crawl = models.ForeignKey(CrawlRun, on_delete=models.CASCADE) 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) message = models.TextField() @staticmethod def log(crawl, msg, severity=None, host=None): if not severity: severity = CrawlLog.ERROR log = CrawlLog() log.crawl = crawl log.message = msg log.severity = severity log.host = host log.save() def __str__(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) class AS(models.Model): # asno crawl = models.ForeignKey(CrawlRun, on_delete=models.CASCADE) 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) class Meta: unique_together = (('crawl', 'number'),) index_together = ( ('crawl', 'number'), ) def __str__(self): return "AS %s (crawl %d)" % (self.number, self.crawl.pk) def setOnline(self): if not self.online: self.online = True self.lastSeen = None self.save() def getPeerings(self): return Peering.objects.filter(Q(as1=self) | Q(as2=self)) def getAnnouncedPrefixes(self): return list(set(map(lambda _x: "%(ip)s/%(prefix)s" % _x, self.announcement_set.all().values('ip', 'prefix')))) def formatLastSeen(self): 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) routerID = models.GenericIPAddressField() pingable = models.BooleanField(default=False) reachable = models.BooleanField(default=False) def __str__(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) class Announcement(models.Model): router = models.ForeignKey(BorderRouter, on_delete=models.CASCADE) ip = models.GenericIPAddressField() prefix = models.IntegerField() # 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) class Peering(models.Model): DIRECT = 'direct' PATH = 'path' ORIGIN = ( (PATH, 'BGP Path'), (DIRECT, 'Direct Connection'), ) index_together = ( ('as1', 'as2'), ) as1 = models.ForeignKey(AS, related_name='peering1', on_delete=models.CASCADE) as2 = models.ForeignKey(AS, related_name='peering2', on_delete=models.CASCADE) 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 containsAS(self, AS): return AS in (self.as1, self.as2) @staticmethod def getPeering(as1, as2): """ Find matching peering """ try: return Peering.objects.get(as1=as1, as2=as2) 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) def __str__(self): return "%s <--> %s (crawl %d)" % (self.router1, self.router2, self.router1.AS.crawl.pk) @staticmethod def getPairing(peering, router1, router2): try: 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)