Merge branch 'collectd-5.0' into collectd-5.1
[collectd.git] / src / tcpconns.c
1 /**
2  * collectd - src/tcpconns.c
3  * Copyright (C) 2007,2008  Florian octo Forster
4  * Copyright (C) 2008       Michael Stapelberg
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; only version 2 of the License is applicable.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Author:
20  *   Florian octo Forster <octo at verplant.org>
21  *   Michael Stapelberg <michael+git at stapelberg.de>
22  **/
23
24 /**
25  * Code within `HAVE_LIBKVM_NLIST' blocks is provided under the following
26  * license:
27  *
28  * $collectd: parts of tcpconns.c, 2008/08/08 03:48:30 Michael Stapelberg $
29  * $OpenBSD: inet.c,v 1.100 2007/06/19 05:28:30 ray Exp $
30  * $NetBSD: inet.c,v 1.14 1995/10/03 21:42:37 thorpej Exp $
31  *
32  * Copyright (c) 1983, 1988, 1993
33  *      The Regents of the University of California.  All rights reserved.
34  *
35  * Redistribution and use in source and binary forms, with or without
36  * modification, are permitted provided that the following conditions
37  * are met:
38  * 1. Redistributions of source code must retain the above copyright
39  *    notice, this list of conditions and the following disclaimer.
40  * 2. Redistributions in binary form must reproduce the above copyright
41  *    notice, this list of conditions and the following disclaimer in the
42  *    documentation and/or other materials provided with the distribution.
43  * 3. Neither the name of the University nor the names of its contributors
44  *    may be used to endorse or promote products derived from this software
45  *    without specific prior written permission.
46  *
47  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
48  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
51  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57  * SUCH DAMAGE.
58  */
59
60 #include "collectd.h"
61 #include "common.h"
62 #include "plugin.h"
63
64 #if defined(__OpenBSD__) || defined(__NetBSD__)
65 #undef HAVE_SYSCTLBYNAME /* force HAVE_LIBKVM_NLIST path */
66 #endif
67
68 #if !KERNEL_LINUX && !HAVE_SYSCTLBYNAME && !HAVE_LIBKVM_NLIST && !KERNEL_AIX
69 # error "No applicable input method."
70 #endif
71
72 #if KERNEL_LINUX
73 /* #endif KERNEL_LINUX */
74
75 #elif HAVE_SYSCTLBYNAME
76 # include <sys/socketvar.h>
77 # include <sys/sysctl.h>
78
79 /* Some includes needed for compiling on FreeBSD */
80 #include <sys/time.h>
81 #if HAVE_SYS_TYPES_H
82 # include <sys/types.h>
83 #endif
84 #if HAVE_SYS_SOCKET_H
85 # include <sys/socket.h>
86 #endif
87 #if HAVE_NET_IF_H
88 # include <net/if.h>
89 #endif
90
91 # include <net/route.h>
92 # include <netinet/in.h>
93 # include <netinet/in_systm.h>
94 # include <netinet/ip.h>
95 # include <netinet/ip6.h>
96 # include <netinet/in_pcb.h>
97 # include <netinet/ip_var.h>
98 # include <netinet/tcp.h>
99 # include <netinet/tcpip.h>
100 # include <netinet/tcp_seq.h>
101 # include <netinet/tcp_var.h>
102 /* #endif HAVE_SYSCTLBYNAME */
103
104 /* This is for OpenBSD and NetBSD. */
105 #elif HAVE_LIBKVM_NLIST
106 # include <sys/queue.h>
107 # include <sys/socket.h>
108 # include <net/route.h>
109 # include <netinet/in.h>
110 # include <netinet/in_systm.h>
111 # include <netinet/ip.h>
112 # include <netinet/ip_var.h>
113 # include <netinet/in_pcb.h>
114 # include <netinet/tcp.h>
115 # include <netinet/tcp_timer.h>
116 # include <netinet/tcp_var.h>
117 # include <netdb.h>
118 # include <arpa/inet.h>
119 # if !defined(HAVE_BSD_NLIST_H) || !HAVE_BSD_NLIST_H
120 #  include <nlist.h>
121 # else /* HAVE_BSD_NLIST_H */
122 #  include <bsd/nlist.h>
123 # endif
124 # include <kvm.h>
125 /* #endif HAVE_LIBKVM_NLIST */
126
127 #elif KERNEL_AIX
128 # include <arpa/inet.h>
129 # include <sys/socketvar.h>
130 #endif /* KERNEL_AIX */
131
132 #if KERNEL_LINUX
133 static const char *tcp_state[] =
134 {
135   "", /* 0 */
136   "ESTABLISHED",
137   "SYN_SENT",
138   "SYN_RECV",
139   "FIN_WAIT1",
140   "FIN_WAIT2",
141   "TIME_WAIT",
142   "CLOSED",
143   "CLOSE_WAIT",
144   "LAST_ACK",
145   "LISTEN", /* 10 */
146   "CLOSING"
147 };
148
149 # define TCP_STATE_LISTEN 10
150 # define TCP_STATE_MIN 1
151 # define TCP_STATE_MAX 11
152 /* #endif KERNEL_LINUX */
153
154 #elif HAVE_SYSCTLBYNAME
155 static const char *tcp_state[] =
156 {
157   "CLOSED",
158   "LISTEN",
159   "SYN_SENT",
160   "SYN_RECV",
161   "ESTABLISHED",
162   "CLOSE_WAIT",
163   "FIN_WAIT1",
164   "CLOSING",
165   "LAST_ACK",
166   "FIN_WAIT2",
167   "TIME_WAIT"
168 };
169
170 # define TCP_STATE_LISTEN 1
171 # define TCP_STATE_MIN 0
172 # define TCP_STATE_MAX 10
173 /* #endif HAVE_SYSCTLBYNAME */
174
175 #elif HAVE_LIBKVM_NLIST
176 static const char *tcp_state[] =
177 {
178   "CLOSED",
179   "LISTEN",
180   "SYN_SENT",
181   "SYN_RECV",
182   "ESTABLISHED",
183   "CLOSE_WAIT",
184   "FIN_WAIT1",
185   "CLOSING",
186   "LAST_ACK",
187   "FIN_WAIT2",
188   "TIME_WAIT"
189 };
190
191 static kvm_t *kvmd;
192 static u_long      inpcbtable_off = 0;
193 struct inpcbtable *inpcbtable_ptr = NULL;
194
195 # define TCP_STATE_LISTEN 1
196 # define TCP_STATE_MIN 1
197 # define TCP_STATE_MAX 10
198 /* #endif HAVE_LIBKVM_NLIST */
199
200 #elif KERNEL_AIX
201 static const char *tcp_state[] =
202 {
203   "CLOSED",
204   "LISTEN",
205   "SYN_SENT",
206   "SYN_RCVD",
207   "ESTABLISHED",
208   "CLOSE_WAIT",
209   "FIN_WAIT_1",
210   "CLOSING",
211   "LAST_ACK",
212   "FIN_WAIT_2",
213   "TIME_WAIT"
214 };
215
216 # define TCP_STATE_LISTEN 1
217 # define TCP_STATE_MIN 0
218 # define TCP_STATE_MAX 10
219
220 struct netinfo_conn {
221   uint32_t unknow1[2];
222   uint16_t dstport;
223   uint16_t unknow2;
224   struct in6_addr dstaddr;
225   uint16_t srcport;
226   uint16_t unknow3;
227   struct in6_addr srcaddr;
228   uint32_t unknow4[36];
229   uint16_t tcp_state;
230   uint16_t unknow5[7];
231 };
232
233 struct netinfo_header {
234   unsigned int proto;
235   unsigned int size;
236 };
237
238 # define NETINFO_TCP 3
239 extern int netinfo (int proto, void *data, int *size,  int n);
240 #endif /* KERNEL_AIX */
241
242 #define PORT_COLLECT_LOCAL  0x01
243 #define PORT_COLLECT_REMOTE 0x02
244 #define PORT_IS_LISTENING   0x04
245
246 typedef struct port_entry_s
247 {
248   uint16_t port;
249   uint16_t flags;
250   uint32_t count_local[TCP_STATE_MAX + 1];
251   uint32_t count_remote[TCP_STATE_MAX + 1];
252   struct port_entry_s *next;
253 } port_entry_t;
254
255 static const char *config_keys[] =
256 {
257   "ListeningPorts",
258   "LocalPort",
259   "RemotePort"
260 };
261 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
262
263 static int port_collect_listening = 0;
264 static port_entry_t *port_list_head = NULL;
265
266 static void conn_submit_port_entry (port_entry_t *pe)
267 {
268   value_t values[1];
269   value_list_t vl = VALUE_LIST_INIT;
270   int i;
271
272   vl.values = values;
273   vl.values_len = 1;
274   sstrncpy (vl.host, hostname_g, sizeof (vl.host));
275   sstrncpy (vl.plugin, "tcpconns", sizeof (vl.plugin));
276   sstrncpy (vl.type, "tcp_connections", sizeof (vl.type));
277
278   if (((port_collect_listening != 0) && (pe->flags & PORT_IS_LISTENING))
279       || (pe->flags & PORT_COLLECT_LOCAL))
280   {
281     ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
282         "%"PRIu16"-local", pe->port);
283
284     for (i = 1; i <= TCP_STATE_MAX; i++)
285     {
286       vl.values[0].gauge = pe->count_local[i];
287
288       sstrncpy (vl.type_instance, tcp_state[i], sizeof (vl.type_instance));
289
290       plugin_dispatch_values (&vl);
291     }
292   }
293
294   if (pe->flags & PORT_COLLECT_REMOTE)
295   {
296     ssnprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
297         "%"PRIu16"-remote", pe->port);
298
299     for (i = 1; i <= TCP_STATE_MAX; i++)
300     {
301       vl.values[0].gauge = pe->count_remote[i];
302
303       sstrncpy (vl.type_instance, tcp_state[i], sizeof (vl.type_instance));
304
305       plugin_dispatch_values (&vl);
306     }
307   }
308 } /* void conn_submit */
309
310 static void conn_submit_all (void)
311 {
312   port_entry_t *pe;
313
314   for (pe = port_list_head; pe != NULL; pe = pe->next)
315     conn_submit_port_entry (pe);
316 } /* void conn_submit_all */
317
318 static port_entry_t *conn_get_port_entry (uint16_t port, int create)
319 {
320   port_entry_t *ret;
321
322   ret = port_list_head;
323   while (ret != NULL)
324   {
325     if (ret->port == port)
326       break;
327     ret = ret->next;
328   }
329
330   if ((ret == NULL) && (create != 0))
331   {
332     ret = (port_entry_t *) malloc (sizeof (port_entry_t));
333     if (ret == NULL)
334       return (NULL);
335     memset (ret, '\0', sizeof (port_entry_t));
336
337     ret->port = port;
338     ret->next = port_list_head;
339     port_list_head = ret;
340   }
341
342   return (ret);
343 } /* port_entry_t *conn_get_port_entry */
344
345 /* Removes ports that were added automatically due to the `ListeningPorts'
346  * setting but which are no longer listening. */
347 static void conn_reset_port_entry (void)
348 {
349   port_entry_t *prev = NULL;
350   port_entry_t *pe = port_list_head;
351
352   while (pe != NULL)
353   {
354     /* If this entry was created while reading the files (ant not when handling
355      * the configuration) remove it now. */
356     if ((pe->flags & (PORT_COLLECT_LOCAL
357             | PORT_COLLECT_REMOTE
358             | PORT_IS_LISTENING)) == 0)
359     {
360       port_entry_t *next = pe->next;
361
362       DEBUG ("tcpconns plugin: Removing temporary entry "
363           "for listening port %"PRIu16, pe->port);
364
365       if (prev == NULL)
366         port_list_head = next;
367       else
368         prev->next = next;
369
370       sfree (pe);
371       pe = next;
372
373       continue;
374     }
375
376     memset (pe->count_local, '\0', sizeof (pe->count_local));
377     memset (pe->count_remote, '\0', sizeof (pe->count_remote));
378     pe->flags &= ~PORT_IS_LISTENING;
379
380     pe = pe->next;
381   }
382 } /* void conn_reset_port_entry */
383
384 static int conn_handle_ports (uint16_t port_local, uint16_t port_remote, uint8_t state)
385 {
386   port_entry_t *pe = NULL;
387
388   if ((state > TCP_STATE_MAX)
389 #if TCP_STATE_MIN > 0
390       || (state < TCP_STATE_MIN)
391 #endif
392      )
393   {
394     NOTICE ("tcpconns plugin: Ignoring connection with "
395         "unknown state 0x%02"PRIx8".", state);
396     return (-1);
397   }
398
399   /* Listening sockets */
400   if ((state == TCP_STATE_LISTEN) && (port_collect_listening != 0))
401   {
402     pe = conn_get_port_entry (port_local, 1 /* create */);
403     if (pe != NULL)
404       pe->flags |= PORT_IS_LISTENING;
405   }
406
407   DEBUG ("tcpconns plugin: Connection %"PRIu16" <-> %"PRIu16" (%s)",
408       port_local, port_remote, tcp_state[state]);
409
410   pe = conn_get_port_entry (port_local, 0 /* no create */);
411   if (pe != NULL)
412     pe->count_local[state]++;
413
414   pe = conn_get_port_entry (port_remote, 0 /* no create */);
415   if (pe != NULL)
416     pe->count_remote[state]++;
417
418   return (0);
419 } /* int conn_handle_ports */
420
421 #if KERNEL_LINUX
422 static int conn_handle_line (char *buffer)
423 {
424   char *fields[32];
425   int   fields_len;
426
427   char *endptr;
428
429   char *port_local_str;
430   char *port_remote_str;
431   uint16_t port_local;
432   uint16_t port_remote;
433
434   uint8_t state;
435
436   int buffer_len = strlen (buffer);
437
438   while ((buffer_len > 0) && (buffer[buffer_len - 1] < 32))
439     buffer[--buffer_len] = '\0';
440   if (buffer_len <= 0)
441     return (-1);
442
443   fields_len = strsplit (buffer, fields, STATIC_ARRAY_SIZE (fields));
444   if (fields_len < 12)
445   {
446     DEBUG ("tcpconns plugin: Got %i fields, expected at least 12.", fields_len);
447     return (-1);
448   }
449
450   port_local_str  = strchr (fields[1], ':');
451   port_remote_str = strchr (fields[2], ':');
452
453   if ((port_local_str == NULL) || (port_remote_str == NULL))
454     return (-1);
455   port_local_str++;
456   port_remote_str++;
457   if ((*port_local_str == '\0') || (*port_remote_str == '\0'))
458     return (-1);
459
460   endptr = NULL;
461   port_local = (uint16_t) strtol (port_local_str, &endptr, 16);
462   if ((endptr == NULL) || (*endptr != '\0'))
463     return (-1);
464
465   endptr = NULL;
466   port_remote = (uint16_t) strtol (port_remote_str, &endptr, 16);
467   if ((endptr == NULL) || (*endptr != '\0'))
468     return (-1);
469
470   endptr = NULL;
471   state = (uint8_t) strtol (fields[3], &endptr, 16);
472   if ((endptr == NULL) || (*endptr != '\0'))
473     return (-1);
474
475   return (conn_handle_ports (port_local, port_remote, state));
476 } /* int conn_handle_line */
477
478 static int conn_read_file (const char *file)
479 {
480   FILE *fh;
481   char buffer[1024];
482
483   fh = fopen (file, "r");
484   if (fh == NULL)
485     return (-1);
486
487   while (fgets (buffer, sizeof (buffer), fh) != NULL)
488   {
489     conn_handle_line (buffer);
490   } /* while (fgets) */
491
492   fclose (fh);
493
494   return (0);
495 } /* int conn_read_file */
496 /* #endif KERNEL_LINUX */
497
498 #elif HAVE_SYSCTLBYNAME
499 /* #endif HAVE_SYSCTLBYNAME */
500
501 #elif HAVE_LIBKVM_NLIST
502 #endif /* HAVE_LIBKVM_NLIST */
503
504 static int conn_config (const char *key, const char *value)
505 {
506   if (strcasecmp (key, "ListeningPorts") == 0)
507   {
508     if (IS_TRUE (value))
509       port_collect_listening = 1;
510     else
511       port_collect_listening = 0;
512   }
513   else if ((strcasecmp (key, "LocalPort") == 0)
514       || (strcasecmp (key, "RemotePort") == 0))
515   {
516       port_entry_t *pe;
517       int port = atoi (value);
518
519       if ((port < 1) || (port > 65535))
520       {
521         ERROR ("tcpconns plugin: Invalid port: %i", port);
522         return (1);
523       }
524
525       pe = conn_get_port_entry ((uint16_t) port, 1 /* create */);
526       if (pe == NULL)
527       {
528         ERROR ("tcpconns plugin: conn_get_port_entry failed.");
529         return (1);
530       }
531
532       if (strcasecmp (key, "LocalPort") == 0)
533         pe->flags |= PORT_COLLECT_LOCAL;
534       else
535         pe->flags |= PORT_COLLECT_REMOTE;
536   }
537   else
538   {
539     return (-1);
540   }
541
542   return (0);
543 } /* int conn_config */
544
545 #if KERNEL_LINUX
546 static int conn_init (void)
547 {
548   if (port_list_head == NULL)
549     port_collect_listening = 1;
550
551   return (0);
552 } /* int conn_init */
553
554 static int conn_read (void)
555 {
556   int errors_num = 0;
557
558   conn_reset_port_entry ();
559
560   if (conn_read_file ("/proc/net/tcp") != 0)
561     errors_num++;
562   if (conn_read_file ("/proc/net/tcp6") != 0)
563     errors_num++;
564
565   if (errors_num < 2)
566   {
567     conn_submit_all ();
568   }
569   else
570   {
571     ERROR ("tcpconns plugin: Neither /proc/net/tcp nor /proc/net/tcp6 "
572         "coult be read.");
573     return (-1);
574   }
575
576   return (0);
577 } /* int conn_read */
578 /* #endif KERNEL_LINUX */
579
580 #elif HAVE_SYSCTLBYNAME
581 static int conn_read (void)
582 {
583   int status;
584   char *buffer;
585   size_t buffer_len;;
586
587   struct xinpgen *in_orig;
588   struct xinpgen *in_ptr;
589
590   conn_reset_port_entry ();
591
592   buffer_len = 0;
593   status = sysctlbyname ("net.inet.tcp.pcblist", NULL, &buffer_len, 0, 0);
594   if (status < 0)
595   {
596     ERROR ("tcpconns plugin: sysctlbyname failed.");
597     return (-1);
598   }
599
600   buffer = (char *) malloc (buffer_len);
601   if (buffer == NULL)
602   {
603     ERROR ("tcpconns plugin: malloc failed.");
604     return (-1);
605   }
606
607   status = sysctlbyname ("net.inet.tcp.pcblist", buffer, &buffer_len, 0, 0);
608   if (status < 0)
609   {
610     ERROR ("tcpconns plugin: sysctlbyname failed.");
611     sfree (buffer);
612     return (-1);
613   }
614
615   if (buffer_len <= sizeof (struct xinpgen))
616   {
617     ERROR ("tcpconns plugin: (buffer_len <= sizeof (struct xinpgen))");
618     sfree (buffer);
619     return (-1);
620   }
621
622   in_orig = (struct xinpgen *) buffer;
623   for (in_ptr = (struct xinpgen *) (((char *) in_orig) + in_orig->xig_len);
624       in_ptr->xig_len > sizeof (struct xinpgen);
625       in_ptr = (struct xinpgen *) (((char *) in_ptr) + in_ptr->xig_len))
626   {
627     struct tcpcb *tp = &((struct xtcpcb *) in_ptr)->xt_tp;
628     struct inpcb *inp = &((struct xtcpcb *) in_ptr)->xt_inp;
629     struct xsocket *so = &((struct xtcpcb *) in_ptr)->xt_socket;
630
631     /* Ignore non-TCP sockets */
632     if (so->xso_protocol != IPPROTO_TCP)
633       continue;
634
635     /* Ignore PCBs which were freed during copyout. */
636     if (inp->inp_gencnt > in_orig->xig_gen)
637       continue;
638
639     if (((inp->inp_vflag & INP_IPV4) == 0)
640         && ((inp->inp_vflag & INP_IPV6) == 0))
641       continue;
642
643     conn_handle_ports (ntohs (inp->inp_lport), ntohs (inp->inp_fport),
644         tp->t_state);
645   } /* for (in_ptr) */
646
647   in_orig = NULL;
648   in_ptr = NULL;
649   sfree (buffer);
650
651   conn_submit_all ();
652
653   return (0);
654 } /* int conn_read */
655 /* #endif HAVE_SYSCTLBYNAME */
656
657 #elif HAVE_LIBKVM_NLIST
658 static int kread (u_long addr, void *buf, int size)
659 {
660   int status;
661
662   status = kvm_read (kvmd, addr, buf, size);
663   if (status != size)
664   {
665     ERROR ("tcpconns plugin: kvm_read failed (got %i, expected %i): %s\n",
666         status, size, kvm_geterr (kvmd));
667     return (-1);
668   }
669   return (0);
670 } /* int kread */
671
672 static int conn_init (void)
673 {
674   char buf[_POSIX2_LINE_MAX];
675   struct nlist nl[] =
676   {
677 #define N_TCBTABLE 0
678     { "_tcbtable" },
679     { "" }
680   };
681   int status;
682
683   kvmd = kvm_openfiles (NULL, NULL, NULL, O_RDONLY, buf);
684   if (kvmd == NULL)
685   {
686     ERROR ("tcpconns plugin: kvm_openfiles failed: %s", buf);
687     return (-1);
688   }
689
690   status = kvm_nlist (kvmd, nl);
691   if (status < 0)
692   {
693     ERROR ("tcpconns plugin: kvm_nlist failed with status %i.", status);
694     return (-1);
695   }
696
697   if (nl[N_TCBTABLE].n_type == 0)
698   {
699     ERROR ("tcpconns plugin: Error looking up kernel's namelist: "
700         "N_TCBTABLE is invalid.");
701     return (-1);
702   }
703
704   inpcbtable_off = (u_long) nl[N_TCBTABLE].n_value;
705   inpcbtable_ptr = (struct inpcbtable *) nl[N_TCBTABLE].n_value;
706
707   return (0);
708 } /* int conn_init */
709
710 static int conn_read (void)
711 {
712   struct inpcbtable table;
713   struct inpcb *head;
714   struct inpcb *next;
715   struct inpcb inpcb;
716   struct tcpcb tcpcb;
717   int status;
718
719   conn_reset_port_entry ();
720
721   /* Read the pcbtable from the kernel */
722   status = kread (inpcbtable_off, &table, sizeof (table));
723   if (status != 0)
724     return (-1);
725
726   /* Get the `head' pcb */
727   head = (struct inpcb *) &(inpcbtable_ptr->inpt_queue);
728   /* Get the first pcb */
729   next = (struct inpcb *)CIRCLEQ_FIRST (&table.inpt_queue);
730
731   while (next != head)
732   {
733     /* Read the pcb pointed to by `next' into `inpcb' */
734     kread ((u_long) next, &inpcb, sizeof (inpcb));
735
736     /* Advance `next' */
737     next = (struct inpcb *)CIRCLEQ_NEXT (&inpcb, inp_queue);
738
739     /* Ignore sockets, that are not connected. */
740 #ifdef __NetBSD__
741     if (inpcb.inp_af == AF_INET6)
742       continue; /* XXX see netbsd/src/usr.bin/netstat/inet6.c */
743 #else
744     if (!(inpcb.inp_flags & INP_IPV6)
745         && (inet_lnaof(inpcb.inp_laddr) == INADDR_ANY))
746       continue;
747     if ((inpcb.inp_flags & INP_IPV6)
748         && IN6_IS_ADDR_UNSPECIFIED (&inpcb.inp_laddr6))
749       continue;
750 #endif
751
752     kread ((u_long) inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb));
753     conn_handle_ports (ntohs(inpcb.inp_lport), ntohs(inpcb.inp_fport), tcpcb.t_state);
754   } /* while (next != head) */
755
756   conn_submit_all ();
757
758   return (0);
759 }
760 /* #endif HAVE_LIBKVM_NLIST */
761
762 #elif KERNEL_AIX
763
764 static int conn_read (void)
765 {
766   int size;
767   int i;
768   int nconn;
769   void *data;
770   struct netinfo_header *header;
771   struct netinfo_conn *conn;
772
773   conn_reset_port_entry ();
774
775   size = netinfo(NETINFO_TCP, 0, 0, 0);
776   if (size < 0)
777   {
778     ERROR ("tcpconns plugin: netinfo failed return: %i", size);
779     return (-1);
780   }
781
782   if (size == 0)
783     return (0);
784
785   if ((size - sizeof (struct netinfo_header)) % sizeof (struct netinfo_conn))
786   {
787     ERROR ("tcpconns plugin: invalid buffer size");
788     return (-1);
789   }
790
791   data = malloc(size);
792   if (data == NULL)
793   {
794     ERROR ("tcpconns plugin: malloc failed");
795     return (-1);
796   }
797
798   if (netinfo(NETINFO_TCP, data, &size, 0) < 0)
799   {
800     ERROR ("tcpconns plugin: netinfo failed");
801     free(data);
802     return (-1);
803   }
804
805   header = (struct netinfo_header *)data;
806   nconn = header->size;
807   conn = (struct netinfo_conn *)(data + sizeof(struct netinfo_header));
808
809   for (i=0; i < nconn; conn++, i++)
810   {
811     conn_handle_ports (conn->srcport, conn->dstport, conn->tcp_state);
812   }
813
814   free(data);
815
816   conn_submit_all ();
817
818   return (0);
819 }
820 #endif /* KERNEL_AIX */
821
822 void module_register (void)
823 {
824         plugin_register_config ("tcpconns", conn_config,
825                         config_keys, config_keys_num);
826 #if KERNEL_LINUX
827         plugin_register_init ("tcpconns", conn_init);
828 #elif HAVE_SYSCTLBYNAME
829         /* no initialization */
830 #elif HAVE_LIBKVM_NLIST
831         plugin_register_init ("tcpconns", conn_init);
832 #elif KERNEL_AIX
833         /* no initialization */
834 #endif
835         plugin_register_read ("tcpconns", conn_read);
836 } /* void module_register */
837
838 /*
839  * vim: set shiftwidth=2 softtabstop=2 tabstop=8 fdm=marker :
840  */