BadVPN – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /** |
2 | * @file |
||
3 | * MIB tree access/construction functions. |
||
4 | */ |
||
5 | |||
6 | /* |
||
7 | * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands. |
||
8 | * All rights reserved. |
||
9 | * |
||
10 | * Redistribution and use in source and binary forms, with or without modification, |
||
11 | * are permitted provided that the following conditions are met: |
||
12 | * |
||
13 | * 1. Redistributions of source code must retain the above copyright notice, |
||
14 | * this list of conditions and the following disclaimer. |
||
15 | * 2. Redistributions in binary form must reproduce the above copyright notice, |
||
16 | * this list of conditions and the following disclaimer in the documentation |
||
17 | * and/or other materials provided with the distribution. |
||
18 | * 3. The name of the author may not be used to endorse or promote products |
||
19 | * derived from this software without specific prior written permission. |
||
20 | * |
||
21 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
||
22 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
||
23 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT |
||
24 | * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||
25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT |
||
26 | * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||
29 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
||
30 | * OF SUCH DAMAGE. |
||
31 | * |
||
32 | * Author: Christiaan Simons <christiaan.simons@axon.tv> |
||
33 | * Martin Hentschel <info@cl-soft.de> |
||
34 | */ |
||
35 | |||
36 | /** |
||
37 | * @defgroup snmp SNMPv2c/v3 agent |
||
38 | * @ingroup apps |
||
39 | * SNMPv2c and SNMPv3 compatible agent\n |
||
40 | * There is also a MIB compiler and a MIB viewer in lwIP contrib repository |
||
41 | * (lwip-contrib/apps/LwipMibCompiler).\n |
||
42 | * The agent implements the most important MIB2 MIBs including IPv6 support |
||
43 | * (interfaces, UDP, TCP, SNMP, ICMP, SYSTEM). IP MIB is an older version |
||
44 | * without IPv6 statistics (TODO).\n |
||
45 | * Rewritten by Martin Hentschel <info@cl-soft.de> and |
||
46 | * Dirk Ziegelmeier <dziegel@gmx.de>\n |
||
47 | * |
||
48 | * 0 Agent Capabilities |
||
49 | * ==================== |
||
50 | * |
||
51 | * Features: |
||
52 | * --------- |
||
53 | * - SNMPv2c support. |
||
54 | * - SNMPv3 support (a port to ARM mbedtls is provided, LWIP_SNMP_V3_MBEDTLS option). |
||
55 | * - Low RAM usage - no memory pools, stack only. |
||
56 | * - MIB2 implementation is separated from SNMP stack. |
||
57 | * - Support for multiple MIBs (snmp_set_mibs() call) - e.g. for private MIB. |
||
58 | * - Simple and generic API for MIB implementation. |
||
59 | * - Comfortable node types and helper functions for scalar arrays and tables. |
||
60 | * - Counter64, bit and truthvalue datatype support. |
||
61 | * - Callbacks for SNMP writes e.g. to implement persistency. |
||
62 | * - Runs on two APIs: RAW and netconn. |
||
63 | * - Async API is gone - the stack now supports netconn API instead, |
||
64 | * so blocking operations can be done in MIB calls. |
||
65 | * SNMP runs in a worker thread when netconn API is used. |
||
66 | * - Simplified thread sync support for MIBs - useful when MIBs |
||
67 | * need to access variables shared with other threads where no locking is |
||
68 | * possible. Used in MIB2 to access lwIP stats from lwIP thread. |
||
69 | * |
||
70 | * MIB compiler (code generator): |
||
71 | * ------------------------------ |
||
72 | * - Provided in lwIP contrib repository. |
||
73 | * - Written in C#. MIB viewer used Windows Forms. |
||
74 | * - Developed on Windows with Visual Studio 2010. |
||
75 | * - Can be compiled and used on all platforms with http://www.monodevelop.com/. |
||
76 | * - Based on a heavily modified version of of SharpSnmpLib (a4bd05c6afb4) |
||
77 | * (https://sharpsnmplib.codeplex.com/SourceControl/network/forks/Nemo157/MIBParserUpdate). |
||
78 | * - MIB parser, C file generation framework and LWIP code generation are cleanly |
||
79 | * separated, which means the code may be useful as a base for code generation |
||
80 | * of other SNMP agents. |
||
81 | * |
||
82 | * Notes: |
||
83 | * ------ |
||
84 | * - Stack and MIB compiler were used to implement a Profinet device. |
||
85 | * Compiled/implemented MIBs: LLDP-MIB, LLDP-EXT-DOT3-MIB, LLDP-EXT-PNO-MIB. |
||
86 | * |
||
87 | * SNMPv1 per RFC1157 and SNMPv2c per RFC 3416 |
||
88 | * ------------------------------------------- |
||
89 | * Note the S in SNMP stands for "Simple". Note that "Simple" is |
||
90 | * relative. SNMP is simple compared to the complex ISO network |
||
91 | * management protocols CMIP (Common Management Information Protocol) |
||
92 | * and CMOT (CMip Over Tcp). |
||
93 | * |
||
94 | * SNMPv3 |
||
95 | * ------ |
||
96 | * When SNMPv3 is used, several functions from snmpv3.h must be implemented |
||
97 | * by the user. This is mainly user management and persistence handling. |
||
98 | * The sample provided in lwip-contrib is insecure, don't use it in production |
||
99 | * systems, especially the missing persistence for engine boots variable |
||
100 | * simplifies replay attacks. |
||
101 | * |
||
102 | * MIB II |
||
103 | * ------ |
||
104 | * The standard lwIP stack management information base. |
||
105 | * This is a required MIB, so this is always enabled. |
||
106 | * The groups EGP, CMOT and transmission are disabled by default. |
||
107 | * |
||
108 | * Most mib-2 objects are not writable except: |
||
109 | * sysName, sysLocation, sysContact, snmpEnableAuthenTraps. |
||
110 | * Writing to or changing the ARP and IP address and route |
||
111 | * tables is not possible. |
||
112 | * |
||
113 | * Note lwIP has a very limited notion of IP routing. It currently |
||
114 | * doen't have a route table and doesn't have a notion of the U,G,H flags. |
||
115 | * Instead lwIP uses the interface list with only one default interface |
||
116 | * acting as a single gateway interface (G) for the default route. |
||
117 | * |
||
118 | * The agent returns a "virtual table" with the default route 0.0.0.0 |
||
119 | * for the default interface and network routes (no H) for each |
||
120 | * network interface in the netif_list. |
||
121 | * All routes are considered to be up (U). |
||
122 | * |
||
123 | * Loading additional MIBs |
||
124 | * ----------------------- |
||
125 | * MIBs can only be added in compile-time, not in run-time. |
||
126 | * |
||
127 | * |
||
128 | * 1 Building the Agent |
||
129 | * ==================== |
||
130 | * First of all you'll need to add the following define |
||
131 | * to your local lwipopts.h: |
||
132 | * \#define LWIP_SNMP 1 |
||
133 | * |
||
134 | * and add the source files your makefile. |
||
135 | * |
||
136 | * Note you'll might need to adapt you network driver to update |
||
137 | * the mib2 variables for your interface. |
||
138 | * |
||
139 | * 2 Running the Agent |
||
140 | * =================== |
||
141 | * The following function calls must be made in your program to |
||
142 | * actually get the SNMP agent running. |
||
143 | * |
||
144 | * Before starting the agent you should supply pointers |
||
145 | * for sysContact, sysLocation, and snmpEnableAuthenTraps. |
||
146 | * You can do this by calling |
||
147 | * |
||
148 | * - snmp_mib2_set_syscontact() |
||
149 | * - snmp_mib2_set_syslocation() |
||
150 | * - snmp_set_auth_traps_enabled() |
||
151 | * |
||
152 | * You can register a callback which is called on successful write access: |
||
153 | * snmp_set_write_callback(). |
||
154 | * |
||
155 | * Additionally you may want to set |
||
156 | * |
||
157 | * - snmp_mib2_set_sysdescr() |
||
158 | * - snmp_set_device_enterprise_oid() |
||
159 | * - snmp_mib2_set_sysname() |
||
160 | * |
||
161 | * Also before starting the agent you need to setup |
||
162 | * one or more trap destinations using these calls: |
||
163 | * |
||
164 | * - snmp_trap_dst_enable() |
||
165 | * - snmp_trap_dst_ip_set() |
||
166 | * |
||
167 | * If you need more than MIB2, set the MIBs you want to use |
||
168 | * by snmp_set_mibs(). |
||
169 | * |
||
170 | * Finally, enable the agent by calling snmp_init() |
||
171 | * |
||
172 | * @defgroup snmp_core Core |
||
173 | * @ingroup snmp |
||
174 | * |
||
175 | * @defgroup snmp_traps Traps |
||
176 | * @ingroup snmp |
||
177 | */ |
||
178 | |||
179 | #include "lwip/apps/snmp_opts.h" |
||
180 | |||
181 | #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ |
||
182 | |||
183 | #include "lwip/apps/snmp.h" |
||
184 | #include "lwip/apps/snmp_core.h" |
||
185 | #include "snmp_core_priv.h" |
||
186 | #include "lwip/netif.h" |
||
187 | #include <string.h> |
||
188 | |||
189 | |||
190 | #if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0)) |
||
191 | #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h" |
||
192 | #endif |
||
193 | #if (!LWIP_UDP && LWIP_SNMP) |
||
194 | #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h" |
||
195 | #endif |
||
196 | #if SNMP_MAX_OBJ_ID_LEN > 255 |
||
197 | #error "SNMP_MAX_OBJ_ID_LEN must fit into an u8_t" |
||
198 | #endif |
||
199 | |||
200 | struct snmp_statistics snmp_stats; |
||
201 | static const struct snmp_obj_id snmp_device_enterprise_oid_default = {SNMP_DEVICE_ENTERPRISE_OID_LEN, SNMP_DEVICE_ENTERPRISE_OID}; |
||
202 | static const struct snmp_obj_id *snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default; |
||
203 | |||
204 | const u32_t snmp_zero_dot_zero_values[] = { 0, 0 }; |
||
205 | const struct snmp_obj_id_const_ref snmp_zero_dot_zero = { LWIP_ARRAYSIZE(snmp_zero_dot_zero_values), snmp_zero_dot_zero_values }; |
||
206 | |||
207 | #if SNMP_LWIP_MIB2 && LWIP_SNMP_V3 |
||
208 | #include "lwip/apps/snmp_mib2.h" |
||
209 | #include "lwip/apps/snmp_snmpv2_framework.h" |
||
210 | #include "lwip/apps/snmp_snmpv2_usm.h" |
||
211 | static const struct snmp_mib *const default_mibs[] = { &mib2, &snmpframeworkmib, &snmpusmmib }; |
||
212 | static u8_t snmp_num_mibs = LWIP_ARRAYSIZE(default_mibs); |
||
213 | #elif SNMP_LWIP_MIB2 |
||
214 | #include "lwip/apps/snmp_mib2.h" |
||
215 | static const struct snmp_mib *const default_mibs[] = { &mib2 }; |
||
216 | static u8_t snmp_num_mibs = LWIP_ARRAYSIZE(default_mibs); |
||
217 | #else |
||
218 | static const struct snmp_mib *const default_mibs[] = { NULL }; |
||
219 | static u8_t snmp_num_mibs = 0; |
||
220 | #endif |
||
221 | |||
222 | /* List of known mibs */ |
||
223 | static struct snmp_mib const *const *snmp_mibs = default_mibs; |
||
224 | |||
225 | /** |
||
226 | * @ingroup snmp_core |
||
227 | * Sets the MIBs to use. |
||
228 | * Example: call snmp_set_mibs() as follows: |
||
229 | * static const struct snmp_mib *my_snmp_mibs[] = { |
||
230 | * &mib2, |
||
231 | * &private_mib |
||
232 | * }; |
||
233 | * snmp_set_mibs(my_snmp_mibs, LWIP_ARRAYSIZE(my_snmp_mibs)); |
||
234 | */ |
||
235 | void |
||
236 | snmp_set_mibs(const struct snmp_mib **mibs, u8_t num_mibs) |
||
237 | { |
||
238 | LWIP_ASSERT("mibs pointer must be != NULL", (mibs != NULL)); |
||
239 | LWIP_ASSERT("num_mibs pointer must be != 0", (num_mibs != 0)); |
||
240 | snmp_mibs = mibs; |
||
241 | snmp_num_mibs = num_mibs; |
||
242 | } |
||
243 | |||
244 | /** |
||
245 | * @ingroup snmp_core |
||
246 | * 'device enterprise oid' is used for 'device OID' field in trap PDU's (for identification of generating device) |
||
247 | * as well as for value returned by MIB-2 'sysObjectID' field (if internal MIB2 implementation is used). |
||
248 | * The 'device enterprise oid' shall point to an OID located under 'private-enterprises' branch (1.3.6.1.4.1.XXX). If a vendor |
||
249 | * wants to provide a custom object there, he has to get its own enterprise oid from IANA (http://www.iana.org). It |
||
250 | * is not allowed to use LWIP enterprise ID! |
||
251 | * In order to identify a specific device it is recommended to create a dedicated OID for each device type under its own |
||
252 | * enterprise oid. |
||
253 | * e.g. |
||
254 | * device a > 1.3.6.1.4.1.XXX(ent-oid).1(devices).1(device a) |
||
255 | * device b > 1.3.6.1.4.1.XXX(ent-oid).1(devices).2(device b) |
||
256 | * for more details see description of 'sysObjectID' field in RFC1213-MIB |
||
257 | */ |
||
258 | void snmp_set_device_enterprise_oid(const struct snmp_obj_id *device_enterprise_oid) |
||
259 | { |
||
260 | if (device_enterprise_oid == NULL) { |
||
261 | snmp_device_enterprise_oid = &snmp_device_enterprise_oid_default; |
||
262 | } else { |
||
263 | snmp_device_enterprise_oid = device_enterprise_oid; |
||
264 | } |
||
265 | } |
||
266 | |||
267 | /** |
||
268 | * @ingroup snmp_core |
||
269 | * Get 'device enterprise oid' |
||
270 | */ |
||
271 | const struct snmp_obj_id *snmp_get_device_enterprise_oid(void) |
||
272 | { |
||
273 | return snmp_device_enterprise_oid; |
||
274 | } |
||
275 | |||
276 | #if LWIP_IPV4 |
||
277 | /** |
||
278 | * Conversion from InetAddressIPv4 oid to lwIP ip4_addr |
||
279 | * @param oid points to u32_t ident[4] input |
||
280 | * @param ip points to output struct |
||
281 | */ |
||
282 | u8_t |
||
283 | snmp_oid_to_ip4(const u32_t *oid, ip4_addr_t *ip) |
||
284 | { |
||
285 | if ((oid[0] > 0xFF) || |
||
286 | (oid[1] > 0xFF) || |
||
287 | (oid[2] > 0xFF) || |
||
288 | (oid[3] > 0xFF)) { |
||
289 | ip4_addr_copy(*ip, *IP4_ADDR_ANY4); |
||
290 | return 0; |
||
291 | } |
||
292 | |||
293 | IP4_ADDR(ip, oid[0], oid[1], oid[2], oid[3]); |
||
294 | return 1; |
||
295 | } |
||
296 | |||
297 | /** |
||
298 | * Convert ip4_addr to InetAddressIPv4 (no InetAddressType) |
||
299 | * @param ip points to input struct |
||
300 | * @param oid points to u32_t ident[4] output |
||
301 | */ |
||
302 | void |
||
303 | snmp_ip4_to_oid(const ip4_addr_t *ip, u32_t *oid) |
||
304 | { |
||
305 | oid[0] = ip4_addr1(ip); |
||
306 | oid[1] = ip4_addr2(ip); |
||
307 | oid[2] = ip4_addr3(ip); |
||
308 | oid[3] = ip4_addr4(ip); |
||
309 | } |
||
310 | #endif /* LWIP_IPV4 */ |
||
311 | |||
312 | #if LWIP_IPV6 |
||
313 | /** |
||
314 | * Conversion from InetAddressIPv6 oid to lwIP ip6_addr |
||
315 | * @param oid points to u32_t oid[16] input |
||
316 | * @param ip points to output struct |
||
317 | */ |
||
318 | u8_t |
||
319 | snmp_oid_to_ip6(const u32_t *oid, ip6_addr_t *ip) |
||
320 | { |
||
321 | if ((oid[0] > 0xFF) || |
||
322 | (oid[1] > 0xFF) || |
||
323 | (oid[2] > 0xFF) || |
||
324 | (oid[3] > 0xFF) || |
||
325 | (oid[4] > 0xFF) || |
||
326 | (oid[5] > 0xFF) || |
||
327 | (oid[6] > 0xFF) || |
||
328 | (oid[7] > 0xFF) || |
||
329 | (oid[8] > 0xFF) || |
||
330 | (oid[9] > 0xFF) || |
||
331 | (oid[10] > 0xFF) || |
||
332 | (oid[11] > 0xFF) || |
||
333 | (oid[12] > 0xFF) || |
||
334 | (oid[13] > 0xFF) || |
||
335 | (oid[14] > 0xFF) || |
||
336 | (oid[15] > 0xFF)) { |
||
337 | ip6_addr_set_any(ip); |
||
338 | return 0; |
||
339 | } |
||
340 | |||
341 | ip->addr[0] = (oid[0] << 24) | (oid[1] << 16) | (oid[2] << 8) | (oid[3] << 0); |
||
342 | ip->addr[1] = (oid[4] << 24) | (oid[5] << 16) | (oid[6] << 8) | (oid[7] << 0); |
||
343 | ip->addr[2] = (oid[8] << 24) | (oid[9] << 16) | (oid[10] << 8) | (oid[11] << 0); |
||
344 | ip->addr[3] = (oid[12] << 24) | (oid[13] << 16) | (oid[14] << 8) | (oid[15] << 0); |
||
345 | return 1; |
||
346 | } |
||
347 | |||
348 | /** |
||
349 | * Convert ip6_addr to InetAddressIPv6 (no InetAddressType) |
||
350 | * @param ip points to input struct |
||
351 | * @param oid points to u32_t ident[16] output |
||
352 | */ |
||
353 | void |
||
354 | snmp_ip6_to_oid(const ip6_addr_t *ip, u32_t *oid) |
||
355 | { |
||
356 | oid[0] = (ip->addr[0] & 0xFF000000) >> 24; |
||
357 | oid[1] = (ip->addr[0] & 0x00FF0000) >> 16; |
||
358 | oid[2] = (ip->addr[0] & 0x0000FF00) >> 8; |
||
359 | oid[3] = (ip->addr[0] & 0x000000FF) >> 0; |
||
360 | oid[4] = (ip->addr[1] & 0xFF000000) >> 24; |
||
361 | oid[5] = (ip->addr[1] & 0x00FF0000) >> 16; |
||
362 | oid[6] = (ip->addr[1] & 0x0000FF00) >> 8; |
||
363 | oid[7] = (ip->addr[1] & 0x000000FF) >> 0; |
||
364 | oid[8] = (ip->addr[2] & 0xFF000000) >> 24; |
||
365 | oid[9] = (ip->addr[2] & 0x00FF0000) >> 16; |
||
366 | oid[10] = (ip->addr[2] & 0x0000FF00) >> 8; |
||
367 | oid[11] = (ip->addr[2] & 0x000000FF) >> 0; |
||
368 | oid[12] = (ip->addr[3] & 0xFF000000) >> 24; |
||
369 | oid[13] = (ip->addr[3] & 0x00FF0000) >> 16; |
||
370 | oid[14] = (ip->addr[3] & 0x0000FF00) >> 8; |
||
371 | oid[15] = (ip->addr[3] & 0x000000FF) >> 0; |
||
372 | } |
||
373 | #endif /* LWIP_IPV6 */ |
||
374 | |||
375 | #if LWIP_IPV4 || LWIP_IPV6 |
||
376 | /** |
||
377 | * Convert to InetAddressType+InetAddress+InetPortNumber |
||
378 | * @param ip IP address |
||
379 | * @param port Port |
||
380 | * @param oid OID |
||
381 | * @return OID length |
||
382 | */ |
||
383 | u8_t |
||
384 | snmp_ip_port_to_oid(const ip_addr_t *ip, u16_t port, u32_t *oid) |
||
385 | { |
||
386 | u8_t idx; |
||
387 | |||
388 | idx = snmp_ip_to_oid(ip, oid); |
||
389 | oid[idx] = port; |
||
390 | idx++; |
||
391 | |||
392 | return idx; |
||
393 | } |
||
394 | |||
395 | /** |
||
396 | * Convert to InetAddressType+InetAddress |
||
397 | * @param ip IP address |
||
398 | * @param oid OID |
||
399 | * @return OID length |
||
400 | */ |
||
401 | u8_t |
||
402 | snmp_ip_to_oid(const ip_addr_t *ip, u32_t *oid) |
||
403 | { |
||
404 | if (IP_IS_ANY_TYPE_VAL(*ip)) { |
||
405 | oid[0] = 0; /* any */ |
||
406 | oid[1] = 0; /* no IP OIDs follow */ |
||
407 | return 2; |
||
408 | } else if (IP_IS_V6(ip)) { |
||
409 | #if LWIP_IPV6 |
||
410 | oid[0] = 2; /* ipv6 */ |
||
411 | oid[1] = 16; /* 16 InetAddressIPv6 OIDs follow */ |
||
412 | snmp_ip6_to_oid(ip_2_ip6(ip), &oid[2]); |
||
413 | return 18; |
||
414 | #else /* LWIP_IPV6 */ |
||
415 | return 0; |
||
416 | #endif /* LWIP_IPV6 */ |
||
417 | } else { |
||
418 | #if LWIP_IPV4 |
||
419 | oid[0] = 1; /* ipv4 */ |
||
420 | oid[1] = 4; /* 4 InetAddressIPv4 OIDs follow */ |
||
421 | snmp_ip4_to_oid(ip_2_ip4(ip), &oid[2]); |
||
422 | return 6; |
||
423 | #else /* LWIP_IPV4 */ |
||
424 | return 0; |
||
425 | #endif /* LWIP_IPV4 */ |
||
426 | } |
||
427 | } |
||
428 | |||
429 | /** |
||
430 | * Convert from InetAddressType+InetAddress to ip_addr_t |
||
431 | * @param oid OID |
||
432 | * @param oid_len OID length |
||
433 | * @param ip IP address |
||
434 | * @return Parsed OID length |
||
435 | */ |
||
436 | u8_t |
||
437 | snmp_oid_to_ip(const u32_t *oid, u8_t oid_len, ip_addr_t *ip) |
||
438 | { |
||
439 | /* InetAddressType */ |
||
440 | if (oid_len < 1) { |
||
441 | return 0; |
||
442 | } |
||
443 | |||
444 | if (oid[0] == 0) { /* any */ |
||
445 | /* 1x InetAddressType, 1x OID len */ |
||
446 | if (oid_len < 2) { |
||
447 | return 0; |
||
448 | } |
||
449 | if (oid[1] != 0) { |
||
450 | return 0; |
||
451 | } |
||
452 | |||
453 | memset(ip, 0, sizeof(*ip)); |
||
454 | IP_SET_TYPE(ip, IPADDR_TYPE_ANY); |
||
455 | |||
456 | return 2; |
||
457 | } else if (oid[0] == 1) { /* ipv4 */ |
||
458 | #if LWIP_IPV4 |
||
459 | /* 1x InetAddressType, 1x OID len, 4x InetAddressIPv4 */ |
||
460 | if (oid_len < 6) { |
||
461 | return 0; |
||
462 | } |
||
463 | |||
464 | /* 4x ipv4 OID */ |
||
465 | if (oid[1] != 4) { |
||
466 | return 0; |
||
467 | } |
||
468 | |||
469 | IP_SET_TYPE(ip, IPADDR_TYPE_V4); |
||
470 | if (!snmp_oid_to_ip4(&oid[2], ip_2_ip4(ip))) { |
||
471 | return 0; |
||
472 | } |
||
473 | |||
474 | return 6; |
||
475 | #else /* LWIP_IPV4 */ |
||
476 | return 0; |
||
477 | #endif /* LWIP_IPV4 */ |
||
478 | } else if (oid[0] == 2) { /* ipv6 */ |
||
479 | #if LWIP_IPV6 |
||
480 | /* 1x InetAddressType, 1x OID len, 16x InetAddressIPv6 */ |
||
481 | if (oid_len < 18) { |
||
482 | return 0; |
||
483 | } |
||
484 | |||
485 | /* 16x ipv6 OID */ |
||
486 | if (oid[1] != 16) { |
||
487 | return 0; |
||
488 | } |
||
489 | |||
490 | IP_SET_TYPE(ip, IPADDR_TYPE_V6); |
||
491 | if (!snmp_oid_to_ip6(&oid[2], ip_2_ip6(ip))) { |
||
492 | return 0; |
||
493 | } |
||
494 | |||
495 | return 18; |
||
496 | #else /* LWIP_IPV6 */ |
||
497 | return 0; |
||
498 | #endif /* LWIP_IPV6 */ |
||
499 | } else { /* unsupported InetAddressType */ |
||
500 | return 0; |
||
501 | } |
||
502 | } |
||
503 | |||
504 | /** |
||
505 | * Convert from InetAddressType+InetAddress+InetPortNumber to ip_addr_t and u16_t |
||
506 | * @param oid OID |
||
507 | * @param oid_len OID length |
||
508 | * @param ip IP address |
||
509 | * @param port Port |
||
510 | * @return Parsed OID length |
||
511 | */ |
||
512 | u8_t |
||
513 | snmp_oid_to_ip_port(const u32_t *oid, u8_t oid_len, ip_addr_t *ip, u16_t *port) |
||
514 | { |
||
515 | u8_t idx; |
||
516 | |||
517 | /* InetAddressType + InetAddress */ |
||
518 | idx = snmp_oid_to_ip(&oid[0], oid_len, ip); |
||
519 | if (idx == 0) { |
||
520 | return 0; |
||
521 | } |
||
522 | |||
523 | /* InetPortNumber */ |
||
524 | if (oid_len < (idx + 1)) { |
||
525 | return 0; |
||
526 | } |
||
527 | if (oid[idx] > 0xffff) { |
||
528 | return 0; |
||
529 | } |
||
530 | *port = (u16_t)oid[idx]; |
||
531 | idx++; |
||
532 | |||
533 | return idx; |
||
534 | } |
||
535 | |||
536 | #endif /* LWIP_IPV4 || LWIP_IPV6 */ |
||
537 | |||
538 | /** |
||
539 | * Assign an OID to struct snmp_obj_id |
||
540 | * @param target Assignment target |
||
541 | * @param oid OID |
||
542 | * @param oid_len OID length |
||
543 | */ |
||
544 | void |
||
545 | snmp_oid_assign(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len) |
||
546 | { |
||
547 | LWIP_ASSERT("oid_len <= LWIP_SNMP_OBJ_ID_LEN", oid_len <= SNMP_MAX_OBJ_ID_LEN); |
||
548 | |||
549 | target->len = oid_len; |
||
550 | |||
551 | if (oid_len > 0) { |
||
552 | MEMCPY(target->id, oid, oid_len * sizeof(u32_t)); |
||
553 | } |
||
554 | } |
||
555 | |||
556 | /** |
||
557 | * Prefix an OID to OID in struct snmp_obj_id |
||
558 | * @param target Assignment target to prefix |
||
559 | * @param oid OID |
||
560 | * @param oid_len OID length |
||
561 | */ |
||
562 | void |
||
563 | snmp_oid_prefix(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len) |
||
564 | { |
||
565 | LWIP_ASSERT("target->len + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN); |
||
566 | |||
567 | if (oid_len > 0) { |
||
568 | /* move existing OID to make room at the beginning for OID to insert */ |
||
569 | int i; |
||
570 | for (i = target->len - 1; i >= 0; i--) { |
||
571 | target->id[i + oid_len] = target->id[i]; |
||
572 | } |
||
573 | |||
574 | /* paste oid at the beginning */ |
||
575 | MEMCPY(target->id, oid, oid_len * sizeof(u32_t)); |
||
576 | } |
||
577 | } |
||
578 | |||
579 | /** |
||
580 | * Combine two OIDs into struct snmp_obj_id |
||
581 | * @param target Assignmet target |
||
582 | * @param oid1 OID 1 |
||
583 | * @param oid1_len OID 1 length |
||
584 | * @param oid2 OID 2 |
||
585 | * @param oid2_len OID 2 length |
||
586 | */ |
||
587 | void |
||
588 | snmp_oid_combine(struct snmp_obj_id *target, const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len) |
||
589 | { |
||
590 | snmp_oid_assign(target, oid1, oid1_len); |
||
591 | snmp_oid_append(target, oid2, oid2_len); |
||
592 | } |
||
593 | |||
594 | /** |
||
595 | * Append OIDs to struct snmp_obj_id |
||
596 | * @param target Assignment target to append to |
||
597 | * @param oid OID |
||
598 | * @param oid_len OID length |
||
599 | */ |
||
600 | void |
||
601 | snmp_oid_append(struct snmp_obj_id *target, const u32_t *oid, u8_t oid_len) |
||
602 | { |
||
603 | LWIP_ASSERT("offset + oid_len <= LWIP_SNMP_OBJ_ID_LEN", (target->len + oid_len) <= SNMP_MAX_OBJ_ID_LEN); |
||
604 | |||
605 | if (oid_len > 0) { |
||
606 | MEMCPY(&target->id[target->len], oid, oid_len * sizeof(u32_t)); |
||
607 | target->len = (u8_t)(target->len + oid_len); |
||
608 | } |
||
609 | } |
||
610 | |||
611 | /** |
||
612 | * Compare two OIDs |
||
613 | * @param oid1 OID 1 |
||
614 | * @param oid1_len OID 1 length |
||
615 | * @param oid2 OID 2 |
||
616 | * @param oid2_len OID 2 length |
||
617 | * @return -1: OID1<OID2 1: OID1 >OID2 0: equal |
||
618 | */ |
||
619 | s8_t |
||
620 | snmp_oid_compare(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len) |
||
621 | { |
||
622 | u8_t level = 0; |
||
623 | LWIP_ASSERT("'oid1' param must not be NULL or 'oid1_len' param be 0!", (oid1 != NULL) || (oid1_len == 0)); |
||
624 | LWIP_ASSERT("'oid2' param must not be NULL or 'oid2_len' param be 0!", (oid2 != NULL) || (oid2_len == 0)); |
||
625 | |||
626 | while ((level < oid1_len) && (level < oid2_len)) { |
||
627 | if (*oid1 < *oid2) { |
||
628 | return -1; |
||
629 | } |
||
630 | if (*oid1 > *oid2) { |
||
631 | return 1; |
||
632 | } |
||
633 | |||
634 | level++; |
||
635 | oid1++; |
||
636 | oid2++; |
||
637 | } |
||
638 | |||
639 | /* common part of both OID's is equal, compare length */ |
||
640 | if (oid1_len < oid2_len) { |
||
641 | return -1; |
||
642 | } |
||
643 | if (oid1_len > oid2_len) { |
||
644 | return 1; |
||
645 | } |
||
646 | |||
647 | /* they are equal */ |
||
648 | return 0; |
||
649 | } |
||
650 | |||
651 | |||
652 | /** |
||
653 | * Check of two OIDs are equal |
||
654 | * @param oid1 OID 1 |
||
655 | * @param oid1_len OID 1 length |
||
656 | * @param oid2 OID 2 |
||
657 | * @param oid2_len OID 2 length |
||
658 | * @return 1: equal 0: non-equal |
||
659 | */ |
||
660 | u8_t |
||
661 | snmp_oid_equal(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len) |
||
662 | { |
||
663 | return (snmp_oid_compare(oid1, oid1_len, oid2, oid2_len) == 0) ? 1 : 0; |
||
664 | } |
||
665 | |||
666 | /** |
||
667 | * Convert netif to interface index |
||
668 | * @param netif netif |
||
669 | * @return index |
||
670 | */ |
||
671 | u8_t |
||
672 | netif_to_num(const struct netif *netif) |
||
673 | { |
||
674 | return netif_get_index(netif); |
||
675 | } |
||
676 | |||
677 | static const struct snmp_mib * |
||
678 | snmp_get_mib_from_oid(const u32_t *oid, u8_t oid_len) |
||
679 | { |
||
680 | const u32_t *list_oid; |
||
681 | const u32_t *searched_oid; |
||
682 | u8_t i, l; |
||
683 | |||
684 | u8_t max_match_len = 0; |
||
685 | const struct snmp_mib *matched_mib = NULL; |
||
686 | |||
687 | LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL)); |
||
688 | |||
689 | if (oid_len == 0) { |
||
690 | return NULL; |
||
691 | } |
||
692 | |||
693 | for (i = 0; i < snmp_num_mibs; i++) { |
||
694 | LWIP_ASSERT("MIB array not initialized correctly", (snmp_mibs[i] != NULL)); |
||
695 | LWIP_ASSERT("MIB array not initialized correctly - base OID is NULL", (snmp_mibs[i]->base_oid != NULL)); |
||
696 | |||
697 | if (oid_len >= snmp_mibs[i]->base_oid_len) { |
||
698 | l = snmp_mibs[i]->base_oid_len; |
||
699 | list_oid = snmp_mibs[i]->base_oid; |
||
700 | searched_oid = oid; |
||
701 | |||
702 | while (l > 0) { |
||
703 | if (*list_oid != *searched_oid) { |
||
704 | break; |
||
705 | } |
||
706 | |||
707 | l--; |
||
708 | list_oid++; |
||
709 | searched_oid++; |
||
710 | } |
||
711 | |||
712 | if ((l == 0) && (snmp_mibs[i]->base_oid_len > max_match_len)) { |
||
713 | max_match_len = snmp_mibs[i]->base_oid_len; |
||
714 | matched_mib = snmp_mibs[i]; |
||
715 | } |
||
716 | } |
||
717 | } |
||
718 | |||
719 | return matched_mib; |
||
720 | } |
||
721 | |||
722 | static const struct snmp_mib * |
||
723 | snmp_get_next_mib(const u32_t *oid, u8_t oid_len) |
||
724 | { |
||
725 | u8_t i; |
||
726 | const struct snmp_mib *next_mib = NULL; |
||
727 | |||
728 | LWIP_ASSERT("'oid' param must not be NULL!", (oid != NULL)); |
||
729 | |||
730 | if (oid_len == 0) { |
||
731 | return NULL; |
||
732 | } |
||
733 | |||
734 | for (i = 0; i < snmp_num_mibs; i++) { |
||
735 | if (snmp_mibs[i]->base_oid != NULL) { |
||
736 | /* check if mib is located behind starting point */ |
||
737 | if (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, oid, oid_len) > 0) { |
||
738 | if ((next_mib == NULL) || |
||
739 | (snmp_oid_compare(snmp_mibs[i]->base_oid, snmp_mibs[i]->base_oid_len, |
||
740 | next_mib->base_oid, next_mib->base_oid_len) < 0)) { |
||
741 | next_mib = snmp_mibs[i]; |
||
742 | } |
||
743 | } |
||
744 | } |
||
745 | } |
||
746 | |||
747 | return next_mib; |
||
748 | } |
||
749 | |||
750 | static const struct snmp_mib * |
||
751 | snmp_get_mib_between(const u32_t *oid1, u8_t oid1_len, const u32_t *oid2, u8_t oid2_len) |
||
752 | { |
||
753 | const struct snmp_mib *next_mib = snmp_get_next_mib(oid1, oid1_len); |
||
754 | |||
755 | LWIP_ASSERT("'oid2' param must not be NULL!", (oid2 != NULL)); |
||
756 | LWIP_ASSERT("'oid2_len' param must be greater than 0!", (oid2_len > 0)); |
||
757 | |||
758 | if (next_mib != NULL) { |
||
759 | if (snmp_oid_compare(next_mib->base_oid, next_mib->base_oid_len, oid2, oid2_len) < 0) { |
||
760 | return next_mib; |
||
761 | } |
||
762 | } |
||
763 | |||
764 | return NULL; |
||
765 | } |
||
766 | |||
767 | u8_t |
||
768 | snmp_get_node_instance_from_oid(const u32_t *oid, u8_t oid_len, struct snmp_node_instance *node_instance) |
||
769 | { |
||
770 | u8_t result = SNMP_ERR_NOSUCHOBJECT; |
||
771 | const struct snmp_mib *mib; |
||
772 | const struct snmp_node *mn = NULL; |
||
773 | |||
774 | mib = snmp_get_mib_from_oid(oid, oid_len); |
||
775 | if (mib != NULL) { |
||
776 | u8_t oid_instance_len; |
||
777 | |||
778 | mn = snmp_mib_tree_resolve_exact(mib, oid, oid_len, &oid_instance_len); |
||
779 | if ((mn != NULL) && (mn->node_type != SNMP_NODE_TREE)) { |
||
780 | /* get instance */ |
||
781 | const struct snmp_leaf_node *leaf_node = (const struct snmp_leaf_node *)(const void *)mn; |
||
782 | |||
783 | node_instance->node = mn; |
||
784 | snmp_oid_assign(&node_instance->instance_oid, oid + (oid_len - oid_instance_len), oid_instance_len); |
||
785 | |||
786 | result = leaf_node->get_instance( |
||
787 | oid, |
||
788 | oid_len - oid_instance_len, |
||
789 | node_instance); |
||
790 | |||
791 | #ifdef LWIP_DEBUG |
||
792 | if (result == SNMP_ERR_NOERROR) { |
||
793 | if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) { |
||
794 | LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n")); |
||
795 | } |
||
796 | if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) { |
||
797 | LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value and/or set_test function is specified\n")); |
||
798 | } |
||
799 | } |
||
800 | #endif |
||
801 | } |
||
802 | } |
||
803 | |||
804 | return result; |
||
805 | } |
||
806 | |||
807 | u8_t |
||
808 | snmp_get_next_node_instance_from_oid(const u32_t *oid, u8_t oid_len, snmp_validate_node_instance_method validate_node_instance_method, void *validate_node_instance_arg, struct snmp_obj_id *node_oid, struct snmp_node_instance *node_instance) |
||
809 | { |
||
810 | const struct snmp_mib *mib; |
||
811 | const struct snmp_node *mn = NULL; |
||
812 | const u32_t *start_oid = NULL; |
||
813 | u8_t start_oid_len = 0; |
||
814 | |||
815 | /* resolve target MIB from passed OID */ |
||
816 | mib = snmp_get_mib_from_oid(oid, oid_len); |
||
817 | if (mib == NULL) { |
||
818 | /* passed OID does not reference any known MIB, start at the next closest MIB */ |
||
819 | mib = snmp_get_next_mib(oid, oid_len); |
||
820 | |||
821 | if (mib != NULL) { |
||
822 | start_oid = mib->base_oid; |
||
823 | start_oid_len = mib->base_oid_len; |
||
824 | } |
||
825 | } else { |
||
826 | start_oid = oid; |
||
827 | start_oid_len = oid_len; |
||
828 | } |
||
829 | |||
830 | /* resolve target node from MIB, skip to next MIB if no suitable node is found in current MIB */ |
||
831 | while ((mib != NULL) && (mn == NULL)) { |
||
832 | u8_t oid_instance_len; |
||
833 | |||
834 | /* check if OID directly references a node inside current MIB, in this case we have to ask this node for the next instance */ |
||
835 | mn = snmp_mib_tree_resolve_exact(mib, start_oid, start_oid_len, &oid_instance_len); |
||
836 | if (mn != NULL) { |
||
837 | snmp_oid_assign(node_oid, start_oid, start_oid_len - oid_instance_len); /* set oid to node */ |
||
838 | snmp_oid_assign(&node_instance->instance_oid, start_oid + (start_oid_len - oid_instance_len), oid_instance_len); /* set (relative) instance oid */ |
||
839 | } else { |
||
840 | /* OID does not reference a node, search for the next closest node inside MIB; set instance_oid.len to zero because we want the first instance of this node */ |
||
841 | mn = snmp_mib_tree_resolve_next(mib, start_oid, start_oid_len, node_oid); |
||
842 | node_instance->instance_oid.len = 0; |
||
843 | } |
||
844 | |||
845 | /* validate the node; if the node has no further instance or the returned instance is invalid, search for the next in MIB and validate again */ |
||
846 | node_instance->node = mn; |
||
847 | while (mn != NULL) { |
||
848 | u8_t result; |
||
849 | |||
850 | /* clear fields which may have values from previous loops */ |
||
851 | node_instance->asn1_type = 0; |
||
852 | node_instance->access = SNMP_NODE_INSTANCE_NOT_ACCESSIBLE; |
||
853 | node_instance->get_value = NULL; |
||
854 | node_instance->set_test = NULL; |
||
855 | node_instance->set_value = NULL; |
||
856 | node_instance->release_instance = NULL; |
||
857 | node_instance->reference.ptr = NULL; |
||
858 | node_instance->reference_len = 0; |
||
859 | |||
860 | result = ((const struct snmp_leaf_node *)(const void *)mn)->get_next_instance( |
||
861 | node_oid->id, |
||
862 | node_oid->len, |
||
863 | node_instance); |
||
864 | |||
865 | if (result == SNMP_ERR_NOERROR) { |
||
866 | #ifdef LWIP_DEBUG |
||
867 | if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_READ) != 0) && (node_instance->get_value == NULL)) { |
||
868 | LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is readable but no get_value function is specified\n")); |
||
869 | } |
||
870 | if (((node_instance->access & SNMP_NODE_INSTANCE_ACCESS_WRITE) != 0) && (node_instance->set_value == NULL)) { |
||
871 | LWIP_DEBUGF(SNMP_DEBUG, ("SNMP inconsistent access: node is writable but no set_value function is specified\n")); |
||
872 | } |
||
873 | #endif |
||
874 | |||
875 | /* validate node because the node may be not accessible for example (but let the caller decide what is valid */ |
||
876 | if ((validate_node_instance_method == NULL) || |
||
877 | (validate_node_instance_method(node_instance, validate_node_instance_arg) == SNMP_ERR_NOERROR)) { |
||
878 | /* node_oid "returns" the full result OID (including the instance part) */ |
||
879 | snmp_oid_append(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len); |
||
880 | break; |
||
881 | } |
||
882 | |||
883 | if (node_instance->release_instance != NULL) { |
||
884 | node_instance->release_instance(node_instance); |
||
885 | } |
||
886 | /* |
||
887 | the instance itself is not valid, ask for next instance from same node. |
||
888 | we don't have to change any variables because node_instance->instance_oid is used as input (starting point) |
||
889 | as well as output (resulting next OID), so we have to simply call get_next_instance method again |
||
890 | */ |
||
891 | } else { |
||
892 | if (node_instance->release_instance != NULL) { |
||
893 | node_instance->release_instance(node_instance); |
||
894 | } |
||
895 | |||
896 | /* the node has no further instance, skip to next node */ |
||
897 | mn = snmp_mib_tree_resolve_next(mib, node_oid->id, node_oid->len, &node_instance->instance_oid); /* misuse node_instance->instance_oid as tmp buffer */ |
||
898 | if (mn != NULL) { |
||
899 | /* prepare for next loop */ |
||
900 | snmp_oid_assign(node_oid, node_instance->instance_oid.id, node_instance->instance_oid.len); |
||
901 | node_instance->instance_oid.len = 0; |
||
902 | node_instance->node = mn; |
||
903 | } |
||
904 | } |
||
905 | } |
||
906 | |||
907 | if (mn != NULL) { |
||
908 | /* |
||
909 | we found a suitable next node, |
||
910 | now we have to check if a inner MIB is located between the searched OID and the resulting OID. |
||
911 | this is possible because MIB's may be located anywhere in the global tree, that means also in |
||
912 | the subtree of another MIB (e.g. if searched OID is .2 and resulting OID is .4, then another |
||
913 | MIB having .3 as root node may exist) |
||
914 | */ |
||
915 | const struct snmp_mib *intermediate_mib; |
||
916 | intermediate_mib = snmp_get_mib_between(start_oid, start_oid_len, node_oid->id, node_oid->len); |
||
917 | |||
918 | if (intermediate_mib != NULL) { |
||
919 | /* search for first node inside intermediate mib in next loop */ |
||
920 | if (node_instance->release_instance != NULL) { |
||
921 | node_instance->release_instance(node_instance); |
||
922 | } |
||
923 | |||
924 | mn = NULL; |
||
925 | mib = intermediate_mib; |
||
926 | start_oid = mib->base_oid; |
||
927 | start_oid_len = mib->base_oid_len; |
||
928 | } |
||
929 | /* else { we found out target node } */ |
||
930 | } else { |
||
931 | /* |
||
932 | there is no further (suitable) node inside this MIB, search for the next MIB with following priority |
||
933 | 1. search for inner MIB's (whose root is located inside tree of current MIB) |
||
934 | 2. search for surrouding MIB's (where the current MIB is the inner MIB) and continue there if any |
||
935 | 3. take the next closest MIB (not being related to the current MIB) |
||
936 | */ |
||
937 | const struct snmp_mib *next_mib; |
||
938 | next_mib = snmp_get_next_mib(start_oid, start_oid_len); /* returns MIB's related to point 1 and 3 */ |
||
939 | |||
940 | /* is the found MIB an inner MIB? (point 1) */ |
||
941 | if ((next_mib != NULL) && (next_mib->base_oid_len > mib->base_oid_len) && |
||
942 | (snmp_oid_compare(next_mib->base_oid, mib->base_oid_len, mib->base_oid, mib->base_oid_len) == 0)) { |
||
943 | /* yes it is -> continue at inner MIB */ |
||
944 | mib = next_mib; |
||
945 | start_oid = mib->base_oid; |
||
946 | start_oid_len = mib->base_oid_len; |
||
947 | } else { |
||
948 | /* check if there is a surrounding mib where to continue (point 2) (only possible if OID length > 1) */ |
||
949 | if (mib->base_oid_len > 1) { |
||
950 | mib = snmp_get_mib_from_oid(mib->base_oid, mib->base_oid_len - 1); |
||
951 | |||
952 | if (mib == NULL) { |
||
953 | /* no surrounding mib, use next mib encountered above (point 3) */ |
||
954 | mib = next_mib; |
||
955 | |||
956 | if (mib != NULL) { |
||
957 | start_oid = mib->base_oid; |
||
958 | start_oid_len = mib->base_oid_len; |
||
959 | } |
||
960 | } |
||
961 | /* else { start_oid stays the same because we want to continue from current offset in surrounding mib (point 2) } */ |
||
962 | } |
||
963 | } |
||
964 | } |
||
965 | } |
||
966 | |||
967 | if (mib == NULL) { |
||
968 | /* loop is only left when mib == null (error) or mib_node != NULL (success) */ |
||
969 | return SNMP_ERR_ENDOFMIBVIEW; |
||
970 | } |
||
971 | |||
972 | return SNMP_ERR_NOERROR; |
||
973 | } |
||
974 | |||
975 | /** |
||
976 | * Searches tree for the supplied object identifier. |
||
977 | * |
||
978 | */ |
||
979 | const struct snmp_node * |
||
980 | snmp_mib_tree_resolve_exact(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, u8_t *oid_instance_len) |
||
981 | { |
||
982 | const struct snmp_node *const *node = &mib->root_node; |
||
983 | u8_t oid_offset = mib->base_oid_len; |
||
984 | |||
985 | while ((oid_offset < oid_len) && ((*node)->node_type == SNMP_NODE_TREE)) { |
||
986 | /* search for matching sub node */ |
||
987 | u32_t subnode_oid = *(oid + oid_offset); |
||
988 | |||
989 | u32_t i = (*(const struct snmp_tree_node * const *)node)->subnode_count; |
||
990 | node = (*(const struct snmp_tree_node * const *)node)->subnodes; |
||
991 | while ((i > 0) && ((*node)->oid != subnode_oid)) { |
||
992 | node++; |
||
993 | i--; |
||
994 | } |
||
995 | |||
996 | if (i == 0) { |
||
997 | /* no matching subnode found */ |
||
998 | return NULL; |
||
999 | } |
||
1000 | |||
1001 | oid_offset++; |
||
1002 | } |
||
1003 | |||
1004 | if ((*node)->node_type != SNMP_NODE_TREE) { |
||
1005 | /* we found a leaf node */ |
||
1006 | *oid_instance_len = oid_len - oid_offset; |
||
1007 | return (*node); |
||
1008 | } |
||
1009 | |||
1010 | return NULL; |
||
1011 | } |
||
1012 | |||
1013 | const struct snmp_node * |
||
1014 | snmp_mib_tree_resolve_next(const struct snmp_mib *mib, const u32_t *oid, u8_t oid_len, struct snmp_obj_id *oidret) |
||
1015 | { |
||
1016 | u8_t oid_offset = mib->base_oid_len; |
||
1017 | const struct snmp_node *const *node; |
||
1018 | const struct snmp_tree_node *node_stack[SNMP_MAX_OBJ_ID_LEN]; |
||
1019 | s32_t nsi = 0; /* NodeStackIndex */ |
||
1020 | u32_t subnode_oid; |
||
1021 | |||
1022 | if (mib->root_node->node_type != SNMP_NODE_TREE) { |
||
1023 | /* a next operation on a mib with only a leaf node will always return NULL because there is no other node */ |
||
1024 | return NULL; |
||
1025 | } |
||
1026 | |||
1027 | /* first build node stack related to passed oid (as far as possible), then go backwards to determine the next node */ |
||
1028 | node_stack[nsi] = (const struct snmp_tree_node *)(const void *)mib->root_node; |
||
1029 | while (oid_offset < oid_len) { |
||
1030 | /* search for matching sub node */ |
||
1031 | u32_t i = node_stack[nsi]->subnode_count; |
||
1032 | node = node_stack[nsi]->subnodes; |
||
1033 | |||
1034 | subnode_oid = *(oid + oid_offset); |
||
1035 | |||
1036 | while ((i > 0) && ((*node)->oid != subnode_oid)) { |
||
1037 | node++; |
||
1038 | i--; |
||
1039 | } |
||
1040 | |||
1041 | if ((i == 0) || ((*node)->node_type != SNMP_NODE_TREE)) { |
||
1042 | /* no (matching) tree-subnode found */ |
||
1043 | break; |
||
1044 | } |
||
1045 | nsi++; |
||
1046 | node_stack[nsi] = (const struct snmp_tree_node *)(const void *)(*node); |
||
1047 | |||
1048 | oid_offset++; |
||
1049 | } |
||
1050 | |||
1051 | |||
1052 | if (oid_offset >= oid_len) { |
||
1053 | /* passed oid references a tree node -> return first useable sub node of it */ |
||
1054 | subnode_oid = 0; |
||
1055 | } else { |
||
1056 | subnode_oid = *(oid + oid_offset) + 1; |
||
1057 | } |
||
1058 | |||
1059 | while (nsi >= 0) { |
||
1060 | const struct snmp_node *subnode = NULL; |
||
1061 | |||
1062 | /* find next node on current level */ |
||
1063 | s32_t i = node_stack[nsi]->subnode_count; |
||
1064 | node = node_stack[nsi]->subnodes; |
||
1065 | while (i > 0) { |
||
1066 | if ((*node)->oid == subnode_oid) { |
||
1067 | subnode = *node; |
||
1068 | break; |
||
1069 | } else if (((*node)->oid > subnode_oid) && ((subnode == NULL) || ((*node)->oid < subnode->oid))) { |
||
1070 | subnode = *node; |
||
1071 | } |
||
1072 | |||
1073 | node++; |
||
1074 | i--; |
||
1075 | } |
||
1076 | |||
1077 | if (subnode == NULL) { |
||
1078 | /* no further node found on this level, go one level up and start searching with index of current node*/ |
||
1079 | subnode_oid = node_stack[nsi]->node.oid + 1; |
||
1080 | nsi--; |
||
1081 | } else { |
||
1082 | if (subnode->node_type == SNMP_NODE_TREE) { |
||
1083 | /* next is a tree node, go into it and start searching */ |
||
1084 | nsi++; |
||
1085 | node_stack[nsi] = (const struct snmp_tree_node *)(const void *)subnode; |
||
1086 | subnode_oid = 0; |
||
1087 | } else { |
||
1088 | /* we found a leaf node -> fill oidret and return it */ |
||
1089 | snmp_oid_assign(oidret, mib->base_oid, mib->base_oid_len); |
||
1090 | i = 1; |
||
1091 | while (i <= nsi) { |
||
1092 | oidret->id[oidret->len] = node_stack[i]->node.oid; |
||
1093 | oidret->len++; |
||
1094 | i++; |
||
1095 | } |
||
1096 | |||
1097 | oidret->id[oidret->len] = subnode->oid; |
||
1098 | oidret->len++; |
||
1099 | |||
1100 | return subnode; |
||
1101 | } |
||
1102 | } |
||
1103 | } |
||
1104 | |||
1105 | return NULL; |
||
1106 | } |
||
1107 | |||
1108 | /** initialize struct next_oid_state using this function before passing it to next_oid_check */ |
||
1109 | void |
||
1110 | snmp_next_oid_init(struct snmp_next_oid_state *state, |
||
1111 | const u32_t *start_oid, u8_t start_oid_len, |
||
1112 | u32_t *next_oid_buf, u8_t next_oid_max_len) |
||
1113 | { |
||
1114 | state->start_oid = start_oid; |
||
1115 | state->start_oid_len = start_oid_len; |
||
1116 | state->next_oid = next_oid_buf; |
||
1117 | state->next_oid_len = 0; |
||
1118 | state->next_oid_max_len = next_oid_max_len; |
||
1119 | state->status = SNMP_NEXT_OID_STATUS_NO_MATCH; |
||
1120 | } |
||
1121 | |||
1122 | /** checks if the passed incomplete OID may be a possible candidate for snmp_next_oid_check(); |
||
1123 | this methid is intended if the complete OID is not yet known but it is very expensive to build it up, |
||
1124 | so it is possible to test the starting part before building up the complete oid and pass it to snmp_next_oid_check()*/ |
||
1125 | u8_t |
||
1126 | snmp_next_oid_precheck(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len) |
||
1127 | { |
||
1128 | if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) { |
||
1129 | u8_t start_oid_len = (oid_len < state->start_oid_len) ? oid_len : state->start_oid_len; |
||
1130 | |||
1131 | /* check passed OID is located behind start offset */ |
||
1132 | if (snmp_oid_compare(oid, oid_len, state->start_oid, start_oid_len) >= 0) { |
||
1133 | /* check if new oid is located closer to start oid than current closest oid */ |
||
1134 | if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) || |
||
1135 | (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) { |
||
1136 | return 1; |
||
1137 | } |
||
1138 | } |
||
1139 | } |
||
1140 | |||
1141 | return 0; |
||
1142 | } |
||
1143 | |||
1144 | /** checks the passed OID if it is a candidate to be the next one (get_next); returns !=0 if passed oid is currently closest, otherwise 0 */ |
||
1145 | u8_t |
||
1146 | snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, u8_t oid_len, void *reference) |
||
1147 | { |
||
1148 | /* do not overwrite a fail result */ |
||
1149 | if (state->status != SNMP_NEXT_OID_STATUS_BUF_TO_SMALL) { |
||
1150 | /* check passed OID is located behind start offset */ |
||
1151 | if (snmp_oid_compare(oid, oid_len, state->start_oid, state->start_oid_len) > 0) { |
||
1152 | /* check if new oid is located closer to start oid than current closest oid */ |
||
1153 | if ((state->status == SNMP_NEXT_OID_STATUS_NO_MATCH) || |
||
1154 | (snmp_oid_compare(oid, oid_len, state->next_oid, state->next_oid_len) < 0)) { |
||
1155 | if (oid_len <= state->next_oid_max_len) { |
||
1156 | MEMCPY(state->next_oid, oid, oid_len * sizeof(u32_t)); |
||
1157 | state->next_oid_len = oid_len; |
||
1158 | state->status = SNMP_NEXT_OID_STATUS_SUCCESS; |
||
1159 | state->reference = reference; |
||
1160 | return 1; |
||
1161 | } else { |
||
1162 | state->status = SNMP_NEXT_OID_STATUS_BUF_TO_SMALL; |
||
1163 | } |
||
1164 | } |
||
1165 | } |
||
1166 | } |
||
1167 | |||
1168 | return 0; |
||
1169 | } |
||
1170 | |||
1171 | u8_t |
||
1172 | snmp_oid_in_range(const u32_t *oid_in, u8_t oid_len, const struct snmp_oid_range *oid_ranges, u8_t oid_ranges_len) |
||
1173 | { |
||
1174 | u8_t i; |
||
1175 | |||
1176 | if (oid_len != oid_ranges_len) { |
||
1177 | return 0; |
||
1178 | } |
||
1179 | |||
1180 | for (i = 0; i < oid_ranges_len; i++) { |
||
1181 | if ((oid_in[i] < oid_ranges[i].min) || (oid_in[i] > oid_ranges[i].max)) { |
||
1182 | return 0; |
||
1183 | } |
||
1184 | } |
||
1185 | |||
1186 | return 1; |
||
1187 | } |
||
1188 | |||
1189 | snmp_err_t |
||
1190 | snmp_set_test_ok(struct snmp_node_instance *instance, u16_t value_len, void *value) |
||
1191 | { |
||
1192 | LWIP_UNUSED_ARG(instance); |
||
1193 | LWIP_UNUSED_ARG(value_len); |
||
1194 | LWIP_UNUSED_ARG(value); |
||
1195 | |||
1196 | return SNMP_ERR_NOERROR; |
||
1197 | } |
||
1198 | |||
1199 | /** |
||
1200 | * Decodes BITS pseudotype value from ASN.1 OctetString. |
||
1201 | * |
||
1202 | * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly |
||
1203 | * be encoded/decoded by the agent. Instead call this function as required from |
||
1204 | * get/test/set methods. |
||
1205 | * |
||
1206 | * @param buf points to a buffer holding the ASN1 octet string |
||
1207 | * @param buf_len length of octet string |
||
1208 | * @param bit_value decoded Bit value with Bit0 == LSB |
||
1209 | * @return ERR_OK if successful, ERR_ARG if bit value contains more than 32 bit |
||
1210 | */ |
||
1211 | err_t |
||
1212 | snmp_decode_bits(const u8_t *buf, u32_t buf_len, u32_t *bit_value) |
||
1213 | { |
||
1214 | u8_t b; |
||
1215 | u8_t bits_processed = 0; |
||
1216 | *bit_value = 0; |
||
1217 | |||
1218 | while (buf_len > 0) { |
||
1219 | /* any bit set in this byte? */ |
||
1220 | if (*buf != 0x00) { |
||
1221 | if (bits_processed >= 32) { |
||
1222 | /* accept more than 4 bytes, but only when no bits are set */ |
||
1223 | return ERR_VAL; |
||
1224 | } |
||
1225 | |||
1226 | b = *buf; |
||
1227 | do { |
||
1228 | if (b & 0x80) { |
||
1229 | *bit_value |= (1 << bits_processed); |
||
1230 | } |
||
1231 | bits_processed++; |
||
1232 | b <<= 1; |
||
1233 | } while ((bits_processed & 0x07) != 0); /* &0x07 -> % 8 */ |
||
1234 | } else { |
||
1235 | bits_processed += 8; |
||
1236 | } |
||
1237 | |||
1238 | buf_len--; |
||
1239 | buf++; |
||
1240 | } |
||
1241 | |||
1242 | return ERR_OK; |
||
1243 | } |
||
1244 | |||
1245 | err_t |
||
1246 | snmp_decode_truthvalue(const s32_t *asn1_value, u8_t *bool_value) |
||
1247 | { |
||
1248 | /* defined by RFC1443: |
||
1249 | TruthValue ::= TEXTUAL-CONVENTION |
||
1250 | STATUS current |
||
1251 | DESCRIPTION |
||
1252 | "Represents a boolean value." |
||
1253 | SYNTAX INTEGER { true(1), false(2) } |
||
1254 | */ |
||
1255 | |||
1256 | if ((asn1_value == NULL) || (bool_value == NULL)) { |
||
1257 | return ERR_ARG; |
||
1258 | } |
||
1259 | |||
1260 | if (*asn1_value == 1) { |
||
1261 | *bool_value = 1; |
||
1262 | } else if (*asn1_value == 2) { |
||
1263 | *bool_value = 0; |
||
1264 | } else { |
||
1265 | return ERR_VAL; |
||
1266 | } |
||
1267 | |||
1268 | return ERR_OK; |
||
1269 | } |
||
1270 | |||
1271 | /** |
||
1272 | * Encodes BITS pseudotype value into ASN.1 OctetString. |
||
1273 | * |
||
1274 | * @note Because BITS pseudo type is encoded as OCTET STRING, it cannot directly |
||
1275 | * be encoded/decoded by the agent. Instead call this function as required from |
||
1276 | * get/test/set methods. |
||
1277 | * |
||
1278 | * @param buf points to a buffer where the resulting ASN1 octet string is stored to |
||
1279 | * @param buf_len max length of the bufffer |
||
1280 | * @param bit_value Bit value to encode with Bit0 == LSB |
||
1281 | * @param bit_count Number of possible bits for the bit value (according to rfc we have to send all bits independant from their truth value) |
||
1282 | * @return number of bytes used from buffer to store the resulting OctetString |
||
1283 | */ |
||
1284 | u8_t |
||
1285 | snmp_encode_bits(u8_t *buf, u32_t buf_len, u32_t bit_value, u8_t bit_count) |
||
1286 | { |
||
1287 | u8_t len = 0; |
||
1288 | u8_t min_bytes = (bit_count + 7) >> 3; /* >>3 -> / 8 */ |
||
1289 | |||
1290 | while ((buf_len > 0) && (bit_value != 0x00)) { |
||
1291 | s8_t i = 7; |
||
1292 | *buf = 0x00; |
||
1293 | while (i >= 0) { |
||
1294 | if (bit_value & 0x01) { |
||
1295 | *buf |= 0x01; |
||
1296 | } |
||
1297 | |||
1298 | if (i > 0) { |
||
1299 | *buf <<= 1; |
||
1300 | } |
||
1301 | |||
1302 | bit_value >>= 1; |
||
1303 | i--; |
||
1304 | } |
||
1305 | |||
1306 | buf++; |
||
1307 | buf_len--; |
||
1308 | len++; |
||
1309 | } |
||
1310 | |||
1311 | if (len < min_bytes) { |
||
1312 | buf += len; |
||
1313 | buf_len -= len; |
||
1314 | |||
1315 | while ((len < min_bytes) && (buf_len > 0)) { |
||
1316 | *buf = 0x00; |
||
1317 | buf++; |
||
1318 | buf_len--; |
||
1319 | len++; |
||
1320 | } |
||
1321 | } |
||
1322 | |||
1323 | return len; |
||
1324 | } |
||
1325 | |||
1326 | u8_t |
||
1327 | snmp_encode_truthvalue(s32_t *asn1_value, u32_t bool_value) |
||
1328 | { |
||
1329 | /* defined by RFC1443: |
||
1330 | TruthValue ::= TEXTUAL-CONVENTION |
||
1331 | STATUS current |
||
1332 | DESCRIPTION |
||
1333 | "Represents a boolean value." |
||
1334 | SYNTAX INTEGER { true(1), false(2) } |
||
1335 | */ |
||
1336 | |||
1337 | if (asn1_value == NULL) { |
||
1338 | return 0; |
||
1339 | } |
||
1340 | |||
1341 | if (bool_value) { |
||
1342 | *asn1_value = 1; /* defined by RFC1443 */ |
||
1343 | } else { |
||
1344 | *asn1_value = 2; /* defined by RFC1443 */ |
||
1345 | } |
||
1346 | |||
1347 | return sizeof(s32_t); |
||
1348 | } |
||
1349 | |||
1350 | #endif /* LWIP_SNMP */ |