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