1use super::types::{NxActionSubtype, NICIRA_VENDOR_ID, ActionType};
7
8#[allow(dead_code)]
10pub mod learn_flags {
11 pub const SEND_FLOW_REM: u16 = 1 << 0;
13 pub const DELETE_LEARNED: u16 = 1 << 1;
15 pub const WRITE_RESULT: u16 = 1 << 2;
17}
18
19#[derive(Debug, Clone, Default)]
24pub struct NxLearn {
25 pub idle_timeout: u16,
27 pub hard_timeout: u16,
29 pub priority: u16,
31 pub cookie: u64,
33 pub flags: u16,
35 pub table_id: u8,
37 pub fin_idle_timeout: u16,
39 pub fin_hard_timeout: u16,
41 pub specs: Vec<LearnSpec>,
43}
44
45#[derive(Debug, Clone)]
50pub enum LearnSpec {
51 MatchField {
53 src_field: u32,
55 dst_field: u32,
57 n_bits: u16,
59 },
60 MatchImmediate {
62 dst_field: u32,
64 value: Vec<u8>,
66 n_bits: u16,
68 },
69 LoadField {
71 src_field: u32,
73 dst_field: u32,
75 n_bits: u16,
77 },
78 LoadImmediate {
80 dst_field: u32,
82 value: Vec<u8>,
84 n_bits: u16,
86 },
87 OutputField {
89 src_field: u32,
91 n_bits: u16,
93 },
94}
95
96impl NxLearn {
97 pub fn new() -> Self {
99 Self::default()
100 }
101
102 pub fn idle_timeout(mut self, timeout: u16) -> Self {
104 self.idle_timeout = timeout;
105 self
106 }
107
108 pub fn hard_timeout(mut self, timeout: u16) -> Self {
110 self.hard_timeout = timeout;
111 self
112 }
113
114 pub fn priority(mut self, priority: u16) -> Self {
116 self.priority = priority;
117 self
118 }
119
120 pub fn cookie(mut self, cookie: u64) -> Self {
122 self.cookie = cookie;
123 self
124 }
125
126 pub fn table(mut self, table_id: u8) -> Self {
128 self.table_id = table_id;
129 self
130 }
131
132 pub fn flags(mut self, flags: u16) -> Self {
134 self.flags = flags;
135 self
136 }
137
138 pub fn match_field(mut self, src_field: u32, dst_field: u32, n_bits: u16) -> Self {
140 self.specs.push(LearnSpec::MatchField { src_field, dst_field, n_bits });
141 self
142 }
143
144 pub fn match_immediate(mut self, dst_field: u32, value: Vec<u8>, n_bits: u16) -> Self {
146 self.specs.push(LearnSpec::MatchImmediate { dst_field, value, n_bits });
147 self
148 }
149
150 pub fn load_field(mut self, src_field: u32, dst_field: u32, n_bits: u16) -> Self {
152 self.specs.push(LearnSpec::LoadField { src_field, dst_field, n_bits });
153 self
154 }
155
156 pub fn load_immediate(mut self, dst_field: u32, value: Vec<u8>, n_bits: u16) -> Self {
158 self.specs.push(LearnSpec::LoadImmediate { dst_field, value, n_bits });
159 self
160 }
161
162 pub fn output_field(mut self, src_field: u32, n_bits: u16) -> Self {
164 self.specs.push(LearnSpec::OutputField { src_field, n_bits });
165 self
166 }
167}
168
169pub(crate) fn encode_nx_header(subtype: NxActionSubtype, len: u16) -> Vec<u8> {
175 let mut buf = Vec::with_capacity(16);
176 buf.extend((ActionType::Experimenter as u16).to_be_bytes());
177 buf.extend(len.to_be_bytes());
178 buf.extend(NICIRA_VENDOR_ID.to_be_bytes());
179 buf.extend((subtype as u16).to_be_bytes());
180 buf
181}
182
183pub(crate) fn encode_set_tunnel_id(tun_id: u64) -> Vec<u8> {
185 let mut buf = encode_nx_header(NxActionSubtype::RegLoad2, 24);
188
189 let oxm_header: u32 = (1 << 16) | (16 << 9) | 8;
191 buf.extend(oxm_header.to_be_bytes());
192 buf.extend(tun_id.to_be_bytes());
193 buf.extend([0u8; 2]); buf
195}
196
197pub(crate) fn encode_nx_resubmit(in_port: Option<u16>, table: Option<u8>) -> Vec<u8> {
199 let mut buf = encode_nx_header(NxActionSubtype::ResubmitTable, 16);
201 buf.extend(in_port.unwrap_or(0xfff8).to_be_bytes()); buf.push(table.unwrap_or(255)); buf.extend([0u8; 3]); buf
205}
206
207pub(crate) fn encode_nx_ct(flags: u16, zone: u16, table: Option<u8>) -> Vec<u8> {
209 let mut buf = encode_nx_header(NxActionSubtype::Ct, 24);
212 buf.extend(flags.to_be_bytes());
213 buf.extend(0u32.to_be_bytes()); buf.extend(zone.to_be_bytes()); buf.push(table.unwrap_or(255)); buf.extend([0u8; 3]); buf.extend(0u16.to_be_bytes()); buf
220}
221
222pub(crate) fn encode_nx_ct_nat(
224 flags: u16,
225 zone: u16,
226 table: Option<u8>,
227 nat: &super::NatConfig,
228) -> Vec<u8> {
229 let nat_action = encode_nx_nat(nat);
231
232 let ct_header_len = 24; let total_len = ct_header_len + nat_action.len();
237 let padded_len = (total_len + 7) & !7;
239
240 let mut buf = Vec::with_capacity(padded_len);
241
242 buf.extend((ActionType::Experimenter as u16).to_be_bytes());
244 buf.extend((padded_len as u16).to_be_bytes());
245 buf.extend(NICIRA_VENDOR_ID.to_be_bytes());
246 buf.extend((NxActionSubtype::Ct as u16).to_be_bytes());
247
248 buf.extend(flags.to_be_bytes());
250 buf.extend(0u32.to_be_bytes()); buf.extend(zone.to_be_bytes()); buf.push(table.unwrap_or(255)); buf.extend([0u8; 3]); buf.extend(0u16.to_be_bytes()); buf.extend(nat_action);
258
259 buf.resize(padded_len, 0);
261 buf
262}
263
264fn encode_nx_nat(nat: &super::NatConfig) -> Vec<u8> {
269 let range_present = nat.range_present();
270
271 let mut optional_len = 0;
273 if nat.ipv4_min.is_some() {
274 optional_len += 4;
275 }
276 if nat.ipv4_max.is_some() {
277 optional_len += 4;
278 }
279 if nat.ipv6_min.is_some() {
280 optional_len += 16;
281 }
282 if nat.ipv6_max.is_some() {
283 optional_len += 16;
284 }
285 if nat.port_min.is_some() {
286 optional_len += 2;
287 }
288 if nat.port_max.is_some() {
289 optional_len += 2;
290 }
291
292 let header_len = 16;
294 let total_len = header_len + optional_len;
295 let padded_len = (total_len + 7) & !7;
297
298 let mut buf = encode_nx_header(NxActionSubtype::Nat, padded_len as u16);
299 buf.extend([0u8; 2]); buf.extend(nat.flags.to_be_bytes());
301 buf.extend(range_present.to_be_bytes());
302
303 if let Some(addr) = nat.ipv4_min {
305 buf.extend(addr.octets());
306 }
307 if let Some(addr) = nat.ipv4_max {
308 buf.extend(addr.octets());
309 }
310 if let Some(addr) = nat.ipv6_min {
311 buf.extend(addr.octets());
312 }
313 if let Some(addr) = nat.ipv6_max {
314 buf.extend(addr.octets());
315 }
316 if let Some(port) = nat.port_min {
317 buf.extend(port.to_be_bytes());
318 }
319 if let Some(port) = nat.port_max {
320 buf.extend(port.to_be_bytes());
321 }
322
323 buf.resize(padded_len, 0);
325 buf
326}
327
328#[allow(dead_code)]
332pub fn encode_nx_reg_load(reg_num: u8, value: u32, start_bit: u8, n_bits: u8) -> Vec<u8> {
333 let mut buf = encode_nx_header(NxActionSubtype::RegLoad, 24);
336
337 let ofs_nbits = ((start_bit as u16) << 6) | ((n_bits - 1) as u16);
339 buf.extend(ofs_nbits.to_be_bytes());
340
341 let dst_header: u32 = (1 << 16) | ((reg_num as u32) << 9) | 4;
343 buf.extend(dst_header.to_be_bytes());
344
345 buf.extend((value as u64).to_be_bytes());
347 buf
348}
349
350pub(crate) fn encode_nx_reg_load_nxm(dst_field: u32, dst_ofs: u16, n_bits: u16, value: u64) -> Vec<u8> {
355 let mut buf = encode_nx_header(NxActionSubtype::RegLoad, 24);
358
359 let ofs_nbits = (dst_ofs << 6) | (n_bits - 1);
361 buf.extend(ofs_nbits.to_be_bytes());
362
363 buf.extend(dst_field.to_be_bytes());
365
366 buf.extend(value.to_be_bytes());
368 buf
369}
370
371pub(crate) fn encode_nx_move(
375 src_field: u32,
376 dst_field: u32,
377 n_bits: u16,
378 src_ofs: u16,
379 dst_ofs: u16,
380) -> Vec<u8> {
381 let mut buf = encode_nx_header(NxActionSubtype::Move, 24);
384 buf.extend(n_bits.to_be_bytes());
385 buf.extend(src_ofs.to_be_bytes());
386 buf.extend(dst_ofs.to_be_bytes());
387 buf.extend(src_field.to_be_bytes());
388 buf.extend(dst_field.to_be_bytes());
389 buf
390}
391
392pub(crate) fn encode_nx_learn(learn: &NxLearn) -> Vec<u8> {
416 let specs_bytes = encode_learn_specs(&learn.specs);
418
419 let header_and_fields = 32; let total_len = header_and_fields + specs_bytes.len();
422 let padded_len = (total_len + 7) & !7;
424
425 let mut buf = Vec::with_capacity(padded_len);
427
428 buf.extend((ActionType::Experimenter as u16).to_be_bytes());
430 buf.extend((padded_len as u16).to_be_bytes());
431 buf.extend(NICIRA_VENDOR_ID.to_be_bytes());
432 buf.extend((NxActionSubtype::Learn as u16).to_be_bytes());
433
434 buf.extend(learn.idle_timeout.to_be_bytes());
436 buf.extend(learn.hard_timeout.to_be_bytes());
437 buf.extend(learn.priority.to_be_bytes());
438 buf.extend(learn.cookie.to_be_bytes());
439 buf.extend(learn.flags.to_be_bytes());
440 buf.push(learn.table_id);
441 buf.push(0); buf.extend(learn.fin_idle_timeout.to_be_bytes());
443 buf.extend(learn.fin_hard_timeout.to_be_bytes());
444
445 buf.extend(specs_bytes);
447
448 buf.resize(padded_len, 0);
450 buf
451}
452
453mod learn_spec_header {
455 pub const SRC_FIELD: u16 = 0 << 13;
457 pub const SRC_IMMEDIATE: u16 = 1 << 13;
459 pub const DST_MATCH: u16 = 0 << 11;
461 pub const DST_LOAD: u16 = 1 << 11;
463 pub const DST_OUTPUT: u16 = 2 << 11;
465}
466
467fn encode_learn_subfield(buf: &mut Vec<u8>, nxm_header: u32, ofs: u16) {
473 buf.extend(nxm_header.to_be_bytes());
474 buf.extend(ofs.to_be_bytes());
475}
476
477fn encode_learn_specs(specs: &[LearnSpec]) -> Vec<u8> {
479 let mut buf = Vec::new();
480
481 for spec in specs {
482 match spec {
483 LearnSpec::MatchField { src_field, dst_field, n_bits } => {
484 let header = learn_spec_header::SRC_FIELD
486 | learn_spec_header::DST_MATCH
487 | (n_bits - 1);
488 buf.extend(header.to_be_bytes());
489 encode_learn_subfield(&mut buf, *src_field, 0);
491 encode_learn_subfield(&mut buf, *dst_field, 0);
492 }
493 LearnSpec::MatchImmediate { dst_field, value, n_bits } => {
494 let header = learn_spec_header::SRC_IMMEDIATE
496 | learn_spec_header::DST_MATCH
497 | (n_bits - 1);
498 buf.extend(header.to_be_bytes());
499 let value_len = (*n_bits as usize).div_ceil(16) * 2;
501 let mut padded_value = vec![0u8; value_len];
502 let copy_len = value.len().min(value_len);
503 padded_value[value_len - copy_len..].copy_from_slice(&value[..copy_len]);
504 buf.extend(padded_value);
505 encode_learn_subfield(&mut buf, *dst_field, 0);
507 }
508 LearnSpec::LoadField { src_field, dst_field, n_bits } => {
509 let header = learn_spec_header::SRC_FIELD
511 | learn_spec_header::DST_LOAD
512 | (n_bits - 1);
513 buf.extend(header.to_be_bytes());
514 encode_learn_subfield(&mut buf, *src_field, 0);
516 encode_learn_subfield(&mut buf, *dst_field, 0);
517 }
518 LearnSpec::LoadImmediate { dst_field, value, n_bits } => {
519 let header = learn_spec_header::SRC_IMMEDIATE
521 | learn_spec_header::DST_LOAD
522 | (n_bits - 1);
523 buf.extend(header.to_be_bytes());
524 let value_len = (*n_bits as usize).div_ceil(16) * 2;
526 let mut padded_value = vec![0u8; value_len];
527 let copy_len = value.len().min(value_len);
528 padded_value[value_len - copy_len..].copy_from_slice(&value[..copy_len]);
529 buf.extend(padded_value);
530 encode_learn_subfield(&mut buf, *dst_field, 0);
532 }
533 LearnSpec::OutputField { src_field, n_bits } => {
534 let header = learn_spec_header::SRC_FIELD
536 | learn_spec_header::DST_OUTPUT
537 | (n_bits - 1);
538 buf.extend(header.to_be_bytes());
539 encode_learn_subfield(&mut buf, *src_field, 0);
541 }
542 }
543 }
544
545 buf
546}
547
548use super::Action;
553use crate::oxm::OxmClass;
554
555pub(crate) fn decode_nicira_action(data: &[u8]) -> crate::Result<Action> {
559 if data.len() < 2 {
560 return Err(crate::Error::Parse("nicira action too short".into()));
561 }
562
563 let subtype = u16::from_be_bytes([data[0], data[1]]);
564
565 match subtype {
566 s if s == NxActionSubtype::ResubmitTable as u16 => {
567 if data.len() < 6 {
569 return Err(crate::Error::Parse("resubmit action too short".into()));
570 }
571 let in_port = u16::from_be_bytes([data[2], data[3]]);
572 let table = data[4];
573 let port = if in_port == 0xfff8 { None } else { Some(in_port) };
574 let table = if table == 255 { None } else { Some(table) };
575 Ok(Action::NxResubmit { port, table })
576 }
577 s if s == NxActionSubtype::Resubmit as u16 => {
578 if data.len() < 4 {
580 return Err(crate::Error::Parse("resubmit action too short".into()));
581 }
582 let in_port = u16::from_be_bytes([data[2], data[3]]);
583 let port = if in_port == 0xfff8 { None } else { Some(in_port) };
584 Ok(Action::NxResubmit { port, table: None })
585 }
586 s if s == NxActionSubtype::Ct as u16 => {
587 if data.len() < 10 {
589 return Err(crate::Error::Parse("ct action too short".into()));
590 }
591 let flags = u16::from_be_bytes([data[2], data[3]]);
592 let zone = u16::from_be_bytes([data[8], data[9]]);
594 let recirc_table = if data.len() > 10 { data[10] } else { 255 };
595 let table = if recirc_table == 255 { None } else { Some(recirc_table) };
596 Ok(Action::NxCt { flags, zone, table })
597 }
598 s if s == NxActionSubtype::RegLoad2 as u16 => {
599 if data.len() < 6 {
601 return Err(crate::Error::Parse("reg_load2 action too short".into()));
602 }
603 let oxm_header = u32::from_be_bytes([data[2], data[3], data[4], data[5]]);
604 let oxm_class = (oxm_header >> 16) as u16;
605 let field = ((oxm_header >> 9) & 0x7f) as u8;
606 let length = (oxm_header & 0xff) as usize;
607
608 if data.len() < 6 + length {
609 return Err(crate::Error::Parse("reg_load2 value truncated".into()));
610 }
611
612 let value = &data[6..6 + length];
613
614 if oxm_class == OxmClass::Nxm1 as u16 && field == 16 && length >= 8 {
616 let tun_id = u64::from_be_bytes([
617 value[0], value[1], value[2], value[3],
618 value[4], value[5], value[6], value[7],
619 ]);
620 Ok(Action::SetTunnelId(tun_id))
621 } else {
622 Ok(Action::Drop)
623 }
624 }
625 s if s == NxActionSubtype::Learn as u16 => {
626 if data.len() < 22 {
630 return Err(crate::Error::Parse("learn action too short".into()));
631 }
632 let idle_timeout = u16::from_be_bytes([data[2], data[3]]);
633 let hard_timeout = u16::from_be_bytes([data[4], data[5]]);
634 let priority = u16::from_be_bytes([data[6], data[7]]);
635 let cookie = u64::from_be_bytes([
636 data[8], data[9], data[10], data[11],
637 data[12], data[13], data[14], data[15],
638 ]);
639 let flags = u16::from_be_bytes([data[16], data[17]]);
640 let table_id = data[18];
641 let fin_idle_timeout = u16::from_be_bytes([data[20], data[21]]);
643 let fin_hard_timeout = if data.len() > 23 {
644 u16::from_be_bytes([data[22], data[23]])
645 } else {
646 0
647 };
648
649 let specs = if data.len() > 24 {
651 decode_learn_specs(&data[24..])
652 } else {
653 Vec::new()
654 };
655
656 Ok(Action::NxLearn(NxLearn {
657 idle_timeout,
658 hard_timeout,
659 priority,
660 cookie,
661 flags,
662 table_id,
663 fin_idle_timeout,
664 fin_hard_timeout,
665 specs,
666 }))
667 }
668 _ => {
669 Ok(Action::Drop)
671 }
672 }
673}
674
675pub(crate) fn decode_learn_specs(data: &[u8]) -> Vec<LearnSpec> {
677 let mut specs = Vec::new();
678 let mut offset = 0;
679
680 while offset + 2 <= data.len() {
681 let header = u16::from_be_bytes([data[offset], data[offset + 1]]);
682 if header == 0 {
683 break; }
685 offset += 2;
686
687 let n_bits = (header & 0x07ff) + 1; let src_type = (header >> 13) & 0x01; let dst_type = (header >> 11) & 0x03; match (src_type, dst_type) {
692 (0, 0) => {
693 if offset + 12 > data.len() {
696 break;
697 }
698 let src_field = u32::from_be_bytes([
699 data[offset], data[offset + 1], data[offset + 2], data[offset + 3],
700 ]);
701 let dst_field = u32::from_be_bytes([
703 data[offset + 6], data[offset + 7], data[offset + 8], data[offset + 9],
704 ]);
705 offset += 12;
707 specs.push(LearnSpec::MatchField { src_field, dst_field, n_bits });
708 }
709 (1, 0) => {
710 let value_len = (n_bits as usize).div_ceil(16) * 2;
712 if offset + value_len + 6 > data.len() {
713 break;
714 }
715 let value = data[offset..offset + value_len].to_vec();
716 offset += value_len;
717 let dst_field = u32::from_be_bytes([
718 data[offset], data[offset + 1], data[offset + 2], data[offset + 3],
719 ]);
720 offset += 6; specs.push(LearnSpec::MatchImmediate { dst_field, value, n_bits });
722 }
723 (0, 1) => {
724 if offset + 12 > data.len() {
726 break;
727 }
728 let src_field = u32::from_be_bytes([
729 data[offset], data[offset + 1], data[offset + 2], data[offset + 3],
730 ]);
731 let dst_field = u32::from_be_bytes([
732 data[offset + 6], data[offset + 7], data[offset + 8], data[offset + 9],
733 ]);
734 offset += 12;
735 specs.push(LearnSpec::LoadField { src_field, dst_field, n_bits });
736 }
737 (1, 1) => {
738 let value_len = (n_bits as usize).div_ceil(16) * 2;
740 if offset + value_len + 6 > data.len() {
741 break;
742 }
743 let value = data[offset..offset + value_len].to_vec();
744 offset += value_len;
745 let dst_field = u32::from_be_bytes([
746 data[offset], data[offset + 1], data[offset + 2], data[offset + 3],
747 ]);
748 offset += 6; specs.push(LearnSpec::LoadImmediate { dst_field, value, n_bits });
750 }
751 (0, 2) => {
752 if offset + 6 > data.len() {
754 break;
755 }
756 let src_field = u32::from_be_bytes([
757 data[offset], data[offset + 1], data[offset + 2], data[offset + 3],
758 ]);
759 offset += 6; specs.push(LearnSpec::OutputField { src_field, n_bits });
761 }
762 _ => {
763 break;
765 }
766 }
767 }
768
769 specs
770}