Skip to main content

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}