1use std::collections::HashSet;
4use std::fmt::Write;
5use std::net::IpAddr;
6
7use num::bigint::BigUint;
8use num::ToPrimitive;
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, PartialEq, Hash, Eq, Serialize, Deserialize)]
12pub struct BigUintKey {
13 pub value: BigUint,
14 pub width: usize,
15}
16
17#[derive(Debug, Clone, PartialEq, Hash, Eq, Serialize, Deserialize)]
20pub enum Key {
21 Exact(BigUintKey),
22 Range(BigUintKey, BigUintKey),
23 Ternary(Ternary),
24 Lpm(Prefix),
25}
26
27impl Default for Key {
28 fn default() -> Self {
29 Self::Ternary(Ternary::default())
30 }
31}
32
33impl Key {
34 pub fn to_bytes(&self) -> Vec<u8> {
35 match self {
36 Key::Exact(x) => {
37 let mut buf = x.value.to_bytes_le();
38
39 buf.resize(x.width, 0);
43 buf
44 }
45 Key::Range(a, z) => {
46 let mut buf_a = a.value.to_bytes_le();
47 let mut buf_z = z.value.to_bytes_le();
48
49 buf_a.resize(a.width, 0);
50 buf_z.resize(z.width, 0);
51 buf_a.extend_from_slice(&buf_z);
52 buf_a
53 }
54 Key::Ternary(t) => match t {
55 Ternary::DontCare => {
56 let mut buf = Vec::new();
57 buf.clear();
58 buf
59 }
60 Ternary::Value(v) => {
61 let mut buf = v.value.to_bytes_le();
62 buf.resize(v.width, 0);
63 buf
64 }
65 Ternary::Masked(v, m, w) => {
66 let mut buf_a = v.to_bytes_le();
67 let mut buf_b = m.to_bytes_le();
68 buf_a.resize(*w, 0);
69 buf_b.resize(*w, 0);
70 buf_a.extend_from_slice(&buf_b);
71 buf_a
72 }
73 },
74 Key::Lpm(p) => {
75 let mut v: Vec<u8> = match p.addr {
76 IpAddr::V4(a) => a.octets().into(),
77 IpAddr::V6(a) => a.octets().into(),
78 };
79 v.push(p.len);
80 v
81 }
82 }
83 }
84}
85
86#[derive(Debug, Clone, PartialEq, Hash, Eq, Serialize, Deserialize)]
87pub enum Ternary {
88 DontCare,
89 Value(BigUintKey),
90 Masked(BigUint, BigUint, usize),
91}
92
93impl Default for Ternary {
94 fn default() -> Self {
95 Self::DontCare
96 }
97}
98
99#[derive(Debug, Clone, PartialEq, Hash, Eq, Serialize, Deserialize)]
100pub struct Prefix {
101 pub addr: IpAddr,
102 pub len: u8,
103}
104
105pub struct Table<const D: usize, A: Clone> {
106 pub entries: HashSet<TableEntry<D, A>>,
107}
108
109impl<const D: usize, A: Clone> Default for Table<D, A> {
110 fn default() -> Self {
111 Self::new()
112 }
113}
114
115impl<const D: usize, A: Clone> Table<D, A> {
116 pub fn new() -> Self {
117 Self {
118 entries: HashSet::new(),
119 }
120 }
121
122 pub fn match_selector(
123 &self,
124 keyset: &[BigUint; D],
125 ) -> Vec<TableEntry<D, A>> {
126 let mut result = Vec::new();
127 for entry in &self.entries {
128 if keyset_matches(keyset, &entry.key) {
129 result.push(entry.clone());
130 }
131 }
132 sort_entries(result)
133 }
134
135 pub fn dump(&self) -> String {
136 let mut s = String::new();
137 for e in &self.entries {
138 writeln!(s, "{:?}", e.key).unwrap();
139 }
140 s
141 }
142}
143
144pub fn sort_entries<const D: usize, A: Clone>(
148 mut entries: Vec<TableEntry<D, A>>,
149) -> Vec<TableEntry<D, A>> {
150 if entries.is_empty() {
151 return entries;
152 }
153
154 for (i, k) in entries[0].key.iter().enumerate() {
169 match k {
170 Key::Lpm(_) => {
171 let mut entries = prune_entries_by_lpm(i, &entries);
172 sort_entries_by_priority(&mut entries);
173 return entries;
174 }
175 _ => continue,
176 }
177 }
178
179 sort_entries_by_priority(&mut entries);
180 entries
181}
182
183pub fn prune_entries_by_lpm<const D: usize, A: Clone>(
189 d: usize,
190 entries: &Vec<TableEntry<D, A>>,
191) -> Vec<TableEntry<D, A>> {
192 let mut longest_prefix = 0u8;
193
194 for e in entries {
195 if let Key::Lpm(x) = &e.key[d] {
196 if x.len > longest_prefix {
197 longest_prefix = x.len
198 }
199 }
200 }
201
202 let mut result = Vec::new();
203 for e in entries {
204 if let Key::Lpm(x) = &e.key[d] {
205 if x.len == longest_prefix {
206 result.push(e.clone())
207 }
208 }
209 }
210
211 result
212}
213
214pub fn sort_entries_by_priority<const D: usize, A: Clone>(
215 entries: &mut [TableEntry<D, A>],
216) {
217 entries
218 .sort_by(|a, b| -> std::cmp::Ordering { b.priority.cmp(&a.priority) });
219}
220
221pub fn key_matches(selector: &BigUint, key: &Key) -> bool {
222 match key {
223 Key::Exact(x) => {
224 let hit = selector == &x.value;
225 if !hit {
226 let dump = format!("{:x} != {:x}", selector, x.value);
227 crate::p4rs_provider::match_miss!(|| &dump);
228 }
229 hit
230 }
231 Key::Range(begin, end) => {
232 let hit = selector >= &begin.value && selector <= &end.value;
233 if !hit {
234 let dump = format!(
235 "begin={} end={} sel={}",
236 begin.value, end.value, selector
237 );
238 crate::p4rs_provider::match_miss!(|| &dump);
239 }
240 hit
241 }
242 Key::Ternary(t) => match t {
243 Ternary::DontCare => true,
244 Ternary::Value(x) => selector == &x.value,
245 Ternary::Masked(x, m, _) => selector & m == x & m,
246 },
247 Key::Lpm(p) => match p.addr {
248 IpAddr::V6(addr) => {
249 assert!(p.len <= 128);
250 let key: u128 = addr.into();
251 let mask = if p.len == 128 {
252 u128::MAX
253 } else if p.len == 0 {
254 0u128
255 } else {
256 ((1u128 << p.len) - 1) << (128 - p.len)
257 };
258 let selector_v6 = selector.to_u128().unwrap();
260 let hit = selector_v6 & mask == key & mask;
261 if !hit {
262 let dump = format!(
263 "{:x} & {:x} == {:x} & {:x} | {:x} == {:x}",
264 selector_v6,
265 mask,
266 key,
267 mask,
268 selector_v6 & mask,
269 key & mask
270 );
271 crate::p4rs_provider::match_miss!(|| &dump);
273 }
274 hit
275 }
276 IpAddr::V4(addr) => {
277 assert!(p.len <= 32);
278 let key: u32 = addr.into();
279 let mask = if p.len == 32 {
280 u32::MAX
281 } else if p.len == 0 {
282 0
283 } else {
284 ((1u32 << p.len) - 1) << (32 - p.len)
285 };
286 let selector_v4: u32 = selector.to_u32().unwrap();
287 let hit = selector_v4 & mask == key & mask;
288 if !hit {
289 let dump = format!(
290 "{:x} & {:x} == {:x} & {:x} | {:x} = {:x}",
291 selector_v4,
292 mask,
293 key,
294 mask,
295 selector_v4 & mask,
296 key & mask
297 );
298 crate::p4rs_provider::match_miss!(|| &dump);
299 }
300 hit
301 }
302 },
303 }
304}
305
306pub fn keyset_matches<const D: usize>(
307 selector: &[BigUint; D],
308 key: &[Key; D],
309) -> bool {
310 for i in 0..D {
311 if !key_matches(&selector[i], &key[i]) {
312 return false;
313 }
314 }
315 true
316}
317
318#[derive(Clone)]
319pub struct TableEntry<const D: usize, A: Clone> {
320 pub key: [Key; D],
321 pub action: A,
322 pub priority: u32,
323 pub name: String,
324
325 pub action_id: String,
328 pub parameter_data: Vec<u8>,
329}
330
331impl<const D: usize, A: Clone> std::hash::Hash for TableEntry<D, A> {
333 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
334 self.key.hash(state);
335 }
336}
337
338impl<const D: usize, A: Clone> std::cmp::PartialEq for TableEntry<D, A> {
339 fn eq(&self, other: &Self) -> bool {
340 self.key == other.key
341 }
342}
343
344impl<const D: usize, A: Clone> std::fmt::Debug for TableEntry<D, A> {
345 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
346 f.debug_struct(&format!("TableEntry<{}>", D))
347 .field("key", &self.key)
348 .field("priority", &self.priority)
349 .field("name", &self.name)
350 .finish()
351 }
352}
353
354impl<const D: usize, A: Clone> std::cmp::Eq for TableEntry<D, A> {}
355
356#[cfg(test)]
357mod tests {
358
359 use super::*;
360 use std::net::Ipv6Addr;
361 use std::sync::Arc;
362
363 fn contains_entry<const D: usize, A: Clone>(
364 entries: &Vec<TableEntry<D, A>>,
365 name: &str,
366 ) -> bool {
367 for e in entries {
368 if e.name.as_str() == name {
369 return true;
370 }
371 }
372 false
373 }
374
375 fn tk(
376 name: &str,
377 addr: Ternary,
378 ingress: Ternary,
379 icmp: Ternary,
380 priority: u32,
381 ) -> TableEntry<3, ()> {
382 TableEntry::<3, ()> {
383 key: [
384 Key::Ternary(addr),
385 Key::Ternary(ingress),
386 Key::Ternary(icmp),
387 ],
388 priority,
389 name: name.into(),
390 action: (),
391 action_id: String::new(),
392 parameter_data: Vec::new(),
393 }
394 }
395
396 #[test]
397 fn match_ternary_1() {
412 let table = Table::<3, ()> {
413 entries: HashSet::from([
414 tk(
415 "a0",
416 Ternary::Value(BigUintKey {
417 value: 1u8.into(),
418 width: 1,
419 }),
420 Ternary::DontCare,
421 Ternary::Value(BigUintKey {
422 value: 1u8.into(),
423 width: 1,
424 }),
425 10,
426 ),
427 tk(
428 "a1",
429 Ternary::Value(BigUintKey {
430 value: 1u8.into(),
431 width: 1,
432 }),
433 Ternary::DontCare,
434 Ternary::Value(BigUintKey {
435 value: 0u8.into(),
436 width: 1,
437 }),
438 1,
439 ),
440 tk(
441 "a2",
442 Ternary::DontCare,
443 Ternary::Value(BigUintKey {
444 value: 2u16.into(),
445 width: 2,
446 }),
447 Ternary::DontCare,
448 1,
449 ),
450 tk(
451 "a3",
452 Ternary::DontCare,
453 Ternary::Value(BigUintKey {
454 value: 4u16.into(),
455 width: 2,
456 }),
457 Ternary::DontCare,
458 1,
459 ),
460 tk(
461 "a4",
462 Ternary::DontCare,
463 Ternary::Value(BigUintKey {
464 value: 7u16.into(),
465 width: 2,
466 }),
467 Ternary::DontCare,
468 1,
469 ),
470 tk(
471 "a5",
472 Ternary::DontCare,
473 Ternary::Value(BigUintKey {
474 value: 19u16.into(),
475 width: 2,
476 }),
477 Ternary::DontCare,
478 1,
479 ),
480 tk(
481 "a6",
482 Ternary::DontCare,
483 Ternary::Value(BigUintKey {
484 value: 33u16.into(),
485 width: 2,
486 }),
487 Ternary::DontCare,
488 1,
489 ),
490 tk(
491 "a7",
492 Ternary::DontCare,
493 Ternary::Value(BigUintKey {
494 value: 47u16.into(),
495 width: 2,
496 }),
497 Ternary::DontCare,
498 1,
499 ),
500 ]),
501 };
502
503 let selector =
505 [BigUint::from(1u8), BigUint::from(99u16), BigUint::from(1u8)];
506 let matches = table.match_selector(&selector);
507 assert_eq!(matches.len(), 1);
509 assert!(contains_entry(&matches, "a0"));
510
511 let selector =
513 [BigUint::from(1u8), BigUint::from(47u16), BigUint::from(1u8)];
514 let matches = table.match_selector(&selector);
515 assert_eq!(matches.len(), 2);
517 assert!(contains_entry(&matches, "a0"));
518 assert!(contains_entry(&matches, "a7"));
519 assert_eq!(matches[0].name.as_str(), "a0");
521 }
522
523 fn lpm(name: &str, addr: &str, len: u8) -> TableEntry<1, ()> {
524 let addr: IpAddr = addr.parse().unwrap();
525 TableEntry::<1, ()> {
526 key: [Key::Lpm(Prefix { addr, len })],
527 priority: 1,
528 name: name.into(),
529 action: (),
530 action_id: String::new(),
531 parameter_data: Vec::new(),
532 }
533 }
534
535 #[test]
536 fn match_lpm_1() {
559 let mut table = Table::<1, ()>::new();
560 table.entries.insert(lpm("a0", "fd00:4700::", 24));
561 table.entries.insert(lpm("a1", "fd00:4701::", 32));
562 table.entries.insert(lpm("a2", "fd00:4702::", 32));
563 table.entries.insert(lpm("a3", "fd00:4701:0001::", 48));
564 table.entries.insert(lpm("a4", "fd00:4701:0002::", 48));
565 table.entries.insert(lpm("a5", "fd00:4702:0001::", 48));
566 table.entries.insert(lpm("a6", "fd00:4702:0002::", 48));
567 table.entries.insert(lpm("a7", "fd00:4701:0001:0001::", 64));
568 table.entries.insert(lpm("a8", "fd00:4701:0001:0002::", 64));
569 table.entries.insert(lpm("a9", "fd00:4702:0001:0001::", 64));
570 table
571 .entries
572 .insert(lpm("a10", "fd00:4702:0001:0002::", 64));
573 table
574 .entries
575 .insert(lpm("a11", "fd00:4701:0002:0001::", 64));
576 table
577 .entries
578 .insert(lpm("a12", "fd00:4701:0002:0002::", 64));
579 table
580 .entries
581 .insert(lpm("a13", "fd00:4702:0002:0001::", 64));
582 table
583 .entries
584 .insert(lpm("a14", "fd00:4702:0002:0002::", 64));
585 table.entries.insert(lpm("a15", "fd00:1701::", 32));
586
587 let addr: Ipv6Addr = "fd00:4700::1".parse().unwrap();
588 let selector = [BigUint::from(u128::from_be_bytes(addr.octets()))];
589 let matches = table.match_selector(&selector);
590 assert_eq!(matches.len(), 1);
592 assert!(contains_entry(&matches, "a0"));
593
594 let addr: Ipv6Addr = "fd00:4800::1".parse().unwrap();
595 let selector = [BigUint::from(u128::from_be_bytes(addr.octets()))];
596 let matches = table.match_selector(&selector);
597 assert_eq!(matches.len(), 0);
598 let addr: Ipv6Addr = "fd00:4702:0002:0002::1".parse().unwrap();
601 let selector = [BigUint::from(u128::from_be_bytes(addr.octets()))];
602 let matches = table.match_selector(&selector);
603 assert_eq!(matches.len(), 1); assert!(contains_entry(&matches, "a14"));
606 assert_eq!(matches[0].name.as_str(), "a14");
608 }
609
610 fn tlpm(
611 name: &str,
612 addr: &str,
613 len: u8,
614 zone: Ternary,
615 priority: u32,
616 ) -> TableEntry<2, ()> {
617 TableEntry::<2, ()> {
618 key: [
619 Key::Lpm(Prefix {
620 addr: addr.parse().unwrap(),
621 len,
622 }),
623 Key::Ternary(zone),
624 ],
625 priority,
626 name: name.into(),
627 action: (),
628 action_id: String::new(),
629 parameter_data: Vec::new(),
630 }
631 }
632
633 #[test]
634 fn match_lpm_ternary_1() {
635 let table = Table::<2, ()> {
636 entries: HashSet::from([
637 tlpm("a0", "fd00:1::", 64, Ternary::DontCare, 1),
638 tlpm(
639 "a1",
640 "fd00:1::",
641 64,
642 Ternary::Value(BigUintKey {
643 value: 1u16.into(),
644 width: 2,
645 }),
646 10,
647 ),
648 tlpm(
649 "a2",
650 "fd00:1::",
651 64,
652 Ternary::Value(BigUintKey {
653 value: 2u16.into(),
654 width: 2,
655 }),
656 10,
657 ),
658 tlpm(
659 "a3",
660 "fd00:1::",
661 64,
662 Ternary::Value(BigUintKey {
663 value: 3u16.into(),
664 width: 2,
665 }),
666 10,
667 ),
668 ]),
669 };
670
671 let dst: Ipv6Addr = "fd00:1::1".parse().unwrap();
672 let selector = [
673 BigUint::from(u128::from_le_bytes(dst.octets())),
674 BigUint::from(0u16),
675 ];
676 let matches = table.match_selector(&selector);
677 println!("zone-0: {:#?}", matches);
678 let selector = [
679 BigUint::from(u128::from_le_bytes(dst.octets())),
680 BigUint::from(2u16),
681 ];
682 let matches = table.match_selector(&selector);
683 println!("zone-2: {:#?}", matches);
684 }
685
686 fn lpre(
687 name: &str,
688 addr: &str,
689 len: u8,
690 zone: Ternary,
691 range: (u32, u32),
692 tag: u64,
693 priority: u32,
694 ) -> TableEntry<4, ()> {
695 TableEntry::<4, ()> {
696 key: [
697 Key::Lpm(Prefix {
698 addr: addr.parse().unwrap(),
699 len,
700 }),
701 Key::Ternary(zone),
702 Key::Range(
703 BigUintKey {
704 value: range.0.into(),
705 width: 4,
706 },
707 BigUintKey {
708 value: range.1.into(),
709 width: 4,
710 },
711 ),
712 Key::Exact(BigUintKey {
713 value: tag.into(),
714 width: 8,
715 }),
716 ],
717 priority,
718 name: name.into(),
719 action: (),
720 action_id: String::new(),
721 parameter_data: Vec::new(),
722 }
723 }
724
725 struct ActionData {
726 value: u64,
727 }
728
729 #[test]
730 fn match_lpm_ternary_range() {
731 let table = Table::<4, ()> {
732 entries: HashSet::from([
733 lpre("a0", "fd00:1::", 64, Ternary::DontCare, (80, 80), 100, 1),
734 lpre(
735 "a1",
736 "fd00:1::",
737 64,
738 Ternary::DontCare,
739 (443, 443),
740 100,
741 1,
742 ),
743 lpre("a2", "fd00:1::", 64, Ternary::DontCare, (80, 80), 200, 1),
744 lpre(
745 "a3",
746 "fd00:1::",
747 64,
748 Ternary::DontCare,
749 (443, 443),
750 200,
751 1,
752 ),
753 lpre(
754 "a4",
755 "fd00:1::",
756 64,
757 Ternary::Value(BigUintKey {
758 value: 99u16.into(),
759 width: 2,
760 }),
761 (443, 443),
762 200,
763 10,
764 ),
765 ]),
766 };
767 let dst: Ipv6Addr = "fd00:1::1".parse().unwrap();
768 let selector = [
769 BigUint::from(u128::from_le_bytes(dst.octets())),
770 BigUint::from(0u16),
771 BigUint::from(80u32),
772 BigUint::from(100u64),
773 ];
774 let matches = table.match_selector(&selector);
775 println!("m1: {:#?}", matches);
776
777 let selector = [
778 BigUint::from(u128::from_le_bytes(dst.octets())),
779 BigUint::from(0u16),
780 BigUint::from(443u32),
781 BigUint::from(200u64),
782 ];
783 let matches = table.match_selector(&selector);
784 println!("m2: {:#?}", matches);
785
786 let selector = [
787 BigUint::from(u128::from_le_bytes(dst.octets())),
788 BigUint::from(99u16),
789 BigUint::from(443u32),
790 BigUint::from(200u64),
791 ];
792 let matches = table.match_selector(&selector);
793 println!("m3: {:#?}", matches);
794
795 let selector = [
796 BigUint::from(u128::from_le_bytes(dst.octets())),
797 BigUint::from(99u16),
798 BigUint::from(80u32),
799 BigUint::from(200u64),
800 ];
801 let matches = table.match_selector(&selector);
802 println!("m4: {:#?}", matches);
803 }
804
805 #[test]
806 fn match_with_action() {
807 let mut data = ActionData { value: 47 };
808
809 let table = Table::<1, Arc<dyn Fn(&mut ActionData)>> {
810 entries: HashSet::from([
811 TableEntry::<1, Arc<dyn Fn(&mut ActionData)>> {
812 key: [Key::Exact(BigUintKey {
813 value: 1u8.into(),
814 width: 1,
815 })],
816 priority: 0,
817 name: "a0".into(),
818 action: Arc::new(|a: &mut ActionData| {
819 a.value += 10;
820 }),
821 action_id: String::new(),
822 parameter_data: Vec::new(),
823 },
824 TableEntry::<1, Arc<dyn Fn(&mut ActionData)>> {
825 key: [Key::Exact(BigUintKey {
826 value: 2u8.into(),
827 width: 1,
828 })],
829 priority: 0,
830 name: "a1".into(),
831 action: Arc::new(|a: &mut ActionData| {
832 a.value -= 10;
833 }),
834 action_id: String::new(),
835 parameter_data: Vec::new(),
836 },
837 ]),
838 };
839
840 let selector = [BigUint::from(1u8)];
841 let matches = table.match_selector(&selector);
842 println!("m4: {:#?}", matches);
843 assert_eq!(matches.len(), 1);
844 (matches[0].action)(&mut data);
845 assert_eq!(data.value, 57);
846 }
847}