iptables: Remove dependency on table in iterator_entries_cb_t
[platform/upstream/connman.git] / src / iptables.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <getopt.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <sys/errno.h>
32 #include <sys/socket.h>
33 #include <xtables.h>
34
35 #include <linux/netfilter_ipv4/ip_tables.h>
36
37 #include "connman.h"
38
39
40 /*
41  * Some comments on how the iptables API works (some of them from the
42  * source code from iptables and the kernel):
43  *
44  * - valid_hooks: bit indicates valid IDs for hook_entry
45  * - hook_entry[ID] offset to the chain start
46  * - overflows should be end of entry chains, and uncodintional policy nodes.
47  * - policy entry: last entry in a chain
48  * - user chain: end of last builtin + policy entry
49  * - final entry must be error node
50  * - Underflows must be unconditional and use the STANDARD target with
51  *   ACCEPT/DROP
52  * - IPT_SO_GET_INFO and IPT_SO_GET_ENTRIES are used to read a table
53  * - IPT_SO_GET_INFO: struct ipt_getinfo (note the lack of table content)
54  * - IPT_SO_GET_ENTRIES: struct ipt_get_entries (contains only parts of the
55  *   table header/meta info. The table is appended after the header. The entries
56  *   are of the type struct ipt_entry.
57  * - After the ipt_entry the matches are appended. After the matches
58  *   the target is appended.
59  * - ipt_entry->target_offset =  Size of ipt_entry + matches
60  * - ipt_entry->next_offset =  Size of ipt_entry + matches + target
61  * - IPT_SO_SET_REPLACE is used to write a table (contains the complete
62  * - hook_entry and overflow mark the begining and the end of a chain, e.g
63  *     entry hook: pre/in/fwd/out/post -1/0/352/504/-1
64  *     underflow:  pre/in/fwd/out/post -1/200/352/904/-1
65  *   means that INPUT starts at offset 0 and ends at 200 (the start offset to
66  *   the last element). FORWARD has one entry starting/ending at 352. The entry
67  *   has a size of 152. 352 + 152 = 504 which is the start of the OUTPUT chain
68  *   which then ends at 904. PREROUTING and POSTROUTING are invalid hooks in
69  *   the filter table.
70  * - 'iptables -t filter -A INPUT -m mark --mark 999 -j LOG'
71  *   writing that table looks like this:
72  *
73  *   filter valid_hooks 0x0000000e  num_entries 5  size 856
74  *   entry hook: pre/in/fwd/out/post -1/0/376/528/-1
75  *   underflow:  pre/in/fwd/out/post -1/224/376/528/-1
76  *   entry 0x699d30  offset 0  size 224
77  *     RULE  match 0x699da0  target 0x699dd0
78  *             match  mark match 0x3e7
79  *             target  LOG flags 0 level 4
80  *             src 0.0.0.0/0.0.0.0
81  *             dst 0.0.0.0/0.0.0.0
82  *   entry 0x699e10  offset 224  size 152
83  *     RULE  match 0x699e80  target 0x699e80
84  *             target ACCEPT
85  *             src 0.0.0.0/0.0.0.0
86  *             dst 0.0.0.0/0.0.0.0
87  *   entry 0x699ea8  offset 376  size 152
88  *     RULE  match 0x699f18  target 0x699f18
89  *             target ACCEPT
90  *             src 0.0.0.0/0.0.0.0
91  *             dst 0.0.0.0/0.0.0.0
92  *   entry 0x699f40  offset 528  size 152
93  *     RULE  match 0x699fb0  target 0x699fb0
94  *             target ACCEPT
95  *             src 0.0.0.0/0.0.0.0
96  *             dst 0.0.0.0/0.0.0.0
97  *   entry 0x699fd8  offset 680  size 176
98  *     USER CHAIN (ERROR)  match 0x69a048  target 0x69a048
99  *
100  *   Reading the filter table looks like this:
101  *
102  *   filter valid_hooks 0x0000000e  num_entries 5  size 856
103  *   entry hook: pre/in/fwd/out/post -1/0/376/528/-1
104  *   underflow:  pre/in/fwd/out/post -1/224/376/528/-1
105  *   entry 0x25fec28  offset 0  size 224
106  *     CHAIN (INPUT)  match 0x25fec98  target 0x25fecc8
107  *             match  mark match 0x3e7
108  *             target  LOG flags 0 level 4
109  *             src 0.0.0.0/0.0.0.0
110  *             dst 0.0.0.0/0.0.0.0
111  *   entry 0x25fed08  offset 224  size 152
112  *     RULE  match 0x25fed78  target 0x25fed78
113  *             target ACCEPT
114  *             src 0.0.0.0/0.0.0.0
115  *             dst 0.0.0.0/0.0.0.0
116  *   entry 0x25feda0  offset 376  size 152
117  *     CHAIN (FORWARD)  match 0x25fee10  target 0x25fee10
118  *             target ACCEPT
119  *             src 0.0.0.0/0.0.0.0
120  *             dst 0.0.0.0/0.0.0.0
121  *   entry 0x25fee38  offset 528  size 152
122  *     CHAIN (OUTPUT)  match 0x25feea8  target 0x25feea8
123  *             target ACCEPT
124  *             src 0.0.0.0/0.0.0.0
125  *             dst 0.0.0.0/0.0.0.0
126  *   entry 0x25feed0  offset 680  size 176
127  *     End of CHAIN
128  */
129
130 static const char *hooknames[] = {
131         [NF_IP_PRE_ROUTING]     = "PREROUTING",
132         [NF_IP_LOCAL_IN]        = "INPUT",
133         [NF_IP_FORWARD]         = "FORWARD",
134         [NF_IP_LOCAL_OUT]       = "OUTPUT",
135         [NF_IP_POST_ROUTING]    = "POSTROUTING",
136 };
137
138 #define LABEL_ACCEPT  "ACCEPT"
139 #define LABEL_DROP    "DROP"
140 #define LABEL_QUEUE   "QUEUE"
141 #define LABEL_RETURN  "RETURN"
142
143 #define XT_OPTION_OFFSET_SCALE 256
144
145 #define MIN_ALIGN (__alignof__(struct ipt_entry))
146
147 #define ALIGN(s) (((s) + ((MIN_ALIGN)-1)) & ~((MIN_ALIGN)-1))
148
149 struct error_target {
150         struct xt_entry_target t;
151         char error[IPT_TABLE_MAXNAMELEN];
152 };
153
154 struct connman_iptables_entry {
155         int offset;
156         int builtin;
157
158         struct ipt_entry *entry;
159 };
160
161 struct connman_iptables {
162         int ipt_sock;
163
164         struct ipt_getinfo *info;
165         struct ipt_get_entries *blob_entries;
166
167         unsigned int num_entries;
168         unsigned int old_entries;
169         unsigned int size;
170
171         unsigned int underflow[NF_INET_NUMHOOKS];
172         unsigned int hook_entry[NF_INET_NUMHOOKS];
173
174         GList *entries;
175 };
176
177 static GHashTable *table_hash = NULL;
178
179 typedef int (*iterate_entries_cb_t)(struct ipt_entry *entry, int builtin,
180                                         unsigned int hook,size_t size,
181                                         unsigned int offset, void *user_data);
182
183 static int iterate_entries(struct ipt_entry *entries,
184                                 unsigned int valid_hooks,
185                                 unsigned int *hook_entry,
186                                 size_t size, iterate_entries_cb_t cb,
187                                 void *user_data)
188 {
189         unsigned int i, h;
190         int builtin, err;
191         struct ipt_entry *entry;
192
193         if (valid_hooks != 0)
194                 h = __builtin_ffs(valid_hooks) - 1;
195         else
196                 h = NF_INET_NUMHOOKS;
197
198         for (i = 0, entry = entries; i < size;
199                         i += entry->next_offset) {
200                 builtin = -1;
201                 entry = (void *)entries + i;
202
203                 /*
204                  * Find next valid hook which offset is higher
205                  * or equal with the current offset.
206                  */
207                 if (h < NF_INET_NUMHOOKS) {
208                         if (hook_entry[h] < i) {
209                                 valid_hooks ^= (1 << h);
210
211                                 if (valid_hooks != 0)
212                                         h = __builtin_ffs(valid_hooks) - 1;
213                                 else
214                                         h = NF_INET_NUMHOOKS;
215                         }
216
217                         if (hook_entry[h] == i)
218                                 builtin = h;
219                 }
220
221                 err = cb(entry, builtin, h, size, i, user_data);
222                 if (err < 0)
223                         return err;
224
225         }
226
227         return 0;
228 }
229
230 static int target_to_verdict(const char *target_name)
231 {
232         if (!strcmp(target_name, LABEL_ACCEPT))
233                 return -NF_ACCEPT - 1;
234
235         if (!strcmp(target_name, LABEL_DROP))
236                 return -NF_DROP - 1;
237
238         if (!strcmp(target_name, LABEL_QUEUE))
239                 return -NF_QUEUE - 1;
240
241         if (!strcmp(target_name, LABEL_RETURN))
242                 return XT_RETURN;
243
244         return 0;
245 }
246
247 static gboolean is_builtin_target(const char *target_name)
248 {
249         if (!strcmp(target_name, LABEL_ACCEPT) ||
250                 !strcmp(target_name, LABEL_DROP) ||
251                 !strcmp(target_name, LABEL_QUEUE) ||
252                 !strcmp(target_name, LABEL_RETURN))
253                 return TRUE;
254
255         return FALSE;
256 }
257
258 static gboolean is_jump(struct connman_iptables_entry *e)
259 {
260         struct xt_entry_target *target;
261
262         target = ipt_get_target(e->entry);
263
264         if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
265                 struct xt_standard_target *t;
266
267                 t = (struct xt_standard_target *)target;
268
269                 switch (t->verdict) {
270                 case XT_RETURN:
271                 case -NF_ACCEPT - 1:
272                 case -NF_DROP - 1:
273                 case -NF_QUEUE - 1:
274                 case -NF_STOP - 1:
275                         return false;
276
277                 default:
278                         return true;
279                 }
280         }
281
282         return false;
283 }
284
285 static gboolean is_chain(struct connman_iptables *table,
286                                 struct connman_iptables_entry *e)
287 {
288         struct ipt_entry *entry;
289         struct xt_entry_target *target;
290
291         entry = e->entry;
292         if (e->builtin >= 0)
293                 return TRUE;
294
295         target = ipt_get_target(entry);
296         if (!strcmp(target->u.user.name, IPT_ERROR_TARGET))
297                 return TRUE;
298
299         return FALSE;
300 }
301
302 static GList *find_chain_head(struct connman_iptables *table,
303                                 const char *chain_name)
304 {
305         GList *list;
306         struct connman_iptables_entry *head;
307         struct ipt_entry *entry;
308         struct xt_entry_target *target;
309         int builtin;
310
311         for (list = table->entries; list; list = list->next) {
312                 head = list->data;
313                 entry = head->entry;
314
315                 /* Buit-in chain */
316                 builtin = head->builtin;
317                 if (builtin >= 0 && !strcmp(hooknames[builtin], chain_name))
318                         break;
319
320                 /* User defined chain */
321                 target = ipt_get_target(entry);
322                 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET) &&
323                     !strcmp((char *)target->data, chain_name))
324                         break;
325         }
326
327         return list;
328 }
329
330 static GList *find_chain_tail(struct connman_iptables *table,
331                                 const char *chain_name)
332 {
333         struct connman_iptables_entry *tail;
334         GList *chain_head, *list;
335
336         chain_head = find_chain_head(table, chain_name);
337         if (chain_head == NULL)
338                 return NULL;
339
340         /* Then we look for the next chain */
341         for (list = chain_head->next; list; list = list->next) {
342                 tail = list->data;
343
344                 if (is_chain(table, tail))
345                         return list;
346         }
347
348         /* Nothing found, we return the table end */
349         return g_list_last(table->entries);
350 }
351
352
353 static void update_offsets(struct connman_iptables *table)
354 {
355         GList *list, *prev;
356         struct connman_iptables_entry *entry, *prev_entry;
357
358         for (list = table->entries; list; list = list->next) {
359                 entry = list->data;
360
361                 if (list == table->entries) {
362                         entry->offset = 0;
363
364                         continue;
365                 }
366
367                 prev = list->prev;
368                 prev_entry = prev->data;
369
370                 entry->offset = prev_entry->offset +
371                                         prev_entry->entry->next_offset;
372         }
373 }
374
375 static void update_targets_reference(struct connman_iptables *table,
376                                 struct connman_iptables_entry *entry_before,
377                                 struct connman_iptables_entry *modified_entry,
378                                 gboolean is_removing)
379 {
380         struct connman_iptables_entry *tmp;
381         struct xt_standard_target *t;
382         GList *list;
383         int offset;
384
385         offset = modified_entry->entry->next_offset;
386
387         for (list = table->entries; list; list = list->next) {
388                 tmp = list->data;
389
390                 if (!is_jump(tmp))
391                         continue;
392
393                 t = (struct xt_standard_target *)ipt_get_target(tmp->entry);
394
395                 if (is_removing == TRUE) {
396                         if (t->verdict >= entry_before->offset)
397                                 t->verdict -= offset;
398                 } else {
399                         if (t->verdict > entry_before->offset)
400                                 t->verdict += offset;
401                 }
402         }
403 }
404
405 static int iptables_add_entry(struct connman_iptables *table,
406                                 struct ipt_entry *entry, GList *before,
407                                         int builtin)
408 {
409         struct connman_iptables_entry *e, *entry_before;
410
411         if (table == NULL)
412                 return -1;
413
414         e = g_try_malloc0(sizeof(struct connman_iptables_entry));
415         if (e == NULL)
416                 return -1;
417
418         e->entry = entry;
419         e->builtin = builtin;
420
421         table->entries = g_list_insert_before(table->entries, before, e);
422         table->num_entries++;
423         table->size += entry->next_offset;
424
425         if (before == NULL) {
426                 e->offset = table->size - entry->next_offset;
427
428                 return 0;
429         }
430
431         entry_before = before->data;
432
433         /*
434          * We've just appended/insterted a new entry. All references
435          * should be bumped accordingly.
436          */
437         update_targets_reference(table, entry_before, e, FALSE);
438
439         update_offsets(table);
440
441         return 0;
442 }
443
444 static int remove_table_entry(struct connman_iptables *table,
445                                 struct connman_iptables_entry *entry)
446 {
447         int removed = 0;
448
449         table->num_entries--;
450         table->size -= entry->entry->next_offset;
451         removed = entry->entry->next_offset;
452
453         g_free(entry->entry);
454
455         table->entries = g_list_remove(table->entries, entry);
456
457         return removed;
458 }
459
460 static int iptables_flush_chain(struct connman_iptables *table,
461                                                 const char *name)
462 {
463         GList *chain_head, *chain_tail, *list, *next;
464         struct connman_iptables_entry *entry;
465         int builtin, removed = 0;
466
467         chain_head = find_chain_head(table, name);
468         if (chain_head == NULL)
469                 return -EINVAL;
470
471         chain_tail = find_chain_tail(table, name);
472         if (chain_tail == NULL)
473                 return -EINVAL;
474
475         entry = chain_head->data;
476         builtin = entry->builtin;
477
478         if (builtin >= 0)
479                 list = chain_head;
480         else
481                 list = chain_head->next;
482
483         if (list == chain_tail->prev)
484                 return 0;
485
486         while (list != chain_tail->prev) {
487                 entry = list->data;
488                 next = g_list_next(list);
489
490                 removed += remove_table_entry(table, entry);
491
492                 list = next;
493         }
494
495         if (builtin >= 0) {
496                 struct connman_iptables_entry *e;
497
498                 entry = list->data;
499
500                 entry->builtin = builtin;
501
502                 table->underflow[builtin] -= removed;
503
504                 for (list = chain_tail; list; list = list->next) {
505                         e = list->data;
506
507                         builtin = e->builtin;
508                         if (builtin < 0)
509                                 continue;
510
511                         table->hook_entry[builtin] -= removed;
512                         table->underflow[builtin] -= removed;
513                 }
514         }
515
516         update_offsets(table);
517
518         return 0;
519 }
520
521 static int iptables_add_chain(struct connman_iptables *table,
522                                 const char *name)
523 {
524         GList *last;
525         struct ipt_entry *entry_head;
526         struct ipt_entry *entry_return;
527         struct error_target *error;
528         struct ipt_standard_target *standard;
529         u_int16_t entry_head_size, entry_return_size;
530
531         last = g_list_last(table->entries);
532
533         /*
534          * An empty chain is composed of:
535          * - A head entry, with no match and an error target.
536          *   The error target data is the chain name.
537          * - A tail entry, with no match and a standard target.
538          *   The standard target verdict is XT_RETURN (return to the
539          *   caller).
540          */
541
542         /* head entry */
543         entry_head_size = sizeof(struct ipt_entry) +
544                                 sizeof(struct error_target);
545         entry_head = g_try_malloc0(entry_head_size);
546         if (entry_head == NULL)
547                 goto err_head;
548
549         memset(entry_head, 0, entry_head_size);
550
551         entry_head->target_offset = sizeof(struct ipt_entry);
552         entry_head->next_offset = entry_head_size;
553
554         error = (struct error_target *) entry_head->elems;
555         strcpy(error->t.u.user.name, IPT_ERROR_TARGET);
556         error->t.u.user.target_size = ALIGN(sizeof(struct error_target));
557         strcpy(error->error, name);
558
559         if (iptables_add_entry(table, entry_head, last, -1) < 0)
560                 goto err_head;
561
562         /* tail entry */
563         entry_return_size = sizeof(struct ipt_entry) +
564                                 sizeof(struct ipt_standard_target);
565         entry_return = g_try_malloc0(entry_return_size);
566         if (entry_return == NULL)
567                 goto err;
568
569         memset(entry_return, 0, entry_return_size);
570
571         entry_return->target_offset = sizeof(struct ipt_entry);
572         entry_return->next_offset = entry_return_size;
573
574         standard = (struct ipt_standard_target *) entry_return->elems;
575         standard->target.u.user.target_size =
576                                 ALIGN(sizeof(struct ipt_standard_target));
577         standard->verdict = XT_RETURN;
578
579         if (iptables_add_entry(table, entry_return, last, -1) < 0)
580                 goto err;
581
582         return 0;
583
584 err:
585         g_free(entry_return);
586 err_head:
587         g_free(entry_head);
588
589         return -ENOMEM;
590 }
591
592 static int iptables_delete_chain(struct connman_iptables *table,
593                                         const char *name)
594 {
595         struct connman_iptables_entry *entry;
596         GList *chain_head, *chain_tail;
597
598         chain_head = find_chain_head(table, name);
599         if (chain_head == NULL)
600                 return -EINVAL;
601
602         entry = chain_head->data;
603
604         /* We cannot remove builtin chain */
605         if (entry->builtin >= 0)
606                 return -EINVAL;
607
608         chain_tail = find_chain_tail(table, name);
609         if (chain_tail == NULL)
610                 return -EINVAL;
611
612         /* Chain must be flushed */
613         if (chain_head->next != chain_tail->prev)
614                 return -EINVAL;
615
616         remove_table_entry(table, entry);
617
618         entry = chain_tail->prev->data;
619         remove_table_entry(table, entry);
620
621         update_offsets(table);
622
623         return 0;
624 }
625
626 static struct ipt_entry *new_rule(struct ipt_ip *ip,
627                 const char *target_name, struct xtables_target *xt_t,
628                 struct xtables_rule_match *xt_rm)
629 {
630         struct xtables_rule_match *tmp_xt_rm;
631         struct ipt_entry *new_entry;
632         size_t match_size, target_size;
633
634         match_size = 0;
635         for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL; tmp_xt_rm = tmp_xt_rm->next)
636                 match_size += tmp_xt_rm->match->m->u.match_size;
637
638         if (xt_t)
639                 target_size = ALIGN(xt_t->t->u.target_size);
640         else
641                 target_size = ALIGN(sizeof(struct xt_standard_target));
642
643         new_entry = g_try_malloc0(sizeof(struct ipt_entry) + target_size +
644                                                                 match_size);
645         if (new_entry == NULL)
646                 return NULL;
647
648         memcpy(&new_entry->ip, ip, sizeof(struct ipt_ip));
649
650         new_entry->target_offset = sizeof(struct ipt_entry) + match_size;
651         new_entry->next_offset = sizeof(struct ipt_entry) + target_size +
652                                                                 match_size;
653
654         match_size = 0;
655         for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
656                                 tmp_xt_rm = tmp_xt_rm->next) {
657                 memcpy(new_entry->elems + match_size, tmp_xt_rm->match->m,
658                                         tmp_xt_rm->match->m->u.match_size);
659                 match_size += tmp_xt_rm->match->m->u.match_size;
660         }
661
662         if (xt_t) {
663                 struct xt_entry_target *entry_target;
664
665                 entry_target = ipt_get_target(new_entry);
666                 memcpy(entry_target, xt_t->t, target_size);
667         }
668
669         return new_entry;
670 }
671
672 static void update_hooks(struct connman_iptables *table, GList *chain_head,
673                                 struct ipt_entry *entry)
674 {
675         GList *list;
676         struct connman_iptables_entry *head, *e;
677         int builtin;
678
679         if (chain_head == NULL)
680                 return;
681
682         head = chain_head->data;
683
684         builtin = head->builtin;
685         if (builtin < 0)
686                 return;
687
688         table->underflow[builtin] += entry->next_offset;
689
690         for (list = chain_head->next; list; list = list->next) {
691                 e = list->data;
692
693                 builtin = e->builtin;
694                 if (builtin < 0)
695                         continue;
696
697                 table->hook_entry[builtin] += entry->next_offset;
698                 table->underflow[builtin] += entry->next_offset;
699         }
700 }
701
702 static struct ipt_entry *prepare_rule_inclusion(struct connman_iptables *table,
703                                 struct ipt_ip *ip, const char *chain_name,
704                                 const char *target_name,
705                                 struct xtables_target *xt_t,
706                                 int *builtin, struct xtables_rule_match *xt_rm)
707 {
708         GList *chain_tail, *chain_head;
709         struct ipt_entry *new_entry;
710         struct connman_iptables_entry *head;
711
712         chain_head = find_chain_head(table, chain_name);
713         if (chain_head == NULL)
714                 return NULL;
715
716         chain_tail = find_chain_tail(table, chain_name);
717         if (chain_tail == NULL)
718                 return NULL;
719
720         new_entry = new_rule(ip, target_name, xt_t, xt_rm);
721         if (new_entry == NULL)
722                 return NULL;
723
724         update_hooks(table, chain_head, new_entry);
725
726         /*
727          * If the chain is builtin, and does not have any rule,
728          * then the one that we're inserting is becoming the head
729          * and thus needs the builtin flag.
730          */
731         head = chain_head->data;
732         if (head->builtin < 0)
733                 *builtin = -1;
734         else if (chain_head == chain_tail->prev) {
735                 *builtin = head->builtin;
736                 head->builtin = -1;
737         }
738
739         return new_entry;
740 }
741
742 static int iptables_append_rule(struct connman_iptables *table,
743                                 struct ipt_ip *ip, char *chain_name,
744                                 char *target_name, struct xtables_target *xt_t,
745                                 struct xtables_rule_match *xt_rm)
746 {
747         GList *chain_tail;
748         struct ipt_entry *new_entry;
749         int builtin = -1, ret;
750
751         DBG("");
752
753         chain_tail = find_chain_tail(table, chain_name);
754         if (chain_tail == NULL)
755                 return -EINVAL;
756
757         new_entry = prepare_rule_inclusion(table, ip, chain_name,
758                                         target_name, xt_t, &builtin, xt_rm);
759         if (new_entry == NULL)
760                 return -EINVAL;
761
762         ret = iptables_add_entry(table, new_entry, chain_tail->prev, builtin);
763         if (ret < 0)
764                 g_free(new_entry);
765
766         return ret;
767 }
768
769 static int iptables_insert_rule(struct connman_iptables *table,
770                                 struct ipt_ip *ip, const char *chain_name,
771                                 const char *target_name,
772                                 struct xtables_target *xt_t,
773                                 struct xtables_rule_match *xt_rm)
774 {
775         struct ipt_entry *new_entry;
776         int builtin = -1, ret;
777         GList *chain_head;
778
779         chain_head = find_chain_head(table, chain_name);
780         if (chain_head == NULL)
781                 return -EINVAL;
782
783         new_entry = prepare_rule_inclusion(table, ip, chain_name,
784                                         target_name, xt_t, &builtin, xt_rm);
785         if (new_entry == NULL)
786                 return -EINVAL;
787
788         if (builtin == -1)
789                 chain_head = chain_head->next;
790
791         ret = iptables_add_entry(table, new_entry, chain_head, builtin);
792         if (ret < 0)
793                 g_free(new_entry);
794
795         return ret;
796 }
797
798 static gboolean is_same_ipt_entry(struct ipt_entry *i_e1,
799                                         struct ipt_entry *i_e2)
800 {
801         if (memcmp(&i_e1->ip, &i_e2->ip, sizeof(struct ipt_ip)) != 0)
802                 return FALSE;
803
804         if (i_e1->target_offset != i_e2->target_offset)
805                 return FALSE;
806
807         if (i_e1->next_offset != i_e2->next_offset)
808                 return FALSE;
809
810         return TRUE;
811 }
812
813 static gboolean is_same_target(struct xt_entry_target *xt_e_t1,
814                                         struct xt_entry_target *xt_e_t2)
815 {
816         if (xt_e_t1 == NULL || xt_e_t2 == NULL)
817                 return FALSE;
818
819         if (strcmp(xt_e_t1->u.user.name, IPT_STANDARD_TARGET) == 0) {
820                 struct xt_standard_target *xt_s_t1;
821                 struct xt_standard_target *xt_s_t2;
822
823                 xt_s_t1 = (struct xt_standard_target *) xt_e_t1;
824                 xt_s_t2 = (struct xt_standard_target *) xt_e_t2;
825
826                 if (xt_s_t1->verdict != xt_s_t2->verdict)
827                         return FALSE;
828         } else {
829                 if (xt_e_t1->u.target_size != xt_e_t2->u.target_size)
830                         return FALSE;
831
832                 if (strcmp(xt_e_t1->u.user.name, xt_e_t2->u.user.name) != 0)
833                         return FALSE;
834         }
835
836         return TRUE;
837 }
838
839 static gboolean is_same_match(struct xt_entry_match *xt_e_m1,
840                                 struct xt_entry_match *xt_e_m2)
841 {
842         if (xt_e_m1 == NULL || xt_e_m2 == NULL)
843                 return FALSE;
844
845         if (xt_e_m1->u.match_size != xt_e_m2->u.match_size)
846                 return FALSE;
847
848         if (xt_e_m1->u.user.revision != xt_e_m2->u.user.revision)
849                 return FALSE;
850
851         if (strcmp(xt_e_m1->u.user.name, xt_e_m2->u.user.name) != 0)
852                 return FALSE;
853
854         return TRUE;
855 }
856
857 static GList *find_existing_rule(struct connman_iptables *table,
858                                 struct ipt_ip *ip, const char *chain_name,
859                                 const char *target_name,
860                                 struct xtables_target *xt_t,
861                                 struct xtables_match *xt_m,
862                                 struct xtables_rule_match *xt_rm)
863 {
864         GList *chain_tail, *chain_head, *list;
865         struct xt_entry_target *xt_e_t = NULL;
866         struct xt_entry_match *xt_e_m = NULL;
867         struct connman_iptables_entry *entry;
868         struct ipt_entry *entry_test;
869         int builtin;
870
871         chain_head = find_chain_head(table, chain_name);
872         if (chain_head == NULL)
873                 return NULL;
874
875         chain_tail = find_chain_tail(table, chain_name);
876         if (chain_tail == NULL)
877                 return NULL;
878
879         if (!xt_t && !xt_m)
880                 return NULL;
881
882         entry_test = new_rule(ip, target_name, xt_t, xt_rm);
883         if (entry_test == NULL)
884                 return NULL;
885
886         if (xt_t != NULL)
887                 xt_e_t = ipt_get_target(entry_test);
888         if (xt_m != NULL)
889                 xt_e_m = (struct xt_entry_match *)entry_test->elems;
890
891         entry = chain_head->data;
892         builtin = entry->builtin;
893
894         if (builtin >= 0)
895                 list = chain_head;
896         else
897                 list = chain_head->next;
898
899         for (; list != chain_tail->prev; list = list->next) {
900                 struct connman_iptables_entry *tmp;
901                 struct ipt_entry *tmp_e;
902
903                 tmp = list->data;
904                 tmp_e = tmp->entry;
905
906                 if (is_same_ipt_entry(entry_test, tmp_e) == FALSE)
907                         continue;
908
909                 if (xt_t != NULL) {
910                         struct xt_entry_target *tmp_xt_e_t;
911
912                         tmp_xt_e_t = ipt_get_target(tmp_e);
913
914                         if (!is_same_target(tmp_xt_e_t, xt_e_t))
915                                 continue;
916                 }
917
918                 if (xt_m != NULL) {
919                         struct xt_entry_match *tmp_xt_e_m;
920
921                         tmp_xt_e_m = (struct xt_entry_match *)tmp_e->elems;
922
923                         if (!is_same_match(tmp_xt_e_m, xt_e_m))
924                                 continue;
925                 }
926
927                 break;
928         }
929
930         g_free(entry_test);
931
932         if (list != chain_tail->prev)
933                 return list;
934
935         return NULL;
936 }
937
938 static int iptables_delete_rule(struct connman_iptables *table,
939                                 struct ipt_ip *ip, const char *chain_name,
940                                 const char *target_name,
941                                 struct xtables_target *xt_t,
942                                 struct xtables_match *xt_m,
943                                 struct xtables_rule_match *xt_rm)
944 {
945         struct connman_iptables_entry *entry;
946         GList *chain_tail, *list;
947         int builtin, removed;
948
949         removed = 0;
950
951         chain_tail = find_chain_tail(table, chain_name);
952         if (chain_tail == NULL)
953                 return -EINVAL;
954
955         list = find_existing_rule(table, ip, chain_name, target_name,
956                                                         xt_t, xt_m, xt_rm);
957         if (list == NULL)
958                 return -EINVAL;
959
960         entry = list->data;
961         if (entry == NULL)
962                 return -EINVAL;
963
964         builtin = entry->builtin;
965
966         /* We have deleted a rule,
967          * all references should be bumped accordingly */
968         if (list->next != NULL)
969                 update_targets_reference(table, list->next->data,
970                                                 list->data, TRUE);
971
972         removed += remove_table_entry(table, entry);
973
974         if (builtin >= 0) {
975                 list = list->next;
976                 if (list) {
977                         entry = list->data;
978                         entry->builtin = builtin;
979                 }
980
981                 table->underflow[builtin] -= removed;
982                 for (list = chain_tail; list; list = list->next) {
983                         entry = list->data;
984
985                         builtin = entry->builtin;
986                         if (builtin < 0)
987                                 continue;
988
989                         table->hook_entry[builtin] -= removed;
990                         table->underflow[builtin] -= removed;
991                 }
992         }
993
994         update_offsets(table);
995
996         return 0;
997 }
998
999 static int iptables_compare_rule(struct connman_iptables *table,
1000                                 struct ipt_ip *ip, const char *chain_name,
1001                                 const char *target_name,
1002                                 struct xtables_target *xt_t,
1003                                 struct xtables_match *xt_m,
1004                                 struct xtables_rule_match *xt_rm)
1005 {
1006         struct connman_iptables_entry *entry;
1007         GList *found;
1008
1009         found = find_existing_rule(table, ip, chain_name, target_name,
1010                                                         xt_t, xt_m, xt_rm);
1011         if (found == NULL)
1012                 return -EINVAL;
1013
1014         entry = found->data;
1015         if (entry == NULL)
1016                 return -EINVAL;
1017
1018         return 0;
1019 }
1020
1021
1022 static int iptables_change_policy(struct connman_iptables *table,
1023                                 const char *chain_name, const char *policy)
1024 {
1025         GList *chain_head;
1026         struct connman_iptables_entry *entry;
1027         struct xt_entry_target *target;
1028         struct xt_standard_target *t;
1029         int verdict;
1030
1031         verdict = target_to_verdict(policy);
1032         if (verdict == 0)
1033                 return -EINVAL;
1034
1035         chain_head = find_chain_head(table, chain_name);
1036         if (chain_head == NULL)
1037                 return -EINVAL;
1038
1039         entry = chain_head->data;
1040         if (entry->builtin < 0)
1041                 return -EINVAL;
1042
1043         target = ipt_get_target(entry->entry);
1044
1045         t = (struct xt_standard_target *)target;
1046         t->verdict = verdict;
1047
1048         return 0;
1049 }
1050
1051 static struct ipt_replace *iptables_blob(struct connman_iptables *table)
1052 {
1053         struct ipt_replace *r;
1054         GList *list;
1055         struct connman_iptables_entry *e;
1056         unsigned char *entry_index;
1057
1058         r = g_try_malloc0(sizeof(struct ipt_replace) + table->size);
1059         if (r == NULL)
1060                 return NULL;
1061
1062         memset(r, 0, sizeof(*r) + table->size);
1063
1064         r->counters = g_try_malloc0(sizeof(struct xt_counters)
1065                                 * table->old_entries);
1066         if (r->counters == NULL) {
1067                 g_free(r);
1068                 return NULL;
1069         }
1070
1071         strcpy(r->name, table->info->name);
1072         r->num_entries = table->num_entries;
1073         r->size = table->size;
1074
1075         r->num_counters = table->old_entries;
1076         r->valid_hooks  = table->info->valid_hooks;
1077
1078         memcpy(r->hook_entry, table->hook_entry, sizeof(table->hook_entry));
1079         memcpy(r->underflow, table->underflow, sizeof(table->underflow));
1080
1081         entry_index = (unsigned char *)r->entries;
1082         for (list = table->entries; list; list = list->next) {
1083                 e = list->data;
1084
1085                 memcpy(entry_index, e->entry, e->entry->next_offset);
1086                 entry_index += e->entry->next_offset;
1087         }
1088
1089         return r;
1090 }
1091
1092 static void dump_ip(struct ipt_entry *entry)
1093 {
1094         struct ipt_ip *ip = &entry->ip;
1095         char ip_string[INET6_ADDRSTRLEN];
1096         char ip_mask[INET6_ADDRSTRLEN];
1097
1098         if (strlen(ip->iniface))
1099                 connman_info("\tin %s", ip->iniface);
1100
1101         if (strlen(ip->outiface))
1102                 connman_info("\tout %s", ip->outiface);
1103
1104         if (inet_ntop(AF_INET, &ip->src, ip_string, INET6_ADDRSTRLEN) != NULL &&
1105                         inet_ntop(AF_INET, &ip->smsk,
1106                                         ip_mask, INET6_ADDRSTRLEN) != NULL)
1107                 connman_info("\tsrc %s/%s", ip_string, ip_mask);
1108
1109         if (inet_ntop(AF_INET, &ip->dst, ip_string, INET6_ADDRSTRLEN) != NULL &&
1110                         inet_ntop(AF_INET, &ip->dmsk,
1111                                         ip_mask, INET6_ADDRSTRLEN) != NULL)
1112                 connman_info("\tdst %s/%s", ip_string, ip_mask);
1113 }
1114
1115 static void dump_target(struct ipt_entry *entry)
1116
1117 {
1118         struct xtables_target *xt_t;
1119         struct xt_entry_target *target;
1120
1121         target = ipt_get_target(entry);
1122
1123         if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
1124                 struct xt_standard_target *t;
1125
1126                 t = (struct xt_standard_target *)target;
1127
1128                 switch (t->verdict) {
1129                 case XT_RETURN:
1130                         connman_info("\ttarget RETURN");
1131                         break;
1132
1133                 case -NF_ACCEPT - 1:
1134                         connman_info("\ttarget ACCEPT");
1135                         break;
1136
1137                 case -NF_DROP - 1:
1138                         connman_info("\ttarget DROP");
1139                         break;
1140
1141                 case -NF_QUEUE - 1:
1142                         connman_info("\ttarget QUEUE");
1143                         break;
1144
1145                 case -NF_STOP - 1:
1146                         connman_info("\ttarget STOP");
1147                         break;
1148
1149                 default:
1150                         connman_info("\tJUMP (0x%x)", t->verdict);
1151                         break;
1152                 }
1153
1154                 xt_t = xtables_find_target(IPT_STANDARD_TARGET,
1155                                                 XTF_LOAD_MUST_SUCCEED);
1156
1157                 if(xt_t->print != NULL)
1158                         xt_t->print(NULL, target, 1);
1159         } else {
1160                 xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD);
1161                 if (xt_t == NULL) {
1162                         connman_info("\ttarget %s", target->u.user.name);
1163                         return;
1164                 }
1165
1166                 if(xt_t->print != NULL) {
1167                         connman_info("\ttarget ");
1168                         xt_t->print(NULL, target, 1);
1169                 }
1170         }
1171 }
1172
1173 static void dump_match(struct ipt_entry *entry)
1174 {
1175         struct xtables_match *xt_m;
1176         struct xt_entry_match *match;
1177
1178         if (entry->elems == (unsigned char *)entry + entry->target_offset)
1179                 return;
1180
1181         match = (struct xt_entry_match *) entry->elems;
1182
1183         if (!strlen(match->u.user.name))
1184                 return;
1185
1186         xt_m = xtables_find_match(match->u.user.name, XTF_TRY_LOAD, NULL);
1187         if (xt_m == NULL)
1188                 goto out;
1189
1190         if(xt_m->print != NULL) {
1191                 connman_info("\tmatch ");
1192                 xt_m->print(NULL, match, 1);
1193
1194                 return;
1195         }
1196
1197 out:
1198         connman_info("\tmatch %s", match->u.user.name);
1199
1200 }
1201
1202 static int dump_entry(struct ipt_entry *entry, int builtin,
1203                         unsigned int hook, size_t size, unsigned int offset,
1204                         void *user_data)
1205 {
1206         struct xt_entry_target *target;
1207
1208         target = ipt_get_target(entry);
1209
1210         if (offset + entry->next_offset == size) {
1211                 connman_info("End of CHAIN 0x%x", offset);
1212                 return 0;
1213         }
1214
1215         if (!strcmp(target->u.user.name, IPT_ERROR_TARGET)) {
1216                 connman_info("USER CHAIN (%s) %p  match %p  target %p  size %d",
1217                         target->data, entry, entry->elems,
1218                         (char *)entry + entry->target_offset,
1219                                 entry->next_offset);
1220
1221                 return 0;
1222         } else if (builtin >= 0) {
1223                 connman_info("CHAIN (%s) %p  match %p  target %p  size %d",
1224                         hooknames[builtin], entry, entry->elems,
1225                         (char *)entry + entry->target_offset,
1226                                 entry->next_offset);
1227         } else {
1228                 connman_info("RULE %p  match %p  target %p  size %d", entry,
1229                         entry->elems,
1230                         (char *)entry + entry->target_offset,
1231                                 entry->next_offset);
1232         }
1233
1234         dump_match(entry);
1235         dump_target(entry);
1236         dump_ip(entry);
1237
1238         return 0;
1239 }
1240
1241 static void iptables_dump(struct connman_iptables *table)
1242 {
1243         connman_info("%s valid_hooks=0x%08x, num_entries=%u, size=%u",
1244                         table->info->name,
1245                         table->info->valid_hooks, table->info->num_entries,
1246                                 table->info->size);
1247
1248         iterate_entries(table->blob_entries->entrytable,
1249                         table->info->valid_hooks,
1250                         table->info->hook_entry,
1251                         table->blob_entries->size,
1252                         dump_entry, NULL);
1253
1254 }
1255
1256 static int iptables_get_entries(struct connman_iptables *table)
1257 {
1258         socklen_t entry_size;
1259
1260         entry_size = sizeof(struct ipt_get_entries) + table->info->size;
1261
1262         return getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_ENTRIES,
1263                                 table->blob_entries, &entry_size);
1264 }
1265
1266 static int iptables_replace(struct connman_iptables *table,
1267                                         struct ipt_replace *r)
1268 {
1269         return setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r,
1270                          sizeof(*r) + r->size);
1271 }
1272
1273 static int add_entry(struct ipt_entry *entry, int builtin, unsigned int hook,
1274                         size_t size, unsigned offset, void *user_data)
1275 {
1276         struct connman_iptables *table = user_data;
1277         struct ipt_entry *new_entry;
1278
1279         new_entry = g_try_malloc0(entry->next_offset);
1280         if (new_entry == NULL)
1281                 return -ENOMEM;
1282
1283         memcpy(new_entry, entry, entry->next_offset);
1284
1285         return iptables_add_entry(table, new_entry, NULL, builtin);
1286 }
1287
1288 static void table_cleanup(struct connman_iptables *table)
1289 {
1290         GList *list;
1291         struct connman_iptables_entry *entry;
1292
1293         if (table == NULL)
1294                 return;
1295
1296         if (table->ipt_sock >= 0)
1297                 close(table->ipt_sock);
1298
1299         for (list = table->entries; list; list = list->next) {
1300                 entry = list->data;
1301
1302                 g_free(entry->entry);
1303                 g_free(entry);
1304         }
1305
1306         g_list_free(table->entries);
1307         g_free(table->info);
1308         g_free(table->blob_entries);
1309         g_free(table);
1310 }
1311
1312 static struct connman_iptables *iptables_init(const char *table_name)
1313 {
1314         struct connman_iptables *table = NULL;
1315         char *module = NULL;
1316         socklen_t s;
1317
1318         if (table_name == NULL)
1319                 table_name = "filter";
1320
1321         DBG("%s", table_name);
1322
1323         if (xtables_insmod("ip_tables", NULL, TRUE) != 0)
1324                 DBG("ip_tables module loading gives error but trying anyway");
1325
1326         module = g_strconcat("iptable_", table_name, NULL);
1327         if (module == NULL)
1328                 return NULL;
1329
1330         if (xtables_insmod(module, NULL, TRUE) != 0)
1331                 DBG("%s module loading gives error but trying anyway", module);
1332
1333         g_free(module);
1334
1335         table = g_hash_table_lookup(table_hash, table_name);
1336         if (table != NULL)
1337                 return table;
1338
1339         table = g_try_new0(struct connman_iptables, 1);
1340         if (table == NULL)
1341                 return NULL;
1342
1343         table->info = g_try_new0(struct ipt_getinfo, 1);
1344         if (table->info == NULL)
1345                 goto err;
1346
1347         table->ipt_sock = socket(AF_INET, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
1348         if (table->ipt_sock < 0)
1349                 goto err;
1350
1351         s = sizeof(*table->info);
1352         strcpy(table->info->name, table_name);
1353         if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO,
1354                                                 table->info, &s) < 0) {
1355                 connman_error("iptables support missing error %d (%s)", errno,
1356                         strerror(errno));
1357                 goto err;
1358         }
1359
1360         table->blob_entries = g_try_malloc0(sizeof(struct ipt_get_entries) +
1361                                                 table->info->size);
1362         if (table->blob_entries == NULL)
1363                 goto err;
1364
1365         strcpy(table->blob_entries->name, table_name);
1366         table->blob_entries->size = table->info->size;
1367
1368         if (iptables_get_entries(table) < 0)
1369                 goto err;
1370
1371         table->num_entries = 0;
1372         table->old_entries = table->info->num_entries;
1373         table->size = 0;
1374
1375         memcpy(table->underflow, table->info->underflow,
1376                                 sizeof(table->info->underflow));
1377         memcpy(table->hook_entry, table->info->hook_entry,
1378                                 sizeof(table->info->hook_entry));
1379
1380         iterate_entries(table->blob_entries->entrytable,
1381                         table->info->valid_hooks, table->info->hook_entry,
1382                         table->blob_entries->size, add_entry, table);
1383
1384         g_hash_table_insert(table_hash, g_strdup(table_name), table);
1385
1386         return table;
1387
1388 err:
1389         table_cleanup(table);
1390
1391         return NULL;
1392 }
1393
1394 static struct option iptables_opts[] = {
1395         {.name = "append",        .has_arg = 1, .val = 'A'},
1396         {.name = "compare",       .has_arg = 1, .val = 'C'},
1397         {.name = "delete",        .has_arg = 1, .val = 'D'},
1398         {.name = "flush-chain",   .has_arg = 1, .val = 'F'},
1399         {.name = "insert",        .has_arg = 1, .val = 'I'},
1400         {.name = "list",          .has_arg = 2, .val = 'L'},
1401         {.name = "new-chain",     .has_arg = 1, .val = 'N'},
1402         {.name = "policy",        .has_arg = 1, .val = 'P'},
1403         {.name = "delete-chain",  .has_arg = 1, .val = 'X'},
1404         {.name = "destination",   .has_arg = 1, .val = 'd'},
1405         {.name = "in-interface",  .has_arg = 1, .val = 'i'},
1406         {.name = "jump",          .has_arg = 1, .val = 'j'},
1407         {.name = "match",         .has_arg = 1, .val = 'm'},
1408         {.name = "out-interface", .has_arg = 1, .val = 'o'},
1409         {.name = "source",        .has_arg = 1, .val = 's'},
1410         {.name = "table",         .has_arg = 1, .val = 't'},
1411         {NULL},
1412 };
1413
1414 struct xtables_globals iptables_globals = {
1415         .option_offset = 0,
1416         .opts = iptables_opts,
1417         .orig_opts = iptables_opts,
1418 };
1419
1420 static struct xtables_target *prepare_target(struct connman_iptables *table,
1421                                                         const char *target_name)
1422 {
1423         struct xtables_target *xt_t = NULL;
1424         gboolean is_builtin, is_user_defined;
1425         GList *chain_head = NULL;
1426         size_t target_size;
1427
1428         is_builtin = FALSE;
1429         is_user_defined = FALSE;
1430
1431         if (is_builtin_target(target_name))
1432                 is_builtin = TRUE;
1433         else {
1434                 chain_head = find_chain_head(table, target_name);
1435                 if (chain_head != NULL && chain_head->next != NULL)
1436                         is_user_defined = TRUE;
1437         }
1438
1439         if (is_builtin || is_user_defined)
1440                 xt_t = xtables_find_target(IPT_STANDARD_TARGET,
1441                                                 XTF_LOAD_MUST_SUCCEED);
1442         else
1443                 xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
1444
1445         if (xt_t == NULL)
1446                 return NULL;
1447
1448         target_size = ALIGN(sizeof(struct ipt_entry_target)) + xt_t->size;
1449
1450         xt_t->t = g_try_malloc0(target_size);
1451         if (xt_t->t == NULL)
1452                 return NULL;
1453
1454         xt_t->t->u.target_size = target_size;
1455
1456         if (is_builtin || is_user_defined) {
1457                 struct xt_standard_target *target;
1458
1459                 target = (struct xt_standard_target *)(xt_t->t);
1460                 strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
1461
1462                 if (is_builtin == TRUE)
1463                         target->verdict = target_to_verdict(target_name);
1464                 else if (is_user_defined == TRUE) {
1465                         struct connman_iptables_entry *target_rule;
1466
1467                         if (chain_head == NULL) {
1468                                 g_free(xt_t->t);
1469                                 return NULL;
1470                         }
1471
1472                         target_rule = chain_head->next->data;
1473                         target->verdict = target_rule->offset;
1474                 }
1475         } else {
1476                 strcpy(xt_t->t->u.user.name, target_name);
1477                 xt_t->t->u.user.revision = xt_t->revision;
1478                 if (xt_t->init != NULL)
1479                         xt_t->init(xt_t->t);
1480         }
1481
1482 #if XTABLES_VERSION_CODE > 5
1483         if (xt_t->x6_options != NULL)
1484                 iptables_globals.opts =
1485                         xtables_options_xfrm(
1486                                 iptables_globals.orig_opts,
1487                                 iptables_globals.opts,
1488                                 xt_t->x6_options,
1489                                 &xt_t->option_offset);
1490         else
1491 #endif
1492                 iptables_globals.opts =
1493                         xtables_merge_options(
1494 #if XTABLES_VERSION_CODE > 5
1495                                 iptables_globals.orig_opts,
1496 #endif
1497                                 iptables_globals.opts,
1498                                 xt_t->extra_opts,
1499                                 &xt_t->option_offset);
1500
1501         if (iptables_globals.opts == NULL) {
1502                 g_free(xt_t->t);
1503                 xt_t = NULL;
1504         }
1505
1506         return xt_t;
1507 }
1508
1509 static struct xtables_match *prepare_matches(struct connman_iptables *table,
1510                                         struct xtables_rule_match **xt_rm,
1511                                         const char *match_name)
1512 {
1513         struct xtables_match *xt_m;
1514         size_t match_size;
1515
1516         if (match_name == NULL)
1517                 return NULL;
1518
1519         xt_m = xtables_find_match(match_name, XTF_LOAD_MUST_SUCCEED, xt_rm);
1520         match_size = ALIGN(sizeof(struct ipt_entry_match)) + xt_m->size;
1521
1522         xt_m->m = g_try_malloc0(match_size);
1523         if (xt_m->m == NULL)
1524                 return NULL;
1525
1526         xt_m->m->u.match_size = match_size;
1527         strcpy(xt_m->m->u.user.name, xt_m->name);
1528         xt_m->m->u.user.revision = xt_m->revision;
1529
1530         if (xt_m->init != NULL)
1531                 xt_m->init(xt_m->m);
1532
1533         if (xt_m == xt_m->next)
1534                 goto done;
1535
1536 #if XTABLES_VERSION_CODE > 5
1537         if (xt_m->x6_options != NULL)
1538                 iptables_globals.opts =
1539                         xtables_options_xfrm(
1540                                 iptables_globals.orig_opts,
1541                                 iptables_globals.opts,
1542                                 xt_m->x6_options,
1543                                 &xt_m->option_offset);
1544         else
1545 #endif
1546                         iptables_globals.opts =
1547                         xtables_merge_options(
1548 #if XTABLES_VERSION_CODE > 5
1549                                 iptables_globals.orig_opts,
1550 #endif
1551                                 iptables_globals.opts,
1552                                 xt_m->extra_opts,
1553                                 &xt_m->option_offset);
1554
1555         if (iptables_globals.opts == NULL) {
1556                 g_free(xt_m->m);
1557                 xt_m = NULL;
1558         }
1559
1560 done:
1561         return xt_m;
1562 }
1563
1564 static int parse_ip_and_mask(const char *str, struct in_addr *ip, struct in_addr *mask)
1565 {
1566         char **tokens;
1567         uint32_t prefixlength;
1568         uint32_t tmp;
1569         int err;
1570
1571         tokens = g_strsplit(str, "/", 2);
1572         if (tokens == NULL)
1573                 return -1;
1574
1575         if (!inet_pton(AF_INET, tokens[0], ip)) {
1576                 err = -1;
1577                 goto out;
1578         }
1579
1580         if (tokens[1] != NULL) {
1581                 prefixlength = strtol(tokens[1], NULL, 10);
1582                 if (prefixlength > 31) {
1583                         err = -1;
1584                         goto out;
1585                 }
1586
1587                 tmp = ~(0xffffffff >> prefixlength);
1588         } else {
1589                 tmp = 0xffffffff;
1590         }
1591
1592         mask->s_addr = htonl(tmp);
1593         ip->s_addr = ip->s_addr & mask->s_addr;
1594         err = 0;
1595 out:
1596         g_strfreev(tokens);
1597
1598         return err;
1599 }
1600
1601 static struct connman_iptables *pre_load_table(const char *table_name,
1602                                         struct connman_iptables *table)
1603 {
1604         if (table != NULL)
1605                 return table;
1606
1607         return iptables_init(table_name);
1608 }
1609
1610 static void clear_tables_flags(void)
1611 {
1612         struct xtables_match *xt_m;
1613         struct xtables_target *xt_t;
1614
1615         /*
1616          * Clear all flags because the flags are only valid
1617          * for one rule.
1618          */
1619         for (xt_m = xtables_matches; xt_m != NULL; xt_m = xt_m->next)
1620                 xt_m->mflags = 0;
1621
1622         for (xt_t = xtables_targets; xt_t != NULL; xt_t = xt_t->next) {
1623                 xt_t->tflags = 0;
1624                 xt_t->used = 0;
1625         }
1626 }
1627
1628 static int iptables_command(int argc, char *argv[])
1629 {
1630         struct connman_iptables *table;
1631         struct xtables_rule_match *xt_rm, *tmp_xt_rm;
1632         struct xtables_match *xt_m, *xt_m_t;
1633         struct xtables_target *xt_t;
1634         struct ipt_ip ip;
1635         char *table_name, *chain, *new_chain, *match_name, *target_name;
1636         char *flush_chain, *delete_chain, *policy;
1637         int c, ret, in_len, out_len;
1638         gboolean dump, invert, insert, delete, compare;
1639
1640         if (argc == 0)
1641                 return -EINVAL;
1642
1643         dump = FALSE;
1644         invert = FALSE;
1645         insert = FALSE;
1646         delete = FALSE;
1647         compare = FALSE;
1648         chain = new_chain = match_name = target_name = NULL;
1649         flush_chain = delete_chain = policy = table_name = NULL;
1650         memset(&ip, 0, sizeof(struct ipt_ip));
1651         table = NULL;
1652         xt_rm = NULL;
1653         xt_m = NULL;
1654         xt_t = NULL;
1655         /* Default code for options parsing */
1656         ret = -EINVAL;
1657
1658         clear_tables_flags();
1659
1660         /* extension's options will generate false-positives errors */
1661         opterr = 0;
1662
1663         optind = 0;
1664
1665         while ((c = getopt_long(argc, argv,
1666                                         "-A:C:D:F:I:L::N:P:X:d:j:i:m:o:s:t:",
1667                                         iptables_globals.opts, NULL)) != -1) {
1668                 switch (c) {
1669                 case 'A':
1670                         /* It is either -A, -C, -D or -I at once */
1671                         if (chain)
1672                                 goto out;
1673
1674                         chain = optarg;
1675                         break;
1676
1677                 case 'C':
1678                         /* It is either -A, -C, -D or -I at once */
1679                         if (chain)
1680                                 goto out;
1681
1682                         chain = optarg;
1683                         compare = TRUE;
1684                         break;
1685
1686                 case 'D':
1687                         /* It is either -A, -C, -D or -I at once */
1688                         if (chain)
1689                                 goto out;
1690
1691                         chain = optarg;
1692                         delete = TRUE;
1693                         break;
1694
1695                 case 'F':
1696                         flush_chain = optarg;
1697                         break;
1698
1699                 case 'I':
1700                         /* It is either -A, -C, -D or -I at once */
1701                         if (chain)
1702                                 goto out;
1703
1704                         chain = optarg;
1705                         insert = TRUE;
1706                         break;
1707
1708                 case 'L':
1709                         dump = TRUE;
1710                         break;
1711
1712                 case 'N':
1713                         new_chain = optarg;
1714                         break;
1715
1716                 case 'P':
1717                         chain = optarg;
1718                         if (optind < argc)
1719                                 policy = argv[optind++];
1720                         else
1721                                 goto out;
1722
1723                         break;
1724
1725                 case 'X':
1726                         delete_chain = optarg;
1727                         break;
1728
1729                 case 'd':
1730                         if (!parse_ip_and_mask(optarg, &ip.dst, &ip.dmsk))
1731                                 break;
1732
1733                         if (invert)
1734                                 ip.invflags |= IPT_INV_DSTIP;
1735
1736                         break;
1737
1738                 case 'i':
1739                         in_len = strlen(optarg);
1740
1741                         if (in_len + 1 > IFNAMSIZ)
1742                                 break;
1743
1744                         strcpy(ip.iniface, optarg);
1745                         memset(ip.iniface_mask, 0xff, in_len + 1);
1746
1747                         if (invert)
1748                                 ip.invflags |= IPT_INV_VIA_IN;
1749
1750                         break;
1751
1752                 case 'j':
1753                         target_name = optarg;
1754
1755                         table = pre_load_table(table_name, table);
1756                         if (table == NULL)
1757                                 goto out;
1758
1759                         xt_t = prepare_target(table, target_name);
1760                         if (xt_t == NULL)
1761                                 goto out;
1762
1763                         break;
1764
1765                 case 'm':
1766                         match_name = optarg;
1767
1768                         table = pre_load_table(table_name, table);
1769                         if (table == NULL)
1770                                 goto out;
1771
1772                         xt_m = prepare_matches(table, &xt_rm, match_name);
1773                         if (xt_m == NULL)
1774                                 goto out;
1775
1776                         break;
1777
1778                 case 'o':
1779                         out_len = strlen(optarg);
1780
1781                         if (out_len + 1 > IFNAMSIZ)
1782                                 break;
1783
1784                         strcpy(ip.outiface, optarg);
1785                         memset(ip.outiface_mask, 0xff, out_len + 1);
1786
1787                         if (invert)
1788                                 ip.invflags |= IPT_INV_VIA_OUT;
1789
1790                         break;
1791
1792                 case 's':
1793                         if (!parse_ip_and_mask(optarg, &ip.src, &ip.smsk))
1794                                 break;
1795
1796                         if (invert)
1797                                 ip.invflags |= IPT_INV_SRCIP;
1798
1799                         break;
1800
1801                 case 't':
1802                         table_name = optarg;
1803
1804                         table = pre_load_table(table_name, table);
1805                         if (table == NULL)
1806                                 goto out;
1807
1808                         break;
1809
1810                 case 1:
1811                         if (optarg[0] == '!' && optarg[1] == '\0') {
1812                                 invert = TRUE;
1813                                 optarg[0] = '\0';
1814                                 continue;
1815                         }
1816
1817                         connman_error("Invalid option");
1818
1819                         goto out;
1820
1821                 default:
1822 #if XTABLES_VERSION_CODE > 5
1823                         if (xt_t != NULL && (xt_t->x6_parse != NULL ||
1824                                                 xt_t->parse != NULL) &&
1825                                         (c >= (int) xt_t->option_offset &&
1826                                         c < (int) xt_t->option_offset +
1827                                         XT_OPTION_OFFSET_SCALE)) {
1828                                 xtables_option_tpcall(c, argv,
1829                                                         invert, xt_t, NULL);
1830
1831                                 break;
1832                         }
1833
1834                         for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
1835                                                 tmp_xt_rm = tmp_xt_rm->next) {
1836                                 xt_m_t = tmp_xt_rm->match;
1837
1838                                 if (tmp_xt_rm->completed ||
1839                                                 (xt_m_t->x6_parse == NULL &&
1840                                                  xt_m_t->parse == NULL))
1841                                         continue;
1842
1843                                 if (c < (int) xt_m_t->option_offset ||
1844                                         c >= (int) xt_m_t->option_offset
1845                                         + XT_OPTION_OFFSET_SCALE)
1846                                         continue;
1847
1848                                 xtables_option_mpcall(c, argv,
1849                                                         invert, xt_m_t, NULL);
1850
1851                                 break;
1852                         }
1853 #else
1854                         if (xt_t == NULL || xt_t->parse == NULL ||
1855                                 !xt_t->parse(c - xt_t->option_offset,
1856                                 argv, invert, &xt_t->tflags, NULL, &xt_t->t)) {
1857
1858                                 for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
1859                                                 tmp_xt_rm = tmp_xt_rm->next) {
1860                                         xt_m_t = tmp_xt_rm->match;
1861
1862                                         if (tmp_xt_rm->completed ||
1863                                                         xt_m_t->parse == NULL)
1864                                                 continue;
1865
1866                                         if (xt_m->parse(c - xt_m->option_offset,
1867                                                 argv, invert, &xt_m->mflags,
1868                                                 NULL, &xt_m->m))
1869                                                 break;
1870                                 }
1871                         }
1872 #endif
1873                         break;
1874                 }
1875
1876                 invert = FALSE;
1877         }
1878
1879 #if XTABLES_VERSION_CODE > 5
1880         for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
1881                                 tmp_xt_rm = tmp_xt_rm->next)
1882                 xtables_option_mfcall(tmp_xt_rm->match);
1883
1884         if (xt_t != NULL)
1885                 xtables_option_tfcall(xt_t);
1886 #else
1887         for (tmp_xt_rm = xt_rm; tmp_xt_rm != NULL;
1888                                 tmp_xt_rm = tmp_xt_rm->next)
1889                 if (tmp_xt_rm->match->final_check != NULL)
1890                         tmp_xt_rm->match->final_check(
1891                                         tmp_xt_rm->match->mflags);
1892
1893         if (xt_t != NULL && xt_t->final_check != NULL)
1894                 xt_t->final_check(xt_t->tflags);
1895 #endif
1896
1897         table = pre_load_table(table_name, table);
1898         if (table == NULL)
1899                 goto out;
1900
1901         /* Option parsing went fine, falling back to succes code */
1902         ret = 0;
1903
1904         if (delete_chain != NULL) {
1905                 printf("Delete chain %s\n", delete_chain);
1906
1907                 iptables_delete_chain(table, delete_chain);
1908
1909                 goto out;
1910         }
1911
1912         if (dump) {
1913                 iptables_dump(table);
1914
1915                 goto out;
1916         }
1917
1918         if (flush_chain) {
1919                 DBG("Flush chain %s", flush_chain);
1920
1921                 iptables_flush_chain(table, flush_chain);
1922
1923                 goto out;
1924         }
1925
1926         if (chain && new_chain) {
1927                 ret = -EINVAL;
1928                 goto out;
1929         }
1930
1931         if (new_chain) {
1932                 DBG("New chain %s", new_chain);
1933
1934                 ret = iptables_add_chain(table, new_chain);
1935                 goto out;
1936         }
1937
1938         if (chain) {
1939                 if (policy != NULL) {
1940                         printf("Changing policy of %s to %s\n", chain, policy);
1941
1942                         iptables_change_policy(table, chain, policy);
1943
1944                         goto out;
1945                 }
1946
1947                 if (xt_t == NULL)
1948                         goto out;
1949
1950                 if (compare == TRUE) {
1951                         ret = iptables_compare_rule(table, &ip, chain,
1952                                         target_name, xt_t, xt_m, xt_rm);
1953                         goto out;
1954                 }
1955
1956                 if (delete == TRUE) {
1957                         DBG("Deleting %s to %s (match %s)\n",
1958                                         target_name, chain, match_name);
1959
1960                         ret = iptables_delete_rule(table, &ip, chain,
1961                                         target_name, xt_t, xt_m, xt_rm);
1962
1963                         goto out;
1964                 }
1965
1966                 if (insert == TRUE) {
1967                         DBG("Inserting %s to %s (match %s)",
1968                                         target_name, chain, match_name);
1969
1970                         ret = iptables_insert_rule(table, &ip, chain,
1971                                                 target_name, xt_t, xt_rm);
1972
1973                         goto out;
1974                 } else {
1975                         DBG("Adding %s to %s (match %s)",
1976                                         target_name, chain, match_name);
1977
1978                         ret = iptables_append_rule(table, &ip, chain,
1979                                                 target_name, xt_t, xt_rm);
1980
1981                         goto out;
1982                 }
1983         }
1984
1985 out:
1986         if (xt_t)
1987                 g_free(xt_t->t);
1988
1989         if (xt_m)
1990                 g_free(xt_m->m);
1991
1992         return ret;
1993 }
1994
1995 int __connman_iptables_command(const char *format, ...)
1996 {
1997         char **argv, **arguments, *command;
1998         int argc, i, ret;
1999         va_list args;
2000
2001         if (format == NULL)
2002                 return -EINVAL;
2003
2004         va_start(args, format);
2005
2006         command = g_strdup_vprintf(format, args);
2007
2008         va_end(args);
2009
2010         if (command == NULL)
2011                 return -ENOMEM;
2012
2013         arguments = g_strsplit_set(command, " ", -1);
2014
2015         for (argc = 0; arguments[argc]; argc++);
2016         ++argc;
2017
2018         DBG("command %s argc %d", command, argc);
2019
2020         argv = g_try_malloc0(argc * sizeof(char *));
2021         if (argv == NULL) {
2022                 g_free(command);
2023                 g_strfreev(arguments);
2024                 return -ENOMEM;
2025         }
2026
2027         argv[0] = "iptables";
2028         for (i = 1; i < argc; i++)
2029                 argv[i] = arguments[i - 1];
2030
2031         ret = iptables_command(argc, argv);
2032
2033         g_free(command);
2034         g_strfreev(arguments);
2035         g_free(argv);
2036
2037         return ret;
2038 }
2039
2040
2041 int __connman_iptables_commit(const char *table_name)
2042 {
2043         struct connman_iptables *table;
2044         struct ipt_replace *repl;
2045         int err;
2046
2047         DBG("%s", table_name);
2048
2049         table = g_hash_table_lookup(table_hash, table_name);
2050         if (table == NULL)
2051                 return -EINVAL;
2052
2053         repl = iptables_blob(table);
2054
2055         err = iptables_replace(table, repl);
2056
2057         g_free(repl->counters);
2058         g_free(repl);
2059
2060         if (err < 0)
2061             return err;
2062
2063         g_hash_table_remove(table_hash, table_name);
2064
2065         return 0;
2066 }
2067
2068 static void remove_table(gpointer user_data)
2069 {
2070         struct connman_iptables *table = user_data;
2071
2072         table_cleanup(table);
2073 }
2074
2075 int __connman_iptables_init(void)
2076 {
2077         DBG("");
2078
2079         table_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
2080                                                 g_free, remove_table);
2081
2082         xtables_init_all(&iptables_globals, NFPROTO_IPV4);
2083
2084         return 0;
2085
2086 }
2087
2088 void __connman_iptables_cleanup(void)
2089 {
2090         DBG("");
2091
2092         g_hash_table_destroy(table_hash);
2093
2094         xtables_free_opts(1);
2095 }