bgpkit_commons/irr/types.rs
1//! Typed RPSL object representations for IRR data.
2//!
3//! Each struct corresponds to a specific RPSL object type. Fields are
4//! extracted from parsed `rpsl::Object<Raw>` instances and stored as owned
5//! data so the original text can be dropped.
6
7use std::collections::BTreeMap;
8
9use ipnet::IpNet;
10use serde::{Deserialize, Serialize};
11
12use crate::irr::extract;
13use crate::{BgpkitCommonsError, Result};
14
15/// One ordered RPSL attribute from an IRR source record.
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
17pub struct IrrAttribute {
18 pub name: String,
19 pub value: String,
20}
21
22/// One source-faithful RPSL object.
23///
24/// Attributes remain ordered and repeated attributes remain separate. The
25/// record is not restricted to the object classes with typed projections.
26#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
27pub struct IrrRecord {
28 pub object_type: String,
29 pub attributes: Vec<IrrAttribute>,
30}
31
32impl IrrRecord {
33 /// Convert a supported raw record to the existing typed representation.
34 /// Unsupported object classes return `Ok(None)`.
35 pub fn to_typed(&self) -> Result<Option<IrrObject>> {
36 let mut text = String::new();
37 for attribute in &self.attributes {
38 let mut values = attribute.value.split('\n');
39 let first = values.next().unwrap_or_default();
40 text.push_str(&attribute.name);
41 text.push_str(": ");
42 text.push_str(first);
43 text.push('\n');
44 for continuation in values {
45 text.push(' ');
46 text.push_str(continuation);
47 text.push('\n');
48 }
49 }
50 text.push('\n');
51
52 let parsed = rpsl::parse_object(&text).map_err(|error| {
53 BgpkitCommonsError::invalid_format("RPSL object", &self.object_type, error.to_string())
54 })?;
55 extract::extract(&parsed)
56 }
57}
58
59/// RPSL object types that this module knows how to extract.
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
61pub enum IrrObjectType {
62 /// `aut-num` — AS name/description/maintainer.
63 AutNum,
64 /// `route` — IPv4 prefix-origin registration.
65 Route,
66 /// `route6` — IPv6 prefix-origin registration.
67 Route6,
68 /// `as-set` — named group of ASes (may be recursive).
69 AsSet,
70 /// `route-set` — named group of prefixes.
71 RouteSet,
72 /// `mntner` — maintainer object with auth methods.
73 Mntner,
74 /// `organisation` — organisation details (RIPE) / org (others).
75 Organisation,
76}
77
78impl IrrObjectType {
79 /// The RPSL attribute name that starts an object of this type.
80 /// This is also the key of the first attribute in the parsed object.
81 pub fn key_attr(&self) -> &'static str {
82 match self {
83 IrrObjectType::AutNum => "aut-num",
84 IrrObjectType::Route => "route",
85 IrrObjectType::Route6 => "route6",
86 IrrObjectType::AsSet => "as-set",
87 IrrObjectType::RouteSet => "route-set",
88 IrrObjectType::Mntner => "mntner",
89 IrrObjectType::Organisation => "organisation",
90 }
91 }
92
93 /// Parse the key attribute name from a parsed RPSL object's first
94 /// attribute to determine the object type. Returns `None` for unknown
95 /// or unsupported types.
96 pub fn from_first_attr(name: &str) -> Option<Self> {
97 match name {
98 "aut-num" => Some(IrrObjectType::AutNum),
99 "route" => Some(IrrObjectType::Route),
100 "route6" => Some(IrrObjectType::Route6),
101 "as-set" => Some(IrrObjectType::AsSet),
102 "route-set" => Some(IrrObjectType::RouteSet),
103 "mntner" => Some(IrrObjectType::Mntner),
104 "organisation" | "org" => Some(IrrObjectType::Organisation),
105 _ => None,
106 }
107 }
108
109 /// Returns all supported object types.
110 pub fn all() -> &'static [IrrObjectType] {
111 &[
112 IrrObjectType::AutNum,
113 IrrObjectType::Route,
114 IrrObjectType::Route6,
115 IrrObjectType::AsSet,
116 IrrObjectType::RouteSet,
117 IrrObjectType::Mntner,
118 IrrObjectType::Organisation,
119 ]
120 }
121}
122
123// ============================================================================
124// Typed RPSL Objects
125// ============================================================================
126
127/// An `aut-num` object: AS-level routing registry entry.
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct AutNum {
130 /// The AS number, e.g. `13335`.
131 pub asn: u32,
132 /// The `as-name` attribute.
133 pub as_name: String,
134 /// The `descr` attributes (may be multiple).
135 pub descr: Vec<String>,
136 /// The `source` attribute (registry provenance).
137 pub source: String,
138 /// All other attributes as (name, value) pairs, preserving order.
139 pub extra: BTreeMap<String, Vec<String>>,
140}
141
142/// A `route` or `route6` object: prefix-origin registration.
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct Route {
145 /// The registered prefix.
146 pub prefix: IpNet,
147 /// The origin AS number.
148 pub origin: u32,
149 /// The `descr` attributes.
150 pub descr: Vec<String>,
151 /// The `source` attribute (registry provenance).
152 pub source: String,
153 /// All other attributes.
154 pub extra: BTreeMap<String, Vec<String>>,
155}
156
157/// An `as-set` object: named collection of ASes and/or other as-sets.
158#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct AsSet {
160 /// The as-set name, e.g. `AS-EXAMPLE`.
161 pub name: String,
162 /// Direct AS members (numeric).
163 pub members: Vec<u32>,
164 /// AS-set members (named references, may require recursive resolution).
165 pub set_members: Vec<String>,
166 /// The `descr` attributes.
167 pub descr: Vec<String>,
168 /// The `source` attribute.
169 pub source: String,
170 /// All other attributes.
171 pub extra: BTreeMap<String, Vec<String>>,
172}
173
174/// A `route-set` object: named collection of prefixes and/or other route-sets.
175#[derive(Debug, Clone, Serialize, Deserialize)]
176pub struct RouteSet {
177 /// The route-set name, e.g. `RS-EXAMPLE`.
178 pub name: String,
179 /// Direct prefix members.
180 pub members: Vec<IpNet>,
181 /// Route-set members (named references, may require recursive resolution).
182 pub set_members: Vec<String>,
183 /// The `descr` attributes.
184 pub descr: Vec<String>,
185 /// The `source` attribute.
186 pub source: String,
187 /// All other attributes.
188 pub extra: BTreeMap<String, Vec<String>>,
189}
190
191/// A `mntner` object: database maintainer with authentication info.
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct Mntner {
194 /// The maintainer name, e.g. `MAINT-AS13335`.
195 pub name: String,
196 /// Authentication methods declared (e.g. `MD5-PW`, `PGPKEY-...`).
197 /// Values are kept verbatim but passwords are always stripped by IRR dumps.
198 pub auth: Vec<String>,
199 /// The `upd-to` notification email.
200 pub upd_to: Vec<String>,
201 /// The `mnt-nfy` notification email.
202 pub mnt_nfy: Vec<String>,
203 /// The `source` attribute.
204 pub source: String,
205 /// All other attributes.
206 pub extra: BTreeMap<String, Vec<String>>,
207}
208
209/// An `organisation` (or `org`) object: entity details.
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct Organisation {
212 /// The organisation ID, e.g. `ORG-GCI2-RIPE`.
213 pub id: String,
214 /// The organisation name.
215 pub name: String,
216 /// The organisation type (RIPE-specific, e.g. `LIR`, `RIR`).
217 pub org_type: Option<String>,
218 /// Address lines.
219 pub address: Vec<String>,
220 /// Country code.
221 pub country: Option<String>,
222 /// Abuse contact email.
223 pub abuse_c: Option<String>,
224 /// The `source` attribute.
225 pub source: String,
226 /// All other attributes.
227 pub extra: BTreeMap<String, Vec<String>>,
228}
229
230/// A typed IRR object, tagged by its type.
231#[derive(Debug, Clone, Serialize, Deserialize)]
232pub enum IrrObject {
233 AutNum(AutNum),
234 Route(Route),
235 Route6(Route),
236 AsSet(AsSet),
237 RouteSet(RouteSet),
238 Mntner(Mntner),
239 Organisation(Organisation),
240}
241
242impl IrrObject {
243 /// The `source:` attribute value (registry provenance).
244 pub fn source(&self) -> &str {
245 match self {
246 IrrObject::AutNum(o) => &o.source,
247 IrrObject::Route(o) | IrrObject::Route6(o) => &o.source,
248 IrrObject::AsSet(o) => &o.source,
249 IrrObject::RouteSet(o) => &o.source,
250 IrrObject::Mntner(o) => &o.source,
251 IrrObject::Organisation(o) => &o.source,
252 }
253 }
254}