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