1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
use crate::rislive::messages::RisLiveClientMessage;
use ipnet::IpNet;
use serde::Serialize;
use std::net::IpAddr;
#[derive(Debug, Serialize)]
#[allow(clippy::upper_case_acronyms)]
pub enum RisSubscribeType {
UPDATE,
OPEN,
NOTIFICATION,
KEEPALIVE,
RIS_PEER_STATE,
}
#[derive(Debug, Serialize)]
pub struct RisSubscribeSocketOptions {
/// Include a Base64-encoded version of the original binary BGP message as `raw` for all subscriptions
///
/// *Default: false*
#[serde(rename = "includeRaw")]
pub include_raw: Option<bool>,
/// Send a `ris_subscribe_ok` message for all succesful subscriptions
///
/// *Default: false*
pub acknowledge: Option<bool>,
}
#[derive(Default, Debug, Serialize)]
pub struct RisSubscribe {
/// Only include messages collected by a particular RRC
#[serde(skip_serializing_if = "Option::is_none")]
pub host: Option<String>,
/// Only include messages of a given BGP or RIS type
#[serde(rename = "type")]
#[serde(skip_serializing_if = "Option::is_none")]
pub data_type: Option<RisSubscribeType>,
/// Only include messages containing a given key
///
/// Examples:
/// * "announcements"
/// * "withdrawals"
#[serde(skip_serializing_if = "Option::is_none")]
pub require: Option<String>,
/// Only include messages sent by the given BGP peer
#[serde(skip_serializing_if = "Option::is_none")]
pub peer: Option<IpAddr>,
/// ASN or pattern to match against the AS PATH attribute
///
/// Any of:
/// * ASN (integer)
/// * AS path pattern (string)
///
/// Comma-separated pattern describing all or part of the AS path.
/// Can optionally begin with ^ to match the first item of the path (the last traversed ASN),
/// and/or end with $ to match the last item of the path
/// (the originating ASN).
///
/// The entire pattern can be prefixed with ! to invert the match.
/// AS_SETs should be written as JSON arrays of integers in ascending order and with no spaces.
/// Note: this is not a regular expression.
///
/// Examples:
/// * "789$"
/// * "^123,456,789,\[789,10111\]$"
/// * "!6666$"
/// * "!^3333"
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
/// Filter UPDATE messages by prefixes in announcements or withdrawals
///
/// Any of:
/// * IPv4 or IPv6 CIDR prefix (string)
/// * Array of CIDR prefixes (array)
///
/// For the purposes of subsequent `ris_unsubscribe` messages,
/// each prefix results in a separate subscription that can be stopped independently
///
/// Array items:
/// * IPv4 or IPv6 CIDR prefix (string)
#[serde(skip_serializing_if = "Option::is_none")]
pub prefix: Option<IpNet>,
/// Match prefixes that are more specific (part of) `prefix`
///
/// *Default: true*
#[serde(rename = "moreSpecific")]
#[serde(skip_serializing_if = "Option::is_none")]
pub more_specific: Option<bool>,
/// Match prefixes that are less specific (contain) `prefix`
///
/// *Default: false*
#[serde(rename = "lessSpecific")]
#[serde(skip_serializing_if = "Option::is_none")]
pub less_specific: Option<bool>,
/// Options that apply to all subscriptions over the current WebSocket.
/// If a new subscription contains `socketOptions` it will override those from previous subscriptions
#[serde(rename = "socketOptions")]
#[serde(skip_serializing_if = "Option::is_none")]
pub socket_options: Option<RisSubscribeSocketOptions>,
}
impl RisSubscribe {
pub fn new() -> Self {
Default::default()
}
pub fn host(mut self, host: &str) -> Self {
self.host = Some(host.to_string());
self
}
pub fn data_type(mut self, data_type: RisSubscribeType) -> Self {
self.data_type = Some(data_type);
self
}
pub fn require(mut self, require: &str) -> Self {
self.require = Some(require.to_string());
self
}
pub fn peer(mut self, peer: IpAddr) -> Self {
self.peer = Some(peer);
self
}
pub fn path(mut self, path: &str) -> Self {
self.path = Some(path.to_string());
self
}
pub fn prefix(mut self, prefix: IpNet) -> Self {
self.prefix = Some(prefix);
self
}
pub fn more_specific(mut self, more_specific: bool) -> Self {
self.more_specific = Some(more_specific);
self
}
pub fn less_specific(mut self, less_specific: bool) -> Self {
self.less_specific = Some(less_specific);
self
}
pub fn include_raw(mut self, include_raw: bool) -> Self {
match self.socket_options.as_mut() {
None => {
self.socket_options = Some(RisSubscribeSocketOptions {
include_raw: Some(include_raw),
acknowledge: None,
});
}
Some(o) => {
o.include_raw = Some(include_raw);
}
}
self
}
pub fn acknowledge(mut self, acknowledge: bool) -> Self {
match self.socket_options.as_mut() {
None => {
self.socket_options = Some(RisSubscribeSocketOptions {
include_raw: None,
acknowledge: Some(acknowledge),
});
}
Some(o) => {
o.acknowledge = Some(acknowledge);
}
}
self
}
}
impl RisLiveClientMessage for RisSubscribe {
fn msg_type(&self) -> &'static str {
"ris_subscribe"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let ris_subscribe = RisSubscribe::new()
.host("rrc00")
.data_type(RisSubscribeType::UPDATE);
assert_eq!(ris_subscribe.host, Some("rrc00".to_string()));
println!("{}", ris_subscribe.to_json_string());
}
}