BadVPN – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /** |
2 | * @file |
||
3 | * SNMPv1 traps implementation. |
||
4 | */ |
||
5 | |||
6 | /* |
||
7 | * Copyright (c) 2001-2004 Swedish Institute of Computer Science. |
||
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 | * This file is part of the lwIP TCP/IP stack. |
||
33 | * |
||
34 | * Author: Martin Hentschel |
||
35 | * Christiaan Simons <christiaan.simons@axon.tv> |
||
36 | * |
||
37 | */ |
||
38 | |||
39 | #include "lwip/apps/snmp_opts.h" |
||
40 | |||
41 | #if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */ |
||
42 | |||
43 | #include <string.h> |
||
44 | |||
45 | #include "lwip/snmp.h" |
||
46 | #include "lwip/sys.h" |
||
47 | #include "lwip/apps/snmp.h" |
||
48 | #include "lwip/apps/snmp_core.h" |
||
49 | #include "lwip/prot/iana.h" |
||
50 | #include "snmp_msg.h" |
||
51 | #include "snmp_asn1.h" |
||
52 | #include "snmp_core_priv.h" |
||
53 | |||
54 | struct snmp_msg_trap { |
||
55 | /* source enterprise ID (sysObjectID) */ |
||
56 | const struct snmp_obj_id *enterprise; |
||
57 | /* source IP address, raw network order format */ |
||
58 | ip_addr_t sip; |
||
59 | /* generic trap code */ |
||
60 | u32_t gen_trap; |
||
61 | /* specific trap code */ |
||
62 | u32_t spc_trap; |
||
63 | /* timestamp */ |
||
64 | u32_t ts; |
||
65 | /* snmp_version */ |
||
66 | u32_t snmp_version; |
||
67 | |||
68 | /* output trap lengths used in ASN encoding */ |
||
69 | /* encoding pdu length */ |
||
70 | u16_t pdulen; |
||
71 | /* encoding community length */ |
||
72 | u16_t comlen; |
||
73 | /* encoding sequence length */ |
||
74 | u16_t seqlen; |
||
75 | /* encoding varbinds sequence length */ |
||
76 | u16_t vbseqlen; |
||
77 | }; |
||
78 | |||
79 | static u16_t snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds); |
||
80 | static u16_t snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len); |
||
81 | static err_t snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream); |
||
82 | static err_t snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds); |
||
83 | |||
84 | #define BUILD_EXEC(code) \ |
||
85 | if ((code) != ERR_OK) { \ |
||
86 | LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound trap frame!")); \ |
||
87 | return ERR_ARG; \ |
||
88 | } |
||
89 | |||
90 | /** Agent community string for sending traps */ |
||
91 | extern const char *snmp_community_trap; |
||
92 | |||
93 | void *snmp_traps_handle; |
||
94 | |||
95 | struct snmp_trap_dst { |
||
96 | /* destination IP address in network order */ |
||
97 | ip_addr_t dip; |
||
98 | /* set to 0 when disabled, >0 when enabled */ |
||
99 | u8_t enable; |
||
100 | }; |
||
101 | static struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS]; |
||
102 | |||
103 | static u8_t snmp_auth_traps_enabled = 0; |
||
104 | |||
105 | /** |
||
106 | * @ingroup snmp_traps |
||
107 | * Sets enable switch for this trap destination. |
||
108 | * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 |
||
109 | * @param enable switch if 0 destination is disabled >0 enabled. |
||
110 | */ |
||
111 | void |
||
112 | snmp_trap_dst_enable(u8_t dst_idx, u8_t enable) |
||
113 | { |
||
114 | if (dst_idx < SNMP_TRAP_DESTINATIONS) { |
||
115 | trap_dst[dst_idx].enable = enable; |
||
116 | } |
||
117 | } |
||
118 | |||
119 | /** |
||
120 | * @ingroup snmp_traps |
||
121 | * Sets IPv4 address for this trap destination. |
||
122 | * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1 |
||
123 | * @param dst IPv4 address in host order. |
||
124 | */ |
||
125 | void |
||
126 | snmp_trap_dst_ip_set(u8_t dst_idx, const ip_addr_t *dst) |
||
127 | { |
||
128 | if (dst_idx < SNMP_TRAP_DESTINATIONS) { |
||
129 | ip_addr_set(&trap_dst[dst_idx].dip, dst); |
||
130 | } |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * @ingroup snmp_traps |
||
135 | * Enable/disable authentication traps |
||
136 | */ |
||
137 | void |
||
138 | snmp_set_auth_traps_enabled(u8_t enable) |
||
139 | { |
||
140 | snmp_auth_traps_enabled = enable; |
||
141 | } |
||
142 | |||
143 | /** |
||
144 | * @ingroup snmp_traps |
||
145 | * Get authentication traps enabled state |
||
146 | */ |
||
147 | u8_t |
||
148 | snmp_get_auth_traps_enabled(void) |
||
149 | { |
||
150 | return snmp_auth_traps_enabled; |
||
151 | } |
||
152 | |||
153 | |||
154 | /** |
||
155 | * @ingroup snmp_traps |
||
156 | * Sends a generic or enterprise specific trap message. |
||
157 | * |
||
158 | * @param eoid points to enterprise object identifier |
||
159 | * @param generic_trap is the trap code |
||
160 | * @param specific_trap used for enterprise traps when generic_trap == 6 |
||
161 | * @param varbinds linked list of varbinds to be sent |
||
162 | * @return ERR_OK when success, ERR_MEM if we're out of memory |
||
163 | * |
||
164 | * @note the use of the enterprise identifier field |
||
165 | * is per RFC1215. |
||
166 | * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps |
||
167 | * and .iso.org.dod.internet.private.enterprises.yourenterprise |
||
168 | * (sysObjectID) for specific traps. |
||
169 | */ |
||
170 | err_t |
||
171 | snmp_send_trap(const struct snmp_obj_id *eoid, s32_t generic_trap, s32_t specific_trap, struct snmp_varbind *varbinds) |
||
172 | { |
||
173 | struct snmp_msg_trap trap_msg; |
||
174 | struct snmp_trap_dst *td; |
||
175 | struct pbuf *p; |
||
176 | u16_t i, tot_len; |
||
177 | err_t err = ERR_OK; |
||
178 | |||
179 | trap_msg.snmp_version = 0; |
||
180 | |||
181 | for (i = 0, td = &trap_dst[0]; i < SNMP_TRAP_DESTINATIONS; i++, td++) { |
||
182 | if ((td->enable != 0) && !ip_addr_isany(&td->dip)) { |
||
183 | /* lookup current source address for this dst */ |
||
184 | if (snmp_get_local_ip_for_dst(snmp_traps_handle, &td->dip, &trap_msg.sip)) { |
||
185 | if (eoid == NULL) { |
||
186 | trap_msg.enterprise = snmp_get_device_enterprise_oid(); |
||
187 | } else { |
||
188 | trap_msg.enterprise = eoid; |
||
189 | } |
||
190 | |||
191 | trap_msg.gen_trap = generic_trap; |
||
192 | if (generic_trap == SNMP_GENTRAP_ENTERPRISE_SPECIFIC) { |
||
193 | trap_msg.spc_trap = specific_trap; |
||
194 | } else { |
||
195 | trap_msg.spc_trap = 0; |
||
196 | } |
||
197 | |||
198 | MIB2_COPY_SYSUPTIME_TO(&trap_msg.ts); |
||
199 | |||
200 | /* pass 0, calculate length fields */ |
||
201 | tot_len = snmp_trap_varbind_sum(&trap_msg, varbinds); |
||
202 | tot_len = snmp_trap_header_sum(&trap_msg, tot_len); |
||
203 | |||
204 | /* allocate pbuf(s) */ |
||
205 | p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_RAM); |
||
206 | if (p != NULL) { |
||
207 | struct snmp_pbuf_stream pbuf_stream; |
||
208 | snmp_pbuf_stream_init(&pbuf_stream, p, 0, tot_len); |
||
209 | |||
210 | /* pass 1, encode packet into the pbuf(s) */ |
||
211 | snmp_trap_header_enc(&trap_msg, &pbuf_stream); |
||
212 | snmp_trap_varbind_enc(&trap_msg, &pbuf_stream, varbinds); |
||
213 | |||
214 | snmp_stats.outtraps++; |
||
215 | snmp_stats.outpkts++; |
||
216 | |||
217 | /** send to the TRAP destination */ |
||
218 | snmp_sendto(snmp_traps_handle, p, &td->dip, LWIP_IANA_PORT_SNMP_TRAP); |
||
219 | pbuf_free(p); |
||
220 | } else { |
||
221 | err = ERR_MEM; |
||
222 | } |
||
223 | } else { |
||
224 | /* routing error */ |
||
225 | err = ERR_RTE; |
||
226 | } |
||
227 | } |
||
228 | } |
||
229 | return err; |
||
230 | } |
||
231 | |||
232 | /** |
||
233 | * @ingroup snmp_traps |
||
234 | * Send generic SNMP trap |
||
235 | */ |
||
236 | err_t |
||
237 | snmp_send_trap_generic(s32_t generic_trap) |
||
238 | { |
||
239 | static const struct snmp_obj_id oid = { 7, { 1, 3, 6, 1, 2, 1, 11 } }; |
||
240 | return snmp_send_trap(&oid, generic_trap, 0, NULL); |
||
241 | } |
||
242 | |||
243 | /** |
||
244 | * @ingroup snmp_traps |
||
245 | * Send specific SNMP trap with variable bindings |
||
246 | */ |
||
247 | err_t |
||
248 | snmp_send_trap_specific(s32_t specific_trap, struct snmp_varbind *varbinds) |
||
249 | { |
||
250 | return snmp_send_trap(NULL, SNMP_GENTRAP_ENTERPRISE_SPECIFIC, specific_trap, varbinds); |
||
251 | } |
||
252 | |||
253 | /** |
||
254 | * @ingroup snmp_traps |
||
255 | * Send coldstart trap |
||
256 | */ |
||
257 | void |
||
258 | snmp_coldstart_trap(void) |
||
259 | { |
||
260 | snmp_send_trap_generic(SNMP_GENTRAP_COLDSTART); |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * @ingroup snmp_traps |
||
265 | * Send authentication failure trap (used internally by agent) |
||
266 | */ |
||
267 | void |
||
268 | snmp_authfail_trap(void) |
||
269 | { |
||
270 | if (snmp_auth_traps_enabled != 0) { |
||
271 | snmp_send_trap_generic(SNMP_GENTRAP_AUTH_FAILURE); |
||
272 | } |
||
273 | } |
||
274 | |||
275 | static u16_t |
||
276 | snmp_trap_varbind_sum(struct snmp_msg_trap *trap, struct snmp_varbind *varbinds) |
||
277 | { |
||
278 | struct snmp_varbind *varbind; |
||
279 | u16_t tot_len; |
||
280 | u8_t tot_len_len; |
||
281 | |||
282 | tot_len = 0; |
||
283 | varbind = varbinds; |
||
284 | while (varbind != NULL) { |
||
285 | struct snmp_varbind_len len; |
||
286 | |||
287 | if (snmp_varbind_length(varbind, &len) == ERR_OK) { |
||
288 | tot_len += 1 + len.vb_len_len + len.vb_value_len; |
||
289 | } |
||
290 | |||
291 | varbind = varbind->next; |
||
292 | } |
||
293 | |||
294 | trap->vbseqlen = tot_len; |
||
295 | snmp_asn1_enc_length_cnt(trap->vbseqlen, &tot_len_len); |
||
296 | tot_len += 1 + tot_len_len; |
||
297 | |||
298 | return tot_len; |
||
299 | } |
||
300 | |||
301 | /** |
||
302 | * Sums trap header field lengths from tail to head and |
||
303 | * returns trap_header_lengths for second encoding pass. |
||
304 | * |
||
305 | * @param trap Trap message |
||
306 | * @param vb_len varbind-list length |
||
307 | * @return the required length for encoding the trap header |
||
308 | */ |
||
309 | static u16_t |
||
310 | snmp_trap_header_sum(struct snmp_msg_trap *trap, u16_t vb_len) |
||
311 | { |
||
312 | u16_t tot_len; |
||
313 | u16_t len; |
||
314 | u8_t lenlen; |
||
315 | |||
316 | tot_len = vb_len; |
||
317 | |||
318 | snmp_asn1_enc_u32t_cnt(trap->ts, &len); |
||
319 | snmp_asn1_enc_length_cnt(len, &lenlen); |
||
320 | tot_len += 1 + len + lenlen; |
||
321 | |||
322 | snmp_asn1_enc_s32t_cnt(trap->spc_trap, &len); |
||
323 | snmp_asn1_enc_length_cnt(len, &lenlen); |
||
324 | tot_len += 1 + len + lenlen; |
||
325 | |||
326 | snmp_asn1_enc_s32t_cnt(trap->gen_trap, &len); |
||
327 | snmp_asn1_enc_length_cnt(len, &lenlen); |
||
328 | tot_len += 1 + len + lenlen; |
||
329 | |||
330 | if (IP_IS_V6_VAL(trap->sip)) { |
||
331 | #if LWIP_IPV6 |
||
332 | len = sizeof(ip_2_ip6(&trap->sip)->addr); |
||
333 | #endif |
||
334 | } else { |
||
335 | #if LWIP_IPV4 |
||
336 | len = sizeof(ip_2_ip4(&trap->sip)->addr); |
||
337 | #endif |
||
338 | } |
||
339 | snmp_asn1_enc_length_cnt(len, &lenlen); |
||
340 | tot_len += 1 + len + lenlen; |
||
341 | |||
342 | snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &len); |
||
343 | snmp_asn1_enc_length_cnt(len, &lenlen); |
||
344 | tot_len += 1 + len + lenlen; |
||
345 | |||
346 | trap->pdulen = tot_len; |
||
347 | snmp_asn1_enc_length_cnt(trap->pdulen, &lenlen); |
||
348 | tot_len += 1 + lenlen; |
||
349 | |||
350 | trap->comlen = (u16_t)LWIP_MIN(strlen(snmp_community_trap), 0xFFFF); |
||
351 | snmp_asn1_enc_length_cnt(trap->comlen, &lenlen); |
||
352 | tot_len += 1 + lenlen + trap->comlen; |
||
353 | |||
354 | snmp_asn1_enc_s32t_cnt(trap->snmp_version, &len); |
||
355 | snmp_asn1_enc_length_cnt(len, &lenlen); |
||
356 | tot_len += 1 + len + lenlen; |
||
357 | |||
358 | trap->seqlen = tot_len; |
||
359 | snmp_asn1_enc_length_cnt(trap->seqlen, &lenlen); |
||
360 | tot_len += 1 + lenlen; |
||
361 | |||
362 | return tot_len; |
||
363 | } |
||
364 | |||
365 | static err_t |
||
366 | snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds) |
||
367 | { |
||
368 | struct snmp_asn1_tlv tlv; |
||
369 | struct snmp_varbind *varbind; |
||
370 | |||
371 | varbind = varbinds; |
||
372 | |||
373 | SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->vbseqlen); |
||
374 | BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); |
||
375 | |||
376 | while (varbind != NULL) { |
||
377 | BUILD_EXEC( snmp_append_outbound_varbind(pbuf_stream, varbind) ); |
||
378 | |||
379 | varbind = varbind->next; |
||
380 | } |
||
381 | |||
382 | return ERR_OK; |
||
383 | } |
||
384 | |||
385 | /** |
||
386 | * Encodes trap header from head to tail. |
||
387 | */ |
||
388 | static err_t |
||
389 | snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream) |
||
390 | { |
||
391 | struct snmp_asn1_tlv tlv; |
||
392 | |||
393 | /* 'Message' sequence */ |
||
394 | SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_SEQUENCE, 0, trap->seqlen); |
||
395 | BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); |
||
396 | |||
397 | /* version */ |
||
398 | SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); |
||
399 | snmp_asn1_enc_s32t_cnt(trap->snmp_version, &tlv.value_len); |
||
400 | BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); |
||
401 | BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->snmp_version) ); |
||
402 | |||
403 | /* community */ |
||
404 | SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OCTET_STRING, 0, trap->comlen); |
||
405 | BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); |
||
406 | BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)snmp_community_trap, trap->comlen) ); |
||
407 | |||
408 | /* 'PDU' sequence */ |
||
409 | SNMP_ASN1_SET_TLV_PARAMS(tlv, (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_TRAP), 0, trap->pdulen); |
||
410 | BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); |
||
411 | |||
412 | /* object ID */ |
||
413 | SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_OBJECT_ID, 0, 0); |
||
414 | snmp_asn1_enc_oid_cnt(trap->enterprise->id, trap->enterprise->len, &tlv.value_len); |
||
415 | BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); |
||
416 | BUILD_EXEC( snmp_asn1_enc_oid(pbuf_stream, trap->enterprise->id, trap->enterprise->len) ); |
||
417 | |||
418 | /* IP addr */ |
||
419 | if (IP_IS_V6_VAL(trap->sip)) { |
||
420 | #if LWIP_IPV6 |
||
421 | SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip6(&trap->sip)->addr)); |
||
422 | BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); |
||
423 | BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip6(&trap->sip)->addr, sizeof(ip_2_ip6(&trap->sip)->addr)) ); |
||
424 | #endif |
||
425 | } else { |
||
426 | #if LWIP_IPV4 |
||
427 | SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_IPADDR, 0, sizeof(ip_2_ip4(&trap->sip)->addr)); |
||
428 | BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); |
||
429 | BUILD_EXEC( snmp_asn1_enc_raw(pbuf_stream, (const u8_t *)&ip_2_ip4(&trap->sip)->addr, sizeof(ip_2_ip4(&trap->sip)->addr)) ); |
||
430 | #endif |
||
431 | } |
||
432 | |||
433 | /* trap length */ |
||
434 | SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); |
||
435 | snmp_asn1_enc_s32t_cnt(trap->gen_trap, &tlv.value_len); |
||
436 | BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); |
||
437 | BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->gen_trap) ); |
||
438 | |||
439 | /* specific trap */ |
||
440 | SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_INTEGER, 0, 0); |
||
441 | snmp_asn1_enc_s32t_cnt(trap->spc_trap, &tlv.value_len); |
||
442 | BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); |
||
443 | BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->spc_trap) ); |
||
444 | |||
445 | /* timestamp */ |
||
446 | SNMP_ASN1_SET_TLV_PARAMS(tlv, SNMP_ASN1_TYPE_TIMETICKS, 0, 0); |
||
447 | snmp_asn1_enc_s32t_cnt(trap->ts, &tlv.value_len); |
||
448 | BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) ); |
||
449 | BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts) ); |
||
450 | |||
451 | return ERR_OK; |
||
452 | } |
||
453 | |||
454 | #endif /* LWIP_SNMP */ |