src/plugin.c: Remove a legacy debug message.
[collectd.git] / src / iptables.c
1 /**
2  * collectd - src/iptables.c
3  * Copyright (C) 2007 Sjoerd van der Berg
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
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  * Authors:
20  *  Sjoerd van der Berg <harekiet at users.sourceforge.net>
21  **/
22
23 #include "collectd.h"
24 #include "common.h"
25 #include "plugin.h"
26 #include "configfile.h"
27
28 #if HAVE_LIBIPTC_LIBIPTC_H
29 # include <libiptc/libiptc.h>
30 #endif
31
32 #if HAVE_LIBIPTC_LIBIPTC_H
33 # define IPTABLES_HAVE_READ 1
34 #else
35 # define IPTABLES_HAVE_READ 0
36 #endif
37
38 #define MODULE_NAME "iptables"
39 #define BUFSIZE 512
40
41 /*
42  * (Module-)Global variables
43  */
44
45 /*
46  * Removed packet count for now, should have config option if you want to save
47  * them Although other collectd models don't seem to care much for options
48  * eitherway for what to log
49  */
50 #if IPTABLES_HAVE_READ
51 /*
52  * Config format should be `Chain table chainname',
53  * e. g. `Chain mangle incoming'
54  */
55 static const char *config_keys[] =
56 {
57         "Chain",
58         NULL
59 };
60 static int config_keys_num = 1;
61 /*
62     Each table/chain combo that will be queried goes into this list
63 */
64 #ifndef XT_TABLE_MAXNAMELEN
65 # define XT_TABLE_MAXNAMELEN 32
66 #endif
67 typedef struct {
68     char table[XT_TABLE_MAXNAMELEN];
69     char chain[XT_TABLE_MAXNAMELEN];
70     union
71     {
72         int   num;
73         char *comment;
74     } rule;
75     enum
76     {
77         RTYPE_NUM,
78         RTYPE_COMMENT,
79         RTYPE_COMMENT_ALL
80     } rule_type;
81     char name[64];
82 } ip_chain_t;
83
84 static ip_chain_t **chain_list = NULL;
85 static int chain_num = 0;
86
87 static int iptables_config (const char *key, const char *value)
88 {
89         if (strcasecmp (key, "Chain") == 0)
90         {
91                 ip_chain_t temp, *final, **list;
92                 char *table;
93                 int   table_len;
94                 char *chain;
95                 int   chain_len;
96
97                 char *value_copy;
98                 char *fields[4];
99                 int   fields_num;
100                 
101                 memset (&temp, 0, sizeof (temp));
102
103                 value_copy = strdup (value);
104                 if (value_copy == NULL)
105                 {
106                     char errbuf[1024];
107                     ERROR ("strdup failed: %s",
108                             sstrerror (errno, errbuf, sizeof (errbuf)));
109                     return (1);
110                 }
111
112                 /* Chain <table> <chain> [<comment|num> [name]] */
113                 fields_num = strsplit (value_copy, fields, 4);
114                 if (fields_num < 2)
115                 {
116                     free (value_copy);
117                     return (1);
118                 }
119
120                 table = fields[0];
121                 chain = fields[1];
122
123                 table_len = strlen (table);
124                 if (table_len >= sizeof(temp.table))
125                 {
126                         ERROR ("Table `%s' too long.", table);
127                         free (value_copy);
128                         return (1);
129                 }
130                 strncpy (temp.table, table, table_len);
131                 temp.table[table_len] = '\0';
132
133                 chain_len = strlen (chain);
134                 if (chain_len >= sizeof(temp.chain))
135                 {
136                         ERROR ("Chain `%s' too long.", chain);
137                         free (value_copy);
138                         return (1);
139                 }
140                 strncpy (temp.chain, chain, chain_len);
141                 temp.chain[chain_len] = '\0'; 
142
143                 if (fields_num >= 3)
144                 {
145                     char *comment = fields[2];
146                     int   rule = atoi (comment);
147
148                     if (rule)
149                     {
150                         temp.rule.num = rule;
151                         temp.rule_type = RTYPE_NUM;
152                     }
153                     else
154                     {
155                         temp.rule.comment = strdup (comment);
156                         if (temp.rule.comment == NULL)
157                         {
158                             free (value_copy);
159                             return (1);
160                         }
161                         temp.rule_type = RTYPE_COMMENT;
162                     }
163                 }
164                 else
165                 {
166                     temp.rule_type = RTYPE_COMMENT_ALL;
167                 }
168
169                 if (fields_num >= 4)
170                     strncpy (temp.name, fields[3], sizeof (temp.name) - 1);
171
172                 free (value_copy);
173                 value_copy = NULL;
174                 table = NULL;
175                 chain = NULL;
176
177                 list = (ip_chain_t **) realloc (chain_list, (chain_num + 1) * sizeof (ip_chain_t *));
178                 if (list == NULL)
179                 {
180                     char errbuf[1024];
181                     ERROR ("realloc failed: %s",
182                             sstrerror (errno, errbuf, sizeof (errbuf)));
183                     return (1);
184                 }
185
186                 chain_list = list;
187                 final = (ip_chain_t *) malloc( sizeof(temp) );
188                 if (final == NULL) 
189                 {
190                     char errbuf[1024];
191                     ERROR ("malloc failed: %s",
192                             sstrerror (errno, errbuf, sizeof (errbuf)));
193                     return (1);
194                 }
195                 memcpy (final, &temp, sizeof (temp));
196                 chain_list[chain_num] = final;
197                 chain_num++;
198
199                 DEBUG ("Chain #%i: table = %s; chain = %s;", chain_num, final->table, final->chain);
200         }
201         else 
202         {
203                 return (-1);
204         }
205
206         return (0);
207 } /* int iptables_config */
208 #endif /* IPTABLES_HAVE_READ */
209
210 #if IPTABLES_HAVE_READ
211 /* This needs to return `int' for IPT_MATCH_ITERATE to work. */
212 static int submit_match (const struct ipt_entry_match *match,
213                 const struct ipt_entry *entry,
214                 const ip_chain_t *chain,
215                 int rule_num) 
216 {
217     int status;
218     value_t values[1];
219     value_list_t vl = VALUE_LIST_INIT;
220
221     /* Select the rules to collect */
222     if (chain->rule_type == RTYPE_NUM)
223     {
224         if (chain->rule.num != rule_num)
225             return (0);
226     }
227     else
228     {
229         if (strcmp (match->u.user.name, "comment") != 0)
230             return (0);
231         if ((chain->rule_type == RTYPE_COMMENT)
232                 && (strcmp (chain->rule.comment, (char *) match->data) != 0))
233             return (0);
234     }
235
236     vl.values = values;
237     vl.values_len = 1;
238     vl.time = time (NULL);
239     strcpy (vl.host, hostname_g);
240     strcpy (vl.plugin, "iptables");
241
242     status = snprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
243             "%s-%s", chain->table, chain->chain);
244     if ((status >= sizeof (vl.plugin_instance)) || (status < 1))
245         return (0);
246
247     if (chain->name[0] != '\0')
248     {
249         strncpy (vl.type_instance, chain->name, sizeof (vl.type_instance));
250     }
251     else
252     {
253         if (chain->rule_type == RTYPE_NUM)
254             snprintf (vl.type_instance, sizeof (vl.type_instance),
255                     "%i", chain->rule.num);
256         else
257             strncpy (vl.type_instance, (char *) match->data,
258                     sizeof (vl.type_instance));
259     }
260     vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
261
262     values[0].counter = (counter_t) entry->counters.bcnt;
263     plugin_dispatch_values ("ipt_bytes", &vl);
264
265     values[0].counter = (counter_t) entry->counters.pcnt;
266     plugin_dispatch_values ("ipt_packets", &vl);
267
268     return (0);
269 } /* void submit_match */
270
271 static void submit_chain( iptc_handle_t *handle, ip_chain_t *chain ) {
272     const struct ipt_entry *entry;
273     int rule_num;
274
275     /* Find first rule for chain and use the iterate macro */    
276     entry = iptc_first_rule( chain->chain, handle );
277     if (entry == NULL)
278     {
279         DEBUG ("iptc_first_rule failed: %s", iptc_strerror (errno));
280         return;
281     }
282
283     rule_num = 1;
284     while (entry)
285     {
286         if (chain->rule_type == RTYPE_NUM)
287         {
288             submit_match (NULL, entry, chain, rule_num);
289         }
290         else
291         {
292             IPT_MATCH_ITERATE( entry, submit_match, entry, chain, rule_num );
293         }
294
295         entry = iptc_next_rule( entry, handle );
296         rule_num++;
297     } /* while (entry) */
298 }
299
300
301 static int iptables_read (void)
302 {
303     int i;
304     static complain_t complaint;
305
306     /* Init the iptc handle structure and query the correct table */    
307     for (i = 0; i < chain_num; i++)
308     {
309         iptc_handle_t handle;
310         ip_chain_t *chain;
311         
312         chain = chain_list[i];
313         if (!chain)
314         {
315             DEBUG ("chain == NULL");
316             continue;
317         }
318
319         handle = iptc_init( chain->table );
320         if (!handle)
321         {
322             DEBUG ("iptc_init (%s) failed: %s", chain->table, iptc_strerror (errno));
323             plugin_complain (LOG_ERR, &complaint, "iptc_init (%s) failed: %s",
324                     chain->table, iptc_strerror (errno));
325             continue;
326         }
327         plugin_relief (LOG_INFO, &complaint, "iptc_init (%s) succeeded",
328                 chain->table);
329
330         submit_chain (&handle, chain);
331         iptc_free (&handle);
332     }
333
334     return (0);
335 } /* int iptables_read */
336
337 static int iptables_shutdown (void)
338 {
339     int i;
340
341     for (i = 0; i < chain_num; i++)
342     {
343         if ((chain_list[i] != NULL) && (chain_list[i]->rule_type == RTYPE_COMMENT))
344         {
345             sfree (chain_list[i]->rule.comment);
346         }
347         sfree (chain_list[i]);
348     }
349     sfree (chain_list);
350
351     return (0);
352 } /* int iptables_shutdown */
353 #endif /* IPTABLES_HAVE_READ */
354
355 void module_register (void)
356 {
357 #if IPTABLES_HAVE_READ
358     plugin_register_config ("iptables", iptables_config,
359             config_keys, config_keys_num);
360     plugin_register_read ("iptables", iptables_read);
361     plugin_register_shutdown ("iptables", iptables_shutdown);
362 #endif
363 } /* void module_register */
364
365 #undef BUFSIZE
366 #undef MODULE_NAME
367
368 /*
369  * vim:shiftwidth=4:softtabstop=4:tabstop=8
370  */