routes/bmp/encode.rs
1use std::convert::TryFrom;
2use std::{net::IpAddr, ops::Deref, str::FromStr};
3
4use bytes::{BufMut, Bytes, BytesMut};
5use chrono::Utc;
6use routecore::addr::Prefix;
7use routecore::asn::Asn;
8use routecore::bgp::aspath::HopPath;
9use routecore::bgp::communities::Community;
10use routecore::bgp::types::{
11 NextHop, OriginType, PathAttributeType, Afi, Safi,
12};
13use routecore::bmp::message::{
14 InformationTlvType, MessageType, PeerType, TerminationInformation,
15};
16
17pub fn mk_initiation_msg(sys_name: &str, sys_descr: &str) -> Bytes {
18 let mut buf = BytesMut::new();
19 push_bmp_common_header(&mut buf, MessageType::InitiationMessage);
20
21 // 4.3. Initiation Message
22 //
23 // "The initiation message consists of the common BMP header followed by
24 // two or more Information TLVs (Section 4.4) containing information
25 // about the monitored router. The sysDescr and sysName Information
26 // TLVs MUST be sent, any others are optional. The string TLV MAY be
27 // included multiple times."
28 //
29 // From: https://www.rfc-editor.org/rfc/rfc7854.html#section-4.3
30
31 // 4.4. Information TLV
32 //
33 // 0 1 2 3
34 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
35 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36 // | Information Type | Information Length |
37 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
38 // | Information (variable) |
39 // ~ ~
40 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41 //
42 // From: https://www.rfc-editor.org/rfc/rfc7854.html#section-4.4
43
44 push_bmp_information_tlv(
45 &mut buf,
46 InformationTlvType::SysName,
47 sys_name.as_bytes(),
48 );
49 push_bmp_information_tlv(
50 &mut buf,
51 InformationTlvType::SysDesc,
52 sys_descr.as_bytes(),
53 );
54
55 finalize_bmp_msg_len(&mut buf);
56 buf.freeze()
57}
58
59// Returns the generated bytes and a, possibly empty, set of warning messages.
60#[allow(clippy::too_many_arguments)]
61#[allow(clippy::vec_init_then_push)]
62pub fn mk_peer_up_notification_msg(
63 per_peer_header: &PerPeerHeader,
64 local_address: IpAddr,
65 local_port: u16,
66 remote_port: u16,
67 sent_open_asn: u16,
68 received_open_asn: u16,
69 sent_bgp_identifier: u32,
70 received_bgp_identifier: u32,
71 information_tlvs: Vec<(InformationTlvType, String)>,
72 eor_capable: bool,
73) -> (Bytes, Vec<String>) {
74 let mut buf = BytesMut::new();
75 push_bmp_common_header(&mut buf, MessageType::PeerUpNotification);
76 let warnings = push_bmp_per_peer_header(&mut buf, per_peer_header);
77
78 // 4.10. Peer Up Notification
79 //
80 // 0 1 2 3
81 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
82 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
83 // | Local Address (16 bytes) |
84 // ~ ~
85 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
86 // | Local Port | Remote Port |
87 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
88 // | Sent OPEN Message |
89 // ~ ~
90 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
91 // | Received OPEN Message |
92 // ~ ~
93 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
94 // | Information (variable) |
95 // ~ ~
96 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
97 //
98 // From: https://www.rfc-editor.org/rfc/rfc7854.html#section-4.10
99
100 match local_address {
101 IpAddr::V4(addr) => {
102 assert!(per_peer_header.is_ipv4());
103 buf.resize(buf.len() + 12, 0u8);
104 buf.extend_from_slice(&addr.octets());
105 }
106 IpAddr::V6(addr) => {
107 assert!(per_peer_header.is_ipv6());
108 buf.extend_from_slice(&addr.octets());
109 }
110 }
111
112 buf.extend_from_slice(&local_port.to_be_bytes());
113 buf.extend_from_slice(&remote_port.to_be_bytes());
114
115 // 4.2. OPEN Message Format
116 //
117 // After a TCP connection is established, the first message sent by each
118 // side is an OPEN message. If the OPEN message is acceptable, a
119 // KEEPALIVE message confirming the OPEN is sent back.
120 //
121 // In addition to the fixed-size BGP header, the OPEN message contains
122 // the following fields:
123 //
124 // 0 1 2 3
125 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
126 // +-+-+-+-+-+-+-+-+
127 // | Version |
128 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
129 // | My Autonomous System |
130 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
131 // | Hold Time |
132 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
133 // | BGP Identifier |
134 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
135 // | Opt Parm Len |
136 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
137 // | |
138 // | Optional Parameters (variable) |
139 // | |
140 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
141 //
142 // From: https://datatracker.ietf.org/doc/html/rfc4271#section-4.2
143
144 // insert fake BGP open sent message
145 let mut bgp_msg_buf = BytesMut::new();
146 // Fixed size BGP header
147 bgp_msg_buf.resize(bgp_msg_buf.len() + 16, 0xFFu8); // marker
148 bgp_msg_buf.resize(bgp_msg_buf.len() + 2, 0); // placeholder length, to be replaced later
149 bgp_msg_buf.extend_from_slice(&1u8.to_be_bytes()); // 1 - OPEN
150 bgp_msg_buf.extend_from_slice(&4u8.to_be_bytes()); // BGP version 4
151
152 // Other fields
153 bgp_msg_buf.extend_from_slice(&sent_open_asn.to_be_bytes());
154 bgp_msg_buf.extend_from_slice(&0u16.to_be_bytes()); // 0 hold time - disables keep alive
155 bgp_msg_buf.extend_from_slice(&sent_bgp_identifier.to_be_bytes());
156 bgp_msg_buf.extend_from_slice(&0u8.to_be_bytes()); // 0 optional parameters
157
158 // Finalize BGP message
159 finalize_bgp_msg_len(&mut bgp_msg_buf);
160 buf.extend_from_slice(&bgp_msg_buf);
161
162 // insert fake BGP open received message
163 let mut bgp_msg_buf = BytesMut::new();
164 // Fixed size BGP header
165 bgp_msg_buf.resize(bgp_msg_buf.len() + 16, 0xFFu8); // marker
166 bgp_msg_buf.resize(bgp_msg_buf.len() + 2, 0); // placeholder length, to be replaced later
167 bgp_msg_buf.extend_from_slice(&1u8.to_be_bytes()); // 1 - OPEN
168 bgp_msg_buf.extend_from_slice(&4u8.to_be_bytes()); // BGP version 4
169
170 // Other fields
171 bgp_msg_buf.extend_from_slice(&received_open_asn.to_be_bytes());
172 bgp_msg_buf.extend_from_slice(&0u16.to_be_bytes()); // 0 hold time - disables keep alive
173 bgp_msg_buf.extend_from_slice(&received_bgp_identifier.to_be_bytes());
174
175 if !eor_capable {
176 bgp_msg_buf.extend_from_slice(&0u8.to_be_bytes()); // 0 optional parameter bytes
177 } else {
178 // A peer capable of sending the special End-Of-Rib marker BGP
179 // UPDATE message advertises this ability using a BGP capability
180 // (RFC 5492) which is expressed as an optional parameter type 2
181 // with a capability code 64 (from RFC 4274).
182
183 // Optional Parameters:
184 //
185 // This field contains a list of optional parameters, in which
186 // each parameter is encoded as a <Parameter Type, Parameter
187 // Length, Parameter Value> triplet.
188 //
189 // 0 1
190 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
191 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...
192 // | Parm. Type | Parm. Length | Parameter Value (variable)
193 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...
194 //
195 // Parameter Type is a one octet field that unambiguously
196 // identifies individual parameters. Parameter Length is a one
197 // octet field that contains the length of the Parameter Value
198 // field in octets. Parameter Value is a variable length field
199 // that is interpreted according to the value of the Parameter
200 // Type field.
201 //
202 // [RFC3392] defines the Capabilities Optional Parameter.
203 //
204 // From: https://datatracker.ietf.org/doc/html/rfc4271#section-4.2
205 // Note that RFC 3392 was obsoleted by RFC 5492
206 //
207 // Which leads us to...
208
209 // 4. Capabilities Optional Parameter (Parameter Type 2):
210 //
211 // This is an Optional Parameter that is used by a BGP speaker to convey
212 // to its BGP peer the list of capabilities supported by the speaker.
213 // The encoding of BGP Optional Parameters is specified in Section 4.2
214 // of [RFC4271]. The parameter type of the Capabilities Optional
215 // Parameter is 2.
216 //
217 // The parameter contains one or more triples <Capability Code,
218 // Capability Length, Capability Value>, where each triple is encoded as
219 // shown below:
220 //
221 // +------------------------------+
222 // | Capability Code (1 octet) |
223 // +------------------------------+
224 // | Capability Length (1 octet) |
225 // +------------------------------+
226 // | Capability Value (variable) |
227 // ~ ~
228 // +------------------------------+
229 //
230 // The use and meaning of these fields are as follows:
231 //
232 // Capability Code:
233 //
234 // Capability Code is a one-octet unsigned binary integer that
235 // unambiguously identifies individual capabilities.
236 //
237 // Capability Length:
238 //
239 // Capability Length is a one-octet unsigned binary integer that
240 // contains the length of the Capability Value field in octets.
241 //
242 // Capability Value:
243 //
244 // Capability Value is a variable-length field that is interpreted
245 // according to the value of the Capability Code field.
246 //
247 // From: https://datatracker.ietf.org/doc/html/rfc5492#section-4
248 //
249 // Which leads us further on to ...
250
251 // 3. Graceful Restart Capability
252 //
253 // The Graceful Restart Capability is a new BGP capability [BGP-CAP]
254 // that can be used by a BGP speaker to indicate its ability to preserve
255 // its forwarding state during BGP restart. It can also be used to
256 // convey to its peer its intention of generating the End-of-RIB marker
257 // upon the completion of its initial routing updates.
258 //
259 // This capability is defined as follows:
260 //
261 // Capability code: 64
262 //
263 // Capability length: variable
264 //
265 // Capability value: Consists of the "Restart Flags" field, "Restart
266 // Time" field, and 0 to 63 of the tuples <AFI, SAFI, Flags for
267 // address family> as follows:
268 //
269 // +--------------------------------------------------+
270 // | Restart Flags (4 bits) |
271 // +--------------------------------------------------+
272 // | Restart Time in seconds (12 bits) |
273 // +--------------------------------------------------+
274 // | Address Family Identifier (16 bits) |
275 // +--------------------------------------------------+
276 // | Subsequent Address Family Identifier (8 bits) |
277 // +--------------------------------------------------+
278 // | Flags for Address Family (8 bits) |
279 // +--------------------------------------------------+
280 // | ... |
281 // +--------------------------------------------------+
282 // | Address Family Identifier (16 bits) |
283 // +--------------------------------------------------+
284 // | Subsequent Address Family Identifier (8 bits) |
285 // +--------------------------------------------------+
286 // | Flags for Address Family (8 bits) |
287 // +--------------------------------------------------+
288 //
289 // The use and meaning of the fields are as follows:
290 //
291 // Restart Flags:
292 //
293 // This field contains bit flags related to restart.
294 //
295 // 0 1 2 3
296 // +-+-+-+-+
297 // |R|Resv.|
298 // +-+-+-+-+
299 //
300 // The most significant bit is defined as the Restart State (R)
301 // bit, which can be used to avoid possible deadlock caused by
302 // waiting for the End-of-RIB marker when multiple BGP speakers
303 // peering with each other restart. When set (value 1), this bit
304 // indicates that the BGP speaker has restarted, and its peer MUST
305 // NOT wait for the End-of-RIB marker from the speaker before
306 // advertising routing information to the speaker.
307 //
308 // The remaining bits are reserved and MUST be set to zero by the
309 // sender and ignored by the receiver.
310 //
311 // Restart Time:
312 //
313 // This is the estimated time (in seconds) it will take for the
314 // BGP session to be re-established after a restart. This can be
315 // used to speed up routing convergence by its peer in case that
316 // the BGP speaker does not come back after a restart.
317 //
318 // Address Family Identifier (AFI), Subsequent Address Family
319 // Identifier (SAFI):
320 //
321 // The AFI and SAFI, taken in combination, indicate that Graceful
322 // Restart is supported for routes that are advertised with the
323 // same AFI and SAFI. Routes may be explicitly associated with a
324 // particular AFI and SAFI using the encoding of [BGP-MP] or
325 // implicitly associated with <AFI=IPv4, SAFI=Unicast> if using
326 // the encoding of [BGP-4].
327 //
328 // Flags for Address Family:
329 //
330 // This field contains bit flags relating to routes that were
331 // advertised with the given AFI and SAFI.
332 //
333 // 0 1 2 3 4 5 6 7
334 // +-+-+-+-+-+-+-+-+
335 // |F| Reserved |
336 // +-+-+-+-+-+-+-+-+
337 //
338 // The most significant bit is defined as the Forwarding State (F)
339 // bit, which can be used to indicate whether the forwarding state
340 // for routes that were advertised with the given AFI and SAFI has
341 // indeed been preserved during the previous BGP restart. When
342 // set (value 1), the bit indicates that the forwarding state has
343 // been preserved.
344 //
345 // The remaining bits are reserved and MUST be set to zero by the
346 // sender and ignored by the receiver.
347 // From: https://datatracker.ietf.org/doc/html/rfc4724#section-3
348
349 // BGP optional parameters: optp_len, optp_params
350 // Where:
351 // optp_len = octet len of optp_params
352 // optp_params = [(optpi_type, optpi_len, optpi_value), ...]
353 // Where:
354 // optpi_type = 2 (the RFC 5492 capabilities optional parameter code)
355 // optpi_len = the octet len of optpi_vals
356 // optpi_value = [(cap_code, cap_len, cap_val), ...]
357 // Where there is a single tuple with:
358 // cap_code = 64 (the RFC 4724 graceful restart capability code)
359 // cap_len = 2 (two bytes for 4-bit flags + 12-bit restart time)
360 // cap_val = 0 (0 4-bit flags + 0 12-bit restart time)
361 // }
362 // }
363
364 // innermost layer
365 let cap_code = 64u8;
366 let cap_len = 2u8;
367 let cap_val = 0u16;
368
369 // middle layer
370 let optpi_type = 2u8;
371 let mut optpi_value = Vec::<u8>::new();
372 optpi_value.push(cap_code);
373 optpi_value.push(cap_len);
374 optpi_value.extend_from_slice(&cap_val.to_be_bytes());
375 let optpi_len = u8::try_from(optpi_value.len()).unwrap();
376
377 // outer layer
378 let mut optp_params = Vec::<u8>::new();
379 optp_params.push(optpi_type);
380 optp_params.push(optpi_len);
381 optp_params.append(&mut optpi_value);
382 let optp_len = u8::try_from(optp_params.len()).unwrap();
383
384 // extend the BGP OPEN message with the optional parameters
385 bgp_msg_buf.extend_from_slice(&[optp_len]);
386 bgp_msg_buf.extend_from_slice(&optp_params);
387 }
388
389 // Finalize BGP message
390 finalize_bgp_msg_len(&mut bgp_msg_buf);
391 buf.extend_from_slice(&bgp_msg_buf);
392
393 for (typ, val) in information_tlvs {
394 push_bmp_information_tlv(&mut buf, typ, val.as_bytes());
395 }
396
397 finalize_bmp_msg_len(&mut buf);
398
399 (buf.freeze(), warnings)
400}
401
402#[allow(clippy::vec_init_then_push)]
403pub fn mk_route_monitoring_msg(
404 per_peer_header: &PerPeerHeader,
405 withdrawals: &Prefixes,
406 announcements: &Announcements,
407 extra_path_attributes: &[u8],
408) -> (Bytes, Vec<String>) {
409 let (bgp_msg_buf, mut warnings) =
410 mk_bgp_update(per_peer_header, withdrawals, announcements, extra_path_attributes);
411 let (bytes, mut more_warnings) = mk_raw_route_monitoring_msg(per_peer_header, bgp_msg_buf);
412
413 warnings.append(&mut more_warnings);
414
415 (bytes, warnings)
416}
417
418// Returns the generated bytes and a, possibly empty, set of warning messages.
419pub fn mk_raw_route_monitoring_msg(
420 per_peer_header: &PerPeerHeader,
421 bgp_msg_buf: Bytes,
422) -> (Bytes, Vec<String>) {
423 // 4.6. Route Monitoring
424 //
425 // "Following the common BMP header and per-peer header is a BGP Update
426 // PDU."
427 //
428 // From: https://www.rfc-editor.org/rfc/rfc7854.html#section-4.6
429
430 let mut buf = BytesMut::new();
431 push_bmp_common_header(&mut buf, MessageType::RouteMonitoring);
432 let warnings = push_bmp_per_peer_header(&mut buf, per_peer_header);
433 buf.extend_from_slice(&bgp_msg_buf);
434 finalize_bmp_msg_len(&mut buf);
435
436 (buf.freeze(), warnings)
437}
438
439// Returns the generated bytes and a, possibly empty, set of warning messages.
440#[allow(clippy::vec_init_then_push)]
441pub fn mk_bgp_update(
442 per_peer_header: &PerPeerHeader,
443 withdrawals: &Prefixes,
444 announcements: &Announcements,
445 extra_path_attributes: &[u8],
446) -> (Bytes, Vec<String>) {
447 let mut warnings = vec![];
448
449 // 4.3. UPDATE Message Format
450 //
451 // "The UPDATE message always includes the fixed-size BGP
452 // header, and also includes the other fields, as shown below (note,
453 // some of the shown fields may not be present in every UPDATE message):"
454 //
455 // +-----------------------------------------------------+
456 // | Withdrawn Routes Length (2 octets) |
457 // +-----------------------------------------------------+
458 // | Withdrawn Routes (variable) |
459 // +-----------------------------------------------------+
460 // | Total Path Attribute Length (2 octets) |
461 // +-----------------------------------------------------+
462 // | Path Attributes (variable) |
463 // +-----------------------------------------------------+
464 // | Network Layer Reachability Information (variable) |
465 // +-----------------------------------------------------+
466 //
467 // From: https://datatracker.ietf.org/doc/html/rfc4271#section-4.3
468
469 let mut buf = BytesMut::new();
470
471 // Fixed size BGP header
472 buf.resize(buf.len() + 16, 0xFFu8);
473 // marker
474 buf.resize(buf.len() + 2, 0);
475 // placeholder length, to be replaced later
476 buf.extend_from_slice(&2u8.to_be_bytes());
477 // 2 - UPDATE
478
479 // Other fields
480 // Route withdrawals
481 // "Withdrawn Routes Length:
482 //
483 // This 2-octets unsigned integer indicates the total length of
484 // the Withdrawn Routes field in octets. Its value allows the
485 // length of the Network Layer Reachability Information field to
486 // be determined, as specified below.
487 //
488 // A value of 0 indicates that no routes are being withdrawn from
489 // service, and that the WITHDRAWN ROUTES field is not present in
490 // this UPDATE message.
491 //
492 // Withdrawn Routes:
493 //
494 // This is a variable-length field that contains a list of IP
495 // address prefixes for the routes that are being withdrawn from
496 // service. Each IP address prefix is encoded as a 2-tuple of the
497 // form <length, prefix>, whose fields are described below:
498 //
499 // +---------------------------+
500 // | Length (1 octet) |
501 // +---------------------------+
502 // | Prefix (variable) |
503 // +---------------------------+
504 //
505 // The use and the meaning of these fields are as follows:
506 //
507 // a) Length:
508 //
509 // The Length field indicates the length in bits of the IP
510 // address prefix. A length of zero indicates a prefix that
511 // matches all IP addresses (with prefix, itself, of zero
512 // octets).
513 //
514 // b) Prefix:
515 //
516 // The Prefix field contains an IP address prefix, followed by
517 // the minimum number of trailing bits needed to make the end
518 // of the field fall on an octet boundary. Note that the value
519 // of trailing bits is irrelevant."
520 //
521 // From: https://datatracker.ietf.org/doc/html/rfc4271#section-4.3
522 let mut withdrawn_routes = BytesMut::new();
523 let mut mp_unreach_nlri = BytesMut::new();
524
525 for prefix in withdrawals.iter() {
526 let (addr, len) = prefix.addr_and_len();
527 match addr {
528 IpAddr::V4(addr) => {
529 withdrawn_routes.extend_from_slice(&[len]);
530 if len > 0 {
531 let min_bytes = div_ceil(len, 8) as usize;
532 withdrawn_routes
533 .extend_from_slice(&addr.octets()[..min_bytes]);
534 }
535 }
536 IpAddr::V6(addr) => {
537 // https://datatracker.ietf.org/doc/html/rfc4760#section-4
538 if mp_unreach_nlri.is_empty() {
539 mp_unreach_nlri.put_u16(Afi::Ipv6.into());
540 mp_unreach_nlri.put_u8(
541 u8::from(Safi::Unicast) | u8::from(Safi::Multicast),
542 );
543 }
544 mp_unreach_nlri.extend_from_slice(&[len]);
545 if len > 0 {
546 let min_bytes = div_ceil(len, 8) as usize;
547 mp_unreach_nlri
548 .extend_from_slice(&addr.octets()[..min_bytes]);
549 }
550 }
551 }
552 }
553 let num_withdrawn_route_bytes =
554 u16::try_from(withdrawn_routes.len()).unwrap();
555 buf.extend_from_slice(&num_withdrawn_route_bytes.to_be_bytes());
556 // N withdrawn route bytes
557 if num_withdrawn_route_bytes > 0 {
558 buf.extend(&withdrawn_routes); // the withdrawn routes
559 }
560
561 // Route announcements
562 // "Total Path Attribute Length:
563 //
564 // This 2-octet unsigned integer indicates the total length of the
565 // Path Attributes field in octets. Its value allows the length
566 // of the Network Layer Reachability field to be determined as
567 // specified below.
568 //
569 // A value of 0 indicates that neither the Network Layer
570 // Reachability Information field nor the Path Attribute field is
571 // present in this UPDATE message.
572 //
573 // Path Attributes:
574 //
575 // A variable-length sequence of path attributes is present in
576 // every UPDATE message, except for an UPDATE message that carries
577 // only the withdrawn routes. Each path attribute is a triple
578 // <attribute type, attribute length, attribute value> of variable
579 // length.
580 //
581 // Attribute Type is a two-octet field that consists of the
582 // Attribute Flags octet, followed by the Attribute Type Code
583 // octet.
584 //
585 // 0 1
586 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
587 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
588 // | Attr. Flags |Attr. Type Code|
589 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+"
590 //
591 // ...
592 //
593 // "Network Layer Reachability Information:
594 //
595 // This variable length field contains a list of IP address
596 // prefixes. The length, in octets, of the Network Layer
597 // Reachability Information is not encoded explicitly, but can be
598 // calculated as:
599 //
600 // UPDATE message Length - 23 - Total Path Attributes Length
601 // - Withdrawn Routes Length
602 //
603 // where UPDATE message Length is the value encoded in the fixed-
604 // size BGP header, Total Path Attribute Length, and Withdrawn
605 // Routes Length are the values encoded in the variable part of
606 // the UPDATE message, and 23 is a combined length of the fixed-
607 // size BGP header, the Total Path Attribute Length field, and the
608 // Withdrawn Routes Length field.
609 //
610 // Reachability information is encoded as one or more 2-tuples of
611 // the form <length, prefix>, whose fields are described below:
612 //
613 // +---------------------------+
614 // | Length (1 octet) |
615 // +---------------------------+
616 // | Prefix (variable) |
617 // +---------------------------+
618 //
619 // The use and the meaning of these fields are as follows:
620 //
621 // a) Length:
622 //
623 // The Length field indicates the length in bits of the IP
624 // address prefix. A length of zero indicates a prefix that
625 // matches all IP addresses (with prefix, itself, of zero
626 // octets).
627 //
628 // b) Prefix:
629 //
630 // The Prefix field contains an IP address prefix, followed by
631 // enough trailing bits to make the end of the field fall on an
632 // octet boundary. Note that the value of the trailing bits is
633 // irrelevant."
634 //
635 // From: https://datatracker.ietf.org/doc/html/rfc4271#section-4.3
636 match announcements {
637 Announcements::None => {
638 buf.extend_from_slice(&0u16.to_be_bytes()); // 0 path attributes and no NLRI field
639 }
640 Announcements::Some {
641 origin,
642 as_path,
643 next_hop,
644 communities,
645 prefixes,
646 } => {
647 fn push_attributes(
648 out_bytes: &mut Vec<u8>,
649 r#type: PathAttributeType,
650 pa_bytes: &[u8],
651 ) {
652 // Path Attributes:
653 //
654 // A variable-length sequence of path attributes is present in
655 // every UPDATE message, except for an UPDATE message that carries
656 // only the withdrawn routes. Each path attribute is a triple
657 // <attribute type, attribute length, attribute value> of variable
658 // length.
659 //
660 // Attribute Type is a two-octet field that consists of the
661 // Attribute Flags octet, followed by the Attribute Type Code
662 // octet.
663 //
664 // 0 1
665 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
666 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
667 // | Attr. Flags |Attr. Type Code|
668 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
669 //
670 // The high-order bit (bit 0) of the Attribute Flags octet is the
671 // Optional bit. It defines whether the attribute is optional (if
672 // set to 1) or well-known (if set to 0).
673 //
674 // The second high-order bit (bit 1) of the Attribute Flags octet
675 // is the Transitive bit. It defines whether an optional
676 // attribute is transitive (if set to 1) or non-transitive (if set
677 // to 0).
678 //
679 // For well-known attributes, the Transitive bit MUST be set to 1.
680 // (See Section 5 for a discussion of transitive attributes.)
681 //
682 // The third high-order bit (bit 2) of the Attribute Flags octet
683 // is the Partial bit. It defines whether the information
684 // contained in the optional transitive attribute is partial (if
685 // set to 1) or complete (if set to 0). For well-known attributes
686 // and for optional non-transitive attributes, the Partial bit
687 // MUST be set to 0.
688 //
689 // The fourth high-order bit (bit 3) of the Attribute Flags octet
690 // is the Extended Length bit. It defines whether the Attribute
691 // Length is one octet (if set to 0) or two octets (if set to 1).
692 //
693 // The lower-order four bits of the Attribute Flags octet are
694 // unused. They MUST be zero when sent and MUST be ignored when
695 // received.
696 //
697 // The Attribute Type Code octet contains the Attribute Type Code.
698 // Currently defined Attribute Type Codes are discussed in Section
699 // 5.
700 //
701 // If the Extended Length bit of the Attribute Flags octet is set
702 // to 0, the third octet of the Path Attribute contains the length
703 // of the attribute data in octets.
704 //
705 // If the Extended Length bit of the Attribute Flags octet is set
706 // to 1, the third and fourth octets of the path attribute contain
707 // the length of the attribute data in octets.
708 //
709 // The remaining octets of the Path Attribute represent the
710 // attribute value and are interpreted according to the Attribute
711 // Flags and the Attribute Type Code. The supported Attribute
712 // Type Codes, and their attribute values and uses are as follows:
713
714 let len = pa_bytes.len();
715
716 let (optional, transitive, partial) = match r#type {
717 PathAttributeType::AsPath
718 | PathAttributeType::NextHop
719 | PathAttributeType::Origin => (false, true, false),
720 PathAttributeType::Communities
721 | PathAttributeType::ExtendedCommunities
722 | PathAttributeType::LargeCommunities => (true, true, false),
723 PathAttributeType::MpReachNlri => (true, false, false),
724 _ => todo!(),
725 };
726
727 let mut flags = 0u8;
728 if optional {
729 flags |= 0b1000_0000;
730 }
731 if !optional || transitive {
732 flags |= 0b0100_0000;
733 }
734 if !optional || !transitive {
735 flags &= 0b1101_1111;
736 } else if partial {
737 flags |= 0b0010_0000;
738 }
739 if len > 255 {
740 flags |= 0b0001_0000;
741 }
742
743 out_bytes.put_u8(flags); // attr. flags
744 out_bytes.put_u8(u8::from(r#type)); // attr. type
745 if len <= 255 {
746 out_bytes.put_u8(u8::try_from(len).unwrap()); // attr. octet length
747 } else {
748 out_bytes.put_u16(u16::try_from(len).unwrap()); // attr. octet length
749 };
750
751 out_bytes.extend_from_slice(pa_bytes);
752 }
753
754 let mut path_attributes = Vec::<u8>::new();
755
756 // -------------------------------------------------------------------
757 // "ORIGIN (Type Code 1):
758 //
759 // ORIGIN is a well-known mandatory attribute that defines the origin
760 // of the path information."
761 //
762 // From: https://datatracker.ietf.org/doc/html/rfc4271#section-4.3
763 push_attributes(
764 &mut path_attributes,
765 PathAttributeType::Origin,
766 &[origin.into()],
767 );
768
769 // -------------------------------------------------------------------
770 // "AS_PATH (Type Code 2):
771 //
772 // AS_PATH is a well-known mandatory attribute that is composed of a
773 // sequence of AS path segments. Each AS path segment is represented
774 // by a triple <path segment type, path segment length, path segment
775 // value>."
776 //
777 // From: https://datatracker.ietf.org/doc/html/rfc4271#section-4.3
778 let mut as_path_attr_value_bytes = Vec::<u8>::new();
779
780 let as_path = if per_peer_header.is_legacy_two_byte_as_path_format() {
781 match as_path.try_to_asn16_path::<Vec<u8>>() {
782 Ok(as_path) => as_path,
783 Err(err) => {
784 warnings.push(format!("RFC 7854 section 4.2 Per-Peer Header violation: Peer Flags A-bit is SET but should be unset because the AS PATH being encoded contains ASNs that cannot be encoded in 16-bits: {err}"));
785 as_path.to_as_path::<Vec<u8>>().unwrap()
786 }
787 }
788 } else {
789 as_path.to_as_path::<Vec<u8>>().unwrap()
790 };
791
792 // sequence of AS path segments [(seg. type, seg. len, seg. val), ...]
793 for segment in as_path.segments() {
794 segment.compose(&mut as_path_attr_value_bytes).unwrap();
795 }
796
797 push_attributes(
798 &mut path_attributes,
799 PathAttributeType::AsPath,
800 &as_path_attr_value_bytes,
801 );
802
803 // -------------------------------------------------------------------
804 // "NEXT_HOP (Type Code 3):
805 //
806 // This is a well-known mandatory attribute that defines the (unicast)
807 // IP address of the router that SHOULD be used as the next hop to the
808 // destinations listed in the Network Layer Reachability Information
809 // field of the UPDATE message."
810 //
811 // From: https://datatracker.ietf.org/doc/html/rfc4271#section-4.3
812 if let NextHop::Unicast(IpAddr::V4(addr)) = next_hop.0 {
813 push_attributes(
814 &mut path_attributes,
815 PathAttributeType::NextHop,
816 &addr.octets(),
817 );
818 }
819
820 // -------------------------------------------------------------------
821 // "COMMUNITIES attribute:
822 //
823 // This document creates the COMMUNITIES path attribute is an optional
824 // transitive attribute of variable length. The attribute consists of a
825 // set of four octet values, each of which specify a community. All
826 // routes with this attribute belong to the communities listed in the
827 // attribute.
828 //
829 // The COMMUNITIES attribute has Type Code 8.
830 //
831 // Communities are treated as 32 bit values, however for administrative
832 // assignment, the following presumptions may be made:
833 //
834 // The community attribute values ranging from 0x0000000 through
835 // 0x0000FFFF and 0xFFFF0000 through 0xFFFFFFFF are hereby reserved.
836 //
837 // The rest of the community attribute values shall be encoded using an
838 // autonomous system number in the first two octets. The semantics of
839 // the final two octets may be defined by the autonomous system (e.g. AS
840 // 690 may define research, educational and commercial community values
841 // that may be used for policy routing as defined by the operators of
842 // that AS using community attribute values 0x02B20000 through
843 // 0x02B2FFFF)."
844 //
845 // From: https://www.rfc-editor.org/rfc/rfc1997.html
846 if !communities.is_empty() {
847 let mut communities_attribute_bytes = Vec::<u8>::new();
848 let mut extended_communities_attribute_bytes =
849 Vec::<u8>::new();
850 let mut large_communities_attribute_bytes = Vec::<u8>::new();
851
852 for community in communities.deref() {
853 match community {
854 Community::Standard(c) => communities_attribute_bytes
855 .extend_from_slice(&c.to_raw()),
856 Community::Extended(c) => {
857 extended_communities_attribute_bytes
858 .extend_from_slice(&c.to_raw())
859 }
860 Community::Ipv6Extended(_) => todo!(),
861 Community::Large(c) => {
862 large_communities_attribute_bytes
863 .extend_from_slice(&c.to_raw())
864 }
865 }
866 }
867
868 if !communities_attribute_bytes.is_empty() {
869 push_attributes(
870 &mut path_attributes,
871 PathAttributeType::Communities,
872 &communities_attribute_bytes,
873 );
874 }
875
876 if !extended_communities_attribute_bytes.is_empty() {
877 push_attributes(
878 &mut path_attributes,
879 PathAttributeType::ExtendedCommunities,
880 &extended_communities_attribute_bytes,
881 );
882 }
883
884 if !large_communities_attribute_bytes.is_empty() {
885 push_attributes(
886 &mut path_attributes,
887 PathAttributeType::LargeCommunities,
888 &large_communities_attribute_bytes,
889 );
890 }
891 }
892
893 // Now add the list of NLRI IP addresses
894 let mut announced_routes = Vec::<u8>::new();
895 let mut mp_reach_nlri = BytesMut::new();
896
897 for prefix in prefixes.iter() {
898 let (addr, len) = prefix.addr_and_len();
899 match addr {
900 IpAddr::V4(addr) => {
901 announced_routes.extend_from_slice(&[len]);
902 if len > 0 {
903 let min_bytes = div_ceil(len, 8) as usize;
904 announced_routes.extend_from_slice(
905 &addr.octets()[..min_bytes],
906 );
907 }
908 }
909 IpAddr::V6(addr) => {
910 // https://datatracker.ietf.org/doc/html/rfc4760#section-3
911 if mp_reach_nlri.is_empty() {
912 mp_reach_nlri.put_u16(Afi::Ipv6.into());
913 mp_reach_nlri.put_u8(u8::from(Safi::Unicast));
914 if let NextHop::Unicast(IpAddr::V6(addr)) = next_hop.0 {
915 mp_reach_nlri
916 .put_u8(addr.octets().len() as u8);
917 mp_reach_nlri
918 .extend_from_slice(&addr.octets());
919 } else {
920 unreachable!();
921 }
922 mp_reach_nlri.put_u8(0u8); // reserved
923 }
924 mp_reach_nlri.extend_from_slice(&[len]);
925 if len > 0 {
926 let min_bytes = div_ceil(len, 8) as usize;
927 mp_reach_nlri.extend_from_slice(
928 &addr.octets()[..min_bytes],
929 );
930 }
931 }
932 }
933 }
934
935 if !mp_reach_nlri.is_empty() {
936 push_attributes(
937 &mut path_attributes,
938 PathAttributeType::MpReachNlri,
939 &mp_reach_nlri,
940 );
941 }
942
943 let num_path_attribute_bytes = u16::try_from(
944 path_attributes.len() + extra_path_attributes.len(),
945 )
946 .unwrap();
947 buf.extend_from_slice(&num_path_attribute_bytes.to_be_bytes()); // N path attribute bytes
948 buf.extend_from_slice(&path_attributes);
949 buf.extend_from_slice(extra_path_attributes);
950
951 if !announced_routes.is_empty() {
952 buf.extend_from_slice(&announced_routes); // the announced routes
953 }
954 }
955 }
956
957 // Finalize BGP message
958 finalize_bgp_msg_len(&mut buf);
959
960 (buf.freeze(), warnings)
961}
962
963// Returns the generated bytes and a, possibly empty, set of warning messages.
964pub fn mk_peer_down_notification_msg(
965 per_peer_header: &PerPeerHeader,
966) -> (Bytes, Vec<String>) {
967 let mut buf = BytesMut::new();
968 push_bmp_common_header(&mut buf, MessageType::PeerDownNotification);
969 let warnings = push_bmp_per_peer_header(&mut buf, per_peer_header);
970
971 // 4.9. Peer Down Notification
972 //
973 // 0 1 2 3
974 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
975 // +-+-+-+-+-+-+-+-+
976 // | Reason |
977 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
978 // | Data (present if Reason = 1, 2 or 3) |
979 // ~ ~
980 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
981 //
982 // From: https://www.rfc-editor.org/rfc/rfc7854.html#section-4.9
983
984 buf.extend_from_slice(&5u8.to_be_bytes()); // reason code 5
985
986 finalize_bmp_msg_len(&mut buf);
987
988 (buf.freeze(), warnings)
989}
990
991// Returns the generated bytes and a, possibly empty, set of warning messages.
992pub fn mk_statistics_report_msg(per_peer_header: &PerPeerHeader) -> (Bytes, Vec<String>) {
993 // 4.8. Stats Reports
994 //
995 // "Following the common BMP header and per-peer header is a 4-byte field
996 // that indicates the number of counters in the stats message where each
997 // counter is encoded as a TLV."
998 //
999 // 0 1 2 3
1000 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1001 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1002 // | Stats Count |
1003 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1004 //
1005 // Each counter is encoded as follows:
1006 //
1007 // 0 1 2 3
1008 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1009 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1010 // | Stat Type | Stat Len |
1011 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1012 // | Stat Data |
1013 // ~ ~
1014 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1015 //
1016 // o Stat Type (2 bytes): Defines the type of the statistic carried in
1017 // the Stat Data field.
1018 //
1019 // o Stat Len (2 bytes): Defines the length of the Stat Data field.
1020 //
1021 // From: https://www.rfc-editor.org/rfc/rfc7854.html#section-4.8
1022
1023 let mut buf = BytesMut::new();
1024 push_bmp_common_header(&mut buf, MessageType::StatisticsReport);
1025 let warnings = push_bmp_per_peer_header(&mut buf, per_peer_header);
1026
1027 buf.extend_from_slice(&0u32.to_be_bytes()); // zero stats
1028
1029 finalize_bmp_msg_len(&mut buf);
1030
1031 (buf.freeze(), warnings)
1032}
1033
1034pub fn mk_termination_msg() -> Bytes {
1035 let mut buf = BytesMut::new();
1036 push_bmp_common_header(&mut buf, MessageType::TerminationMessage);
1037
1038 // 4.5. Termination Message
1039 //
1040 // "The termination message consists of the common BMP header followed by
1041 // one or more TLVs containing information about the reason for the
1042 // termination, as follows:"
1043 //
1044 // From: https://www.rfc-editor.org/rfc/rfc7854.html#section-4.5
1045
1046 // 4.4 Information TLV
1047 //
1048 // 0 1 2 3
1049 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1050 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1051 // | Information Type | Information Length |
1052 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1053 // | Information (variable) |
1054 // ~ ~
1055 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1056 //
1057 // From: https://www.rfc-editor.org/rfc/rfc7854.html#section-4.4
1058
1059 push_bmp_termination_tlv(
1060 &mut buf,
1061 TerminationInformation::AdminClose,
1062 &[0u8, 0u8],
1063 );
1064
1065 finalize_bmp_msg_len(&mut buf);
1066 buf.freeze()
1067}
1068
1069fn finalize_bmp_msg_len(buf: &mut BytesMut) {
1070 let len_bytes: [u8; 4] = (buf.len() as u32).to_be_bytes();
1071 buf[1] = len_bytes[0];
1072 buf[2] = len_bytes[1];
1073 buf[3] = len_bytes[2];
1074 buf[4] = len_bytes[3];
1075}
1076
1077fn finalize_bgp_msg_len(buf: &mut BytesMut) {
1078 assert!(buf.len() >= 19);
1079 assert!(buf.len() <= 4096);
1080
1081 let len_bytes: [u8; 2] = (buf.len() as u16).to_be_bytes();
1082 buf[16] = len_bytes[0];
1083 buf[17] = len_bytes[1];
1084}
1085
1086fn push_bmp_common_header(buf: &mut BytesMut, msg_type: MessageType) {
1087 // 0 1 2 3
1088 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1089 // +-+-+-+-+-+-+-+-+
1090 // | Version |
1091 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1092 // | Message Length |
1093 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1094 // | Msg. Type |
1095 // +---------------+
1096 //
1097 // From: https://datatracker.ietf.org/doc/html/rfc4271#section-4.1
1098 buf.extend_from_slice(&[3u8]); // version 3
1099 buf.resize(buf.len() + 4, 0u8); // placeholder length, to be replaced later
1100 buf.extend_from_slice(&u8::from(msg_type).to_be_bytes());
1101}
1102
1103#[derive(Debug, PartialEq, Eq)]
1104pub struct PerPeerHeader {
1105 pub peer_type: MyPeerType,
1106 pub peer_flags: u8,
1107 pub peer_distinguisher: [u8; 8],
1108 pub peer_address: IpAddr,
1109 pub peer_as: Asn,
1110 pub peer_bgp_id: [u8; 4],
1111}
1112
1113impl PerPeerHeader {
1114 fn is_ipv4(&self) -> bool {
1115 self.peer_flags & 0x80 == 0
1116 }
1117
1118 fn is_ipv6(&self) -> bool {
1119 self.peer_flags & 0x80 == 0x80
1120 }
1121
1122 fn is_legacy_two_byte_as_path_format(&self) -> bool {
1123 self.peer_flags & 0x20 == 0x20
1124 }
1125}
1126
1127pub fn mk_per_peer_header(peer_ip: &str, peer_as: u32) -> PerPeerHeader {
1128 PerPeerHeader {
1129 peer_type: PeerType::GlobalInstance.into(),
1130 peer_flags: 0,
1131 peer_distinguisher: [0u8; 8],
1132 peer_address: peer_ip.parse().unwrap(),
1133 peer_as: Asn::from_u32(peer_as),
1134 peer_bgp_id: [1u8, 2u8, 3u8, 4u8],
1135 }
1136}
1137
1138// Returns warnings about incorrect Per-Peer Header flags, if any.
1139fn push_bmp_per_peer_header(buf: &mut BytesMut, pph: &PerPeerHeader) -> Vec<String> {
1140 let mut warnings = vec![];
1141
1142 // 0 1 2 3
1143 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1144 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1145 // | Peer Type | Peer Flags |
1146 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1147 // | Peer Distinguisher (present based on peer type) |
1148 // | |
1149 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1150 // | Peer Address (16 bytes) |
1151 // ~ ~
1152 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1153 // | Peer AS |
1154 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1155 // | Peer BGP ID |
1156 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1157 // | Timestamp (seconds) |
1158 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1159 // | Timestamp (microseconds) |
1160 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1161 //
1162 // From: https://www.rfc-editor.org/rfc/rfc7854.html#section-4.2
1163
1164 // Peer Flags (1 byte): These flags provide more information about
1165 // the peer. The flags are defined as follows:
1166 //
1167 // 0 1 2 3 4 5 6 7
1168 // +-+-+-+-+-+-+-+-+
1169 // |V|L|A| Reserved|
1170 // +-+-+-+-+-+-+-+-+
1171 //
1172 // * The V flag indicates that the Peer address is an IPv6 address.
1173 // For IPv4 peers, this is set to 0.
1174 //
1175 // * The L flag, if set to 1, indicates that the message reflects
1176 // the post-policy Adj-RIB-In (i.e., its path attributes reflect
1177 // the application of inbound policy). It is set to 0 if the
1178 // message reflects the pre-policy Adj-RIB-In. Locally sourced
1179 // routes also carry an L flag of 1. See Section 5 for further
1180 // detail. This flag has no significance when used with route
1181 // mirroring messages (Section 4.7).
1182 //
1183 // * The A flag, if set to 1, indicates that the message is
1184 // formatted using the legacy 2-byte AS_PATH format. If set to 0,
1185 // the message is formatted using the 4-byte AS_PATH format
1186 // [RFC6793]. A BMP speaker MAY choose to propagate the AS_PATH
1187 // information as received from its peer, or it MAY choose to
1188 // reformat all AS_PATH information into a 4-byte format
1189 // regardless of how it was received from the peer. In the latter
1190 // case, AS4_PATH or AS4_AGGREGATOR path attributes SHOULD NOT be
1191 // sent in the BMP UPDATE message. This flag has no significance
1192 // when used with route mirroring messages (Section 4.7).
1193 //
1194 // * The remaining bits are reserved for future use. They MUST be
1195 // transmitted as 0 and their values MUST be ignored on receipt.
1196
1197 // "Timestamp: The time when the encapsulated routes were received (one
1198 // may also think of this as the time when they were installed in the
1199 // Adj-RIB-In), expressed in seconds and microseconds since midnight
1200 // (zero hour), January 1, 1970 (UTC). If zero, the time is
1201 // unavailable. Precision of the timestamp is implementation-dependent."
1202 //
1203 // From: https://www.rfc-editor.org/rfc/rfc7854.html#section-4.2
1204 let now = Utc::now();
1205 let epoch_seconds = u32::try_from(now.timestamp()).unwrap();
1206 let epoch_micros = now.timestamp_subsec_micros();
1207
1208 buf.put_u8(u8::from(*pph.peer_type));
1209 buf.put_u8(pph.peer_flags);
1210 buf.extend_from_slice(&pph.peer_distinguisher);
1211
1212 // "Peer Address: The remote IP address associated with the TCP session
1213 // over which the encapsulated PDU was received. It is 4 bytes long if
1214 // an IPv4 address is carried in this field (with the 12 most significant
1215 // bytes zero-filled) and 16 bytes long if an IPv6 address is carried in
1216 // this field."
1217 //
1218 // From: https://www.rfc-editor.org/rfc/rfc7854.html#section-4.2
1219 match pph.peer_address {
1220 IpAddr::V4(addr) => {
1221 buf.resize(buf.len() + 12, 0u8);
1222 buf.extend_from_slice(&addr.octets());
1223 if pph.is_ipv6() {
1224 warnings.push(format!("RFC 7854 section 4.2 Per-Peer Header violation: Peer Flags V-bit is SET but should be unset because peer address {addr} is an IPv4 address, not IPv6."));
1225 }
1226 }
1227 IpAddr::V6(addr) => {
1228 buf.extend_from_slice(&addr.octets());
1229 if pph.is_ipv4() {
1230 warnings.push(format!("RFC 7854 section 4.2 Per-Peer Header violation: Peer Flags V-bit is NOT set but should be set because peer address {addr} is an IPv6 address, not IPv4."));
1231 }
1232 }
1233 }
1234
1235 buf.extend_from_slice(&pph.peer_as.into_u32().to_be_bytes()); // assumes 32-bit ASN
1236 buf.extend_from_slice(&pph.peer_bgp_id);
1237 buf.extend_from_slice(&epoch_seconds.to_be_bytes());
1238 buf.extend_from_slice(&epoch_micros.to_be_bytes());
1239
1240 warnings
1241}
1242
1243fn push_bmp_information_tlv(
1244 buf: &mut BytesMut,
1245 tlv_type: InformationTlvType,
1246 tlv_value: &[u8],
1247) {
1248 // 0 1 2 3
1249 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1250 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1251 // | Information Type | Information Length |
1252 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1253 // | Information (variable) |
1254 // ~ ~
1255 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1256 //
1257 // From: https://www.rfc-editor.org/rfc/rfc7854.html#section-4.4
1258 buf.extend_from_slice(&information_tlv_type_to_be_bytes(tlv_type));
1259 buf.extend_from_slice(&(tlv_value.len() as u16).to_be_bytes());
1260 buf.extend_from_slice(tlv_value);
1261}
1262
1263fn push_bmp_termination_tlv(
1264 buf: &mut BytesMut,
1265 tlv_type: TerminationInformation,
1266 tlv_value: &[u8],
1267) {
1268 // 0 1 2 3
1269 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1270 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1271 // | Information Type | Information Length |
1272 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1273 // | Information (variable) |
1274 // ~ ~
1275 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1276 //
1277 // From: https://www.rfc-editor.org/rfc/rfc7854.html#section-4.4
1278 buf.extend_from_slice(&termination_tlv_type_to_be_bytes(tlv_type));
1279 buf.extend_from_slice(&(tlv_value.len() as u16).to_be_bytes());
1280 buf.extend_from_slice(tlv_value);
1281}
1282
1283fn information_tlv_type_to_be_bytes(typ: InformationTlvType) -> [u8; 2] {
1284 match typ {
1285 InformationTlvType::String => 0u16.to_be_bytes(),
1286 InformationTlvType::SysDesc => 1u16.to_be_bytes(),
1287 InformationTlvType::SysName => 2u16.to_be_bytes(),
1288 _ => unreachable!(),
1289 }
1290}
1291
1292fn termination_tlv_type_to_be_bytes(typ: TerminationInformation) -> [u8; 2] {
1293 match typ {
1294 TerminationInformation::CustomString(_) => 0u16.to_be_bytes(),
1295 TerminationInformation::AdminClose
1296 | TerminationInformation::Unspecified
1297 | TerminationInformation::OutOfResources
1298 | TerminationInformation::RedundantConnection
1299 | TerminationInformation::PermAdminClose => 1u16.to_be_bytes(),
1300 TerminationInformation::Undefined(_) => unreachable!(),
1301 }
1302}
1303
1304#[derive(Debug, PartialEq, Eq)]
1305pub struct MyPeerType(PeerType);
1306
1307impl From<PeerType> for MyPeerType {
1308 fn from(peer_type: PeerType) -> Self {
1309 MyPeerType(peer_type)
1310 }
1311}
1312
1313impl Deref for MyPeerType {
1314 type Target = PeerType;
1315
1316 fn deref(&self) -> &Self::Target {
1317 &self.0
1318 }
1319}
1320
1321impl FromStr for MyPeerType {
1322 type Err = anyhow::Error;
1323
1324 fn from_str(s: &str) -> Result<Self, Self::Err> {
1325 let peer_type = match s {
1326 "global" => PeerType::GlobalInstance,
1327 _ => todo!(),
1328 };
1329
1330 Ok(MyPeerType(peer_type))
1331 }
1332}
1333
1334#[derive(Default)]
1335pub struct Prefixes(Vec<Prefix>);
1336
1337impl Prefixes {
1338 pub fn new(prefixes: Vec<Prefix>) -> Self {
1339 Self(prefixes)
1340 }
1341}
1342
1343impl Deref for Prefixes {
1344 type Target = Vec<Prefix>;
1345
1346 fn deref(&self) -> &Self::Target {
1347 &self.0
1348 }
1349}
1350
1351impl FromStr for Prefixes {
1352 type Err = anyhow::Error;
1353
1354 fn from_str(s: &str) -> Result<Self, Self::Err> {
1355 match s.to_lowercase().as_str() {
1356 "" | "none" => Ok(Prefixes(vec![])),
1357
1358 _ => {
1359 let mut prefixes = Vec::new();
1360 for prefix_str in s.split(',') {
1361 prefixes.push(prefix_str.parse()?);
1362 }
1363 Ok(Prefixes(prefixes))
1364 }
1365 }
1366 }
1367}
1368
1369// Based on `div_ceil()` from Rust nightly.
1370pub const fn div_ceil(lhs: u8, rhs: u8) -> u8 {
1371 let d = lhs / rhs;
1372 let r = lhs % rhs;
1373 if r > 0 && rhs > 0 {
1374 d + 1
1375 } else {
1376 d
1377 }
1378}
1379
1380pub struct MyOriginType(OriginType);
1381
1382impl Deref for MyOriginType {
1383 type Target = OriginType;
1384
1385 fn deref(&self) -> &Self::Target {
1386 &self.0
1387 }
1388}
1389
1390impl FromStr for MyOriginType {
1391 type Err = anyhow::Error;
1392
1393 fn from_str(s: &str) -> Result<Self, Self::Err> {
1394 match s {
1395 "i" => Ok(Self(OriginType::Igp)),
1396 "e" => Ok(Self(OriginType::Egp)),
1397 "?" => Ok(Self(OriginType::Incomplete)),
1398 _ => Ok(s.parse::<u8>()?.into()),
1399 }
1400 }
1401}
1402
1403impl From<u8> for MyOriginType {
1404 fn from(v: u8) -> Self {
1405 let origin_type = match v {
1406 0 => OriginType::Igp,
1407 1 => OriginType::Egp,
1408 2 => OriginType::Incomplete,
1409 _ => OriginType::Unimplemented(v),
1410 };
1411 Self(origin_type)
1412 }
1413}
1414
1415impl From<&MyOriginType> for u8 {
1416 fn from(v: &MyOriginType) -> Self {
1417 match v {
1418 MyOriginType(OriginType::Igp) => 0,
1419 MyOriginType(OriginType::Egp) => 1,
1420 MyOriginType(OriginType::Incomplete) => 2,
1421 MyOriginType(OriginType::Unimplemented(v)) => *v,
1422 }
1423 }
1424}
1425
1426pub struct MyAsPath(HopPath);
1427
1428impl Deref for MyAsPath {
1429 type Target = HopPath;
1430
1431 fn deref(&self) -> &Self::Target {
1432 &self.0
1433 }
1434}
1435
1436impl FromStr for MyAsPath {
1437 type Err = anyhow::Error;
1438
1439 fn from_str(s: &str) -> Result<Self, Self::Err> {
1440 let mut hop_path = HopPath::new();
1441 if s.starts_with('[') && s.ends_with(']') {
1442 let s = &s[1..s.len() - 1];
1443 for asn in s.split(',') {
1444 let asn: Asn = asn.parse()?;
1445 hop_path.append(asn);
1446 }
1447 Ok(Self(hop_path))
1448 } else {
1449 Err(anyhow::anyhow!("Expected [asn, ...]"))
1450 }
1451 }
1452}
1453
1454pub struct MyNextHop(NextHop);
1455
1456impl Deref for MyNextHop {
1457 type Target = NextHop;
1458
1459 fn deref(&self) -> &Self::Target {
1460 &self.0
1461 }
1462}
1463
1464impl FromStr for MyNextHop {
1465 type Err = anyhow::Error;
1466
1467 fn from_str(s: &str) -> Result<Self, Self::Err> {
1468 let ip_addr: IpAddr = s.parse()?;
1469 match ip_addr {
1470 IpAddr::V4(addr) => Ok(MyNextHop(NextHop::Unicast(IpAddr::V4(addr)))),
1471 IpAddr::V6(addr) => Ok(MyNextHop(NextHop::Unicast(IpAddr::V6(addr)))),
1472 }
1473 }
1474}
1475
1476pub struct MyCommunities(Vec<Community>);
1477
1478impl Deref for MyCommunities {
1479 type Target = Vec<Community>;
1480
1481 fn deref(&self) -> &Self::Target {
1482 &self.0
1483 }
1484}
1485
1486impl FromStr for MyCommunities {
1487 type Err = anyhow::Error;
1488
1489 fn from_str(s: &str) -> Result<Self, Self::Err> {
1490 match s.to_lowercase().as_str() {
1491 "" | "none" => Ok(MyCommunities(vec![])),
1492
1493 _ => {
1494 let mut communities = Vec::new();
1495 for community_str in s.split(',') {
1496 communities.push(community_str.parse().unwrap());
1497 }
1498 Ok(MyCommunities(communities))
1499 }
1500 }
1501 }
1502}
1503
1504#[derive(Default)]
1505pub enum Announcements {
1506 #[default]
1507 None,
1508 Some {
1509 origin: MyOriginType,
1510 as_path: MyAsPath,
1511 next_hop: MyNextHop,
1512 communities: MyCommunities,
1513 prefixes: Prefixes,
1514 },
1515}
1516
1517impl FromStr for Announcements {
1518 type Err = anyhow::Error;
1519
1520 fn from_str(s: &str) -> Result<Self, Self::Err> {
1521 match s.to_lowercase().as_str() {
1522 "" | "none" => Ok(Self::None),
1523
1524 _ => {
1525 let parts: Vec<&str> = s.splitn(5, ' ').collect();
1526 assert_eq!(parts.len(), 5);
1527 let origin = parts[0].parse().unwrap();
1528 let as_path = parts[1].parse().unwrap();
1529 let next_hop = parts[2].parse().unwrap();
1530 let communities = parts[3].parse().unwrap();
1531 let prefixes = parts[4].parse().unwrap();
1532 Ok(Self::Some {
1533 origin,
1534 as_path,
1535 next_hop,
1536 communities,
1537 prefixes,
1538 })
1539 }
1540 }
1541 }
1542}