Imported Upstream connman version 1.38
[platform/upstream/connman.git] / src / iptables.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2013  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 <errno.h>
32 #include <sys/socket.h>
33 #include <xtables.h>
34 #include <inttypes.h>
35 #include <setjmp.h>
36
37 #include <linux/netfilter_ipv4/ip_tables.h>
38 #include <linux/netfilter_ipv6/ip6_tables.h>
39
40 #include "connman.h"
41 #include "src/shared/util.h"
42
43 /*
44  * Some comments on how the iptables API works (some of them from the
45  * source code from iptables and the kernel):
46  *
47  * - valid_hooks: bit indicates valid IDs for hook_entry
48  * - hook_entry[ID] offset to the chain start
49  * - overflows should be end of entry chains, and uncodintional policy nodes.
50  * - policy entry: last entry in a chain
51  * - user chain: end of last builtin + policy entry
52  * - final entry must be error node
53  * - Underflows must be unconditional and use the STANDARD target with
54  *   ACCEPT/DROP
55  * - IPT_SO_GET_INFO and IPT_SO_GET_ENTRIES are used to read a table
56  * - IPT_SO_GET_INFO: struct ipt_getinfo (note the lack of table content)
57  * - IPT_SO_GET_ENTRIES: struct ipt_get_entries (contains only parts of the
58  *   table header/meta info. The table is appended after the header. The entries
59  *   are of the type struct ipt_entry.
60  * - After the ipt_entry the matches are appended. After the matches
61  *   the target is appended.
62  * - ipt_entry->target_offset =  Size of ipt_entry + matches
63  * - ipt_entry->next_offset =  Size of ipt_entry + matches + target
64  * - IPT_SO_SET_REPLACE is used to write a table (contains the complete
65  * - hook_entry and overflow mark the beginning and the end of a chain, e.g
66  *     entry hook: pre/in/fwd/out/post -1/0/352/504/-1
67  *     underflow:  pre/in/fwd/out/post -1/200/352/904/-1
68  *   means that INPUT starts at offset 0 and ends at 200 (the start offset to
69  *   the last element). FORWARD has one entry starting/ending at 352. The entry
70  *   has a size of 152. 352 + 152 = 504 which is the start of the OUTPUT chain
71  *   which then ends at 904. PREROUTING and POSTROUTING are invalid hooks in
72  *   the filter table.
73  * - 'iptables -t filter -A INPUT -m mark --mark 999 -j LOG'
74  *   writing that table looks like this:
75  *
76  *   filter valid_hooks 0x0000000e  num_entries 5  size 856
77  *   entry hook: pre/in/fwd/out/post -1/0/376/528/-1
78  *   underflow:  pre/in/fwd/out/post -1/224/376/528/-1
79  *   entry 0x699d30  offset 0  size 224
80  *     RULE  match 0x699da0  target 0x699dd0
81  *             match  mark match 0x3e7
82  *             target  LOG flags 0 level 4
83  *             src 0.0.0.0/0.0.0.0
84  *             dst 0.0.0.0/0.0.0.0
85  *   entry 0x699e10  offset 224  size 152
86  *     RULE  match 0x699e80  target 0x699e80
87  *             target ACCEPT
88  *             src 0.0.0.0/0.0.0.0
89  *             dst 0.0.0.0/0.0.0.0
90  *   entry 0x699ea8  offset 376  size 152
91  *     RULE  match 0x699f18  target 0x699f18
92  *             target ACCEPT
93  *             src 0.0.0.0/0.0.0.0
94  *             dst 0.0.0.0/0.0.0.0
95  *   entry 0x699f40  offset 528  size 152
96  *     RULE  match 0x699fb0  target 0x699fb0
97  *             target ACCEPT
98  *             src 0.0.0.0/0.0.0.0
99  *             dst 0.0.0.0/0.0.0.0
100  *   entry 0x699fd8  offset 680  size 176
101  *     USER CHAIN (ERROR)  match 0x69a048  target 0x69a048
102  *
103  *   Reading the filter table looks like this:
104  *
105  *   filter valid_hooks 0x0000000e  num_entries 5  size 856
106  *   entry hook: pre/in/fwd/out/post -1/0/376/528/-1
107  *   underflow:  pre/in/fwd/out/post -1/224/376/528/-1
108  *   entry 0x25fec28  offset 0  size 224
109  *     CHAIN (INPUT)  match 0x25fec98  target 0x25fecc8
110  *             match  mark match 0x3e7
111  *             target  LOG flags 0 level 4
112  *             src 0.0.0.0/0.0.0.0
113  *             dst 0.0.0.0/0.0.0.0
114  *   entry 0x25fed08  offset 224  size 152
115  *     RULE  match 0x25fed78  target 0x25fed78
116  *             target ACCEPT
117  *             src 0.0.0.0/0.0.0.0
118  *             dst 0.0.0.0/0.0.0.0
119  *   entry 0x25feda0  offset 376  size 152
120  *     CHAIN (FORWARD)  match 0x25fee10  target 0x25fee10
121  *             target ACCEPT
122  *             src 0.0.0.0/0.0.0.0
123  *             dst 0.0.0.0/0.0.0.0
124  *   entry 0x25fee38  offset 528  size 152
125  *     CHAIN (OUTPUT)  match 0x25feea8  target 0x25feea8
126  *             target ACCEPT
127  *             src 0.0.0.0/0.0.0.0
128  *             dst 0.0.0.0/0.0.0.0
129  *   entry 0x25feed0  offset 680  size 176
130  *     End of CHAIN
131  */
132
133 /*
134  * Values for the index values used here  are defined as equal for both IPv4
135  * and IPv6 (NF_IP_* and NF_IP6_*) in Netfilter headers.
136  */
137 static const char *hooknames[] = {
138         [NF_IP_PRE_ROUTING]     = "PREROUTING",
139         [NF_IP_LOCAL_IN]        = "INPUT",
140         [NF_IP_FORWARD]         = "FORWARD",
141         [NF_IP_LOCAL_OUT]       = "OUTPUT",
142         [NF_IP_POST_ROUTING]    = "POSTROUTING",
143 };
144
145 #define LABEL_ACCEPT  "ACCEPT"
146 #define LABEL_DROP    "DROP"
147 #define LABEL_QUEUE   "QUEUE"
148 #define LABEL_RETURN  "RETURN"
149
150 #define XT_OPTION_OFFSET_SCALE 256
151
152 struct connman_iptables_entry {
153         int type;
154         unsigned int offset;
155         int builtin;
156         int counter_idx;
157
158         struct ipt_entry *entry;
159         struct ip6t_entry *entry6;
160 };
161
162 struct connman_iptables {
163         int type;
164         char *name;
165         int ipt_sock;
166
167         struct ipt_getinfo *info;
168         struct ipt_get_entries *blob_entries;
169         struct ip6t_getinfo *info6;
170         struct ip6t_get_entries *blob_entries6;
171
172         unsigned int num_entries;
173         unsigned int old_entries;
174         unsigned int size;
175
176         unsigned int underflow[NF_INET_NUMHOOKS];
177         unsigned int hook_entry[NF_INET_NUMHOOKS];
178
179         GList *entries;
180 };
181
182 static GHashTable *table_hash = NULL;
183 static GHashTable *table_hash_ipv6 = NULL;
184 static bool debug_enabled = false;
185
186 struct iptables_ip {
187         int type;
188         struct ipt_ip *ip;
189         struct ip6t_ip6 *ip6;
190 };
191
192 struct iptables_replace {
193         int type;
194         struct ipt_replace *r;
195         struct ip6t_replace *r6;
196 };
197
198 static jmp_buf env_state;
199 static bool jmp_set = false;
200
201 static void enable_jmp()
202 {
203         jmp_set = true;
204 }
205
206 static void disable_jmp()
207 {
208         jmp_set = false;
209 }
210
211 static bool can_jmp()
212 {
213         DBG("%s", jmp_set ? "true" : "false");
214         return jmp_set;
215 }
216
217 typedef int (*iterate_entries_cb_t)(struct connman_iptables_entry *entry,
218                                         int builtin, unsigned int hook,
219                                         size_t size, unsigned int offset,
220                                         void *user_data);
221
222 static u_int16_t iptables_entry_get_next_offset(
223                                         struct connman_iptables_entry *entry)
224 {
225         if (!entry)
226                 return 0;
227
228         switch (entry->type) {
229         case AF_INET:
230                 return entry->entry ? entry->entry->next_offset : 0;
231         case AF_INET6:
232                 return entry->entry6 ? entry->entry6->next_offset : 0;
233         }
234
235         return 0;
236 }
237
238 static u_int16_t iptables_entry_get_target_offset(
239                                         struct connman_iptables_entry *entry)
240 {
241         if (!entry)
242                 return 0;
243
244         switch (entry->type) {
245         case AF_INET:
246                 return entry->entry ? entry->entry->target_offset : 0;
247         case AF_INET6:
248                 return entry->entry6 ? entry->entry6->target_offset : 0;
249         }
250
251         return 0;
252 }
253
254 static unsigned char *iptables_entry_get_elems(
255                                         struct connman_iptables_entry *entry)
256 {
257         if (!entry)
258                 return NULL;
259
260         switch (entry->type) {
261         case AF_INET:
262                 return entry->entry ? entry->entry->elems : NULL;
263         case AF_INET6:
264                 return entry->entry6 ? entry->entry6->elems : NULL;
265         }
266
267         return NULL;
268 }
269
270 static struct xt_entry_target *iptables_entry_get_target(
271                                         struct connman_iptables_entry *entry)
272 {
273         if (!entry)
274                 return NULL;
275
276         switch (entry->type) {
277         case AF_INET:
278                 return entry->entry ? ipt_get_target(entry->entry) : NULL;
279         case AF_INET6:
280                 return entry->entry6 ? ip6t_get_target(entry->entry6) : NULL;
281         }
282
283         return NULL;
284 }
285
286 static struct xt_counters *iptables_entry_get_counters(
287                                         struct connman_iptables_entry *entry)
288 {
289         if (!entry)
290                 return NULL;
291
292         switch (entry->type) {
293         case AF_INET:
294                 return entry->entry ? &entry->entry->counters : NULL;
295         case AF_INET6:
296                 return entry->entry6 ? &entry->entry6->counters : NULL;
297         }
298
299         return NULL;
300 }
301
302 static void iptables_entry_free(struct connman_iptables_entry *entry)
303 {
304         if (!entry)
305                 return;
306
307         g_free(entry->entry);
308         g_free(entry->entry6);
309         g_free(entry);
310 }
311
312 static const char *iptables_table_get_info_name(struct connman_iptables* table)
313 {
314         if (!table)
315                 return NULL;
316
317         switch (table->type) {
318         case AF_INET:
319                 return table->info->name;
320         case AF_INET6:
321                 return table->info6->name;
322         }
323
324         return NULL;
325 }
326
327 static unsigned int iptables_table_get_info_num_entries(
328                                         struct connman_iptables* table)
329 {
330         if (!table)
331                 return 0;
332
333         switch (table->type) {
334         case AF_INET:
335                 return table->info->num_entries;
336         case AF_INET6:
337                 return table->info6->num_entries;
338         }
339
340         return 0;
341 }
342
343 static unsigned int iptables_table_get_info_size(struct connman_iptables* table)
344 {
345         if (!table)
346                 return 0;
347
348         switch (table->type) {
349         case AF_INET:
350                 return table->info->size;
351         case AF_INET6:
352                 return table->info6->size;
353         }
354
355         return 0;
356 }
357
358 static unsigned int iptables_table_get_info_valid_hooks(
359                                         struct connman_iptables* table)
360 {
361         if (!table)
362                 return 0;
363
364         switch (table->type) {
365         case AF_INET:
366                 return table->info->valid_hooks;
367         case AF_INET6:
368                 return table->info6->valid_hooks;
369         }
370
371         return 0;
372 }
373
374 static unsigned int *iptables_table_get_info_hook_entry(
375                                         struct connman_iptables* table)
376 {
377         if (!table)
378                 return NULL;
379
380         switch (table->type) {
381         case AF_INET:
382                 return table->info->hook_entry;
383         case AF_INET6:
384                 return table->info6->hook_entry;
385         }
386
387         return NULL;
388 }
389
390 static unsigned int *iptables_table_get_info_underflow(
391                                         struct connman_iptables* table)
392 {
393         if (!table)
394                 return NULL;
395
396         switch (table->type) {
397         case AF_INET:
398                 return table->info->underflow;
399         case AF_INET6:
400                 return table->info6->underflow;
401         }
402
403         return NULL;
404 }
405
406 static unsigned int iptables_table_get_entries_size(
407                                         struct connman_iptables* table)
408 {
409         if (!table)
410                 return 0;
411
412         switch (table->type) {
413         case AF_INET:
414                 return table->blob_entries->size;
415         case AF_INET6:
416                 return table->blob_entries6->size;
417         }
418
419         return 0;
420 }
421
422 static const char *get_error_target(int type)
423 {
424         switch (type) {
425         case AF_INET:
426                 return IPT_ERROR_TARGET;
427         case AF_INET6:
428                 return IP6T_ERROR_TARGET;
429         default:
430                 return XT_ERROR_TARGET;
431         }
432 }
433
434 static const char *get_standard_target(int type)
435 {
436         switch (type) {
437         case AF_INET:
438                 return IPT_STANDARD_TARGET;
439         case AF_INET6:
440                 return IP6T_STANDARD_TARGET;
441         default:
442                 return XT_STANDARD_TARGET;
443         }
444 }
445
446 static struct connman_iptables *hash_table_lookup(int type,
447                                         const char *table_name) {
448
449         switch (type) {
450         case AF_INET:
451                 return g_hash_table_lookup(table_hash, table_name);
452         case AF_INET6:
453                 return g_hash_table_lookup(table_hash_ipv6, table_name);
454         }
455
456         return NULL;
457 }
458
459 static bool hash_table_replace(int type,
460                                         char *table_name,
461                                         struct connman_iptables *table) {
462
463         switch (type) {
464         case AF_INET:
465                 return g_hash_table_replace(table_hash, table_name, table);
466         case AF_INET6:
467                 return g_hash_table_replace(table_hash_ipv6, table_name, table);
468         }
469
470         return false;
471 }
472
473 static bool hash_table_remove(int type, const char *table_name)
474 {
475         switch (type) {
476         case AF_INET:
477                 return g_hash_table_remove(table_hash, table_name);
478         case AF_INET6:
479                 return g_hash_table_remove(table_hash_ipv6, table_name);
480         }
481
482         return false;
483 }
484
485 static unsigned int next_hook_entry_index(unsigned int *valid_hooks)
486 {
487         unsigned int h;
488
489         if (*valid_hooks == 0)
490                 return NF_INET_NUMHOOKS;
491
492         h = __builtin_ffs(*valid_hooks) - 1;
493         *valid_hooks ^= (1 << h);
494
495         return h;
496 }
497
498 static int iterate_entries(struct connman_iptables_entry *entries,
499                                 unsigned int valid_hooks,
500                                 unsigned int *hook_entry,
501                                 unsigned int *underflow,
502                                 size_t size, iterate_entries_cb_t cb,
503                                 void *user_data)
504 {
505         unsigned int offset, h, hook;
506         int builtin, err;
507         struct connman_iptables_entry entry;
508
509         if (!entries)
510                 return -EINVAL;
511
512         switch (entries->type) {
513         case AF_INET:
514                 if (!entries->entry)
515                         return -EINVAL;
516
517                 break;
518         case AF_INET6:
519                 if (!entries->entry6)
520                         return -EINVAL;
521
522                 break;
523         default:
524                 return -EINVAL;
525         }
526
527         h = next_hook_entry_index(&valid_hooks);
528         hook = h;
529
530         entry.type = entries->type;
531         entry.entry = entries->entry;
532         entry.entry6 = entries->entry6;
533
534         for (offset = 0; offset < size;
535                         offset += iptables_entry_get_next_offset(&entry)) {
536                 builtin = -1;
537
538                 switch (entries->type) {
539                 case AF_INET:
540                         entry.entry = (void* )entries->entry + offset;
541                         break;
542                 case AF_INET6:
543                         entry.entry6 = (void* )entries->entry6 + offset;
544                         break;
545                 }
546
547                 /*
548                  * Updating builtin, hook and h is very tricky.
549                  * The rules are:
550                  * - builtin is only set to the current hook number
551                  *   if the current entry is the hook entry (aka chain
552                  *   head). And only for builtin chains, never for
553                  *   the user chains.
554                  * - hook is the current hook number. If we
555                  *   look at user chains it needs to be NF_INET_NETNUMHOOKS.
556                  * - h is the next hook entry. Thous we need to be carefully
557                  *   not to access the table when h is NF_INET_NETNUMHOOKS.
558                  */
559                 if (h < NF_INET_NUMHOOKS && hook_entry[h] == offset) {
560                         builtin = h;
561                         hook = h;
562                 }
563
564                 if (h == NF_INET_NUMHOOKS)
565                         hook = h;
566
567                 if (h < NF_INET_NUMHOOKS && underflow[h] <= offset)
568                         h = next_hook_entry_index(&valid_hooks);
569
570                 err = cb(&entry, builtin, hook, size, offset, user_data);
571                 if (err < 0)
572                         return err;
573         }
574
575         return 0;
576 }
577
578 static int print_entry(struct connman_iptables_entry *entry, int builtin,
579                                         unsigned int hook, size_t size,
580                                         unsigned int offset, void *user_data)
581 {
582         iterate_entries_cb_t cb;
583         struct xt_counters *counters;
584
585         cb = user_data;
586         counters = iptables_entry_get_counters(entry);
587
588         DBG("entry %p  hook %u  offset %u  size %u  packets %"PRIu64"  "
589                 "bytes %"PRIu64, entry, hook, offset,
590                         iptables_entry_get_next_offset(entry),
591                         (uint64_t) counters->pcnt, (uint64_t) counters->bcnt);
592
593         return cb(entry, builtin, hook, size, offset, NULL);
594 }
595
596 static int target_to_verdict(const char *target_name)
597 {
598         if (!g_strcmp0(target_name, LABEL_ACCEPT))
599                 return -NF_ACCEPT - 1;
600
601         if (!g_strcmp0(target_name, LABEL_DROP))
602                 return -NF_DROP - 1;
603
604         if (!g_strcmp0(target_name, LABEL_QUEUE))
605                 return -NF_QUEUE - 1;
606
607         if (!g_strcmp0(target_name, LABEL_RETURN))
608                 return XT_RETURN;
609
610         return 0;
611 }
612
613 static bool is_builtin_target(const char *target_name)
614 {
615         if (!g_strcmp0(target_name, LABEL_ACCEPT) ||
616                 !g_strcmp0(target_name, LABEL_DROP) ||
617                 !g_strcmp0(target_name, LABEL_QUEUE) ||
618                 !g_strcmp0(target_name, LABEL_RETURN))
619                 return true;
620
621         return false;
622 }
623
624 static bool is_jump(struct connman_iptables_entry *e)
625 {
626         struct xt_entry_target *target;
627
628         target = iptables_entry_get_target(e);
629
630         if (!target)
631                 return false;
632
633         if (!g_strcmp0(target->u.user.name, get_standard_target(e->type))) {
634                 struct xt_standard_target *t;
635
636                 t = (struct xt_standard_target *)target;
637
638                 switch (t->verdict) {
639                 case XT_RETURN:
640                 case -NF_ACCEPT - 1:
641                 case -NF_DROP - 1:
642                 case -NF_QUEUE - 1:
643                 case -NF_STOP - 1:
644                         return false;
645
646                 default:
647                         return true;
648                 }
649         }
650
651         return false;
652 }
653
654 static bool is_fallthrough(struct connman_iptables_entry *e)
655 {
656         struct xt_entry_target *target;
657
658         target = iptables_entry_get_target(e);
659
660         if (!target)
661                 return false;
662
663         if (!g_strcmp0(target->u.user.name, get_standard_target(e->type))) {
664                 struct xt_standard_target *t;
665
666                 t = (struct xt_standard_target *)target;
667                 if (t->verdict == 0)
668                         return true;
669         }
670         return false;
671 }
672
673 static bool is_chain(struct connman_iptables *table,
674                                 struct connman_iptables_entry *e)
675 {
676         struct xt_entry_target *target;
677
678         if (!e)
679                 return false;
680
681         if (e->builtin >= 0)
682                 return true;
683
684         target = iptables_entry_get_target(e);
685
686         if (!target)
687                 return false;
688
689         if (!g_strcmp0(target->u.user.name, get_error_target(e->type)))
690                 return true;
691
692         return false;
693 }
694
695 static GList *find_chain_head(struct connman_iptables *table,
696                                 const char *chain_name)
697 {
698         GList *list;
699         struct connman_iptables_entry *head;
700         struct xt_entry_target *target;
701         int builtin;
702
703         switch (table->type) {
704         case AF_INET:
705         case AF_INET6:
706                 break;
707         default:
708                 return NULL;
709         }
710
711         for (list = table->entries; list; list = list->next) {
712                 head = list->data;
713
714                 /* Buit-in chain */
715                 builtin = head->builtin;
716
717                 if (builtin >= 0 && !g_strcmp0(hooknames[builtin], chain_name))
718                         break;
719
720                 /* User defined chain */
721                 target = iptables_entry_get_target(head);
722
723                 if (!target)
724                         continue;
725
726                 if (!g_strcmp0(target->u.user.name,
727                         get_error_target(table->type)) &&
728                         !g_strcmp0((char *)target->data, chain_name))
729                         break;
730         }
731
732         return list;
733 }
734
735 static GList *find_chain_tail(struct connman_iptables *table,
736                                 const char *chain_name)
737 {
738         struct connman_iptables_entry *tail;
739         GList *chain_head, *list;
740
741         chain_head = find_chain_head(table, chain_name);
742         if (!chain_head)
743                 return NULL;
744
745         /* Then we look for the next chain */
746         for (list = chain_head->next; list; list = list->next) {
747                 tail = list->data;
748
749                 if (is_chain(table, tail))
750                         return list;
751         }
752
753         /* Nothing found, we return the table end */
754         return g_list_last(table->entries);
755 }
756
757 static void update_offsets(struct connman_iptables *table)
758 {
759         GList *list, *prev;
760         struct connman_iptables_entry *entry, *prev_entry;
761
762         for (list = table->entries; list; list = list->next) {
763                 entry = list->data;
764
765                 if (list == table->entries) {
766                         entry->offset = 0;
767
768                         continue;
769                 }
770
771                 prev = list->prev;
772                 prev_entry = prev->data;
773
774                 entry->offset = prev_entry->offset +
775                                         iptables_entry_get_next_offset(
776                                                 prev_entry);
777         }
778 }
779
780 static void update_targets_reference(struct connman_iptables *table,
781                                 struct connman_iptables_entry *entry_before,
782                                 struct connman_iptables_entry *modified_entry,
783                                 bool is_removing)
784 {
785         struct connman_iptables_entry *tmp;
786         struct xt_standard_target *t;
787         GList *list;
788         unsigned int offset;
789
790         offset = iptables_entry_get_next_offset(modified_entry);
791
792         for (list = table->entries; list; list = list->next) {
793                 tmp = list->data;
794
795                 if (!is_jump(tmp))
796                         continue;
797
798                 t = (struct xt_standard_target *)
799                         iptables_entry_get_target(tmp);
800
801                 if (!t)
802                         continue;
803
804                 if (is_removing) {
805                         if (t->verdict >= entry_before->offset)
806                                 t->verdict -= offset;
807                 } else {
808                         if (t->verdict > entry_before->offset)
809                                 t->verdict += offset;
810                 }
811         }
812
813         if (is_fallthrough(modified_entry)) {
814                 t = (struct xt_standard_target *)
815                         iptables_entry_get_target(modified_entry);
816
817                 if (!t)
818                         return;
819
820                 t->verdict = entry_before->offset +
821                         iptables_entry_get_target_offset(modified_entry) +
822                         XT_ALIGN(sizeof(struct xt_standard_target));
823                 t->target.u.target_size =
824                         XT_ALIGN(sizeof(struct xt_standard_target));
825         }
826 }
827
828 static int iptables_add_entry(struct connman_iptables *table,
829                                 struct connman_iptables_entry *entry,
830                                 GList *before, int builtin, int counter_idx)
831 {
832         struct connman_iptables_entry *e, *entry_before;
833
834         if (!table) {
835                 return -EINVAL;
836         }
837
838         e = g_try_malloc0(sizeof(struct connman_iptables_entry));
839         if (!e)
840                 return -ENOMEM;
841
842         switch (table->type) {
843         case AF_INET:
844                 e->entry = entry->entry;
845                 break;
846         case AF_INET6:
847                 e->entry6 = entry->entry6;
848                 break;
849         default:
850                 g_free(e);
851                 return -EINVAL;
852         }
853
854         e->type = entry->type;
855         e->builtin = builtin;
856         e->counter_idx = counter_idx;
857
858         table->entries = g_list_insert_before(table->entries, before, e);
859         table->num_entries++;
860         table->size += iptables_entry_get_next_offset(e);
861
862         if (!before) {
863                 e->offset = table->size -
864                                 iptables_entry_get_next_offset(e);
865                 return 0;
866         }
867
868         entry_before = before->data;
869
870         /*
871          * We've just appended/inserted a new entry. All references
872          * should be bumped accordingly.
873          */
874         update_targets_reference(table, entry_before, e, false);
875
876         update_offsets(table);
877
878         return 0;
879 }
880
881 static int remove_table_entry(struct connman_iptables *table,
882                                 struct connman_iptables_entry *entry)
883 {
884         int removed = 0;
885         u_int16_t next_offset;
886
887         next_offset = iptables_entry_get_next_offset(entry);
888         table->num_entries--;
889
890         table->size -= next_offset;
891         removed = next_offset;
892
893         table->entries = g_list_remove(table->entries, entry);
894
895         iptables_entry_free(entry);
896
897         return removed;
898 }
899
900 static void delete_update_hooks(struct connman_iptables *table,
901                                 int builtin, GList *chain_head,
902                                 int removed)
903 {
904         struct connman_iptables_entry *e;
905         GList *list;
906
907         e = chain_head->data;
908         e->builtin = builtin;
909
910         table->underflow[builtin] -= removed;
911
912         for (list = chain_head->next; list; list = list->next) {
913                 e = list->data;
914
915                 if (e->builtin < 0)
916                         continue;
917
918                 table->hook_entry[e->builtin] -= removed;
919                 table->underflow[e->builtin] -= removed;
920         }
921 }
922
923 static int iptables_flush_chain(struct connman_iptables *table,
924                                                 const char *name)
925 {
926         GList *chain_head, *chain_tail, *list, *next;
927         struct connman_iptables_entry *entry;
928         int builtin, removed = 0;
929
930         DBG("table %s chain %s", table->name, name);
931
932         chain_head = find_chain_head(table, name);
933         if (!chain_head)
934                 return -EINVAL;
935
936         chain_tail = find_chain_tail(table, name);
937         if (!chain_tail)
938                 return -EINVAL;
939
940         entry = chain_head->data;
941         builtin = entry->builtin;
942
943         if (builtin >= 0)
944                 list = chain_head;
945         else
946                 list = chain_head->next;
947
948         if (list == chain_tail->prev)
949                 return 0;
950
951         while (list != chain_tail->prev) {
952                 entry = list->data;
953                 next = g_list_next(list);
954
955                 removed += remove_table_entry(table, entry);
956
957                 list = next;
958         }
959
960         if (builtin >= 0)
961                 delete_update_hooks(table, builtin, chain_tail->prev, removed);
962
963         update_offsets(table);
964
965         return 0;
966 }
967
968 static int iptables_add_chain(struct connman_iptables *table,
969                                 const char *name)
970 {
971         GList *last;
972         struct ipt_entry *entry_head = NULL;
973         struct ipt_entry *entry_return = NULL;
974         struct ip6t_entry *entry6_head = NULL;
975         struct ip6t_entry *entry6_return = NULL;
976         struct connman_iptables_entry entry = { 0 };
977         struct xt_error_target *error = NULL;
978         struct ipt_standard_target *standard = NULL;
979         u_int16_t entry_head_size, entry_return_size;
980         size_t entry_struct_size = 0;
981         size_t xt_error_target_size = 0;
982         size_t standard_target_size = 0;
983
984         DBG("table %s chain %s", table->name, name);
985
986         entry.type = table->type;
987
988         /* Do not allow to add duplicate chains */
989         if (find_chain_head(table, name))
990                 return -EEXIST;
991
992         last = g_list_last(table->entries);
993
994         xt_error_target_size = XT_ALIGN(sizeof(struct xt_error_target));
995
996         /*
997          * An empty chain is composed of:
998          * - A head entry, with no match and an error target.
999          *   The error target data is the chain name.
1000          * - A tail entry, with no match and a standard target.
1001          *   The standard target verdict is XT_RETURN (return to the
1002          *   caller).
1003          */
1004
1005         /* head entry */
1006         switch (entry.type) {
1007         case AF_INET:
1008                 entry_struct_size = XT_ALIGN(sizeof(struct ipt_entry));
1009                 entry_head_size = entry_struct_size + xt_error_target_size;
1010
1011                 entry_head = g_try_malloc0(entry_head_size);
1012                 if (!entry_head)
1013                         goto err_head;
1014
1015                 entry_head->target_offset = entry_struct_size;
1016                 entry_head->next_offset = entry_head_size;
1017
1018                 error = (struct xt_error_target *) entry_head->elems;
1019
1020                 entry.entry = entry_head;
1021                 break;
1022         case AF_INET6:
1023                 entry_struct_size = XT_ALIGN(sizeof(struct ip6t_entry));
1024                 entry_head_size = entry_struct_size + xt_error_target_size;
1025
1026                 entry6_head = g_try_malloc0(entry_head_size);
1027                 if (!entry6_head)
1028                         goto err_head;
1029
1030                 entry6_head->target_offset = entry_struct_size;
1031                 entry6_head->next_offset = entry_head_size;
1032
1033                 error = (struct xt_error_target *) entry6_head->elems;
1034
1035                 entry.entry6 = entry6_head;
1036                 break;
1037         default:
1038                 return -EINVAL;
1039         }
1040
1041         g_stpcpy(error->target.u.user.name, get_error_target(entry.type));
1042         error->target.u.user.target_size = xt_error_target_size;
1043         g_stpcpy(error->errorname, name);
1044
1045         if (iptables_add_entry(table, &entry, last, -1, -1) < 0)
1046                 goto err_head;
1047
1048         standard_target_size = XT_ALIGN(sizeof(struct ipt_standard_target));
1049         entry_return_size = entry_struct_size + standard_target_size;
1050
1051         /* tail entry */
1052         switch (entry.type) {
1053         case AF_INET:
1054                 entry_return = g_try_malloc0(entry_return_size);
1055                 if (!entry_return)
1056                         goto err;
1057
1058                 entry_return->target_offset = entry_struct_size;
1059                 entry_return->next_offset = entry_return_size;
1060
1061                 standard = (struct ipt_standard_target *) entry_return->elems;
1062
1063                 entry.entry = entry_return;
1064                 break;
1065         case AF_INET6:
1066                 entry6_return = g_try_malloc0(entry_return_size);
1067                 if (!entry6_return)
1068                         goto err;
1069
1070                 entry6_return->target_offset = entry_struct_size;
1071                 entry6_return->next_offset = entry_return_size;
1072
1073                 standard = (struct ipt_standard_target *) entry6_return->elems;
1074
1075                 entry.entry6 = entry6_return;
1076                 break;
1077         }
1078
1079         standard->target.u.user.target_size = standard_target_size;
1080         standard->verdict = XT_RETURN;
1081
1082         if (iptables_add_entry(table, &entry, last, -1, -1) < 0)
1083                 goto err;
1084
1085         return 0;
1086
1087 err:
1088         g_free(entry_return);
1089         g_free(entry6_return);
1090 err_head:
1091         g_free(entry_head);
1092         g_free(entry6_head);
1093
1094         return -ENOMEM;
1095 }
1096
1097 static int iptables_delete_chain(struct connman_iptables *table,
1098                                         const char *name)
1099 {
1100         struct connman_iptables_entry *entry;
1101         GList *chain_head, *chain_tail;
1102
1103         DBG("table %s chain %s", table->name, name);
1104
1105         chain_head = find_chain_head(table, name);
1106         if (!chain_head)
1107                 return -EINVAL;
1108
1109         entry = chain_head->data;
1110
1111         /* We cannot remove builtin chain */
1112         if (entry->builtin >= 0)
1113                 return -EINVAL;
1114
1115         chain_tail = find_chain_tail(table, name);
1116         if (!chain_tail)
1117                 return -EINVAL;
1118
1119         /* Chain must be flushed */
1120         if (chain_head->next != chain_tail->prev)
1121                 return -EINVAL;
1122
1123         remove_table_entry(table, entry);
1124
1125         entry = chain_tail->prev->data;
1126         remove_table_entry(table, entry);
1127
1128         update_offsets(table);
1129
1130         return 0;
1131 }
1132
1133 static struct connman_iptables_entry *new_rule(struct iptables_ip *ip,
1134                 const char *target_name, struct xtables_target *xt_t,
1135                 struct xtables_rule_match *xt_rm)
1136 {
1137         struct xtables_rule_match *tmp_xt_rm;
1138         struct connman_iptables_entry *new_entry;
1139         size_t match_size, target_size;
1140
1141         new_entry = g_try_malloc0(sizeof(struct connman_iptables_entry));
1142
1143         if (!new_entry)
1144                 return NULL;
1145
1146         new_entry->type = ip->type;
1147
1148         match_size = 0;
1149         for (tmp_xt_rm = xt_rm; tmp_xt_rm; tmp_xt_rm = tmp_xt_rm->next)
1150                 match_size += tmp_xt_rm->match->m->u.match_size;
1151
1152         if (xt_t)
1153                 target_size = xt_t->t->u.target_size;
1154         else
1155                 target_size = XT_ALIGN(sizeof(struct xt_standard_target));
1156
1157         switch (ip->type) {
1158         case AF_INET:
1159                 new_entry->entry = g_try_malloc0(
1160                                         XT_ALIGN(sizeof(struct ipt_entry)) +
1161                                         target_size + match_size);
1162                 if (!new_entry->entry)
1163                         goto err;
1164
1165                 memcpy(&new_entry->entry->ip, ip->ip, sizeof(struct ipt_ip));
1166
1167                 new_entry->entry->target_offset =
1168                                         XT_ALIGN(sizeof(struct ipt_entry)) +
1169                                         match_size;
1170                 new_entry->entry->next_offset =
1171                                         XT_ALIGN(sizeof(struct ipt_entry)) +
1172                                         target_size + match_size;
1173                 break;
1174         case AF_INET6:
1175                 new_entry->entry6 = g_try_malloc0(
1176                                         XT_ALIGN(sizeof(struct ip6t_entry)) +
1177                                         target_size + match_size);
1178                 if (!new_entry->entry6)
1179                         goto err;
1180
1181                 memcpy(&new_entry->entry6->ipv6, ip->ip6,
1182                                                 sizeof(struct ip6t_ip6));
1183
1184                 new_entry->entry6->target_offset =
1185                                         XT_ALIGN(sizeof(struct ip6t_entry)) +
1186                                         match_size;
1187                 new_entry->entry6->next_offset =
1188                                         XT_ALIGN(sizeof(struct ip6t_entry)) +
1189                                         target_size + match_size;
1190                 break;
1191         default:
1192                 goto err;
1193         }
1194
1195         match_size = 0;
1196         for (tmp_xt_rm = xt_rm; tmp_xt_rm;
1197                                 tmp_xt_rm = tmp_xt_rm->next) {
1198
1199                 switch (new_entry->type) {
1200                 case AF_INET:
1201                         memcpy(new_entry->entry->elems + match_size,
1202                                         tmp_xt_rm->match->m,
1203                                         tmp_xt_rm->match->m->u.match_size);
1204                         break;
1205                 case AF_INET6:
1206                         memcpy(new_entry->entry6->elems + match_size,
1207                                         tmp_xt_rm->match->m,
1208                                         tmp_xt_rm->match->m->u.match_size);
1209                         break;
1210                 }
1211                 match_size += tmp_xt_rm->match->m->u.match_size;
1212         }
1213
1214         if (xt_t) {
1215                 struct xt_entry_target *entry_target;
1216
1217                 entry_target = iptables_entry_get_target(new_entry);
1218                 memcpy(entry_target, xt_t->t, target_size);
1219         }
1220
1221         return new_entry;
1222
1223 err:
1224         g_free(new_entry);
1225
1226         return NULL;
1227 }
1228
1229 static void update_hooks(struct connman_iptables *table, GList *chain_head,
1230                                 struct connman_iptables_entry *entry)
1231 {
1232         GList *list;
1233         struct connman_iptables_entry *head, *e;
1234         int builtin;
1235         u_int16_t next_offset;
1236
1237         if (!table || !chain_head)
1238                 return;
1239
1240         head = chain_head->data;
1241
1242         builtin = head->builtin;
1243         if (builtin < 0)
1244                 return;
1245
1246         next_offset = iptables_entry_get_next_offset(entry);
1247
1248         table->underflow[builtin] += next_offset;
1249
1250         for (list = chain_head->next; list; list = list->next) {
1251                 e = list->data;
1252
1253                 builtin = e->builtin;
1254                 if (builtin < 0)
1255                         continue;
1256
1257                 table->hook_entry[builtin] += next_offset;
1258                 table->underflow[builtin] += next_offset;
1259         }
1260 }
1261
1262 static struct connman_iptables_entry *prepare_rule_inclusion(
1263                                 struct connman_iptables *table,
1264                                 struct iptables_ip *ip,
1265                                 const char *chain_name,
1266                                 const char *target_name,
1267                                 struct xtables_target *xt_t,
1268                                 int *builtin,
1269                                 struct xtables_rule_match *xt_rm,
1270                                 bool insert)
1271 {
1272         GList *chain_tail, *chain_head;
1273         struct connman_iptables_entry *head;
1274         struct connman_iptables_entry *new_entry;
1275
1276         chain_head = find_chain_head(table, chain_name);
1277         if (!chain_head)
1278                 return NULL;
1279
1280         chain_tail = find_chain_tail(table, chain_name);
1281         if (!chain_tail)
1282                 return NULL;
1283
1284         new_entry = new_rule(ip, target_name, xt_t, xt_rm);
1285
1286         switch (new_entry->type) {
1287         case AF_INET:
1288                 if (new_entry->entry)
1289                         break;
1290         case AF_INET6:
1291                 if (new_entry->entry6)
1292                         break;
1293         default:
1294                 goto err;
1295         }
1296
1297         update_hooks(table, chain_head, new_entry);
1298
1299         /*
1300          * If the chain is builtin, and does not have any rule,
1301          * then the one that we're inserting is becoming the head
1302          * and thus needs the builtin flag.
1303          */
1304         head = chain_head->data;
1305         if (head->builtin < 0)
1306                 *builtin = -1;
1307         else if (insert || chain_head == chain_tail->prev) {
1308                 *builtin = head->builtin;
1309                 head->builtin = -1;
1310         }
1311
1312         return new_entry;
1313
1314 err:
1315         g_free(new_entry);
1316
1317         return NULL;
1318 }
1319
1320 static int iptables_append_rule(struct connman_iptables *table,
1321                                 struct iptables_ip *ip,
1322                                 const char *chain_name,
1323                                 const char *target_name,
1324                                 struct xtables_target *xt_t,
1325                                 struct xtables_rule_match *xt_rm)
1326 {
1327         struct connman_iptables_entry *new_entry;
1328         int builtin = -1, ret;
1329         GList *chain_tail;
1330
1331         DBG("table %s chain %s", table->name, chain_name);
1332
1333         chain_tail = find_chain_tail(table, chain_name);
1334         if (!chain_tail)
1335                 return -EINVAL;
1336
1337         new_entry = prepare_rule_inclusion(table, ip, chain_name, target_name,
1338                                         xt_t, &builtin, xt_rm, false);
1339
1340         if (!new_entry)
1341                 return -EINVAL;
1342
1343         switch (new_entry->type) {
1344         case AF_INET:
1345                 if (new_entry->entry)
1346                         break;
1347         case AF_INET6:
1348                 if (new_entry->entry6)
1349                         break;
1350         default:
1351                 ret = -EINVAL;
1352                 goto err;
1353         }
1354
1355         ret = iptables_add_entry(table, new_entry, chain_tail->prev,
1356                                         builtin, -1);
1357         if (ret < 0)
1358                 goto err;
1359
1360         /*
1361          * Free only the container, not the content  iptables_add_entry()
1362          * allocates new containers for entries.
1363          */
1364         g_free(new_entry);
1365
1366         return ret;
1367
1368 err:
1369         iptables_entry_free(new_entry);
1370
1371         return ret;
1372 }
1373
1374 static int iptables_insert_rule(struct connman_iptables *table,
1375                                 struct iptables_ip *ip,
1376                                 const char *chain_name,
1377                                 const char *target_name,
1378                                 struct xtables_target *xt_t,
1379                                 struct xtables_rule_match *xt_rm)
1380 {
1381         struct connman_iptables_entry *new_entry;
1382         int builtin = -1, ret;
1383         GList *chain_head;
1384
1385         DBG("table %s chain %s", table->name, chain_name);
1386
1387         chain_head = find_chain_head(table, chain_name);
1388         if (!chain_head)
1389                 return -EINVAL;
1390
1391         new_entry = prepare_rule_inclusion(table, ip, chain_name, target_name,
1392                                         xt_t, &builtin, xt_rm, true);
1393
1394         if (!new_entry)
1395                 return -EINVAL;
1396
1397         switch (new_entry->type) {
1398         case AF_INET:
1399                 if (new_entry->entry)
1400                         break;
1401         case AF_INET6:
1402                 if (new_entry->entry6)
1403                         break;
1404         default:
1405                 ret = -EINVAL;
1406                 goto err;
1407         }
1408
1409         if (builtin == -1)
1410                 chain_head = chain_head->next;
1411
1412         ret = iptables_add_entry(table, new_entry, chain_head, builtin, -1);
1413         if (ret < 0)
1414                 goto err;
1415
1416         /*
1417          * Free only the container, not the content  iptables_add_entry()
1418          * allocates new containers for entries.
1419          */
1420         g_free(new_entry);
1421
1422         return ret;
1423
1424 err:
1425         iptables_entry_free(new_entry);
1426
1427         return ret;
1428 }
1429
1430 static bool is_same_ipt_entry(struct ipt_entry *i_e1,
1431                                         struct ipt_entry *i_e2)
1432 {
1433         if (memcmp(&i_e1->ip, &i_e2->ip, sizeof(struct ipt_ip)) != 0)
1434                 return false;
1435
1436         if (i_e1->target_offset != i_e2->target_offset)
1437                 return false;
1438
1439         if (i_e1->next_offset != i_e2->next_offset)
1440                 return false;
1441
1442         return true;
1443 }
1444
1445 /* A copy of is_same_ipt_entry with IPv6 structures */
1446 static bool is_same_ip6t_entry(struct ip6t_entry *i_e1,
1447                                         struct ip6t_entry *i_e2)
1448 {
1449         if (memcmp(&i_e1->ipv6, &i_e2->ipv6, sizeof(struct ip6t_ip6)) != 0)
1450                 return false;
1451
1452         if (i_e1->target_offset != i_e2->target_offset)
1453                 return false;
1454
1455         if (i_e1->next_offset != i_e2->next_offset)
1456                 return false;
1457
1458         return true;
1459 }
1460
1461 static bool is_same_iptables_entry(struct connman_iptables_entry *e1,
1462                                                 struct connman_iptables_entry *e2)
1463 {
1464         if (e1->type != e2->type)
1465                 return false;
1466
1467         switch (e1->type) {
1468         case AF_INET:
1469                 return is_same_ipt_entry(e1->entry, e2->entry);
1470         case AF_INET6:
1471                 return is_same_ip6t_entry(e1->entry6, e2->entry6);
1472         }
1473
1474         return false;
1475 }
1476
1477 static bool is_same_target(struct xt_entry_target *xt_e_t1,
1478                                         struct xt_entry_target *xt_e_t2)
1479 {
1480         unsigned int i;
1481
1482         if (!xt_e_t1 || !xt_e_t2)
1483                 return false;
1484
1485         if (g_strcmp0(xt_e_t1->u.user.name, "") == 0 &&
1486                         g_strcmp0(xt_e_t2->u.user.name, "") == 0) {
1487                 /* fallthrough */
1488                 return true;
1489
1490         /*
1491          * IPT_STANDARD_TARGET and IP6T_STANDARD_TARGET are defined by
1492          * XT_STANDARD_TARGET
1493          */
1494         } else if (g_strcmp0(xt_e_t1->u.user.name, XT_STANDARD_TARGET) == 0) {
1495                 struct xt_standard_target *xt_s_t1;
1496                 struct xt_standard_target *xt_s_t2;
1497
1498                 xt_s_t1 = (struct xt_standard_target *) xt_e_t1;
1499                 xt_s_t2 = (struct xt_standard_target *) xt_e_t2;
1500
1501                 if (xt_s_t1->verdict != xt_s_t2->verdict)
1502                         return false;
1503         } else {
1504                 if (xt_e_t1->u.target_size != xt_e_t2->u.target_size)
1505                         return false;
1506
1507                 if (g_strcmp0(xt_e_t1->u.user.name, xt_e_t2->u.user.name) != 0)
1508                         return false;
1509
1510                 for (i = 0; i < xt_e_t1->u.target_size -
1511                                 sizeof(struct xt_standard_target); i++) {
1512                         if ((xt_e_t1->data[i] ^ xt_e_t2->data[i]) != 0)
1513                                 return false;
1514                 }
1515         }
1516
1517         return true;
1518 }
1519
1520 static bool is_same_match(struct xt_entry_match *xt_e_m1,
1521                                 struct xt_entry_match *xt_e_m2)
1522 {
1523         unsigned int i;
1524
1525         if (!xt_e_m1 || !xt_e_m2)
1526                 return false;
1527
1528         if (xt_e_m1->u.match_size != xt_e_m2->u.match_size)
1529                 return false;
1530
1531         if (xt_e_m1->u.user.revision != xt_e_m2->u.user.revision)
1532                 return false;
1533
1534         if (g_strcmp0(xt_e_m1->u.user.name, xt_e_m2->u.user.name) != 0)
1535                 return false;
1536
1537         for (i = 0; i < xt_e_m1->u.match_size - sizeof(struct xt_entry_match);
1538                         i++) {
1539                 if ((xt_e_m1->data[i] ^ xt_e_m2->data[i]) != 0)
1540                         return false;
1541         }
1542
1543         return true;
1544 }
1545
1546 static GList *find_existing_rule(struct connman_iptables *table,
1547                                 struct iptables_ip *ip,
1548                                 const char *chain_name,
1549                                 const char *target_name,
1550                                 struct xtables_target *xt_t,
1551                                 GList *matches,
1552                                 struct xtables_rule_match *xt_rm)
1553 {
1554         GList *chain_tail, *chain_head, *list;
1555         struct xt_entry_target *xt_e_t = NULL;
1556         struct xt_entry_match *xt_e_m = NULL;
1557         struct connman_iptables_entry *entry;
1558         struct connman_iptables_entry *entry_test;
1559         int builtin;
1560
1561         chain_head = find_chain_head(table, chain_name);
1562         if (!chain_head)
1563                 return NULL;
1564
1565         chain_tail = find_chain_tail(table, chain_name);
1566         if (!chain_tail)
1567                 return NULL;
1568
1569         if (!xt_t && !matches)
1570                 return NULL;
1571
1572         entry_test = new_rule(ip, target_name, xt_t, xt_rm);
1573
1574         switch (entry_test->type) {
1575         case AF_INET:
1576                 if (!entry_test->entry) {
1577 #if defined TIZEN_EXT
1578                         iptables_entry_free(entry_test);
1579 #endif
1580                         return NULL;
1581                 }
1582                 break;
1583         case AF_INET6:
1584                 if (!entry_test->entry6) {
1585 #if defined TIZEN_EXT
1586                         iptables_entry_free(entry_test);
1587 #endif
1588                         return NULL;
1589                 }
1590                 break;
1591         default:
1592 #if defined TIZEN_EXT
1593                 iptables_entry_free(entry_test);
1594 #endif
1595                 return NULL;
1596         }
1597
1598         if (xt_t)
1599                 xt_e_t = iptables_entry_get_target(entry_test);
1600         if (matches)
1601                 xt_e_m = (struct xt_entry_match *)
1602                                 iptables_entry_get_elems(entry_test);
1603
1604         entry = chain_head->data;
1605         builtin = entry->builtin;
1606
1607         if (builtin >= 0)
1608                 list = chain_head;
1609         else
1610                 list = chain_head->next;
1611
1612         for (; list != chain_tail->prev; list = list->next) {
1613                 struct connman_iptables_entry *tmp;
1614
1615                 tmp = list->data;
1616
1617                 if (!is_same_iptables_entry(entry_test, tmp))
1618                         continue;
1619
1620                 if (xt_t) {
1621                         struct xt_entry_target *tmp_xt_e_t = NULL;
1622
1623                         tmp_xt_e_t = iptables_entry_get_target(tmp);
1624
1625                         if (!is_same_target(tmp_xt_e_t, xt_e_t))
1626                                 continue;
1627                 }
1628
1629                 if (matches) {
1630                         struct xt_entry_match *tmp_xt_e_m;
1631
1632                         tmp_xt_e_m = (struct xt_entry_match *)
1633                                         iptables_entry_get_elems(tmp);
1634
1635                         if (!is_same_match(tmp_xt_e_m, xt_e_m))
1636                                 continue;
1637                 }
1638
1639                 break;
1640         }
1641
1642         iptables_entry_free(entry_test);
1643
1644         if (list != chain_tail->prev)
1645                 return list;
1646
1647         return NULL;
1648 }
1649
1650 static int iptables_delete_rule(struct connman_iptables *table,
1651                                 struct iptables_ip *ip,
1652                                 const char *chain_name,
1653                                 const char *target_name,
1654                                 struct xtables_target *xt_t,
1655                                 GList *matches,
1656                                 struct xtables_rule_match *xt_rm)
1657 {
1658         struct connman_iptables_entry *entry;
1659         GList *chain_head, *chain_tail, *list;
1660         int builtin, removed;
1661
1662         DBG("table %s chain %s", table->name, chain_name);
1663
1664         removed = 0;
1665
1666         chain_head = find_chain_head(table, chain_name);
1667         if (!chain_head)
1668                 return -EINVAL;
1669
1670         chain_tail = find_chain_tail(table, chain_name);
1671         if (!chain_tail)
1672                 return -EINVAL;
1673
1674         list = find_existing_rule(table, ip, chain_name, target_name,
1675                                                 xt_t, matches, xt_rm);
1676
1677         if (!list)
1678                 return -EINVAL;
1679
1680         entry = chain_head->data;
1681         builtin = entry->builtin;
1682
1683         if (builtin >= 0 && list == chain_head) {
1684                 /*
1685                  * We are about to remove the first rule in the
1686                  * chain. In this case we need to store the builtin
1687                  * value to the new chain_head.
1688                  *
1689                  * Note, for builtin chains, chain_head->next is
1690                  * always valid. A builtin chain has always a policy
1691                  * rule at the end.
1692                  */
1693                 chain_head = chain_head->next;
1694
1695                 entry = chain_head->data;
1696                 entry->builtin = builtin;
1697         }
1698
1699         entry = list->data;
1700         if (!entry)
1701                 return -EINVAL;
1702
1703         /* We have deleted a rule,
1704          * all references should be bumped accordingly */
1705         if (list->next)
1706                 update_targets_reference(table, list->next->data,
1707                                                 list->data, true);
1708
1709         removed += remove_table_entry(table, entry);
1710
1711         if (builtin >= 0)
1712                 delete_update_hooks(table, builtin, chain_head, removed);
1713
1714         update_offsets(table);
1715
1716         return 0;
1717 }
1718
1719 static int iptables_change_policy(struct connman_iptables *table,
1720                                 const char *chain_name, const char *policy)
1721 {
1722         GList *chain_head, *chain_tail;
1723         struct connman_iptables_entry *entry;
1724         struct xt_entry_target *target;
1725         struct xt_standard_target *t;
1726         int verdict;
1727
1728         DBG("table %s chain %s policy %s", table->name, chain_name, policy);
1729
1730         verdict = target_to_verdict(policy);
1731         switch (verdict) {
1732         case -NF_ACCEPT - 1:
1733         case -NF_DROP - 1:
1734                 break;
1735         default:
1736                 return -EINVAL;
1737         }
1738
1739         chain_head = find_chain_head(table, chain_name);
1740         if (!chain_head)
1741                 return -EINVAL;
1742
1743         entry = chain_head->data;
1744         if (entry->builtin < 0)
1745                 return -EINVAL;
1746
1747         chain_tail = find_chain_tail(table, chain_name);
1748         if (!chain_tail)
1749                 return -EINVAL;
1750
1751         entry = chain_tail->prev->data;
1752
1753         target = iptables_entry_get_target(entry);
1754
1755         if (!target)
1756                 return -EINVAL;
1757
1758         t = (struct xt_standard_target *)target;
1759         if (t->verdict != verdict)
1760                 entry->counter_idx = -1;
1761         t->verdict = verdict;
1762
1763         return 0;
1764 }
1765
1766 static struct ipt_replace *iptables_blob(struct connman_iptables *table)
1767 {
1768         struct ipt_replace *r;
1769         GList *list;
1770         struct connman_iptables_entry *e;
1771         unsigned char *entry_index;
1772
1773         r = g_try_malloc0(sizeof(struct ipt_replace) + table->size);
1774         if (!r)
1775                 return NULL;
1776
1777         memset(r, 0, sizeof(*r) + table->size);
1778
1779         r->counters = g_try_malloc0(sizeof(struct xt_counters)
1780                                 * table->old_entries);
1781         if (!r->counters) {
1782                 g_free(r);
1783                 return NULL;
1784         }
1785
1786         g_stpcpy(r->name, table->info->name);
1787         r->num_entries = table->num_entries;
1788         r->size = table->size;
1789
1790         r->num_counters = table->old_entries;
1791         r->valid_hooks  = table->info->valid_hooks;
1792
1793         memcpy(r->hook_entry, table->hook_entry, sizeof(table->hook_entry));
1794         memcpy(r->underflow, table->underflow, sizeof(table->underflow));
1795
1796         entry_index = (unsigned char *)r->entries;
1797         for (list = table->entries; list; list = list->next) {
1798                 e = list->data;
1799
1800                 memcpy(entry_index, e->entry, e->entry->next_offset);
1801                 entry_index += e->entry->next_offset;
1802         }
1803
1804         return r;
1805 }
1806
1807 /* A copy of iptables_blob() with IPv6 structures */
1808 static struct ip6t_replace *ip6tables_blob(struct connman_iptables *table)
1809 {
1810         struct ip6t_replace *r;
1811         GList *list;
1812         struct connman_iptables_entry *e;
1813         unsigned char *entry_index;
1814
1815         r = g_try_malloc0(sizeof(struct ip6t_replace) + table->size);
1816         if (!r)
1817                 return NULL;
1818
1819         memset(r, 0, sizeof(*r) + table->size);
1820
1821         r->counters = g_try_malloc0(sizeof(struct xt_counters)
1822                                 * table->old_entries);
1823         if (!r->counters) {
1824                 g_free(r);
1825                 return NULL;
1826         }
1827
1828         g_stpcpy(r->name, table->info6->name);
1829         r->num_entries = table->num_entries;
1830         r->size = table->size;
1831
1832         r->num_counters = table->old_entries;
1833         r->valid_hooks  = table->info6->valid_hooks;
1834
1835         memcpy(r->hook_entry, table->hook_entry, sizeof(table->hook_entry));
1836         memcpy(r->underflow, table->underflow, sizeof(table->underflow));
1837
1838         entry_index = (unsigned char *)r->entries;
1839         for (list = table->entries; list; list = list->next) {
1840                 e = list->data;
1841
1842                 memcpy(entry_index, e->entry6, e->entry6->next_offset);
1843                 entry_index += e->entry6->next_offset;
1844         }
1845
1846         return r;
1847 }
1848
1849 static void dump_ip(struct connman_iptables_entry *entry)
1850 {
1851         char *iniface, *outiface;
1852         char ip_string[INET6_ADDRSTRLEN];
1853         char ip_mask[INET6_ADDRSTRLEN];
1854
1855         switch (entry->type) {
1856         case AF_INET:
1857                 iniface = entry->entry->ip.iniface;
1858                 outiface = entry->entry->ip.outiface;
1859                 break;
1860         case AF_INET6:
1861                 iniface = entry->entry6->ipv6.iniface;
1862                 outiface = entry->entry6->ipv6.outiface;
1863                 break;
1864         default:
1865                 return;
1866         }
1867
1868         if (strlen(iniface))
1869                 DBG("\tin %s", iniface);
1870
1871         if (strlen(outiface))
1872                 DBG("\tout %s", outiface);
1873
1874         if (entry->type == AF_INET) {
1875                 if (inet_ntop(entry->type, &entry->entry->ip.src, ip_string,
1876                                 INET6_ADDRSTRLEN) && inet_ntop(entry->type,
1877                                 &entry->entry->ip.smsk, ip_mask,
1878                                 INET6_ADDRSTRLEN))
1879                         DBG("\tsrc %s/%s", ip_string, ip_mask);
1880
1881                 if (inet_ntop(entry->type, &entry->entry->ip.dst, ip_string,
1882                                 INET6_ADDRSTRLEN) && inet_ntop(entry->type,
1883                                 &entry->entry->ip.dmsk, ip_mask,
1884                                 INET6_ADDRSTRLEN))
1885                         DBG("\tdst %s/%s", ip_string, ip_mask);
1886         }
1887
1888         if (entry->type == AF_INET6) {
1889                 if (inet_ntop(entry->type, &entry->entry6->ipv6.src, ip_string,
1890                                 INET6_ADDRSTRLEN) && inet_ntop(entry->type,
1891                                 &entry->entry6->ipv6.smsk, ip_mask,
1892                                 INET6_ADDRSTRLEN))
1893                         DBG("\tsrc %s/%s", ip_string, ip_mask);
1894
1895                 if (inet_ntop(entry->type, &entry->entry6->ipv6.dst, ip_string,
1896                                 INET6_ADDRSTRLEN) && inet_ntop(entry->type,
1897                                 &entry->entry6->ipv6.dmsk, ip_mask,
1898                                 INET6_ADDRSTRLEN))
1899                         DBG("\tdst %s/%s", ip_string, ip_mask);
1900         }
1901 }
1902
1903 static void dump_target(struct connman_iptables_entry *entry)
1904 {
1905         struct xtables_target *xt_t;
1906         struct xt_entry_target *target;
1907         int err;
1908
1909         target = iptables_entry_get_target(entry);
1910
1911         if (!target)
1912                 return;
1913
1914         if (!g_strcmp0(target->u.user.name, get_standard_target(entry->type))) {
1915                 struct xt_standard_target *t;
1916
1917                 t = (struct xt_standard_target *)target;
1918
1919                 switch (t->verdict) {
1920                 case XT_RETURN:
1921                         DBG("\ttarget RETURN");
1922                         break;
1923
1924                 case -NF_ACCEPT - 1:
1925                         DBG("\ttarget ACCEPT");
1926                         break;
1927
1928                 case -NF_DROP - 1:
1929                         DBG("\ttarget DROP");
1930                         break;
1931
1932                 case -NF_QUEUE - 1:
1933                         DBG("\ttarget QUEUE");
1934                         break;
1935
1936                 case -NF_STOP - 1:
1937                         DBG("\ttarget STOP");
1938                         break;
1939
1940                 default:
1941                         DBG("\tJUMP %u", t->verdict);
1942                         break;
1943                 }
1944
1945                 enable_jmp();
1946
1947                 if ((err = setjmp(env_state)) != 0) {
1948                         DBG("setjmp() called by longjmp() with value %d", err);
1949                         disable_jmp();
1950                         return;
1951                 }
1952
1953                 xt_t = xtables_find_target(get_standard_target(entry->type),
1954                                                 XTF_LOAD_MUST_SUCCEED);
1955
1956                 disable_jmp();
1957
1958                 if (xt_t->print)
1959                         xt_t->print(NULL, target, 1);
1960         } else {
1961                 enable_jmp();
1962
1963                 if ((err = setjmp(env_state)) != 0) {
1964                         DBG("setjmp() called by longjmp() with value %d", err);
1965                         disable_jmp();
1966                         return;
1967                 }
1968
1969                 xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD);
1970
1971                 disable_jmp();
1972
1973                 if (!xt_t) {
1974                         DBG("\ttarget %s", target->u.user.name);
1975                         return;
1976                 }
1977
1978                 if (xt_t->print) {
1979                         DBG("\ttarget ");
1980                         xt_t->print(NULL, target, 1);
1981                 }
1982         }
1983
1984         if (xt_t == xt_t->next)
1985                 free(xt_t);
1986 }
1987
1988 static void dump_match(struct connman_iptables_entry *entry)
1989 {
1990         struct xtables_match *xt_m;
1991         struct xt_entry_match *match;
1992         u_int16_t target_offset;
1993         int err;
1994
1995         target_offset = iptables_entry_get_target_offset(entry);
1996
1997         switch (entry->type) {
1998         case AF_INET:
1999                 if (entry->entry->elems == (unsigned char *)entry->entry +
2000                                 target_offset)
2001                         return;
2002                 break;
2003         case AF_INET6:
2004                 if (entry->entry6->elems == (unsigned char *)entry->entry6 +
2005                                 target_offset)
2006                         return;
2007                 break;
2008         default:
2009                 return;
2010         }
2011
2012         match = (struct xt_entry_match *) iptables_entry_get_elems(entry);
2013
2014         if (!strlen(match->u.user.name))
2015                 return;
2016
2017         enable_jmp();
2018
2019         if ((err = setjmp(env_state)) != 0) {
2020                 DBG("setjmp() called by longjmp() with value %d", err);
2021                 disable_jmp();
2022                 return;
2023         }
2024
2025         xt_m = xtables_find_match(match->u.user.name, XTF_TRY_LOAD, NULL);
2026
2027         disable_jmp();
2028
2029         if (!xt_m)
2030                 goto out;
2031
2032         if (xt_m->print) {
2033                 DBG("\tmatch ");
2034                 xt_m->print(NULL, match, 1);
2035
2036                 return;
2037         }
2038         if (xt_m == xt_m->next)
2039                 free(xt_m);
2040
2041 out:
2042         DBG("\tmatch %s", match->u.user.name);
2043
2044 }
2045
2046 static int dump_entry(struct connman_iptables_entry *entry, int builtin,
2047                         unsigned int hook, size_t size, unsigned int offset,
2048                         void *user_data)
2049 {
2050         struct xt_entry_target *target;
2051         char *char_entry;
2052
2053         target = iptables_entry_get_target(entry);
2054
2055         if (!target)
2056                 return -EINVAL;
2057
2058         if (offset + iptables_entry_get_next_offset(entry) == size) {
2059                 DBG("\tEnd of CHAIN");
2060                 return 0;
2061         }
2062
2063         switch (entry->type) {
2064         case AF_INET:
2065                 char_entry = (char *)entry->entry;
2066                 break;
2067         case AF_INET6:
2068                 char_entry = (char *)entry->entry6;
2069                 break;
2070         default:
2071                 return 0;
2072         }
2073
2074         if (!g_strcmp0(target->u.user.name, IPT_ERROR_TARGET)) {
2075                 DBG("\tUSER CHAIN (%s) match %p  target %p",
2076                         target->data, iptables_entry_get_elems(entry),
2077                         char_entry + iptables_entry_get_target_offset(entry));
2078
2079                 return 0;
2080         } else if (builtin >= 0) {
2081                 DBG("\tCHAIN (%s) match %p  target %p",
2082                         hooknames[builtin], iptables_entry_get_elems(entry),
2083                         char_entry + iptables_entry_get_target_offset(entry));
2084         } else {
2085                 DBG("\tRULE  match %p  target %p",
2086                         iptables_entry_get_elems(entry),
2087                         char_entry + iptables_entry_get_target_offset(entry));
2088         }
2089
2090         dump_match(entry);
2091         dump_target(entry);
2092         dump_ip(entry);
2093
2094         return 0;
2095 }
2096
2097 static void dump_table(struct connman_iptables *table)
2098 {
2099         struct connman_iptables_entry entry = { 0 };
2100         unsigned int *hook_entry;
2101         unsigned int *underflow;
2102         unsigned int valid_hooks;
2103         unsigned int size;
2104
2105         hook_entry = iptables_table_get_info_hook_entry(table);
2106         underflow = iptables_table_get_info_underflow(table);
2107         valid_hooks = iptables_table_get_info_valid_hooks(table);
2108         size = iptables_table_get_info_size(table);
2109
2110         DBG("%s valid_hooks=0x%08x, num_entries=%u, size=%u",
2111                 iptables_table_get_info_name(table),
2112                 valid_hooks,
2113                 iptables_table_get_info_num_entries(table),
2114                 size);
2115
2116         DBG("entry hook: pre/in/fwd/out/post %d/%d/%d/%d/%d",
2117                 hook_entry[NF_IP_PRE_ROUTING],
2118                 hook_entry[NF_IP_LOCAL_IN],
2119                 hook_entry[NF_IP_FORWARD],
2120                 hook_entry[NF_IP_LOCAL_OUT],
2121                 hook_entry[NF_IP_POST_ROUTING]);
2122         DBG("underflow:  pre/in/fwd/out/post %d/%d/%d/%d/%d",
2123                 underflow[NF_IP_PRE_ROUTING],
2124                 underflow[NF_IP_LOCAL_IN],
2125                 underflow[NF_IP_FORWARD],
2126                 underflow[NF_IP_LOCAL_OUT],
2127                 underflow[NF_IP_POST_ROUTING]);
2128
2129         entry.type = table->type;
2130
2131         switch (table->type) {
2132         case AF_INET:
2133                 entry.entry = table->blob_entries->entrytable;
2134                 break;
2135         case AF_INET6:
2136                 entry.entry6 = table->blob_entries6->entrytable;
2137         }
2138
2139         iterate_entries(&entry,
2140                 valid_hooks,
2141                 hook_entry,
2142                 underflow,
2143                 size,
2144                 print_entry, dump_entry);
2145 }
2146
2147 static const char *iptables_replace_get_name(struct iptables_replace *replace)
2148 {
2149         if (!replace)
2150                 return NULL;
2151
2152         switch (replace->type) {
2153         case AF_INET:
2154                 return replace->r->name;
2155         case AF_INET6:
2156                 return replace->r6->name;
2157         }
2158
2159         return NULL;
2160 }
2161
2162 static unsigned int iptables_replace_get_valid_hooks(
2163                                         struct iptables_replace *replace)
2164 {
2165         if (!replace)
2166                 return 0;
2167
2168         switch (replace->type) {
2169         case AF_INET:
2170                 return replace->r->valid_hooks;
2171         case AF_INET6:
2172                 return replace->r6->valid_hooks;
2173         }
2174
2175         return 0;
2176 }
2177
2178 static unsigned int iptables_replace_get_num_entries(
2179                                         struct iptables_replace *replace)
2180 {
2181         if (!replace)
2182                 return 0;
2183
2184         switch (replace->type) {
2185         case AF_INET:
2186                 return replace->r->num_entries;
2187         case AF_INET6:
2188                 return replace->r6->num_entries;
2189         }
2190
2191         return 0;
2192 }
2193
2194 static unsigned int *iptables_replace_get_hook_entry(
2195                                         struct iptables_replace *replace)
2196 {
2197         if (!replace)
2198                 return NULL;
2199
2200         switch (replace->type) {
2201         case AF_INET:
2202                 return replace->r->hook_entry;
2203         case AF_INET6:
2204                 return replace->r6->hook_entry;
2205         }
2206
2207         return NULL;
2208 }
2209
2210 static unsigned int *iptables_replace_get_underflow(
2211                                         struct iptables_replace *replace)
2212 {
2213         if (!replace)
2214                 return NULL;
2215
2216         switch (replace->type) {
2217         case AF_INET:
2218                 return replace->r->underflow;
2219         case AF_INET6:
2220                 return replace->r6->underflow;
2221         }
2222
2223         return NULL;
2224 }
2225
2226 static unsigned int iptables_replace_get_size(struct iptables_replace *replace)
2227 {
2228         if (!replace)
2229                 return 0;
2230
2231         switch (replace->type) {
2232         case AF_INET:
2233                 return replace->r->size;
2234         case AF_INET6:
2235                 return replace->r6->size;
2236         }
2237
2238         return 0;
2239 }
2240
2241 static void dump_replace(struct iptables_replace *repl)
2242 {
2243         struct connman_iptables_entry entry = { 0 };
2244         unsigned int *hook_entry;
2245         unsigned int *underflow;
2246         unsigned int valid_hooks;
2247         unsigned int size;
2248
2249         hook_entry = iptables_replace_get_hook_entry(repl);
2250         underflow = iptables_replace_get_underflow(repl);
2251         valid_hooks = iptables_replace_get_valid_hooks(repl);
2252         size = iptables_replace_get_size(repl);
2253
2254         switch (repl->type) {
2255         case AF_INET:
2256                 entry.entry = repl->r->entries;
2257                 break;
2258         case AF_INET6:
2259                 entry.entry6 = repl->r6->entries;
2260                 break;
2261         default:
2262                 return;
2263         }
2264
2265         DBG("%s valid_hooks 0x%08x  num_entries %u  size %u",
2266                         iptables_replace_get_name(repl),
2267                         valid_hooks,
2268                         iptables_replace_get_num_entries(repl), size);
2269
2270         DBG("entry hook: pre/in/fwd/out/post %d/%d/%d/%d/%d",
2271                 hook_entry[NF_IP_PRE_ROUTING],
2272                 hook_entry[NF_IP_LOCAL_IN],
2273                 hook_entry[NF_IP_FORWARD],
2274                 hook_entry[NF_IP_LOCAL_OUT],
2275                 hook_entry[NF_IP_POST_ROUTING]);
2276         DBG("underflow:  pre/in/fwd/out/post %d/%d/%d/%d/%d",
2277                 underflow[NF_IP_PRE_ROUTING],
2278                 underflow[NF_IP_LOCAL_IN],
2279                 underflow[NF_IP_FORWARD],
2280                 underflow[NF_IP_LOCAL_OUT],
2281                 underflow[NF_IP_POST_ROUTING]);
2282
2283         iterate_entries(&entry, valid_hooks, hook_entry, underflow,
2284                         size, print_entry, dump_entry);
2285 }
2286
2287 static int iptables_get_entries(struct connman_iptables *table)
2288 {
2289         socklen_t entry_size;
2290         int err;
2291
2292         switch (table->type) {
2293         case AF_INET:
2294                 entry_size = sizeof(struct ipt_get_entries) + table->info->size;
2295
2296                 err = getsockopt(table->ipt_sock, IPPROTO_IP,
2297                                 IPT_SO_GET_ENTRIES, table->blob_entries,
2298                                 &entry_size);
2299                 break;
2300         case AF_INET6:
2301                 entry_size = sizeof(struct ip6t_get_entries) +
2302                                         table->info6->size;
2303
2304                 err = getsockopt(table->ipt_sock, IPPROTO_IPV6,
2305                                 IP6T_SO_GET_ENTRIES, table->blob_entries6,
2306                                 &entry_size);
2307                 break;
2308         default:
2309                 return -EINVAL;
2310         }
2311
2312         if (err < 0)
2313                 return -errno;
2314
2315         return 0;
2316 }
2317
2318 static int iptables_replace(struct connman_iptables *table,
2319                                         struct iptables_replace *r)
2320 {
2321         int err;
2322
2323         switch (r->type) {
2324         case AF_INET:
2325                 if (!r->r)
2326                         return -EINVAL;
2327
2328                 err = setsockopt(table->ipt_sock, IPPROTO_IP,
2329                                 IPT_SO_SET_REPLACE, r->r,
2330                                 sizeof(*r->r) + r->r->size);
2331                 break;
2332         case AF_INET6:
2333                 if (!r->r6)
2334                         return -EINVAL;
2335
2336                 err = setsockopt(table->ipt_sock, IPPROTO_IPV6,
2337                                 IP6T_SO_SET_REPLACE, r->r6,
2338                                 sizeof(*r->r6) + r->r6->size);
2339                 break;
2340         default:
2341                 return -EINVAL;
2342         }
2343
2344         if (err < 0)
2345                 return -errno;
2346
2347         return 0;
2348 }
2349
2350 static int iptables_add_counters(struct connman_iptables *table,
2351                 struct xt_counters_info *c)
2352 {
2353         int err;
2354         int level;
2355         int optname;
2356
2357         switch (table->type) {
2358         case AF_INET:
2359                 level = IPPROTO_IP;
2360                 optname = IPT_SO_SET_ADD_COUNTERS;
2361                 break;
2362         case AF_INET6:
2363                 level = IPPROTO_IPV6;
2364                 optname = IP6T_SO_SET_ADD_COUNTERS;
2365                 break;
2366         default:
2367                 return -EINVAL;
2368         }
2369
2370         err = setsockopt(table->ipt_sock, level, optname, c,
2371                 sizeof(*c) + sizeof(struct xt_counters) * c->num_counters);
2372
2373         if (err < 0)
2374                 return -errno;
2375
2376         return 0;
2377 }
2378
2379 static int add_entry(struct connman_iptables_entry *entry, int builtin,
2380                         unsigned int hook, size_t size, unsigned offset,
2381                         void *user_data)
2382 {
2383         struct connman_iptables *table = user_data;
2384         struct connman_iptables_entry new_entry = { 0 };
2385         u_int16_t next_offset;
2386
2387         new_entry.type = entry->type;
2388         next_offset = iptables_entry_get_next_offset(entry);
2389
2390         switch (entry->type) {
2391         case AF_INET:
2392                 new_entry.entry = g_try_malloc0(next_offset);
2393                 if (!new_entry.entry)
2394                         return -ENOMEM;
2395
2396                 memcpy(new_entry.entry, entry->entry, next_offset);
2397                 break;
2398         case AF_INET6:
2399                 new_entry.entry6 = g_try_malloc0(next_offset);
2400                 if (!new_entry.entry6)
2401                         return -ENOMEM;
2402
2403                 memcpy(new_entry.entry6, entry->entry6, next_offset);
2404                 break;
2405         default:
2406                 return -EINVAL;
2407         }
2408
2409         return iptables_add_entry(table, &new_entry, NULL, builtin,
2410                                 table->num_entries);
2411 }
2412
2413 static void table_cleanup(struct connman_iptables *table)
2414 {
2415         GList *list;
2416         struct connman_iptables_entry *entry;
2417
2418         if (!table)
2419                 return;
2420
2421         if (table->ipt_sock >= 0)
2422                 close(table->ipt_sock);
2423
2424         for (list = table->entries; list; list = list->next) {
2425                 entry = list->data;
2426
2427                 iptables_entry_free(entry);
2428         }
2429
2430         g_list_free(table->entries);
2431         g_free(table->name);
2432
2433         if (table->type == AF_INET) {
2434                 g_free(table->info);
2435                 g_free(table->blob_entries);
2436         }
2437
2438         if (table->type == AF_INET6) {
2439                 g_free(table->info6);
2440                 g_free(table->blob_entries6);
2441         }
2442
2443         g_free(table);
2444 }
2445
2446 static int setup_xtables(int type);
2447 static void reset_xtables();
2448
2449 static struct connman_iptables *iptables_init(int type, const char *table_name)
2450 {
2451         struct connman_iptables *table = NULL;
2452         struct connman_iptables_entry entry = { 0 };
2453         char *iptables_mod = NULL;
2454         char *module = NULL;
2455         socklen_t s;
2456
2457         switch(type) {
2458         case AF_INET:
2459                 iptables_mod = g_strdup("ip_tables");
2460                 module = g_strconcat("iptable_", table_name, NULL);
2461                 break;
2462         case AF_INET6:
2463                 iptables_mod = g_strdup("ip6_tables");
2464                 module = g_strconcat("ip6table_", table_name, NULL);
2465                 break;
2466         default:
2467                 return NULL;
2468         }
2469
2470         DBG("%d %s", type, table_name);
2471
2472         if (setup_xtables(type)) {
2473 #if defined TIZEN_EXT
2474                 g_free(iptables_mod);
2475                 g_free(module);
2476 #endif
2477                 return NULL;
2478         }
2479
2480         if (xtables_insmod(iptables_mod, NULL, TRUE) != 0)
2481                 DBG("%s module loading gives error but trying anyway",
2482                                 iptables_mod);
2483
2484         g_free(iptables_mod);
2485
2486         if (xtables_insmod(module, NULL, TRUE) != 0)
2487                 DBG("%s module loading gives error but trying anyway", module);
2488
2489         g_free(module);
2490
2491         table = g_try_new0(struct connman_iptables, 1);
2492         if (!table)
2493                 return NULL;
2494
2495         table->type = entry.type = type;
2496
2497         table->ipt_sock = socket(type, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_RAW);
2498         if (table->ipt_sock < 0)
2499                 goto err;
2500
2501         switch (type) {
2502         case AF_INET:
2503                 table->info = g_try_new0(struct ipt_getinfo, 1);
2504                 if (!table->info)
2505                         goto err;
2506
2507                 s = sizeof(*table->info);
2508                 g_stpcpy(table->info->name, table_name);
2509
2510                 if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO,
2511                                                         table->info, &s) < 0) {
2512                         connman_error("iptables support missing error %d (%s)",
2513                                                         errno, strerror(errno));
2514                         goto err;
2515                 }
2516
2517                 table->blob_entries = g_try_malloc0(
2518                                         sizeof(struct ipt_get_entries) +
2519                                         table->info->size);
2520                 if (!table->blob_entries)
2521                         goto err;
2522
2523                 g_stpcpy(table->blob_entries->name, table_name);
2524                 table->blob_entries->size = table->info->size;
2525
2526                 break;
2527         case AF_INET6:
2528                 table->info6 = g_try_new0(struct ip6t_getinfo, 1);
2529                 if (!table->info6)
2530                         goto err;
2531
2532                 s = sizeof(*table->info6);
2533                 g_stpcpy(table->info6->name, table_name);
2534
2535                 if (getsockopt(table->ipt_sock, IPPROTO_IPV6, IP6T_SO_GET_INFO,
2536                                                 table->info6, &s) < 0) {
2537                         connman_error("ip6tables support missing error %d (%s)",
2538                                         errno, strerror(errno));
2539                         goto err;
2540                 }
2541
2542                 table->blob_entries6 = g_try_malloc0(
2543                                         sizeof(struct ip6t_get_entries) +
2544                                         table->info6->size);
2545                 if (!table->blob_entries6)
2546                         goto err;
2547
2548                 g_stpcpy(table->blob_entries6->name, table_name);
2549                 table->blob_entries6->size = table->info6->size;
2550
2551                 break;
2552         }
2553
2554         if (iptables_get_entries(table) < 0)
2555                 goto err;
2556
2557         table->num_entries = 0;
2558         table->size = 0;
2559
2560         switch (type) {
2561         case AF_INET:
2562                 table->old_entries = table->info->num_entries;
2563
2564                 memcpy(table->underflow, table->info->underflow,
2565                                         sizeof(table->info->underflow));
2566                 memcpy(table->hook_entry, table->info->hook_entry,
2567                                         sizeof(table->info->hook_entry));
2568
2569                 entry.entry = table->blob_entries->entrytable;
2570                 break;
2571         case AF_INET6:
2572                 table->old_entries = table->info6->num_entries;
2573
2574                 memcpy(table->underflow, table->info6->underflow,
2575                                 sizeof(table->info6->underflow));
2576                 memcpy(table->hook_entry, table->info6->hook_entry,
2577                                         sizeof(table->info6->hook_entry));
2578
2579                 entry.entry6 = table->blob_entries6->entrytable;
2580                 break;
2581         }
2582
2583         iterate_entries(&entry,
2584                         iptables_table_get_info_valid_hooks(table),
2585                         iptables_table_get_info_hook_entry(table),
2586                         iptables_table_get_info_underflow(table),
2587                         iptables_table_get_entries_size(table),
2588                         add_entry,
2589                         table);
2590
2591         if (debug_enabled)
2592                 dump_table(table);
2593
2594         reset_xtables();
2595
2596         return table;
2597
2598 err:
2599         table_cleanup(table);
2600         reset_xtables();
2601
2602         return NULL;
2603 }
2604
2605 static struct option iptables_opts[] = {
2606         {.name = "append",        .has_arg = 1, .val = 'A'},
2607         {.name = "compare",       .has_arg = 1, .val = 'C'},
2608         {.name = "delete",        .has_arg = 1, .val = 'D'},
2609         {.name = "flush-chain",   .has_arg = 1, .val = 'F'},
2610         {.name = "insert",        .has_arg = 1, .val = 'I'},
2611         {.name = "list",          .has_arg = 2, .val = 'L'},
2612         {.name = "new-chain",     .has_arg = 1, .val = 'N'},
2613         {.name = "policy",        .has_arg = 1, .val = 'P'},
2614         {.name = "delete-chain",  .has_arg = 1, .val = 'X'},
2615         {.name = "destination",   .has_arg = 1, .val = 'd'},
2616         {.name = "in-interface",  .has_arg = 1, .val = 'i'},
2617         {.name = "jump",          .has_arg = 1, .val = 'j'},
2618         {.name = "match",         .has_arg = 1, .val = 'm'},
2619         {.name = "out-interface", .has_arg = 1, .val = 'o'},
2620         {.name = "source",        .has_arg = 1, .val = 's'},
2621         {.name = "table",         .has_arg = 1, .val = 't'},
2622         {.name = "protocol",      .has_arg = 1, .val = 'p'},
2623         {NULL},
2624 };
2625
2626 void iptables_exit(enum xtables_exittype status, const char *msg, ...)
2627                         __attribute__((noreturn, format(printf,2,3)));
2628
2629 void iptables_exit(enum xtables_exittype status, const char *msg, ...)
2630 {
2631         va_list args;
2632         gchar str[256] = { 0 };
2633
2634         switch (status) {
2635         case OTHER_PROBLEM:
2636                 DBG("OTHER_PROBLEM");
2637                 break;
2638         case PARAMETER_PROBLEM:
2639                 DBG("PARAMETER_PROBLEM");
2640                 break;
2641         case VERSION_PROBLEM:
2642                 DBG("VERSION_PROBLEM");
2643                 break;
2644         case RESOURCE_PROBLEM:
2645                 DBG("RESOURCE_PROBLEM");
2646                 break;
2647         case XTF_ONLY_ONCE:
2648                 DBG("XTF_ONLY_ONCE");
2649                 break;
2650         case XTF_NO_INVERT:
2651                 DBG("XTF_NO_INVERT");
2652                 break;
2653         case XTF_BAD_VALUE:
2654                 DBG("XTF_BAD_VALUE");
2655                 break;
2656         case XTF_ONE_ACTION:
2657                 DBG("XTF_ONE_ACTION");
2658                 break;
2659         }
2660
2661         va_start(args, msg);
2662         vsnprintf(str, 256, msg, args);
2663         va_end(args);
2664
2665         connman_error("iptables rule error: %s", str);
2666
2667         if (can_jmp()) {
2668                 DBG("calling longjmp()");
2669                  /* enum xtables_exittype begins from 1 */
2670                 longjmp(env_state, status);
2671         }
2672
2673         connman_error("exit because of iptables error");
2674
2675         exit(status);
2676 }
2677
2678 struct xtables_globals iptables_globals = {
2679         .option_offset = 0,
2680         .opts = iptables_opts,
2681         .orig_opts = iptables_opts,
2682         .exit_err = iptables_exit,
2683 #if XTABLES_VERSION_CODE > 10
2684         .compat_rev = xtables_compatible_revision,
2685 #endif
2686 };
2687
2688 struct xtables_globals ip6tables_globals = {
2689         .option_offset = 0,
2690         .opts = iptables_opts,
2691         .orig_opts = iptables_opts,
2692         .exit_err = iptables_exit,
2693 #if XTABLES_VERSION_CODE > 10
2694         .compat_rev = xtables_compatible_revision,
2695 #endif
2696 };
2697
2698 static struct xtables_target *prepare_target(struct connman_iptables *table,
2699                                                         const char *target_name)
2700 {
2701         struct xtables_target *xt_t = NULL;
2702         bool is_builtin, is_user_defined;
2703         GList *chain_head = NULL;
2704         size_t target_size;
2705         int err;
2706
2707         is_builtin = false;
2708         is_user_defined = false;
2709
2710         DBG("target %s", target_name);
2711
2712         if (!table)
2713                 return NULL;
2714
2715         if (is_builtin_target(target_name))
2716                 is_builtin = true;
2717         else {
2718                 chain_head = find_chain_head(table, target_name);
2719                 if (chain_head && chain_head->next)
2720                         is_user_defined = true;
2721         }
2722
2723         enable_jmp();
2724
2725         if ((err = setjmp(env_state)) != 0) {
2726                 DBG("setjmp() called by longjmp() with value %d", err);
2727                 disable_jmp();
2728                 return NULL;
2729         }
2730
2731         if (is_builtin || is_user_defined)
2732                 xt_t = xtables_find_target(get_standard_target(table->type),
2733                                                 XTF_LOAD_MUST_SUCCEED);
2734         else
2735                 xt_t = xtables_find_target(target_name, XTF_TRY_LOAD);
2736
2737         disable_jmp();
2738
2739         if (!xt_t)
2740                 return NULL;
2741
2742         switch (table->type) {
2743         case AF_INET:
2744                 target_size = XT_ALIGN(sizeof(struct ipt_entry_target)) +
2745                                         xt_t->size;
2746                 break;
2747         case AF_INET6:
2748                 target_size = XT_ALIGN(sizeof(struct ip6t_entry_target)) +
2749                                         xt_t->size;
2750                 break;
2751         default:
2752                 return NULL;
2753         }
2754
2755         xt_t->t = g_try_malloc0(target_size);
2756         if (!xt_t->t)
2757                 return NULL;
2758
2759         xt_t->t->u.target_size = target_size;
2760
2761         if (is_builtin || is_user_defined) {
2762                 struct xt_standard_target *target;
2763
2764                 target = (struct xt_standard_target *)(xt_t->t);
2765                 g_stpcpy(target->target.u.user.name,
2766                                 get_standard_target(table->type));
2767
2768                 if (is_builtin)
2769                         target->verdict = target_to_verdict(target_name);
2770                 else if (is_user_defined) {
2771                         struct connman_iptables_entry *target_rule;
2772
2773                         target_rule = chain_head->next->data;
2774                         target->verdict = target_rule->offset;
2775                 }
2776         } else {
2777                 g_stpcpy(xt_t->t->u.user.name, target_name);
2778                 xt_t->t->u.user.revision = xt_t->revision;
2779                 if (xt_t->init)
2780                         xt_t->init(xt_t->t);
2781         }
2782
2783         switch (table->type) {
2784         case AF_INET:
2785                 if (xt_t->x6_options)
2786                         iptables_globals.opts =
2787                                 xtables_options_xfrm(
2788                                         iptables_globals.orig_opts,
2789                                         iptables_globals.opts,
2790                                         xt_t->x6_options,
2791                                         &xt_t->option_offset);
2792                 else
2793                         iptables_globals.opts =
2794                                 xtables_merge_options(
2795                                         iptables_globals.orig_opts,
2796                                         iptables_globals.opts,
2797                                         xt_t->extra_opts,
2798                                         &xt_t->option_offset);
2799
2800                 if (!iptables_globals.opts) {
2801                         g_free(xt_t->t);
2802                         xt_t = NULL;
2803                 }
2804
2805                 break;
2806         case AF_INET6:
2807                 if (xt_t->x6_options)
2808                         ip6tables_globals.opts =
2809                                 xtables_options_xfrm(
2810                                         ip6tables_globals.orig_opts,
2811                                         ip6tables_globals.opts,
2812                                         xt_t->x6_options,
2813                                         &xt_t->option_offset);
2814                 else
2815                         ip6tables_globals.opts =
2816                                 xtables_merge_options(
2817                                         ip6tables_globals.orig_opts,
2818                                         ip6tables_globals.opts,
2819                                         xt_t->extra_opts,
2820                                         &xt_t->option_offset);
2821
2822                 if (!ip6tables_globals.opts) {
2823                         g_free(xt_t->t);
2824                         xt_t = NULL;
2825                 }
2826
2827                 break;
2828         }
2829
2830         return xt_t;
2831 }
2832
2833 static struct xtables_match *prepare_matches(struct connman_iptables *table,
2834                                         struct xtables_rule_match **xt_rm,
2835                                         const char *match_name)
2836 {
2837         struct xtables_match *xt_m;
2838         size_t match_size;
2839         int err;
2840
2841         if (!table || !match_name)
2842                 return NULL;
2843
2844         enable_jmp();
2845
2846         if ((err = setjmp(env_state)) != 0) {
2847                 DBG("setjmp() called by longjmp() with value %d", err);
2848                 disable_jmp();
2849                 return NULL;
2850         }
2851
2852         xt_m = xtables_find_match(match_name, XTF_LOAD_MUST_SUCCEED, xt_rm);
2853
2854         disable_jmp();
2855
2856         switch (table->type) {
2857         case AF_INET:
2858                 match_size = XT_ALIGN(sizeof(struct ipt_entry_match)) +
2859                                                 xt_m->size;
2860                 break;
2861         case AF_INET6:
2862                 match_size = XT_ALIGN(sizeof(struct ip6t_entry_match)) +
2863                                                 xt_m->size;
2864                 break;
2865         default:
2866                 return NULL;
2867         }
2868
2869         xt_m->m = g_try_malloc0(match_size);
2870         if (!xt_m->m)
2871                 return NULL;
2872
2873         xt_m->m->u.match_size = match_size;
2874         g_stpcpy(xt_m->m->u.user.name, xt_m->name);
2875         xt_m->m->u.user.revision = xt_m->revision;
2876
2877         if (xt_m->init)
2878                 xt_m->init(xt_m->m);
2879
2880         switch (table->type) {
2881         case AF_INET:
2882                 if (xt_m->x6_options)
2883                         iptables_globals.opts =
2884                                 xtables_options_xfrm(
2885                                         iptables_globals.orig_opts,
2886                                         iptables_globals.opts,
2887                                         xt_m->x6_options,
2888                                         &xt_m->option_offset);
2889                 else
2890                         iptables_globals.opts =
2891                                 xtables_merge_options(
2892                                         iptables_globals.orig_opts,
2893                                         iptables_globals.opts,
2894                                         xt_m->extra_opts,
2895                                         &xt_m->option_offset);
2896
2897                 if (!iptables_globals.opts) {
2898                         g_free(xt_m->m);
2899
2900                         if (xt_m == xt_m->next)
2901                                 free(xt_m);
2902
2903                         xt_m = NULL;
2904                 }
2905
2906                 break;
2907         case AF_INET6:
2908                 if (xt_m->x6_options)
2909                         ip6tables_globals.opts =
2910                                 xtables_options_xfrm(
2911                                         ip6tables_globals.orig_opts,
2912                                         ip6tables_globals.opts,
2913                                         xt_m->x6_options,
2914                                         &xt_m->option_offset);
2915                 else
2916                         ip6tables_globals.opts =
2917                                 xtables_merge_options(
2918                                         ip6tables_globals.orig_opts,
2919                                         ip6tables_globals.opts,
2920                                         xt_m->extra_opts,
2921                                         &xt_m->option_offset);
2922
2923                 if (!ip6tables_globals.opts) {
2924                         g_free(xt_m->m);
2925
2926                         if (xt_m == xt_m->next)
2927                                 free(xt_m);
2928
2929                         xt_m = NULL;
2930                 }
2931
2932                 break;
2933         }
2934
2935         return xt_m;
2936 }
2937
2938 static int parse_ip_and_mask(const char *str, struct in_addr *ip,
2939                                 struct in_addr *mask)
2940 {
2941         char **tokens;
2942         uint32_t prefixlength;
2943         uint32_t tmp;
2944         int err;
2945
2946         tokens = g_strsplit(str, "/", 2);
2947         if (!tokens)
2948                 return -1;
2949
2950         if (!inet_pton(AF_INET, tokens[0], ip)) {
2951                 err = -1;
2952                 goto out;
2953         }
2954
2955         if (tokens[1]) {
2956                 prefixlength = strtol(tokens[1], NULL, 10);
2957                 if (prefixlength > 32) {
2958                         err = -1;
2959                         goto out;
2960                 } else if (prefixlength == 32) {
2961                         tmp = 0xffffffff;
2962                 } else {
2963                         tmp = ~(0xffffffff >> prefixlength);
2964                 }
2965         } else {
2966                 tmp = 0xffffffff;
2967         }
2968
2969         mask->s_addr = htonl(tmp);
2970         ip->s_addr = ip->s_addr & mask->s_addr;
2971         err = 0;
2972 out:
2973         g_strfreev(tokens);
2974
2975         return err;
2976 }
2977
2978 static int parse_ipv6_and_mask(const char *str, struct in6_addr *ip,
2979                                 struct in6_addr *mask)
2980 {
2981         char **tokens;
2982         uint32_t prefixlength;
2983         struct in6_addr in6;
2984         int i, j;
2985         int err;
2986
2987         tokens = g_strsplit(str, "/", 2);
2988         if (!tokens)
2989                 return -1;
2990
2991         if (!inet_pton(AF_INET6, tokens[0], ip)) {
2992                 err = -1;
2993                 goto out;
2994         }
2995
2996         if (tokens[1]) {
2997                 prefixlength = strtol(tokens[1], NULL, 10);
2998                 if (prefixlength > 128) {
2999                         err = -1;
3000                         goto out;
3001                 }
3002         } else {
3003                 prefixlength = 128;
3004         }
3005
3006         /*
3007          * This part was adapted from (no need to re-invent the wheel):
3008          * https://gitlab.com/ipcalc/ipcalc/blob/master/ipcalc.c#L733
3009          */
3010         memset(&in6, 0, sizeof(struct in6_addr));
3011
3012         for (i = prefixlength, j = 0; i > 0; i -= 8, j++) {
3013                 if (i >= 8)
3014                         in6.s6_addr[j] = 0xff;
3015                 else
3016                         in6.s6_addr[j] = (unsigned long)(0xffU << (8 - i));
3017         }
3018
3019         memcpy(mask, &in6, sizeof(struct in6_addr));
3020
3021         for (i = 0; i < 16 ; i++)
3022                 ip->s6_addr[i] = ip->s6_addr[i] & mask->s6_addr[i];
3023
3024         err = 0;
3025 out:
3026         g_strfreev(tokens);
3027
3028         return err;
3029 }
3030
3031 static struct connman_iptables *get_table(int type, const char *table_name)
3032 {
3033         struct connman_iptables *table = NULL;
3034
3035         if (!table_name)
3036                 table_name = "filter";
3037
3038         table = hash_table_lookup(type, table_name);
3039
3040         if (table)
3041                 return table;
3042
3043         table = iptables_init(type, table_name);
3044
3045         if (!table)
3046                 return NULL;
3047
3048         if (table->name)
3049                 g_free(table->name);
3050
3051         table->name = g_strdup(table_name);
3052
3053         hash_table_replace(type, table->name, table);
3054
3055         return table;
3056 }
3057
3058 struct parse_context {
3059         int type;
3060         int argc;
3061         char **argv;
3062         struct ipt_ip *ip;
3063         struct ip6t_ip6 *ipv6;
3064         struct xtables_target *xt_t;
3065         GList *xt_m;
3066         struct xtables_rule_match *xt_rm;
3067         uint16_t proto;
3068 };
3069
3070 static int prepare_getopt_args(const char *str, struct parse_context *ctx)
3071 {
3072         char **tokens;
3073         int i;
3074
3075         tokens = g_strsplit_set(str, " ", -1);
3076
3077         i = g_strv_length(tokens);
3078
3079         /* Add space for the argv[0] value */
3080         ctx->argc = i + 1;
3081
3082         /* Don't forget the last NULL entry */
3083         ctx->argv = g_try_malloc0((ctx->argc + 1) * sizeof(char *));
3084         if (!ctx->argv) {
3085                 g_strfreev(tokens);
3086                 return -ENOMEM;
3087         }
3088
3089         /*
3090          * getopt_long() jumps over the first token; we need to add some
3091          * random argv[0] entry.
3092          */
3093         ctx->argv[0] = g_strdup("argh");
3094         for (i = 1; i < ctx->argc; i++)
3095                 ctx->argv[i] = tokens[i - 1];
3096
3097         g_free(tokens);
3098
3099         return 0;
3100 }
3101
3102 static int parse_xt_modules(int c, bool invert,
3103                                 struct parse_context *ctx)
3104 {
3105         struct xtables_match *m;
3106         struct xtables_rule_match *rm;
3107         struct ipt_entry fw;
3108         struct ip6t_entry fw6;
3109         int err;
3110
3111         switch (ctx->type) {
3112         case AF_INET:
3113                 memset(&fw, 0, sizeof(fw));
3114
3115                 /* The SNAT parser wants to know the protocol. */
3116                 if (ctx->proto == 0)
3117                         ctx->proto = IPPROTO_IP;
3118
3119                 fw.ip.proto = ctx->proto;
3120                 break;
3121         case AF_INET6:
3122                 memset(&fw6, 0, sizeof(fw6));
3123
3124                 if (ctx->proto == 0)
3125                         ctx->proto = IPPROTO_IPV6;
3126
3127                 fw6.ipv6.proto = ctx->proto;
3128
3129                 /* Flags must be set for IPv6 if protocol is set. */
3130                 fw6.ipv6.flags |= IP6T_F_PROTO;
3131
3132                 break;
3133         default:
3134                 return 0;
3135         }
3136
3137         for (rm = ctx->xt_rm; rm; rm = rm->next) {
3138                 if (rm->completed != 0)
3139                         continue;
3140
3141                 m = rm->match;
3142
3143                 if (!m->x6_parse && !m->parse)
3144                         continue;
3145
3146                 if (c < (int) m->option_offset ||
3147                                 c >= (int) m->option_offset
3148                                         + XT_OPTION_OFFSET_SCALE)
3149                         continue;
3150
3151                 enable_jmp();
3152
3153                 if ((err = setjmp(env_state)) != 0) {
3154                         DBG("setjmp() called by longjmp() with value %d", err);
3155                         disable_jmp();
3156                         return -EINVAL;
3157                 }
3158
3159                 switch (ctx->type) {
3160                 case AF_INET:
3161                         xtables_option_mpcall(c, ctx->argv, invert, m, &fw);
3162                         break;
3163                 case AF_INET6:
3164                         xtables_option_mpcall(c, ctx->argv, invert, m, &fw6);
3165                         break;
3166                 }
3167
3168                 disable_jmp();
3169         }
3170
3171         if (!ctx->xt_t)
3172                 return 0;
3173
3174         if (!ctx->xt_t->x6_parse && !ctx->xt_t->parse)
3175                 return 0;
3176
3177         if (c < (int) ctx->xt_t->option_offset ||
3178                         c >= (int) ctx->xt_t->option_offset
3179                                         + XT_OPTION_OFFSET_SCALE)
3180                 return 0;
3181
3182         enable_jmp();
3183
3184         if ((err = setjmp(env_state)) != 0) {
3185                 DBG("setjmp() called by longjmp() with value %d", err);
3186                 disable_jmp();
3187                 return -EINVAL;
3188         }
3189
3190         switch (ctx->type) {
3191         case AF_INET:
3192                 xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, &fw);
3193                 break;
3194         case AF_INET6:
3195                 xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, &fw6);
3196                 break;
3197         }
3198
3199         disable_jmp();
3200
3201         return 0;
3202 }
3203
3204 static int final_check_xt_modules(struct parse_context *ctx)
3205 {
3206         struct xtables_rule_match *rm;
3207         int err;
3208
3209         for (rm = ctx->xt_rm; rm; rm = rm->next) {
3210                 enable_jmp();
3211
3212                 if ((err = setjmp(env_state)) != 0) {
3213                         DBG("setjmp() called by longjmp() with value %d", err);
3214                         disable_jmp();
3215                         return -EINVAL;
3216                 }
3217
3218                 xtables_option_mfcall(rm->match);
3219
3220                 disable_jmp();
3221         }
3222
3223         enable_jmp();
3224
3225         if ((err = setjmp(env_state)) != 0) {
3226                 DBG("setjmp() called by longjmp() with value %d", err);
3227                 disable_jmp();
3228                 return -EINVAL;
3229         }
3230
3231         if (ctx->xt_t)
3232                 xtables_option_tfcall(ctx->xt_t);
3233
3234         disable_jmp();
3235
3236         return 0;
3237 }
3238
3239 static int parse_rule_spec(struct connman_iptables *table,
3240                                 struct parse_context *ctx)
3241 {
3242         /*
3243          * How the parser works:
3244          *
3245          *  - If getopt finds 's', 'd', 'i', 'o'.
3246          *    just extract the information.
3247          *  - if '!' is found, set the invert flag to true and
3248          *    removes the '!' from the optarg string and jumps
3249          *    back to getopt to reparse the current optarg string.
3250          *    After reparsing the invert flag is reset to false.
3251          *  - If 'm' or 'j' is found then call either
3252          *    prepare_matches() or prepare_target(). Those function
3253          *    will modify (extend) the longopts for getopt_long.
3254          *    That means getopt will change its matching context according
3255          *    the loaded target.
3256          *
3257          *    Here an example with iptables-test
3258          *
3259          *    argv[0] = ./tools/iptables-test
3260          *    argv[1] = -t
3261          *    argv[2] = filter
3262          *    argv[3] = -A
3263          *    argv[4] = INPUT
3264          *    argv[5] = -m
3265          *    argv[6] = mark
3266          *    argv[7] = --mark
3267          *    argv[8] = 999
3268          *    argv[9] = -j
3269          *    argv[10] = LOG
3270          *
3271          *    getopt found 'm' then the optarg is "mark" and optind 7
3272          *    The longopts array containts before hitting the `case 'm'`
3273          *
3274          *    val A has_arg 1 name append
3275          *    val C has_arg 1 name compare
3276          *    val D has_arg 1 name delete
3277          *    val F has_arg 1 name flush-chain
3278          *    val I has_arg 1 name insert
3279          *    val L has_arg 2 name list
3280          *    val N has_arg 1 name new-chain
3281          *    val P has_arg 1 name policy
3282          *    val X has_arg 1 name delete-chain
3283          *    val d has_arg 1 name destination
3284          *    val i has_arg 1 name in-interface
3285          *    val j has_arg 1 name jump
3286          *    val m has_arg 1 name match
3287          *    val o has_arg 1 name out-interface
3288          *    val s has_arg 1 name source
3289          *    val t has_arg 1 name table
3290          *
3291          *    After executing the `case 'm'` block longopts is
3292          *
3293          *    val A has_arg 1 name append
3294          *    val C has_arg 1 name compare
3295          *    val D has_arg 1 name delete
3296          *    val F has_arg 1 name flush-chain
3297          *    val I has_arg 1 name insert
3298          *    val L has_arg 2 name list
3299          *    val N has_arg 1 name new-chain
3300          *    val P has_arg 1 name policy
3301          *    val X has_arg 1 name delete-chain
3302          *    val d has_arg 1 name destination
3303          *    val i has_arg 1 name in-interface
3304          *    val j has_arg 1 name jump
3305          *    val m has_arg 1 name match
3306          *    val o has_arg 1 name out-interface
3307          *    val s has_arg 1 name source
3308          *    val t has_arg 1 name table
3309          *    val   has_arg 1 name mark
3310          *
3311          *    So the 'mark' matcher has added the 'mark' options
3312          *    and getopt will then return c '256' optarg "999" optind 9
3313          *    And we will hit the 'default' statement which then
3314          *    will call the matchers parser (xt_m->parser() or
3315          *    xtables_option_mpcall() depending on which version
3316          *    of libxtables is found.
3317          */
3318         struct xtables_match *xt_m;
3319         bool invert = false;
3320         int len, c, err;
3321
3322         if (ctx->type != table->type) {
3323                 DBG("ctx->type %d does not match table->type %d", ctx->type,
3324                                 table->type);
3325                 return -EINVAL;
3326         }
3327
3328         switch (ctx->type) {
3329         case AF_INET:
3330                 ctx->ip = g_try_new0(struct ipt_ip, 1);
3331                 if (!ctx->ip)
3332                         return -ENOMEM;
3333
3334                 break;
3335         case AF_INET6:
3336                 ctx->ipv6 = g_try_new0(struct ip6t_ip6, 1);
3337                 if (!ctx->ipv6)
3338                         return -ENOMEM;
3339
3340                 break;
3341         default:
3342                 return -EINVAL;
3343         }
3344
3345         /*
3346          * Tell getopt_long not to generate error messages for unknown
3347          * options and also reset optind back to 0.
3348          */
3349         opterr = 0;
3350         optind = 0;
3351
3352         while ((c = getopt_long(ctx->argc, ctx->argv,
3353                                         "-:d:i:o:s:m:j:p:",
3354                                         ctx->type == AF_INET ?
3355                                                 iptables_globals.opts :
3356                                                 ip6tables_globals.opts,
3357                                         NULL)) != -1) {
3358                 switch (c) {
3359                 case 's':
3360                         if (ctx->type == AF_INET) {
3361                                 /* Source specification */
3362                                 if (!parse_ip_and_mask(optarg,
3363                                                         &ctx->ip->src,
3364                                                         &ctx->ip->smsk))
3365                                         break;
3366
3367                                 if (invert)
3368                                         ctx->ip->invflags |= IPT_INV_SRCIP;
3369                         }
3370
3371                         if (ctx->type == AF_INET6) {
3372                                 if (!parse_ipv6_and_mask(optarg,
3373                                                         &ctx->ipv6->src,
3374                                                         &ctx->ipv6->smsk))
3375                                         break;
3376
3377                                 if (invert)
3378                                         ctx->ipv6->invflags |= IP6T_INV_SRCIP;
3379                         }
3380
3381                         break;
3382                 case 'd':
3383                         if (ctx->type == AF_INET) {
3384                                 /* Destination specification */
3385                                 if (!parse_ip_and_mask(optarg,
3386                                                         &ctx->ip->dst,
3387                                                         &ctx->ip->dmsk))
3388                                         break;
3389
3390                                 if (invert)
3391                                         ctx->ip->invflags |= IPT_INV_DSTIP;
3392                         }
3393
3394                         if (ctx->type == AF_INET6) {
3395                                 /* Destination specification */
3396                                 if (!parse_ipv6_and_mask(optarg,
3397                                                         &ctx->ipv6->dst,
3398                                                         &ctx->ipv6->dmsk))
3399                                         break;
3400
3401                                 if (invert)
3402                                         ctx->ip->invflags |= IP6T_INV_DSTIP;
3403                         }
3404
3405                         break;
3406                 case 'i':
3407                         /* In interface specification */
3408                         len = strlen(optarg);
3409
3410                         if (len + 1 > IFNAMSIZ)
3411                                 break;
3412
3413                         if (ctx->type == AF_INET) {
3414                                 g_stpcpy(ctx->ip->iniface, optarg);
3415                                 memset(ctx->ip->iniface_mask, 0xff, len + 1);
3416
3417                                 if (invert)
3418                                         ctx->ip->invflags |= IPT_INV_VIA_IN;
3419                         }
3420
3421                         if (ctx->type == AF_INET6) {
3422                                 g_stpcpy(ctx->ipv6->iniface, optarg);
3423                                 memset(ctx->ipv6->iniface_mask, 0xff, len + 1);
3424
3425                                 if (invert)
3426                                         ctx->ipv6->invflags |= IP6T_INV_VIA_IN;
3427                         }
3428
3429                         break;
3430                 case 'o':
3431                         /* Out interface specification */
3432                         len = strlen(optarg);
3433
3434                         if (len + 1 > IFNAMSIZ)
3435                                 break;
3436
3437                         if (ctx->type == AF_INET) {
3438                                 g_stpcpy(ctx->ip->outiface, optarg);
3439                                 memset(ctx->ip->outiface_mask, 0xff, len + 1);
3440
3441                                 if (invert)
3442                                         ctx->ip->invflags |= IPT_INV_VIA_OUT;
3443                         }
3444
3445                         if (ctx->type == AF_INET6) {
3446                                 g_stpcpy(ctx->ipv6->outiface, optarg);
3447                                 memset(ctx->ipv6->outiface_mask, 0xff, len + 1);
3448
3449                                 if (invert)
3450                                         ctx->ipv6->invflags |= IP6T_INV_VIA_OUT;
3451                         }
3452
3453                         break;
3454                 case 'm':
3455                         /* Matches */
3456                         xt_m = prepare_matches(table, &ctx->xt_rm, optarg);
3457                         if (!xt_m) {
3458                                 err = -EINVAL;
3459                                 goto out;
3460                         }
3461                         ctx->xt_m = g_list_append(ctx->xt_m, xt_m);
3462
3463                         break;
3464                 case 'p':
3465                         enable_jmp();
3466
3467                         if ((err = setjmp(env_state)) != 0) {
3468                                 DBG("setjmp() called by longjmp() with value "
3469                                                         "%d", err);
3470                                 disable_jmp();
3471
3472                                 /* Errors from parse_rule_spec are negative */
3473                                 err = -EINVAL;
3474                                 goto out;
3475                         }
3476
3477                         ctx->proto = xtables_parse_protocol(optarg);
3478
3479                         disable_jmp();
3480
3481                         /*
3482                          * If protocol was set add it to ipt_ip.
3483                          * xtables_parse_protocol() returns 0 or
3484                          * UINT16_MAX (-1) on error
3485                          */
3486                         if (ctx->proto > 0 && ctx->proto < UINT16_MAX) {
3487                                 if (ctx->type == AF_INET)
3488                                         ctx->ip->proto = ctx->proto;
3489
3490                                 if (ctx->type == AF_INET6) {
3491                                         ctx->ipv6->proto = ctx->proto;
3492
3493                                         /*
3494                                          * Flags must be set for IPv6 if
3495                                          * protocol is set.
3496                                          */
3497                                         ctx->ipv6->flags |= IP6T_F_PROTO;
3498                                 }
3499                         }
3500                         break;
3501                 case 'j':
3502                         /* Target */
3503                         ctx->xt_t = prepare_target(table, optarg);
3504                         if (!ctx->xt_t) {
3505                                 err = -EINVAL;
3506                                 goto out;
3507                         }
3508
3509                         break;
3510                 case 1:
3511                         if (optarg[0] == '!' && optarg[1] == '\0') {
3512                                 invert = true;
3513
3514                                 /* Remove the '!' from the optarg */
3515                                 optarg[0] = '\0';
3516
3517                                 /*
3518                                  * And recall getopt_long without resetting
3519                                  * invert.
3520                                  */
3521                                 continue;
3522                         }
3523
3524                         break;
3525                 default:
3526                         err = parse_xt_modules(c, invert, ctx);
3527                         if (err == 1)
3528                                 continue;
3529                         else if (err == -EINVAL)
3530                                 goto out;
3531
3532                         break;
3533                 }
3534
3535                 invert = false;
3536         }
3537
3538         err = final_check_xt_modules(ctx);
3539
3540 out:
3541         return err;
3542 }
3543
3544 static int current_type = -1;
3545
3546 static int setup_xtables(int type)
3547 {
3548         int err;
3549
3550         DBG("%d", type);
3551
3552         if (type == current_type)
3553                 return 0;
3554
3555         if (current_type != -1)
3556                 reset_xtables();
3557
3558         switch (type) {
3559         case AF_INET:
3560                 err = xtables_init_all(&iptables_globals, NFPROTO_IPV4);
3561                 break;
3562         case AF_INET6:
3563                 err = xtables_init_all(&ip6tables_globals, NFPROTO_IPV6);
3564                 break;
3565         default:
3566                 return -1;
3567         }
3568
3569         if (!err) {
3570                 current_type = type;
3571         } else {
3572                 connman_error("error initializing xtables");
3573                 current_type = -1;
3574                 reset_xtables();
3575         }
3576
3577         return err;
3578 }
3579
3580 static void reset_xtables(void)
3581 {
3582         struct xtables_match *xt_m;
3583         struct xtables_target *xt_t;
3584
3585         /*
3586          * As side effect parsing a rule sets some global flags
3587          * which will be evaluated/verified. Let's reset them
3588          * to ensure we can parse more than one rule.
3589          *
3590          * Clear all flags because the flags are only valid
3591          * for one rule.
3592          */
3593         for (xt_m = xtables_matches; xt_m; xt_m = xt_m->next)
3594                 xt_m->mflags = 0;
3595
3596         for (xt_t = xtables_targets; xt_t; xt_t = xt_t->next) {
3597                 xt_t->tflags = 0;
3598                 xt_t->used = 0;
3599         }
3600
3601         /*
3602          * We need also to free the memory implicitly allocated
3603          * during parsing (see xtables_options_xfrm()).
3604          * Note xt_params is actually iptables_globals.
3605          */
3606         if (xt_params->opts != xt_params->orig_opts) {
3607                 g_free(xt_params->opts);
3608                 xt_params->opts = xt_params->orig_opts;
3609         }
3610         xt_params->option_offset = 0;
3611 }
3612
3613 static void cleanup_parse_context(struct parse_context *ctx)
3614 {
3615         struct xtables_rule_match *rm, *tmp;
3616         GList *list;
3617
3618         g_strfreev(ctx->argv);
3619
3620         g_free(ctx->ip);
3621         g_free(ctx->ipv6);
3622
3623         if (ctx->xt_t) {
3624                 g_free(ctx->xt_t->t);
3625                 ctx->xt_t->t = NULL;
3626         }
3627
3628         for (list = ctx->xt_m; list; list = list->next) {
3629                 struct xtables_match *xt_m = list->data;
3630
3631                 g_free(xt_m->m);
3632
3633                 if (xt_m != xt_m->next)
3634                         continue;
3635
3636                 g_free(xt_m);
3637         }
3638         g_list_free(ctx->xt_m);
3639
3640         for (tmp = NULL, rm = ctx->xt_rm; rm; rm = rm->next) {
3641                 if (tmp)
3642                         g_free(tmp);
3643                 tmp = rm;
3644         }
3645         g_free(tmp);
3646
3647         g_free(ctx);
3648 }
3649
3650 int __connman_iptables_dump(int type, const char *table_name)
3651 {
3652         struct connman_iptables *table;
3653
3654         DBG("%d -t %s -L", type, table_name);
3655
3656         table = get_table(type, table_name);
3657         if (!table)
3658                 return -EINVAL;
3659
3660         dump_table(table);
3661
3662         return 0;
3663 }
3664
3665 int __connman_iptables_new_chain(int type,
3666                                         const char *table_name,
3667                                         const char *chain)
3668 {
3669         struct connman_iptables *table;
3670
3671         DBG("%d -t %s -N %s", type, table_name, chain);
3672
3673         table = get_table(type, table_name);
3674         if (!table) {
3675                 return -EINVAL;
3676         }
3677
3678         switch (type) {
3679         case AF_INET:
3680         case AF_INET6:
3681                 return iptables_add_chain(table, chain);
3682         }
3683
3684         return -EINVAL;
3685 }
3686
3687 int __connman_iptables_delete_chain(int type,
3688                                         const char *table_name,
3689                                         const char *chain)
3690 {
3691         struct connman_iptables *table;
3692
3693         DBG("%d -t %s -X %s", type, table_name, chain);
3694
3695         table = get_table(type, table_name);
3696         if (!table)
3697                 return -EINVAL;
3698
3699         return iptables_delete_chain(table, chain);
3700 }
3701
3702 int __connman_iptables_flush_chain(int type,
3703                                         const char *table_name,
3704                                         const char *chain)
3705 {
3706         struct connman_iptables *table;
3707
3708         DBG("%d -t %s -F %s", type, table_name, chain);
3709
3710         table = get_table(type, table_name);
3711         if (!table)
3712                 return -EINVAL;
3713
3714         return iptables_flush_chain(table, chain);
3715 }
3716
3717 int __connman_iptables_find_chain(int type,
3718                                         const char *table_name,
3719                                         const char *chain)
3720 {
3721         struct connman_iptables *table;
3722
3723         DBG("%d -t %s -F %s", type, table_name, chain);
3724
3725         table = get_table(type, table_name);
3726         if (!table)
3727                 return -EINVAL;
3728
3729         if(!find_chain_head(table, chain))
3730                 return -ENOENT; // Not Found
3731
3732         return 0; // Found
3733 }
3734
3735 int __connman_iptables_change_policy(int type,
3736                                         const char *table_name,
3737                                         const char *chain,
3738                                         const char *policy)
3739 {
3740         struct connman_iptables *table;
3741
3742         DBG("%d -t %s -F %s", type, table_name, chain);
3743
3744         table = get_table(type, table_name);
3745         if (!table)
3746                 return -EINVAL;
3747
3748         return iptables_change_policy(table, chain, policy);
3749 }
3750
3751 static void iptables_ip_setup(struct iptables_ip *ip, struct parse_context *ctx)
3752 {
3753         if (!ip || !ctx)
3754                 return;
3755
3756         ip->type = ctx->type;
3757         ip->ip = ctx->ip;
3758         ip->ip6 = ctx->ipv6;
3759 }
3760
3761 int __connman_iptables_append(int type,
3762                                 const char *table_name,
3763                                 const char *chain,
3764                                 const char *rule_spec)
3765 {
3766         struct connman_iptables *table;
3767         struct parse_context *ctx;
3768         struct iptables_ip ip = { 0 };
3769         const char *target_name;
3770         int err;
3771
3772         err = setup_xtables(type);
3773
3774         if (err < 0)
3775                 return err;
3776
3777         ctx = g_try_new0(struct parse_context, 1);
3778         if (!ctx)
3779                 return -ENOMEM;
3780
3781         ctx->type = type;
3782
3783         DBG("%d -t %s -A %s %s", type, table_name, chain, rule_spec);
3784
3785         err = prepare_getopt_args(rule_spec, ctx);
3786         if (err < 0)
3787                 goto out;
3788
3789         table = get_table(type, table_name);
3790         if (!table) {
3791                 err = -EINVAL;
3792                 goto out;
3793         }
3794
3795         err = parse_rule_spec(table, ctx);
3796         if (err < 0)
3797                 goto out;
3798
3799         if (!ctx->xt_t)
3800                 target_name = NULL;
3801         else
3802                 target_name = ctx->xt_t->name;
3803
3804         iptables_ip_setup(&ip, ctx);
3805
3806         err = iptables_append_rule(table, &ip, chain, target_name, ctx->xt_t,
3807                                                                 ctx->xt_rm);
3808 out:
3809         cleanup_parse_context(ctx);
3810         reset_xtables();
3811
3812         return err;
3813 }
3814
3815 int __connman_iptables_insert(int type,
3816                                 const char *table_name,
3817                                 const char *chain,
3818                                 const char *rule_spec)
3819 {
3820         struct connman_iptables *table;
3821         struct parse_context *ctx;
3822         struct iptables_ip ip = { 0 };
3823         const char *target_name;
3824         int err;
3825
3826         err = setup_xtables(type);
3827
3828         if (err < 0)
3829                 return err;
3830
3831         ctx = g_try_new0(struct parse_context, 1);
3832         if (!ctx)
3833                 return -ENOMEM;
3834
3835         ctx->type = type;
3836
3837         DBG("%d -t %s -I %s %s", type, table_name, chain, rule_spec);
3838
3839         err = prepare_getopt_args(rule_spec, ctx);
3840         if (err < 0)
3841                 goto out;
3842
3843         table = get_table(type, table_name);
3844         if (!table) {
3845                 err = -EINVAL;
3846                 goto out;
3847         }
3848
3849         err = parse_rule_spec(table, ctx);
3850         if (err < 0)
3851                 goto out;
3852
3853         if (!ctx->xt_t)
3854                 target_name = NULL;
3855         else
3856                 target_name = ctx->xt_t->name;
3857
3858         iptables_ip_setup(&ip, ctx);
3859
3860         err = iptables_insert_rule(table, &ip, chain, target_name, ctx->xt_t,
3861                                                                 ctx->xt_rm);
3862 out:
3863         cleanup_parse_context(ctx);
3864         reset_xtables();
3865
3866         return err;
3867 }
3868
3869 int __connman_iptables_delete(int type,
3870                                 const char *table_name,
3871                                 const char *chain,
3872                                 const char *rule_spec)
3873 {
3874         struct connman_iptables *table;
3875         struct parse_context *ctx;
3876         struct iptables_ip ip = { 0 };
3877         const char *target_name;
3878         int err;
3879
3880         err = setup_xtables(type);
3881
3882         if (err < 0)
3883                 return err;
3884
3885         ctx = g_try_new0(struct parse_context, 1);
3886         if (!ctx)
3887                 return -ENOMEM;
3888
3889         ctx->type = type;
3890
3891         DBG("%d -t %s -D %s %s", type, table_name, chain, rule_spec);
3892
3893         err = prepare_getopt_args(rule_spec, ctx);
3894         if (err < 0)
3895                 goto out;
3896
3897         table = get_table(type, table_name);
3898         if (!table) {
3899                 err = -EINVAL;
3900                 goto out;
3901         }
3902
3903         err = parse_rule_spec(table, ctx);
3904         if (err < 0)
3905                 goto out;
3906
3907         if (!ctx->xt_t)
3908                 target_name = NULL;
3909         else
3910                 target_name = ctx->xt_t->name;
3911
3912         iptables_ip_setup(&ip, ctx);
3913
3914         err = iptables_delete_rule(table, &ip, chain, target_name, ctx->xt_t,
3915                                 ctx->xt_m, ctx->xt_rm);
3916 out:
3917         cleanup_parse_context(ctx);
3918         reset_xtables();
3919
3920         return err;
3921 }
3922
3923 int __connman_iptables_commit(int type, const char *table_name)
3924 {
3925         struct connman_iptables *table;
3926         struct iptables_replace repl = { 0 };
3927         int err;
3928         struct xt_counters_info *counters;
3929         struct connman_iptables_entry *e;
3930         GList *list;
3931         unsigned int cnt;
3932
3933         err = setup_xtables(type);
3934
3935         if (err < 0)
3936                 return err;
3937
3938         DBG("%d %s", type, table_name);
3939
3940         repl.type = type;
3941
3942         table = hash_table_lookup(type, table_name);
3943         if (!table)
3944                 return -EINVAL;
3945
3946         switch (type) {
3947         case AF_INET:
3948                 repl.r = iptables_blob(table);
3949                 if (!repl.r)
3950                         return -ENOMEM;
3951
3952                 break;
3953         case AF_INET6:
3954                 repl.r6 = ip6tables_blob(table);
3955                 if (!repl.r6)
3956                         return -ENOMEM;
3957         }
3958
3959         if (debug_enabled)
3960                 dump_replace(&repl);
3961
3962         err = iptables_replace(table, &repl);
3963
3964         if (err < 0)
3965                 goto out_free;
3966
3967         counters = g_try_malloc0(sizeof(*counters) +
3968                         sizeof(struct xt_counters) * table->num_entries);
3969         if (!counters) {
3970                 err = -ENOMEM;
3971                 goto out_hash_remove;
3972         }
3973         g_stpcpy(counters->name, iptables_table_get_info_name(table));
3974         counters->num_counters = table->num_entries;
3975         for (list = table->entries, cnt = 0; list; list = list->next, cnt++) {
3976                 e = list->data;
3977                 if (e->counter_idx >= 0) {
3978
3979                         switch (type) {
3980                         case AF_INET:
3981                                 counters->counters[cnt] =
3982                                         repl.r->counters[e->counter_idx];
3983                                 break;
3984                         case AF_INET6:
3985                                 counters->counters[cnt] =
3986                                         repl.r6->counters[e->counter_idx];
3987                                 break;
3988                         }
3989                 }
3990         }
3991         err = iptables_add_counters(table, counters);
3992         g_free(counters);
3993
3994         if (err < 0)
3995                 goto out_hash_remove;
3996
3997         err = 0;
3998
3999 out_hash_remove:
4000         hash_table_remove(type, table_name);
4001 out_free:
4002         if (type == AF_INET && repl.r)
4003                 g_free(repl.r->counters);
4004
4005         if (type == AF_INET6 && repl.r6)
4006                 g_free(repl.r6->counters);
4007
4008         g_free(repl.r);
4009         g_free(repl.r6);
4010
4011         reset_xtables();
4012
4013         return err;
4014 }
4015
4016 static void remove_table(gpointer user_data)
4017 {
4018         struct connman_iptables *table = user_data;
4019
4020         table_cleanup(table);
4021 }
4022
4023 static int iterate_chains_cb(struct connman_iptables_entry *entry, int builtin,
4024                                 unsigned int hook, size_t size,
4025                                 unsigned int offset, void *user_data)
4026 {
4027         struct cb_data *cbd = user_data;
4028         connman_iptables_iterate_chains_cb_t cb = cbd->cb;
4029         struct xt_entry_target *target;
4030
4031         if (offset + iptables_entry_get_next_offset(entry) == size)
4032                 return 0;
4033
4034         target = iptables_entry_get_target(entry);
4035
4036         if (!g_strcmp0(target->u.user.name, get_error_target(entry->type))) {
4037                 (*cb)((const char *)target->data, cbd->user_data);
4038         } else if (builtin >= 0) {
4039                 (*cb)(hooknames[builtin], cbd->user_data);
4040         }
4041
4042         return 0;
4043 }
4044
4045 int __connman_iptables_iterate_chains(int type, const char *table_name,
4046                                 connman_iptables_iterate_chains_cb_t cb,
4047                                 void *user_data)
4048 {
4049         struct cb_data *cbd = cb_data_new(cb, user_data);
4050         struct connman_iptables *table;
4051         struct connman_iptables_entry entry = { 0 };
4052         int err;
4053
4054         err = setup_xtables(type);
4055 #if defined TIZEN_EXT
4056         if (err < 0) {
4057                 g_free(cbd);
4058                 return err;
4059         }
4060 #else
4061         if (err < 0)
4062                 return err;
4063 #endif
4064
4065         table = get_table(type, table_name);
4066         if (!table) {
4067                 g_free(cbd);
4068                 return -EINVAL;
4069         }
4070
4071         entry.type = type;
4072
4073         if (type == AF_INET)
4074                 entry.entry = table->blob_entries->entrytable;
4075
4076         if (type == AF_INET6)
4077                 entry.entry6 = table->blob_entries6->entrytable;
4078
4079         iterate_entries(&entry,
4080                         iptables_table_get_info_valid_hooks(table),
4081                         iptables_table_get_info_hook_entry(table),
4082                         iptables_table_get_info_underflow(table),
4083                         iptables_table_get_entries_size(table),
4084                         iterate_chains_cb, cbd);
4085
4086         g_free(cbd);
4087
4088         reset_xtables();
4089
4090         return 0;
4091 }
4092
4093 int __connman_iptables_init(void)
4094 {
4095         DBG("");
4096
4097         if (getenv("CONNMAN_IPTABLES_DEBUG"))
4098                 debug_enabled = true;
4099
4100         table_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
4101                                                 NULL, remove_table);
4102
4103         table_hash_ipv6 = g_hash_table_new_full(g_str_hash, g_str_equal,
4104                                                 NULL, remove_table);
4105
4106         return 0;
4107 }
4108
4109 void __connman_iptables_cleanup(void)
4110 {
4111         DBG("");
4112
4113         g_hash_table_destroy(table_hash);
4114         g_hash_table_destroy(table_hash_ipv6);
4115 }
4116