1use std::fmt;
11use std::net::Ipv4Addr;
12
13use crate::capability::Afi;
14use crate::error::{DecodeError, EncodeError};
15use crate::nlri::{Ipv4Prefix, Ipv6Prefix};
16
17pub const MAX_FLOWSPEC_NLRI_RULE_LEN: usize = 0x0FFF;
23
24#[expect(clippy::struct_excessive_bools)]
33#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
34pub struct NumericMatch {
35 pub end_of_list: bool,
37 pub and_bit: bool,
39 pub lt: bool,
41 pub gt: bool,
43 pub eq: bool,
45 pub value: u64,
47}
48
49#[expect(clippy::struct_excessive_bools)]
51#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
52pub struct BitmaskMatch {
53 pub end_of_list: bool,
55 pub and_bit: bool,
57 pub not_bit: bool,
59 pub match_bit: bool,
61 pub value: u16,
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
71pub struct Ipv6PrefixOffset {
72 pub prefix: Ipv6Prefix,
74 pub offset: u8,
76}
77
78#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
87pub enum FlowSpecComponent {
88 DestinationPrefix(FlowSpecPrefix),
90 SourcePrefix(FlowSpecPrefix),
92 IpProtocol(Vec<NumericMatch>),
94 Port(Vec<NumericMatch>),
96 DestinationPort(Vec<NumericMatch>),
98 SourcePort(Vec<NumericMatch>),
100 IcmpType(Vec<NumericMatch>),
102 IcmpCode(Vec<NumericMatch>),
104 TcpFlags(Vec<BitmaskMatch>),
106 PacketLength(Vec<NumericMatch>),
108 Dscp(Vec<NumericMatch>),
110 Fragment(Vec<BitmaskMatch>),
112 FlowLabel(Vec<NumericMatch>),
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
118pub enum FlowSpecPrefix {
119 V4(Ipv4Prefix),
121 V6(Ipv6PrefixOffset),
123}
124
125impl FlowSpecComponent {
126 #[must_use]
128 pub fn type_code(&self) -> u8 {
129 match self {
130 Self::DestinationPrefix(_) => 1,
131 Self::SourcePrefix(_) => 2,
132 Self::IpProtocol(_) => 3,
133 Self::Port(_) => 4,
134 Self::DestinationPort(_) => 5,
135 Self::SourcePort(_) => 6,
136 Self::IcmpType(_) => 7,
137 Self::IcmpCode(_) => 8,
138 Self::TcpFlags(_) => 9,
139 Self::PacketLength(_) => 10,
140 Self::Dscp(_) => 11,
141 Self::Fragment(_) => 12,
142 Self::FlowLabel(_) => 13,
143 }
144 }
145}
146
147#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
156pub struct FlowSpecRule {
157 pub components: Vec<FlowSpecComponent>,
159}
160
161impl FlowSpecRule {
162 pub fn validate(&self) -> Result<(), DecodeError> {
169 if self.components.is_empty() {
170 return Err(DecodeError::MalformedField {
171 message_type: "UPDATE",
172 detail: "FlowSpec rule has no components".to_string(),
173 });
174 }
175 for window in self.components.windows(2) {
176 if window[0].type_code() >= window[1].type_code() {
177 return Err(DecodeError::MalformedField {
178 message_type: "UPDATE",
179 detail: format!(
180 "FlowSpec components out of order: type {} >= {}",
181 window[0].type_code(),
182 window[1].type_code()
183 ),
184 });
185 }
186 }
187 Ok(())
188 }
189
190 #[must_use]
193 pub fn encoded_len(&self, afi: Afi) -> usize {
194 let mut rule_bytes = Vec::new();
195 encode_flowspec_rule(self, &mut rule_bytes, afi);
196 rule_bytes.len()
197 }
198
199 pub fn validate_encoded_len(&self, afi: Afi) -> Result<(), EncodeError> {
206 let len = self.encoded_len(afi);
207 if len > MAX_FLOWSPEC_NLRI_RULE_LEN {
208 return Err(EncodeError::ValueOutOfRange {
209 field: "FlowSpec NLRI rule length",
210 value: len.to_string(),
211 });
212 }
213 Ok(())
214 }
215
216 #[must_use]
218 pub fn display_string(&self) -> String {
219 let mut parts = Vec::new();
220 for c in &self.components {
221 parts.push(format_component(c));
222 }
223 parts.join(" && ")
224 }
225
226 #[must_use]
228 pub fn destination_prefix(&self) -> Option<crate::nlri::Prefix> {
229 self.components.iter().find_map(|c| match c {
230 FlowSpecComponent::DestinationPrefix(FlowSpecPrefix::V4(p)) => {
231 Some(crate::nlri::Prefix::V4(*p))
232 }
233 FlowSpecComponent::DestinationPrefix(FlowSpecPrefix::V6(p)) => {
234 Some(crate::nlri::Prefix::V6(p.prefix))
235 }
236 _ => None,
237 })
238 }
239}
240
241impl fmt::Display for FlowSpecRule {
242 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
243 write!(f, "{}", self.display_string())
244 }
245}
246
247#[derive(Debug, Clone, PartialEq)]
253pub enum FlowSpecAction {
254 TrafficRateBytes {
256 asn: u16,
258 rate: f32,
260 },
261 TrafficRatePackets {
263 asn: u16,
265 rate: f32,
267 },
268 TrafficAction {
270 sample: bool,
272 terminal: bool,
274 },
275 TrafficMarking {
277 dscp: u8,
279 },
280 Redirect2Octet {
282 asn: u16,
284 value: u32,
286 },
287 RedirectIpv4 {
289 addr: Ipv4Addr,
291 value: u16,
293 },
294 Redirect4Octet {
296 asn: u32,
298 value: u16,
300 },
301}
302
303impl crate::attribute::ExtendedCommunity {
304 #[must_use]
306 pub fn as_flowspec_action(&self) -> Option<FlowSpecAction> {
307 let raw = self.as_u64();
308 let bytes = raw.to_be_bytes();
309 let type_high = bytes[0];
310 let subtype = bytes[1];
311
312 match (type_high, subtype) {
313 (0x80, 0x06) => {
315 let asn = u16::from_be_bytes([bytes[2], bytes[3]]);
316 let rate = f32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
317 Some(FlowSpecAction::TrafficRateBytes { asn, rate })
318 }
319 (0x80, 0x07) => {
321 let flags = bytes[7];
323 Some(FlowSpecAction::TrafficAction {
324 sample: flags & 0x02 != 0,
325 terminal: flags & 0x01 != 0,
326 })
327 }
328 (0x80, 0x08) => {
330 let asn = u16::from_be_bytes([bytes[2], bytes[3]]);
331 let value = u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
332 Some(FlowSpecAction::Redirect2Octet { asn, value })
333 }
334 (0x80, 0x09) => Some(FlowSpecAction::TrafficMarking {
336 dscp: bytes[7] & 0x3F,
337 }),
338 (0x80, 0x0c) => {
340 let asn = u16::from_be_bytes([bytes[2], bytes[3]]);
341 let rate = f32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]);
342 Some(FlowSpecAction::TrafficRatePackets { asn, rate })
343 }
344 (0x81, 0x08) => {
346 let addr = Ipv4Addr::new(bytes[2], bytes[3], bytes[4], bytes[5]);
347 let value = u16::from_be_bytes([bytes[6], bytes[7]]);
348 Some(FlowSpecAction::RedirectIpv4 { addr, value })
349 }
350 (0x82, 0x08) => {
352 let asn = u32::from_be_bytes([bytes[2], bytes[3], bytes[4], bytes[5]]);
353 let value = u16::from_be_bytes([bytes[6], bytes[7]]);
354 Some(FlowSpecAction::Redirect4Octet { asn, value })
355 }
356 _ => None,
357 }
358 }
359
360 #[must_use]
362 pub fn from_flowspec_action(action: &FlowSpecAction) -> Self {
363 let mut bytes = [0u8; 8];
364 match action {
365 FlowSpecAction::TrafficRateBytes { asn, rate } => {
366 bytes[0] = 0x80;
367 bytes[1] = 0x06;
368 bytes[2..4].copy_from_slice(&asn.to_be_bytes());
369 bytes[4..8].copy_from_slice(&rate.to_be_bytes());
370 }
371 FlowSpecAction::TrafficRatePackets { asn, rate } => {
372 bytes[0] = 0x80;
373 bytes[1] = 0x0c;
374 bytes[2..4].copy_from_slice(&asn.to_be_bytes());
375 bytes[4..8].copy_from_slice(&rate.to_be_bytes());
376 }
377 FlowSpecAction::TrafficAction { sample, terminal } => {
378 bytes[0] = 0x80;
379 bytes[1] = 0x07;
380 let mut flags = 0u8;
381 if *sample {
382 flags |= 0x02;
383 }
384 if *terminal {
385 flags |= 0x01;
386 }
387 bytes[7] = flags;
388 }
389 FlowSpecAction::TrafficMarking { dscp } => {
390 bytes[0] = 0x80;
391 bytes[1] = 0x09;
392 bytes[7] = *dscp & 0x3F;
393 }
394 FlowSpecAction::Redirect2Octet { asn, value } => {
395 bytes[0] = 0x80;
396 bytes[1] = 0x08;
397 bytes[2..4].copy_from_slice(&asn.to_be_bytes());
398 bytes[4..8].copy_from_slice(&value.to_be_bytes());
399 }
400 FlowSpecAction::RedirectIpv4 { addr, value } => {
401 bytes[0] = 0x81;
402 bytes[1] = 0x08;
403 bytes[2..6].copy_from_slice(&addr.octets());
404 bytes[6..8].copy_from_slice(&value.to_be_bytes());
405 }
406 FlowSpecAction::Redirect4Octet { asn, value } => {
407 bytes[0] = 0x82;
408 bytes[1] = 0x08;
409 bytes[2..6].copy_from_slice(&asn.to_be_bytes());
410 bytes[6..8].copy_from_slice(&value.to_be_bytes());
411 }
412 }
413 Self::new(u64::from_be_bytes(bytes))
414 }
415}
416
417pub fn decode_flowspec_nlri(mut buf: &[u8], afi: Afi) -> Result<Vec<FlowSpecRule>, DecodeError> {
431 let mut rules = Vec::new();
432 while !buf.is_empty() {
433 let (rule_len, consumed) = decode_flowspec_length(buf)?;
435 buf = &buf[consumed..];
436 if buf.len() < rule_len {
437 return Err(DecodeError::MalformedField {
438 message_type: "UPDATE",
439 detail: format!(
440 "FlowSpec NLRI truncated: need {rule_len} bytes, have {}",
441 buf.len()
442 ),
443 });
444 }
445 let rule_bytes = &buf[..rule_len];
446 buf = &buf[rule_len..];
447
448 let rule = decode_flowspec_rule(rule_bytes, afi)?;
449 rule.validate()?;
450 rules.push(rule);
451 }
452 Ok(rules)
453}
454
455pub fn try_encode_flowspec_nlri(
462 rules: &[FlowSpecRule],
463 buf: &mut Vec<u8>,
464 afi: Afi,
465) -> Result<(), EncodeError> {
466 let start_len = buf.len();
467 for rule in rules {
468 let mut rule_bytes = Vec::new();
469 encode_flowspec_rule(rule, &mut rule_bytes, afi);
470 if rule_bytes.len() > MAX_FLOWSPEC_NLRI_RULE_LEN {
471 buf.truncate(start_len);
472 return Err(EncodeError::ValueOutOfRange {
473 field: "FlowSpec NLRI rule length",
474 value: rule_bytes.len().to_string(),
475 });
476 }
477 encode_flowspec_length(rule_bytes.len(), buf);
478 buf.extend_from_slice(&rule_bytes);
479 }
480 Ok(())
481}
482
483pub(crate) fn encode_flowspec_nlri(rules: &[FlowSpecRule], buf: &mut Vec<u8>, afi: Afi) {
490 try_encode_flowspec_nlri(rules, buf, afi)
491 .expect("FlowSpec NLRI encoder received an oversized rule");
492}
493
494fn decode_flowspec_length(buf: &[u8]) -> Result<(usize, usize), DecodeError> {
496 if buf.is_empty() {
497 return Err(DecodeError::MalformedField {
498 message_type: "UPDATE",
499 detail: "FlowSpec NLRI length: empty buffer".to_string(),
500 });
501 }
502 if buf[0] < 0xF0 {
503 Ok((buf[0] as usize, 1))
504 } else {
505 if buf.len() < 2 {
506 return Err(DecodeError::MalformedField {
507 message_type: "UPDATE",
508 detail: "FlowSpec NLRI 2-byte length truncated".to_string(),
509 });
510 }
511 let len = u16::from_be_bytes([buf[0], buf[1]]) as usize;
512 let real_len = len & 0x0FFF;
514 Ok((real_len, 2))
515 }
516}
517
518fn encode_flowspec_length(len: usize, buf: &mut Vec<u8>) {
520 if len < 0xF0 {
521 #[expect(clippy::cast_possible_truncation)]
522 buf.push(len as u8);
523 } else {
524 #[expect(clippy::cast_possible_truncation)]
525 let val = (0xF000 | (len & 0x0FFF)) as u16;
526 buf.extend_from_slice(&val.to_be_bytes());
527 }
528}
529
530fn decode_flowspec_rule(mut buf: &[u8], afi: Afi) -> Result<FlowSpecRule, DecodeError> {
532 let mut components = Vec::new();
533 while !buf.is_empty() {
534 let (component, consumed) = decode_component(buf, afi)?;
535 components.push(component);
536 buf = &buf[consumed..];
537 }
538 Ok(FlowSpecRule { components })
539}
540
541fn decode_component(buf: &[u8], afi: Afi) -> Result<(FlowSpecComponent, usize), DecodeError> {
544 if buf.is_empty() {
545 return Err(DecodeError::MalformedField {
546 message_type: "UPDATE",
547 detail: "FlowSpec component: empty buffer".to_string(),
548 });
549 }
550
551 let type_code = buf[0];
552 let rest = &buf[1..];
553
554 match type_code {
555 1 | 2 => {
556 let (prefix, consumed) = decode_prefix_component(rest, afi)?;
558 let component = if type_code == 1 {
559 FlowSpecComponent::DestinationPrefix(prefix)
560 } else {
561 FlowSpecComponent::SourcePrefix(prefix)
562 };
563 Ok((component, 1 + consumed))
564 }
565 3 => {
566 let (ops, consumed) = decode_numeric_ops(rest)?;
567 Ok((FlowSpecComponent::IpProtocol(ops), 1 + consumed))
568 }
569 4 => {
570 let (ops, consumed) = decode_numeric_ops(rest)?;
571 Ok((FlowSpecComponent::Port(ops), 1 + consumed))
572 }
573 5 => {
574 let (ops, consumed) = decode_numeric_ops(rest)?;
575 Ok((FlowSpecComponent::DestinationPort(ops), 1 + consumed))
576 }
577 6 => {
578 let (ops, consumed) = decode_numeric_ops(rest)?;
579 Ok((FlowSpecComponent::SourcePort(ops), 1 + consumed))
580 }
581 7 => {
582 let (ops, consumed) = decode_numeric_ops(rest)?;
583 Ok((FlowSpecComponent::IcmpType(ops), 1 + consumed))
584 }
585 8 => {
586 let (ops, consumed) = decode_numeric_ops(rest)?;
587 Ok((FlowSpecComponent::IcmpCode(ops), 1 + consumed))
588 }
589 9 => {
590 let (ops, consumed) = decode_bitmask_ops(rest)?;
591 Ok((FlowSpecComponent::TcpFlags(ops), 1 + consumed))
592 }
593 10 => {
594 let (ops, consumed) = decode_numeric_ops(rest)?;
595 Ok((FlowSpecComponent::PacketLength(ops), 1 + consumed))
596 }
597 11 => {
598 let (ops, consumed) = decode_numeric_ops(rest)?;
599 Ok((FlowSpecComponent::Dscp(ops), 1 + consumed))
600 }
601 12 => {
602 let (ops, consumed) = decode_bitmask_ops(rest)?;
603 Ok((FlowSpecComponent::Fragment(ops), 1 + consumed))
604 }
605 13 => {
606 let (ops, consumed) = decode_numeric_ops(rest)?;
607 Ok((FlowSpecComponent::FlowLabel(ops), 1 + consumed))
608 }
609 _ => Err(DecodeError::MalformedField {
610 message_type: "UPDATE",
611 detail: format!("unknown FlowSpec component type {type_code}"),
612 }),
613 }
614}
615
616fn decode_prefix_component(buf: &[u8], afi: Afi) -> Result<(FlowSpecPrefix, usize), DecodeError> {
618 match afi {
619 Afi::L2Vpn => Err(DecodeError::MalformedField {
620 message_type: "UPDATE",
621 detail: "FlowSpec prefix component not valid for L2VPN family".to_string(),
622 }),
623 Afi::Ipv4 => {
624 if buf.is_empty() {
626 return Err(DecodeError::MalformedField {
627 message_type: "UPDATE",
628 detail: "FlowSpec IPv4 prefix: missing length byte".to_string(),
629 });
630 }
631 let prefix_len = buf[0];
632 if prefix_len > 32 {
633 return Err(DecodeError::MalformedField {
634 message_type: "UPDATE",
635 detail: format!("FlowSpec IPv4 prefix length {prefix_len} > 32"),
636 });
637 }
638 let byte_count = (prefix_len as usize).div_ceil(8);
639 if buf.len() < 1 + byte_count {
640 return Err(DecodeError::MalformedField {
641 message_type: "UPDATE",
642 detail: "FlowSpec IPv4 prefix truncated".to_string(),
643 });
644 }
645 let mut octets = [0u8; 4];
646 octets[..byte_count].copy_from_slice(&buf[1..=byte_count]);
647 let addr = Ipv4Addr::from(octets);
648 Ok((
649 FlowSpecPrefix::V4(Ipv4Prefix::new(addr, prefix_len)),
650 1 + byte_count,
651 ))
652 }
653 Afi::Ipv6 => {
654 if buf.len() < 2 {
656 return Err(DecodeError::MalformedField {
657 message_type: "UPDATE",
658 detail: "FlowSpec IPv6 prefix: need length+offset bytes".to_string(),
659 });
660 }
661 let prefix_len = buf[0];
662 let offset = buf[1];
663 if prefix_len > 128 {
664 return Err(DecodeError::MalformedField {
665 message_type: "UPDATE",
666 detail: format!("FlowSpec IPv6 prefix length {prefix_len} > 128"),
667 });
668 }
669 let byte_count = (prefix_len as usize).div_ceil(8);
670 if buf.len() < 2 + byte_count {
671 return Err(DecodeError::MalformedField {
672 message_type: "UPDATE",
673 detail: "FlowSpec IPv6 prefix truncated".to_string(),
674 });
675 }
676 let mut octets = [0u8; 16];
677 octets[..byte_count].copy_from_slice(&buf[2..2 + byte_count]);
678 let addr = std::net::Ipv6Addr::from(octets);
679 Ok((
680 FlowSpecPrefix::V6(Ipv6PrefixOffset {
681 prefix: Ipv6Prefix::new(addr, prefix_len),
682 offset,
683 }),
684 2 + byte_count,
685 ))
686 }
687 }
688}
689
690fn decode_numeric_ops(mut buf: &[u8]) -> Result<(Vec<NumericMatch>, usize), DecodeError> {
692 let mut ops = Vec::new();
693 let start_len = buf.len();
694 loop {
695 if buf.is_empty() {
696 return Err(DecodeError::MalformedField {
697 message_type: "UPDATE",
698 detail: "FlowSpec numeric operators: unexpected end of data".to_string(),
699 });
700 }
701 let op_byte = buf[0];
702 buf = &buf[1..];
703
704 let end_of_list = op_byte & 0x80 != 0;
705 let and_bit = op_byte & 0x40 != 0;
706 let value_len_code = (op_byte >> 4) & 0x03;
707 let value_len = 1usize << value_len_code; let lt = op_byte & 0x04 != 0;
709 let gt = op_byte & 0x02 != 0;
710 let eq = op_byte & 0x01 != 0;
711
712 if buf.len() < value_len {
713 return Err(DecodeError::MalformedField {
714 message_type: "UPDATE",
715 detail: format!(
716 "FlowSpec numeric value truncated: need {value_len}, have {}",
717 buf.len()
718 ),
719 });
720 }
721
722 let value = match value_len {
723 1 => u64::from(buf[0]),
724 2 => u64::from(u16::from_be_bytes([buf[0], buf[1]])),
725 4 => u64::from(u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]])),
726 8 => u64::from_be_bytes([
727 buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
728 ]),
729 _ => unreachable!(),
730 };
731 buf = &buf[value_len..];
732
733 ops.push(NumericMatch {
734 end_of_list,
735 and_bit,
736 lt,
737 gt,
738 eq,
739 value,
740 });
741
742 if end_of_list {
743 break;
744 }
745 }
746 Ok((ops, start_len - buf.len()))
747}
748
749fn decode_bitmask_ops(mut buf: &[u8]) -> Result<(Vec<BitmaskMatch>, usize), DecodeError> {
751 let mut ops = Vec::new();
752 let start_len = buf.len();
753 loop {
754 if buf.is_empty() {
755 return Err(DecodeError::MalformedField {
756 message_type: "UPDATE",
757 detail: "FlowSpec bitmask operators: unexpected end of data".to_string(),
758 });
759 }
760 let op_byte = buf[0];
761 buf = &buf[1..];
762
763 let end_of_list = op_byte & 0x80 != 0;
764 let and_bit = op_byte & 0x40 != 0;
765 let value_len_code = (op_byte >> 4) & 0x03;
766 let value_len = 1usize << value_len_code; let not_bit = op_byte & 0x02 != 0;
768 let match_bit = op_byte & 0x01 != 0;
769
770 if buf.len() < value_len {
771 return Err(DecodeError::MalformedField {
772 message_type: "UPDATE",
773 detail: format!(
774 "FlowSpec bitmask value truncated: need {value_len}, have {}",
775 buf.len()
776 ),
777 });
778 }
779
780 let value = match value_len {
781 1 => u16::from(buf[0]),
782 2 => u16::from_be_bytes([buf[0], buf[1]]),
783 _ => {
784 return Err(DecodeError::MalformedField {
785 message_type: "UPDATE",
786 detail: format!("FlowSpec bitmask value length {value_len} unsupported"),
787 });
788 }
789 };
790 buf = &buf[value_len..];
791
792 ops.push(BitmaskMatch {
793 end_of_list,
794 and_bit,
795 not_bit,
796 match_bit,
797 value,
798 });
799
800 if end_of_list {
801 break;
802 }
803 }
804 Ok((ops, start_len - buf.len()))
805}
806
807fn encode_flowspec_rule(rule: &FlowSpecRule, buf: &mut Vec<u8>, afi: Afi) {
813 for component in &rule.components {
814 buf.push(component.type_code());
815 match component {
816 FlowSpecComponent::DestinationPrefix(p) | FlowSpecComponent::SourcePrefix(p) => {
817 encode_prefix_component(p, buf, afi);
818 }
819 FlowSpecComponent::IpProtocol(ops)
820 | FlowSpecComponent::Port(ops)
821 | FlowSpecComponent::DestinationPort(ops)
822 | FlowSpecComponent::SourcePort(ops)
823 | FlowSpecComponent::IcmpType(ops)
824 | FlowSpecComponent::IcmpCode(ops)
825 | FlowSpecComponent::PacketLength(ops)
826 | FlowSpecComponent::Dscp(ops)
827 | FlowSpecComponent::FlowLabel(ops) => {
828 encode_numeric_ops(ops, buf);
829 }
830 FlowSpecComponent::TcpFlags(ops) | FlowSpecComponent::Fragment(ops) => {
831 encode_bitmask_ops(ops, buf);
832 }
833 }
834 }
835}
836
837fn encode_prefix_component(prefix: &FlowSpecPrefix, buf: &mut Vec<u8>, _afi: Afi) {
839 match prefix {
840 FlowSpecPrefix::V4(p) => {
841 buf.push(p.len);
842 let byte_count = (p.len as usize).div_ceil(8);
843 buf.extend_from_slice(&p.addr.octets()[..byte_count]);
844 }
845 FlowSpecPrefix::V6(p) => {
846 buf.push(p.prefix.len);
847 buf.push(p.offset);
848 let byte_count = (p.prefix.len as usize).div_ceil(8);
849 buf.extend_from_slice(&p.prefix.addr.octets()[..byte_count]);
850 }
851 }
852}
853
854fn numeric_value_len_code(value: u64) -> u8 {
856 if value <= 0xFF {
857 0 } else if value <= 0xFFFF {
859 1 } else if value <= 0xFFFF_FFFF {
861 2 } else {
863 3 }
865}
866
867fn encode_numeric_ops(ops: &[NumericMatch], buf: &mut Vec<u8>) {
869 for (i, op) in ops.iter().enumerate() {
870 let is_last = i == ops.len() - 1;
871 let len_code = numeric_value_len_code(op.value);
872 let mut op_byte: u8 = 0;
873 if is_last {
874 op_byte |= 0x80; }
876 if op.and_bit {
877 op_byte |= 0x40;
878 }
879 op_byte |= len_code << 4;
880 if op.lt {
881 op_byte |= 0x04;
882 }
883 if op.gt {
884 op_byte |= 0x02;
885 }
886 if op.eq {
887 op_byte |= 0x01;
888 }
889 buf.push(op_byte);
890 let value_len = 1usize << len_code;
891 match value_len {
892 1 => {
893 #[expect(clippy::cast_possible_truncation)]
894 buf.push(op.value as u8);
895 }
896 2 => {
897 #[expect(clippy::cast_possible_truncation)]
898 buf.extend_from_slice(&(op.value as u16).to_be_bytes());
899 }
900 4 => {
901 #[expect(clippy::cast_possible_truncation)]
902 buf.extend_from_slice(&(op.value as u32).to_be_bytes());
903 }
904 8 => buf.extend_from_slice(&op.value.to_be_bytes()),
905 _ => unreachable!(),
906 }
907 }
908}
909
910fn bitmask_value_len_code(value: u16) -> u8 {
912 u8::from(value > 0xFF)
913}
914
915fn encode_bitmask_ops(ops: &[BitmaskMatch], buf: &mut Vec<u8>) {
917 for (i, op) in ops.iter().enumerate() {
918 let is_last = i == ops.len() - 1;
919 let len_code = bitmask_value_len_code(op.value);
920 let mut op_byte: u8 = 0;
921 if is_last {
922 op_byte |= 0x80; }
924 if op.and_bit {
925 op_byte |= 0x40;
926 }
927 op_byte |= len_code << 4;
928 if op.not_bit {
929 op_byte |= 0x02;
930 }
931 if op.match_bit {
932 op_byte |= 0x01;
933 }
934 buf.push(op_byte);
935 match 1usize << len_code {
936 1 => {
937 #[expect(clippy::cast_possible_truncation)]
938 buf.push(op.value as u8);
939 }
940 2 => buf.extend_from_slice(&op.value.to_be_bytes()),
941 _ => unreachable!(),
942 }
943 }
944}
945
946fn format_component(c: &FlowSpecComponent) -> String {
951 match c {
952 FlowSpecComponent::DestinationPrefix(p) => format!("dst {}", format_prefix(p)),
953 FlowSpecComponent::SourcePrefix(p) => format!("src {}", format_prefix(p)),
954 FlowSpecComponent::IpProtocol(ops) => format!("proto {}", format_numeric(ops)),
955 FlowSpecComponent::Port(ops) => format!("port {}", format_numeric(ops)),
956 FlowSpecComponent::DestinationPort(ops) => format!("dport {}", format_numeric(ops)),
957 FlowSpecComponent::SourcePort(ops) => format!("sport {}", format_numeric(ops)),
958 FlowSpecComponent::IcmpType(ops) => format!("icmp-type {}", format_numeric(ops)),
959 FlowSpecComponent::IcmpCode(ops) => format!("icmp-code {}", format_numeric(ops)),
960 FlowSpecComponent::TcpFlags(ops) => format!("tcp-flags {}", format_bitmask(ops)),
961 FlowSpecComponent::PacketLength(ops) => format!("pkt-len {}", format_numeric(ops)),
962 FlowSpecComponent::Dscp(ops) => format!("dscp {}", format_numeric(ops)),
963 FlowSpecComponent::Fragment(ops) => format!("fragment {}", format_bitmask(ops)),
964 FlowSpecComponent::FlowLabel(ops) => format!("flow-label {}", format_numeric(ops)),
965 }
966}
967
968fn format_prefix(p: &FlowSpecPrefix) -> String {
969 match p {
970 FlowSpecPrefix::V4(v4) => format!("{}/{}", v4.addr, v4.len),
971 FlowSpecPrefix::V6(v6) => {
972 if v6.offset == 0 {
973 format!("{}/{}", v6.prefix.addr, v6.prefix.len)
974 } else {
975 format!("{}/{} offset {}", v6.prefix.addr, v6.prefix.len, v6.offset)
976 }
977 }
978 }
979}
980
981fn format_numeric(ops: &[NumericMatch]) -> String {
982 let mut parts = Vec::new();
983 for op in ops {
984 let cmp = match (op.lt, op.gt, op.eq) {
985 (false, false, true) => "==",
986 (true, false, false) => "<",
987 (false, true, false) => ">",
988 (true, false, true) => "<=",
989 (false, true, true) => ">=",
990 (true, true, false) => "!=",
991 _ => "?",
992 };
993 parts.push(format!("{cmp}{}", op.value));
994 }
995 parts.join(",")
996}
997
998fn format_bitmask(ops: &[BitmaskMatch]) -> String {
999 let mut parts = Vec::new();
1000 for op in ops {
1001 let prefix = if op.not_bit { "!" } else { "" };
1002 let suffix = if op.match_bit { "/match" } else { "" };
1003 parts.push(format!("{prefix}0x{:x}{suffix}", op.value));
1004 }
1005 parts.join(",")
1006}
1007
1008#[cfg(test)]
1013mod tests {
1014 use super::*;
1015 use std::net::Ipv4Addr;
1016
1017 fn oversized_rule() -> FlowSpecRule {
1018 let mut ops: Vec<NumericMatch> = (0..2_200)
1019 .map(|i| NumericMatch {
1020 end_of_list: false,
1021 and_bit: i != 0,
1022 lt: false,
1023 gt: false,
1024 eq: true,
1025 value: i,
1026 })
1027 .collect();
1028 ops.last_mut().expect("non-empty test rule").end_of_list = true;
1029 FlowSpecRule {
1030 components: vec![FlowSpecComponent::Port(ops)],
1031 }
1032 }
1033
1034 fn exact_max_len_rule() -> FlowSpecRule {
1035 let mut ops: Vec<NumericMatch> = (0..2047)
1037 .map(|i| NumericMatch {
1038 end_of_list: false,
1039 and_bit: i != 0,
1040 lt: false,
1041 gt: false,
1042 eq: true,
1043 value: 1,
1044 })
1045 .collect();
1046 ops.last_mut().expect("non-empty test rule").end_of_list = true;
1047 FlowSpecRule {
1048 components: vec![FlowSpecComponent::Port(ops)],
1049 }
1050 }
1051
1052 #[test]
1053 fn numeric_ops_roundtrip() {
1054 let ops = vec![
1055 NumericMatch {
1056 end_of_list: false,
1057 and_bit: false,
1058 lt: false,
1059 gt: false,
1060 eq: true,
1061 value: 6,
1062 },
1063 NumericMatch {
1064 end_of_list: true,
1065 and_bit: false,
1066 lt: false,
1067 gt: false,
1068 eq: true,
1069 value: 17,
1070 },
1071 ];
1072 let mut buf = Vec::new();
1073 encode_numeric_ops(&ops, &mut buf);
1074 let (decoded, consumed) = decode_numeric_ops(&buf).unwrap();
1075 assert_eq!(consumed, buf.len());
1076 assert_eq!(decoded.len(), 2);
1077 assert_eq!(decoded[0].value, 6);
1078 assert!(decoded[0].eq);
1079 assert_eq!(decoded[1].value, 17);
1080 assert!(decoded[1].end_of_list);
1081 }
1082
1083 #[test]
1084 fn bitmask_ops_roundtrip() {
1085 let ops = vec![BitmaskMatch {
1086 end_of_list: true,
1087 and_bit: false,
1088 not_bit: false,
1089 match_bit: true,
1090 value: 0x02, }];
1092 let mut buf = Vec::new();
1093 encode_bitmask_ops(&ops, &mut buf);
1094 let (decoded, consumed) = decode_bitmask_ops(&buf).unwrap();
1095 assert_eq!(consumed, buf.len());
1096 assert_eq!(decoded.len(), 1);
1097 assert_eq!(decoded[0].value, 0x02);
1098 assert!(decoded[0].match_bit);
1099 }
1100
1101 #[test]
1102 fn ipv4_prefix_component_roundtrip() {
1103 let prefix = FlowSpecPrefix::V4(Ipv4Prefix::new(Ipv4Addr::new(10, 0, 0, 0), 8));
1104 let mut buf = Vec::new();
1105 encode_prefix_component(&prefix, &mut buf, Afi::Ipv4);
1106 let (decoded, consumed) = decode_prefix_component(&buf, Afi::Ipv4).unwrap();
1107 assert_eq!(consumed, buf.len());
1108 assert_eq!(decoded, prefix);
1109 }
1110
1111 #[test]
1112 fn ipv6_prefix_component_roundtrip() {
1113 let prefix = FlowSpecPrefix::V6(Ipv6PrefixOffset {
1114 prefix: Ipv6Prefix::new(std::net::Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0), 32),
1115 offset: 0,
1116 });
1117 let mut buf = Vec::new();
1118 encode_prefix_component(&prefix, &mut buf, Afi::Ipv6);
1119 let (decoded, consumed) = decode_prefix_component(&buf, Afi::Ipv6).unwrap();
1120 assert_eq!(consumed, buf.len());
1121 assert_eq!(decoded, prefix);
1122 }
1123
1124 #[test]
1125 fn simple_ipv4_rule_roundtrip() {
1126 let rule = FlowSpecRule {
1127 components: vec![
1128 FlowSpecComponent::DestinationPrefix(FlowSpecPrefix::V4(Ipv4Prefix::new(
1129 Ipv4Addr::new(10, 0, 0, 0),
1130 24,
1131 ))),
1132 FlowSpecComponent::IpProtocol(vec![NumericMatch {
1133 end_of_list: true,
1134 and_bit: false,
1135 lt: false,
1136 gt: false,
1137 eq: true,
1138 value: 6, }]),
1140 FlowSpecComponent::DestinationPort(vec![NumericMatch {
1141 end_of_list: true,
1142 and_bit: false,
1143 lt: false,
1144 gt: false,
1145 eq: true,
1146 value: 80,
1147 }]),
1148 ],
1149 };
1150 let mut buf = Vec::new();
1151 encode_flowspec_nlri(std::slice::from_ref(&rule), &mut buf, Afi::Ipv4);
1152 let decoded = decode_flowspec_nlri(&buf, Afi::Ipv4).unwrap();
1153 assert_eq!(decoded.len(), 1);
1154 assert_eq!(decoded[0], rule);
1155 }
1156
1157 #[test]
1158 fn multi_rule_roundtrip() {
1159 let rule1 = FlowSpecRule {
1160 components: vec![FlowSpecComponent::IpProtocol(vec![NumericMatch {
1161 end_of_list: true,
1162 and_bit: false,
1163 lt: false,
1164 gt: false,
1165 eq: true,
1166 value: 17, }])],
1168 };
1169 let rule2 = FlowSpecRule {
1170 components: vec![FlowSpecComponent::DestinationPort(vec![NumericMatch {
1171 end_of_list: true,
1172 and_bit: false,
1173 lt: false,
1174 gt: false,
1175 eq: true,
1176 value: 53,
1177 }])],
1178 };
1179 let mut buf = Vec::new();
1180 encode_flowspec_nlri(&[rule1.clone(), rule2.clone()], &mut buf, Afi::Ipv4);
1181 let decoded = decode_flowspec_nlri(&buf, Afi::Ipv4).unwrap();
1182 assert_eq!(decoded.len(), 2);
1183 assert_eq!(decoded[0], rule1);
1184 assert_eq!(decoded[1], rule2);
1185 }
1186
1187 #[test]
1188 fn component_type_ordering_validated() {
1189 let rule = FlowSpecRule {
1190 components: vec![
1191 FlowSpecComponent::DestinationPort(vec![NumericMatch {
1192 end_of_list: true,
1193 and_bit: false,
1194 lt: false,
1195 gt: false,
1196 eq: true,
1197 value: 80,
1198 }]),
1199 FlowSpecComponent::IpProtocol(vec![NumericMatch {
1200 end_of_list: true,
1201 and_bit: false,
1202 lt: false,
1203 gt: false,
1204 eq: true,
1205 value: 6,
1206 }]),
1207 ],
1208 };
1209 assert!(rule.validate().is_err());
1210 }
1211
1212 #[test]
1213 fn empty_rule_rejected() {
1214 let rule = FlowSpecRule { components: vec![] };
1215 assert!(rule.validate().is_err());
1216 }
1217
1218 #[test]
1219 fn two_byte_length_prefix() {
1220 let mut buf = Vec::new();
1222 let len = 250; encode_flowspec_length(len, &mut buf);
1224 assert_eq!(buf.len(), 2);
1225 let (decoded_len, consumed) = decode_flowspec_length(&buf).unwrap();
1226 assert_eq!(decoded_len, len);
1227 assert_eq!(consumed, 2);
1228 }
1229
1230 #[test]
1231 fn encoded_len_rejects_over_4095_byte_rule() {
1232 let rule = oversized_rule();
1233 assert!(rule.encoded_len(Afi::Ipv4) > MAX_FLOWSPEC_NLRI_RULE_LEN);
1234
1235 let err = rule.validate_encoded_len(Afi::Ipv4).unwrap_err();
1236 let EncodeError::ValueOutOfRange { field, value } = err else {
1237 panic!("expected ValueOutOfRange");
1238 };
1239 assert_eq!(field, "FlowSpec NLRI rule length");
1240 assert_eq!(value, rule.encoded_len(Afi::Ipv4).to_string());
1241 }
1242
1243 #[test]
1244 fn encoded_len_accepts_exact_4095_byte_rule() {
1245 let rule = exact_max_len_rule();
1246 assert_eq!(rule.encoded_len(Afi::Ipv4), MAX_FLOWSPEC_NLRI_RULE_LEN);
1247 rule.validate_encoded_len(Afi::Ipv4).unwrap();
1248 }
1249
1250 #[test]
1251 fn try_encode_flowspec_nlri_rejects_oversized_rule_without_mutating_buffer() {
1252 let mut buf = vec![0xaa, 0xbb];
1253 let rule = oversized_rule();
1254
1255 let err =
1256 try_encode_flowspec_nlri(std::slice::from_ref(&rule), &mut buf, Afi::Ipv4).unwrap_err();
1257
1258 let EncodeError::ValueOutOfRange { field, value } = err else {
1259 panic!("expected ValueOutOfRange");
1260 };
1261 assert_eq!(field, "FlowSpec NLRI rule length");
1262 assert_eq!(value, rule.encoded_len(Afi::Ipv4).to_string());
1263 assert_eq!(buf, vec![0xaa, 0xbb]);
1264 }
1265
1266 #[test]
1267 fn one_byte_length_prefix() {
1268 let mut buf = Vec::new();
1269 let len = 100;
1270 encode_flowspec_length(len, &mut buf);
1271 assert_eq!(buf.len(), 1);
1272 let (decoded_len, consumed) = decode_flowspec_length(&buf).unwrap();
1273 assert_eq!(decoded_len, len);
1274 assert_eq!(consumed, 1);
1275 }
1276
1277 #[test]
1278 fn numeric_2byte_value() {
1279 let ops = vec![NumericMatch {
1280 end_of_list: true,
1281 and_bit: false,
1282 lt: false,
1283 gt: false,
1284 eq: true,
1285 value: 8080,
1286 }];
1287 let mut buf = Vec::new();
1288 encode_numeric_ops(&ops, &mut buf);
1289 let (decoded, _) = decode_numeric_ops(&buf).unwrap();
1290 assert_eq!(decoded[0].value, 8080);
1291 }
1292
1293 #[test]
1294 fn numeric_4byte_value() {
1295 let ops = vec![NumericMatch {
1296 end_of_list: true,
1297 and_bit: false,
1298 lt: false,
1299 gt: false,
1300 eq: true,
1301 value: 100_000,
1302 }];
1303 let mut buf = Vec::new();
1304 encode_numeric_ops(&ops, &mut buf);
1305 let (decoded, _) = decode_numeric_ops(&buf).unwrap();
1306 assert_eq!(decoded[0].value, 100_000);
1307 }
1308
1309 #[test]
1310 fn bitmask_2byte_value() {
1311 let ops = vec![BitmaskMatch {
1312 end_of_list: true,
1313 and_bit: false,
1314 not_bit: false,
1315 match_bit: true,
1316 value: 0x0FFF,
1317 }];
1318 let mut buf = Vec::new();
1319 encode_bitmask_ops(&ops, &mut buf);
1320 let (decoded, _) = decode_bitmask_ops(&buf).unwrap();
1321 assert_eq!(decoded[0].value, 0x0FFF);
1322 }
1323
1324 #[test]
1325 fn display_string_formatting() {
1326 let rule = FlowSpecRule {
1327 components: vec![
1328 FlowSpecComponent::DestinationPrefix(FlowSpecPrefix::V4(Ipv4Prefix::new(
1329 Ipv4Addr::new(192, 168, 1, 0),
1330 24,
1331 ))),
1332 FlowSpecComponent::IpProtocol(vec![NumericMatch {
1333 end_of_list: true,
1334 and_bit: false,
1335 lt: false,
1336 gt: false,
1337 eq: true,
1338 value: 6,
1339 }]),
1340 ],
1341 };
1342 let s = rule.display_string();
1343 assert!(s.contains("dst 192.168.1.0/24"));
1344 assert!(s.contains("proto ==6"));
1345 }
1346
1347 #[test]
1348 fn traffic_rate_bytes_action_roundtrip() {
1349 let action = FlowSpecAction::TrafficRateBytes {
1350 asn: 65000,
1351 rate: 0.0,
1352 };
1353 let ec = crate::attribute::ExtendedCommunity::from_flowspec_action(&action);
1354 let decoded = ec.as_flowspec_action().unwrap();
1355 match decoded {
1356 FlowSpecAction::TrafficRateBytes { asn, rate } => {
1357 assert_eq!(asn, 65000);
1358 assert!((rate - 0.0).abs() < f32::EPSILON);
1359 }
1360 _ => panic!("wrong action type"),
1361 }
1362 }
1363
1364 #[test]
1365 fn traffic_action_roundtrip() {
1366 let action = FlowSpecAction::TrafficAction {
1367 sample: true,
1368 terminal: false,
1369 };
1370 let ec = crate::attribute::ExtendedCommunity::from_flowspec_action(&action);
1371 let decoded = ec.as_flowspec_action().unwrap();
1372 match decoded {
1373 FlowSpecAction::TrafficAction { sample, terminal } => {
1374 assert!(sample);
1375 assert!(!terminal);
1376 }
1377 _ => panic!("wrong action type"),
1378 }
1379 }
1380
1381 #[test]
1382 fn traffic_marking_roundtrip() {
1383 let action = FlowSpecAction::TrafficMarking { dscp: 46 };
1384 let ec = crate::attribute::ExtendedCommunity::from_flowspec_action(&action);
1385 let decoded = ec.as_flowspec_action().unwrap();
1386 assert!(matches!(
1387 decoded,
1388 FlowSpecAction::TrafficMarking { dscp: 46 }
1389 ));
1390 }
1391
1392 #[test]
1393 fn redirect_2octet_roundtrip() {
1394 let action = FlowSpecAction::Redirect2Octet {
1395 asn: 65001,
1396 value: 100,
1397 };
1398 let ec = crate::attribute::ExtendedCommunity::from_flowspec_action(&action);
1399 let decoded = ec.as_flowspec_action().unwrap();
1400 assert!(matches!(
1401 decoded,
1402 FlowSpecAction::Redirect2Octet {
1403 asn: 65001,
1404 value: 100
1405 }
1406 ));
1407 }
1408
1409 #[test]
1410 fn redirect_ipv4_roundtrip() {
1411 let action = FlowSpecAction::RedirectIpv4 {
1412 addr: Ipv4Addr::new(10, 0, 0, 1),
1413 value: 200,
1414 };
1415 let ec = crate::attribute::ExtendedCommunity::from_flowspec_action(&action);
1416 let decoded = ec.as_flowspec_action().unwrap();
1417 match decoded {
1418 FlowSpecAction::RedirectIpv4 { addr, value } => {
1419 assert_eq!(addr, Ipv4Addr::new(10, 0, 0, 1));
1420 assert_eq!(value, 200);
1421 }
1422 _ => panic!("wrong action type"),
1423 }
1424 }
1425
1426 #[test]
1427 fn redirect_4octet_roundtrip() {
1428 let action = FlowSpecAction::Redirect4Octet {
1429 asn: 400_000,
1430 value: 300,
1431 };
1432 let ec = crate::attribute::ExtendedCommunity::from_flowspec_action(&action);
1433 let decoded = ec.as_flowspec_action().unwrap();
1434 match decoded {
1435 FlowSpecAction::Redirect4Octet { asn, value } => {
1436 assert_eq!(asn, 400_000);
1437 assert_eq!(value, 300);
1438 }
1439 _ => panic!("wrong action type"),
1440 }
1441 }
1442
1443 #[test]
1444 fn traffic_rate_packets_roundtrip() {
1445 let action = FlowSpecAction::TrafficRatePackets {
1446 asn: 0,
1447 rate: 1000.0,
1448 };
1449 let ec = crate::attribute::ExtendedCommunity::from_flowspec_action(&action);
1450 let decoded = ec.as_flowspec_action().unwrap();
1451 match decoded {
1452 FlowSpecAction::TrafficRatePackets { asn, rate } => {
1453 assert_eq!(asn, 0);
1454 assert!((rate - 1000.0).abs() < f32::EPSILON);
1455 }
1456 _ => panic!("wrong action type"),
1457 }
1458 }
1459
1460 #[test]
1461 fn flow_label_ipv6_roundtrip() {
1462 let rule = FlowSpecRule {
1463 components: vec![FlowSpecComponent::FlowLabel(vec![NumericMatch {
1464 end_of_list: true,
1465 and_bit: false,
1466 lt: false,
1467 gt: false,
1468 eq: true,
1469 value: 12345,
1470 }])],
1471 };
1472 let mut buf = Vec::new();
1473 encode_flowspec_nlri(std::slice::from_ref(&rule), &mut buf, Afi::Ipv6);
1474 let decoded = decode_flowspec_nlri(&buf, Afi::Ipv6).unwrap();
1475 assert_eq!(decoded[0], rule);
1476 }
1477
1478 #[test]
1479 fn ipv6_rule_with_prefix_and_flow_label() {
1480 let rule = FlowSpecRule {
1481 components: vec![
1482 FlowSpecComponent::DestinationPrefix(FlowSpecPrefix::V6(Ipv6PrefixOffset {
1483 prefix: Ipv6Prefix::new(
1484 std::net::Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
1485 32,
1486 ),
1487 offset: 0,
1488 })),
1489 FlowSpecComponent::FlowLabel(vec![NumericMatch {
1490 end_of_list: true,
1491 and_bit: false,
1492 lt: false,
1493 gt: false,
1494 eq: true,
1495 value: 42,
1496 }]),
1497 ],
1498 };
1499 let mut buf = Vec::new();
1500 encode_flowspec_nlri(std::slice::from_ref(&rule), &mut buf, Afi::Ipv6);
1501 let decoded = decode_flowspec_nlri(&buf, Afi::Ipv6).unwrap();
1502 assert_eq!(decoded[0], rule);
1503 }
1504
1505 #[test]
1506 #[expect(clippy::too_many_lines)]
1507 fn all_13_component_types_in_order() {
1508 let rule = FlowSpecRule {
1509 components: vec![
1510 FlowSpecComponent::DestinationPrefix(FlowSpecPrefix::V4(Ipv4Prefix::new(
1511 Ipv4Addr::new(10, 0, 0, 0),
1512 8,
1513 ))),
1514 FlowSpecComponent::SourcePrefix(FlowSpecPrefix::V4(Ipv4Prefix::new(
1515 Ipv4Addr::new(172, 16, 0, 0),
1516 12,
1517 ))),
1518 FlowSpecComponent::IpProtocol(vec![NumericMatch {
1519 end_of_list: true,
1520 and_bit: false,
1521 lt: false,
1522 gt: false,
1523 eq: true,
1524 value: 6,
1525 }]),
1526 FlowSpecComponent::Port(vec![NumericMatch {
1527 end_of_list: true,
1528 and_bit: false,
1529 lt: false,
1530 gt: false,
1531 eq: true,
1532 value: 80,
1533 }]),
1534 FlowSpecComponent::DestinationPort(vec![NumericMatch {
1535 end_of_list: true,
1536 and_bit: false,
1537 lt: false,
1538 gt: false,
1539 eq: true,
1540 value: 443,
1541 }]),
1542 FlowSpecComponent::SourcePort(vec![NumericMatch {
1543 end_of_list: true,
1544 and_bit: false,
1545 lt: false,
1546 gt: true,
1547 eq: false,
1548 value: 1024,
1549 }]),
1550 FlowSpecComponent::IcmpType(vec![NumericMatch {
1551 end_of_list: true,
1552 and_bit: false,
1553 lt: false,
1554 gt: false,
1555 eq: true,
1556 value: 8,
1557 }]),
1558 FlowSpecComponent::IcmpCode(vec![NumericMatch {
1559 end_of_list: true,
1560 and_bit: false,
1561 lt: false,
1562 gt: false,
1563 eq: true,
1564 value: 0,
1565 }]),
1566 FlowSpecComponent::TcpFlags(vec![BitmaskMatch {
1567 end_of_list: true,
1568 and_bit: false,
1569 not_bit: false,
1570 match_bit: true,
1571 value: 0x02,
1572 }]),
1573 FlowSpecComponent::PacketLength(vec![NumericMatch {
1574 end_of_list: true,
1575 and_bit: false,
1576 lt: true,
1577 gt: false,
1578 eq: true,
1579 value: 1500,
1580 }]),
1581 FlowSpecComponent::Dscp(vec![NumericMatch {
1582 end_of_list: true,
1583 and_bit: false,
1584 lt: false,
1585 gt: false,
1586 eq: true,
1587 value: 46,
1588 }]),
1589 FlowSpecComponent::Fragment(vec![BitmaskMatch {
1590 end_of_list: true,
1591 and_bit: false,
1592 not_bit: false,
1593 match_bit: true,
1594 value: 0x01,
1595 }]),
1596 FlowSpecComponent::FlowLabel(vec![NumericMatch {
1597 end_of_list: true,
1598 and_bit: false,
1599 lt: false,
1600 gt: false,
1601 eq: true,
1602 value: 99999,
1603 }]),
1604 ],
1605 };
1606 assert!(rule.validate().is_ok());
1607 let mut buf = Vec::new();
1608 encode_flowspec_nlri(std::slice::from_ref(&rule), &mut buf, Afi::Ipv4);
1609 let decoded = decode_flowspec_nlri(&buf, Afi::Ipv4).unwrap();
1610 assert_eq!(decoded[0], rule);
1611 }
1612
1613 #[test]
1614 fn destination_prefix_extraction() {
1615 let rule = FlowSpecRule {
1616 components: vec![
1617 FlowSpecComponent::DestinationPrefix(FlowSpecPrefix::V4(Ipv4Prefix::new(
1618 Ipv4Addr::new(10, 0, 0, 0),
1619 24,
1620 ))),
1621 FlowSpecComponent::IpProtocol(vec![NumericMatch {
1622 end_of_list: true,
1623 and_bit: false,
1624 lt: false,
1625 gt: false,
1626 eq: true,
1627 value: 6,
1628 }]),
1629 ],
1630 };
1631 let prefix = rule.destination_prefix().unwrap();
1632 match prefix {
1633 crate::nlri::Prefix::V4(p) => {
1634 assert_eq!(p.addr, Ipv4Addr::new(10, 0, 0, 0));
1635 assert_eq!(p.len, 24);
1636 }
1637 crate::nlri::Prefix::V6(_) => panic!("expected V4 prefix"),
1638 }
1639 }
1640
1641 #[test]
1642 fn rule_without_destination_prefix() {
1643 let rule = FlowSpecRule {
1644 components: vec![FlowSpecComponent::IpProtocol(vec![NumericMatch {
1645 end_of_list: true,
1646 and_bit: false,
1647 lt: false,
1648 gt: false,
1649 eq: true,
1650 value: 17,
1651 }])],
1652 };
1653 assert!(rule.destination_prefix().is_none());
1654 }
1655
1656 #[test]
1657 fn and_bit_numeric_ops() {
1658 let ops = vec![
1659 NumericMatch {
1660 end_of_list: false,
1661 and_bit: false,
1662 lt: false,
1663 gt: true,
1664 eq: true,
1665 value: 100,
1666 },
1667 NumericMatch {
1668 end_of_list: true,
1669 and_bit: true,
1670 lt: true,
1671 gt: false,
1672 eq: true,
1673 value: 200,
1674 },
1675 ];
1676 let mut buf = Vec::new();
1677 encode_numeric_ops(&ops, &mut buf);
1678 let (decoded, _) = decode_numeric_ops(&buf).unwrap();
1679 assert_eq!(decoded.len(), 2);
1680 assert!(!decoded[0].and_bit);
1681 assert!(decoded[0].gt);
1682 assert!(decoded[0].eq);
1683 assert!(decoded[1].and_bit);
1684 assert!(decoded[1].lt);
1685 assert!(decoded[1].eq);
1686 }
1687}