iptables: (tomj) fixed badly broken memory handling, basic stuff now working
[ipt_ACCOUNT] / linux / net / ipv4 / netfilter / ipt_ACCOUNT.c
1 /*
2  * This is a module which is used for counting packets.
3  */
4 #include <linux/module.h>
5 #include <linux/skbuff.h>
6 #include <linux/ip.h>
7 #include <linux/spinlock.h>
8 #include <net/icmp.h>
9 #include <net/udp.h>
10 #include <net/tcp.h>
11 #include <linux/netfilter_ipv4/ip_tables.h>
12
13 struct in_device;
14 #include <net/route.h>
15 #include <linux/netfilter_ipv4/ipt_ACCOUNT.h>
16
17 //#if 0
18 #define DEBUGP printk
19 //#else
20 //#define DEBUGP(format, args...)
21 //#endif
22
23 struct ipt_account_table *ipt_account_tables = NULL;
24 struct ipt_account_handle *ipt_account_handles = NULL;
25
26 static spinlock_t ipt_account_lock = SPIN_LOCK_UNLOCKED;
27
28 static unsigned int
29 ipt_account_target(struct sk_buff **pskb,
30     unsigned int hooknum,
31     const struct net_device *in,
32     const struct net_device *out,
33     const void *targinfo,
34     void *userinfo)
35 {
36     spin_lock_bh(&ipt_account_lock);
37     spin_unlock_bh(&ipt_account_lock);
38
39     return IPT_CONTINUE;
40 }
41
42 /* Recursive free of all data structures */
43 void ipt_account_data_free(void *data, unsigned char depth)
44 {
45     // Empty data set
46     if (!data)
47         return;
48         
49     // Free for 8 bit network
50     if (depth == 0)
51     {
52         free_page((unsigned long)data);
53         data = NULL;
54         return;
55     }
56     
57     // Free for 16 bit network
58     if (depth == 1)
59     {
60         struct ipt_account_mask_16 *mask_16 = (struct ipt_account_mask_16 *)data;
61         unsigned char b;
62         for (b=0; b < 255; b++)
63         {
64             if (mask_16->mask_24[b] != 0)
65             {
66                 free_page((unsigned long)mask_16->mask_24[b]);
67                 mask_16->mask_24[b] = NULL;
68             }
69         }
70         free_page((unsigned long)data);
71         data = NULL;
72         return;
73     } 
74    
75     // Free for 24 bit network
76     if (depth == 3)
77     {
78         unsigned char a, b;
79         for (a=0; a < 255; a++)
80         {
81             if (((struct ipt_account_mask_8 *)data)->mask_16[a])
82             {
83                 struct ipt_account_mask_16 *mask_16 = (struct ipt_account_mask_16*)((struct ipt_account_mask_8 *)data)->mask_16[a];
84                 for (b=0; b < 255; b++)
85                 {
86                     if (mask_16->mask_24[b]) {
87                         free_page((unsigned long)mask_16->mask_24[b]);
88                         mask_16->mask_24[b] = NULL;
89                     }
90                 }
91                 free_page((unsigned long)mask_16);
92                 mask_16 = NULL;
93             }
94         }
95         free_page((unsigned long)data);
96         data = NULL;
97         return;
98     }
99     
100     printk("ACCOUNT: ipt_account_data_free called with unknown depth: %d\n", depth);
101     return;
102 }
103
104 /* Look for existing table / insert new one. Return internal ID or -1 on error */
105 int ipt_account_table_insert(char *name, unsigned int ip, unsigned int netmask)
106 {
107     unsigned int i;
108
109     DEBUGP("ACCOUNT: ipt_account_table_insert: %s, %u.%u.%u.%u/%u.%u.%u.%u\n", name, NIPQUAD(ip), NIPQUAD(netmask));
110
111     // Look for existing table
112     for (i = 0; i < ACCOUNT_MAX_TABLES; i++)
113     {
114         if (strcmp(ipt_account_tables[i].name, name) == 0)
115         {
116             DEBUGP("ACCOUNT: Found existing slot: %d - %u.%u.%u.%u/%u.%u.%u.%u\n", i,
117                    NIPQUAD(ipt_account_tables[i].ip), NIPQUAD(ipt_account_tables[i].netmask));
118             
119             if (ipt_account_tables[i].ip != ip || ipt_account_tables[i].netmask != netmask)
120             {
121                 printk("ACCOUNT: Table %s found, but IP/netmask mismatch. IP/netmask found: %u.%u.%u.%u/%u.%u.%u.%u\n",
122                         name, NIPQUAD(ipt_account_tables[i].ip), NIPQUAD(ipt_account_tables[i].netmask));
123                 return -1;
124             }
125
126             ipt_account_tables[i].refcount++;
127             DEBUGP("ACCOUNT: Refcount: %d\n", ipt_account_tables[i].refcount);
128             return i;
129         }
130     }
131
132     // Insert new table
133     for (i = 0; i < ACCOUNT_MAX_TABLES; i++)
134     {
135         // Found free slot
136         if (ipt_account_tables[i].name[0] == 0)
137         {
138             DEBUGP("ACCOUNT: Found free slot: %d\n", i);
139         
140             strncpy (ipt_account_tables[i].name, name, ACCOUNT_TABLE_NAME_LEN-1);
141             
142             ipt_account_tables[i].ip = ip;
143             ipt_account_tables[i].netmask = netmask;
144             
145             // Calculate netsize
146             unsigned int j, calc_mask, netsize=0;
147             calc_mask = htonl(netmask);
148             for (j = 31; j > 0; j--)
149             {
150                 if (calc_mask&(1<<j))
151                     netsize++;
152                 else
153                     break;
154             }
155             
156             // Calculate depth from netsize
157             if (netsize >= 24)
158                 ipt_account_tables[i].depth = 0;
159             else if (netsize >= 16)
160                 ipt_account_tables[i].depth = 1;
161             else if(netsize >= 8)
162                 ipt_account_tables[i].depth = 2;
163             
164             printk("ACCOUNT: calculated netsize: %u -> ipt_account_table depth %u\n", netsize, ipt_account_tables[i].depth);
165                         
166             ipt_account_tables[i].refcount++;
167             if (!(ipt_account_tables[i].data = (void *)get_zeroed_page(GFP_KERNEL)))
168             {
169                 printk("ACCOUNT: out of memory for data of table: %s\n", name);
170                 memset(&ipt_account_tables[i], 0, sizeof(struct ipt_account_table));
171                 return -1;
172             }
173             
174             return i;
175         }
176     }
177         
178     // No free slot found
179     printk("ACCOUNT: No free table slot found (max: %d). Please increase ACCOUNT_MAX_TABLES.\n", ACCOUNT_MAX_TABLES);
180     return -1;
181 }
182
183 static int ipt_account_checkentry(const char *tablename,
184     const struct ipt_entry *e,
185     void *targinfo,
186     unsigned int targinfosize,
187     unsigned int hook_mask)
188 {
189     struct ipt_account_info *info = targinfo;
190     
191     if (targinfosize != IPT_ALIGN(sizeof(struct ipt_account_info))) {
192         DEBUGP("ACCOUNT: targinfosize %u != %u\n",
193                 targinfosize, IPT_ALIGN(sizeof(struct ipt_account_info)));
194         return 0;
195     }
196
197     spin_lock_bh(&ipt_account_lock);
198     int table_nr = ipt_account_table_insert(info->table_name, info->net_ip, info->net_mask);
199     if (table_nr == -1)
200     {
201         printk("ACCOUNT: Table insert problem. Aborting\n");
202         spin_unlock_bh(&ipt_account_lock);
203         return 0;
204     }
205     // Table nr caching so we don't have to do an extra string compare for every packet
206     info->table_nr = table_nr;
207     
208     spin_unlock_bh(&ipt_account_lock);
209
210     return 1;
211 }
212
213 void ipt_account_deleteentry(void *targinfo, unsigned int targinfosize)
214 {
215     unsigned int i;
216     struct ipt_account_info *info = targinfo;
217     
218     if (targinfosize != IPT_ALIGN(sizeof(struct ipt_account_info))) {
219         DEBUGP("ACCOUNT: targinfosize %u != %u\n",
220                 targinfosize, IPT_ALIGN(sizeof(struct ipt_account_info)));
221     }
222
223     spin_lock_bh(&ipt_account_lock);
224     
225     DEBUGP("ACCOUNT: ipt_account_deleteentry called for table: %s (#%d)\n", info->table_name, info->table_nr);
226     
227     info->table_nr = -1;    // Set back to original state
228     
229     // Look for table
230     for (i = 0; i < ACCOUNT_MAX_TABLES; i++)
231     {
232         if (strcmp(ipt_account_tables[i].name, info->table_name) == 0)
233         {
234             DEBUGP("ACCOUNT: Found table at slot: %d\n", i);
235             
236             ipt_account_tables[i].refcount--;
237             DEBUGP("ACCOUNT: Refcount left: %d\n", ipt_account_tables[i].refcount);
238
239             // Table not needed anymore?
240             if (ipt_account_tables[i].refcount == 0)
241             {
242                 DEBUGP("ACCOUNT: Destroying table at slot: %d\n", i);
243                 ipt_account_data_free(ipt_account_tables[i].data, ipt_account_tables[i].depth);
244                 memset(&ipt_account_tables[i], 0, sizeof(struct ipt_account_table));
245             }
246             
247             spin_unlock_bh(&ipt_account_lock);
248             return;
249         }
250     }
251
252     // Table not found
253     printk("ACCOUNT: Table %s not found for destroy\n", info->table_name);
254     spin_unlock_bh(&ipt_account_lock);
255 }
256
257 static struct ipt_target ipt_account_reg
258 = { { NULL, NULL }, "ACCOUNT", ipt_account_target, ipt_account_checkentry, ipt_account_deleteentry, 
259     THIS_MODULE };
260
261 static int __init init(void)
262 {
263     if (!(ipt_account_tables = kmalloc(ACCOUNT_MAX_TABLES*sizeof(struct ipt_account_table), GFP_KERNEL)))
264     {
265             printk("ACCOUNT: Out of memory allocating account_tables structure");
266             return -EINVAL;
267     }
268     memset(ipt_account_tables, 0, ACCOUNT_MAX_TABLES*sizeof(struct ipt_account_table));
269                             
270     if (!(ipt_account_handles = kmalloc(ACCOUNT_MAX_HANDLES*sizeof(struct ipt_account_handle), GFP_KERNEL)))
271     {
272             printk("ACCOUNT: Out of memory allocating account_handles structure");
273             kfree (ipt_account_tables);
274             ipt_account_tables = NULL;
275             return -EINVAL;
276     }
277     memset(ipt_account_handles, 0, ACCOUNT_MAX_HANDLES*sizeof(struct ipt_account_handle));
278     
279     if (ipt_register_target(&ipt_account_reg))
280             return -EINVAL;
281
282     return 0;
283 }
284
285 static void __exit fini(void)
286 {
287     ipt_unregister_target(&ipt_account_reg);
288
289     kfree(ipt_account_tables);
290     ipt_account_tables = NULL;
291         
292     kfree(ipt_account_handles);
293     ipt_account_handles = NULL;
294 }
295
296 module_init(init);
297 module_exit(fini);
298 MODULE_LICENSE("GPL");