Include `config.h' from all files.
[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                         strncpy (temp.rule.comment, comment,
156                                 sizeof (temp.rule.comment) - 1);
157                         temp.rule_type = RTYPE_COMMENT;
158                     }
159                 }
160                 else
161                 {
162                     temp.rule_type = RTYPE_COMMENT_ALL;
163                 }
164
165                 if (fields_num >= 4)
166                     strncpy (temp.name, fields[3], sizeof (temp.name) - 1);
167
168                 free (value_copy);
169                 value_copy = NULL;
170                 table = NULL;
171                 chain = NULL;
172
173                 list = (ip_chain_t **) realloc (chain_list, (chain_num + 1) * sizeof (ip_chain_t *));
174                 if (list == NULL)
175                 {
176                     char errbuf[1024];
177                     ERROR ("realloc failed: %s",
178                             sstrerror (errno, errbuf, sizeof (errbuf)));
179                     return (1);
180                 }
181
182                 chain_list = list;
183                 final = (ip_chain_t *) malloc( sizeof(temp) );
184                 if (final == NULL) 
185                 {
186                     char errbuf[1024];
187                     ERROR ("malloc failed: %s",
188                             sstrerror (errno, errbuf, sizeof (errbuf)));
189                     return (1);
190                 }
191                 memcpy (final, &temp, sizeof (temp));
192                 chain_list[chain_num] = final;
193                 chain_num++;
194
195                 DEBUG ("Chain #%i: table = %s; chain = %s;", chain_num, final->table, final->chain);
196         }
197         else 
198         {
199                 return (-1);
200         }
201
202         return (0);
203 } /* int iptables_config */
204 #endif /* IPTABLES_HAVE_READ */
205
206 #if IPTABLES_HAVE_READ
207 /* This needs to return `int' for IPT_MATCH_ITERATE to work. */
208 static int submit_match (const struct ipt_entry_match *match,
209                 const struct ipt_entry *entry,
210                 const ip_chain_t *chain,
211                 int rule_num) 
212 {
213     int status;
214     value_t values[1];
215     value_list_t vl = VALUE_LIST_INIT;
216
217     /* Select the rules to collect */
218     if (chain->rule_type == RTYPE_NUM)
219     {
220         if (chain->rule.num != rule_num)
221             return (0);
222     }
223     else
224     {
225         if (strcmp (match->u.user.name, "comment") != 0)
226             return (0);
227         if ((chain->rule_type == RTYPE_COMMENT)
228                 && (strcmp (chain->rule.comment, (char *) match->data) != 0))
229             return (0);
230     }
231
232     vl.values = values;
233     vl.values_len = 1;
234     vl.time = time (NULL);
235     strcpy (vl.host, hostname_g);
236     strcpy (vl.plugin, "iptables");
237
238     status = snprintf (vl.plugin_instance, sizeof (vl.plugin_instance),
239             "%s-%s", chain->table, chain->chain);
240     if ((status >= sizeof (vl.plugin_instance)) || (status < 1))
241         return (0);
242
243     if (chain->name[0] != '\0')
244     {
245         strncpy (vl.type_instance, chain->name, sizeof (vl.type_instance));
246     }
247     else
248     {
249         if (chain->rule_type == RTYPE_NUM)
250             snprintf (vl.type_instance, sizeof (vl.type_instance),
251                     "%i", chain->rule.num);
252         else
253             strncpy (vl.type_instance, (char *) match->data,
254                     sizeof (vl.type_instance));
255     }
256     vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
257
258     values[0].counter = (counter_t) entry->counters.bcnt;
259     plugin_dispatch_values ("ipt_bytes", &vl);
260
261     values[0].counter = (counter_t) entry->counters.pcnt;
262     plugin_dispatch_values ("ipt_packets", &vl);
263
264     return (0);
265 } /* void submit_match */
266
267 static void submit_chain( iptc_handle_t *handle, ip_chain_t *chain ) {
268     const struct ipt_entry *entry;
269     int rule_num;
270
271     /* Find first rule for chain and use the iterate macro */    
272     entry = iptc_first_rule( chain->chain, handle );
273     if (entry == NULL)
274     {
275         DEBUG ("iptc_first_rule failed: %s", iptc_strerror (errno));
276         return;
277     }
278
279     rule_num = 1;
280     while (entry)
281     {
282         if (chain->rule_type == RTYPE_NUM)
283         {
284             submit_match (NULL, entry, chain, rule_num);
285         }
286         else
287         {
288             IPT_MATCH_ITERATE( entry, submit_match, entry, chain, rule_num );
289         }
290
291         entry = iptc_next_rule( entry, handle );
292         rule_num++;
293     } /* while (entry) */
294 }
295
296
297 static int iptables_read (void)
298 {
299     int i;
300     static complain_t complaint;
301
302     /* Init the iptc handle structure and query the correct table */    
303     for (i = 0; i < chain_num; i++)
304     {
305         iptc_handle_t handle;
306         ip_chain_t *chain;
307         
308         chain = chain_list[i];
309         if (!chain)
310         {
311             DEBUG ("chain == NULL");
312             continue;
313         }
314
315         handle = iptc_init( chain->table );
316         if (!handle)
317         {
318             DEBUG ("iptc_init (%s) failed: %s", chain->table, iptc_strerror (errno));
319             plugin_complain (LOG_ERR, &complaint, "iptc_init (%s) failed: %s",
320                     chain->table, iptc_strerror (errno));
321             continue;
322         }
323         plugin_relief (LOG_INFO, &complaint, "iptc_init (%s) succeeded",
324                 chain->table);
325
326         submit_chain (&handle, chain);
327         iptc_free (&handle);
328     }
329
330     return (0);
331 } /* int iptables_read */
332
333 static int iptables_shutdown (void)
334 {
335     int i;
336
337     for (i = 0; i < chain_num; i++)
338     {
339         sfree (chain_list[i]);
340     }
341     sfree (chain_list);
342
343     return (0);
344 } /* int iptables_shutdown */
345 #endif /* IPTABLES_HAVE_READ */
346
347 void module_register (void)
348 {
349 #if IPTABLES_HAVE_READ
350     plugin_register_config ("iptables", iptables_config,
351             config_keys, config_keys_num);
352     plugin_register_read ("iptables", iptables_read);
353     plugin_register_shutdown ("iptables", iptables_shutdown);
354 #endif
355 } /* void module_register */
356
357 #undef BUFSIZE
358 #undef MODULE_NAME
359
360 /*
361  * vim:shiftwidth=4:softtabstop=4:tabstop=8
362  */