1use std::fmt;
7
8use crate::action::ActionList;
9
10#[allow(dead_code)]
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[repr(u16)]
14pub enum InstructionType {
15 GotoTable = 1,
17 WriteMetadata = 2,
19 WriteActions = 3,
21 ApplyActions = 4,
23 ClearActions = 5,
25 Meter = 6,
27}
28
29impl TryFrom<u16> for InstructionType {
30 type Error = crate::Error;
31
32 fn try_from(v: u16) -> Result<Self, Self::Error> {
33 match v {
34 1 => Ok(Self::GotoTable),
35 2 => Ok(Self::WriteMetadata),
36 3 => Ok(Self::WriteActions),
37 4 => Ok(Self::ApplyActions),
38 5 => Ok(Self::ClearActions),
39 6 => Ok(Self::Meter),
40 _ => Err(crate::Error::Parse(format!(
41 "unknown instruction type: {v}"
42 ))),
43 }
44 }
45}
46
47#[derive(Debug, Clone)]
49pub enum Instruction {
50 GotoTable(u8),
52
53 WriteMetadata { metadata: u64, mask: u64 },
55
56 ApplyActions(ActionList),
58
59 WriteActions(ActionList),
61
62 ClearActions,
64
65 Meter(u32),
67}
68
69impl fmt::Display for Instruction {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 match self {
72 Self::GotoTable(table) => write!(f, "goto_table:{table}"),
73 Self::WriteMetadata { metadata, mask } => {
74 write!(f, "write_metadata:0x{metadata:x}/0x{mask:x}")
75 }
76 Self::ApplyActions(actions) => write!(f, "apply_actions({actions})"),
77 Self::WriteActions(actions) => write!(f, "write_actions({actions})"),
78 Self::ClearActions => write!(f, "clear_actions"),
79 Self::Meter(id) => write!(f, "meter:{id}"),
80 }
81 }
82}
83
84impl fmt::Display for InstructionList {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 if self.instructions.len() == 1 {
88 if let Instruction::ApplyActions(actions) = &self.instructions[0] {
89 return write!(f, "{actions}");
90 }
91 }
92 if self.instructions.is_empty() {
93 return write!(f, "drop");
94 }
95 for (i, inst) in self.instructions.iter().enumerate() {
96 if i > 0 {
97 write!(f, ",")?;
98 }
99 write!(f, "{inst}")?;
100 }
101 Ok(())
102 }
103}
104
105impl Instruction {
106 pub fn encode(&self) -> Vec<u8> {
108 match self {
109 Self::GotoTable(table_id) => encode_goto_table(*table_id),
110 Self::WriteMetadata { metadata, mask } => encode_write_metadata(*metadata, *mask),
111 Self::ApplyActions(actions) => encode_apply_actions(actions),
112 Self::WriteActions(actions) => encode_write_actions(actions),
113 Self::ClearActions => encode_clear_actions(),
114 Self::Meter(meter_id) => encode_meter(*meter_id),
115 }
116 }
117
118 pub fn decode(data: &[u8]) -> crate::Result<(Self, usize)> {
122 if data.len() < 4 {
123 return Err(crate::Error::Parse("instruction too short".into()));
124 }
125
126 let inst_type = u16::from_be_bytes([data[0], data[1]]);
127 let length = u16::from_be_bytes([data[2], data[3]]) as usize;
128
129 if data.len() < length {
130 return Err(crate::Error::Parse("instruction truncated".into()));
131 }
132
133 let inst_type = InstructionType::try_from(inst_type)?;
134
135 let instruction = match inst_type {
136 InstructionType::GotoTable => {
137 if length < 8 {
138 return Err(crate::Error::Parse("goto_table instruction too short".into()));
139 }
140 let table_id = data[4];
141 Self::GotoTable(table_id)
142 }
143 InstructionType::WriteMetadata => {
144 if length < 24 {
145 return Err(crate::Error::Parse(
146 "write_metadata instruction too short".into(),
147 ));
148 }
149 let metadata = u64::from_be_bytes([
151 data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
152 ]);
153 let mask = u64::from_be_bytes([
154 data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23],
155 ]);
156 Self::WriteMetadata { metadata, mask }
157 }
158 InstructionType::ApplyActions => {
159 let actions_data = &data[8..length];
161 let actions = ActionList::decode(actions_data)?;
162 Self::ApplyActions(actions)
163 }
164 InstructionType::WriteActions => {
165 let actions_data = &data[8..length];
167 let actions = ActionList::decode(actions_data)?;
168 Self::WriteActions(actions)
169 }
170 InstructionType::ClearActions => Self::ClearActions,
171 InstructionType::Meter => {
172 if length < 8 {
173 return Err(crate::Error::Parse("meter instruction too short".into()));
174 }
175 let meter_id = u32::from_be_bytes([data[4], data[5], data[6], data[7]]);
176 Self::Meter(meter_id)
177 }
178 };
179
180 Ok((instruction, length))
181 }
182}
183
184#[derive(Debug, Clone, Default)]
186pub struct InstructionList {
187 instructions: Vec<Instruction>,
188}
189
190impl InstructionList {
191 pub fn new() -> Self {
193 Self::default()
194 }
195
196 pub fn push(&mut self, instruction: Instruction) {
198 self.instructions.push(instruction);
199 }
200
201 pub fn goto_table(mut self, table_id: u8) -> Self {
203 self.instructions.push(Instruction::GotoTable(table_id));
204 self
205 }
206
207 pub fn write_metadata(mut self, metadata: u64, mask: u64) -> Self {
209 self.instructions
210 .push(Instruction::WriteMetadata { metadata, mask });
211 self
212 }
213
214 pub fn apply_actions(mut self, actions: ActionList) -> Self {
216 self.instructions.push(Instruction::ApplyActions(actions));
217 self
218 }
219
220 pub fn write_actions(mut self, actions: ActionList) -> Self {
222 self.instructions.push(Instruction::WriteActions(actions));
223 self
224 }
225
226 pub fn clear_actions(mut self) -> Self {
228 self.instructions.push(Instruction::ClearActions);
229 self
230 }
231
232 pub fn meter(mut self, meter_id: u32) -> Self {
234 self.instructions.push(Instruction::Meter(meter_id));
235 self
236 }
237
238 pub fn instructions(&self) -> &[Instruction] {
240 &self.instructions
241 }
242
243 pub fn is_empty(&self) -> bool {
245 self.instructions.is_empty()
246 }
247
248 pub fn len(&self) -> usize {
250 self.instructions.len()
251 }
252
253 pub fn encode(&self) -> Vec<u8> {
255 let mut buf = Vec::new();
256 for instruction in &self.instructions {
257 buf.extend(instruction.encode());
258 }
259 buf
260 }
261
262 pub fn decode(data: &[u8]) -> crate::Result<Self> {
266 let mut instructions = Vec::new();
267 let mut offset = 0;
268
269 while offset < data.len() {
270 if data.len() - offset < 4 {
272 break;
273 }
274
275 let length = u16::from_be_bytes([data[offset + 2], data[offset + 3]]) as usize;
277 if length == 0 {
278 break;
279 }
280
281 let (instruction, consumed) = Instruction::decode(&data[offset..])?;
282 instructions.push(instruction);
283 offset += consumed;
284 }
285
286 Ok(Self { instructions })
287 }
288}
289
290fn encode_goto_table(table_id: u8) -> Vec<u8> {
304 let mut buf = Vec::with_capacity(8);
305 buf.extend((InstructionType::GotoTable as u16).to_be_bytes());
306 buf.extend(8u16.to_be_bytes()); buf.push(table_id);
308 buf.extend([0u8; 3]); buf
310}
311
312fn encode_write_metadata(metadata: u64, mask: u64) -> Vec<u8> {
328 let mut buf = Vec::with_capacity(24);
329 buf.extend((InstructionType::WriteMetadata as u16).to_be_bytes());
330 buf.extend(24u16.to_be_bytes()); buf.extend([0u8; 4]); buf.extend(metadata.to_be_bytes());
333 buf.extend(mask.to_be_bytes());
334 buf
335}
336
337fn encode_apply_actions(actions: &ActionList) -> Vec<u8> {
349 let actions_bytes = actions.encode();
350 let len = 8 + actions_bytes.len(); let mut buf = Vec::with_capacity(len);
353 buf.extend((InstructionType::ApplyActions as u16).to_be_bytes());
354 buf.extend((len as u16).to_be_bytes());
355 buf.extend([0u8; 4]); buf.extend(actions_bytes);
357 buf
358}
359
360fn encode_write_actions(actions: &ActionList) -> Vec<u8> {
364 let actions_bytes = actions.encode();
365 let len = 8 + actions_bytes.len();
366
367 let mut buf = Vec::with_capacity(len);
368 buf.extend((InstructionType::WriteActions as u16).to_be_bytes());
369 buf.extend((len as u16).to_be_bytes());
370 buf.extend([0u8; 4]); buf.extend(actions_bytes);
372 buf
373}
374
375fn encode_clear_actions() -> Vec<u8> {
385 let mut buf = Vec::with_capacity(8);
386 buf.extend((InstructionType::ClearActions as u16).to_be_bytes());
387 buf.extend(8u16.to_be_bytes()); buf.extend([0u8; 4]); buf
390}
391
392fn encode_meter(meter_id: u32) -> Vec<u8> {
402 let mut buf = Vec::with_capacity(8);
403 buf.extend((InstructionType::Meter as u16).to_be_bytes());
404 buf.extend(8u16.to_be_bytes()); buf.extend(meter_id.to_be_bytes());
406 buf
407}
408
409#[cfg(test)]
414mod tests {
415 use super::*;
416 use crate::action::OutputPort;
417
418 #[test]
419 fn instruction_type_wire_values() {
420 assert_eq!(InstructionType::GotoTable as u16, 1);
421 assert_eq!(InstructionType::WriteMetadata as u16, 2);
422 assert_eq!(InstructionType::WriteActions as u16, 3);
423 assert_eq!(InstructionType::ApplyActions as u16, 4);
424 assert_eq!(InstructionType::ClearActions as u16, 5);
425 assert_eq!(InstructionType::Meter as u16, 6);
426 }
427
428 #[test]
429 fn encode_goto_table_instruction() {
430 let bytes = encode_goto_table(5);
431 assert_eq!(bytes.len(), 8);
432 assert_eq!(&bytes[0..2], &[0x00, 0x01]);
434 assert_eq!(&bytes[2..4], &[0x00, 0x08]);
436 assert_eq!(bytes[4], 5);
438 assert_eq!(&bytes[5..8], &[0, 0, 0]);
440 }
441
442 #[test]
443 fn encode_write_metadata_instruction() {
444 let bytes = encode_write_metadata(0x1234_5678_9abc_def0, 0xffff_ffff_ffff_ffff);
445 assert_eq!(bytes.len(), 24);
446 assert_eq!(&bytes[0..2], &[0x00, 0x02]);
448 assert_eq!(&bytes[2..4], &[0x00, 0x18]);
450 assert_eq!(&bytes[4..8], &[0, 0, 0, 0]);
452 assert_eq!(
454 &bytes[8..16],
455 &[0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0]
456 );
457 assert_eq!(
459 &bytes[16..24],
460 &[0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]
461 );
462 }
463
464 #[test]
465 fn encode_apply_actions_instruction() {
466 let actions = ActionList::new().output(OutputPort::Port(1));
467 let bytes = encode_apply_actions(&actions);
468 assert_eq!(bytes.len(), 24);
470 assert_eq!(&bytes[0..2], &[0x00, 0x04]);
472 assert_eq!(&bytes[2..4], &[0x00, 0x18]);
474 assert_eq!(&bytes[4..8], &[0, 0, 0, 0]);
476 assert_eq!(&bytes[8..10], &[0x00, 0x00]);
478 }
479
480 #[test]
481 fn encode_apply_actions_empty() {
482 let actions = ActionList::new();
483 let bytes = encode_apply_actions(&actions);
484 assert_eq!(bytes.len(), 8);
486 assert_eq!(&bytes[0..2], &[0x00, 0x04]);
488 assert_eq!(&bytes[2..4], &[0x00, 0x08]);
490 }
491
492 #[test]
493 fn encode_write_actions_instruction() {
494 let actions = ActionList::new().output(OutputPort::Port(2));
495 let bytes = encode_write_actions(&actions);
496 assert_eq!(bytes.len(), 24);
498 assert_eq!(&bytes[0..2], &[0x00, 0x03]);
500 assert_eq!(&bytes[2..4], &[0x00, 0x18]);
502 }
503
504 #[test]
505 fn encode_clear_actions_instruction() {
506 let bytes = encode_clear_actions();
507 assert_eq!(bytes.len(), 8);
508 assert_eq!(&bytes[0..2], &[0x00, 0x05]);
510 assert_eq!(&bytes[2..4], &[0x00, 0x08]);
512 assert_eq!(&bytes[4..8], &[0, 0, 0, 0]);
514 }
515
516 #[test]
517 fn encode_meter_instruction() {
518 let bytes = encode_meter(100);
519 assert_eq!(bytes.len(), 8);
520 assert_eq!(&bytes[0..2], &[0x00, 0x06]);
522 assert_eq!(&bytes[2..4], &[0x00, 0x08]);
524 assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x00, 0x64]);
526 }
527
528 #[test]
529 fn instruction_goto_table_encode() {
530 let inst = Instruction::GotoTable(10);
531 let bytes = inst.encode();
532 assert_eq!(bytes.len(), 8);
533 assert_eq!(bytes[4], 10);
534 }
535
536 #[test]
537 fn instruction_write_metadata_encode() {
538 let inst = Instruction::WriteMetadata {
539 metadata: 0x1234,
540 mask: 0xffff,
541 };
542 let bytes = inst.encode();
543 assert_eq!(bytes.len(), 24);
544 }
545
546 #[test]
547 fn instruction_apply_actions_encode() {
548 let actions = ActionList::new().pop_vlan().output(OutputPort::Port(1));
549 let inst = Instruction::ApplyActions(actions);
550 let bytes = inst.encode();
551 assert_eq!(bytes.len(), 32);
553 }
554
555 #[test]
556 fn instruction_clear_actions_encode() {
557 let inst = Instruction::ClearActions;
558 let bytes = inst.encode();
559 assert_eq!(bytes.len(), 8);
560 assert_eq!(&bytes[0..2], &[0x00, 0x05]);
561 }
562
563 #[test]
564 fn instruction_meter_encode() {
565 let inst = Instruction::Meter(42);
566 let bytes = inst.encode();
567 assert_eq!(bytes.len(), 8);
568 assert_eq!(&bytes[4..8], &[0x00, 0x00, 0x00, 0x2a]);
569 }
570
571 #[test]
572 fn instruction_list_encode_empty() {
573 let list = InstructionList::new();
574 let bytes = list.encode();
575 assert!(bytes.is_empty());
576 }
577
578 #[test]
579 fn instruction_list_encode_single() {
580 let list = InstructionList::new().goto_table(5);
581 let bytes = list.encode();
582 assert_eq!(bytes.len(), 8);
583 assert_eq!(&bytes[0..2], &[0x00, 0x01]);
584 assert_eq!(bytes[4], 5);
585 }
586
587 #[test]
588 fn instruction_list_encode_multiple() {
589 let actions = ActionList::new().output(OutputPort::Port(1));
590 let list = InstructionList::new()
591 .apply_actions(actions)
592 .goto_table(10);
593 let bytes = list.encode();
594 assert_eq!(bytes.len(), 32);
596 assert_eq!(&bytes[0..2], &[0x00, 0x04]);
598 assert_eq!(&bytes[24..26], &[0x00, 0x01]);
600 }
601
602 #[test]
603 fn instruction_list_builder_methods() {
604 let actions = ActionList::new().output(OutputPort::Port(1));
605 let list = InstructionList::new()
606 .write_metadata(0x1234, 0xffff)
607 .apply_actions(actions)
608 .clear_actions()
609 .meter(100)
610 .goto_table(5);
611 assert_eq!(list.len(), 5);
612 assert!(!list.is_empty());
613 }
614
615 #[test]
620 fn decode_goto_table_instruction() {
621 let inst = Instruction::GotoTable(10);
622 let encoded = inst.encode();
623 let (decoded, len) = Instruction::decode(&encoded).unwrap();
624 assert_eq!(len, 8);
625 match decoded {
626 Instruction::GotoTable(table_id) => assert_eq!(table_id, 10),
627 _ => panic!("expected GotoTable instruction"),
628 }
629 }
630
631 #[test]
632 fn decode_write_metadata_instruction() {
633 let inst = Instruction::WriteMetadata {
634 metadata: 0x1234_5678_9abc_def0,
635 mask: 0xffff_ffff_0000_0000,
636 };
637 let encoded = inst.encode();
638 let (decoded, len) = Instruction::decode(&encoded).unwrap();
639 assert_eq!(len, 24);
640 match decoded {
641 Instruction::WriteMetadata { metadata, mask } => {
642 assert_eq!(metadata, 0x1234_5678_9abc_def0);
643 assert_eq!(mask, 0xffff_ffff_0000_0000);
644 }
645 _ => panic!("expected WriteMetadata instruction"),
646 }
647 }
648
649 #[test]
650 fn decode_apply_actions_instruction() {
651 let actions = ActionList::new()
652 .pop_vlan()
653 .output(OutputPort::Port(2));
654 let inst = Instruction::ApplyActions(actions);
655 let encoded = inst.encode();
656 let (decoded, _) = Instruction::decode(&encoded).unwrap();
657 match decoded {
658 Instruction::ApplyActions(actions) => {
659 assert_eq!(actions.len(), 2);
660 }
661 _ => panic!("expected ApplyActions instruction"),
662 }
663 }
664
665 #[test]
666 fn decode_write_actions_instruction() {
667 let actions = ActionList::new().output(OutputPort::Port(1));
668 let inst = Instruction::WriteActions(actions);
669 let encoded = inst.encode();
670 let (decoded, _) = Instruction::decode(&encoded).unwrap();
671 match decoded {
672 Instruction::WriteActions(actions) => {
673 assert_eq!(actions.len(), 1);
674 }
675 _ => panic!("expected WriteActions instruction"),
676 }
677 }
678
679 #[test]
680 fn decode_clear_actions_instruction() {
681 let inst = Instruction::ClearActions;
682 let encoded = inst.encode();
683 let (decoded, len) = Instruction::decode(&encoded).unwrap();
684 assert_eq!(len, 8);
685 assert!(matches!(decoded, Instruction::ClearActions));
686 }
687
688 #[test]
689 fn decode_meter_instruction() {
690 let inst = Instruction::Meter(42);
691 let encoded = inst.encode();
692 let (decoded, len) = Instruction::decode(&encoded).unwrap();
693 assert_eq!(len, 8);
694 match decoded {
695 Instruction::Meter(meter_id) => assert_eq!(meter_id, 42),
696 _ => panic!("expected Meter instruction"),
697 }
698 }
699
700 #[test]
701 fn decode_instruction_list_multiple() {
702 let actions = ActionList::new().output(OutputPort::Port(1));
703 let original = InstructionList::new()
704 .apply_actions(actions)
705 .goto_table(10);
706 let encoded = original.encode();
707 let decoded = InstructionList::decode(&encoded).unwrap();
708 assert_eq!(decoded.len(), 2);
709 assert!(matches!(decoded.instructions()[0], Instruction::ApplyActions(_)));
710 assert!(matches!(decoded.instructions()[1], Instruction::GotoTable(10)));
711 }
712
713 #[test]
714 fn decode_instruction_list_empty() {
715 let original = InstructionList::new();
716 let encoded = original.encode();
717 let decoded = InstructionList::decode(&encoded).unwrap();
718 assert!(decoded.is_empty());
719 }
720
721 #[test]
722 fn roundtrip_instruction_list() {
723 let actions = ActionList::new()
724 .push_vlan(0x8100)
725 .set_vlan_vid(100)
726 .output(OutputPort::Port(3));
727 let original = InstructionList::new()
728 .write_metadata(0xabcd, 0xffff)
729 .apply_actions(actions)
730 .meter(50)
731 .goto_table(5);
732
733 let encoded = original.encode();
734 let decoded = InstructionList::decode(&encoded).unwrap();
735
736 assert_eq!(decoded.len(), 4);
737 match &decoded.instructions()[0] {
738 Instruction::WriteMetadata { metadata, mask } => {
739 assert_eq!(*metadata, 0xabcd);
740 assert_eq!(*mask, 0xffff);
741 }
742 _ => panic!("expected WriteMetadata"),
743 }
744 match &decoded.instructions()[1] {
745 Instruction::ApplyActions(actions) => assert_eq!(actions.len(), 3),
746 _ => panic!("expected ApplyActions"),
747 }
748 match &decoded.instructions()[2] {
749 Instruction::Meter(meter_id) => assert_eq!(*meter_id, 50),
750 _ => panic!("expected Meter"),
751 }
752 match &decoded.instructions()[3] {
753 Instruction::GotoTable(table_id) => assert_eq!(*table_id, 5),
754 _ => panic!("expected GotoTable"),
755 }
756 }
757
758 #[test]
759 fn instruction_type_try_from() {
760 assert_eq!(InstructionType::try_from(1).unwrap(), InstructionType::GotoTable);
761 assert_eq!(InstructionType::try_from(2).unwrap(), InstructionType::WriteMetadata);
762 assert_eq!(InstructionType::try_from(3).unwrap(), InstructionType::WriteActions);
763 assert_eq!(InstructionType::try_from(4).unwrap(), InstructionType::ApplyActions);
764 assert_eq!(InstructionType::try_from(5).unwrap(), InstructionType::ClearActions);
765 assert_eq!(InstructionType::try_from(6).unwrap(), InstructionType::Meter);
766 assert!(InstructionType::try_from(99).is_err());
767 }
768}