aboutsummaryrefslogtreecommitdiff
path: root/rpkid
diff options
context:
space:
mode:
Diffstat (limited to 'rpkid')
-rw-r--r--rpkid/portal-gui/scripts/rpkigui-rcynic.py31
-rw-r--r--rpkid/rpki/gui/app/views.py28
-rw-r--r--rpkid/rpki/gui/cacheview/models.py153
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