4 * Copyright (C) 2007-2010 Intel Corporation. All rights reserved.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <sys/errno.h>
26 #include <sys/socket.h>
29 #include <linux/netfilter_ipv4/ip_tables.h>
33 static const char *hooknames[] = {
34 [NF_IP_PRE_ROUTING] = "PREROUTING",
35 [NF_IP_LOCAL_IN] = "INPUT",
36 [NF_IP_FORWARD] = "FORWARD",
37 [NF_IP_LOCAL_OUT] = "OUTPUT",
38 [NF_IP_POST_ROUTING] = "POSTROUTING",
41 #define LABEL_ACCEPT "ACCEPT"
42 #define LABEL_DROP "DROP"
43 #define LABEL_QUEUE "QUEUE"
44 #define LABEL_RETURN "RETURN"
46 /* fn returns 0 to continue iteration */
47 #define _XT_ENTRY_ITERATE_CONTINUE(type, entries, size, n, fn, args...) \
54 for (__i = 0, __n = 0; __i < (size); \
55 __i += __entry->next_offset, __n++) { \
56 __entry = (void *)(entries) + __i; \
60 __ret = fn(__entry, ## args); \
67 /* fn returns 0 to continue iteration */
68 #define _XT_ENTRY_ITERATE(type, entries, size, fn, args...) \
69 _XT_ENTRY_ITERATE_CONTINUE(type, entries, size, 0, fn, args)
71 #define ENTRY_ITERATE(entries, size, fn, args...) \
72 _XT_ENTRY_ITERATE(struct ipt_entry, entries, size, fn, ## args)
74 #define MIN_ALIGN (__alignof__(struct ipt_entry))
76 #define ALIGN(s) (((s) + ((MIN_ALIGN)-1)) & ~((MIN_ALIGN)-1))
78 struct ipt_error_target {
79 struct xt_entry_target t;
80 char error[IPT_TABLE_MAXNAMELEN];
83 struct connman_iptables_entry {
88 struct ipt_entry *entry;
91 struct connman_iptables {
94 struct ipt_getinfo *info;
95 struct ipt_get_entries *blob_entries;
97 unsigned int num_entries;
98 unsigned int old_entries;
105 static struct ipt_entry *get_entry(struct connman_iptables *table,
108 return (struct ipt_entry *)((char *)table->blob_entries->entrytable +
112 static int is_hook_entry(struct connman_iptables *table,
113 struct ipt_entry *entry)
117 for (i = 0; i < NF_INET_NUMHOOKS; i++) {
118 if ((table->info->valid_hooks & (1 << i))
119 && get_entry(table, table->info->hook_entry[i]) == entry)
126 static unsigned long entry_to_offset(struct connman_iptables *table,
127 struct ipt_entry *entry)
129 return (void *)entry - (void *)table->blob_entries->entrytable;
132 static int target_to_verdict(char *target_name)
134 if (!strcmp(target_name, LABEL_ACCEPT))
135 return -NF_ACCEPT - 1;
137 if (!strcmp(target_name, LABEL_DROP))
140 if (!strcmp(target_name, LABEL_QUEUE))
141 return -NF_QUEUE - 1;
143 if (!strcmp(target_name, LABEL_RETURN))
149 static gboolean is_builtin_target(char *target_name)
151 if (!strcmp(target_name, LABEL_ACCEPT) ||
152 !strcmp(target_name, LABEL_DROP) ||
153 !strcmp(target_name, LABEL_QUEUE) ||
154 !strcmp(target_name, LABEL_RETURN))
160 static gboolean is_chain(struct connman_iptables *table,
161 struct connman_iptables_entry *e)
164 struct ipt_entry *entry;
165 struct xt_entry_target *target;
168 builtin = is_hook_entry(table, entry);
172 target = ipt_get_target(entry);
173 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET))
179 static GList *find_chain_tail(struct connman_iptables *table,
182 GList *chain_head, *list;
183 struct connman_iptables_entry *head, *tail;
184 struct ipt_entry *entry;
185 struct xt_entry_target *target;
188 /* First we look for the head */
189 for (list = table->entries; list; list = list->next) {
194 builtin = is_hook_entry(table, entry);
195 if (builtin >= 0 && !strcmp(hooknames[builtin], chain_name))
198 /* User defined chain */
199 target = ipt_get_target(entry);
200 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET) &&
201 !strcmp((char *)target->data, chain_name))
210 /* Then we look for the next chain */
211 for (list = chain_head->next; list; list = list->next) {
215 if (is_chain(table, tail))
219 /* Nothing found, we return the table end */
220 return g_list_last(table->entries);
223 static int connman_add_entry(struct connman_iptables *table,
224 struct ipt_entry *entry, GList *before)
226 struct connman_iptables_entry *e;
231 e = g_try_malloc0(sizeof(struct connman_iptables_entry));
237 table->entries = g_list_insert_before(table->entries, before, e);
238 table->num_entries++;
239 table->size += entry->next_offset;
244 static int connman_iptables_add_chain(struct connman_iptables *table,
248 struct ipt_entry *entry_head;
249 struct ipt_entry *entry_return;
250 struct ipt_error_target *error;
251 struct ipt_standard_target *standard;
252 u_int16_t entry_head_size, entry_return_size;
254 last = g_list_last(table->entries);
257 * An empty chain is composed of:
258 * - A head entry, with no match and an error target.
259 * The error target data is the chain name.
260 * - A tail entry, with no match and a standard target.
261 * The standard target verdict is XT_RETURN (return to the
266 entry_head_size = sizeof(struct ipt_entry) +
267 sizeof(struct ipt_error_target);
268 entry_head = g_try_malloc0(entry_head_size);
269 if (entry_head == NULL)
272 memset(entry_head, 0, entry_head_size);
274 entry_head->target_offset = sizeof(struct ipt_entry);
275 entry_head->next_offset = entry_head_size;
277 error = (struct ipt_error_target *) entry_head->elems;
278 strcpy(error->t.u.user.name, IPT_ERROR_TARGET);
279 error->t.u.user.target_size = ALIGN(sizeof(struct ipt_error_target));
280 strcpy(error->error, name);
282 if (connman_add_entry(table, entry_head, last) < 0)
286 entry_return_size = sizeof(struct ipt_entry) +
287 sizeof(struct ipt_standard_target);
288 entry_return = g_try_malloc0(entry_return_size);
289 if (entry_return == NULL)
292 memset(entry_return, 0, entry_return_size);
294 entry_return->target_offset = sizeof(struct ipt_entry);
295 entry_return->next_offset = entry_return_size;
297 standard = (struct ipt_standard_target *) entry_return->elems;
298 standard->target.u.user.target_size =
299 ALIGN(sizeof(struct ipt_standard_target));
300 standard->verdict = XT_RETURN;
302 if (connman_add_entry(table, entry_return, last) < 0)
309 g_free(entry_return);
314 static struct ipt_entry *
315 new_builtin_rule(char *target_name,
316 char *match_name, int match_argc, char **match_argv)
318 struct ipt_entry *new_entry;
319 size_t match_size, target_size;
320 struct xtables_match *xt_m;
321 struct xt_standard_target *target;
327 xt_m = xtables_find_match(match_name, XTF_TRY_LOAD, NULL);
331 match_size = ALIGN(sizeof(struct xt_entry_match)) + xt_m->size;
334 target_size = ALIGN(sizeof(struct xt_standard_target));
336 new_entry = g_try_malloc0(sizeof(struct ipt_entry) + target_size +
338 if (new_entry == NULL)
341 new_entry->target_offset = sizeof(struct ipt_entry) + match_size;
342 new_entry->next_offset = sizeof(struct ipt_entry) + target_size +
346 struct xt_entry_match *entry_match;
348 entry_match = (struct xt_entry_match *)new_entry->elems;
349 entry_match->u.match_size = match_size;
350 strcpy(entry_match->u.user.name, xt_m->name);
351 entry_match->u.user.revision = xt_m->revision;
352 if (xt_m->init != NULL)
353 xt_m->init(entry_match);
356 target = (struct xt_standard_target *)(new_entry->elems + match_size);
357 strcpy(target->target.u.user.name, IPT_STANDARD_TARGET);
358 target->target.u.user.target_size =
359 ALIGN(sizeof(struct ipt_standard_target));
360 target->verdict = target_to_verdict(target_name);
365 static struct ipt_entry *
366 new_custom_rule(char *target_name, int target_argc, char **target_argv,
367 char *match_name, int match_argc, char **match_argv)
372 static struct ipt_entry *
373 new_rule(char *target_name, int target_argc, char **target_argv,
374 char *match_name, int match_argc, char **match_argv)
376 struct ipt_entry *new_entry;
378 if (is_builtin_target(target_name))
379 new_entry = new_builtin_rule(target_name,
380 match_name, match_argc, match_argv);
382 new_entry = new_custom_rule(target_name,
383 target_argc, target_argv,
384 match_name, match_argc, match_argv);
390 connman_iptables_add_rule(struct connman_iptables *table, char *chain_name,
391 char *target_name, int target_argc, char **target_argv,
392 char *match_name, int match_argc, char **match_argv)
395 struct ipt_entry *new_entry;
397 chain_tail = find_chain_tail(table, chain_name);
398 if (chain_tail == NULL)
401 printf("Chains found\n");
403 new_entry = new_rule(target_name, target_argc, target_argv,
404 match_name, match_argc, match_argv);
405 if (new_entry == NULL)
408 return connman_add_entry(table, new_entry, chain_tail->prev);
411 static struct ipt_replace *
412 connman_iptables_blob(struct connman_iptables *table)
414 struct ipt_replace *r;
416 struct connman_iptables_entry *e;
417 unsigned char *entry_index;
419 r = g_try_malloc0(sizeof(struct ipt_replace) + table->size);
423 memset(r, 0, sizeof(*r) + table->size);
425 r->counters = g_try_malloc0(sizeof(struct xt_counters)
426 * table->num_entries);
427 if (r->counters == NULL) {
432 strcpy(r->name, table->info->name);
433 r->num_entries = table->num_entries;
434 r->size = table->size;
436 r->num_counters = table->old_entries;
437 r->valid_hooks = table->info->valid_hooks;
439 memcpy(r->hook_entry, table->info->hook_entry,
440 sizeof(table->info->hook_entry));
441 memcpy(r->underflow, table->info->underflow,
442 sizeof(table->info->underflow));
444 entry_index = (unsigned char *)r->entries;
445 for (list = table->entries; list; list = list->next) {
448 memcpy(entry_index, e->entry, e->entry->next_offset);
449 entry_index += e->entry->next_offset;
455 static void dump_target(struct connman_iptables *table,
456 struct ipt_entry *entry)
459 struct xtables_target *xt_t;
460 struct xt_entry_target *target;
462 target = ipt_get_target(entry);
464 if (!strcmp(target->u.user.name, IPT_STANDARD_TARGET)) {
465 struct xt_standard_target *t;
467 t = (struct xt_standard_target *)target;
469 switch (t->verdict) {
471 printf("\ttarget RETURN\n");
475 printf("\ttarget ACCEPT\n");
479 printf("\ttarget DROP\n");
483 printf("\ttarget QUEUE\n");
487 printf("\ttarget STOP\n");
491 printf("\tJUMP @%p (0x%x)\n",
492 (char*)table->blob_entries->entrytable +
493 t->verdict, t->verdict);
497 xt_t = xtables_find_target(IPT_STANDARD_TARGET,
498 XTF_LOAD_MUST_SUCCEED);
500 if(xt_t->print != NULL)
501 xt_t->print(NULL, target, 1);
503 printf("\ttarget %s\n", target->u.user.name);
505 xt_t = xtables_find_target(target->u.user.name, XTF_TRY_LOAD);
509 if(xt_t->print != NULL)
510 xt_t->print(NULL, target, 1);
514 static void dump_match(struct connman_iptables *table, struct ipt_entry *entry)
516 struct xtables_match *xt_m;
517 struct xt_entry_match *match;
519 match = (struct xt_entry_match *) entry->elems;
521 if (!strlen(match->u.user.name))
524 xt_m = xtables_find_match(match->u.user.name, XTF_TRY_LOAD, NULL);
528 if(xt_m->print != NULL) {
530 xt_m->print(NULL, match, 1);
537 printf("\tmatch %s\n", match->u.user.name);
541 static int connman_iptables_dump_entry(struct ipt_entry *entry,
542 struct connman_iptables *table)
544 struct xt_entry_target *target;
548 offset = (char *)entry - (char *)table->blob_entries->entrytable;
549 target = ipt_get_target(entry);
550 builtin = is_hook_entry(table, entry);
552 if (entry_to_offset(table, entry) + entry->next_offset ==
553 table->blob_entries->size) {
554 printf("End of CHAIN 0x%x\n", offset);
558 if (!strcmp(target->u.user.name, IPT_ERROR_TARGET)) {
559 printf("USER CHAIN (%s) %p match %p target %p size %d\n",
560 target->data, entry, entry->elems,
561 (char *)entry + entry->target_offset,
565 } else if (builtin >= 0) {
566 printf("CHAIN (%s) %p match %p target %p size %d\n",
567 hooknames[builtin], entry, entry->elems,
568 (char *)entry + entry->target_offset,
571 printf("RULE %p match %p target %p size %d\n", entry,
573 (char *)entry + entry->target_offset,
577 dump_match(table, entry);
578 dump_target(table, entry);
583 static void connman_iptables_dump(struct connman_iptables *table)
585 printf("%s valid_hooks=0x%08x, num_entries=%u, size=%u\n",
587 table->info->valid_hooks, table->info->num_entries,
590 ENTRY_ITERATE(table->blob_entries->entrytable,
591 table->blob_entries->size,
592 connman_iptables_dump_entry, table);
596 static int connman_iptables_get_entries(struct connman_iptables *table)
598 socklen_t entry_size;
600 entry_size = sizeof(struct ipt_get_entries) + table->info->size;
602 return getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_ENTRIES,
603 table->blob_entries, &entry_size);
606 static int connman_iptables_replace(struct connman_iptables *table,
607 struct ipt_replace *r)
609 return setsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_SET_REPLACE, r,
610 sizeof(*r) + r->size);
613 static void connman_iptables_cleanup(struct connman_iptables *table)
615 close(table->ipt_sock);
617 g_free(table->blob_entries);
621 static int add_entry(struct ipt_entry *entry, struct connman_iptables *table)
623 return connman_add_entry(table, entry, NULL);
626 static struct connman_iptables *connman_iptables_init(const char *table_name)
628 struct connman_iptables *table;
631 table = g_try_new0(struct connman_iptables, 1);
635 table->info = g_try_new0(struct ipt_getinfo, 1);
636 if (table->info == NULL)
639 table->ipt_sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
640 if (table->ipt_sock < 0)
643 s = sizeof(*table->info);
644 strcpy(table->info->name, table_name);
645 if (getsockopt(table->ipt_sock, IPPROTO_IP, IPT_SO_GET_INFO,
646 table->info, &s) < 0)
649 table->blob_entries = g_try_malloc0(sizeof(struct ipt_get_entries) +
651 if (table->blob_entries == NULL)
654 strcpy(table->blob_entries->name, table_name);
655 table->blob_entries->size = table->info->size;
657 if (connman_iptables_get_entries(table) < 0)
660 table->num_entries = 0;
661 table->old_entries = table->info->num_entries;
664 ENTRY_ITERATE(table->blob_entries->entrytable,
665 table->blob_entries->size,
673 connman_iptables_cleanup(table);
678 int main(int argc, char *argv[])
680 struct ipt_replace *repl;
681 struct connman_iptables *table;
684 xtables_set_nfproto(NFPROTO_IPV4);
686 table = connman_iptables_init("filter");
690 connman_iptables_dump(table);
693 connman_iptables_add_chain(table, argv[1]);
695 connman_iptables_add_rule(table, argv[1],
699 repl = connman_iptables_blob(table);
701 connman_iptables_replace(table, repl);
704 connman_iptables_cleanup(table);