3 * $Id: ipcache.cc,v 1.240 2003/01/23 00:37:22 robertc Exp $
5 * DEBUG: section 14 IP Cache
6 * AUTHOR: Harvest Derived
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
39 typedef struct _ipcache_entry ipcache_entry
;
41 struct _ipcache_entry
{
42 hash_link hash
; /* must be first */
49 struct timeval request_time
;
53 unsigned int negcached
:1;
54 unsigned int fromhosts
:1;
65 int ghbn_calls
; /* # calls to blocking gethostbyname() */
69 static dlink_list lru_list
;
71 static FREE ipcacheFreeEntry
;
73 static HLPCB ipcacheHandleReply
;
75 static IDNSCB ipcacheHandleReply
;
77 static IPH dummy_handler
;
78 static int ipcacheExpiredEntry(ipcache_entry
*);
79 static int ipcache_testname(void);
81 static ipcache_entry
*ipcacheParse(const char *buf
);
83 static ipcache_entry
*ipcacheParse(rfc1035_rr
*, int);
85 static ipcache_entry
*ipcache_get(const char *);
86 static void ipcacheLockEntry(ipcache_entry
*);
87 static void ipcacheStatPrint(ipcache_entry
*, StoreEntry
*);
88 static void ipcacheUnlockEntry(ipcache_entry
*);
89 static void ipcacheRelease(ipcache_entry
*);
91 static ipcache_addrs static_addrs
;
92 static hash_table
*ip_table
= NULL
;
94 static long ipcache_low
= 180;
95 static long ipcache_high
= 200;
97 #if LIBRESOLV_DNS_TTL_HACK
102 ipcache_testname(void)
105 debug(14, 1) ("Performing DNS Tests...\n");
106 if ((w
= Config
.dns_testname_list
) == NULL
)
108 for (; w
; w
= w
->next
) {
109 IpcacheStats
.ghbn_calls
++;
110 if (gethostbyname(w
->key
) != NULL
)
116 /* removes the given ipcache entry */
118 ipcacheRelease(ipcache_entry
* i
)
120 hash_remove_link(ip_table
, (hash_link
*) i
);
121 dlinkDelete(&i
->lru
, &lru_list
);
125 static ipcache_entry
*
126 ipcache_get(const char *name
)
128 if (ip_table
!= NULL
)
129 return (ipcache_entry
*) hash_lookup(ip_table
, name
);
135 ipcacheExpiredEntry(ipcache_entry
* i
)
137 /* all static entries are locked, so this takes care of them too */
140 if (i
->addrs
.count
== 0)
141 if (0 == i
->flags
.negcached
)
143 if (i
->expires
> squid_curtime
)
149 ipcache_purgelru(void *voidnotused
)
152 dlink_node
*prev
= NULL
;
155 eventAdd("ipcache_purgelru", ipcache_purgelru
, NULL
, 10.0, 1);
156 for (m
= lru_list
.tail
; m
; m
= prev
) {
157 if (memInUse(MEM_IPCACHE_ENTRY
) < ipcache_low
)
160 i
= (ipcache_entry
*)m
->data
;
166 debug(14, 9) ("ipcache_purgelru: removed %d entries\n", removed
);
169 /* purges entries added from /etc/hosts (or whatever). */
171 purge_entries_fromhosts(void)
173 dlink_node
*m
= lru_list
.head
;
174 ipcache_entry
*i
= NULL
, *t
;
176 if (i
!= NULL
) { /* need to delay deletion */
177 ipcacheRelease(i
); /* we just override locks */
180 t
= (ipcache_entry
*)m
->data
;
181 if (t
->flags
.fromhosts
)
189 /* create blank ipcache_entry */
190 static ipcache_entry
*
191 ipcacheCreateEntry(const char *name
)
193 static ipcache_entry
*i
;
194 i
= (ipcache_entry
*)memAllocate(MEM_IPCACHE_ENTRY
);
195 i
->hash
.key
= xstrdup(name
);
196 i
->expires
= squid_curtime
+ Config
.negativeDnsTtl
;
201 ipcacheAddEntry(ipcache_entry
* i
)
203 hash_link
*e
= (hash_link
*)hash_lookup(ip_table
, i
->hash
.key
);
205 /* avoid colission */
206 ipcache_entry
*q
= (ipcache_entry
*) e
;
209 hash_join(ip_table
, &i
->hash
);
210 dlinkAdd(i
, &i
->lru
, &lru_list
);
211 i
->lastref
= squid_curtime
;
214 /* walks down the pending list, calling handlers */
216 ipcacheCallback(ipcache_entry
* i
)
218 IPH
*callback
= i
->handler
;
220 i
->lastref
= squid_curtime
;
224 callback
= i
->handler
;
226 if (cbdataReferenceValidDone(i
->handlerData
, &cbdata
)) {
227 dns_error_message
= i
->error_message
;
228 callback(i
->flags
.negcached
? NULL
: &i
->addrs
, cbdata
);
230 ipcacheUnlockEntry(i
);
233 static ipcache_entry
*
235 ipcacheParse(const char *inbuf
)
237 LOCAL_ARRAY(char, buf
, DNS_INBUF_SZ
);
239 static ipcache_entry i
;
245 memset(&i
, '\0', sizeof(i
));
246 i
.expires
= squid_curtime
;
247 i
.flags
.negcached
= 1;
249 debug(14, 1) ("ipcacheParse: Got <NULL> reply\n");
250 i
.error_message
= xstrdup("Internal Squid Error");
253 xstrncpy(buf
, inbuf
, DNS_INBUF_SZ
);
254 debug(14, 5) ("ipcacheParse: parsing: {%s}\n", buf
);
255 token
= strtok(buf
, w_space
);
257 debug(14, 1) ("ipcacheParse: Got <NULL>, expecting '$addr'\n");
260 if (0 == strcmp(token
, "$fail")) {
261 i
.expires
= squid_curtime
+ Config
.negativeDnsTtl
;
262 token
= strtok(NULL
, "\n");
263 assert(NULL
!= token
);
264 i
.error_message
= xstrdup(token
);
267 if (0 != strcmp(token
, "$addr")) {
268 debug(14, 1) ("ipcacheParse: Got '%s', expecting '$addr'\n", token
);
271 token
= strtok(NULL
, w_space
);
273 debug(14, 1) ("ipcacheParse: Got <NULL>, expecting TTL\n");
276 i
.flags
.negcached
= 0;
279 i
.expires
= squid_curtime
+ ttl
;
281 i
.expires
= squid_curtime
+ Config
.positiveDnsTtl
;
282 while (NULL
!= (token
= strtok(NULL
, w_space
))) {
283 xstrncpy(A
[ipcount
], token
, 16);
288 i
.addrs
.in_addrs
= NULL
;
289 i
.addrs
.bad_mask
= NULL
;
291 i
.addrs
.in_addrs
= (struct in_addr
*)xcalloc(ipcount
, sizeof(struct in_addr
));
292 i
.addrs
.bad_mask
= (unsigned char *)xcalloc(ipcount
, sizeof(unsigned char));
294 for (j
= 0, k
= 0; k
< ipcount
; k
++) {
295 if (safe_inet_addr(A
[k
], &i
.addrs
.in_addrs
[j
]))
298 debug(14, 1) ("ipcacheParse: Invalid IP address '%s'\n", A
[k
]);
300 i
.addrs
.count
= (unsigned char) j
;
304 ipcacheParse(rfc1035_rr
* answers
, int nr
)
306 static ipcache_entry i
;
310 memset(&i
, '\0', sizeof(i
));
311 i
.expires
= squid_curtime
+ Config
.negativeDnsTtl
;
312 i
.flags
.negcached
= 1;
314 debug(14, 3) ("ipcacheParse: Lookup failed (error %d)\n",
316 assert(rfc1035_error_message
);
317 i
.error_message
= xstrdup(rfc1035_error_message
);
321 debug(14, 3) ("ipcacheParse: No DNS records\n");
322 i
.error_message
= xstrdup("No DNS records");
326 for (j
= 0, k
= 0; k
< nr
; k
++) {
327 if (answers
[k
].type
!= RFC1035_TYPE_A
)
329 if (answers
[k
]._class
!= RFC1035_CLASS_IN
)
334 debug(14, 1) ("ipcacheParse: No Address records\n");
335 i
.error_message
= xstrdup("No Address records");
338 i
.flags
.negcached
= 0;
339 i
.addrs
.in_addrs
= (struct in_addr
*)xcalloc(na
, sizeof(struct in_addr
));
340 i
.addrs
.bad_mask
= (unsigned char *)xcalloc(na
, sizeof(unsigned char));
341 i
.addrs
.count
= (unsigned char) na
;
342 for (j
= 0, k
= 0; k
< nr
; k
++) {
343 if (answers
[k
].type
!= RFC1035_TYPE_A
)
345 if (answers
[k
]._class
!= RFC1035_CLASS_IN
)
348 i
.expires
= squid_curtime
+ answers
[k
].ttl
;
349 assert(answers
[k
].rdlength
== 4);
350 xmemcpy(&i
.addrs
.in_addrs
[j
++], answers
[k
].rdata
, 4);
351 debug(14, 3) ("ipcacheParse: #%d %s\n",
353 inet_ntoa(i
.addrs
.in_addrs
[j
- 1]));
362 ipcacheHandleReply(void *data
, char *reply
)
364 ipcacheHandleReply(void *data
, rfc1035_rr
* answers
, int na
)
367 generic_cbdata
*c
= (generic_cbdata
*)data
;
368 ipcache_entry
*i
= (ipcache_entry
*)c
->data
;
369 ipcache_entry
*x
= NULL
;
372 IpcacheStats
.replies
++;
373 statHistCount(&statCounter
.dns
.svc_time
,
374 tvSubMsec(i
->request_time
, current_time
));
376 x
= ipcacheParse(reply
);
378 x
= ipcacheParse(answers
, na
);
382 i
->error_message
= x
->error_message
;
383 i
->expires
= x
->expires
;
390 ipcache_nbgethostbyname(const char *name
, IPH
* handler
, void *handlerData
)
392 ipcache_entry
*i
= NULL
;
393 const ipcache_addrs
*addrs
= NULL
;
395 assert(handler
!= NULL
);
396 debug(14, 4) ("ipcache_nbgethostbyname: Name '%s'.\n", name
);
397 IpcacheStats
.requests
++;
398 if (name
== NULL
|| name
[0] == '\0') {
399 debug(14, 4) ("ipcache_nbgethostbyname: Invalid name!\n");
400 handler(NULL
, handlerData
);
403 if ((addrs
= ipcacheCheckNumeric(name
))) {
404 handler(addrs
, handlerData
);
407 i
= ipcache_get(name
);
411 } else if (ipcacheExpiredEntry(i
)) {
412 /* hit, but expired -- bummer */
417 debug(14, 4) ("ipcache_nbgethostbyname: HIT for '%s'\n", name
);
418 if (i
->flags
.negcached
)
419 IpcacheStats
.negative_hits
++;
422 i
->handler
= handler
;
423 i
->handlerData
= cbdataReference(handlerData
);
427 debug(14, 5) ("ipcache_nbgethostbyname: MISS for '%s'\n", name
);
428 IpcacheStats
.misses
++;
429 i
= ipcacheCreateEntry(name
);
430 i
->handler
= handler
;
431 i
->handlerData
= cbdataReference(handlerData
);
432 i
->request_time
= current_time
;
433 c
= cbdataAlloc(generic_cbdata
);
436 dnsSubmit(hashKeyStr(&i
->hash
), ipcacheHandleReply
, c
);
438 idnsALookup(hashKeyStr(&i
->hash
), ipcacheHandleReply
, c
);
442 /* initialize the ipcache */
447 debug(14, 3) ("Initializing IP Cache...\n");
448 memset(&IpcacheStats
, '\0', sizeof(IpcacheStats
));
449 memset(&lru_list
, '\0', sizeof(lru_list
));
450 /* test naming lookup */
451 if (!opt_dns_tests
) {
452 debug(14, 4) ("ipcache_init: Skipping DNS name lookup tests.\n");
453 } else if (!ipcache_testname()) {
454 fatal("ipcache_init: DNS name lookup tests failed.");
456 debug(14, 1) ("Successful DNS name lookup tests...\n");
458 memset(&static_addrs
, '\0', sizeof(ipcache_addrs
));
459 static_addrs
.in_addrs
= (struct in_addr
*)xcalloc(1, sizeof(struct in_addr
));
460 static_addrs
.bad_mask
= (unsigned char *)xcalloc(1, sizeof(unsigned char));
461 ipcache_high
= (long) (((float) Config
.ipcache
.size
*
462 (float) Config
.ipcache
.high
) / (float) 100);
463 ipcache_low
= (long) (((float) Config
.ipcache
.size
*
464 (float) Config
.ipcache
.low
) / (float) 100);
465 n
= hashPrime(ipcache_high
/ 4);
466 ip_table
= hash_create((HASHCMP
*) strcmp
, n
, hash4
);
467 cachemgrRegister("ipcache",
468 "IP Cache Stats and Contents",
469 stat_ipcache_get
, 0, 1);
470 memDataInit(MEM_IPCACHE_ENTRY
, "ipcache_entry", sizeof(ipcache_entry
), 0);
473 const ipcache_addrs
*
474 ipcache_gethostbyname(const char *name
, int flags
)
476 ipcache_entry
*i
= NULL
;
477 ipcache_addrs
*addrs
;
479 debug(14, 3) ("ipcache_gethostbyname: '%s', flags=%x\n", name
, flags
);
480 IpcacheStats
.requests
++;
481 i
= ipcache_get(name
);
484 } else if (ipcacheExpiredEntry(i
)) {
487 } else if (i
->flags
.negcached
) {
488 IpcacheStats
.negative_hits
++;
489 dns_error_message
= i
->error_message
;
493 i
->lastref
= squid_curtime
;
496 if ((addrs
= ipcacheCheckNumeric(name
)))
498 IpcacheStats
.misses
++;
499 if (flags
& IP_LOOKUP_IF_MISS
)
500 ipcache_nbgethostbyname(name
, dummy_handler
, NULL
);
505 ipcacheStatPrint(ipcache_entry
* i
, StoreEntry
* sentry
)
508 storeAppendPrintf(sentry
, " %-32.32s %c%c %6d %6d %2d(%2d)",
509 hashKeyStr(&i
->hash
),
510 i
->flags
.fromhosts
? 'H' : ' ',
511 i
->flags
.negcached
? 'N' : ' ',
512 (int) (squid_curtime
- i
->lastref
),
513 (int) ((i
->flags
.fromhosts
? -1 : i
->expires
- squid_curtime
)),
514 (int) i
->addrs
.count
,
515 (int) i
->addrs
.badcount
);
516 for (k
= 0; k
< (int) i
->addrs
.count
; k
++) {
517 storeAppendPrintf(sentry
, " %15s-%3s", inet_ntoa(i
->addrs
.in_addrs
[k
]),
518 i
->addrs
.bad_mask
[k
] ? "BAD" : "OK ");
520 storeAppendPrintf(sentry
, "\n");
523 /* process objects list */
525 stat_ipcache_get(StoreEntry
* sentry
)
528 assert(ip_table
!= NULL
);
529 storeAppendPrintf(sentry
, "IP Cache Statistics:\n");
530 storeAppendPrintf(sentry
, "IPcache Entries: %d\n",
531 memInUse(MEM_IPCACHE_ENTRY
));
532 storeAppendPrintf(sentry
, "IPcache Requests: %d\n",
533 IpcacheStats
.requests
);
534 storeAppendPrintf(sentry
, "IPcache Hits: %d\n",
536 storeAppendPrintf(sentry
, "IPcache Negative Hits: %d\n",
537 IpcacheStats
.negative_hits
);
538 storeAppendPrintf(sentry
, "IPcache Misses: %d\n",
539 IpcacheStats
.misses
);
540 storeAppendPrintf(sentry
, "Blocking calls to gethostbyname(): %d\n",
541 IpcacheStats
.ghbn_calls
);
542 storeAppendPrintf(sentry
, "Attempts to release locked entries: %d\n",
543 IpcacheStats
.release_locked
);
544 storeAppendPrintf(sentry
, "\n\n");
545 storeAppendPrintf(sentry
, "IP Cache Contents:\n\n");
546 storeAppendPrintf(sentry
, " %-29.29s %3s %6s %6s %1s\n",
552 for (m
= lru_list
.head
; m
; m
= m
->next
)
553 ipcacheStatPrint((ipcache_entry
*)m
->data
, sentry
);
557 dummy_handler(const ipcache_addrs
* addrsnotused
, void *datanotused
)
563 ipcacheInvalidate(const char *name
)
566 if ((i
= ipcache_get(name
)) == NULL
)
568 i
->expires
= squid_curtime
;
570 * NOTE, don't call ipcacheRelease here becuase we might be here due
571 * to a thread started from a callback.
576 ipcacheCheckNumeric(const char *name
)
579 /* check if it's already a IP address in text form. */
580 if (!safe_inet_addr(name
, &ip
))
582 static_addrs
.count
= 1;
583 static_addrs
.cur
= 0;
584 static_addrs
.in_addrs
[0].s_addr
= ip
.s_addr
;
585 static_addrs
.bad_mask
[0] = FALSE
;
586 static_addrs
.badcount
= 0;
587 return &static_addrs
;
591 ipcacheLockEntry(ipcache_entry
* i
)
593 if (i
->locks
++ == 0) {
594 dlinkDelete(&i
->lru
, &lru_list
);
595 dlinkAdd(i
, &i
->lru
, &lru_list
);
600 ipcacheUnlockEntry(ipcache_entry
* i
)
602 assert(i
->locks
> 0);
604 if (ipcacheExpiredEntry(i
))
609 ipcacheCycleAddr(const char *name
, ipcache_addrs
* ia
)
615 if ((i
= ipcache_get(name
)) == NULL
)
617 if (i
->flags
.negcached
)
621 for (k
= 0; k
< ia
->count
; k
++) {
622 if (++ia
->cur
== ia
->count
)
624 if (!ia
->bad_mask
[ia
->cur
])
627 if (k
== ia
->count
) {
628 /* All bad, reset to All good */
629 debug(14, 3) ("ipcacheCycleAddr: Changing ALL %s addrs from BAD to OK\n",
631 for (k
= 0; k
< ia
->count
; k
++)
636 debug(14, 3) ("ipcacheCycleAddr: %s now at %s\n", name
,
637 inet_ntoa(ia
->in_addrs
[ia
->cur
]));
641 * Marks the given address as BAD and calls ipcacheCycleAddr to
642 * advance the current pointer to the next OK address.
645 ipcacheMarkBadAddr(const char *name
, struct in_addr addr
)
650 if ((i
= ipcache_get(name
)) == NULL
)
653 for (k
= 0; k
< (int) ia
->count
; k
++) {
654 if (ia
->in_addrs
[k
].s_addr
== addr
.s_addr
)
657 if (k
== (int) ia
->count
) /* not found */
659 if (!ia
->bad_mask
[k
]) {
660 ia
->bad_mask
[k
] = TRUE
;
662 debug(14, 2) ("ipcacheMarkBadAddr: %s [%s]\n", name
, inet_ntoa(addr
));
664 ipcacheCycleAddr(name
, ia
);
668 ipcacheMarkGoodAddr(const char *name
, struct in_addr addr
)
673 if ((i
= ipcache_get(name
)) == NULL
)
676 for (k
= 0; k
< (int) ia
->count
; k
++) {
677 if (ia
->in_addrs
[k
].s_addr
== addr
.s_addr
)
680 if (k
== (int) ia
->count
) /* not found */
682 if (!ia
->bad_mask
[k
]) /* already OK */
684 ia
->bad_mask
[k
] = FALSE
;
686 debug(14, 2) ("ipcacheMarkGoodAddr: %s [%s]\n", name
, inet_ntoa(addr
));
690 ipcacheFreeEntry(void *data
)
692 ipcache_entry
*i
= (ipcache_entry
*)data
;
693 safe_free(i
->addrs
.in_addrs
);
694 safe_free(i
->addrs
.bad_mask
);
695 safe_free(i
->hash
.key
);
696 safe_free(i
->error_message
);
697 memFree(i
, MEM_IPCACHE_ENTRY
);
701 ipcacheFreeMemory(void)
703 hashFreeItems(ip_table
, ipcacheFreeEntry
);
704 hashFreeMemory(ip_table
);
708 /* Recalculate IP cache size upon reconfigure */
710 ipcache_restart(void)
712 ipcache_high
= (long) (((float) Config
.ipcache
.size
*
713 (float) Config
.ipcache
.high
) / (float) 100);
714 ipcache_low
= (long) (((float) Config
.ipcache
.size
*
715 (float) Config
.ipcache
.low
) / (float) 100);
716 purge_entries_fromhosts();
720 * adds a "static" entry from /etc/hosts.
721 * returns 0 upon success, 1 if the ip address is invalid
724 ipcacheAddEntryFromHosts(const char *name
, const char *ipaddr
)
728 if (!safe_inet_addr(ipaddr
, &ip
)) {
729 if (strchr(ipaddr
, ':') && strspn(ipaddr
, "0123456789abcdefABCDEF:") == strlen(ipaddr
)) {
730 debug(14, 3) ("ipcacheAddEntryFromHosts: Skipping IPv6 address '%s'\n", ipaddr
);
732 debug(14, 1) ("ipcacheAddEntryFromHosts: Bad IP address '%s'\n",
737 if ((i
= ipcache_get(name
))) {
738 if (1 == i
->flags
.fromhosts
) {
739 ipcacheUnlockEntry(i
);
740 } else if (i
->locks
> 0) {
741 debug(14, 1) ("ipcacheAddEntryFromHosts: can't add static entry"
742 " for locked name '%s'\n", name
);
748 i
= ipcacheCreateEntry(name
);
751 i
->addrs
.badcount
= 0;
752 i
->addrs
.in_addrs
= (struct in_addr
*)xcalloc(1, sizeof(struct in_addr
));
753 i
->addrs
.bad_mask
= (unsigned char *)xcalloc(1, sizeof(unsigned char));
754 i
->addrs
.in_addrs
[0].s_addr
= ip
.s_addr
;
755 i
->addrs
.bad_mask
[0] = FALSE
;
756 i
->flags
.fromhosts
= 1;
764 * The function to return the ip cache statistics to via SNMP
768 snmp_netIpFn(variable_list
* Var
, snint
* ErrP
)
770 variable_list
*Answer
= NULL
;
771 debug(49, 5) ("snmp_netIpFn: Processing request:\n");
772 snmpDebugOid(5, Var
->name
, Var
->name_length
);
773 *ErrP
= SNMP_ERR_NOERROR
;
774 switch (Var
->name
[LEN_SQ_NET
+ 1]) {
776 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
777 memInUse(MEM_IPCACHE_ENTRY
),
781 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
782 IpcacheStats
.requests
,
786 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
791 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
796 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
797 IpcacheStats
.negative_hits
,
801 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
806 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
807 IpcacheStats
.ghbn_calls
,
811 Answer
= snmp_var_new_integer(Var
->name
, Var
->name_length
,
812 IpcacheStats
.release_locked
,
816 *ErrP
= SNMP_ERR_NOSUCHNAME
;
817 snmp_var_free(Answer
);
823 #endif /*SQUID_SNMP */