routinator/
slurm.rs

1//! Local exceptions per RFC 8416 aka SLURM.
2
3use std::{error, fmt, fs, io};
4use std::path::Path;
5use std::str::FromStr;
6use std::sync::Arc;
7use log::error;
8use rpki::rtr::payload::{RouteOrigin, RouterKey};
9use rpki::slurm::{BgpsecFilter, PrefixFilter, SlurmFile};
10use crate::config::Config;
11use crate::error::Failed;
12
13
14//------------ LocalExceptions -----------------------------------------------
15
16#[derive(Clone, Debug, Default)]
17pub struct LocalExceptions {
18    origin_filters: Vec<PrefixFilter>,
19    router_key_filters: Vec<BgpsecFilter>,
20
21    origin_assertions: Vec<(RouteOrigin, Arc<ExceptionInfo>)>,
22    router_key_assertions: Vec<(RouterKey, Arc<ExceptionInfo>)>,
23}
24
25impl LocalExceptions {
26    pub fn empty() -> Self {
27        Self::default()
28    }
29
30    pub fn load(config: &Config, keep_comments: bool) -> Result<Self, Failed> {
31        let mut res = LocalExceptions::empty();
32        let mut ok = true;
33        for path in &config.exceptions {
34            if let Err(err) = res.extend_from_file(path, keep_comments) {
35                error!(
36                    "Failed to load exceptions file {}: {}",
37                    path.display(), err
38                );
39                ok = false;
40            }
41        }
42        if ok {
43            Ok(res)
44        }
45        else {
46            Err(Failed)
47        }
48    }
49
50    pub fn from_json(
51        json: &str,
52        keep_comments: bool
53    ) -> Result<Self, serde_json::Error> {
54        let mut res = LocalExceptions::empty();
55        res.extend_from_json(json, keep_comments)?;
56        Ok(res)
57    }
58
59    pub fn from_file<P: AsRef<Path>>(
60        path: P,
61        keep_comments: bool
62    ) -> Result<Self, LoadError> {
63        let mut res = Self::empty();
64        res.extend_from_file(path, keep_comments)?;
65        Ok(res)
66    }
67
68    pub fn extend_from_json(
69        &mut self,
70        json: &str,
71        keep_comments: bool
72    ) -> Result<(), serde_json::Error> {
73        self.extend_from_parsed(
74            SlurmFile::from_str(json)?, None, keep_comments
75        );
76        Ok(())
77    }
78
79    pub fn extend_from_file<P: AsRef<Path>>(
80        &mut self,
81        path: P,
82        keep_comments: bool
83    ) -> Result<(), LoadError> {
84        let buf = fs::read_to_string(&path)?;
85        self.extend_from_parsed(
86            SlurmFile::from_str(&buf)?,
87            Some(path.as_ref().into()), keep_comments
88        );
89        Ok(())
90    }
91
92    fn extend_from_parsed(
93        &mut self,
94        json: SlurmFile,
95        path: Option<Arc<Path>>,
96        keep_comments: bool,
97    ) {
98        // If we don’t keep comments, we can have one info value for
99        // everything and save a bit of memory.
100        let info = (!keep_comments).then(|| {
101            Arc::new(ExceptionInfo {
102                path: path.clone(),
103                comment: None
104            })
105        });
106        let info = info.as_ref(); // So we can use info.cloned() below.
107
108        self.origin_filters.extend(
109            json.filters.prefix.into_iter().map(|mut item| {
110                if !keep_comments {
111                    item.comment = None
112                }
113                item
114            })
115        );
116        self.router_key_filters.extend(
117            json.filters.bgpsec.into_iter().map(|mut item| {
118                if !keep_comments {
119                    item.comment = None
120                }
121                item
122            })
123        );
124        self.origin_assertions.extend(
125            json.assertions.prefix.into_iter().map(|item| {
126                (
127                    RouteOrigin::new(item.prefix, item.asn),
128                    info.cloned().unwrap_or_else(|| {
129                        Arc::new(ExceptionInfo {
130                            path: path.clone(),
131                            comment: item.comment,
132                        })
133                    })
134                )
135            })
136        );
137        self.router_key_assertions.extend(
138            json.assertions.bgpsec.into_iter().map(|item| {
139                (
140                    RouterKey::new(
141                        item.ski, item.asn, item.router_public_key.into()
142                    ),
143                    info.cloned().unwrap_or_else(|| {
144                        Arc::new(ExceptionInfo {
145                            path: path.clone(),
146                            comment: item.comment,
147                        })
148                    })
149                )
150            })
151        );
152    }
153
154    pub fn drop_origin(&self, origin: RouteOrigin) -> bool {
155        self.origin_filters.iter().any(|filter| filter.drop_origin(origin))
156    }
157
158    pub fn drop_router_key(&self, key: &RouterKey) -> bool {
159        self.router_key_filters.iter().any(|filter| {
160            filter.drop_router_key(key)
161        })
162    }
163
164    pub fn origin_assertions(
165        &self
166    ) -> impl Iterator<Item = (RouteOrigin, Arc<ExceptionInfo>)> + '_ {
167        self.origin_assertions.iter().map(|(origin, info)| {
168            (*origin, info.clone())
169        })
170    }
171
172    pub fn router_key_assertions(
173        &self
174    ) -> impl Iterator<Item = (RouterKey, Arc<ExceptionInfo>)> + '_ {
175        self.router_key_assertions.iter().map(|(key, info)| {
176            (key.clone(), info.clone())
177        })
178    }
179}
180
181
182//------------ ExceptionInfo -------------------------------------------------
183
184#[derive(Clone, Debug, Default)]
185pub struct ExceptionInfo {
186    pub path: Option<Arc<Path>>,
187    pub comment: Option<String>,
188}
189
190#[cfg(feature = "arbitrary")]
191impl<'a> arbitrary::Arbitrary<'a> for ExceptionInfo {
192    fn arbitrary(
193        u: &mut arbitrary::Unstructured<'a>
194    ) -> arbitrary::Result<Self> {
195        Ok(Self {
196            path: if bool::arbitrary(u)? {
197                Some(
198                    std::path::PathBuf::arbitrary(u)?.into_boxed_path().into()
199                )
200            }
201            else {
202                None
203            },
204            comment: Option::arbitrary(u)?,
205        })
206    }
207}
208
209
210//------------ LoadError ----------------------------------------------------
211
212#[derive(Debug)]
213pub enum LoadError {
214    Io(io::Error),
215    Json(serde_json::Error),
216}
217
218impl From<io::Error> for LoadError {
219    fn from(err: io::Error) -> LoadError {
220        LoadError::Io(err)
221    }
222}
223
224impl From<serde_json::Error> for LoadError {
225    fn from(err: serde_json::Error) -> LoadError {
226        LoadError::Json(err)
227    }
228}
229
230impl fmt::Display for LoadError {
231    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
232        match *self {
233            LoadError::Io(ref err) => err.fmt(f),
234            LoadError::Json(ref err) => err.fmt(f),
235        }
236    }
237}
238
239impl error::Error for LoadError { }
240