rasn_snmp/
v1.rs

1//! Version 1 (RFC 1157)
2
3use 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            // SEQUENCE -> Message
104            0x30, 0x4f,
105                // INTEGER -> Message::version
106                0x02, 0x01,
107                    0x00,
108                // OCTET STRING -> Message::community
109                0x04, 0x06,
110                    // "public"
111                    0x70, 0x75, 0x62, 0x6c, 0x69, 0x63,
112                // application constructed tag 4 -> Trap
113                0xa4, 0x42,
114                    // OID -> Trap::enterprise
115                    0x06, 0x0c,
116                        // 1.3.6.1.4.1.11779.1.42.3.7.8
117                        0x2b, 0x06, 0x01, 0x04, 0x01, 0xDC, 0x03, 0x01,
118                        0x2a, 0x03, 0x07, 0x08,
119                    // OCTET STRING -> Trap::agent_addr
120                    0x40, 0x04,
121                        // NetworkAddress:Internet(IpAddress(10.11.12.13))
122                        0x0a, 0x0b, 0x0c, 0x0d,
123                    // INTEGER -> Trap::generic_trap
124                    0x02, 0x01,
125                        0x06,
126                    // INTEGER -> Trap::specific_trap
127                    0x02, 0x01,
128                        0x02,
129                    // application tag 3 -> TimeTicks
130                    0x43, 0x02,
131                        // 11_932
132                        0x2e, 0x9c,
133                    // SEQUENCE -> VarBindList
134                    0x30, 0x22,
135                        // SEQUENCE -> VarBind
136                        0x30, 0x0d,
137                            // OID -> VarBind::name
138                            0x06, 0x07,
139                                // 1.3.6.1.2.1.1.3
140                                0x2b, 0x06, 0x01, 0x02, 0x01, 0x01, 0x03,
141                            // application tag 3 -> TimeTicks
142                            0x43, 0x02,
143                                // 11_932
144                                0x2e, 0x9c,
145                        // SEQUENCE -> VarBind
146                        0x30, 0x11,
147                            // OID -> VarBind::name
148                            0x06, 0x0c,
149                                // 1.3.6.1.4.1.11779.1.42.2.1.7
150                                0x2b, 0x06, 0x01, 0x04, 0x01, 0xDC, 0x03, 0x01,
151                                0x2a, 0x02, 0x01, 0x07,
152                            // application tag 2 -> Gauge
153                            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        // TODO: Currently presence of any elements in `variable_bindings` throws a choice error.
199        // Encoding succeeds and is correct with that field empty. There's a smoke-test for that
200        // below for now.
201        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}