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