diff options
-rw-r--r-- | rpkid/left-right-schema.rnc | 18 | ||||
-rw-r--r-- | rpkid/left-right-schema.rng | 31 | ||||
-rw-r--r-- | rpkid/rpki/left_right.py | 50 | ||||
-rw-r--r-- | rpkid/rpki/relaxng.py | 31 | ||||
-rw-r--r-- | rpkid/rpki/resource_set.py | 30 | ||||
-rw-r--r-- | rpkid/rpki/roa.py | 32 | ||||
-rw-r--r-- | rpkid/rpki/x509.py | 3 | ||||
-rw-r--r-- | rpkid/rpkid.sql | 11 | ||||
-rw-r--r-- | rpkid/testbed.py | 6 |
9 files changed, 94 insertions, 118 deletions
diff --git a/rpkid/left-right-schema.rnc b/rpkid/left-right-schema.rnc index 3f4e6e96..14559205 100644 --- a/rpkid/left-right-schema.rnc +++ b/rpkid/left-right-schema.rnc @@ -45,13 +45,9 @@ uri = xsd:anyURI { maxLength="4096" } up_down_name = xsd:token { maxLength="1024" } # Resource lists -asn_list = xsd:string { maxLength="512000" pattern="[\-,/0-9]*" } -ipv4_address_list = xsd:string { maxLength="512000" pattern="[\-,/.0-9]*" } -ipv6_address_list = xsd:string { maxLength="512000" pattern="[\-,/:0-9a-fA-F]*" } - -# Prefix resource lists, same as address resource lists not no ranges allowed -ipv4_prefix_list = xsd:string { maxLength="512000" pattern="[,/.0-9]*" } -ipv6_prefix_list = xsd:string { maxLength="512000" pattern="[,/:0-9a-fA-F]*" } +asn_list = xsd:string { maxLength="512000" pattern="[\-,0-9]*" } +ipv4_list = xsd:string { maxLength="512000" pattern="[\-,0-9/.]*" } +ipv6_list = xsd:string { maxLength="512000" pattern="[\-,0-9/:a-fA-F]*" } # <self/> element @@ -191,8 +187,8 @@ route_origin_bool = attribute suppress_publication { "yes" }? route_origin_payload = (attribute as_number { xsd:positiveInteger }?, attribute exact_match { xsd:boolean }?, - attribute ipv4 { ipv4_prefix_list }?, - attribute ipv6 { ipv6_prefix_list }?) + attribute ipv4 { ipv4_list }?, + attribute ipv6 { ipv6_list }?) route_origin_elt |= element route_origin { ctl_cq, self_id, route_origin_bool, route_origin_payload } route_origin_elt |= element route_origin { ctl_cr, self_id, route_origin_id } @@ -213,8 +209,8 @@ list_resources_elt = element list_resources { attribute valid_until { xsd:dateTime { pattern=".*Z" } }, attribute subject_name { xsd:token { maxLength="1024" } }?, attribute asn { asn_list }?, - attribute ipv4 { ipv4_address_list }?, - attribute ipv6 { ipv6_address_list }? + attribute ipv4 { ipv4_list }?, + attribute ipv6 { ipv6_list }? ) } diff --git a/rpkid/left-right-schema.rng b/rpkid/left-right-schema.rng index 93f79d32..6c5b9d6b 100644 --- a/rpkid/left-right-schema.rng +++ b/rpkid/left-right-schema.rng @@ -160,32 +160,19 @@ <define name="asn_list"> <data type="string"> <param name="maxLength">512000</param> - <param name="pattern">[\-,/0-9]*</param> + <param name="pattern">[\-,0-9]*</param> </data> </define> - <define name="ipv4_address_list"> + <define name="ipv4_list"> <data type="string"> <param name="maxLength">512000</param> - <param name="pattern">[\-,/.0-9]*</param> + <param name="pattern">[\-,0-9/.]*</param> </data> </define> - <define name="ipv6_address_list"> + <define name="ipv6_list"> <data type="string"> <param name="maxLength">512000</param> - <param name="pattern">[\-,/:0-9a-fA-F]*</param> - </data> - </define> - <!-- Prefix resource lists, same as address resource lists not no ranges allowed --> - <define name="ipv4_prefix_list"> - <data type="string"> - <param name="maxLength">512000</param> - <param name="pattern">[,/.0-9]*</param> - </data> - </define> - <define name="ipv6_prefix_list"> - <data type="string"> - <param name="maxLength">512000</param> - <param name="pattern">[,/:0-9a-fA-F]*</param> + <param name="pattern">[\-,0-9/:a-fA-F]*</param> </data> </define> <!-- <self/> element --> @@ -838,12 +825,12 @@ </optional> <optional> <attribute name="ipv4"> - <ref name="ipv4_prefix_list"/> + <ref name="ipv4_list"/> </attribute> </optional> <optional> <attribute name="ipv6"> - <ref name="ipv6_prefix_list"/> + <ref name="ipv6_list"/> </attribute> </optional> </define> @@ -959,12 +946,12 @@ </optional> <optional> <attribute name="ipv4"> - <ref name="ipv4_address_list"/> + <ref name="ipv4_list"/> </attribute> </optional> <optional> <attribute name="ipv6"> - <ref name="ipv6_address_list"/> + <ref name="ipv6_list"/> </attribute> </optional> </group> diff --git a/rpkid/rpki/left_right.py b/rpkid/rpki/left_right.py index e9bb7a92..209302f4 100644 --- a/rpkid/rpki/left_right.py +++ b/rpkid/rpki/left_right.py @@ -826,11 +826,11 @@ class route_origin_elt(data_elt): """<route_origin/> element.""" element_name = "route_origin" - attributes = ("action", "type", "tag", "self_id", "route_origin_id", "as_number", "exact_match", "ipv4", "ipv6") + attributes = ("action", "type", "tag", "self_id", "route_origin_id", "as_number", "ipv4", "ipv6") booleans = ("suppress_publication",) sql_template = rpki.sql.template("route_origin", "route_origin_id", "ca_detail_id", - "self_id", "as_number", "exact_match", + "self_id", "as_number", ("roa", rpki.x509.ROA), ("cert", rpki.x509.X509)) @@ -839,28 +839,28 @@ class route_origin_elt(data_elt): roa = None def sql_fetch_hook(self): - """Extra SQL fetch actions for route_origin_elt -- handle address ranges.""" - self.ipv4 = rpki.resource_set.resource_set_ipv4.from_sql(self.gctx.cur, """ - SELECT start_ip, end_ip FROM route_origin_range - WHERE route_origin_id = %s AND start_ip NOT LIKE '%:%' + """Extra SQL fetch actions for route_origin_elt -- handle prefix list.""" + self.ipv4 = rpki.resource_set.roa_prefix_set_ipv4.from_sql(self.gctx.cur, """ + SELECT address, prefixlen, max_prefixlen FROM route_origin_prefix + WHERE route_origin_id = %s AND address NOT LIKE '%:%' """, (self.route_origin_id,)) - self.ipv6 = rpki.resource_set.resource_set_ipv6.from_sql(self.gctx.cur, """ - SELECT start_ip, end_ip FROM route_origin_range - WHERE route_origin_id = %s AND start_ip LIKE '%:%' + self.ipv6 = rpki.resource_set.roa_prefix_set_ipv6.from_sql(self.gctx.cur, """ + SELECT address, prefixlen, max_prefixlen FROM route_origin_prefix + WHERE route_origin_id = %s AND address LIKE '%:%' """, (self.route_origin_id,)) def sql_insert_hook(self): """Extra SQL insert actions for route_origin_elt -- handle address ranges.""" if self.ipv4 or self.ipv6: self.gctx.cur.executemany(""" - INSERT route_origin_range (route_origin_id, start_ip, end_ip) - VALUES (%s, %s, %s)""", - ((self.route_origin_id, x.min, x.max) + INSERT route_origin_prefix (route_origin_id, address, prefixlen, max_prefixlen) + VALUES (%s, %s, %s, %s)""", + ((self.route_origin_id, x.address, x.prefixlen, x.max_prefixlen) for x in (self.ipv4 or []) + (self.ipv6 or []))) def sql_delete_hook(self): """Extra SQL delete actions for route_origin_elt -- handle address ranges.""" - self.gctx.cur.execute("DELETE FROM route_origin_range WHERE route_origin_id = %s", (self.route_origin_id,)) + self.gctx.cur.execute("DELETE FROM route_origin_prefix WHERE route_origin_id = %s", (self.route_origin_id,)) def ca_detail(self): """Fetch all ca_detail objects that link to this route_origin object.""" @@ -877,9 +877,9 @@ class route_origin_elt(data_elt): if self.as_number is not None: self.as_number = long(self.as_number) if self.ipv4 is not None: - self.ipv4 = rpki.resource_set.resource_set_ipv4(self.ipv4) + self.ipv4 = rpki.resource_set.roa_prefix_set_ipv4(self.ipv4) if self.ipv6 is not None: - self.ipv6 = rpki.resource_set.resource_set_ipv6(self.ipv6) + self.ipv6 = rpki.resource_set.roa_prefix_set_ipv6(self.ipv6) def endElement(self, stack, name, text): """Handle <route_origin/> element.""" @@ -912,8 +912,8 @@ class route_origin_elt(data_elt): if ee_resources.oversized(ca_resources): return self.regenerate_roa() - v4 = self.ipv4 if self.ipv4 is not None else rpki.resource_set.resource_set_ipv4() - v6 = self.ipv6 if self.ipv6 is not None else rpki.resource_set.resource_set_ipv6() + v4 = self.ipv4.to_resource_set() if self.ipv4 is not None else rpki.resource_set.resource_set_ipv4() + v6 = self.ipv6.to_resource_set() if self.ipv6 is not None else rpki.resource_set.resource_set_ipv6() if ee_resources.v4 != v4 or ee_resources.v6 != v6: return self.regenerate_roa() @@ -938,12 +938,8 @@ class route_origin_elt(data_elt): /dev/random, but there is not much we can do about that. """ - if self.exact_match is None: - rpki.log.warn("Can't generate ROA with undefined exactMatch") - return - if self.ipv4 is None and self.ipv6 is None: - rpki.log.warn("Can't generate ROA for empty address list") + rpki.log.warn("Can't generate ROA for empty prefix list") return # Ugly and expensive search for covering ca_detail, there has to @@ -953,6 +949,9 @@ class route_origin_elt(data_elt): # first checking the ca_detail we used last time, but it may not # be active, in which we have to check the ca_detail that replaced it. + v4 = self.ipv4.to_resource_set() if self.ipv4 is not None else rpki.resource_set.resource_set_ipv4() + v6 = self.ipv6.to_resource_set() if self.ipv6 is not None else rpki.resource_set.resource_set_ipv6() + ca_detail = self.ca_detail() if ca_detail is None or ca_detail.state != "active": ca_detail = None @@ -961,8 +960,7 @@ class route_origin_elt(data_elt): ca_detail = ca.fetch_active() if ca_detail is not None: resources = ca_detail.latest_ca_cert.get_3779resources() - if ((self.ipv4 is None or self.ipv4.issubset(resources.v4)) and - (self.ipv6 is None or self.ipv6.issubset(resources.v6))): + if v4.issubset(resources.v4) and v6.issubset(resources.v6): break ca_detail = None if ca_detail is not None: @@ -972,7 +970,7 @@ class route_origin_elt(data_elt): rpki.log.warn("generate_roa() could not find a covering certificate") return - resources = rpki.resource_set.resource_bag(v4 = self.ipv4, v6 = self.ipv6) + resources = rpki.resource_set.resource_bag(v4 = v4, v6 = v6) keypair = rpki.x509.RSA() keypair.generate() @@ -980,7 +978,7 @@ class route_origin_elt(data_elt): sia = ((rpki.oids.name2oid["id-ad-signedObject"], ("uri", self.roa_uri(ca, keypair))),) self.cert = ca_detail.issue_ee(ca, resources, keypair.get_RSApublic(), sia = sia) - self.roa = rpki.x509.ROA.build(self.as_number, self.exact_match, self.ipv4, self.ipv6, keypair, (self.cert,)) + self.roa = rpki.x509.ROA.build(self.as_number, self.ipv4, self.ipv6, keypair, (self.cert,)) self.ca_detail_id = ca_detail.ca_detail_id self.sql_store() diff --git a/rpkid/rpki/relaxng.py b/rpkid/rpki/relaxng.py index 4baf7723..fecb4c2a 100644 --- a/rpkid/rpki/relaxng.py +++ b/rpkid/rpki/relaxng.py @@ -166,32 +166,19 @@ left_right = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" enc <define name="asn_list"> <data type="string"> <param name="maxLength">512000</param> - <param name="pattern">[\-,/0-9]*</param> + <param name="pattern">[\-,0-9]*</param> </data> </define> - <define name="ipv4_address_list"> + <define name="ipv4_list"> <data type="string"> <param name="maxLength">512000</param> - <param name="pattern">[\-,/.0-9]*</param> + <param name="pattern">[\-,0-9/.]*</param> </data> </define> - <define name="ipv6_address_list"> + <define name="ipv6_list"> <data type="string"> <param name="maxLength">512000</param> - <param name="pattern">[\-,/:0-9a-fA-F]*</param> - </data> - </define> - <!-- Prefix resource lists, same as address resource lists not no ranges allowed --> - <define name="ipv4_prefix_list"> - <data type="string"> - <param name="maxLength">512000</param> - <param name="pattern">[,/.0-9]*</param> - </data> - </define> - <define name="ipv6_prefix_list"> - <data type="string"> - <param name="maxLength">512000</param> - <param name="pattern">[,/:0-9a-fA-F]*</param> + <param name="pattern">[\-,0-9/:a-fA-F]*</param> </data> </define> <!-- <self/> element --> @@ -844,12 +831,12 @@ left_right = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" enc </optional> <optional> <attribute name="ipv4"> - <ref name="ipv4_prefix_list"/> + <ref name="ipv4_list"/> </attribute> </optional> <optional> <attribute name="ipv6"> - <ref name="ipv6_prefix_list"/> + <ref name="ipv6_list"/> </attribute> </optional> </define> @@ -965,12 +952,12 @@ left_right = lxml.etree.RelaxNG(lxml.etree.fromstring('''<?xml version="1.0" enc </optional> <optional> <attribute name="ipv4"> - <ref name="ipv4_address_list"/> + <ref name="ipv4_list"/> </attribute> </optional> <optional> <attribute name="ipv6"> - <ref name="ipv6_address_list"/> + <ref name="ipv6_list"/> </attribute> </optional> </group> diff --git a/rpkid/rpki/resource_set.py b/rpkid/rpki/resource_set.py index cd8a68d6..3d92b2ce 100644 --- a/rpkid/rpki/resource_set.py +++ b/rpkid/rpki/resource_set.py @@ -57,6 +57,7 @@ class resource_range(object): def __cmp__(self, other): """Compare two resource_range objects.""" + assert self.__class__ is other.__class__ c = self.min - other.min if c == 0: c = self.max - other.max if c < 0: c = -1 @@ -125,13 +126,6 @@ class resource_range_ip(resource_range): else: return ("addressPrefix", _long2bs(self.min, self.datum_type.bits, prefixlen = prefixlen)) - def to_roa_tuple(self): - """Convert a resource_range_ip to tuple format for ROA ASN.1 encoding.""" - prefixlen = self._prefixlen() - if prefixlen < 0: - raise rpki.exceptions.MustBePrefix, "%s cannot be expressed as a prefix" % str(self) - return _long2bs(self.min, self.datum_type.bits, prefixlen = prefixlen) - @classmethod def make_prefix(cls, address, prefixlen): """Construct a resource range corresponding to a prefix.""" @@ -401,14 +395,6 @@ class resource_set_ip(resource_set): else: return None - def to_roa_tuple(self): - """Convert IP resource set into tuple format used by ROA ASN.1 encoder. - This is a variation on the format used in RFC 3779.""" - if self: - return (self.afi, tuple(a.to_roa_tuple() for a in self)) - else: - return None - class resource_set_ipv4(resource_set_ip): """IPv4 address resource set.""" @@ -592,6 +578,7 @@ class roa_prefix(object): """Compare two ROA prefix objects. Comparision is based on address, prefixlen, and max_prefixlen, in that order. """ + assert self.__class__ is other.__class__ c = self.address - other.address if c == 0: c = self.prefixlen - other.prefixlen if c == 0: c = self.max_prefixlen - other.max_prefixlen @@ -622,6 +609,11 @@ class roa_prefix(object): t = self.range_type.datum_type return t(self.address | ((1 << (t.bits - self.prefixlen)) - 1)) + def to_roa_tuple(self): + """Convert a resource_range_ip to tuple format for ROA ASN.1 encoding.""" + return (_long2bs(self.address, self.range_type.datum_type.bits, prefixlen = self.prefixlen), + None if self.prefixlen == self.max_prefixlen else self.max_prefixlen) + class roa_prefix_ipv4(roa_prefix): """IPv4 ROA prefix.""" @@ -685,6 +677,14 @@ class roa_prefix_set(list): return cls([cls.prefix_type(cls.prefix_type.range_type.datum_type(x), int(y), int(z)) for (x,y,z) in cur.fetchall()]) + def to_roa_tuple(self): + """Convert ROA prefix set into tuple format used by ROA ASN.1 encoder. + This is a variation on the format used in RFC 3779.""" + if self: + return (self.resource_set_type.afi, tuple(a.to_roa_tuple() for a in self)) + else: + return None + class roa_prefix_set_ipv4(roa_prefix_set): """Set of IPv4 ROA prefixes.""" diff --git a/rpkid/rpki/roa.py b/rpkid/rpki/roa.py index 2b1ba7fd..b059e851 100644 --- a/rpkid/rpki/roa.py +++ b/rpkid/rpki/roa.py @@ -18,13 +18,13 @@ At the moment this is just the ASN.1 encoder. -This corresponds to draft-ietf-sidr-roa-format-02, which is a work in +This corresponds to draft-ietf-sidr-roa-format, which is a work in progress, so this may need updating later. """ from POW._der import * -# draft-ietf-sidr-roa-format-02 2.1.3.2 +# draft-ietf-sidr-roa-format-02 2.1.3.2 specifies: # # RouteOriginAttestation ::= SEQUENCE { # version [0] INTEGER DEFAULT 0, @@ -41,9 +41,9 @@ from POW._der import * # addresses SEQUENCE OF IPAddress } # # IPAddress ::= BIT STRING - -# Proposed new format, neither in draft nor in this code yet, but -# included here for reference anyway: +# +# ... but we now implement the new format that will supposedly appear +# in the upcoming draft-ietf-sidr-roa-format-03: # # RouteOriginAttestation ::= SEQUENCE { # version [0] INTEGER DEFAULT 0, @@ -58,22 +58,29 @@ from POW._der import * # # ROAIPAddress ::= { # address IPAddress, -# maxLength INTEGER } +# maxLength INTEGER OPTIONAL } # # IPAddress ::= BIT STRING -class IPAddresses(SequenceOf): +class ROAIPAddress(Sequence): + def __init__(self, optional=0, default=''): + self.address = BitString() + self.maxLength = Integer(1) + contents = [ self.address, self.maxLength ] + Sequence.__init__(self, contents, optional, default) + +class ROAIPAddresses(SequenceOf): def __init__(self, optional=0, default=''): - SequenceOf.__init__(self, BitString, optional, default) + SequenceOf.__init__(self, ROAIPAddress, optional, default) class ROAIPAddressFamily(Sequence): def __init__(self, optional=0, default=''): self.addressFamily = OctetString() - self.addresses = IPAddresses() + self.addresses = ROAIPAddresses() contents = [ self.addressFamily, self.addresses ] Sequence.__init__(self, contents, optional, default) -class ROAIPAddrBlocks(SequenceOf): +class ROAIPAddressFamilies(SequenceOf): def __init__(self, optional=0, default=''): SequenceOf.__init__(self, ROAIPAddressFamily, optional, default) @@ -82,7 +89,6 @@ class RouteOriginAttestation(Sequence): self.version = Integer() self.explicitVersion = Explicit(CLASS_CONTEXT, FORM_CONSTRUCTED, 0, self.version, 0, 'oAMCAQA=') self.asID = Integer() - self.exactMatch = Boolean() - self.ipAddrBlocks = ROAIPAddrBlocks() - contents = [ self.explicitVersion, self.asID, self.exactMatch, self.ipAddrBlocks ] + self.ipAddrBlocks = ROAIPAddressFamilies() + contents = [ self.explicitVersion, self.asID, self.ipAddrBlocks ] Sequence.__init__(self, contents, optional, default) diff --git a/rpkid/rpki/x509.py b/rpkid/rpki/x509.py index 09238370..83cd31e5 100644 --- a/rpkid/rpki/x509.py +++ b/rpkid/rpki/x509.py @@ -801,13 +801,12 @@ class ROA(DER_CMS_object): econtent_oid = POWify_OID("id-ct-routeOriginAttestation") @classmethod - def build(cls, as_number, exact_match, ipv4, ipv6, keypair, certs, version = 0): + def build(cls, as_number, ipv4, ipv6, keypair, certs, version = 0): """Build a ROA.""" self = cls() r = rpki.roa.RouteOriginAttestation() r.version.set(version) r.asID.set(as_number) - r.exactMatch.set(exact_match) r.ipAddrBlocks.set((a.to_roa_tuple() for a in (ipv4, ipv6) if a)) self.set_content(r) self.sign(keypair, certs) diff --git a/rpkid/rpkid.sql b/rpkid/rpkid.sql index d13eb370..fa50c1d6 100644 --- a/rpkid/rpkid.sql +++ b/rpkid/rpkid.sql @@ -177,13 +177,14 @@ CREATE TABLE route_origin ( FOREIGN KEY (ca_detail_id) REFERENCES ca_detail ); -DROP TABLE IF EXISTS route_origin_range; +DROP TABLE IF EXISTS route_origin_prefix; -CREATE TABLE route_origin_range ( - start_ip VARCHAR(40), - end_ip VARCHAR(40), +CREATE TABLE route_origin_prefix ( + address VARCHAR(40) NOT NULL, + prefixlen TINYINT NOT NULL, + max_prefixlen TINYINT NOT NULL, route_origin_id BIGINT unsigned NOT NULL, - PRIMARY KEY (route_origin_id, start_ip, end_ip), + PRIMARY KEY (route_origin_id, address, prefixlen, max_prefixlen), FOREIGN KEY (route_origin_id) REFERENCES route_origin ); diff --git a/rpkid/testbed.py b/rpkid/testbed.py index 3d01c7d1..2c697b42 100644 --- a/rpkid/testbed.py +++ b/rpkid/testbed.py @@ -255,8 +255,8 @@ class route_origin(object): def __init__(self, asn, ipv4, ipv6, exact_match): self.asn = asn - self.v4 = rpki.resource_set.resource_set_ipv4("".join(ipv4.split())) if ipv4 else None - self.v6 = rpki.resource_set.resource_set_ipv6("".join(ipv6.split())) if ipv6 else None + self.v4 = rpki.resource_set.roa_prefix_set_ipv4("".join(ipv4.split())) if ipv4 else None + self.v6 = rpki.resource_set.roa_prefix_set_ipv6("".join(ipv6.split())) if ipv6 else None self.exact_match = exact_match def __eq__(self, other): @@ -308,6 +308,8 @@ class allocation_db(list): def apply_delta(self, delta): """Apply a delta or run a command.""" + if delta is None: + return for d in delta: if isinstance(d, str): c = d.split() |