aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rpkid/portal-gui/scripts/rpkigui-import-routes.py74
-rw-r--r--rpkid/rpki/gui/app/views.py5
-rw-r--r--rpkid/rpki/gui/models.py91
-rw-r--r--rpkid/rpki/gui/routeview/models.py68
4 files changed, 155 insertions, 83 deletions
diff --git a/rpkid/portal-gui/scripts/rpkigui-import-routes.py b/rpkid/portal-gui/scripts/rpkigui-import-routes.py
index b8d2413d..4ca8ef96 100644
--- a/rpkid/portal-gui/scripts/rpkigui-import-routes.py
+++ b/rpkid/portal-gui/scripts/rpkigui-import-routes.py
@@ -1,7 +1,11 @@
import sys, itertools, re
+import struct
+import _mysql_exceptions
from django.db import transaction, connection
+import rpki
+import rpki.gui.models
from rpki.gui.routeview import models
from rpki.resource_set import resource_range_ipv4, resource_range_ipv6
@@ -14,31 +18,50 @@ ip_re = re.compile(r'^[0-9a-fA-F:.]+/\d{1,3}$')
class InvalidPrefix(Exception):
pass
+last_prefix = None
+last_asn = None
+
for row in itertools.islice(f, 5, None):
try:
cols = row.split()
prefix = cols[1]
- # validate the prefix since the "sh ip bgp" output is sometimes corrupt
- # by no space between the prefix and the next hop IP address.
- if not ip_re.match(prefix):
- raise InvalidPrefix(prefix)
-
# index -1 is i/e/? for igp/egp
origin_as = cols[-2]
- # skip AS_SETs
+ # FIXME: skip AS_SETs
if origin_as[0] == '{':
continue
+ # the output may contain multiple paths to the same origin.
+ # if this is the same prefix as the last entry, we don't need
+ # to validate it again.
+ if prefix != last_prefix:
+ # validate the prefix since the "sh ip bgp" output is sometimes corrupt
+ # by no space between the prefix and the next hop IP address.
+
+ if not ip_re.match(prefix):
+ net, bits = prefix.split('/')
+ if len(bits) > 2 and int(bits[0]) <= 3:
+ print 'mask for %s looks fishy...' % prefix,
+ prefix = '%s/%s' % (net, bits[0:2])
+ print 'assuming it should be %s' % prefix
+ if not ip_re.match(prefix):
+ raise InvalidPrefix(prefix)
+ last_prefix = prefix
+ elif origin_as == last_asn:
+ # we are only interested in origins, so skip alternate paths
+ # to same origin as last entry.
+ continue
+ last_asn = origin_as
+
asns = prefixes.get(prefix)
if not asns:
asns = set()
prefixes[prefix] = asns
asns.add(int(origin_as))
- #print 'prefix=%s asns=%s' % (prefix, asns)
except InvalidPrefix, e:
print >>sys.stderr, 'skipping bad entry: ' + row,
print >>sys.stderr, e
@@ -48,17 +71,17 @@ f.close()
def commit():
cursor = connection.cursor()
- # an OperationalError exception is thrown when the index doesn't exist
try:
- print 'Removing existing index...'
- cursor.execute('DROP INDEX routeview_routeorigin_idx ON routeview_routeorigin')
- except Exception, e:
- print type(e)
- print e
- cursor.execute('BEGIN')
+ print 'Dropping existing staging table...'
+ cursor.execute('DROP TABLE IF EXISTS routeview_routeorigin_new')
+ except _mysql_exceptions.Warning:
+ pass
- print 'Deleting rows from table...'
- cursor.execute('DELETE FROM routeview_routeorigin')
+ print 'Creating staging table...'
+ cursor.execute('CREATE TABLE routeview_routeorigin_new LIKE routeview_routeorigin')
+
+ print 'Disabling autocommit...'
+ cursor.execute('SET autocommit=0')
print 'Adding rows to table...'
for prefix, asns in prefixes.iteritems():
@@ -66,14 +89,25 @@ def commit():
cls = resource_range_ipv6 if family == 6 else resource_range_ipv4
rng = cls.parse_str(prefix)
- cursor.executemany("INSERT INTO routeview_routeorigin SET family=%s, asn=%s, prefix_min=X%s, prefix_max=X%s",
- [(family, asn, '%032x' % rng.min, '%032x' % rng.max) for asn in asns])
+ if family == 4:
+ xform = long
+ else:
+ xform = lambda v: struct.pack('!QQ', (long(v) >> 64) &0xffffffffffffffffL, long(v) & 0xFFFFFFFFFFFFFFFFL)
+
+ cursor.executemany("INSERT INTO routeview_routeorigin_new SET asn=%s, prefix_min=%s, prefix_max=%s",
+ [(asn, xform(rng.min), xform(rng.max)) for asn in asns])
print 'Committing...'
cursor.execute('COMMIT')
- print 'Creating index on table...'
- cursor.execute('CREATE INDEX routeview_routeorigin_idx ON routeview_routeorigin (family, prefix_min, prefix_max)')
+ try:
+ print 'Dropping old table...'
+ cursor.execute('DROP TABLE IF EXISTS routeview_routeorigin_old')
+ except _mysql_exceptions.Warning:
+ pass
+
+ print 'Swapping staging table with live table...'
+ cursor.execute('RENAME TABLE routeview_routeorigin TO routeview_routeorigin_old, routeview_routeorigin_new TO routeview_routeorigin')
transaction.commit_unless_managed()
diff --git a/rpkid/rpki/gui/app/views.py b/rpkid/rpki/gui/app/views.py
index a29ecba2..4e89dcda 100644
--- a/rpkid/rpki/gui/app/views.py
+++ b/rpkid/rpki/gui/app/views.py
@@ -918,7 +918,10 @@ def route_view(request):
for p in models.AddressRange.objects.filter(from_cert__parent__in=handle.parents.all()):
r = p.as_resource_range()
print >>log, 'querying for routes matching %s' % r
- qs = rpki.gui.routeview.models.RouteOrigin.objects.filter(family=4, prefix_min__gte=r.min, prefix_max__lte=r.max)
+ if isinstance(r, rpki.resource_set.resource_range_ipv6):
+ print >>log, 'skipping ipv6 address: %s' % r
+ continue
+ 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'
diff --git a/rpkid/rpki/gui/models.py b/rpkid/rpki/gui/models.py
new file mode 100644
index 00000000..323d43e6
--- /dev/null
+++ b/rpkid/rpki/gui/models.py
@@ -0,0 +1,91 @@
+# Common classes for resuse in apps
+
+import struct
+
+from django.db import models
+
+import rpki.resource_set
+import rpki.ipaddrs
+
+class IPv6AddressField(models.Field):
+ "Field large enough to hold a 128-bit unsigned integer."
+
+ __metaclass__ = models.SubfieldBase
+
+ def db_type(self, connection):
+ return 'binary(16)'
+
+ def to_python(self, value):
+ if isinstance(value, rpki.ipaddrs.v6addr):
+ return value
+ x = struct.unpack('!QQ', value)
+ return rpki.ipaddrs.v6addr((x[0] << 64) | x[1])
+
+ def get_db_prep_value(self, value, connection, prepared):
+ return struct.pack('!QQ', (long(value) >> 64) & 0xFFFFFFFFFFFFFFFFL, long(value) & 0xFFFFFFFFFFFFFFFFL)
+
+class IPv4AddressField(models.Field):
+ "Wrapper around rpki.ipaddrs.v4addr."
+
+ __metaclass__ = models.SubfieldBase
+
+ def db_type(self, connection):
+ return 'int UNSIGNED'
+
+ def to_python(self, value):
+ if isinstance(value, rpki.ipaddrs.v4addr):
+ return value
+ return rpki.ipaddrs.v4addr(value)
+
+ def get_db_prep_value(self, value, connection, prepared):
+ return long(value)
+
+class Prefix(models.Model):
+ """Common implementation for models with an IP address range.
+
+ Expects that `range_cls` is set to the appropriate subclass of
+ rpki.resource_set.resource_range_ip."""
+
+ def as_resource_range(self):
+ """
+ Returns the prefix as a rpki.resource_set.resource_range_ip object.
+ """
+ return self.range_cls(self.prefix_min, self.prefix_max)
+
+ def prefixlen(self):
+ "Returns the prefix length for the prefix in this object."
+ return self.as_range().prefixlen()
+
+ def get_prefix_display(self):
+ "Returns a string version of the prefix in this object."
+ return str(self.as_resource_range())
+
+ class Meta:
+ abstract = True
+
+ # default sort order reflects what "sh ip bgp" outputs
+ ordering = ('prefix_min',)
+
+class PrefixV4(Prefix):
+ "IPv4 Prefix."
+
+ range_cls = rpki.resource_set.resource_range_ipv4
+
+ prefix_min = IPv4AddressField(db_index=True, null=False)
+ prefix_max = IPv4AddressField(db_index=True, null=False)
+
+ class Meta:
+ abstract = True
+
+class PrefixV6(Prefix):
+ "IPv6 Prefix."
+
+ range_cls = rpki.resource_set.resource_range_ipv6
+
+ prefix_min = IPv6AddressField(db_index=True, null=False)
+ prefix_max = IPv6AddressField(db_index=True, null=False)
+
+ class Meta:
+ abstract = True
+
+# vim:sw=4 ts=8 expandtab
diff --git a/rpkid/rpki/gui/routeview/models.py b/rpkid/rpki/gui/routeview/models.py
index 4bb6eef5..99dd0297 100644
--- a/rpkid/rpki/gui/routeview/models.py
+++ b/rpkid/rpki/gui/routeview/models.py
@@ -1,68 +1,12 @@
-import binascii
+import django.db.models
+import rpki.gui.models
-from django.db import models
-
-import rpki
-import rpki.resource_set
-import rpki.ipaddrs
-
-class PositiveHugeIntegerField(models.Field):
-
- description = "Represents a 128-bit unsigned integer."
-
- __metaclass__ = models.SubfieldBase
-
- def db_type(self, connection):
- if connection.settings_dict['ENGINE'] == 'django.db.backends.mysql':
- return 'binary(16)'
- return 'blob'
-
- def to_python(self, value):
- if isinstance(value, int):
- return long(value)
- if isinstance(value, long):
- return value
- return long(binascii.hexlify(value), 16)
-
- def get_db_prep_value(self, value, connection, prepared=False):
- return binascii.unhexlify('%032x' % value)
-
-class RouteOrigin(models.Model):
-
- asn = models.PositiveIntegerField(help_text='origin AS')
- family = models.PositiveSmallIntegerField(help_text='IP version')
-
- # address stored as unsigned integer to faciliate lookups
- prefix_min = PositiveHugeIntegerField()
- prefix_max = PositiveHugeIntegerField()
-
- def as_range(self):
- """
- Returns the prefix as a rpki.resource_set.resource_range_ip object.
- """
- cls = rpki.resource_set.resource_range_ipv4 if self.family == 4 else rpki.resource_set.resource_range_ipv6
- ipcls = rpki.ipaddrs.v4addr if self.family == 4 else rpki.ipaddrs.v6addr
- return cls(ipcls(self.prefix_min), ipcls(self.prefix_max))
-
- def get_prefix_display(self):
- """
- Returns a string version of the prefix in the routing entry.
- """
- return str(self.as_range())
-
- def prefixlen(self):
- """
- Returns the prefix length for this route object.
- """
- return self.as_range().prefixlen()
+class RouteOrigin(rpki.gui.models.PrefixV4):
+ "Represents a BGP routing table entry."
+ asn = django.db.models.PositiveIntegerField(help_text='origin AS', null=False)
+
def __unicode__(self):
return u"AS%d's route origin for %s" % (self.asn, self.get_prefix_display())
- class Meta:
- # sort order reflects what "sh ip bgp" outputs
- ordering = ( 'family', 'prefix_min', 'prefix_max', 'asn' )
-
- unique_together = ('family', 'asn', 'prefix_min', 'prefix_max')
-
# vim:sw=4 ts=8 expandtab