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 Interface */
10 
11 #include "squid.h"
12 #include "cache_snmp.h"
13 #include "CachePeer.h"
14 #include "globals.h"
15 #include "mem_node.h"
16 #include "neighbors.h"
17 #include "snmp_agent.h"
18 #include "snmp_core.h"
19 #include "SquidConfig.h"
20 #include "SquidMath.h"
21 #include "SquidTime.h"
22 #include "stat.h"
23 #include "StatCounters.h"
24 #include "StatHist.h"
25 #include "Store.h"
26 #include "tools.h"
27 // for tvSubDsec() which should be in SquidTime.h
28 #include "util.h"
29 
30 /************************************************************************
31 
32  SQUID MIB Implementation
33 
34  ************************************************************************/
35 
36 /*
37  * cacheSystem group
38  */
39 
40 variable_list *
snmp_sysFn(variable_list * Var,snint * ErrP)41 snmp_sysFn(variable_list * Var, snint * ErrP)
42 {
43     variable_list *Answer = NULL;
44     MemBuf tmp;
45     debugs(49, 5, "snmp_sysFn: Processing request:" << snmpDebugOid(Var->name, Var->name_length, tmp));
46     *ErrP = SNMP_ERR_NOERROR;
47 
48     switch (Var->name[LEN_SQ_SYS]) {
49 
50     case SYSVMSIZ:
51         Answer = snmp_var_new_integer(Var->name, Var->name_length,
52                                       mem_node::StoreMemSize() >> 10,
53                                       ASN_INTEGER);
54         break;
55 
56     case SYSSTOR:
57         Answer = snmp_var_new_integer(Var->name, Var->name_length,
58                                       Store::Root().currentSize() >> 10,
59                                       ASN_INTEGER);
60         break;
61 
62     case SYS_UPTIME:
63         Answer = snmp_var_new_integer(Var->name, Var->name_length,
64                                       (int)(tvSubDsec(squid_start, current_time) * 100),
65                                       SMI_TIMETICKS);
66         break;
67 
68     default:
69         *ErrP = SNMP_ERR_NOSUCHNAME;
70         break;
71     }
72 
73     return Answer;
74 }
75 
76 /*
77  * cacheConfig group
78  */
79 variable_list *
snmp_confFn(variable_list * Var,snint * ErrP)80 snmp_confFn(variable_list * Var, snint * ErrP)
81 {
82     variable_list *Answer = NULL;
83     const char *cp = NULL;
84     debugs(49, 5, "snmp_confFn: Processing request with magic " << Var->name[8] << "!");
85     *ErrP = SNMP_ERR_NOERROR;
86 
87     switch (Var->name[LEN_SQ_CONF]) {
88 
89     case CONF_ADMIN:
90         Answer = snmp_var_new(Var->name, Var->name_length);
91         Answer->type = ASN_OCTET_STR;
92         Answer->val_len = strlen(Config.adminEmail);
93         Answer->val.string = (u_char *) xstrdup(Config.adminEmail);
94         break;
95 
96     case CONF_VERSION:
97         Answer = snmp_var_new(Var->name, Var->name_length);
98         Answer->type = ASN_OCTET_STR;
99         Answer->val_len = strlen(APP_SHORTNAME);
100         Answer->val.string = (u_char *) xstrdup(APP_SHORTNAME);
101         break;
102 
103     case CONF_VERSION_ID:
104         Answer = snmp_var_new(Var->name, Var->name_length);
105         Answer->type = ASN_OCTET_STR;
106         Answer->val_len = strlen(VERSION);
107         Answer->val.string = (u_char *) xstrdup(VERSION);
108         break;
109 
110     case CONF_STORAGE:
111 
112         switch (Var->name[LEN_SQ_CONF + 1]) {
113 
114         case CONF_ST_MMAXSZ:
115             Answer = snmp_var_new_integer(Var->name, Var->name_length,
116                                           (snint) (Config.memMaxSize >> 20),
117                                           ASN_INTEGER);
118             break;
119 
120         case CONF_ST_SWMAXSZ:
121             Answer = snmp_var_new_integer(Var->name, Var->name_length,
122                                           (snint) (Store::Root().maxSize() >> 20),
123                                           ASN_INTEGER);
124             break;
125 
126         case CONF_ST_SWHIWM:
127             Answer = snmp_var_new_integer(Var->name, Var->name_length,
128                                           (snint) Config.Swap.highWaterMark,
129                                           ASN_INTEGER);
130             break;
131 
132         case CONF_ST_SWLOWM:
133             Answer = snmp_var_new_integer(Var->name, Var->name_length,
134                                           (snint) Config.Swap.lowWaterMark,
135                                           ASN_INTEGER);
136             break;
137 
138         default:
139             *ErrP = SNMP_ERR_NOSUCHNAME;
140             break;
141         }
142 
143         break;
144 
145     case CONF_LOG_FAC:
146         Answer = snmp_var_new(Var->name, Var->name_length);
147 
148         if (!(cp = Debug::debugOptions))
149             cp = "None";
150 
151         Answer->type = ASN_OCTET_STR;
152 
153         Answer->val_len = strlen(cp);
154 
155         Answer->val.string = (u_char *) xstrdup(cp);
156 
157         break;
158 
159     case CONF_UNIQNAME:
160         Answer = snmp_var_new(Var->name, Var->name_length);
161 
162         cp = uniqueHostname();
163 
164         Answer->type = ASN_OCTET_STR;
165 
166         Answer->val_len = strlen(cp);
167 
168         Answer->val.string = (u_char *) xstrdup(cp);
169 
170         break;
171 
172     default:
173         *ErrP = SNMP_ERR_NOSUCHNAME;
174 
175         break;
176     }
177 
178     return Answer;
179 }
180 
181 /*
182  * cacheMesh group
183  *   - cachePeerTable
184  */
185 variable_list *
snmp_meshPtblFn(variable_list * Var,snint * ErrP)186 snmp_meshPtblFn(variable_list * Var, snint * ErrP)
187 {
188     variable_list *Answer = NULL;
189 
190     Ip::Address laddr;
191     char *cp = NULL;
192     CachePeer *p = NULL;
193     int cnt = 0;
194     debugs(49, 5, "snmp_meshPtblFn: peer " << Var->name[LEN_SQ_MESH + 3] << " requested!");
195     *ErrP = SNMP_ERR_NOERROR;
196 
197     u_int index = Var->name[LEN_SQ_MESH + 3] ;
198     for (p = Config.peers; p != NULL; p = p->next, ++cnt) {
199         if (p->index == index) {
200             laddr = p->in_addr ;
201             break;
202         }
203     }
204 
205     if (p == NULL) {
206         *ErrP = SNMP_ERR_NOSUCHNAME;
207         return NULL;
208     }
209 
210     switch (Var->name[LEN_SQ_MESH + 2]) {
211     case MESH_PTBL_INDEX: { // FIXME INET6: Should be visible?
212         Answer = snmp_var_new_integer(Var->name, Var->name_length,
213                                       (snint)p->index, SMI_INTEGER);
214     }
215     break;
216 
217     case MESH_PTBL_NAME:
218         cp = p->host;
219         Answer = snmp_var_new(Var->name, Var->name_length);
220         Answer->type = ASN_OCTET_STR;
221         Answer->val_len = strlen(cp);
222         Answer->val.string = (u_char *) xstrdup(cp);
223 
224         break;
225 
226     case MESH_PTBL_ADDR_TYPE: {
227         int ival;
228         ival = laddr.isIPv4() ? INETADDRESSTYPE_IPV4 : INETADDRESSTYPE_IPV6 ;
229         Answer = snmp_var_new_integer(Var->name, Var->name_length,
230                                       ival, SMI_INTEGER);
231     }
232     break;
233     case MESH_PTBL_ADDR: {
234         Answer = snmp_var_new(Var->name, Var->name_length);
235         // InetAddress doesn't have its own ASN.1 type,
236         // like IpAddr does (SMI_IPADDRESS)
237         // See: rfc4001.txt
238         Answer->type = ASN_OCTET_STR;
239         char host[MAX_IPSTRLEN];
240         laddr.toStr(host,MAX_IPSTRLEN);
241         Answer->val_len = strlen(host);
242         Answer->val.string =  (u_char *) xstrdup(host);
243     }
244     break;
245 
246     case MESH_PTBL_HTTP:
247         Answer = snmp_var_new_integer(Var->name, Var->name_length,
248                                       (snint) p->http_port,
249                                       ASN_INTEGER);
250         break;
251 
252     case MESH_PTBL_ICP:
253         Answer = snmp_var_new_integer(Var->name, Var->name_length,
254                                       (snint) p->icp.port,
255                                       ASN_INTEGER);
256         break;
257 
258     case MESH_PTBL_TYPE:
259         Answer = snmp_var_new_integer(Var->name, Var->name_length,
260                                       (snint) p->type,
261                                       ASN_INTEGER);
262         break;
263 
264     case MESH_PTBL_STATE:
265         Answer = snmp_var_new_integer(Var->name, Var->name_length,
266                                       (snint) neighborUp(p),
267                                       ASN_INTEGER);
268         break;
269 
270     case MESH_PTBL_SENT:
271         Answer = snmp_var_new_integer(Var->name, Var->name_length,
272                                       p->stats.pings_sent,
273                                       SMI_COUNTER32);
274         break;
275 
276     case MESH_PTBL_PACKED:
277         Answer = snmp_var_new_integer(Var->name, Var->name_length,
278                                       p->stats.pings_acked,
279                                       SMI_COUNTER32);
280         break;
281 
282     case MESH_PTBL_FETCHES:
283         Answer = snmp_var_new_integer(Var->name, Var->name_length,
284                                       p->stats.fetches,
285                                       SMI_COUNTER32);
286         break;
287 
288     case MESH_PTBL_RTT:
289         Answer = snmp_var_new_integer(Var->name, Var->name_length,
290                                       p->stats.rtt,
291                                       ASN_INTEGER);
292         break;
293 
294     case MESH_PTBL_IGN:
295         Answer = snmp_var_new_integer(Var->name, Var->name_length,
296                                       p->stats.ignored_replies,
297                                       SMI_COUNTER32);
298         break;
299 
300     case MESH_PTBL_KEEPAL_S:
301         Answer = snmp_var_new_integer(Var->name, Var->name_length,
302                                       p->stats.n_keepalives_sent,
303                                       SMI_COUNTER32);
304         break;
305 
306     case MESH_PTBL_KEEPAL_R:
307         Answer = snmp_var_new_integer(Var->name, Var->name_length,
308                                       p->stats.n_keepalives_recv,
309                                       SMI_COUNTER32);
310         break;
311 
312     default:
313         *ErrP = SNMP_ERR_NOSUCHNAME;
314         break;
315     }
316 
317     return Answer;
318 }
319 
320 variable_list *
snmp_prfSysFn(variable_list * Var,snint * ErrP)321 snmp_prfSysFn(variable_list * Var, snint * ErrP)
322 {
323     variable_list *Answer = NULL;
324 
325     static struct rusage rusage;
326     debugs(49, 5, "snmp_prfSysFn: Processing request with magic " << Var->name[LEN_SQ_PRF + 1] << "!");
327     *ErrP = SNMP_ERR_NOERROR;
328 
329     switch (Var->name[LEN_SQ_PRF + 1]) {
330 
331     case PERF_SYS_PF:
332         squid_getrusage(&rusage);
333         Answer = snmp_var_new_integer(Var->name, Var->name_length,
334                                       rusage_pagefaults(&rusage),
335                                       SMI_COUNTER32);
336         break;
337 
338     case PERF_SYS_NUMR:
339         Answer = snmp_var_new_integer(Var->name, Var->name_length,
340                                       IOStats.Http.reads,
341                                       SMI_COUNTER32);
342         break;
343 
344     case PERF_SYS_MEMUSAGE:
345         Answer = snmp_var_new_integer(Var->name, Var->name_length,
346                                       (snint) statMemoryAccounted() >> 10,
347                                       ASN_INTEGER);
348         break;
349 
350     case PERF_SYS_CPUTIME:
351         squid_getrusage(&rusage);
352         Answer = snmp_var_new_integer(Var->name, Var->name_length,
353                                       (snint) rusage_cputime(&rusage),
354                                       ASN_INTEGER);
355         break;
356 
357     case PERF_SYS_CPUUSAGE:
358         squid_getrusage(&rusage);
359         Answer = snmp_var_new_integer(Var->name, Var->name_length,
360                                       (snint) Math::doublePercent(rusage_cputime(&rusage), tvSubDsec(squid_start, current_time)),
361                                       ASN_INTEGER);
362         break;
363 
364     case PERF_SYS_MAXRESSZ:
365         squid_getrusage(&rusage);
366         Answer = snmp_var_new_integer(Var->name, Var->name_length,
367                                       (snint) rusage_maxrss(&rusage),
368                                       ASN_INTEGER);
369         break;
370 
371     case PERF_SYS_CURLRUEXP:
372         /* No global LRU info anymore */
373         Answer = snmp_var_new_integer(Var->name, Var->name_length,
374                                       0,
375                                       SMI_TIMETICKS);
376         break;
377 
378     case PERF_SYS_CURUNLREQ:
379         Answer = snmp_var_new_integer(Var->name, Var->name_length,
380                                       (snint) statCounter.unlink.requests,
381                                       SMI_GAUGE32);
382         break;
383 
384     case PERF_SYS_CURUNUSED_FD:
385         Answer = snmp_var_new_integer(Var->name, Var->name_length,
386                                       (snint) Squid_MaxFD - Number_FD,
387                                       SMI_GAUGE32);
388         break;
389 
390     case PERF_SYS_CURRESERVED_FD:
391         Answer = snmp_var_new_integer(Var->name, Var->name_length,
392                                       (snint) RESERVED_FD,
393                                       SMI_GAUGE32);
394         break;
395 
396     case PERF_SYS_CURUSED_FD:
397         Answer = snmp_var_new_integer(Var->name, Var->name_length,
398                                       (snint) Number_FD,
399                                       SMI_GAUGE32);
400         break;
401 
402     case PERF_SYS_CURMAX_FD:
403         Answer = snmp_var_new_integer(Var->name, Var->name_length,
404                                       (snint) Biggest_FD,
405                                       SMI_GAUGE32);
406         break;
407 
408     case PERF_SYS_NUMOBJCNT:
409         Answer = snmp_var_new_integer(Var->name, Var->name_length,
410                                       (snint) StoreEntry::inUseCount(),
411                                       SMI_GAUGE32);
412         break;
413 
414     default:
415         *ErrP = SNMP_ERR_NOSUCHNAME;
416         break;
417     }
418 
419     return Answer;
420 }
421 
422 variable_list *
snmp_prfProtoFn(variable_list * Var,snint * ErrP)423 snmp_prfProtoFn(variable_list * Var, snint * ErrP)
424 {
425     variable_list *Answer = NULL;
426     static StatCounters *f = NULL;
427     static StatCounters *l = NULL;
428     double x;
429     int minutes;
430     debugs(49, 5, "snmp_prfProtoFn: Processing request with magic " << Var->name[LEN_SQ_PRF] << "!");
431     *ErrP = SNMP_ERR_NOERROR;
432 
433     switch (Var->name[LEN_SQ_PRF + 1]) {
434 
435     case PERF_PROTOSTAT_AGGR:   /* cacheProtoAggregateStats */
436 
437         switch (Var->name[LEN_SQ_PRF + 2]) {
438 
439         case PERF_PROTOSTAT_AGGR_HTTP_REQ:
440             Answer = snmp_var_new_integer(Var->name, Var->name_length,
441                                           (snint) statCounter.client_http.requests,
442                                           SMI_COUNTER32);
443             break;
444 
445         case PERF_PROTOSTAT_AGGR_HTTP_HITS:
446             Answer = snmp_var_new_integer(Var->name, Var->name_length,
447                                           (snint) statCounter.client_http.hits,
448                                           SMI_COUNTER32);
449             break;
450 
451         case PERF_PROTOSTAT_AGGR_HTTP_ERRORS:
452             Answer = snmp_var_new_integer(Var->name, Var->name_length,
453                                           (snint) statCounter.client_http.errors,
454                                           SMI_COUNTER32);
455             break;
456 
457         case PERF_PROTOSTAT_AGGR_HTTP_KBYTES_IN:
458             Answer = snmp_var_new_integer(Var->name, Var->name_length,
459                                           (snint) statCounter.client_http.kbytes_in.kb,
460                                           SMI_COUNTER32);
461             break;
462 
463         case PERF_PROTOSTAT_AGGR_HTTP_KBYTES_OUT:
464             Answer = snmp_var_new_integer(Var->name, Var->name_length,
465                                           (snint) statCounter.client_http.kbytes_out.kb,
466                                           SMI_COUNTER32);
467             break;
468 
469         case PERF_PROTOSTAT_AGGR_ICP_S:
470             Answer = snmp_var_new_integer(Var->name, Var->name_length,
471                                           (snint) statCounter.icp.pkts_sent,
472                                           SMI_COUNTER32);
473             break;
474 
475         case PERF_PROTOSTAT_AGGR_ICP_R:
476             Answer = snmp_var_new_integer(Var->name, Var->name_length,
477                                           (snint) statCounter.icp.pkts_recv,
478                                           SMI_COUNTER32);
479             break;
480 
481         case PERF_PROTOSTAT_AGGR_ICP_SKB:
482             Answer = snmp_var_new_integer(Var->name, Var->name_length,
483                                           (snint) statCounter.icp.kbytes_sent.kb,
484                                           SMI_COUNTER32);
485             break;
486 
487         case PERF_PROTOSTAT_AGGR_ICP_RKB:
488             Answer = snmp_var_new_integer(Var->name, Var->name_length,
489                                           (snint) statCounter.icp.kbytes_recv.kb,
490                                           SMI_COUNTER32);
491             break;
492 
493         case PERF_PROTOSTAT_AGGR_REQ:
494             Answer = snmp_var_new_integer(Var->name, Var->name_length,
495                                           (snint) statCounter.server.all.requests,
496                                           SMI_INTEGER);
497             break;
498 
499         case PERF_PROTOSTAT_AGGR_ERRORS:
500             Answer = snmp_var_new_integer(Var->name, Var->name_length,
501                                           (snint) statCounter.server.all.errors,
502                                           SMI_INTEGER);
503             break;
504 
505         case PERF_PROTOSTAT_AGGR_KBYTES_IN:
506             Answer = snmp_var_new_integer(Var->name, Var->name_length,
507                                           (snint) statCounter.server.all.kbytes_in.kb,
508                                           SMI_COUNTER32);
509             break;
510 
511         case PERF_PROTOSTAT_AGGR_KBYTES_OUT:
512             Answer = snmp_var_new_integer(Var->name, Var->name_length,
513                                           (snint) statCounter.server.all.kbytes_out.kb,
514                                           SMI_COUNTER32);
515             break;
516 
517         case PERF_PROTOSTAT_AGGR_CURSWAP:
518             Answer = snmp_var_new_integer(Var->name, Var->name_length,
519                                           (snint) Store::Root().currentSize() >> 10,
520                                           SMI_GAUGE32);
521             break;
522 
523         case PERF_PROTOSTAT_AGGR_CLIENTS:
524             Answer = snmp_var_new_integer(Var->name, Var->name_length,
525                                           (snint) statCounter.client_http.clients,
526                                           SMI_GAUGE32);
527             break;
528 
529         default:
530             *ErrP = SNMP_ERR_NOSUCHNAME;
531             break;
532         }
533 
534         return Answer;
535 
536     case PERF_PROTOSTAT_MEDIAN:
537 
538         if (Var->name_length == LEN_SQ_PRF + 5)
539             minutes = Var->name[LEN_SQ_PRF + 4];
540         else
541             break;
542 
543         if ((minutes < 1) || (minutes > 60))
544             break;
545 
546         f = snmpStatGet(0);
547 
548         l = snmpStatGet(minutes);
549 
550         debugs(49, 8, "median: min= " << minutes << ", " << Var->name[LEN_SQ_PRF + 3] << " l= " << l << " , f = " << f);
551         debugs(49, 8, "median: l= " << l << " , f = " << f);
552 
553         switch (Var->name[LEN_SQ_PRF + 3]) {
554 
555         case PERF_MEDIAN_TIME:
556             x = minutes;
557             break;
558 
559         case PERF_MEDIAN_HTTP_ALL:
560             x = statHistDeltaMedian(l->client_http.allSvcTime,
561                                     f->client_http.allSvcTime);
562             break;
563 
564         case PERF_MEDIAN_HTTP_MISS:
565             x = statHistDeltaMedian(l->client_http.missSvcTime,
566                                     f->client_http.missSvcTime);
567             break;
568 
569         case PERF_MEDIAN_HTTP_NM:
570             x = statHistDeltaMedian(l->client_http.nearMissSvcTime,
571                                     f->client_http.nearMissSvcTime);
572             break;
573 
574         case PERF_MEDIAN_HTTP_HIT:
575             x = statHistDeltaMedian(l->client_http.hitSvcTime,
576                                     f->client_http.hitSvcTime);
577             break;
578 
579         case PERF_MEDIAN_ICP_QUERY:
580             x = statHistDeltaMedian(l->icp.querySvcTime, f->icp.querySvcTime);
581             break;
582 
583         case PERF_MEDIAN_ICP_REPLY:
584             x = statHistDeltaMedian(l->icp.replySvcTime, f->icp.replySvcTime);
585             break;
586 
587         case PERF_MEDIAN_DNS:
588             x = statHistDeltaMedian(l->dns.svcTime, f->dns.svcTime);
589             break;
590 
591         case PERF_MEDIAN_RHR:
592             x = statRequestHitRatio(minutes);
593             break;
594 
595         case PERF_MEDIAN_BHR:
596             x = statByteHitRatio(minutes);
597             break;
598 
599         case PERF_MEDIAN_HTTP_NH:
600             x = statHistDeltaMedian(l->client_http.nearHitSvcTime,
601                                     f->client_http.nearHitSvcTime);
602             break;
603 
604         default:
605             *ErrP = SNMP_ERR_NOSUCHNAME;
606             return NULL;
607         }
608 
609         return snmp_var_new_integer(Var->name, Var->name_length,
610                                     (snint) x,
611                                     SMI_INTEGER);
612     }
613 
614     *ErrP = SNMP_ERR_NOSUCHNAME;
615     return NULL;
616 }
617 
618