1use crate::ber::{Decoder, EncodeBuf, tag};
6use crate::error::{DecodeErrorKind, Error, ErrorStatus, Result};
7use crate::oid::Oid;
8use crate::varbind::{VarBind, decode_varbind_list, encode_varbind_list};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12#[repr(u8)]
13pub enum PduType {
14 GetRequest = 0xA0,
16 GetNextRequest = 0xA1,
18 Response = 0xA2,
20 SetRequest = 0xA3,
22 TrapV1 = 0xA4,
24 GetBulkRequest = 0xA5,
26 InformRequest = 0xA6,
28 TrapV2 = 0xA7,
30 Report = 0xA8,
32}
33
34impl PduType {
35 pub fn from_tag(tag: u8) -> Option<Self> {
37 match tag {
38 0xA0 => Some(Self::GetRequest),
39 0xA1 => Some(Self::GetNextRequest),
40 0xA2 => Some(Self::Response),
41 0xA3 => Some(Self::SetRequest),
42 0xA4 => Some(Self::TrapV1),
43 0xA5 => Some(Self::GetBulkRequest),
44 0xA6 => Some(Self::InformRequest),
45 0xA7 => Some(Self::TrapV2),
46 0xA8 => Some(Self::Report),
47 _ => None,
48 }
49 }
50
51 pub fn tag(self) -> u8 {
53 self as u8
54 }
55}
56
57impl std::fmt::Display for PduType {
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 match self {
60 Self::GetRequest => write!(f, "GetRequest"),
61 Self::GetNextRequest => write!(f, "GetNextRequest"),
62 Self::Response => write!(f, "Response"),
63 Self::SetRequest => write!(f, "SetRequest"),
64 Self::TrapV1 => write!(f, "TrapV1"),
65 Self::GetBulkRequest => write!(f, "GetBulkRequest"),
66 Self::InformRequest => write!(f, "InformRequest"),
67 Self::TrapV2 => write!(f, "TrapV2"),
68 Self::Report => write!(f, "Report"),
69 }
70 }
71}
72
73#[derive(Debug, Clone)]
75pub struct Pdu {
76 pub pdu_type: PduType,
78 pub request_id: i32,
80 pub error_status: i32,
82 pub error_index: i32,
84 pub varbinds: Vec<VarBind>,
86}
87
88impl Pdu {
89 pub fn get_request(request_id: i32, oids: &[Oid]) -> Self {
91 Self {
92 pdu_type: PduType::GetRequest,
93 request_id,
94 error_status: 0,
95 error_index: 0,
96 varbinds: oids.iter().map(|oid| VarBind::null(oid.clone())).collect(),
97 }
98 }
99
100 pub fn get_next_request(request_id: i32, oids: &[Oid]) -> Self {
102 Self {
103 pdu_type: PduType::GetNextRequest,
104 request_id,
105 error_status: 0,
106 error_index: 0,
107 varbinds: oids.iter().map(|oid| VarBind::null(oid.clone())).collect(),
108 }
109 }
110
111 pub fn set_request(request_id: i32, varbinds: Vec<VarBind>) -> Self {
113 Self {
114 pdu_type: PduType::SetRequest,
115 request_id,
116 error_status: 0,
117 error_index: 0,
118 varbinds,
119 }
120 }
121
122 pub fn get_bulk(
126 request_id: i32,
127 non_repeaters: i32,
128 max_repetitions: i32,
129 varbinds: Vec<VarBind>,
130 ) -> Self {
131 Self {
132 pdu_type: PduType::GetBulkRequest,
133 request_id,
134 error_status: non_repeaters,
135 error_index: max_repetitions,
136 varbinds,
137 }
138 }
139
140 pub fn encode(&self, buf: &mut EncodeBuf) {
142 buf.push_constructed(self.pdu_type.tag(), |buf| {
143 encode_varbind_list(buf, &self.varbinds);
144 buf.push_integer(self.error_index);
145 buf.push_integer(self.error_status);
146 buf.push_integer(self.request_id);
147 });
148 }
149
150 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
152 let tag = decoder.read_tag()?;
153 let pdu_type = PduType::from_tag(tag)
154 .ok_or_else(|| Error::decode(decoder.offset(), DecodeErrorKind::UnknownPduType(tag)))?;
155
156 let len = decoder.read_length()?;
157 let mut pdu_decoder = decoder.sub_decoder(len)?;
158
159 let request_id = pdu_decoder.read_integer()?;
160 let error_status = pdu_decoder.read_integer()?;
161 let error_index = pdu_decoder.read_integer()?;
162 let varbinds = decode_varbind_list(&mut pdu_decoder)?;
163
164 Ok(Pdu {
165 pdu_type,
166 request_id,
167 error_status,
168 error_index,
169 varbinds,
170 })
171 }
172
173 pub fn is_error(&self) -> bool {
175 self.error_status != 0
176 }
177
178 pub fn error_status_enum(&self) -> ErrorStatus {
180 ErrorStatus::from_i32(self.error_status)
181 }
182
183 pub fn to_response(&self) -> Self {
188 Self {
189 pdu_type: PduType::Response,
190 request_id: self.request_id,
191 error_status: 0,
192 error_index: 0,
193 varbinds: self.varbinds.clone(),
194 }
195 }
196
197 pub fn to_error_response(&self, error_status: ErrorStatus, error_index: i32) -> Self {
199 Self {
200 pdu_type: PduType::Response,
201 request_id: self.request_id,
202 error_status: error_status.as_i32(),
203 error_index,
204 varbinds: self.varbinds.clone(),
205 }
206 }
207
208 pub fn is_notification(&self) -> bool {
210 matches!(
211 self.pdu_type,
212 PduType::TrapV1 | PduType::TrapV2 | PduType::InformRequest
213 )
214 }
215
216 pub fn is_confirmed(&self) -> bool {
218 matches!(
219 self.pdu_type,
220 PduType::GetRequest
221 | PduType::GetNextRequest
222 | PduType::GetBulkRequest
223 | PduType::SetRequest
224 | PduType::InformRequest
225 )
226 }
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq)]
231#[repr(i32)]
232pub enum GenericTrap {
233 ColdStart = 0,
235 WarmStart = 1,
237 LinkDown = 2,
239 LinkUp = 3,
241 AuthenticationFailure = 4,
243 EgpNeighborLoss = 5,
245 EnterpriseSpecific = 6,
247}
248
249impl GenericTrap {
250 pub fn from_i32(v: i32) -> Option<Self> {
252 match v {
253 0 => Some(Self::ColdStart),
254 1 => Some(Self::WarmStart),
255 2 => Some(Self::LinkDown),
256 3 => Some(Self::LinkUp),
257 4 => Some(Self::AuthenticationFailure),
258 5 => Some(Self::EgpNeighborLoss),
259 6 => Some(Self::EnterpriseSpecific),
260 _ => None,
261 }
262 }
263
264 pub fn as_i32(self) -> i32 {
266 self as i32
267 }
268}
269
270#[derive(Debug, Clone)]
275pub struct TrapV1Pdu {
276 pub enterprise: Oid,
278 pub agent_addr: [u8; 4],
280 pub generic_trap: i32,
282 pub specific_trap: i32,
284 pub time_stamp: u32,
286 pub varbinds: Vec<VarBind>,
288}
289
290impl TrapV1Pdu {
291 pub fn new(
293 enterprise: Oid,
294 agent_addr: [u8; 4],
295 generic_trap: GenericTrap,
296 specific_trap: i32,
297 time_stamp: u32,
298 varbinds: Vec<VarBind>,
299 ) -> Self {
300 Self {
301 enterprise,
302 agent_addr,
303 generic_trap: generic_trap.as_i32(),
304 specific_trap,
305 time_stamp,
306 varbinds,
307 }
308 }
309
310 pub fn generic_trap_enum(&self) -> Option<GenericTrap> {
312 GenericTrap::from_i32(self.generic_trap)
313 }
314
315 pub fn is_enterprise_specific(&self) -> bool {
317 self.generic_trap == GenericTrap::EnterpriseSpecific as i32
318 }
319
320 pub fn v2_trap_oid(&self) -> Oid {
360 if self.is_enterprise_specific() {
361 let mut arcs: Vec<u32> = self.enterprise.arcs().to_vec();
363 arcs.push(0);
364 arcs.push(self.specific_trap as u32);
365 Oid::new(arcs)
366 } else {
367 let trap_num = self.generic_trap + 1;
370 crate::oid!(1, 3, 6, 1, 6, 3, 1, 1, 5).child(trap_num as u32)
371 }
372 }
373
374 pub fn encode(&self, buf: &mut EncodeBuf) {
376 buf.push_constructed(tag::pdu::TRAP_V1, |buf| {
377 encode_varbind_list(buf, &self.varbinds);
378 buf.push_unsigned32(tag::application::TIMETICKS, self.time_stamp);
379 buf.push_integer(self.specific_trap);
380 buf.push_integer(self.generic_trap);
381 buf.push_bytes(&self.agent_addr);
384 buf.push_length(4);
385 buf.push_tag(tag::application::IP_ADDRESS);
386 buf.push_oid(&self.enterprise);
387 });
388 }
389
390 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
392 let mut pdu = decoder.read_constructed(tag::pdu::TRAP_V1)?;
393
394 let enterprise = pdu.read_oid()?;
396
397 let agent_tag = pdu.read_tag()?;
399 if agent_tag != tag::application::IP_ADDRESS {
400 return Err(Error::decode(
401 pdu.offset(),
402 DecodeErrorKind::UnexpectedTag {
403 expected: 0x40,
404 actual: agent_tag,
405 },
406 ));
407 }
408 let agent_len = pdu.read_length()?;
409 if agent_len != 4 {
410 return Err(Error::decode(
411 pdu.offset(),
412 DecodeErrorKind::InvalidIpAddressLength { length: agent_len },
413 ));
414 }
415 let agent_bytes = pdu.read_bytes(4)?;
416 let agent_addr = [
417 agent_bytes[0],
418 agent_bytes[1],
419 agent_bytes[2],
420 agent_bytes[3],
421 ];
422
423 let generic_trap = pdu.read_integer()?;
425
426 let specific_trap = pdu.read_integer()?;
428
429 let ts_tag = pdu.read_tag()?;
431 if ts_tag != tag::application::TIMETICKS {
432 return Err(Error::decode(
433 pdu.offset(),
434 DecodeErrorKind::UnexpectedTag {
435 expected: 0x43,
436 actual: ts_tag,
437 },
438 ));
439 }
440 let ts_len = pdu.read_length()?;
441 let time_stamp = pdu.read_unsigned32_value(ts_len)?;
442
443 let varbinds = decode_varbind_list(&mut pdu)?;
445
446 Ok(TrapV1Pdu {
447 enterprise,
448 agent_addr,
449 generic_trap,
450 specific_trap,
451 time_stamp,
452 varbinds,
453 })
454 }
455}
456
457#[derive(Debug, Clone)]
459pub struct GetBulkPdu {
460 pub request_id: i32,
462 pub non_repeaters: i32,
464 pub max_repetitions: i32,
466 pub varbinds: Vec<VarBind>,
468}
469
470impl GetBulkPdu {
471 pub fn new(request_id: i32, non_repeaters: i32, max_repetitions: i32, oids: &[Oid]) -> Self {
473 Self {
474 request_id,
475 non_repeaters,
476 max_repetitions,
477 varbinds: oids.iter().map(|oid| VarBind::null(oid.clone())).collect(),
478 }
479 }
480
481 pub fn encode(&self, buf: &mut EncodeBuf) {
483 buf.push_constructed(tag::pdu::GET_BULK_REQUEST, |buf| {
484 encode_varbind_list(buf, &self.varbinds);
485 buf.push_integer(self.max_repetitions);
486 buf.push_integer(self.non_repeaters);
487 buf.push_integer(self.request_id);
488 });
489 }
490
491 pub fn decode(decoder: &mut Decoder) -> Result<Self> {
493 let mut pdu = decoder.read_constructed(tag::pdu::GET_BULK_REQUEST)?;
494
495 let request_id = pdu.read_integer()?;
496 let non_repeaters = pdu.read_integer()?;
497 let max_repetitions = pdu.read_integer()?;
498 let varbinds = decode_varbind_list(&mut pdu)?;
499
500 Ok(GetBulkPdu {
501 request_id,
502 non_repeaters,
503 max_repetitions,
504 varbinds,
505 })
506 }
507}
508
509#[cfg(test)]
510mod tests {
511 use super::*;
512 use crate::oid;
513
514 #[test]
515 fn test_get_request_roundtrip() {
516 let pdu = Pdu::get_request(12345, &[oid!(1, 3, 6, 1, 2, 1, 1, 1, 0)]);
517
518 let mut buf = EncodeBuf::new();
519 pdu.encode(&mut buf);
520 let bytes = buf.finish();
521
522 let mut decoder = Decoder::new(bytes);
523 let decoded = Pdu::decode(&mut decoder).unwrap();
524
525 assert_eq!(decoded.pdu_type, PduType::GetRequest);
526 assert_eq!(decoded.request_id, 12345);
527 assert_eq!(decoded.varbinds.len(), 1);
528 }
529
530 #[test]
531 fn test_getbulk_roundtrip() {
532 let pdu = GetBulkPdu::new(12345, 0, 10, &[oid!(1, 3, 6, 1, 2, 1, 1)]);
533
534 let mut buf = EncodeBuf::new();
535 pdu.encode(&mut buf);
536 let bytes = buf.finish();
537
538 let mut decoder = Decoder::new(bytes);
539 let decoded = GetBulkPdu::decode(&mut decoder).unwrap();
540
541 assert_eq!(decoded.request_id, 12345);
542 assert_eq!(decoded.non_repeaters, 0);
543 assert_eq!(decoded.max_repetitions, 10);
544 }
545
546 #[test]
547 fn test_trap_v1_roundtrip() {
548 use crate::value::Value;
549 use crate::varbind::VarBind;
550
551 let trap = TrapV1Pdu::new(
552 oid!(1, 3, 6, 1, 4, 1, 9999), [192, 168, 1, 1], GenericTrap::LinkDown,
555 0,
556 12345678, vec![VarBind::new(
558 oid!(1, 3, 6, 1, 2, 1, 2, 2, 1, 1, 1),
559 Value::Integer(1),
560 )],
561 );
562
563 let mut buf = EncodeBuf::new();
564 trap.encode(&mut buf);
565 let bytes = buf.finish();
566
567 let mut decoder = Decoder::new(bytes);
568 let decoded = TrapV1Pdu::decode(&mut decoder).unwrap();
569
570 assert_eq!(decoded.enterprise, oid!(1, 3, 6, 1, 4, 1, 9999));
571 assert_eq!(decoded.agent_addr, [192, 168, 1, 1]);
572 assert_eq!(decoded.generic_trap, GenericTrap::LinkDown as i32);
573 assert_eq!(decoded.specific_trap, 0);
574 assert_eq!(decoded.time_stamp, 12345678);
575 assert_eq!(decoded.varbinds.len(), 1);
576 }
577
578 #[test]
579 fn test_trap_v1_enterprise_specific() {
580 let trap = TrapV1Pdu::new(
581 oid!(1, 3, 6, 1, 4, 1, 9999, 1, 2),
582 [10, 0, 0, 1],
583 GenericTrap::EnterpriseSpecific,
584 42, 100,
586 vec![],
587 );
588
589 assert!(trap.is_enterprise_specific());
590 assert_eq!(
591 trap.generic_trap_enum(),
592 Some(GenericTrap::EnterpriseSpecific)
593 );
594
595 let mut buf = EncodeBuf::new();
596 trap.encode(&mut buf);
597 let bytes = buf.finish();
598
599 let mut decoder = Decoder::new(bytes);
600 let decoded = TrapV1Pdu::decode(&mut decoder).unwrap();
601
602 assert_eq!(decoded.specific_trap, 42);
603 }
604
605 #[test]
606 fn test_trap_v1_v2_trap_oid_generic_traps() {
607 let test_cases = [
611 (GenericTrap::ColdStart, oid!(1, 3, 6, 1, 6, 3, 1, 1, 5, 1)),
612 (GenericTrap::WarmStart, oid!(1, 3, 6, 1, 6, 3, 1, 1, 5, 2)),
613 (GenericTrap::LinkDown, oid!(1, 3, 6, 1, 6, 3, 1, 1, 5, 3)),
614 (GenericTrap::LinkUp, oid!(1, 3, 6, 1, 6, 3, 1, 1, 5, 4)),
615 (
616 GenericTrap::AuthenticationFailure,
617 oid!(1, 3, 6, 1, 6, 3, 1, 1, 5, 5),
618 ),
619 (
620 GenericTrap::EgpNeighborLoss,
621 oid!(1, 3, 6, 1, 6, 3, 1, 1, 5, 6),
622 ),
623 ];
624
625 for (generic_trap, expected_oid) in test_cases {
626 let trap = TrapV1Pdu::new(
627 oid!(1, 3, 6, 1, 4, 1, 9999),
628 [192, 168, 1, 1],
629 generic_trap,
630 0,
631 12345,
632 vec![],
633 );
634 assert_eq!(
635 trap.v2_trap_oid(),
636 expected_oid,
637 "Failed for {:?}",
638 generic_trap
639 );
640 }
641 }
642
643 #[test]
644 fn test_trap_v1_v2_trap_oid_enterprise_specific() {
645 let trap = TrapV1Pdu::new(
647 oid!(1, 3, 6, 1, 4, 1, 9999, 1, 2),
648 [192, 168, 1, 1],
649 GenericTrap::EnterpriseSpecific,
650 42,
651 12345,
652 vec![],
653 );
654
655 assert_eq!(
657 trap.v2_trap_oid(),
658 oid!(1, 3, 6, 1, 4, 1, 9999, 1, 2, 0, 42)
659 );
660 }
661
662 #[test]
663 fn test_trap_v1_v2_trap_oid_enterprise_specific_zero() {
664 let trap = TrapV1Pdu::new(
666 oid!(1, 3, 6, 1, 4, 1, 1234),
667 [10, 0, 0, 1],
668 GenericTrap::EnterpriseSpecific,
669 0,
670 100,
671 vec![],
672 );
673
674 assert_eq!(trap.v2_trap_oid(), oid!(1, 3, 6, 1, 4, 1, 1234, 0, 0));
676 }
677
678 #[test]
679 fn test_pdu_to_response() {
680 use crate::value::Value;
681 use crate::varbind::VarBind;
682
683 let inform = Pdu {
684 pdu_type: PduType::InformRequest,
685 request_id: 99999,
686 error_status: 0,
687 error_index: 0,
688 varbinds: vec![
689 VarBind::new(oid!(1, 3, 6, 1, 2, 1, 1, 3, 0), Value::TimeTicks(12345)),
690 VarBind::new(
691 oid!(1, 3, 6, 1, 6, 3, 1, 1, 4, 1, 0),
692 Value::ObjectIdentifier(oid!(1, 3, 6, 1, 6, 3, 1, 1, 5, 1)),
693 ),
694 ],
695 };
696
697 let response = inform.to_response();
698
699 assert_eq!(response.pdu_type, PduType::Response);
700 assert_eq!(response.request_id, 99999);
701 assert_eq!(response.error_status, 0);
702 assert_eq!(response.error_index, 0);
703 assert_eq!(response.varbinds.len(), 2);
704 }
705
706 #[test]
707 fn test_pdu_is_confirmed() {
708 let get = Pdu::get_request(1, &[oid!(1, 3, 6, 1)]);
709 assert!(get.is_confirmed());
710
711 let inform = Pdu {
712 pdu_type: PduType::InformRequest,
713 request_id: 1,
714 error_status: 0,
715 error_index: 0,
716 varbinds: vec![],
717 };
718 assert!(inform.is_confirmed());
719
720 let trap = Pdu {
721 pdu_type: PduType::TrapV2,
722 request_id: 1,
723 error_status: 0,
724 error_index: 0,
725 varbinds: vec![],
726 };
727 assert!(!trap.is_confirmed());
728 assert!(trap.is_notification());
729 }
730}