diff options
Diffstat (limited to 'rpkid')
-rw-r--r-- | rpkid/portal-gui/scripts/rpkigui-rcynic.py | 31 | ||||
-rw-r--r-- | rpkid/rpki/gui/app/views.py | 28 | ||||
-rw-r--r-- | rpkid/rpki/gui/cacheview/models.py | 153 |
3 files changed, 140 insertions, 72 deletions
diff --git a/rpkid/portal-gui/scripts/rpkigui-rcynic.py b/rpkid/portal-gui/scripts/rpkigui-rcynic.py index 3dc0d9bd..abf56ca1 100644 --- a/rpkid/portal-gui/scripts/rpkigui-rcynic.py +++ b/rpkid/portal-gui/scripts/rpkigui-rcynic.py @@ -21,6 +21,7 @@ import time, vobject from rpki.gui.cacheview import models from rpki.rcynic import rcynic_xml_iterator, label_iterator from rpki.sundial import datetime +import rpki from django.db import transaction import django.db.models @@ -55,7 +56,12 @@ class rcynic_object(object): mtime = os.stat(vs.filename)[8] if mtime != inst.mtime: inst.mtime = mtime - obj = vs.obj # causes object to be lazily loaded + try: + obj = vs.obj # causes object to be lazily loaded + except rpki.POW._der.DerError, e: + print >>sys.stderr, '[error] Caught %s while processing %s: %s' % (type(e), vs.filename, e) + return True + inst.not_before = obj.notBefore.to_sql() inst.not_after = obj.notAfter.to_sql() if debug: @@ -93,6 +99,10 @@ class rcynic_object(object): return True +# munge IPv6 addresses +def munge(family, value): + return value if family == 4 else (value >> 65) & 0x7fffffffffffffffL + class rcynic_cert(rcynic_object): model_class = models.Cert @@ -124,7 +134,8 @@ class rcynic_cert(rcynic_object): if debug: sys.stderr.write('processing %s\n' % rng) - attrs = { 'family': family, 'min': str(rng.min), 'max': str(rng.max) } + attrs = { 'family': family, 'min': munge(family, rng.min), + 'max': munge(family, rng.max) } q = models.AddressRange.objects.filter(**attrs) if not q: obj.addresses.create(**attrs) @@ -145,8 +156,8 @@ class rcynic_roa(rcynic_object): family = fam_map[pfxset.__class__.__name__] for pfx in pfxset: attrs = { 'family' : family, - 'prefix': str(pfx.prefix), - 'bits' : pfx.prefixlen, + 'prefix_min': munge(family, pfx.min()), + 'prefix_max': munge(family, pfx.max()), 'max_length': pfx.max_prefixlen } q = models.ROAPrefix.objects.filter(**attrs) if not q: @@ -195,8 +206,10 @@ def process_cache(root, xml_file): # need to defer processing this object, most likely because # the <validation_status/> element for the signing cert hasn't # been seen yet - if not dispatch[vs.file_class.__name__](vs): - defer.append(vs) + with transaction.commit_on_success(): + dispatch[vs.file_class.__name__](vs) + #if not dispatch[vs.file_class.__name__](vs): + # defer.append(vs) # garbage collection # remove all objects which have no ValidationStatus references, which @@ -215,6 +228,7 @@ def process_cache(root, xml_file): stop = time.time() sys.stdout.write('elapsed time %d seconds.\n' % (stop - start)) +@transaction.commit_on_success def process_labels(xml_file): if debug: sys.stderr.write('updating labels...\n') @@ -249,8 +263,7 @@ if __name__ == '__main__': if options.debug: debug = True - with transaction.commit_on_success(): - process_labels(options.logfile) - process_cache(options.root, options.logfile) + process_labels(options.logfile) + process_cache(options.root, options.logfile) # vim:sw=4 ts=8 diff --git a/rpkid/rpki/gui/app/views.py b/rpkid/rpki/gui/app/views.py index 4e89dcda..b96cbff2 100644 --- a/rpkid/rpki/gui/app/views.py +++ b/rpkid/rpki/gui/app/views.py @@ -924,19 +924,27 @@ def route_view(request): qs = rpki.gui.routeview.models.RouteOrigin.objects.filter(prefix_min__gte=r.min, prefix_max__lte=r.max) for obj in qs: # determine the validation status of each route - obj.status_label = 'warning' - obj.status = 'Unknown' + # see draft-sidr-roa-validation-10 + + # 1. fetch all covering ROAs + # FIXME: need to munge IPv6 address prior to this query! + roas = rpki.gui.cacheview.models.ROAPrefix.objects.filter(prefix_min__lte=obj.prefix_min, + prefix_max__gte=obj.prefix_max, family=obj.family) + # 2. if there are candidate set is empty, end with invalid + if not roas.exists(): + obj.status = 'unknown' + obj.status_label = 'warning' + # 3. if any candidate roa matches the origin AS and max_length, end with valid + elif roas.filter(roas__asid=obj.asn, max_length__gte=obj.prefixlen()).exists(): + obj.status_label = 'success' + obj.status = 'valid' + # 4. otherwise the route is invalid + else: + obj.status_label = 'important' + obj.status = 'invalid' routes.append(obj) -# status = 'Not Found' -# status_id = 'notfound' -# -# roas = rpki.gui.cacheview.models.ROAPrefix.objects.filter() -# -# obj.status = status -# obj.status_id = status_id - return render('rpkigui/routes_view.html', { 'routes': routes }, request) # vim:sw=4 ts=8 expandtab diff --git a/rpkid/rpki/gui/cacheview/models.py b/rpkid/rpki/gui/cacheview/models.py index 077a28ff..dfeb4e0b 100644 --- a/rpkid/rpki/gui/cacheview/models.py +++ b/rpkid/rpki/gui/cacheview/models.py @@ -19,8 +19,8 @@ import time from django.db import models -from rpki.resource_set import resource_range_ipv4, resource_range_ipv6 -from rpki.exceptions import MustBePrefix +import rpki.ipaddrs +import rpki.resource_set class TelephoneField(models.CharField): def __init__(self, *args, **kwargs): @@ -28,40 +28,48 @@ class TelephoneField(models.CharField): models.CharField.__init__(self, *args, **kwargs) class AddressRange(models.Model): - family = models.IntegerField() - min = models.IPAddressField(db_index=True) - max = models.IPAddressField(db_index=True) + """Represents an IP address range. + + The db backend doesn't support unsigned 64-bit integer, so store + the /63 in the database, and have the display function regenerate + the full value. since nothing larger than a /48 should be + announced globally, it should be ok to pad the lower 65 bits of + `max` with 1s. """ + + family = models.PositiveSmallIntegerField(null=False) + min = models.BigIntegerField(null=False) + max = models.BigIntegerField(null=False) + + def get_min_display(self): + "Return the min address value as an rpki.ipaddr object." + return rpki.ipaddrs.v4addr(self.min) if self.family == 4 else rpki.ipaddrs.v6addr(self.min << 65) + + def get_max_display(self): + "Return the max address value as an rpki.ipaddr object." + # FIXME this may fail for an IPv6 /64 single block, since we + # don't store the lower 65 bits in the database + return rpki.ipaddrs.v4addr(self.max) if self.family == 4 else rpki.ipaddrs.v6addr((self.max << 65) | 0x1ffffffffffffffffL) class Meta: ordering = ('family', 'min', 'max') - unique_together = ('family', 'min', 'max') @models.permalink def get_absolute_url(self): return ('rpki.gui.cacheview.views.addressrange_detail', [str(self.pk)]) - def __unicode__(self): - if self.min == self.max: - return u'%s' % self.min - - if self.family == 4: - r = resource_range_ipv4.from_strings(self.min, self.max) - elif self.family == 6: - r = resource_range_ipv6.from_strings(self.min, self.max) + def as_resource_range(self): + cls = rpki.resource_set.resource_range_ipv4 if self.family == 4 else rpki.resource_set.resource_range_ipv6 + return cls(self.get_min_display(), self.get_max_display()) - try: - prefixlen = r.prefixlen() - except MustBePrefix: - return u'%s-%s' % (self.min, self.max) - return u'%s/%d' % (self.min, prefixlen) + def __unicode__(self): + return u'%s' % self.as_resource_range() class ASRange(models.Model): - min = models.PositiveIntegerField(db_index=True) - max = models.PositiveIntegerField(db_index=True) + min = models.PositiveIntegerField(null=False) + max = models.PositiveIntegerField(null=False) class Meta: ordering = ('min', 'max') - #unique_together = ('min', 'max') def __unicode__(self): if self.min == self.max: @@ -81,9 +89,9 @@ class ValidationLabel(models.Model): Represents a specific error condition defined in the rcynic XML output file. """ - label = models.CharField(max_length=30, db_index=True, unique=True) - status = models.CharField(max_length=255) - kind = models.PositiveSmallIntegerField(choices=kinds) + label = models.CharField(max_length=79, db_index=True, unique=True) + status = models.CharField(max_length=255, null=False) + kind = models.PositiveSmallIntegerField(choices=kinds, null=False) def __unicode__(self): return self.label @@ -95,9 +103,9 @@ generations = list(enumerate(('current', 'backup'))) generations_dict = dict((val, key) for (key, val) in generations) class ValidationStatus(models.Model): - timestamp = models.DateTimeField() + timestamp = models.DateTimeField(null=False) generation = models.PositiveSmallIntegerField(choices=generations, null=True) - status = models.ForeignKey('ValidationLabel') + status = models.ForeignKey('ValidationLabel', null=False) class Meta: abstract = True @@ -109,20 +117,20 @@ class SignedObject(models.Model): value for the 'related_name' attribute. """ # attributes from rcynic's output XML file - uri = models.URLField(unique=True, db_index=True) + uri = models.URLField(unique=True, db_index=True, null=False) # on-disk file modification time - mtime = models.PositiveIntegerField(default=0) + mtime = models.PositiveIntegerField(default=0, null=False) # SubjectName - name = models.CharField(max_length=255) + name = models.CharField(max_length=255, null=False) # value from the SKI extension - keyid = models.CharField(max_length=50, db_index=True) + keyid = models.CharField(max_length=60, db_index=True, null=False) # validity period from EE cert which signed object - not_before = models.DateTimeField() - not_after = models.DateTimeField() + not_before = models.DateTimeField(null=False) + not_after = models.DateTimeField(null=False) class Meta: abstract = True @@ -161,41 +169,80 @@ class Cert(SignedObject): addresses = models.ManyToManyField(AddressRange, related_name='certs') asns = models.ManyToManyField(ASRange, related_name='certs') issuer = models.ForeignKey('Cert', related_name='children', null=True, blank=True) - sia = models.CharField(max_length=255) + sia = models.CharField(max_length=255, null=False) @models.permalink def get_absolute_url(self): return ('rpki.gui.cacheview.views.cert_detail', [str(self.pk)]) class ValidationStatus_Cert(ValidationStatus): - cert = models.ForeignKey('Cert', related_name='statuses') + cert = models.ForeignKey('Cert', related_name='statuses', + null=False) class ROAPrefix(models.Model): - family = models.PositiveIntegerField() - prefix = models.IPAddressField() - bits = models.PositiveIntegerField() - max_length = models.PositiveIntegerField() + """One prefix in a ROA. + + See comment above in AddressRange about how IPv6 addresses are + stored. + + The prefix is broken out into min and max values rather than min/bits in + order to allow for searches using sql. """ + + family = models.PositiveSmallIntegerField(null=False) + prefix_min = models.BigIntegerField(null=False, db_index=True) + prefix_max = models.BigIntegerField(null=False, db_index=True) + max_length = models.PositiveSmallIntegerField(null=False) class Meta: - ordering = ['family', 'prefix', 'bits', 'max_length'] + ordering = ('prefix_min', 'prefix_max', 'max_length') + verbose_name_plural = 'ROAPrefixes' - def __unicode__(self): - if self.bits == self.max_length: - return u'%s/%d' % (self.prefix, self.bits) + def min(self): + "Return the min prefix value as an rpki.ipaddrs.v?addr object." + if self.family == 4: + return rpki.ipaddrs.v4addr(self.prefix_min) + return rpki.ipaddrs.v6addr(self.prefix_min << 65) + + def max(self): + "Return the max prefix value as an rpki.ipaddrs.v?addr object." + if self.family == 4: + return rpki.ipaddrs.v4addr(self.prefix_max) + return rpki.ipaddrs.v6addr((self.prefix_max << 65) | 0x1ffffffffffffffffL) + + def get_prefix_min_display(self): + return str(self.min()) + + def get_prefix_max_display(self): + return str(self.max()) + + def as_resource_range(self): + "Return the prefix as a rpki.resource_set.resource_range_ip object." + if self.family == 4: + return rpki.resource_set.resource_range_ipv4(self.min(), self.max()) else: - return u'%s/%d-%d' % (self.prefix, self.bits, self.max_length) + return rpki.resource_set.resource_range_ipv6((self.min() << 65), + (self.max() << 65) | 0x1ffffffffffffffffL) + + def as_roa_prefix(self): + "Return value as a rpki.resource_set.roa_prefix_ip object." + rng = self.as_resource_range() + cls = rpki.resource_set.roa_prefix_ipv4 if self.family == 4 else rpki.resource_set.roa_prefix_ipv6 + return cls(rng.min, rng.prefixlen(), self.max_length) + + def __unicode__(self): + return u'%s' % str(self.as_roa_prefix()) class ROA(SignedObject): - asid = models.PositiveIntegerField() + asid = models.PositiveIntegerField(null=False) prefixes = models.ManyToManyField(ROAPrefix, related_name='roas') - issuer = models.ForeignKey('Cert', related_name='roas') + issuer = models.ForeignKey('Cert', related_name='roas', null=False) @models.permalink def get_absolute_url(self): return ('rpki.gui.cacheview.views.roa_detail', [str(self.pk)]) class Meta: - ordering = ['asid'] + ordering = ('asid',) def __unicode__(self): return u'ROA for AS%d' % self.asid @@ -205,14 +252,14 @@ class ROA(SignedObject): return ('rpki.gui.cacheview.views.roa_detail', [str(self.pk)]) class ValidationStatus_ROA(ValidationStatus): - roa = models.ForeignKey('ROA', related_name='statuses') + roa = models.ForeignKey('ROA', related_name='statuses', null=False) class Ghostbuster(SignedObject): - full_name = models.CharField(max_length=40) + full_name = models.CharField(max_length=40) email_address = models.EmailField(blank=True, null=True) - organization = models.CharField(blank=True, null=True, max_length=255) - telephone = TelephoneField(blank=True, null=True) - issuer = models.ForeignKey('Cert', related_name='ghostbusters') + organization = models.CharField(blank=True, null=True, max_length=255) + telephone = TelephoneField(blank=True, null=True) + issuer = models.ForeignKey('Cert', related_name='ghostbusters', null=False) @models.permalink def get_absolute_url(self): @@ -228,6 +275,6 @@ class Ghostbuster(SignedObject): return self.telephone class ValidationStatus_Ghostbuster(ValidationStatus): - gbr = models.ForeignKey('Ghostbuster', related_name='statuses') + gbr = models.ForeignKey('Ghostbuster', related_name='statuses', null=False) # vim:sw=4 ts=8 expandtab |