+ if (!ctx->xt_t->x6_parse && !ctx->xt_t->parse)
+ return 0;
+
+ if (c < (int) ctx->xt_t->option_offset ||
+ c >= (int) ctx->xt_t->option_offset
+ + XT_OPTION_OFFSET_SCALE)
+ return 0;
+
+ enable_jmp();
+
+ if ((err = setjmp(env_state)) != 0) {
+ DBG("setjmp() called by longjmp() with value %d", err);
+ disable_jmp();
+ return -EINVAL;
+ }
+
+ switch (ctx->type) {
+ case AF_INET:
+ xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, &fw);
+ break;
+ case AF_INET6:
+ xtables_option_tpcall(c, ctx->argv, invert, ctx->xt_t, &fw6);
+ break;
+ }
+
+ disable_jmp();
+
+ return 0;
+}
+
+static int final_check_xt_modules(struct parse_context *ctx)
+{
+ struct xtables_rule_match *rm;
+ int err;
+
+ for (rm = ctx->xt_rm; rm; rm = rm->next) {
+ enable_jmp();
+
+ if ((err = setjmp(env_state)) != 0) {
+ DBG("setjmp() called by longjmp() with value %d", err);
+ disable_jmp();
+ return -EINVAL;
+ }
+
+ xtables_option_mfcall(rm->match);
+
+ disable_jmp();
+ }
+
+ enable_jmp();
+
+ if ((err = setjmp(env_state)) != 0) {
+ DBG("setjmp() called by longjmp() with value %d", err);
+ disable_jmp();
+ return -EINVAL;
+ }
+
+ if (ctx->xt_t)
+ xtables_option_tfcall(ctx->xt_t);
+
+ disable_jmp();
+
+ return 0;
+}
+
+static int parse_rule_spec(struct connman_iptables *table,
+ struct parse_context *ctx)
+{
+ /*
+ * How the parser works:
+ *
+ * - If getopt finds 's', 'd', 'i', 'o'.
+ * just extract the information.
+ * - if '!' is found, set the invert flag to true and
+ * removes the '!' from the optarg string and jumps
+ * back to getopt to reparse the current optarg string.
+ * After reparsing the invert flag is reset to false.
+ * - If 'm' or 'j' is found then call either
+ * prepare_matches() or prepare_target(). Those function
+ * will modify (extend) the longopts for getopt_long.
+ * That means getopt will change its matching context according
+ * the loaded target.
+ *
+ * Here an example with iptables-test
+ *
+ * argv[0] = ./tools/iptables-test
+ * argv[1] = -t
+ * argv[2] = filter
+ * argv[3] = -A
+ * argv[4] = INPUT
+ * argv[5] = -m
+ * argv[6] = mark
+ * argv[7] = --mark
+ * argv[8] = 999
+ * argv[9] = -j
+ * argv[10] = LOG
+ *
+ * getopt found 'm' then the optarg is "mark" and optind 7
+ * The longopts array containts before hitting the `case 'm'`
+ *
+ * val A has_arg 1 name append
+ * val C has_arg 1 name compare
+ * val D has_arg 1 name delete
+ * val F has_arg 1 name flush-chain
+ * val I has_arg 1 name insert
+ * val L has_arg 2 name list
+ * val N has_arg 1 name new-chain
+ * val P has_arg 1 name policy
+ * val X has_arg 1 name delete-chain
+ * val d has_arg 1 name destination
+ * val i has_arg 1 name in-interface
+ * val j has_arg 1 name jump
+ * val m has_arg 1 name match
+ * val o has_arg 1 name out-interface
+ * val s has_arg 1 name source
+ * val t has_arg 1 name table
+ *
+ * After executing the `case 'm'` block longopts is
+ *
+ * val A has_arg 1 name append
+ * val C has_arg 1 name compare
+ * val D has_arg 1 name delete
+ * val F has_arg 1 name flush-chain
+ * val I has_arg 1 name insert
+ * val L has_arg 2 name list
+ * val N has_arg 1 name new-chain
+ * val P has_arg 1 name policy
+ * val X has_arg 1 name delete-chain
+ * val d has_arg 1 name destination
+ * val i has_arg 1 name in-interface
+ * val j has_arg 1 name jump
+ * val m has_arg 1 name match
+ * val o has_arg 1 name out-interface
+ * val s has_arg 1 name source
+ * val t has_arg 1 name table
+ * val has_arg 1 name mark
+ *
+ * So the 'mark' matcher has added the 'mark' options
+ * and getopt will then return c '256' optarg "999" optind 9
+ * And we will hit the 'default' statement which then
+ * will call the matchers parser (xt_m->parser() or
+ * xtables_option_mpcall() depending on which version
+ * of libxtables is found.
+ */
+ struct xtables_match *xt_m;
+ bool invert = false;
+ int len, c, err;
+
+ if (ctx->type != table->type) {
+ DBG("ctx->type %d does not match table->type %d", ctx->type,
+ table->type);
+ return -EINVAL;
+ }
+
+ switch (ctx->type) {
+ case AF_INET:
+ ctx->ip = g_try_new0(struct ipt_ip, 1);
+ if (!ctx->ip)
+ return -ENOMEM;
+
+ break;
+ case AF_INET6:
+ ctx->ipv6 = g_try_new0(struct ip6t_ip6, 1);
+ if (!ctx->ipv6)
+ return -ENOMEM;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * Tell getopt_long not to generate error messages for unknown
+ * options and also reset optind back to 0.
+ */
+ opterr = 0;
+ optind = 0;
+
+ while ((c = getopt_long(ctx->argc, ctx->argv,
+ "-:d:i:o:s:m:j:p:",
+ ctx->type == AF_INET ?
+ iptables_globals.opts :
+ ip6tables_globals.opts,
+ NULL)) != -1) {
+ switch (c) {
+ case 's':
+ if (ctx->type == AF_INET) {
+ /* Source specification */
+ if (!parse_ip_and_mask(optarg,
+ &ctx->ip->src,
+ &ctx->ip->smsk))
+ break;
+
+ if (invert)
+ ctx->ip->invflags |= IPT_INV_SRCIP;
+ }
+
+ if (ctx->type == AF_INET6) {
+ if (!parse_ipv6_and_mask(optarg,
+ &ctx->ipv6->src,
+ &ctx->ipv6->smsk))
+ break;
+
+ if (invert)
+ ctx->ipv6->invflags |= IP6T_INV_SRCIP;
+ }