1use rasn::prelude::*;
4use smi::v1::{NetworkAddress, ObjectName, ObjectSyntax, TimeTicks};
5
6#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
7pub struct Message<T> {
8 pub version: Integer,
9 pub community: OctetString,
10 pub data: T,
11}
12
13impl<T> Message<T> {
14 pub const VERSION_1: u64 = 0;
15}
16
17#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
18#[rasn(choice)]
19pub enum Pdus {
20 GetRequest(GetRequest),
21 GetNextRequest(GetNextRequest),
22 GetResponse(GetResponse),
23 SetRequest(SetRequest),
24 Trap(Trap),
25}
26
27#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
28#[rasn(tag(0))]
29#[rasn(delegate)]
30pub struct GetRequest(pub Pdu);
31
32#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
33#[rasn(tag(1))]
34#[rasn(delegate)]
35pub struct GetNextRequest(pub Pdu);
36
37#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
38#[rasn(tag(2))]
39#[rasn(delegate)]
40pub struct GetResponse(pub Pdu);
41
42#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
43#[rasn(tag(3))]
44#[rasn(delegate)]
45pub struct SetRequest(pub Pdu);
46
47pub type VarBindList = alloc::vec::Vec<VarBind>;
48
49#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
50pub struct Pdu {
51 pub request_id: Integer,
52 pub error_status: Integer,
53 pub error_index: Integer,
54 pub variable_bindings: VarBindList,
55}
56
57impl Pdu {
58 pub const ERROR_STATUS_NO_ERROR: u64 = 0;
59 pub const ERROR_STATUS_TOO_BIG: u64 = 1;
60 pub const ERROR_STATUS_NO_SUCH_NAME: u64 = 2;
61 pub const ERROR_STATUS_BAD_VALUE: u64 = 3;
62 pub const ERROR_STATUS_READ_ONLY: u64 = 4;
63 pub const ERROR_STATUS_GEN_ERR: u64 = 5;
64}
65
66#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
67#[rasn(tag(context, 4))]
68pub struct Trap {
69 pub enterprise: ObjectIdentifier,
70 pub agent_addr: NetworkAddress,
71 pub generic_trap: Integer,
72 pub specific_trap: Integer,
73 pub time_stamp: TimeTicks,
74 pub variable_bindings: VarBindList,
75}
76
77#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, PartialOrd, Eq, Ord, Hash)]
78pub struct VarBind {
79 pub name: ObjectName,
80 pub value: ObjectSyntax,
81}
82
83#[cfg(test)]
84mod tests {
85 use super::{Message, Trap, VarBind};
86 use alloc::{string::String, string::ToString, vec, vec::Vec};
87 use pretty_assertions::assert_eq;
88 use rasn::types::ObjectIdentifier;
89 use smi::v1::{Gauge, IpAddress, NetworkAddress, TimeTicks};
90
91 fn string_oid(oid: impl AsRef<[u32]>) -> String {
92 oid.as_ref()
93 .iter()
94 .map(ToString::to_string)
95 .collect::<Vec<_>>()
96 .join(".")
97 }
98
99 #[test]
100 fn trap() {
101 #[rustfmt::skip]
102 let decode_data = [
103 0x30, 0x4f,
105 0x02, 0x01,
107 0x00,
108 0x04, 0x06,
110 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
112 0xa4, 0x42,
114 0x06, 0x0c,
116 0x2b, 0x06, 0x01, 0x04, 0x01, 0xDC, 0x03, 0x01,
118 0x2a, 0x03, 0x07, 0x08,
119 0x40, 0x04,
121 0x0a, 0x0b, 0x0c, 0x0d,
123 0x02, 0x01,
125 0x06,
126 0x02, 0x01,
128 0x02,
129 0x43, 0x02,
131 0x2e, 0x9c,
133 0x30, 0x22,
135 0x30, 0x0d,
137 0x06, 0x07,
139 0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x03,
141 0x43, 0x02,
143 0x2e, 0x9c,
145 0x30, 0x11,
147 0x06, 0x0c,
149 0x2b, 0x06, 0x01, 0x04, 0x01, 0xDC, 0x03, 0x01,
151 0x2a, 0x02, 0x01, 0x07,
152 0x42, 0x01,
154 0x01,
155 ];
156 let decode_msg: Message<Trap> = rasn::ber::decode(&decode_data).unwrap();
157 assert_eq!(decode_msg.version, 0.into());
158 assert_eq!(decode_msg.community, "public".as_bytes());
159 assert_eq!(
160 string_oid(decode_msg.data.enterprise),
161 "1.3.6.1.4.1.11779.1.42.3.7.8"
162 );
163 assert_eq!(
164 decode_msg.data.agent_addr,
165 NetworkAddress::Internet(IpAddress([10, 11, 12, 13].into()))
166 );
167 assert_eq!(decode_msg.data.generic_trap, 6.into());
168 assert_eq!(decode_msg.data.specific_trap, 2.into());
169 assert_eq!(decode_msg.data.time_stamp, TimeTicks(11_932));
170 assert_eq!(decode_msg.data.variable_bindings.len(), 2);
171
172 let encode_msg = Message {
173 version: 0.into(),
174 community: "public".as_bytes().into(),
175 data: Trap {
176 enterprise: ObjectIdentifier::new_unchecked(
177 vec![1, 3, 6, 1, 4, 1, 11779, 1, 42, 3, 7, 8].into(),
178 ),
179 agent_addr: NetworkAddress::Internet(IpAddress([10, 11, 12, 13].into())),
180 generic_trap: 6.into(),
181 specific_trap: 2.into(),
182 time_stamp: TimeTicks(11_932),
183 variable_bindings: vec![
184 VarBind {
185 name: ObjectIdentifier::new_unchecked(vec![1, 3, 6, 1, 2, 1, 1, 3].into()),
186 value: TimeTicks(11_932).into(),
187 },
188 VarBind {
189 name: ObjectIdentifier::new_unchecked(
190 vec![1, 3, 6, 1, 4, 1, 11779, 1, 42, 2, 1, 7].into(),
191 ),
192 value: Gauge(1).into(),
193 },
194 ],
195 },
196 };
197
198 let encode_data = rasn::ber::encode(&encode_msg).unwrap();
202 assert_eq!(encode_data, decode_data);
203
204 let encode_msg_no_bindings = Message {
205 data: Trap {
206 variable_bindings: vec![],
207 ..encode_msg.data
208 },
209 ..encode_msg
210 };
211 assert!(rasn::ber::encode(&encode_msg_no_bindings).is_ok());
212 }
213}