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