1 /*
2  * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 49    SNMP support */
10 
11 #include "squid.h"
12 #include "acl/FilledChecklist.h"
13 #include "base/CbcPointer.h"
14 #include "CachePeer.h"
15 #include "client_db.h"
16 #include "comm.h"
17 #include "comm/Connection.h"
18 #include "comm/Loops.h"
19 #include "comm/UdpOpenDialer.h"
20 #include "fatal.h"
21 #include "ip/Address.h"
22 #include "ip/tools.h"
23 #include "snmp/Forwarder.h"
24 #include "snmp_agent.h"
25 #include "snmp_core.h"
26 #include "SnmpRequest.h"
27 #include "SquidConfig.h"
28 #include "tools.h"
29 
30 static void snmpPortOpened(const Comm::ConnectionPointer &conn, int errNo);
31 
32 mib_tree_entry *mib_tree_head;
33 mib_tree_entry *mib_tree_last;
34 
35 Comm::ConnectionPointer snmpIncomingConn;
36 Comm::ConnectionPointer snmpOutgoingConn;
37 
38 static mib_tree_entry * snmpAddNodeStr(const char *base_str, int o, oid_ParseFn * parsefunction, instance_Fn * instancefunction, AggrType aggrType = atNone);
39 static mib_tree_entry *snmpAddNode(oid * name, int len, oid_ParseFn * parsefunction, instance_Fn * instancefunction, AggrType aggrType, int children,...);
40 static oid *snmpCreateOid(int length,...);
41 mib_tree_entry * snmpLookupNodeStr(mib_tree_entry *entry, const char *str);
42 bool snmpCreateOidFromStr(const char *str, oid **name, int *nl);
43 SQUIDCEXTERN void (*snmplib_debug_hook) (int, char *);
44 static oid *static_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
45 static oid *time_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
46 static oid *peer_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
47 static oid *client_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn);
48 static void snmpDecodePacket(SnmpRequest * rq);
49 static void snmpConstructReponse(SnmpRequest * rq);
50 
51 static oid_ParseFn *snmpTreeNext(oid * Current, snint CurrentLen, oid ** Next, snint * NextLen);
52 static oid_ParseFn *snmpTreeGet(oid * Current, snint CurrentLen);
53 static mib_tree_entry *snmpTreeEntry(oid entry, snint len, mib_tree_entry * current);
54 static mib_tree_entry *snmpTreeSiblingEntry(oid entry, snint len, mib_tree_entry * current);
55 extern "C" void snmpSnmplibDebug(int lvl, char *buf);
56 
57 /*
58  * The functions used during startup:
59  * snmpInit
60  * snmpConnectionOpen
61  * snmpConnectionClose
62  */
63 
64 /*
65  * Turns the MIB into a Tree structure. Called during the startup process.
66  */
67 void
snmpInit(void)68 snmpInit(void)
69 {
70     debugs(49, 5, "snmpInit: Building SNMP mib tree structure");
71 
72     snmplib_debug_hook = snmpSnmplibDebug;
73 
74     /*
75      * This following bit of evil is to get the final node in the "squid" mib
76      * without having a "search" function. A search function should be written
77      * to make this and the other code much less evil.
78      */
79     mib_tree_head = snmpAddNode(snmpCreateOid(1, 1), 1, NULL, NULL, atNone, 0);
80 
81     assert(mib_tree_head);
82     debugs(49, 5, "snmpInit: root is " << mib_tree_head);
83     snmpAddNodeStr("1", 3, NULL, NULL);
84 
85     snmpAddNodeStr("1.3", 6, NULL, NULL);
86 
87     snmpAddNodeStr("1.3.6", 1, NULL, NULL);
88     snmpAddNodeStr("1.3.6.1", 4, NULL, NULL);
89     snmpAddNodeStr("1.3.6.1.4", 1, NULL, NULL);
90     snmpAddNodeStr("1.3.6.1.4.1", 3495, NULL, NULL);
91     mib_tree_entry *m2 = snmpAddNodeStr("1.3.6.1.4.1.3495", 1, NULL, NULL);
92 
93     mib_tree_entry *n = snmpLookupNodeStr(NULL, "1.3.6.1.4.1.3495.1");
94     assert(m2 == n);
95 
96     /* SQ_SYS - 1.3.6.1.4.1.3495.1.1 */
97     snmpAddNodeStr("1.3.6.1.4.1.3495.1", 1, NULL, NULL);
98     snmpAddNodeStr("1.3.6.1.4.1.3495.1.1", SYSVMSIZ, snmp_sysFn, static_Inst, atSum);
99     snmpAddNodeStr("1.3.6.1.4.1.3495.1.1", SYSSTOR, snmp_sysFn, static_Inst, atSum);
100     snmpAddNodeStr("1.3.6.1.4.1.3495.1.1", SYS_UPTIME, snmp_sysFn, static_Inst, atMax);
101 
102     /* SQ_CONF - 1.3.6.1.4.1.3495.1.2 */
103     snmpAddNodeStr("1.3.6.1.4.1.3495.1", 2, NULL, NULL);
104     snmpAddNodeStr("1.3.6.1.4.1.3495.1.2", CONF_ADMIN, snmp_confFn, static_Inst);
105     snmpAddNodeStr("1.3.6.1.4.1.3495.1.2", CONF_VERSION, snmp_confFn, static_Inst);
106     snmpAddNodeStr("1.3.6.1.4.1.3495.1.2", CONF_VERSION_ID, snmp_confFn, static_Inst);
107     snmpAddNodeStr("1.3.6.1.4.1.3495.1.2", CONF_LOG_FAC, snmp_confFn, static_Inst);
108 
109     /* SQ_CONF + CONF_STORAGE - 1.3.6.1.4.1.3495.1.5 */
110     snmpAddNodeStr("1.3.6.1.4.1.3495.1.2", CONF_STORAGE, NULL, NULL);
111     snmpAddNodeStr("1.3.6.1.4.1.3495.1.2.5", CONF_ST_MMAXSZ, snmp_confFn, static_Inst, atSum);
112     snmpAddNodeStr("1.3.6.1.4.1.3495.1.2.5", CONF_ST_SWMAXSZ, snmp_confFn, static_Inst, atSum);
113     snmpAddNodeStr("1.3.6.1.4.1.3495.1.2.5", CONF_ST_SWHIWM, snmp_confFn, static_Inst, atMin);
114     snmpAddNodeStr("1.3.6.1.4.1.3495.1.2.5", CONF_ST_SWLOWM, snmp_confFn, static_Inst, atMin);
115 
116     snmpAddNodeStr("1.3.6.1.4.1.3495.1.2", CONF_UNIQNAME, snmp_confFn, static_Inst);
117 
118     /* SQ_PRF - 1.3.6.1.4.1.3495.1.3 */
119     snmpAddNodeStr("1.3.6.1.4.1.3495.1", 3, NULL, NULL);                    /* SQ_PRF */
120 
121     /* PERF_SYS - 1.3.6.1.4.1.3495.1.3.1 */
122     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3", PERF_SYS, NULL, NULL);
123     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.1", PERF_SYS_PF, snmp_prfSysFn, static_Inst, atSum);
124     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.1", PERF_SYS_NUMR, snmp_prfSysFn, static_Inst, atSum);
125     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.1", PERF_SYS_MEMUSAGE, snmp_prfSysFn, static_Inst, atSum);
126     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.1", PERF_SYS_CPUTIME, snmp_prfSysFn, static_Inst, atSum);
127     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.1", PERF_SYS_CPUUSAGE, snmp_prfSysFn, static_Inst, atSum);
128     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.1", PERF_SYS_MAXRESSZ, snmp_prfSysFn, static_Inst, atSum);
129     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.1", PERF_SYS_NUMOBJCNT, snmp_prfSysFn, static_Inst, atSum);
130     /*
131       Amos comments:
132       The meaning of LRU is "oldest timestamped object in cache,  if LRU algorithm is
133       used"...
134       What this SMP support needs to do is aggregate via a special filter equivalent to
135       min() to retain the semantic oldest-object meaning. A special one is needed that
136       works as unsigned and ignores '0' values.
137      */
138     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.1", PERF_SYS_CURLRUEXP, snmp_prfSysFn, static_Inst);
139     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.1", PERF_SYS_CURUNLREQ, snmp_prfSysFn, static_Inst, atSum);
140     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.1", PERF_SYS_CURUNUSED_FD, snmp_prfSysFn, static_Inst, atSum);
141     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.1", PERF_SYS_CURRESERVED_FD, snmp_prfSysFn, static_Inst, atSum);
142     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.1", PERF_SYS_CURUSED_FD, snmp_prfSysFn, static_Inst, atSum);
143     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.1", PERF_SYS_CURMAX_FD, snmp_prfSysFn, static_Inst, atMax);
144 
145     /* PERF_PROTO - 1.3.6.1.4.1.3495.1.3.2 */
146     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3", PERF_PROTO, NULL, NULL);
147     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2", PERF_PROTOSTAT_AGGR, NULL, NULL);
148     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_HTTP_REQ, snmp_prfProtoFn, static_Inst, atSum);
149     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_HTTP_HITS, snmp_prfProtoFn, static_Inst, atSum);
150     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_HTTP_ERRORS, snmp_prfProtoFn, static_Inst, atSum);
151     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_HTTP_KBYTES_IN, snmp_prfProtoFn, static_Inst, atSum);
152     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_HTTP_KBYTES_OUT, snmp_prfProtoFn, static_Inst, atSum);
153     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_ICP_S, snmp_prfProtoFn, static_Inst, atSum);
154     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_ICP_R, snmp_prfProtoFn, static_Inst, atSum);
155     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_ICP_SKB, snmp_prfProtoFn, static_Inst, atSum);
156     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_ICP_RKB, snmp_prfProtoFn, static_Inst, atSum);
157     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_REQ, snmp_prfProtoFn, static_Inst, atSum);
158     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_ERRORS, snmp_prfProtoFn, static_Inst, atSum);
159     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_KBYTES_IN, snmp_prfProtoFn, static_Inst, atSum);
160     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_KBYTES_OUT, snmp_prfProtoFn, static_Inst, atSum);
161     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_CURSWAP, snmp_prfProtoFn, static_Inst, atSum);
162     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.1", PERF_PROTOSTAT_AGGR_CLIENTS, snmp_prfProtoFn, static_Inst, atSum);
163 
164     /* Note this is time-series rather than 'static' */
165     /* cacheMedianSvcTable */
166     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2", PERF_PROTOSTAT_MEDIAN, NULL, NULL);
167 
168     /* cacheMedianSvcEntry */
169     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.2", 1, NULL, NULL);
170     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.2.1", PERF_MEDIAN_TIME, snmp_prfProtoFn, time_Inst, atAverage);
171     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.2.1", PERF_MEDIAN_HTTP_ALL, snmp_prfProtoFn, time_Inst, atAverage);
172     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.2.1", PERF_MEDIAN_HTTP_MISS, snmp_prfProtoFn, time_Inst, atAverage);
173     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.2.1", PERF_MEDIAN_HTTP_NM, snmp_prfProtoFn, time_Inst, atAverage);
174     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.2.1", PERF_MEDIAN_HTTP_HIT, snmp_prfProtoFn, time_Inst, atAverage);
175     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.2.1", PERF_MEDIAN_ICP_QUERY, snmp_prfProtoFn, time_Inst, atAverage);
176     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.2.1", PERF_MEDIAN_ICP_REPLY, snmp_prfProtoFn, time_Inst, atAverage);
177     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.2.1", PERF_MEDIAN_DNS, snmp_prfProtoFn, time_Inst, atAverage);
178     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.2.1", PERF_MEDIAN_RHR, snmp_prfProtoFn, time_Inst, atAverage);
179     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.2.1", PERF_MEDIAN_BHR, snmp_prfProtoFn, time_Inst, atAverage);
180     snmpAddNodeStr("1.3.6.1.4.1.3495.1.3.2.2.1", PERF_MEDIAN_HTTP_NH, snmp_prfProtoFn, time_Inst, atAverage);
181 
182     /* SQ_NET - 1.3.6.1.4.1.3495.1.4 */
183     snmpAddNodeStr("1.3.6.1.4.1.3495.1", 4, NULL, NULL);
184 
185     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4", NET_IP_CACHE, NULL, NULL);
186     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.1", IP_ENT, snmp_netIpFn, static_Inst, atSum);
187     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.1", IP_REQ, snmp_netIpFn, static_Inst, atSum);
188     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.1", IP_HITS, snmp_netIpFn, static_Inst, atSum);
189     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.1", IP_PENDHIT, snmp_netIpFn, static_Inst, atSum);
190     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.1", IP_NEGHIT, snmp_netIpFn, static_Inst, atSum);
191     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.1", IP_MISS, snmp_netIpFn, static_Inst, atSum);
192     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.1", IP_GHBN, snmp_netIpFn, static_Inst, atSum);
193     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.1", IP_LOC, snmp_netIpFn, static_Inst, atSum);
194 
195     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4", NET_FQDN_CACHE, NULL, NULL);
196     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.2", FQDN_ENT, snmp_netFqdnFn, static_Inst, atSum);
197     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.2", FQDN_REQ, snmp_netFqdnFn, static_Inst, atSum);
198     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.2", FQDN_HITS, snmp_netFqdnFn, static_Inst, atSum);
199     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.2", FQDN_PENDHIT, snmp_netFqdnFn, static_Inst, atSum);
200     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.2", FQDN_NEGHIT, snmp_netFqdnFn, static_Inst, atSum);
201     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.2", FQDN_MISS, snmp_netFqdnFn, static_Inst, atSum);
202     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.2", FQDN_GHBN, snmp_netFqdnFn, static_Inst, atSum);
203 
204     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4", NET_DNS_CACHE, NULL, NULL);
205     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.3", DNS_REQ, snmp_netDnsFn, static_Inst, atSum);
206     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.3", DNS_REP, snmp_netDnsFn, static_Inst, atSum);
207     snmpAddNodeStr("1.3.6.1.4.1.3495.1.4.3", DNS_SERVERS, snmp_netDnsFn, static_Inst, atSum);
208 
209     /* SQ_MESH - 1.3.6.1.4.1.3495.1.5 */
210     snmpAddNodeStr("1.3.6.1.4.1.3495.1", 5, NULL, NULL);
211 
212     /* cachePeerTable - 1.3.6.1.4.1.3495.1.5.1 */
213     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5", MESH_PTBL, NULL, NULL);
214 
215     /* CachePeerTableEntry (version 3) - 1.3.6.1.4.1.3495.1.5.1.3 */
216     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1", 3, NULL, NULL);
217     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_INDEX, snmp_meshPtblFn, peer_Inst);
218     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_NAME, snmp_meshPtblFn, peer_Inst);
219     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_ADDR_TYPE, snmp_meshPtblFn, peer_Inst);
220     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_ADDR, snmp_meshPtblFn, peer_Inst);
221     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_HTTP, snmp_meshPtblFn, peer_Inst);
222     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_ICP, snmp_meshPtblFn, peer_Inst);
223     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_TYPE, snmp_meshPtblFn, peer_Inst);
224     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_STATE, snmp_meshPtblFn, peer_Inst);
225     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_SENT, snmp_meshPtblFn, peer_Inst);
226     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_PACKED, snmp_meshPtblFn, peer_Inst);
227     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_FETCHES, snmp_meshPtblFn, peer_Inst);
228     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_RTT, snmp_meshPtblFn, peer_Inst);
229     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_IGN, snmp_meshPtblFn, peer_Inst);
230     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_KEEPAL_S, snmp_meshPtblFn, peer_Inst);
231     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.1.3", MESH_PTBL_KEEPAL_R, snmp_meshPtblFn, peer_Inst);
232 
233     /* cacheClientTable - 1.3.6.1.4.1.3495.1.5.2 */
234     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5", MESH_CTBL, NULL, NULL);
235 
236     /* BUG 2811: we NEED to create a reliable index for the client DB and make version 3 of the table. */
237     /* for now we have version 2 table with OID capable of mixed IPv4 / IPv6 clients and upgraded address text format. */
238 
239     /* cacheClientEntry - 1.3.6.1.4.1.3495.1.5.2.2 */
240     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.2", 2, NULL, NULL);
241     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.2.2", MESH_CTBL_ADDR_TYPE, snmp_meshCtblFn, client_Inst);
242     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.2.2", MESH_CTBL_ADDR, snmp_meshCtblFn, client_Inst);
243     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.2.2", MESH_CTBL_HTREQ, snmp_meshCtblFn, client_Inst);
244     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.2.2", MESH_CTBL_HTBYTES, snmp_meshCtblFn, client_Inst);
245     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.2.2", MESH_CTBL_HTHITS, snmp_meshCtblFn, client_Inst);
246     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.2.2", MESH_CTBL_HTHITBYTES, snmp_meshCtblFn, client_Inst);
247     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.2.2", MESH_CTBL_ICPREQ, snmp_meshCtblFn, client_Inst);
248     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.2.2", MESH_CTBL_ICPBYTES, snmp_meshCtblFn, client_Inst);
249     snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.2.2", MESH_CTBL_ICPHITS, snmp_meshCtblFn, client_Inst);
250     mib_tree_last = snmpAddNodeStr("1.3.6.1.4.1.3495.1.5.2.2", MESH_CTBL_ICPHITBYTES, snmp_meshCtblFn, client_Inst);
251 
252     debugs(49, 9, "snmpInit: Completed SNMP mib tree structure");
253 }
254 
255 void
snmpOpenPorts(void)256 snmpOpenPorts(void)
257 {
258     debugs(49, 5, "snmpConnectionOpen: Called");
259 
260     if (Config.Port.snmp <= 0)
261         return;
262 
263     snmpIncomingConn = new Comm::Connection;
264     snmpIncomingConn->local = Config.Addrs.snmp_incoming;
265     snmpIncomingConn->local.port(Config.Port.snmp);
266 
267     if (!Ip::EnableIpv6 && !snmpIncomingConn->local.setIPv4()) {
268         debugs(49, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << snmpIncomingConn->local << " is not an IPv4 address.");
269         fatal("SNMP port cannot be opened.");
270     }
271     /* split-stack for now requires IPv4-only SNMP */
272     if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && snmpIncomingConn->local.isAnyAddr()) {
273         snmpIncomingConn->local.setIPv4();
274     }
275 
276     AsyncCall::Pointer call = asyncCall(49, 2, "snmpIncomingConnectionOpened",
277                                         Comm::UdpOpenDialer(&snmpPortOpened));
278     Ipc::StartListening(SOCK_DGRAM, IPPROTO_UDP, snmpIncomingConn, Ipc::fdnInSnmpSocket, call);
279 
280     if (!Config.Addrs.snmp_outgoing.isNoAddr()) {
281         snmpOutgoingConn = new Comm::Connection;
282         snmpOutgoingConn->local = Config.Addrs.snmp_outgoing;
283         snmpOutgoingConn->local.port(Config.Port.snmp);
284 
285         if (!Ip::EnableIpv6 && !snmpOutgoingConn->local.setIPv4()) {
286             debugs(49, DBG_CRITICAL, "ERROR: IPv6 is disabled. " << snmpOutgoingConn->local << " is not an IPv4 address.");
287             fatal("SNMP port cannot be opened.");
288         }
289         /* split-stack for now requires IPv4-only SNMP */
290         if (Ip::EnableIpv6&IPV6_SPECIAL_SPLITSTACK && snmpOutgoingConn->local.isAnyAddr()) {
291             snmpOutgoingConn->local.setIPv4();
292         }
293         AsyncCall::Pointer c = asyncCall(49, 2, "snmpOutgoingConnectionOpened",
294                                          Comm::UdpOpenDialer(&snmpPortOpened));
295         Ipc::StartListening(SOCK_DGRAM, IPPROTO_UDP, snmpOutgoingConn, Ipc::fdnOutSnmpSocket, c);
296     } else {
297         snmpOutgoingConn = snmpIncomingConn;
298         debugs(1, DBG_IMPORTANT, "Sending SNMP messages from " << snmpOutgoingConn->local);
299     }
300 }
301 
302 static void
snmpPortOpened(const Comm::ConnectionPointer & conn,int)303 snmpPortOpened(const Comm::ConnectionPointer &conn, int)
304 {
305     if (!Comm::IsConnOpen(conn))
306         fatalf("Cannot open SNMP %s Port",(conn->fd == snmpIncomingConn->fd?"receiving":"sending"));
307 
308     Comm::SetSelect(conn->fd, COMM_SELECT_READ, snmpHandleUdp, NULL, 0);
309 
310     if (conn->fd == snmpIncomingConn->fd)
311         debugs(1, DBG_IMPORTANT, "Accepting SNMP messages on " << snmpIncomingConn->local);
312     else if (conn->fd == snmpOutgoingConn->fd)
313         debugs(1, DBG_IMPORTANT, "Sending SNMP messages from " << snmpOutgoingConn->local);
314     else
315         fatalf("Lost SNMP port (%d) on FD %d", (int)conn->local.port(), conn->fd);
316 }
317 
318 void
snmpClosePorts(void)319 snmpClosePorts(void)
320 {
321     if (Comm::IsConnOpen(snmpIncomingConn)) {
322         debugs(49, DBG_IMPORTANT, "Closing SNMP receiving port " << snmpIncomingConn->local);
323         snmpIncomingConn->close();
324     }
325     snmpIncomingConn = NULL;
326 
327     if (Comm::IsConnOpen(snmpOutgoingConn) && snmpIncomingConn != snmpOutgoingConn) {
328         // Perform OUT port closure so as not to step on IN port when sharing a conn.
329         debugs(49, DBG_IMPORTANT, "Closing SNMP sending port " << snmpOutgoingConn->local);
330         snmpOutgoingConn->close();
331     }
332     snmpOutgoingConn = NULL;
333 }
334 
335 /*
336  * Functions for handling the requests.
337  */
338 
339 /*
340  * Accept the UDP packet
341  */
342 void
snmpHandleUdp(int sock,void *)343 snmpHandleUdp(int sock, void *)
344 {
345     static char buf[SNMP_REQUEST_SIZE];
346     Ip::Address from;
347     SnmpRequest *snmp_rq;
348     int len;
349 
350     debugs(49, 5, "snmpHandleUdp: Called.");
351 
352     Comm::SetSelect(sock, COMM_SELECT_READ, snmpHandleUdp, NULL, 0);
353 
354     memset(buf, '\0', sizeof(buf));
355 
356     len = comm_udp_recvfrom(sock, buf, sizeof(buf)-1, 0, from);
357 
358     if (len > 0) {
359         debugs(49, 3, "snmpHandleUdp: FD " << sock << ": received " << len << " bytes from " << from << ".");
360 
361         snmp_rq = (SnmpRequest *)xcalloc(1, sizeof(SnmpRequest));
362         snmp_rq->buf = (u_char *) buf;
363         snmp_rq->len = len;
364         snmp_rq->sock = sock;
365         snmp_rq->outbuf = (unsigned char *)xmalloc(snmp_rq->outlen = SNMP_REQUEST_SIZE);
366         snmp_rq->from = from;
367         snmpDecodePacket(snmp_rq);
368         xfree(snmp_rq->outbuf);
369         xfree(snmp_rq);
370     } else {
371         int xerrno = errno;
372         debugs(49, DBG_IMPORTANT, "snmpHandleUdp: FD " << sock << " recvfrom: " << xstrerr(xerrno));
373     }
374 }
375 
376 /*
377  * Turn SNMP packet into a PDU, check available ACL's
378  */
379 static void
snmpDecodePacket(SnmpRequest * rq)380 snmpDecodePacket(SnmpRequest * rq)
381 {
382     struct snmp_pdu *PDU;
383     u_char *Community;
384     u_char *buf = rq->buf;
385     int len = rq->len;
386 
387     if (!Config.accessList.snmp) {
388         debugs(49, DBG_IMPORTANT, "WARNING: snmp_access not configured. agent query DENIED from : " << rq->from);
389         return;
390     }
391 
392     debugs(49, 5, HERE << "Called.");
393     PDU = snmp_pdu_create(0);
394     /* Allways answer on SNMPv1 */
395     rq->session.Version = SNMP_VERSION_1;
396     Community = snmp_parse(&rq->session, PDU, buf, len);
397 
398     /* Check if we have explicit permission to access SNMP data.
399      * default (set above) is to deny all */
400     if (Community) {
401         ACLFilledChecklist checklist(Config.accessList.snmp, NULL, NULL);
402         checklist.src_addr = rq->from;
403         checklist.snmp_community = (char *) Community;
404 
405         if (checklist.fastCheck().allowed() && (snmp_coexist_V2toV1(PDU))) {
406             rq->community = Community;
407             rq->PDU = PDU;
408             debugs(49, 5, "snmpAgentParse: reqid=[" << PDU->reqid << "]");
409             snmpConstructReponse(rq);
410         } else {
411             debugs(49, DBG_IMPORTANT, "WARNING: SNMP agent query DENIED from : " << rq->from);
412             snmp_free_pdu(PDU);
413         }
414         xfree(Community);
415 
416     } else {
417         debugs(49, DBG_IMPORTANT, "WARNING: Failed SNMP agent query from : " << rq->from);
418         snmp_free_pdu(PDU);
419     }
420 }
421 
422 /*
423  * Packet OK, ACL Check OK, Create reponse.
424  */
425 static void
snmpConstructReponse(SnmpRequest * rq)426 snmpConstructReponse(SnmpRequest * rq)
427 {
428 
429     struct snmp_pdu *RespPDU;
430 
431     debugs(49, 5, "snmpConstructReponse: Called.");
432 
433     if (UsingSmp() && IamWorkerProcess()) {
434         AsyncJob::Start(new Snmp::Forwarder(static_cast<Snmp::Pdu&>(*rq->PDU),
435                                             static_cast<Snmp::Session&>(rq->session), rq->sock, rq->from));
436         snmp_free_pdu(rq->PDU);
437         return;
438     }
439 
440     RespPDU = snmpAgentResponse(rq->PDU);
441     snmp_free_pdu(rq->PDU);
442 
443     if (RespPDU != NULL) {
444         snmp_build(&rq->session, RespPDU, rq->outbuf, &rq->outlen);
445         comm_udp_sendto(rq->sock, rq->from, rq->outbuf, rq->outlen);
446         snmp_free_pdu(RespPDU);
447     }
448 }
449 
450 /*
451  * Decide how to respond to the request, construct a response and
452  * return the response to the requester.
453  */
454 
455 struct snmp_pdu *
snmpAgentResponse(struct snmp_pdu * PDU)456 snmpAgentResponse(struct snmp_pdu *PDU) {
457 
458     struct snmp_pdu *Answer = NULL;
459 
460     debugs(49, 5, "snmpAgentResponse: Called.");
461 
462     if ((Answer = snmp_pdu_create(SNMP_PDU_RESPONSE))) {
463         Answer->reqid = PDU->reqid;
464         Answer->errindex = 0;
465 
466         if (PDU->command == SNMP_PDU_GET || PDU->command == SNMP_PDU_GETNEXT) {
467             /* Indirect way */
468             int get_next = (PDU->command == SNMP_PDU_GETNEXT);
469             variable_list *VarPtr_;
470             variable_list **RespVars = &(Answer->variables);
471             oid_ParseFn *ParseFn;
472             int index = 0;
473             /* Loop through all variables */
474 
475             for (VarPtr_ = PDU->variables; VarPtr_; VarPtr_ = VarPtr_->next_variable) {
476                 variable_list *VarPtr = VarPtr_;
477                 variable_list *VarNew = NULL;
478                 oid *NextOidName = NULL;
479                 snint NextOidNameLen = 0;
480 
481                 ++index;
482 
483                 if (get_next)
484                     ParseFn = snmpTreeNext(VarPtr->name, VarPtr->name_length, &NextOidName, &NextOidNameLen);
485                 else
486                     ParseFn = snmpTreeGet(VarPtr->name, VarPtr->name_length);
487 
488                 if (ParseFn == NULL) {
489                     Answer->errstat = SNMP_ERR_NOSUCHNAME;
490                     debugs(49, 5, "snmpAgentResponse: No such oid. ");
491                 } else {
492                     if (get_next) {
493                         VarPtr = snmp_var_new(NextOidName, NextOidNameLen);
494                         xfree(NextOidName);
495                     }
496 
497                     int * errstatTmp =  &(Answer->errstat);
498 
499                     VarNew = (*ParseFn) (VarPtr, (snint *) errstatTmp);
500 
501                     if (get_next)
502                         snmp_var_free(VarPtr);
503                 }
504 
505                 if ((Answer->errstat != SNMP_ERR_NOERROR) || (VarNew == NULL)) {
506                     Answer->errindex = index;
507                     debugs(49, 5, "snmpAgentResponse: error.");
508 
509                     if (VarNew)
510                         snmp_var_free(VarNew);
511 
512                     while ((VarPtr = Answer->variables) != NULL) {
513                         Answer->variables = VarPtr->next_variable;
514                         snmp_var_free(VarPtr);
515                     }
516 
517                     /* Steal the original PDU list of variables for the error response */
518                     Answer->variables = PDU->variables;
519 
520                     PDU->variables = NULL;
521 
522                     return (Answer);
523                 }
524 
525                 /* No error.  Insert this var at the end, and move on to the next.
526                  */
527                 *RespVars = VarNew;
528 
529                 RespVars = &(VarNew->next_variable);
530             }
531         }
532     }
533 
534     return (Answer);
535 }
536 
537 static oid_ParseFn *
snmpTreeGet(oid * Current,snint CurrentLen)538 snmpTreeGet(oid * Current, snint CurrentLen)
539 {
540     oid_ParseFn *Fn = NULL;
541     mib_tree_entry *mibTreeEntry = NULL;
542     int count = 0;
543 
544     debugs(49, 5, "snmpTreeGet: Called");
545 
546     MemBuf tmp;
547     debugs(49, 6, "snmpTreeGet: Current : " << snmpDebugOid(Current, CurrentLen, tmp) );
548 
549     mibTreeEntry = mib_tree_head;
550 
551     if (Current[count] == mibTreeEntry->name[count]) {
552         ++count;
553 
554         while ((mibTreeEntry) && (count < CurrentLen) && (!mibTreeEntry->parsefunction)) {
555             mibTreeEntry = snmpTreeEntry(Current[count], count, mibTreeEntry);
556             ++count;
557         }
558     }
559 
560     if (mibTreeEntry && mibTreeEntry->parsefunction)
561         Fn = mibTreeEntry->parsefunction;
562 
563     debugs(49, 5, "snmpTreeGet: return");
564 
565     return (Fn);
566 }
567 
568 AggrType
snmpAggrType(oid * Current,snint CurrentLen)569 snmpAggrType(oid* Current, snint CurrentLen)
570 {
571     debugs(49, 5, HERE);
572 
573     mib_tree_entry* mibTreeEntry = mib_tree_head;
574     AggrType type = atNone;
575     int count = 0;
576 
577     if (Current[count] == mibTreeEntry->name[count]) {
578         ++count;
579 
580         while (mibTreeEntry != NULL && count < CurrentLen) {
581             mibTreeEntry = snmpTreeEntry(Current[count], count, mibTreeEntry);
582             if (mibTreeEntry != NULL)
583                 type = mibTreeEntry->aggrType;
584             ++count;
585         }
586     }
587 
588     return type;
589 }
590 
591 static oid_ParseFn *
snmpTreeNext(oid * Current,snint CurrentLen,oid ** Next,snint * NextLen)592 snmpTreeNext(oid * Current, snint CurrentLen, oid ** Next, snint * NextLen)
593 {
594     oid_ParseFn *Fn = NULL;
595     int count = 0;
596 
597     debugs(49, 5, "snmpTreeNext: Called");
598 
599     MemBuf tmp;
600     debugs(49, 6, "snmpTreeNext: Current : " << snmpDebugOid(Current, CurrentLen, tmp));
601 
602     mib_tree_entry *mibTreeEntry = mib_tree_head;
603 
604     if (mibTreeEntry && Current[count] == mibTreeEntry->name[count]) {
605         ++count;
606 
607         while ((mibTreeEntry) && (count < CurrentLen) && (!mibTreeEntry->parsefunction)) {
608             mib_tree_entry *nextmibTreeEntry = snmpTreeEntry(Current[count], count, mibTreeEntry);
609 
610             if (!nextmibTreeEntry)
611                 break;
612             else
613                 mibTreeEntry = nextmibTreeEntry;
614 
615             ++count;
616         }
617         debugs(49, 5, "snmpTreeNext: Recursed down to requested object");
618     } else {
619         return NULL;
620     }
621 
622     if (mibTreeEntry == mib_tree_last)
623         return (Fn);
624 
625     if ((mibTreeEntry) && (mibTreeEntry->parsefunction)) {
626         *NextLen = CurrentLen;
627         *Next = (*mibTreeEntry->instancefunction) (Current, NextLen, mibTreeEntry, &Fn);
628         if (*Next) {
629             debugs(49, 6, "snmpTreeNext: Next : " << snmpDebugOid(*Next, *NextLen, tmp));
630             return (Fn);
631         }
632     }
633 
634     if ((mibTreeEntry) && (mibTreeEntry->parsefunction)) {
635         --count;
636         mib_tree_entry *nextoid = snmpTreeSiblingEntry(Current[count], count, mibTreeEntry->parent);
637         if (nextoid) {
638             debugs(49, 5, "snmpTreeNext: Next OID found for sibling" << nextoid );
639             mibTreeEntry = nextoid;
640             ++count;
641         } else {
642             debugs(49, 5, "snmpTreeNext: Attempting to recurse up for next object");
643 
644             while (!nextoid) {
645                 --count;
646 
647                 if (mibTreeEntry->parent->parent) {
648                     nextoid = mibTreeEntry->parent;
649                     mibTreeEntry = snmpTreeEntry(Current[count] + 1, count, nextoid->parent);
650 
651                     if (!mibTreeEntry) {
652                         mibTreeEntry = nextoid;
653                         nextoid = NULL;
654                     }
655                 } else {
656                     nextoid = mibTreeEntry;
657                     mibTreeEntry = NULL;
658                 }
659             }
660         }
661     }
662     while ((mibTreeEntry) && (!mibTreeEntry->parsefunction)) {
663         mibTreeEntry = mibTreeEntry->leaves[0];
664     }
665 
666     if (mibTreeEntry) {
667         *NextLen = mibTreeEntry->len;
668         *Next = (*mibTreeEntry->instancefunction) (mibTreeEntry->name, NextLen, mibTreeEntry, &Fn);
669     }
670 
671     if (*Next) {
672         debugs(49, 6, "snmpTreeNext: Next : " << snmpDebugOid(*Next, *NextLen, tmp));
673         return (Fn);
674     } else
675         return NULL;
676 }
677 
678 static oid *
static_Inst(oid * name,snint * len,mib_tree_entry * current,oid_ParseFn ** Fn)679 static_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn)
680 {
681     oid *instance = NULL;
682     if (*len <= current->len) {
683         instance = (oid *)xmalloc(sizeof(*name) * (*len + 1));
684         memcpy(instance, name, sizeof(*name) * (*len));
685         instance[*len] = 0;
686         *len += 1;
687     }
688     *Fn = current->parsefunction;
689     return (instance);
690 }
691 
692 static oid *
time_Inst(oid * name,snint * len,mib_tree_entry * current,oid_ParseFn ** Fn)693 time_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn)
694 {
695     oid *instance = NULL;
696     int identifier = 0, loop = 0;
697     int index[TIME_INDEX_LEN] = {TIME_INDEX};
698 
699     if (*len <= current->len) {
700         instance = (oid *)xmalloc(sizeof(*name) * (*len + 1));
701         memcpy(instance, name, sizeof(*name) * (*len));
702         instance[*len] = *index;
703         *len += 1;
704     } else {
705         identifier = name[*len - 1];
706 
707         while ((loop < TIME_INDEX_LEN) && (identifier != index[loop]))
708             ++loop;
709 
710         if (loop < (TIME_INDEX_LEN - 1)) {
711             instance = (oid *)xmalloc(sizeof(*name) * (*len));
712             memcpy(instance, name, sizeof(*name) * (*len));
713             instance[*len - 1] = index[++loop];
714         }
715     }
716 
717     *Fn = current->parsefunction;
718     return (instance);
719 }
720 
721 static oid *
peer_Inst(oid * name,snint * len,mib_tree_entry * current,oid_ParseFn ** Fn)722 peer_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn)
723 {
724     oid *instance = NULL;
725     CachePeer *peers = Config.peers;
726 
727     if (peers == NULL) {
728         debugs(49, 6, "snmp peer_Inst: No Peers.");
729         current = current->parent->parent->parent->leaves[1];
730         while ((current) && (!current->parsefunction))
731             current = current->leaves[0];
732 
733         if (!current)
734             return (instance);
735 
736         instance = client_Inst(current->name, len, current, Fn);
737     } else if (*len <= current->len) {
738         debugs(49, 6, "snmp peer_Inst: *len <= current->len ???");
739         instance = (oid *)xmalloc(sizeof(*name) * ( *len + 1));
740         memcpy(instance, name, sizeof(*name) * (*len));
741         instance[*len] = 1 ;
742         *len += 1;
743     } else {
744         int no = name[current->len] ;
745         int i;
746         // Note: This works because the Config.peers keeps its index according to its position.
747         for ( i=0 ; peers && (i < no) ; peers = peers->next , ++i ) ;
748 
749         if (peers) {
750             debugs(49, 6, "snmp peer_Inst: Encode peer #" << i);
751             instance = (oid *)xmalloc(sizeof(*name) * (current->len + 1 ));
752             memcpy(instance, name, (sizeof(*name) * current->len ));
753             instance[current->len] = no + 1 ; // i.e. the next index on cache_peeer table.
754         } else {
755             debugs(49, 6, "snmp peer_Inst: We have " << i << " peers. Can't find #" << no);
756             return (instance);
757         }
758     }
759     *Fn = current->parsefunction;
760     return (instance);
761 }
762 
763 static oid *
client_Inst(oid * name,snint * len,mib_tree_entry * current,oid_ParseFn ** Fn)764 client_Inst(oid * name, snint * len, mib_tree_entry * current, oid_ParseFn ** Fn)
765 {
766     oid *instance = NULL;
767     Ip::Address laddr;
768     Ip::Address *aux;
769     int size = 0;
770     int newshift = 0;
771 
772     if (*len <= current->len) {
773         aux  = client_entry(NULL);
774         if (aux)
775             laddr = *aux;
776         else
777             laddr.setAnyAddr();
778 
779         if (laddr.isIPv4())
780             size = sizeof(in_addr);
781         else
782             size = sizeof(in6_addr);
783 
784         debugs(49, 6, HERE << "len" << *len << ", current-len" << current->len << ", addr=" << laddr << ", size=" << size);
785 
786         instance = (oid *)xmalloc(sizeof(*name) * (*len + size ));
787         memcpy(instance, name, (sizeof(*name) * (*len)));
788 
789         if ( !laddr.isAnyAddr() ) {
790             addr2oid(laddr, &instance[ *len]);  // the addr
791             *len += size ;
792         }
793     } else {
794         int shift = *len - current->len ; // i.e 4 or 16
795         oid2addr(&name[*len - shift], laddr,shift);
796         aux = client_entry(&laddr);
797         if (aux)
798             laddr = *aux;
799         else
800             laddr.setAnyAddr();
801 
802         if (!laddr.isAnyAddr()) {
803             if (laddr.isIPv4())
804                 newshift = sizeof(in_addr);
805             else
806                 newshift = sizeof(in6_addr);
807 
808             debugs(49, 6, HERE << "len" << *len << ", current-len" << current->len << ", addr=" << laddr << ", newshift=" << newshift);
809 
810             instance = (oid *)xmalloc(sizeof(*name) * (current->len +  newshift));
811             memcpy(instance, name, (sizeof(*name) * (current->len)));
812             addr2oid(laddr, &instance[current->len]);  // the addr.
813             *len = current->len + newshift ;
814         }
815     }
816 
817     *Fn = current->parsefunction;
818     return (instance);
819 }
820 
821 /*
822  * Utility functions
823  */
824 
825 /*
826  * Tree utility functions.
827  */
828 
829 /*
830  * Returns a sibling object for the requested child object or NULL
831  * if it does not exit
832  */
833 static mib_tree_entry *
snmpTreeSiblingEntry(oid entry,snint len,mib_tree_entry * current)834 snmpTreeSiblingEntry(oid entry, snint len, mib_tree_entry * current)
835 {
836     mib_tree_entry *next = NULL;
837     int count = 0;
838 
839     while ((!next) && (count < current->children)) {
840         if (current->leaves[count]->name[len] == entry) {
841             next = current->leaves[count];
842         }
843 
844         ++count;
845     }
846 
847     /* Exactly the sibling on right */
848     if (count < current->children) {
849         next = current->leaves[count];
850     } else {
851         next = NULL;
852     }
853 
854     return (next);
855 }
856 
857 /*
858  * Returns the requested child object or NULL if it does not exist
859  */
860 static mib_tree_entry *
snmpTreeEntry(oid entry,snint len,mib_tree_entry * current)861 snmpTreeEntry(oid entry, snint len, mib_tree_entry * current)
862 {
863     mib_tree_entry *next = NULL;
864     int count = 0;
865 
866     while ((!next) && current && (count < current->children)) {
867         if (current->leaves[count]->name[len] == entry) {
868             next = current->leaves[count];
869         }
870 
871         ++count;
872     }
873 
874     return (next);
875 }
876 
877 void
snmpAddNodeChild(mib_tree_entry * entry,mib_tree_entry * child)878 snmpAddNodeChild(mib_tree_entry *entry, mib_tree_entry *child)
879 {
880     debugs(49, 5, "snmpAddNodeChild: assigning " << child << " to parent " << entry);
881     entry->leaves = (mib_tree_entry **)xrealloc(entry->leaves, sizeof(mib_tree_entry *) * (entry->children + 1));
882     entry->leaves[entry->children] = child;
883     entry->leaves[entry->children]->parent = entry;
884     ++ entry->children;
885 }
886 
887 mib_tree_entry *
snmpLookupNodeStr(mib_tree_entry * root,const char * str)888 snmpLookupNodeStr(mib_tree_entry *root, const char *str)
889 {
890     oid *name;
891     int namelen;
892     mib_tree_entry *e;
893 
894     if (root)
895         e = root;
896     else
897         e = mib_tree_head;
898 
899     if (! snmpCreateOidFromStr(str, &name, &namelen))
900         return NULL;
901 
902     /* I wish there were some kind of sensible existing tree traversal
903      * routine to use. I'll worry about that later */
904     if (namelen <= 1) {
905         xfree(name);
906         return e;       /* XXX it should only be this? */
907     }
908 
909     int i, r = 1;
910     while (r < namelen) {
911 
912         /* Find the child node which matches this */
913         for (i = 0; i < e->children && e->leaves[i]->name[r] != name[r]; ++i) ; // seek-loop
914 
915         /* Are we pointing to that node? */
916         if (i >= e->children)
917             break;
918         assert(e->leaves[i]->name[r] == name[r]);
919 
920         /* Skip to that node! */
921         e = e->leaves[i];
922         ++r;
923     }
924 
925     xfree(name);
926     return e;
927 }
928 
929 bool
snmpCreateOidFromStr(const char * str,oid ** name,int * nl)930 snmpCreateOidFromStr(const char *str, oid **name, int *nl)
931 {
932     char const *delim = ".";
933 
934     *name = NULL;
935     *nl = 0;
936     const char *s = str;
937 
938     /* Parse the OID string into oid bits */
939     while (size_t len = strcspn(s, delim)) {
940         *name = (oid*)xrealloc(*name, sizeof(oid) * ((*nl) + 1));
941         (*name)[*nl] = atoi(s); // stops at the '.' delimiter
942         ++(*nl);
943         // exit with true when the last octet has been parsed
944         if (s[len] == '\0')
945             return true;
946         s += len+1;
947     }
948 
949     // if we aborted before the lst octet was found, return false.
950     safe_free(*name);
951     return false;
952 }
953 
954 /*
955  * Create an entry. Return a pointer to the newly created node, or NULL
956  * on failure.
957  */
958 static mib_tree_entry *
snmpAddNodeStr(const char * base_str,int o,oid_ParseFn * parsefunction,instance_Fn * instancefunction,AggrType aggrType)959 snmpAddNodeStr(const char *base_str, int o, oid_ParseFn * parsefunction, instance_Fn * instancefunction, AggrType aggrType)
960 {
961     mib_tree_entry *m, *b;
962     oid *n;
963     int nl;
964     char s[1024];
965 
966     /* Find base node */
967     b = snmpLookupNodeStr(mib_tree_head, base_str);
968     if (! b)
969         return NULL;
970     debugs(49, 5, "snmpAddNodeStr: " << base_str << ": -> " << b);
971 
972     /* Create OID string for new entry */
973     snprintf(s, 1024, "%s.%d", base_str, o);
974     if (! snmpCreateOidFromStr(s, &n, &nl))
975         return NULL;
976 
977     /* Create a node */
978     m = snmpAddNode(n, nl, parsefunction, instancefunction, aggrType, 0);
979 
980     /* Link it into the existing tree */
981     snmpAddNodeChild(b, m);
982 
983     /* Return the node */
984     return m;
985 }
986 
987 /*
988  * Adds a node to the MIB tree structure and adds the appropriate children
989  */
990 static mib_tree_entry *
snmpAddNode(oid * name,int len,oid_ParseFn * parsefunction,instance_Fn * instancefunction,AggrType aggrType,int children,...)991 snmpAddNode(oid * name, int len, oid_ParseFn * parsefunction, instance_Fn * instancefunction, AggrType aggrType, int children,...)
992 {
993     va_list args;
994     int loop;
995     mib_tree_entry *entry = NULL;
996     va_start(args, children);
997 
998     MemBuf tmp;
999     debugs(49, 6, "snmpAddNode: Children : " << children << ", Oid : " << snmpDebugOid(name, len, tmp));
1000 
1001     va_start(args, children);
1002     entry = (mib_tree_entry *)xmalloc(sizeof(mib_tree_entry));
1003     entry->name = name;
1004     entry->len = len;
1005     entry->parsefunction = parsefunction;
1006     entry->instancefunction = instancefunction;
1007     entry->children = children;
1008     entry->leaves = NULL;
1009     entry->aggrType = aggrType;
1010 
1011     if (children > 0) {
1012         entry->leaves = (mib_tree_entry **)xmalloc(sizeof(mib_tree_entry *) * children);
1013 
1014         for (loop = 0; loop < children; ++loop) {
1015             entry->leaves[loop] = va_arg(args, mib_tree_entry *);
1016             entry->leaves[loop]->parent = entry;
1017         }
1018     }
1019 
1020     va_end(args);
1021     return (entry);
1022 }
1023 /* End of tree utility functions */
1024 
1025 /*
1026  * Returns the list of parameters in an oid
1027  */
1028 static oid *
snmpCreateOid(int length,...)1029 snmpCreateOid(int length,...)
1030 {
1031     va_list args;
1032     oid *new_oid;
1033     int loop;
1034     va_start(args, length);
1035 
1036     new_oid = (oid *)xmalloc(sizeof(oid) * length);
1037 
1038     if (length > 0) {
1039         for (loop = 0; loop < length; ++loop) {
1040             new_oid[loop] = va_arg(args, int);
1041         }
1042     }
1043 
1044     va_end(args);
1045     return (new_oid);
1046 }
1047 
1048 /*
1049  * Debug calls, prints out the OID for debugging purposes.
1050  */
1051 const char *
snmpDebugOid(oid * Name,snint Len,MemBuf & outbuf)1052 snmpDebugOid(oid * Name, snint Len, MemBuf &outbuf)
1053 {
1054     char mbuf[16];
1055     int x;
1056     if (outbuf.isNull())
1057         outbuf.init(16, MAX_IPSTRLEN);
1058 
1059     for (x = 0; x < Len; ++x) {
1060         size_t bytes = snprintf(mbuf, sizeof(mbuf), ".%u", (unsigned int) Name[x]);
1061         outbuf.append(mbuf, bytes);
1062     }
1063     return outbuf.content();
1064 }
1065 
1066 void
snmpSnmplibDebug(int lvl,char * buf)1067 snmpSnmplibDebug(int lvl, char *buf)
1068 {
1069     debugs(49, lvl, buf);
1070 }
1071 
1072 /*
1073    IPv4 address: 10.10.0.9  ==>
1074    oid == 10.10.0.9
1075    IPv6 adress : 20:01:32:ef:a2:21:fb:32:00:00:00:00:00:00:00:00:OO:01 ==>
1076    oid == 32.1.50.239.162.33.251.20.50.0.0.0.0.0.0.0.0.0.1
1077 */
1078 void
addr2oid(Ip::Address & addr,oid * Dest)1079 addr2oid(Ip::Address &addr, oid * Dest)
1080 {
1081     u_int i ;
1082     u_char *cp = NULL;
1083     struct in_addr i4addr;
1084     struct in6_addr i6addr;
1085     oid code = addr.isIPv6()? INETADDRESSTYPE_IPV6  : INETADDRESSTYPE_IPV4 ;
1086     u_int size = (code == INETADDRESSTYPE_IPV4) ? sizeof(struct in_addr):sizeof(struct in6_addr);
1087     //  Dest[0] = code ;
1088     if ( code == INETADDRESSTYPE_IPV4 ) {
1089         addr.getInAddr(i4addr);
1090         cp = (u_char *) &(i4addr.s_addr);
1091     } else {
1092         addr.getInAddr(i6addr);
1093         cp = (u_char *) &i6addr;
1094     }
1095     for ( i=0 ; i < size ; ++i) {
1096         // OID's are in network order
1097         Dest[i] = *cp;
1098         ++cp;
1099     }
1100     MemBuf tmp;
1101     debugs(49, 7, "addr2oid: Dest : " << snmpDebugOid(Dest, size, tmp));
1102 }
1103 
1104 /*
1105    oid == 10.10.0.9 ==>
1106    IPv4 address: 10.10.0.9
1107    oid == 32.1.50.239.162.33.251.20.50.0.0.0.0.0.0.0.0.0.1 ==>
1108    IPv6 adress : 20:01:32:ef:a2:21:fb:32:00:00:00:00:00:00:00:00:OO:01
1109 */
1110 void
oid2addr(oid * id,Ip::Address & addr,u_int size)1111 oid2addr(oid * id, Ip::Address &addr, u_int size)
1112 {
1113     struct in_addr i4addr;
1114     struct in6_addr i6addr;
1115     u_int i;
1116     u_char *cp;
1117     if ( size == sizeof(struct in_addr) )
1118         cp = (u_char *) &(i4addr.s_addr);
1119     else
1120         cp = (u_char *) &(i6addr);
1121     MemBuf tmp;
1122     debugs(49, 7, "oid2addr: id : " << snmpDebugOid(id, size, tmp) );
1123     for (i=0 ; i<size; ++i) {
1124         cp[i] = id[i];
1125     }
1126     if ( size == sizeof(struct in_addr) )
1127         addr = i4addr;
1128     else
1129         addr = i6addr;
1130 }
1131 
1132 int
match(ACLData<MatchType> * & data,ACLFilledChecklist * checklist)1133 ACLSNMPCommunityStrategy::match (ACLData<MatchType> * &data, ACLFilledChecklist *checklist)
1134 {
1135     return data->match (checklist->snmp_community);
1136 }
1137 
1138