2 * ConnMan firewall unit tests
4 * Copyright (C) 2019 Jolla Ltd. All rights reserved.
5 * Contact: jussi.laakkonen@jolla.com
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.
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.
29 #include "src/connman.h"
32 TEST_CONFIG_PASS = 0x0001,
33 TEST_CONFIG_INIT_FAIL = 0x0002,
34 TEST_CONFIG_FIND_MATCH_FAIL = 0x0004,
35 TEST_CONFIG_FIND_TARGET_FAIL = 0x0008,
36 TEST_CONFIG_PARSE_PROTOCOL_FAIL = 0x0010,
37 TEST_CONFIG_MFCALL_FAIL = 0x0020,
38 TEST_CONFIG_TFCALL_FAIL = 0x0040,
39 TEST_CONFIG_MPCALL_FAIL = 0x0080,
40 TEST_CONFIG_TPCALL_FAIL = 0x0100,
41 TEST_CONFIG_INSMOD_FAIL = 0x0200,
42 TEST_CONFIG_COMPATIBLE_REV_FAIL = 0x0400,
43 TEST_CONFIG_OPTIONS_XFRM_FAIL = 0x0800,
44 TEST_CONFIG_MERGE_OPTIONS_FAIL = 0x1000,
47 enum configtype test_config_type = TEST_CONFIG_PASS;
49 static void set_test_config(enum configtype type)
51 test_config_type = type;
54 /* Start of dummies */
58 /* From /usr/include/linux/netfilter_ipv4/ip_tables.h */
59 #define IPT_BASE_CTL 64
60 #define IPT_SO_SET_REPLACE (IPT_BASE_CTL)
61 #define IPT_SO_SET_ADD_COUNTERS (IPT_BASE_CTL + 1)
62 #define IPT_SO_GET_INFO (IPT_BASE_CTL)
63 #define IPT_SO_GET_ENTRIES (IPT_BASE_CTL + 1)
65 /* From /usr/include/linux/netfilter_ipv6/ip6_tables.h */
66 #define IP6T_BASE_CTL 64
67 #define IP6T_SO_SET_REPLACE (IP6T_BASE_CTL)
68 #define IP6T_SO_SET_ADD_COUNTERS (IP6T_BASE_CTL + 1)
69 #define IP6T_SO_GET_INFO (IP6T_BASE_CTL)
70 #define IP6T_SO_GET_ENTRIES (IP6T_BASE_CTL + 1)
72 int xt_match_parse(int c, char **argv, int invert, unsigned int *flags,
73 const void *entry, struct xt_entry_match **match)
78 int xt_target_parse(int c, char **argv, int invert, unsigned int *flags,
79 const void *entry, struct xt_entry_target **targetinfo)
84 static void xt_x6_parse(struct xt_option_call *opt) {
88 static void xt_x6_fcheck(struct xt_fcheck_call *call) {
92 static struct xtables_match xt_match = {
100 .size = XT_ALIGN(sizeof(struct xtables_match)),
101 .userspacesize = XT_ALIGN(sizeof(struct xtables_match)),
102 .parse = xt_match_parse,
104 .x6_parse = xt_x6_parse,
105 .x6_fcheck = xt_x6_fcheck,
107 .udata_size = XT_ALIGN(sizeof(struct xtables_match)),
115 static struct xtables_target xt_target = {
119 .real_name = "ACCEPT",
123 .size = XT_ALIGN(sizeof(struct xtables_match)),
124 .userspacesize = XT_ALIGN(sizeof(struct xtables_match)),
125 .parse = xt_target_parse,
127 .x6_parse = xt_x6_parse,
128 .x6_fcheck = xt_x6_fcheck,
130 .udata_size = XT_ALIGN(sizeof(struct xtables_match)),
139 struct xtables_globals *xt_params = NULL;
141 struct xtables_match *xtables_matches = NULL;
142 struct xtables_target *xtables_targets = NULL;
144 static void call_error(const char *src)
150 xt_params->exit_err(PARAMETER_PROBLEM, "longjmp() %s", src);
153 int xtables_init_all(struct xtables_globals *xtp, uint8_t nfproto)
157 if (test_config_type & TEST_CONFIG_INIT_FAIL)
158 call_error("xtables_init_all");
165 struct xtables_match *xtables_find_match(const char *name,
166 enum xtables_tryload tryload,
167 struct xtables_rule_match **matches)
169 DBG("name %s type %d", name, tryload);
171 if (test_config_type & TEST_CONFIG_FIND_MATCH_FAIL)
172 call_error("xtables_find_match");
174 *matches = g_try_new0(struct xtables_rule_match, 1);
175 (*matches)->next = NULL;
176 (*matches)->match = &xt_match;
177 (*matches)->completed = 0;
182 struct xtables_target *xtables_find_target(const char *name,
183 enum xtables_tryload tryload)
185 DBG("name %s type %d", name, tryload);
187 if (test_config_type & TEST_CONFIG_FIND_TARGET_FAIL)
188 call_error("xtables_find_target");
193 uint16_t xtables_parse_protocol(const char *s)
195 DBG("protocol %s", s);
197 if (test_config_type & TEST_CONFIG_PARSE_PROTOCOL_FAIL)
198 call_error("xtables_parse_protocol");
200 if (!g_strcmp0(s, "tcp"))
206 void xtables_option_mfcall(struct xtables_match *m)
210 if (test_config_type & TEST_CONFIG_MFCALL_FAIL)
211 call_error("xtables_option_mfcall");
218 void xtables_option_tfcall(struct xtables_target *t)
222 if (test_config_type & TEST_CONFIG_TFCALL_FAIL)
223 call_error("xtables_option_tfcall");
230 void xtables_option_mpcall(unsigned int c, char **argv, bool invert,
231 struct xtables_match *m, void *fw)
235 if (test_config_type & TEST_CONFIG_MPCALL_FAIL)
236 call_error("xtables_option_mpcall");
243 void xtables_option_tpcall(unsigned int c, char **argv, bool invert,
244 struct xtables_target *t, void *fw)
248 if (test_config_type & TEST_CONFIG_TPCALL_FAIL)
249 call_error("xtables_option_tpcall");
256 int xtables_insmod(const char *modname, const char *modprobe, bool quiet)
258 DBG("mod %s modprobe %s quiet %s", modname, modprobe,
259 quiet ? "true" : "false");
261 if (test_config_type & TEST_CONFIG_INSMOD_FAIL)
262 call_error("xtables_insmod");
267 int xtables_compatible_revision(const char *name, uint8_t revision, int opt)
269 DBG("name %s rev %d opt %d", name, revision, opt);
271 if (test_config_type & TEST_CONFIG_COMPATIBLE_REV_FAIL)
272 call_error("xtables_compatible_revision");
277 struct option *xtables_options_xfrm(struct option *opt1, struct option *opt2,
278 const struct xt_option_entry *entry,
281 if (test_config_type & TEST_CONFIG_OPTIONS_XFRM_FAIL)
282 call_error("xtables_options_xfrm");
287 struct option *xtables_merge_options(struct option *orig_opts,
288 struct option *oldopts, const struct option *newopts,
289 unsigned int *option_offset)
291 if (test_config_type & TEST_CONFIG_MERGE_OPTIONS_FAIL)
292 call_error("xtables_merge_options");
297 /* End of xtables dummies */
301 int global_sockfd = 1000;
303 int socket(int domain, int type, int protocol)
305 DBG("domain %d type %d protocol %d", domain, type, protocol);
307 return global_sockfd;
310 int getsockopt(int sockfd, int level, int optname, void *optval,
313 struct ipt_getinfo *info = NULL;
314 struct ipt_get_entries *entries = NULL;
315 struct ip6t_getinfo *info6 = NULL;
316 struct ip6t_get_entries *entries6 = NULL;
320 g_assert_cmpint(global_sockfd, ==, sockfd);
327 case IPT_SO_GET_ENTRIES:
328 DBG("IPT_SO_GET_ENTRIES");
331 case IPT_SO_GET_INFO:
332 DBG("IPT_SO_GET_INFO");
336 DBG("optname %d", optname);
344 case IP6T_SO_GET_ENTRIES:
345 DBG("IP6T_SO_GET_ENTRIES");
348 case IP6T_SO_GET_INFO:
349 DBG("IP6T_SO_GET_INFO");
353 DBG("optname %d", optname);
366 int setsockopt(int sockfd, int level, int optname, const void *optval,
371 g_assert_cmpint(global_sockfd, ==, sockfd);
377 case IPT_SO_SET_REPLACE:
378 DBG("IPT_SO_SET_REPLACE");
380 case IPT_SO_SET_ADD_COUNTERS:
381 DBG("IPT_SO_SET_ADD_COUNTERS");
384 DBG("optname %d", optname);
393 case IP6T_SO_SET_REPLACE:
394 DBG("IP6T_SO_SET_REPLACE");
396 case IP6T_SO_SET_ADD_COUNTERS:
397 DBG("IP6T_SO_SET_ADD_COUNTERS");
400 DBG("optname %d", optname);
410 /* End of socket dummies */
414 static void iptables_test_basic0()
416 set_test_config(TEST_CONFIG_PASS);
418 __connman_iptables_init();
420 g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
421 g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
422 "-p tcp -m tcp --dport 42 -j ACCEPT"), ==, 0);
424 __connman_iptables_cleanup();
428 * These ok0...ok6 tests test the error handling. The setjmp() position is set
429 * properly for the functions that will trigger it and as a result, depending on
430 * iptables.c, there will be an error or no error at all. Each of these should
431 * return gracefully without calling exit().
434 static void iptables_test_jmp_ok0()
436 set_test_config(TEST_CONFIG_FIND_MATCH_FAIL);
438 __connman_iptables_init();
440 g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
441 g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
442 "-p tcp -m tcp -j ACCEPT"), ==, -EINVAL);
444 __connman_iptables_cleanup();
447 static void iptables_test_jmp_ok1()
449 set_test_config(TEST_CONFIG_FIND_TARGET_FAIL);
451 __connman_iptables_init();
453 g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
454 g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
455 "-p tcp -m tcp -j ACCEPT"), ==, -EINVAL);
457 __connman_iptables_cleanup();
460 static void iptables_test_jmp_ok2()
462 set_test_config(TEST_CONFIG_PARSE_PROTOCOL_FAIL);
464 __connman_iptables_init();
466 g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
467 g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
468 "-p tcp -m tcp --dport 42 -j ACCEPT"), ==,
471 __connman_iptables_cleanup();
474 static void iptables_test_jmp_ok3()
476 set_test_config(TEST_CONFIG_TFCALL_FAIL);
478 __connman_iptables_init();
480 g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
481 g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
482 "-p tcp -m tcp --dport 42 -j ACCEPT"), ==,
485 __connman_iptables_cleanup();
488 static void iptables_test_jmp_ok4()
490 set_test_config(TEST_CONFIG_MFCALL_FAIL);
492 __connman_iptables_init();
494 g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
496 g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
497 "-p tcp -m tcp --dport 42 -j ACCEPT"), ==,
500 __connman_iptables_cleanup();
503 static void iptables_test_jmp_ok5()
505 set_test_config(TEST_CONFIG_TPCALL_FAIL);
507 __connman_iptables_init();
509 g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
511 g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
512 "-p tcp -m tcp --dport 42 -j ACCEPT "
513 "--comment test"), ==, -EINVAL);
515 __connman_iptables_cleanup();
518 static void iptables_test_jmp_ok6()
520 set_test_config(TEST_CONFIG_MPCALL_FAIL);
522 __connman_iptables_init();
524 g_assert(!__connman_iptables_new_chain(AF_INET, "filter", "INPUT"));
526 g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter", "INPUT",
527 "-p tcp -m tcp --dport 42 -j ACCEPT"), ==,
530 __connman_iptables_cleanup();
534 * These exit0...exit2 tests invoke longjmp() via xtables exit_err() without
535 * having env saved with setjmp(). All of these will result calling exit(), thus
536 * forking is required.
539 static void iptables_test_jmp_exit0()
545 * Should work as normal but fork() is needed as exit() is called
546 * when longjmp() is not allowed. At xtables_init_all() exit_err() is
547 * not normally called.
549 set_test_config(TEST_CONFIG_INIT_FAIL);
551 /* Child, run iptables test */
553 __connman_iptables_init();
556 * Address family must be different from previous use because
557 * otherwise xtables_init_all() is not called.
559 g_assert(!__connman_iptables_new_chain(AF_INET6, "filter",
562 __connman_iptables_cleanup();
565 cpid = wait(&cstatus); /* Wait for child */
568 DBG("parent %d child %d exit %d", getpid(), cpid, WEXITSTATUS(cstatus));
570 g_assert(WIFEXITED(cstatus));
571 g_assert_cmpint(WEXITSTATUS(cstatus), ==, PARAMETER_PROBLEM);
574 static void iptables_test_jmp_exit1()
580 * Should work as normal but fork() is needed as exit() is called
581 * when longjmp() is not allowed. At xtables_insmod() exit_err() is not
584 set_test_config(TEST_CONFIG_INSMOD_FAIL);
587 __connman_iptables_init();
589 g_assert(!__connman_iptables_new_chain(AF_INET, "filter",
592 __connman_iptables_cleanup();
595 cpid = wait(&cstatus);
598 DBG("parent %d child %d exit %d", getpid(), cpid, WEXITSTATUS(cstatus));
600 g_assert(WIFEXITED(cstatus));
601 g_assert_cmpint(WEXITSTATUS(cstatus), ==, PARAMETER_PROBLEM);
604 static void iptables_test_jmp_exit2()
609 set_test_config(TEST_CONFIG_OPTIONS_XFRM_FAIL|
610 TEST_CONFIG_MERGE_OPTIONS_FAIL|
611 TEST_CONFIG_COMPATIBLE_REV_FAIL);
614 __connman_iptables_init();
616 g_assert(!__connman_iptables_new_chain(AF_INET, "filter",
618 g_assert_cmpint(__connman_iptables_insert(AF_INET, "filter",
619 "INPUT", "-p tcp -m tcp --dport 42 "
620 "-j ACCEPT --comment test"), ==, 0);
622 __connman_iptables_cleanup();
625 cpid = wait(&cstatus);
628 DBG("parent %d child %d exit %d", getpid(), cpid, WEXITSTATUS(cstatus));
630 g_assert(WIFEXITED(cstatus));
631 g_assert_cmpint(WEXITSTATUS(cstatus), ==, PARAMETER_PROBLEM);
634 static gchar *option_debug = NULL;
636 static bool parse_debug(const char *key, const char *value,
637 gpointer user_data, GError **error)
640 option_debug = g_strdup(value);
642 option_debug = g_strdup("*");
647 static GOptionEntry options[] = {
648 { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG,
649 G_OPTION_ARG_CALLBACK, parse_debug,
650 "Specify debug options to enable", "DEBUG" },
654 int main (int argc, char *argv[])
656 GOptionContext *context;
657 GError *error = NULL;
659 g_test_init(&argc, &argv, NULL);
661 context = g_option_context_new(NULL);
662 g_option_context_add_main_entries(context, options, NULL);
664 if (!g_option_context_parse(context, &argc, &argv, &error)) {
666 g_printerr("%s\n", error->message);
669 g_printerr("An unknown error occurred\n");
673 g_option_context_free(context);
675 __connman_log_init(argv[0], option_debug, false, false,
676 "Unit Tests Connection Manager", VERSION);
678 g_test_add_func("/iptables/test_basic0", iptables_test_basic0);
679 g_test_add_func("/iptables/test_jmp_ok0", iptables_test_jmp_ok0);
680 g_test_add_func("/iptables/test_jmp_ok1", iptables_test_jmp_ok1);
681 g_test_add_func("/iptables/test_jmp_ok2", iptables_test_jmp_ok2);
682 g_test_add_func("/iptables/test_jmp_ok3", iptables_test_jmp_ok3);
683 g_test_add_func("/iptables/test_jmp_ok4", iptables_test_jmp_ok4);
684 g_test_add_func("/iptables/test_jmp_ok5", iptables_test_jmp_ok5);
685 g_test_add_func("/iptables/test_jmp_ok6", iptables_test_jmp_ok6);
686 g_test_add_func("/iptables/test_jmp_exit0", iptables_test_jmp_exit0);
687 g_test_add_func("/iptables/test_jmp_exit1", iptables_test_jmp_exit1);
688 g_test_add_func("/iptables/test_jmp_exit2", iptables_test_jmp_exit2);
697 * indent-tabs-mode: t