iptables plugin: Fix building under non-Linux systems.
[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  *  Files will go into iptables-chain/comment.rrd files
47  */
48 static char *file_template   = "iptables-%s.rrd";
49
50 /*
51  * Removed packet count for now, should have config option if you want to save
52  * them Although other collectd models don't seem to care much for options
53  * eitherway for what to log
54  */
55 static char *ds_def[] =
56 {
57 /*      "DS:packets:COUNTER:"COLLECTD_HEARTBEAT":0:U", */
58         "DS:bytes:DERIVE:"COLLECTD_HEARTBEAT":0:U",
59         NULL
60 };
61 static int ds_num = 1;
62
63 #if IPTABLES_HAVE_READ
64 /*
65  * Config format should be `Chain table chainname',
66  * e. g. `Chain mangle incoming'
67  */
68 static char *config_keys[] =
69 {
70         "Chain",
71         NULL
72 };
73 static int config_keys_num = 1;
74 /*
75     Each table/chain combo that will be queried goes into this list
76 */
77 typedef struct {
78     char table[16];
79     char name[32];
80 } ip_chain_t;
81
82 static ip_chain_t **chain_list = NULL;
83 static int chain_num = 0;
84
85 static int iptables_config (char *key, char *value)
86 {
87         if (strcasecmp (key, "Chain") == 0)
88         {
89                 ip_chain_t temp, *final, **list;
90                 char *chain;
91                 int tLen;
92                 
93                 memset( &temp, 0, sizeof( temp ));
94         
95                 /* simple parsing, only allow a space... */
96                 chain = rindex(value, ' ' );
97                 if (!chain) 
98                 {
99                         syslog (LOG_EMERG, "missing chain." );
100                         return (1);
101                 }
102                 tLen = (int)(chain - value);
103                 if ( tLen > sizeof( temp.table ))
104                 {
105                         syslog (LOG_EMERG, "table too long." );
106                         return (1);
107                 }
108                 memcpy( temp.table, value, tLen );
109                 temp.table[tLen] = 0; 
110                 chain++;
111                 strncpy( temp.name, chain, sizeof( temp.name ));
112                         
113                 list = (ip_chain_t **) realloc (chain_list, (chain_num + 1) * sizeof (ip_chain_t *));
114                 if ( list == NULL )
115                 {
116                         syslog (LOG_EMERG, "Cannot allocate more memory.");
117                         return (1);
118                 }
119                 chain_list = list;
120                 final = (ip_chain_t *) malloc( sizeof(temp) );
121                 if (final == NULL) 
122                 {
123                         syslog (LOG_EMERG, "Cannot allocate memory.");
124                         return (1);
125                 }
126                 *final = temp;
127                 chain_list[chain_num++] = final;
128         } else 
129         {
130                 return (-1);
131         }
132
133         return (0);
134 }
135 #endif /* IPTABLES_HAVE_READ */
136
137 static void iptables_init (void)
138 {       
139     return;
140 }
141
142 static void iptables_write (char *host, char *inst, char *val) 
143 {
144         char file[BUFSIZE];
145         int status;
146
147         status = snprintf (file, BUFSIZE, file_template, inst);
148         if (status < 1)
149                 return;
150         else if (status >= BUFSIZE)
151                 return;
152
153         rrd_update_file (host, file, val, ds_def, ds_num);
154 }
155
156 #if IPTABLES_HAVE_READ
157 static int submit_match (const struct ipt_entry_match *match,
158                 const struct ipt_entry *entry, const ip_chain_t *chain) 
159 {
160     char name[BUFSIZE];
161     char buf[BUFSIZE];
162     int status;
163
164     /* Only log rules that have a comment, although could probably also do numerical targets sometime */
165     if ( strcmp( match->u.user.name, "comment" ) )
166         return 0;
167
168 /*
169     This would also add the table name to the name, but seems a bit overkill
170     status = snprintf (name, BUFSIZE, "%s-%s/%s",
171         table->table, table->chain, match->data );
172 */
173     status = snprintf (name, BUFSIZE, "%s/%s", chain->name, match->data );
174
175     if ((status >= BUFSIZE) || (status < 1))
176         return 0;
177
178     status = snprintf (buf, BUFSIZE, "%u:%lld", /* ":lld", */
179                                 (unsigned int) curtime,
180                                 /* entry->counters.pcnt, */
181                                  entry->counters.bcnt );
182     if ((status >= BUFSIZE) || (status < 1))
183         return 0;
184
185     plugin_submit (MODULE_NAME, name, buf);
186
187     return 0;
188 } /* int submit_match */
189
190 static void submit_chain( iptc_handle_t *handle, ip_chain_t *chain ) {
191     const struct ipt_entry *entry;
192
193     /* Find first rule for chain and use the iterate macro */    
194     entry = iptc_first_rule( chain->name, handle );
195     while ( entry ) {
196         IPT_MATCH_ITERATE( entry, submit_match, entry, chain );
197         entry = iptc_next_rule( entry, handle );
198     }
199 }
200
201
202 static void iptables_read (void) {
203     int i;
204
205     /* Init the iptc handle structure and query the correct table */    
206     for( i = 0; i < chain_num; i++) {
207         iptc_handle_t handle;
208         ip_chain_t *chain;
209         
210         chain = chain_list[i];
211         if (!chain)
212             continue;
213         handle = iptc_init( chain->table );
214         if (!handle)
215             continue;
216         submit_chain( &handle, chain );
217         iptc_free( &handle );
218     }
219 }
220 #else /* !IPTABLES_HAVE_READ */
221 # define iptables_read NULL
222 #endif
223
224 void module_register (void)
225 {
226         plugin_register (MODULE_NAME, iptables_init, iptables_read, iptables_write);
227 #if IPTABLES_HAVE_READ
228         cf_register (MODULE_NAME, iptables_config, config_keys, config_keys_num);
229 #endif
230 }
231
232 #undef BUFSIZE
233 #undef MODULE_NAME
234
235 /*
236  * vim:shiftwidth=4:softtabstop=4:tabstop=8
237  */