4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
30 * SNMP PDU and packet transport related routines
36 #include <sys/types.h>
44 static int snmp_add_null_vars(snmp_pdu_t
*, char *, int, int);
45 static oid
*snmp_oidstr_to_oid(int, char *, int, size_t *);
46 static uchar_t
*snmp_build_pdu(snmp_pdu_t
*, uchar_t
*, size_t *);
47 static uchar_t
*snmp_build_variable(uchar_t
*, size_t *, oid
*, size_t,
48 uchar_t
, void *, size_t);
49 static uchar_t
*snmp_parse_pdu(int, uchar_t
*, size_t *, snmp_pdu_t
*);
50 static uchar_t
*snmp_parse_variable(uchar_t
*, size_t *, pdu_varlist_t
*);
51 static void snmp_free_null_vars(pdu_varlist_t
*);
53 static uchar_t
*snmp_def_community
= (uchar_t
*)SNMP_DEF_COMMUNITY
;
56 * Allocates and creates a PDU for the specified SNMP command. Currently
57 * only SNMP_MSG_GET, SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK are supported
60 snmp_create_pdu(int cmd
, int max_reps
, char *oidstrs
, int n_oids
, int row
)
64 if ((cmd
!= SNMP_MSG_GET
) && (cmd
!= SNMP_MSG_GETNEXT
) &&
65 (cmd
!= SNMP_MSG_GETBULK
)) {
69 pdu
= (snmp_pdu_t
*)calloc(1, sizeof (snmp_pdu_t
));
73 if (cmd
== SNMP_MSG_GET
|| cmd
== SNMP_MSG_GETNEXT
) {
74 pdu
->version
= SNMP_VERSION_1
;
77 } else if (cmd
== SNMP_MSG_GETBULK
) {
78 pdu
->version
= SNMP_VERSION_2c
;
79 pdu
->non_repeaters
= 0;
80 pdu
->max_repetitions
= max_reps
?
81 max_reps
: SNMP_DEF_MAX_REPETITIONS
;
85 pdu
->reqid
= snmp_get_reqid();
86 pdu
->community
= snmp_def_community
;
87 pdu
->community_len
= SNMP_DEF_COMMUNITY_LEN
;
89 if (snmp_add_null_vars(pdu
, oidstrs
, n_oids
, row
) < 0) {
96 pdu
->reply_pkt
= NULL
;
103 * Builds a complete ASN.1 encoded snmp message packet out of the PDU.
104 * Currently the maximum request packet is limited to SNMP_DEF_PKTBUF_SZ.
105 * Since we only send SNMP_MSG_GET, SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK,
106 * as long as the number of bulk oids are not *too* many, we're safe with
107 * this limit (the typical packet size of a bulk request of 10 vars is
111 snmp_make_packet(snmp_pdu_t
*pdu
)
114 uchar_t
*msg_seq_end
;
116 size_t bufsz
= SNMP_DEF_PKTBUF_SZ
;
119 if ((buf
= (uchar_t
*)calloc(1, SNMP_DEF_PKTBUF_SZ
)) == NULL
)
123 * Let's start with the ASN sequence tag. Set the length
124 * to 0 initially and fill it up once the message packetizing
127 id
= ASN_UNIVERSAL
| ASN_CONSTRUCTOR
| ASN_SEQUENCE
;
128 if ((p
= asn_build_sequence(buf
, &bufsz
, id
, 0)) == NULL
) {
137 id
= ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_INTEGER
;
138 if ((p
= asn_build_int(p
, &bufsz
, id
, pdu
->version
)) == NULL
) {
144 * Store the community string
146 id
= ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_OCTET_STR
;
147 p
= asn_build_string(p
, &bufsz
, id
, pdu
->community
, pdu
->community_len
);
156 if ((p
= snmp_build_pdu(pdu
, p
, &bufsz
)) == NULL
) {
162 * Complete the message pkt by updating the message sequence length
164 seqlen
= p
- msg_seq_end
;
165 id
= ASN_UNIVERSAL
| ASN_CONSTRUCTOR
| ASN_SEQUENCE
;
166 (void) asn_build_sequence(buf
, NULL
, id
, seqlen
);
169 * Calculate packet size and return
172 pdu
->req_pktsz
= p
- buf
;
178 * Makes a PDU out of a reply packet. The reply message is parsed
179 * and if the reqid of the incoming packet does not match the reqid
180 * we're waiting for, an error is returned. The PDU is allocated
181 * inside this routine and must be freed by the caller once it is no
185 snmp_parse_reply(int reqid
, uchar_t
*reply_pkt
, size_t reply_pktsz
)
187 snmp_pdu_t
*reply_pdu
;
189 size_t msgsz
= reply_pktsz
;
192 reply_pdu
= (snmp_pdu_t
*)calloc(1, sizeof (snmp_pdu_t
));
193 if (reply_pdu
== NULL
)
197 * Try to parse the ASN sequence out of the beginning of the reply
198 * packet. If we don't find a sequence at the beginning, something's
201 exp_id
= ASN_UNIVERSAL
| ASN_CONSTRUCTOR
| ASN_SEQUENCE
;
202 if ((p
= asn_parse_sequence(reply_pkt
, &msgsz
, exp_id
)) == NULL
) {
203 snmp_free_pdu(reply_pdu
);
208 * Now try to parse the version out of the packet
210 if ((p
= asn_parse_int(p
, &msgsz
, &reply_pdu
->version
)) == NULL
) {
211 snmp_free_pdu(reply_pdu
);
214 if ((reply_pdu
->version
!= SNMP_VERSION_1
) &&
215 (reply_pdu
->version
!= SNMP_VERSION_2c
)) {
216 snmp_free_pdu(reply_pdu
);
221 * Parse the community string (space allocated by asn_parse_string)
223 p
= asn_parse_string(p
, &msgsz
, &reply_pdu
->community
,
224 &reply_pdu
->community_len
);
226 snmp_free_pdu(reply_pdu
);
231 * Parse the PDU part of the message
233 if ((p
= snmp_parse_pdu(reqid
, p
, &msgsz
, reply_pdu
)) == NULL
) {
234 snmp_free_pdu(reply_pdu
);
243 * Convert the OID strings into the standard PDU oid form (sequence of
244 * integer subids) and add them to the PDU's variable list. Note that
245 * this is used only for preparing the request messages (GET, GETNEXT
246 * and GETBULK), so the values of the variables are always null.
249 snmp_add_null_vars(snmp_pdu_t
*pdu
, char *oidstrs
, int n_oids
, int row
)
251 pdu_varlist_t
*vp
, *prev
;
252 pdu_varlist_t
*varblock_p
= NULL
;
258 for (i
= 0; i
< n_oids
; i
++) {
259 if ((vp
= calloc(1, sizeof (pdu_varlist_t
))) == NULL
) {
260 snmp_free_null_vars(varblock_p
);
268 vp
->name
= snmp_oidstr_to_oid(pdu
->command
,
269 p
, row
, &vp
->name_len
);
270 if (vp
->name
== NULL
) {
271 snmp_free_null_vars(varblock_p
);
279 LOGVAR(TAG_NULL_VAR
, vp
);
286 * append the varlist to the PDU
288 if (pdu
->vars
== NULL
)
289 pdu
->vars
= varblock_p
;
291 for (vp
= pdu
->vars
; vp
->nextvar
; vp
= vp
->nextvar
)
293 vp
->nextvar
= varblock_p
;
300 * Some assumptions are in place here to eliminate unnecessary complexity.
301 * All OID strings passed are assumed to be in the numeric string form, have
302 * no leading/trailing '.' or spaces. Since PICL plugin is currently the
303 * only customer, this is quite reasonable.
306 snmp_oidstr_to_oid(int cmd
, char *oidstr
, int row
, size_t *n_subids
)
313 if ((oidstr
== NULL
) || (n_subids
== NULL
))
316 for (count
= 1, p
= oidstr
; p
; count
++, p
++) {
317 if ((p
= strchr(p
, '.')) == NULL
)
322 * Add one more to count for 'row'. Need special processing
323 * for SNMP_MSG_GETNEXT and SNMP_MSG_GETBULK requests; see
326 if ((cmd
== SNMP_MSG_GET
) || (cmd
== SNMP_MSG_GETBULK
&& row
> 0) ||
327 (cmd
== SNMP_MSG_GETNEXT
&& row
>= 0)) {
331 if ((oidstr_dup
= strdup(oidstr
)) == NULL
)
334 objid
= (oid
*) calloc(count
, sizeof (oid
));
341 for (i
= 0; i
< count
- 1; i
++) {
342 if (q
= strchr(p
, '.'))
344 objid
[i
] = (oid
) strtoul(p
, NULL
, 10);
349 * For SNMP_MSG_GET, the leaf subid will simply be the row#.
351 * For SNMP_MSG_GETBULK, if the row# passed is greater than 0,
352 * we pass 'row-1' as the leaf subid, to include the item that
353 * is of interest to us. If the row# is less than or equal to 0,
354 * we will simply ignore it and pass only the prefix part of the
355 * oidstr. For this case, our count would have been 1 less than
356 * usual, and we are yet to save the last subid.
358 * For SNMP_MSG_GETNEXT, if the row# passed is less than 0,
359 * we'll simply ignore it and pass only the prefix part of the
360 * oidstr. For this case, our count would have been 1 less than
361 * usual, and we are yet to save the last subid. If the row#
362 * passed is greater than or equal to 0, we'll simply pass it
363 * verbatim, as the leaf subid.
367 objid
[i
] = (oid
) row
;
370 case SNMP_MSG_GETBULK
:
372 objid
[i
] = (oid
) (row
- 1);
374 objid
[i
] = (oid
) strtoul(p
, NULL
, 10);
377 case SNMP_MSG_GETNEXT
:
379 objid
[i
] = (oid
) strtoul(p
, NULL
, 10);
381 objid
[i
] = (oid
) row
;
387 free((void *) oidstr_dup
);
393 * Builds the PDU part of the snmp message packet.
396 snmp_build_pdu(snmp_pdu_t
*pdu
, uchar_t
*buf
, size_t *bufsz_p
)
399 uchar_t
*pdu_seq_begin
, *pdu_seq_end
;
400 uchar_t
*varlist_seq_begin
, *varlist_seq_end
;
406 * Build ASN sequence for the PDU command (length will be
407 * updated later once the entire command is completely formed)
410 p
= asn_build_sequence(buf
, bufsz_p
, (uchar_t
)pdu
->command
, 0);
416 * Build the request id
418 id
= ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_INTEGER
;
419 if ((p
= asn_build_int(p
, bufsz_p
, id
, pdu
->reqid
)) == NULL
)
423 * Build the non-repeaters and max-repetitions for SNMP_MSG_GETBULK
424 * (same as error status and error index for other message types)
426 id
= ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_INTEGER
;
427 if ((p
= asn_build_int(p
, bufsz_p
, id
, pdu
->non_repeaters
)) == NULL
)
430 id
= ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_INTEGER
;
431 if ((p
= asn_build_int(p
, bufsz_p
, id
, pdu
->max_repetitions
)) == NULL
)
435 * Build ASN sequence for the variables list (update length
436 * after building the varlist)
438 varlist_seq_begin
= p
;
439 id
= ASN_UNIVERSAL
| ASN_CONSTRUCTOR
| ASN_SEQUENCE
;
440 if ((p
= asn_build_sequence(p
, bufsz_p
, id
, 0)) == NULL
)
445 * Build the variables list
447 for (vp
= pdu
->vars
; vp
; vp
= vp
->nextvar
) {
448 p
= snmp_build_variable(p
, bufsz_p
, vp
->name
, vp
->name_len
,
449 vp
->type
, vp
->val
.str
, vp
->val_len
);
455 * Now update the varlist sequence length
457 seqlen
= p
- varlist_seq_end
;
458 id
= ASN_UNIVERSAL
| ASN_CONSTRUCTOR
| ASN_SEQUENCE
;
459 (void) asn_build_sequence(varlist_seq_begin
, NULL
, id
, seqlen
);
462 * And finally, update the length for the PDU sequence
464 seqlen
= p
- pdu_seq_end
;
465 (void) asn_build_sequence(pdu_seq_begin
, NULL
, (uchar_t
)pdu
->command
,
472 * Builds an object variable into the snmp message packet. Although the
473 * code is here to build variables of basic types such as integer, object id
474 * and strings, the only type of variable we ever send via snmp request
475 * messages is the ASN_NULL type.
478 snmp_build_variable(uchar_t
*buf
, size_t *bufsz_p
, oid
*name
, size_t name_len
,
479 uchar_t val_type
, void *val
, size_t val_len
)
481 uchar_t
*p
, *varseq_end
;
486 * Each variable binding is in turn defined as a 'SEQUENCE of' by
487 * the SNMP PDU format, so we'll prepare the sequence and fill up
488 * the length later. Sigh!
490 id
= ASN_UNIVERSAL
| ASN_CONSTRUCTOR
| ASN_SEQUENCE
;
491 if ((p
= asn_build_sequence(buf
, bufsz_p
, id
, 0)) == NULL
)
496 * Build the object id
498 id
= ASN_UNIVERSAL
| ASN_PRIMITIVE
| ASN_OBJECT_ID
;
499 if ((p
= asn_build_objid(p
, bufsz_p
, id
, name
, name_len
)) == NULL
)
503 * Currently we only ever build ASN_NULL vars while sending requests,
504 * since we support only SNMP_MSG_GET, SNMP_MSG_GETNEXT and
507 id
= ASN_UNIVERSAL
| ASN_PRIMITIVE
| val_type
;
510 p
= asn_build_int(p
, bufsz_p
, id
, *((int *)val
));
516 p
= asn_build_objid(p
, bufsz_p
, id
, val
,
517 val_len
/ sizeof (oid
));
523 p
= asn_build_string(p
, bufsz_p
, id
, (uchar_t
*)val
, val_len
);
529 if ((p
= asn_build_null(p
, bufsz_p
, id
)) == NULL
)
538 * Rebuild the variable sequence length
540 seqlen
= p
- varseq_end
;
541 id
= ASN_UNIVERSAL
| ASN_CONSTRUCTOR
| ASN_SEQUENCE
;
542 (void) asn_build_sequence(buf
, NULL
, id
, seqlen
);
548 * Parse the PDU portion of the incoming snmp message into the reply_pdu.
549 * Space for all structure members are allocated as needed and must be freed
550 * by the caller when these are no longer needed.
553 snmp_parse_pdu(int reqid
, uchar_t
*msg
, size_t *msgsz_p
, snmp_pdu_t
*reply_pdu
)
557 pdu_varlist_t
*newvp
, *vp
= NULL
;
560 * Parse the PDU header out of the message
562 if ((p
= asn_parse_header(msg
, msgsz_p
, &id
)) == NULL
)
564 if (id
!= SNMP_MSG_RESPONSE
&& id
!= SNMP_MSG_REPORT
)
566 reply_pdu
->command
= (int)id
;
569 * Parse the request id and verify that this is the response
572 if ((p
= asn_parse_int(p
, msgsz_p
, &reply_pdu
->reqid
)) == NULL
)
574 if (reply_pdu
->reqid
!= reqid
)
578 * Parse the error-status and error-index values
580 if ((p
= asn_parse_int(p
, msgsz_p
, &reply_pdu
->errstat
)) == NULL
)
582 if ((p
= asn_parse_int(p
, msgsz_p
, &reply_pdu
->errindex
)) == NULL
)
586 * Parse the header for the variables list sequence.
588 exp_id
= ASN_UNIVERSAL
| ASN_CONSTRUCTOR
| ASN_SEQUENCE
;
589 if ((p
= asn_parse_sequence(p
, msgsz_p
, exp_id
)) == NULL
)
592 while (((int)*msgsz_p
) > 0) {
593 if ((newvp
= calloc(1, sizeof (pdu_varlist_t
))) == NULL
)
597 reply_pdu
->vars
= newvp
;
602 if ((p
= snmp_parse_variable(p
, msgsz_p
, vp
)) == NULL
)
605 LOGVAR(TAG_RESPONSE_VAR
, vp
);
612 * Allocate and parse the next variable into the varlist
615 snmp_parse_variable(uchar_t
*msg
, size_t *msgsz_p
, pdu_varlist_t
*vp
)
621 * Parse this variable's sequence
623 exp_id
= ASN_UNIVERSAL
| ASN_CONSTRUCTOR
| ASN_SEQUENCE
;
624 if ((p
= asn_parse_sequence(msg
, msgsz_p
, exp_id
)) == NULL
)
628 * Parse the variable's object identifier
630 p
= asn_parse_objid(p
, msgsz_p
, &vp
->name
, &vp
->name_len
);
635 * Parse the object's value
637 if ((p
= asn_parse_objval(p
, msgsz_p
, vp
)) == NULL
)
644 snmp_free_pdu(snmp_pdu_t
*pdu
)
646 pdu_varlist_t
*vp
, *nxt
;
649 if ((pdu
->community
) && (pdu
->community
!= snmp_def_community
))
650 free((void *) pdu
->community
);
652 for (vp
= pdu
->vars
; vp
; vp
= nxt
) {
656 free((void *) vp
->name
);
658 free((void *) vp
->val
.str
);
663 free((void *) pdu
->req_pkt
);
666 free((void *) pdu
->reply_pkt
);
673 snmp_free_null_vars(pdu_varlist_t
*varblock_p
)
675 pdu_varlist_t
*vp
, *nxt
;
677 for (vp
= varblock_p
; vp
; vp
= nxt
) {