1 /*
2 * ProFTPD - mod_snmp SMI routines
3 * Copyright (c) 2008-2016 TJ Saunders
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 *
19 * As a special exemption, TJ Saunders and other respective copyright holders
20 * give permission to link this program with OpenSSL, and distribute the
21 * resulting executable, without including the source code for OpenSSL in the
22 * source distribution.
23 */
24
25 #include "mod_snmp.h"
26 #include "asn1.h"
27 #include "smi.h"
28 #include "mib.h"
29 #include "msg.h"
30
31 static const char *trace_channel = "snmp.smi";
32
snmp_smi_get_varstr(pool * p,unsigned char var_type)33 const char *snmp_smi_get_varstr(pool *p, unsigned char var_type) {
34 const char *varstr = "unknown";
35
36 switch (var_type) {
37 case SNMP_SMI_INTEGER:
38 varstr = "INTEGER";
39 break;
40
41 case SNMP_SMI_STRING:
42 varstr = "STRING";
43 break;
44
45 case SNMP_SMI_OID:
46 varstr = "OID";
47 break;
48
49 case SNMP_SMI_NULL:
50 varstr = "NULL";
51 break;
52
53 case SNMP_SMI_IPADDR:
54 varstr = "IPADDR";
55 break;
56
57 case SNMP_SMI_COUNTER32:
58 varstr = "COUNTER32";
59 break;
60
61 case SNMP_SMI_GAUGE32:
62 varstr = "GAUGE32";
63 break;
64
65 case SNMP_SMI_TIMETICKS:
66 varstr = "TIMETICKS";
67 break;
68
69 case SNMP_SMI_OPAQUE:
70 varstr = "OPAQUE";
71 break;
72
73 case SNMP_SMI_COUNTER64:
74 varstr = "COUNTER64";
75 break;
76
77 case SNMP_SMI_NO_SUCH_OBJECT:
78 varstr = "NO_SUCH_OBJECT";
79 break;
80
81 case SNMP_SMI_NO_SUCH_INSTANCE:
82 varstr = "NO_SUCH_INSTANCE";
83 break;
84
85 case SNMP_SMI_END_OF_MIB_VIEW:
86 varstr = "END_OF_MIB_VIEW";
87 break;
88 }
89
90 return varstr;
91 }
92
snmp_smi_alloc_var(pool * p,oid_t * name,unsigned int namelen)93 struct snmp_var *snmp_smi_alloc_var(pool *p, oid_t *name,
94 unsigned int namelen) {
95 pool *sub_pool;
96 struct snmp_var *var;
97
98 sub_pool = pr_pool_create_sz(p, 64);
99 var = pcalloc(sub_pool, sizeof(struct snmp_var));
100 var->pool = sub_pool;
101 var->next = NULL;
102
103 /* Default type for newly-allocated variables. */
104 var->smi_type = SNMP_SMI_NULL;
105
106 var->namelen = namelen;
107
108 if (var->namelen == 0) {
109 /* Not sure why a caller would do this, but... */
110 return var;
111 }
112
113 /* Even though the name argument may be NULL, we still allocate the space.
114 * Why? Because when reading off variables from a message, we may not
115 * know the name when we are allocating the struct, but we will know at
116 * some point after that.
117 */
118 var->name = pcalloc(var->pool, sizeof(oid_t) * var->namelen);
119
120 if (name != NULL) {
121 memmove(var->name, name, sizeof(oid_t) * var->namelen);
122 }
123
124 return var;
125 }
126
snmp_smi_create_var(pool * p,oid_t * name,unsigned int namelen,unsigned char smi_type,int32_t int_value,char * str_value,size_t str_valuelen)127 struct snmp_var *snmp_smi_create_var(pool *p, oid_t *name, unsigned int namelen,
128 unsigned char smi_type, int32_t int_value, char *str_value,
129 size_t str_valuelen) {
130 struct snmp_var *var = NULL;
131
132 switch (smi_type) {
133 case SNMP_SMI_INTEGER:
134 case SNMP_SMI_COUNTER32:
135 case SNMP_SMI_GAUGE32:
136 case SNMP_SMI_TIMETICKS:
137 var = snmp_smi_create_int(p, name, namelen, smi_type, int_value);
138 break;
139
140 case SNMP_SMI_STRING:
141 case SNMP_SMI_IPADDR:
142 var = snmp_smi_create_string(p, name, namelen, smi_type, str_value,
143 str_valuelen);
144 break;
145
146 default:
147 pr_trace_msg(trace_channel, 16,
148 "unable to create variable for SMI type %s",
149 snmp_smi_get_varstr(p, smi_type));
150 errno = ENOENT;
151 break;
152 }
153
154 return var;
155 }
156
snmp_smi_create_int(pool * p,oid_t * name,unsigned int namelen,unsigned char smi_type,int32_t value)157 struct snmp_var *snmp_smi_create_int(pool *p, oid_t *name, unsigned int namelen,
158 unsigned char smi_type, int32_t value) {
159 struct snmp_var *var;
160
161 var = snmp_smi_alloc_var(p, name, namelen);
162 var->valuelen = sizeof(value);
163 var->value.integer = palloc(var->pool, var->valuelen);
164 *(var->value.integer) = value;
165 var->smi_type = smi_type;
166
167 pr_trace_msg(trace_channel, 19,
168 "created SMI variable %s, value %d", snmp_smi_get_varstr(p, smi_type),
169 value);
170 return var;
171 }
172
snmp_smi_create_string(pool * p,oid_t * name,unsigned int namelen,unsigned char smi_type,char * value,size_t valuelen)173 struct snmp_var *snmp_smi_create_string(pool *p, oid_t *name,
174 unsigned int namelen, unsigned char smi_type, char *value,
175 size_t valuelen) {
176 struct snmp_var *var;
177
178 if (value == NULL) {
179 errno = EINVAL;
180 return NULL;
181 }
182
183 var = snmp_smi_alloc_var(p, name, namelen);
184 var->valuelen = valuelen;
185 var->value.string = pstrndup(var->pool, value, var->valuelen);
186 var->smi_type = smi_type;
187
188 pr_trace_msg(trace_channel, 19,
189 "created SMI variable %s, value '%s'", snmp_smi_get_varstr(p, smi_type),
190 value);
191 return var;
192 }
193
snmp_smi_create_oid(pool * p,oid_t * name,unsigned int namelen,unsigned char smi_type,oid_t * value,unsigned int valuelen)194 struct snmp_var *snmp_smi_create_oid(pool *p, oid_t *name,
195 unsigned int namelen, unsigned char smi_type, oid_t *value,
196 unsigned int valuelen) {
197 struct snmp_var *var;
198
199 if (value == NULL) {
200 errno = EINVAL;
201 return NULL;
202 }
203
204 if (smi_type != SNMP_SMI_OID) {
205 errno = EINVAL;
206 return NULL;
207 }
208
209 var = snmp_smi_alloc_var(p, name, namelen);
210
211 /* The valuelen argument is the number of sub-ids, NOT the number of bytes,
212 * for an OID.
213 */
214 var->valuelen = valuelen;
215 var->value.oid = palloc(var->pool, sizeof(oid_t) * var->valuelen);
216 memmove(var->value.oid, value, sizeof(oid_t) * var->valuelen);
217 var->smi_type = smi_type;
218
219 pr_trace_msg(trace_channel, 19,
220 "created SMI variable %s, value %s", snmp_smi_get_varstr(p, smi_type),
221 snmp_asn1_get_oidstr(p, value, valuelen));
222 return var;
223 }
224
snmp_smi_create_exception(pool * p,oid_t * name,unsigned int namelen,unsigned char smi_type)225 struct snmp_var *snmp_smi_create_exception(pool *p, oid_t *name,
226 unsigned int namelen, unsigned char smi_type) {
227 struct snmp_var *var;
228
229 /* Check that the SMI type is one of the allowed "exceptions"
230 * (terminology from RFC 1905).
231 */
232 switch (smi_type) {
233 case SNMP_SMI_NO_SUCH_OBJECT:
234 case SNMP_SMI_NO_SUCH_INSTANCE:
235 case SNMP_SMI_END_OF_MIB_VIEW:
236 break;
237
238 default:
239 errno = EINVAL;
240 return NULL;
241 }
242
243 var = snmp_smi_alloc_var(p, name, namelen);
244 var->valuelen = 0;
245 var->smi_type = smi_type;
246
247 pr_trace_msg(trace_channel, 19,
248 "created SMI variable %s", snmp_smi_get_varstr(p, smi_type));
249 return var;
250 }
251
252 /* Note: This will duplicate the entire varlist represented by the head
253 * variable.
254 */
snmp_smi_dup_var(pool * p,struct snmp_var * src_var)255 struct snmp_var *snmp_smi_dup_var(pool *p, struct snmp_var *src_var) {
256 struct snmp_var *head_var = NULL, *iter_var = NULL, *tail_var = NULL;
257 unsigned int var_count = 0;
258
259 for (iter_var = src_var; iter_var; iter_var = iter_var->next) {
260 struct snmp_var *var;
261
262 pr_signals_handle();
263
264 var = snmp_smi_alloc_var(p, iter_var->name, iter_var->namelen);
265 var->smi_type = iter_var->smi_type;
266 var->valuelen = iter_var->valuelen;
267
268 if (var->valuelen > 0) {
269 switch (var->smi_type) {
270 case SNMP_SMI_INTEGER:
271 var->value.integer = palloc(var->pool, var->valuelen);
272 memmove(var->value.integer, iter_var->value.integer, var->valuelen);
273 break;
274
275 case SNMP_SMI_STRING:
276 var->value.string = pcalloc(var->pool, var->valuelen);
277 memmove(var->value.string, iter_var->value.string, var->valuelen);
278 break;
279
280 case SNMP_SMI_OID:
281 var->value.oid = palloc(var->pool, var->valuelen);
282 memmove(var->value.oid, iter_var->value.oid, var->valuelen);
283 break;
284
285 default:
286 pr_trace_msg(trace_channel, 1,
287 "unable to dup variable '%s': unsupported",
288 snmp_asn1_get_tagstr(p, var->smi_type));
289
290 /* XXX Destroy the entire chain? */
291 destroy_pool(var->pool);
292 pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
293 errno = EINVAL;
294 return NULL;
295 }
296 }
297
298 if (head_var == NULL) {
299 head_var = var;
300 }
301
302 if (tail_var != NULL) {
303 tail_var->next = var;
304 }
305
306 tail_var = var;
307 var_count++;
308
309 pr_trace_msg(trace_channel, 19,
310 "cloned SMI variable %s", snmp_smi_get_varstr(p, iter_var->smi_type));
311 }
312
313 pr_trace_msg(trace_channel, 19, "cloned %u SMI %s", var_count,
314 var_count != 1 ? "variables" : "variable");
315 return head_var;
316 }
317
318 /* Decode a list of SNMPv2 variable bindings. */
snmp_smi_read_vars(pool * p,unsigned char ** buf,size_t * buflen,struct snmp_var ** varlist,int snmp_version)319 int snmp_smi_read_vars(pool *p, unsigned char **buf, size_t *buflen,
320 struct snmp_var **varlist, int snmp_version) {
321 struct snmp_var *var = NULL, *head = NULL, *tail = NULL;
322 unsigned char asn1_type;
323 unsigned int total_varlen = 0;
324 int res, var_count = 0;
325
326 res = snmp_asn1_read_header(p, buf, buflen, &asn1_type, &total_varlen, 0);
327 if (res < 0) {
328 return -1;
329 }
330
331 /* If this isn't a constructed sequence, error out. */
332 if (asn1_type != (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT)) {
333 pr_trace_msg(trace_channel, 1,
334 "unable to parse tag (%s) as list of variables",
335 snmp_asn1_get_tagstr(p, asn1_type));
336 pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
337 errno = EINVAL;
338 return -1;
339 }
340
341 pr_trace_msg(trace_channel, 17, "reading %s variables (%u bytes)",
342 snmp_msg_get_versionstr(snmp_version), total_varlen);
343
344 while (*buflen > 0) {
345 unsigned int varlen;
346 unsigned char *obj_start = NULL;
347 size_t obj_startlen = 0;
348
349 pr_signals_handle();
350
351 res = snmp_asn1_read_header(p, buf, buflen, &asn1_type, &varlen, 0);
352 if (res < 0) {
353 return -1;
354 }
355
356 /* If this isn't a constructed sequence, error out. */
357 if (asn1_type != (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT)) {
358 pr_trace_msg(trace_channel, 1,
359 "unable to parse tag (%s) as variable binding",
360 snmp_asn1_get_tagstr(p, asn1_type));
361 pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
362 errno = EINVAL;
363 return -1;
364 }
365
366 /* We don't know the name of this variable yet. */
367 var = snmp_smi_alloc_var(p, NULL, SNMP_SMI_MAX_NAMELEN);
368
369 /* Read the variable name/OID. */
370 res = snmp_asn1_read_oid(p, buf, buflen, &asn1_type, var->name,
371 &(var->namelen));
372 if (res < 0) {
373 destroy_pool(var->pool);
374 return -1;
375 }
376
377 if (asn1_type != (SNMP_ASN1_CLASS_UNIVERSAL|SNMP_ASN1_PRIMITIVE|SNMP_ASN1_TYPE_OID)) {
378 pr_trace_msg(trace_channel, 1,
379 "expected OID tag, read tag (%s) from variable list",
380 snmp_asn1_get_tagstr(p, asn1_type));
381
382 destroy_pool(var->pool);
383 pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
384 errno = EINVAL;
385 return -1;
386 }
387
388 if (pr_trace_get_level(trace_channel) >= 19) {
389 struct snmp_mib *mib;
390 int lacks_instance_id = FALSE;
391
392 mib = snmp_mib_get_by_oid(var->name, var->namelen, &lacks_instance_id);
393 if (mib != NULL) {
394 pr_trace_msg(trace_channel, 19,
395 "read variable OID %s (%u sub-ids, name %s)",
396 snmp_asn1_get_oidstr(p, var->name, var->namelen), var->namelen,
397 mib->instance_name);
398
399 } else {
400 pr_trace_msg(trace_channel, 19,
401 "read variable OID %s (%u sub-ids)",
402 snmp_asn1_get_oidstr(p, var->name, var->namelen), var->namelen);
403 }
404 }
405
406 obj_start = *buf;
407 obj_startlen = *buflen;
408
409 /* Now that we know the name/OID of the variable, let's find out what
410 * type of variable it is.
411 *
412 * This is effectively a peek, since the following reader functions
413 * will also want to read the tag/length header values.
414 */
415 res = snmp_asn1_read_header(p, &obj_start, &obj_startlen, &(var->smi_type),
416 &(var->valuelen), 0);
417 if (res < 0) {
418 destroy_pool(var->pool);
419 return -1;
420 }
421
422 pr_trace_msg(trace_channel, 19,
423 "read SMI variable %s, data len %u bytes",
424 snmp_smi_get_varstr(p, var->smi_type), var->valuelen);
425
426 /* Now read in the value */
427 switch (var->smi_type) {
428 case SNMP_SMI_INTEGER:
429 res = snmp_asn1_read_int(p, buf, buflen,
430 &(var->smi_type), var->value.integer, 0);
431 if (res == 0) {
432 pr_trace_msg(trace_channel, 19,
433 "read INTEGER variable (value %d)", *((int *) var->value.integer));
434 }
435 break;
436
437 case SNMP_SMI_COUNTER32:
438 case SNMP_SMI_GAUGE32:
439 case SNMP_SMI_TIMETICKS:
440 res = snmp_asn1_read_uint(p, buf, buflen,
441 &(var->smi_type), (unsigned long *) var->value.integer);
442 if (res == 0) {
443 pr_trace_msg(trace_channel, 19,
444 "read %s variable (value %u)",
445 snmp_smi_get_varstr(p, var->smi_type),
446 *((unsigned int *) var->value.integer));
447 }
448 break;
449
450 case SNMP_SMI_STRING:
451 case SNMP_SMI_IPADDR:
452 case SNMP_SMI_OPAQUE:
453 res = snmp_asn1_read_string(p, buf, buflen,
454 &(var->smi_type), &(var->value.string), &(var->valuelen));
455 if (res == 0) {
456 pr_trace_msg(trace_channel, 19,
457 "read %s variable (value '%.*s')",
458 snmp_smi_get_varstr(p, var->smi_type),
459 var->valuelen, var->value.string);
460 }
461 break;
462
463 case SNMP_SMI_OID:
464 res = snmp_asn1_read_oid(p, buf, buflen,
465 &(var->smi_type), var->value.oid, &(var->valuelen));
466 if (res == 0) {
467 pr_trace_msg(trace_channel, 19,
468 "read %s variable (%u sub-ids, value %s)",
469 snmp_smi_get_varstr(p, var->smi_type), var->valuelen,
470 snmp_asn1_get_oidstr(p, var->value.oid, var->valuelen));
471 }
472 break;
473
474 case SNMP_SMI_NULL:
475 res = snmp_asn1_read_null(p, buf, buflen, &(var->smi_type));
476 if (res == 0) {
477 pr_trace_msg(trace_channel, 19, "read %s variable",
478 snmp_smi_get_varstr(p, var->smi_type));
479 }
480 break;
481
482 case SNMP_SMI_NO_SUCH_OBJECT:
483 case SNMP_SMI_NO_SUCH_INSTANCE:
484 case SNMP_SMI_END_OF_MIB_VIEW:
485 pr_trace_msg(trace_channel, 19, "read %s variable",
486 snmp_smi_get_varstr(p, var->smi_type));
487 break;
488
489 case SNMP_SMI_COUNTER64:
490 pr_trace_msg(trace_channel, 1,
491 "unable to handle COUNTER64 variable (%x)", var->smi_type);
492 /* fallthrough */
493
494 default:
495 pr_trace_msg(trace_channel, 1,
496 "unable to read variable type %x", var->smi_type);
497 destroy_pool(var->pool);
498 pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
499 errno = EINVAL;
500 return -1;
501 }
502
503 if (res < 0) {
504 return -1;
505 }
506
507 /* Add the variable to the end of the list. */
508 if (tail != NULL) {
509 tail->next = var;
510 tail = var;
511
512 } else {
513 head = tail = var;
514 }
515
516 var_count++;
517 }
518
519 *varlist = head;
520 return var_count;
521 }
522
523 /* Encode an SNMPv2 variable binding.
524 *
525 * As per RFC 1905 Protocol Operations for SNMPv2:
526 *
527 * VarBind ::=
528 * SEQUENCE {
529 * name ObjectName
530 * CHOICE {
531 * value ObjectSyntax
532 * unSpecified NULL
533 * noSuchObject[0] NULL
534 * noSuchInstance[1] NULL
535 * endOfMibView[2] NULL
536 * }
537 * }
538 */
snmp_smi_write_vars(pool * p,unsigned char ** buf,size_t * buflen,struct snmp_var * varlist,int snmp_version)539 int snmp_smi_write_vars(pool *p, unsigned char **buf, size_t *buflen,
540 struct snmp_var *varlist, int snmp_version) {
541 struct snmp_var *iter;
542 unsigned char asn1_type, *list_hdr_start, *list_hdr_end;
543 size_t list_hdr_startlen;
544 unsigned int asn1_len;
545 int res;
546
547 /* Write the header for the varlist. */
548 asn1_type = (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT);
549 asn1_len = 0;
550
551 list_hdr_start = *buf;
552 list_hdr_startlen = *buflen;
553
554 res = snmp_asn1_write_header(p, buf, buflen, asn1_type, asn1_len, 0);
555 if (res < 0) {
556 return -1;
557 }
558
559 list_hdr_end = *buf;
560
561 for (iter = varlist; iter; iter = iter->next) {
562 unsigned char *var_hdr_start = NULL, *var_hdr_end = NULL;
563 size_t var_hdr_startlen;
564
565 pr_signals_handle();
566
567 /* Write the header for this variable. */
568 asn1_type = (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT);
569 asn1_len = 0;
570
571 var_hdr_start = *buf;
572 var_hdr_startlen = *buflen;
573
574 res = snmp_asn1_write_header(p, buf, buflen, asn1_type, asn1_len, 0);
575 if (res < 0) {
576 return -1;
577 }
578
579 var_hdr_end = *buf;
580
581 asn1_type = (SNMP_ASN1_CLASS_UNIVERSAL|SNMP_ASN1_PRIMITIVE|SNMP_ASN1_TYPE_OID);
582 res = snmp_asn1_write_oid(p, buf, buflen, asn1_type, iter->name,
583 iter->namelen);
584 if (res < 0) {
585 return -1;
586 }
587
588 switch (iter->smi_type) {
589 case SNMP_SMI_INTEGER:
590 res = snmp_asn1_write_int(p, buf, buflen, iter->smi_type,
591 *((long *) iter->value.integer), 0);
592 break;
593
594 case SNMP_SMI_COUNTER32:
595 case SNMP_SMI_GAUGE32:
596 case SNMP_SMI_TIMETICKS:
597 res = snmp_asn1_write_uint(p, buf, buflen, iter->smi_type,
598 *((unsigned long *) iter->value.integer));
599 break;
600
601 case SNMP_SMI_STRING:
602 case SNMP_SMI_IPADDR:
603 case SNMP_SMI_OPAQUE:
604 res = snmp_asn1_write_string(p, buf, buflen, iter->smi_type,
605 iter->value.string, iter->valuelen);
606 break;
607
608 case SNMP_SMI_OID:
609 res = snmp_asn1_write_oid(p, buf, buflen, iter->smi_type,
610 iter->value.oid, iter->valuelen);
611 break;
612
613 case SNMP_SMI_NO_SUCH_OBJECT:
614 case SNMP_SMI_NO_SUCH_INSTANCE:
615 case SNMP_SMI_END_OF_MIB_VIEW:
616 if (snmp_version == SNMP_PROTOCOL_VERSION_1) {
617 /* SNMPv1 does not support the other error codes. */
618 res = snmp_asn1_write_null(p, buf, buflen, SNMP_SMI_NO_SUCH_OBJECT);
619
620 } else {
621 res = snmp_asn1_write_exception(p, buf, buflen, iter->smi_type, 0);
622 }
623
624 break;
625
626 case SNMP_SMI_NULL:
627 res = snmp_asn1_write_null(p, buf, buflen, iter->smi_type);
628 break;
629
630 case SNMP_SMI_COUNTER64:
631 pr_trace_msg(trace_channel, 1, "%s",
632 "unable to encode COUNTER64 SMI variable");
633 /* fall through */
634
635 default:
636 /* Unsupported type */
637 pr_trace_msg(trace_channel, 1, "%s",
638 "unable to encode unsupported SMI variable type");
639 pr_log_stacktrace(snmp_logfd, MOD_SNMP_VERSION);
640 errno = ENOSYS;
641 return -1;
642 }
643
644 if (res < 0) {
645 return -1;
646 }
647
648 /* Rewrite the header, this time with the appropriate length. */
649 asn1_type = (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT);
650 asn1_len = (*buf - var_hdr_end);
651
652 pr_trace_msg(trace_channel, 18,
653 "updating variable header to have length %u", asn1_len);
654 res = snmp_asn1_write_header(p, &var_hdr_start, &var_hdr_startlen,
655 asn1_type, asn1_len, 0);
656 if (res < 0) {
657 return -1;
658 }
659 }
660
661 /* Rewrite the varlist header, this time with the length of all of the
662 * variables.
663 */
664
665 asn1_type = (SNMP_ASN1_TYPE_SEQUENCE|SNMP_ASN1_CONSTRUCT);
666 asn1_len = (*buf - list_hdr_end);
667
668 pr_trace_msg(trace_channel, 18,
669 "updating variable bindings list header to have length %u", asn1_len);
670 res = snmp_asn1_write_header(p, &list_hdr_start, &list_hdr_startlen,
671 asn1_type, asn1_len, 0);
672 if (res < 0) {
673 return -1;
674 }
675
676 return 0;
677 }
678
snmp_smi_util_add_list_var(struct snmp_var ** head,struct snmp_var ** tail,struct snmp_var * var)679 unsigned int snmp_smi_util_add_list_var(struct snmp_var **head,
680 struct snmp_var **tail, struct snmp_var *var) {
681 unsigned int count = 0;
682 struct snmp_var *iter_var;
683
684 if (*head == NULL) {
685 *head = var;
686 }
687
688 if (*tail != NULL) {
689 (*tail)->next = var;
690 }
691
692 (*tail) = var;
693
694 for (iter_var = *head; iter_var; iter_var = iter_var->next) {
695 count++;
696 }
697
698 return count;
699 }
700
701