+ uint16_t proto;
+};
+
+static int prepare_getopt_args(const char *str, struct parse_context *ctx)
+{
+ char **tokens;
+ int i;
+
+ tokens = g_strsplit_set(str, " ", -1);
+
+ i = g_strv_length(tokens);
+
+ /* Add space for the argv[0] value */
+ ctx->argc = i + 1;
+
+ /* Don't forget the last NULL entry */
+ ctx->argv = g_try_malloc0((ctx->argc + 1) * sizeof(char *));
+ if (!ctx->argv) {
+ g_strfreev(tokens);
+ return -ENOMEM;
+ }
+
+ /*
+ * getopt_long() jumps over the first token; we need to add some
+ * random argv[0] entry.
+ */
+ ctx->argv[0] = g_strdup("argh");
+ for (i = 1; i < ctx->argc; i++)
+ ctx->argv[i] = tokens[i - 1];
+
+ g_free(tokens);
+
+ return 0;
+}
+
+static int parse_xt_modules(int c, bool invert,
+ struct parse_context *ctx)
+{
+ struct xtables_match *m;
+ struct xtables_rule_match *rm;
+ struct ipt_entry fw;
+ struct ip6t_entry fw6;
+ int err;
+
+ switch (ctx->type) {
+ case AF_INET:
+ memset(&fw, 0, sizeof(fw));
+
+ /* The SNAT parser wants to know the protocol. */
+ if (ctx->proto == 0)
+ ctx->proto = IPPROTO_IP;
+
+ fw.ip.proto = ctx->proto;
+ break;
+ case AF_INET6:
+ memset(&fw6, 0, sizeof(fw6));
+
+ if (ctx->proto == 0)
+ ctx->proto = IPPROTO_IPV6;
+
+ fw6.ipv6.proto = ctx->proto;
+
+ /* Flags must be set for IPv6 if protocol is set. */
+ fw6.ipv6.flags |= IP6T_F_PROTO;
+
+ break;
+ default:
+ return 0;
+ }
+
+ for (rm = ctx->xt_rm; rm; rm = rm->next) {
+ if (rm->completed != 0)
+ continue;
+
+ m = rm->match;
+
+ if (!m->x6_parse && !m->parse)
+ continue;
+
+ if (c < (int) m->option_offset ||
+ c >= (int) m->option_offset
+ + XT_OPTION_OFFSET_SCALE)
+ continue;
+
+ 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_mpcall(c, ctx->argv, invert, m, &fw);
+ break;
+ case AF_INET6:
+ xtables_option_mpcall(c, ctx->argv, invert, m, &fw6);
+ break;
+ }
+
+ disable_jmp();
+ }
+
+ if (!ctx->xt_t)
+ return 0;
+
+ 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.
+ */