Carefully taking some first looks into readding agentx master support to
snmpd. One of the things we're going to need is a transaction id as per
RFC2741 section 6.1 subsection h.transactionID:
The transaction ID uniquely identifies, for a given session,
the single SNMP management request (and single SNMP PDU)
with which an AgentX PDU is associated. If a single SNMP
management request results in multiple AgentX PDUs being
sent by the master agent with the same session ID, each of
these AgentX PDUs must contain the same transaction ID;
conversely, AgentX PDUs sent during a particular session,
that result from distinct SNMP management requests, must
have distinct transaction IDs within the limits of the 32-
bit field).
Note that the transaction ID is not the same as the SNMP
PDU's request-id (as described in section 4.1 of RFC 1905
[13], nor is it the same as the SNMP Message's msgID (as
described in section 6.2 of RFC 2572 [11]), nor can it be,
since a master agent might receive SNMP requests with the
same request-ids or msgIDs from different managers.
The transaction ID has no significance and no defined value
in AgentX administrative PDUs, i.e., AgentX PDUs that are
not associated with an SNMP management request.
Our previous implementation took the transaction id from the agentx
context and incremented it by one for every request, which is just
plain wrong.
OK?
martijn@
Index: snmpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpd.h,v
retrieving revision 1.93
diff -u -p -r1.93 snmpd.h
--- snmpd.h 28 Jan 2021 20:45:14 -0000 1.93
+++ snmpd.h 2 Feb 2021 12:23:37 -0000
@@ -399,6 +399,8 @@ struct snmp_message {
u_int8_t sm_data[READ_BUF_SIZE];
size_t sm_datalen;
+ uint32_t sm_transactionid;
+
u_int sm_version;
/* V1, V2c */
@@ -436,7 +438,11 @@ struct snmp_message {
struct ber_element *sm_varbind;
struct ber_element *sm_varbindresp;
+
+ RB_ENTRY(snmp_message) sm_entry;
};
+RB_HEAD(snmp_messages, snmp_message);
+extern struct snmp_messages snmp_messages;
/* Defined in SNMPv2-MIB.txt (RFC 3418) */
struct snmp_stats {
@@ -642,6 +648,8 @@ struct kif_arp *karp_getaddr(struct sock
void snmpe(struct privsep *, struct privsep_proc *);
void snmpe_shutdown(void);
void snmpe_dispatchmsg(struct snmp_message *);
+int snmp_messagecmp(struct snmp_message *, struct snmp_message *);
+RB_PROTOTYPE(snmp_messages, snmp_message, sm_entry, snmp_messagecmp)
/* trap.c */
void trap_init(void);
Index: snmpe.c
===================================================================
RCS file: /cvs/src/usr.sbin/snmpd/snmpe.c,v
retrieving revision 1.68
diff -u -p -r1.68 snmpe.c
--- snmpe.c 22 Jan 2021 06:33:27 -0000 1.68
+++ snmpe.c 2 Feb 2021 12:23:37 -0000
@@ -58,6 +58,8 @@ void snmp_msgfree(struct snmp_message *
struct imsgev *iev_parent;
static const struct timeval snmpe_tcp_timeout = { 10, 0 }; /* 10s */
+struct snmp_messages snmp_messages = RB_INITIALIZER(&snmp_messages);
+
static struct privsep_proc procs[] = {
{ "parent", PROC_PARENT }
};
@@ -210,6 +212,11 @@ snmpe_parse(struct snmp_message *msg)
msg->sm_errstr = "invalid message";
+ do {
+ msg->sm_transactionid = arc4random();
+ } while (msg->sm_transactionid == 0 ||
+ RB_FIND(snmp_messages, &snmp_messages, msg) != NULL);
+
if (ober_scanf_elements(root, "{ie", &ver, &a) != 0)
goto parsefail;
@@ -834,6 +841,8 @@ snmpe_response(struct snmp_message *msg)
void
snmp_msgfree(struct snmp_message *msg)
{
+ if (msg->sm_transactionid != 0)
+ RB_REMOVE(snmp_messages, &snmp_messages, msg);
event_del(&msg->sm_sockev);
ober_free(&msg->sm_ber);
if (msg->sm_req != NULL)
@@ -896,3 +905,12 @@ snmpe_encode(struct snmp_message *msg)
#endif
return 0;
}
+
+int
+snmp_messagecmp(struct snmp_message *m1, struct snmp_message *m2)
+{
+ return (m1->sm_transactionid < m2->sm_transactionid ? -1 :
+ m1->sm_transactionid > m2->sm_transactionid);
+}
+
+RB_GENERATE(snmp_messages, snmp_message, sm_entry, snmp_messagecmp)