Merge branch 'opts' of git://dev.medozas.de/iptables
diff --git a/configure.ac b/configure.ac
index eb447e0..38b7b79 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
 AC_INIT([iptables], [1.4.10])
 
 # See libtool.info "Libtool's versioning system"
-libxtables_vcurrent=5
+libxtables_vcurrent=6
 libxtables_vage=0
 
 AC_CONFIG_HEADERS([config.h])
diff --git a/extensions/libxt_TEE.c b/extensions/libxt_TEE.c
index 00a4de6..fd9a1b2 100644
--- a/extensions/libxt_TEE.c
+++ b/extensions/libxt_TEE.c
@@ -25,15 +25,19 @@
 #include <linux/netfilter/xt_TEE.h>
 
 enum {
-	FLAG_GATEWAY = 1 << 0,
-	FLAG_OIF     = 1 << 1,
+	O_GATEWAY = 0,
+	O_OIF,
 };
 
-static const struct option tee_tg_opts[] = {
-	{.name = "gateway", .has_arg = true, .val = 'g'},
-	{.name = "oif",     .has_arg = true, .val = 'o'},
-	{NULL},
+#define s struct xt_tee_tginfo
+static const struct xt_option_entry tee_tg_opts[] = {
+	{.name = "gateway", .id = O_GATEWAY, .type = XTTYPE_ONEHOST,
+	 .flags = XTOPT_MAND | XTOPT_PUT, XTOPT_POINTER(s, gw)},
+	{.name = "oif", .id = O_OIF, .type = XTTYPE_STRING,
+	 .flags = XTOPT_PUT, XTOPT_POINTER(s, oif)},
+	XTOPT_TABLEEND,
 };
+#undef s
 
 static void tee_tg_help(void)
 {
@@ -44,83 +48,6 @@
 "\n");
 }
 
-static int tee_tg_parse(int c, char **argv, int invert, unsigned int *flags,
-                        const void *entry, struct xt_entry_target **target)
-{
-	struct xt_tee_tginfo *info = (void *)(*target)->data;
-	const struct in_addr *ia;
-
-	switch (c) {
-	case 'g':
-		if (*flags & FLAG_GATEWAY)
-			xtables_error(PARAMETER_PROBLEM,
-			           "Cannot specify --gateway more than once");
-
-		ia = xtables_numeric_to_ipaddr(optarg);
-		if (ia == NULL)
-			xtables_error(PARAMETER_PROBLEM,
-			           "Invalid IP address %s", optarg);
-
-		memcpy(&info->gw, ia, sizeof(*ia));
-		*flags |= FLAG_GATEWAY;
-		return true;
-	case 'o':
-		if (*flags & FLAG_OIF)
-			xtables_error(PARAMETER_PROBLEM,
-				"Cannot specify --oif more than once");
-		if (strlen(optarg) >= sizeof(info->oif))
-			xtables_error(PARAMETER_PROBLEM,
-				"oif name too long");
-		strcpy(info->oif, optarg);
-		*flags |= FLAG_OIF;
-		return true;
-	}
-
-	return false;
-}
-
-static int tee_tg6_parse(int c, char **argv, int invert, unsigned int *flags,
-                         const void *entry, struct xt_entry_target **target)
-{
-	struct xt_tee_tginfo *info = (void *)(*target)->data;
-	const struct in6_addr *ia;
-
-	switch (c) {
-	case 'g':
-		if (*flags & FLAG_GATEWAY)
-			xtables_error(PARAMETER_PROBLEM,
-			           "Cannot specify --gateway more than once");
-
-		ia = xtables_numeric_to_ip6addr(optarg);
-		if (ia == NULL)
-			xtables_error(PARAMETER_PROBLEM,
-			           "Invalid IP address %s", optarg);
-
-		memcpy(&info->gw, ia, sizeof(*ia));
-		*flags |= FLAG_GATEWAY;
-		return true;
-	case 'o':
-		if (*flags & FLAG_OIF)
-			xtables_error(PARAMETER_PROBLEM,
-				"Cannot specify --oif more than once");
-		if (strlen(optarg) >= sizeof(info->oif))
-			xtables_error(PARAMETER_PROBLEM,
-				"oif name too long");
-		strcpy(info->oif, optarg);
-		*flags |= FLAG_OIF;
-		return true;
-	}
-
-	return false;
-}
-
-static void tee_tg_check(unsigned int flags)
-{
-	if (flags == 0)
-		xtables_error(PARAMETER_PROBLEM, "TEE target: "
-		           "--gateway parameter required");
-}
-
 static void tee_tg_print(const void *ip, const struct xt_entry_target *target,
                          int numeric)
 {
@@ -173,11 +100,10 @@
 	.size          = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
 	.userspacesize = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
 	.help          = tee_tg_help,
-	.parse         = tee_tg_parse,
-	.final_check   = tee_tg_check,
 	.print         = tee_tg_print,
 	.save          = tee_tg_save,
-	.extra_opts    = tee_tg_opts,
+	.x6_parse      = xtables_option_parse,
+	.x6_options    = tee_tg_opts,
 };
 
 static struct xtables_target tee_tg6_reg = {
@@ -188,11 +114,10 @@
 	.size          = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
 	.userspacesize = XT_ALIGN(sizeof(struct xt_tee_tginfo)),
 	.help          = tee_tg_help,
-	.parse         = tee_tg6_parse,
-	.final_check   = tee_tg_check,
 	.print         = tee_tg6_print,
 	.save          = tee_tg6_save,
-	.extra_opts    = tee_tg_opts,
+	.x6_parse      = xtables_option_parse,
+	.x6_options    = tee_tg_opts,
 };
 
 void _init(void)
diff --git a/extensions/libxt_TOS.c b/extensions/libxt_TOS.c
index 58ff2fc..cef5876 100644
--- a/extensions/libxt_TOS.c
+++ b/extensions/libxt_TOS.c
@@ -2,7 +2,7 @@
  * Shared library add-on to iptables to add TOS target support
  *
  * Copyright © CC Computer Consultants GmbH, 2007
- * Contact: Jan Engelhardt <jengelh@computergmbh.de>
+ * Contact: Jan Engelhardt <jengelh@medozas.de>
  */
 #include <getopt.h>
 #include <stdbool.h>
@@ -20,20 +20,33 @@
 };
 
 enum {
-	FLAG_TOS = 1 << 0,
+	O_SET_TOS = 0,
+	O_AND_TOS,
+	O_OR_TOS,
+	O_XOR_TOS,
+	F_SET_TOS = 1 << O_SET_TOS,
+	F_AND_TOS = 1 << O_AND_TOS,
+	F_OR_TOS  = 1 << O_OR_TOS,
+	F_XOR_TOS = 1 << O_XOR_TOS,
+	F_ANY     = F_SET_TOS | F_AND_TOS | F_OR_TOS | F_XOR_TOS,
 };
 
-static const struct option tos_tg_opts_v0[] = {
-	{.name = "set-tos", .has_arg = true, .val = '='},
-	XT_GETOPT_TABLEEND,
+static const struct xt_option_entry tos_tg_opts_v0[] = {
+	{.name = "set-tos", .id = O_SET_TOS, .type = XTTYPE_TOSMASK,
+	 .excl = F_ANY, .max = 0xFF},
+	XTOPT_TABLEEND,
 };
 
-static const struct option tos_tg_opts[] = {
-	{.name = "set-tos", .has_arg = true, .val = '='},
-	{.name = "and-tos", .has_arg = true, .val = '&'},
-	{.name = "or-tos",  .has_arg = true, .val = '|'},
-	{.name = "xor-tos", .has_arg = true, .val = '^'},
-	XT_GETOPT_TABLEEND,
+static const struct xt_option_entry tos_tg_opts[] = {
+	{.name = "set-tos", .id = O_SET_TOS, .type = XTTYPE_TOSMASK,
+	 .excl = F_ANY, .max = 0x3F},
+	{.name = "and-tos", .id = O_AND_TOS, .type = XTTYPE_UINT8,
+	 .excl = F_ANY},
+	{.name = "or-tos", .id = O_OR_TOS, .type = XTTYPE_UINT8,
+	 .excl = F_ANY},
+	{.name = "xor-tos", .id = O_XOR_TOS, .type = XTTYPE_UINT8,
+	 .excl = F_ANY},
+	XTOPT_TABLEEND,
 };
 
 static void tos_tg_help_v0(void)
@@ -78,84 +91,48 @@
 );
 }
 
-static int tos_tg_parse_v0(int c, char **argv, int invert, unsigned int *flags,
-                           const void *entry, struct xt_entry_target **target)
+static void tos_tg_parse_v0(struct xt_option_call *cb)
 {
-	struct ipt_tos_target_info *info = (void *)(*target)->data;
-	struct tos_value_mask tvm;
+	struct ipt_tos_target_info *info = cb->data;
 
-	switch (c) {
-	case '=':
-		xtables_param_act(XTF_ONLY_ONCE, "TOS", "--set-tos", *flags & FLAG_TOS);
-		xtables_param_act(XTF_NO_INVERT, "TOS", "--set-tos", invert);
-		if (!tos_parse_symbolic(optarg, &tvm, 0xFF))
-			xtables_param_act(XTF_BAD_VALUE, "TOS", "--set-tos", optarg);
-		if (tvm.mask != 0xFF)
-			xtables_error(PARAMETER_PROBLEM, "tos match: Your kernel "
-			           "is too old to support anything besides "
-				   "/0xFF as a mask.");
-		info->tos = tvm.value;
-		*flags |= FLAG_TOS;
-		return true;
-	}
-
-	return false;
+	xtables_option_parse(cb);
+	if (cb->val.tos_mask != 0xFF)
+		xtables_error(PARAMETER_PROBLEM, "tos match: Your kernel "
+		           "is too old to support anything besides "
+			   "/0xFF as a mask.");
+	info->tos = cb->val.tos_value;
 }
 
-static int tos_tg_parse(int c, char **argv, int invert, unsigned int *flags,
-                         const void *entry, struct xt_entry_target **target)
+static void tos_tg_parse(struct xt_option_call *cb)
 {
-	struct xt_tos_target_info *info = (void *)(*target)->data;
-	struct tos_value_mask tvm;
-	unsigned int bits;
+	struct xt_tos_target_info *info = cb->data;
 
-	switch (c) {
-	case '=': /* --set-tos */
-		xtables_param_act(XTF_ONLY_ONCE, "TOS", "--set-tos", *flags & FLAG_TOS);
-		xtables_param_act(XTF_NO_INVERT, "TOS", "--set-tos", invert);
-		if (!tos_parse_symbolic(optarg, &tvm, 0x3F))
-			xtables_param_act(XTF_BAD_VALUE, "TOS", "--set-tos", optarg);
-		info->tos_value = tvm.value;
-		info->tos_mask  = tvm.mask;
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SET_TOS:
+		info->tos_value = cb->val.tos_value;
+		info->tos_mask  = cb->val.tos_mask;
 		break;
-
-	case '&': /* --and-tos */
-		xtables_param_act(XTF_ONLY_ONCE, "TOS", "--and-tos", *flags & FLAG_TOS);
-		xtables_param_act(XTF_NO_INVERT, "TOS", "--and-tos", invert);
-		if (!xtables_strtoui(optarg, NULL, &bits, 0, UINT8_MAX))
-			xtables_param_act(XTF_BAD_VALUE, "TOS", "--and-tos", optarg);
+	case O_AND_TOS:
 		info->tos_value = 0;
-		info->tos_mask  = ~bits;
+		info->tos_mask  = ~cb->val.u8;
 		break;
-
-	case '|': /* --or-tos */
-		xtables_param_act(XTF_ONLY_ONCE, "TOS", "--or-tos", *flags & FLAG_TOS);
-		xtables_param_act(XTF_NO_INVERT, "TOS", "--or-tos", invert);
-		if (!xtables_strtoui(optarg, NULL, &bits, 0, UINT8_MAX))
-			xtables_param_act(XTF_BAD_VALUE, "TOS", "--or-tos", optarg);
-		info->tos_value = bits;
-		info->tos_mask  = bits;
+	case O_OR_TOS:
+		info->tos_value = cb->val.u8;
+		info->tos_mask  = cb->val.u8;
 		break;
-
-	case '^': /* --xor-tos */
-		xtables_param_act(XTF_ONLY_ONCE, "TOS", "--xor-tos", *flags & FLAG_TOS);
-		xtables_param_act(XTF_NO_INVERT, "TOS", "--xor-tos", invert);
-		if (!xtables_strtoui(optarg, NULL, &bits, 0, UINT8_MAX))
-			xtables_param_act(XTF_BAD_VALUE, "TOS", "--xor-tos", optarg);
-		info->tos_value = bits;
+	case O_XOR_TOS:
+		info->tos_value = cb->val.u8;
 		info->tos_mask  = 0;
 		break;
 	}
-
-	*flags |= FLAG_TOS;
-	return true;
 }
 
-static void tos_tg_check(unsigned int flags)
+static void tos_tg_check(struct xt_fcheck_call *cb)
 {
-	if (flags == 0)
+	if (!(cb->xflags & F_ANY))
 		xtables_error(PARAMETER_PROBLEM,
-		           "TOS: The --set-tos parameter is required");
+		           "TOS: An action is required");
 }
 
 static void tos_tg_print_v0(const void *ip,
@@ -215,11 +192,11 @@
 		.size          = XT_ALIGN(sizeof(struct xt_tos_target_info)),
 		.userspacesize = XT_ALIGN(sizeof(struct xt_tos_target_info)),
 		.help          = tos_tg_help_v0,
-		.parse         = tos_tg_parse_v0,
-		.final_check   = tos_tg_check,
 		.print         = tos_tg_print_v0,
 		.save          = tos_tg_save_v0,
-		.extra_opts    = tos_tg_opts_v0,
+		.x6_parse      = tos_tg_parse_v0,
+		.x6_fcheck     = tos_tg_check,
+		.x6_options    = tos_tg_opts_v0,
 	},
 	{
 		.version       = XTABLES_VERSION,
@@ -229,11 +206,11 @@
 		.size          = XT_ALIGN(sizeof(struct xt_tos_target_info)),
 		.userspacesize = XT_ALIGN(sizeof(struct xt_tos_target_info)),
 		.help          = tos_tg_help,
-		.parse         = tos_tg_parse,
-		.final_check   = tos_tg_check,
 		.print         = tos_tg_print,
 		.save          = tos_tg_save,
-		.extra_opts    = tos_tg_opts,
+		.x6_parse      = tos_tg_parse,
+		.x6_fcheck     = tos_tg_check,
+		.x6_options    = tos_tg_opts,
 	},
 };
 
diff --git a/extensions/libxt_dccp.c b/extensions/libxt_dccp.c
index ee8e009..5aff262 100644
--- a/extensions/libxt_dccp.c
+++ b/extensions/libxt_dccp.c
@@ -5,15 +5,12 @@
  * This program is distributed under the terms of GNU GPL v2, 1991
  *
  */
-#include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include <getopt.h>
 #include <netdb.h>
-#include <ctype.h>
-
-#include <netinet/in.h>
+#include <arpa/inet.h>
 #include <xtables.h>
 #include <linux/dccp.h>
 #include <linux/netfilter/x_tables.h>
@@ -26,6 +23,13 @@
 #define DEBUGP(format, fist...) 
 #endif
 
+enum {
+	O_SOURCE_PORT = 0,
+	O_DEST_PORT,
+	O_DCCP_TYPES,
+	O_DCCP_OPTION,
+};
+
 static void dccp_help(void)
 {
 	printf(
@@ -36,41 +40,23 @@
 " --dport ...\n");
 }
 
-static const struct option dccp_opts[] = {
-	{.name = "source-port",      .has_arg = true, .val = '1'},
-	{.name = "sport",            .has_arg = true, .val = '1'},
-	{.name = "destination-port", .has_arg = true, .val = '2'},
-	{.name = "dport",            .has_arg = true, .val = '2'},
-	{.name = "dccp-types",       .has_arg = true, .val = '3'},
-	{.name = "dccp-option",      .has_arg = true, .val = '4'},
-	XT_GETOPT_TABLEEND,
+#define s struct xt_dccp_info
+static const struct xt_option_entry dccp_opts[] = {
+	{.name = "source-port", .id = O_SOURCE_PORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, spts)},
+	{.name = "sport", .id = O_SOURCE_PORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, spts)},
+	{.name = "destination-port", .id = O_DEST_PORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, dpts)},
+	{.name = "dport", .id = O_DEST_PORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, dpts)},
+	{.name = "dccp-types", .id = O_DCCP_TYPES, .type = XTTYPE_STRING},
+	{.name = "dccp-option", .id = O_DCCP_OPTION, .type = XTTYPE_UINT8,
+	 .min = 1, .max = UINT8_MAX, .flags = XTOPT_PUT,
+	 XTOPT_POINTER(s, option)},
+	XTOPT_TABLEEND,
 };
-
-static void
-parse_dccp_ports(const char *portstring, 
-		 uint16_t *ports)
-{
-	char *buffer;
-	char *cp;
-
-	buffer = strdup(portstring);
-	DEBUGP("%s\n", portstring);
-	if ((cp = strchr(buffer, ':')) == NULL) {
-		ports[0] = ports[1] = xtables_parse_port(buffer, "dccp");
-	}
-	else {
-		*cp = '\0';
-		cp++;
-
-		ports[0] = buffer[0] ? xtables_parse_port(buffer, "dccp") : 0;
-		ports[1] = cp[0] ? xtables_parse_port(cp, "dccp") : 0xFFFF;
-
-		if (ports[0] > ports[1])
-			xtables_error(PARAMETER_PROBLEM,
-				   "invalid portrange (min > max)");
-	}
-	free(buffer);
-}
+#undef s
 
 static const char *const dccp_pkt_types[] = {
 	[DCCP_PKT_REQUEST] 	= "REQUEST",
@@ -110,74 +96,34 @@
 	return typemask;
 }
 
-static uint8_t parse_dccp_option(char *optstring)
+static void dccp_parse(struct xt_option_call *cb)
 {
-	unsigned int ret;
+	struct xt_dccp_info *einfo = cb->data;
 
-	if (!xtables_strtoui(optstring, NULL, &ret, 1, UINT8_MAX))
-		xtables_error(PARAMETER_PROBLEM, "Bad DCCP option \"%s\"",
-			   optstring);
-
-	return ret;
-}
-
-static int
-dccp_parse(int c, char **argv, int invert, unsigned int *flags,
-           const void *entry, struct xt_entry_match **match)
-{
-	struct xt_dccp_info *einfo
-		= (struct xt_dccp_info *)(*match)->data;
-
-	switch (c) {
-	case '1':
-		if (*flags & XT_DCCP_SRC_PORTS)
-			xtables_error(PARAMETER_PROBLEM,
-			           "Only one `--source-port' allowed");
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SOURCE_PORT:
 		einfo->flags |= XT_DCCP_SRC_PORTS;
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
-		parse_dccp_ports(optarg, einfo->spts);
-		if (invert)
+		if (cb->invert)
 			einfo->invflags |= XT_DCCP_SRC_PORTS;
-		*flags |= XT_DCCP_SRC_PORTS;
 		break;
-
-	case '2':
-		if (*flags & XT_DCCP_DEST_PORTS)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Only one `--destination-port' allowed");
+	case O_DEST_PORT:
 		einfo->flags |= XT_DCCP_DEST_PORTS;
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
-		parse_dccp_ports(optarg, einfo->dpts);
-		if (invert)
+		if (cb->invert)
 			einfo->invflags |= XT_DCCP_DEST_PORTS;
-		*flags |= XT_DCCP_DEST_PORTS;
 		break;
-
-	case '3':
-		if (*flags & XT_DCCP_TYPE)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Only one `--dccp-types' allowed");
+	case O_DCCP_TYPES:
 		einfo->flags |= XT_DCCP_TYPE;
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
-		einfo->typemask = parse_dccp_types(optarg);
-		if (invert)
+		einfo->typemask = parse_dccp_types(cb->arg);
+		if (cb->invert)
 			einfo->invflags |= XT_DCCP_TYPE;
-		*flags |= XT_DCCP_TYPE;
 		break;
-
-	case '4':
-		if (*flags & XT_DCCP_OPTION)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Only one `--dccp-option' allowed");
+	case O_DCCP_OPTION:
 		einfo->flags |= XT_DCCP_OPTION;
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
-		einfo->option = parse_dccp_option(optarg);
-		if (invert)
+		if (cb->invert)
 			einfo->invflags |= XT_DCCP_OPTION;
-		*flags |= XT_DCCP_OPTION;
 		break;
 	}
-	return 1;
 }
 
 static char *
@@ -333,10 +279,10 @@
 	.size		= XT_ALIGN(sizeof(struct xt_dccp_info)),
 	.userspacesize	= XT_ALIGN(sizeof(struct xt_dccp_info)),
 	.help		= dccp_help,
-	.parse		= dccp_parse,
 	.print		= dccp_print,
 	.save		= dccp_save,
-	.extra_opts	= dccp_opts,
+	.x6_parse	= dccp_parse,
+	.x6_options	= dccp_opts,
 };
 
 void _init(void)
diff --git a/extensions/libxt_tos.c b/extensions/libxt_tos.c
index 8b83e18..7646a4f 100644
--- a/extensions/libxt_tos.c
+++ b/extensions/libxt_tos.c
@@ -21,12 +21,19 @@
 };
 
 enum {
-	FLAG_TOS = 1 << 0,
+	O_TOS = 1 << 0,
 };
 
-static const struct option tos_mt_opts[] = {
-	{.name = "tos", .has_arg = true, .val = 't'},
-	XT_GETOPT_TABLEEND,
+static const struct xt_option_entry tos_mt_opts_v0[] = {
+	{.name = "tos", .id = O_TOS, .type = XTTYPE_TOSMASK,
+	 .flags = XTOPT_MAND, .max = 0xFF},
+	XTOPT_TABLEEND,
+};
+
+static const struct xt_option_entry tos_mt_opts[] = {
+	{.name = "tos", .id = O_TOS, .type = XTTYPE_TOSMASK,
+	 .flags = XTOPT_MAND, .max = 0x3F},
+	XTOPT_TABLEEND,
 };
 
 static void tos_mt_help(void)
@@ -46,56 +53,29 @@
 	printf("\n");
 }
 
-static int tos_mt_parse_v0(int c, char **argv, int invert, unsigned int *flags,
-                           const void *entry, struct xt_entry_match **match)
+static void tos_mt_parse_v0(struct xt_option_call *cb)
 {
-	struct ipt_tos_info *info = (void *)(*match)->data;
-	struct tos_value_mask tvm;
+	struct ipt_tos_info *info = cb->data;
 
-	switch (c) {
-	case 't':
-		xtables_param_act(XTF_ONLY_ONCE, "tos", "--tos", *flags & FLAG_TOS);
-		if (!tos_parse_symbolic(optarg, &tvm, 0xFF))
-			xtables_param_act(XTF_BAD_VALUE, "tos", "--tos", optarg);
-		if (tvm.mask != 0xFF)
-			xtables_error(PARAMETER_PROBLEM, "tos: Your kernel is "
-			           "too old to support anything besides /0xFF "
-				   "as a mask.");
-		info->tos = tvm.value;
-		if (invert)
-			info->invert = true;
-		*flags |= FLAG_TOS;
-		return true;
-	}
-	return false;
+	xtables_option_parse(cb);
+	if (cb->val.tos_mask != 0xFF)
+		xtables_error(PARAMETER_PROBLEM, "tos: Your kernel is "
+		           "too old to support anything besides /0xFF "
+			   "as a mask.");
+	info->tos = cb->val.tos_value;
+	if (cb->invert)
+		info->invert = true;
 }
 
-static int tos_mt_parse(int c, char **argv, int invert, unsigned int *flags,
-                        const void *entry, struct xt_entry_match **match)
+static void tos_mt_parse(struct xt_option_call *cb)
 {
-	struct xt_tos_match_info *info = (void *)(*match)->data;
-	struct tos_value_mask tvm = {.mask = 0xFF};
+	struct xt_tos_match_info *info = cb->data;
 
-	switch (c) {
-	case 't':
-		xtables_param_act(XTF_ONLY_ONCE, "tos", "--tos", *flags & FLAG_TOS);
-		if (!tos_parse_symbolic(optarg, &tvm, 0x3F))
-			xtables_param_act(XTF_BAD_VALUE, "tos", "--tos", optarg);
-		info->tos_value = tvm.value;
-		info->tos_mask  = tvm.mask;
-		if (invert)
-			info->invert = true;
-		*flags |= FLAG_TOS;
-		return true;
-	}
-	return false;
-}
-
-static void tos_mt_check(unsigned int flags)
-{
-	if (flags == 0)
-		xtables_error(PARAMETER_PROBLEM,
-		           "tos: --tos parameter required");
+	xtables_option_parse(cb);
+	info->tos_value = cb->val.tos_value;
+	info->tos_mask  = cb->val.tos_mask;
+	if (cb->invert)
+		info->invert = true;
 }
 
 static void tos_mt_print_v0(const void *ip, const struct xt_entry_match *match,
@@ -150,11 +130,10 @@
 		.size          = XT_ALIGN(sizeof(struct ipt_tos_info)),
 		.userspacesize = XT_ALIGN(sizeof(struct ipt_tos_info)),
 		.help          = tos_mt_help,
-		.parse         = tos_mt_parse_v0,
-		.final_check   = tos_mt_check,
 		.print         = tos_mt_print_v0,
 		.save          = tos_mt_save_v0,
-		.extra_opts    = tos_mt_opts,
+		.x6_parse      = tos_mt_parse_v0,
+		.x6_options    = tos_mt_opts_v0,
 	},
 	{
 		.version       = XTABLES_VERSION,
@@ -164,11 +143,10 @@
 		.size          = XT_ALIGN(sizeof(struct xt_tos_match_info)),
 		.userspacesize = XT_ALIGN(sizeof(struct xt_tos_match_info)),
 		.help          = tos_mt_help,
-		.parse         = tos_mt_parse,
-		.final_check   = tos_mt_check,
 		.print         = tos_mt_print,
 		.save          = tos_mt_save,
-		.extra_opts    = tos_mt_opts,
+		.x6_parse      = tos_mt_parse,
+		.x6_options    = tos_mt_opts,
 	},
 };
 
diff --git a/extensions/libxt_udp.c b/extensions/libxt_udp.c
index 505b3c8..d493072 100644
--- a/extensions/libxt_udp.c
+++ b/extensions/libxt_udp.c
@@ -1,14 +1,15 @@
-/* Shared library add-on to iptables to add UDP support. */
-#include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <netdb.h>
-#include <string.h>
-#include <stdlib.h>
-#include <getopt.h>
-#include <netinet/in.h>
+#include <arpa/inet.h>
 #include <xtables.h>
 #include <linux/netfilter/xt_tcpudp.h>
 
+enum {
+	O_SOURCE_PORT = 0,
+	O_DEST_PORT,
+};
+
 static void udp_help(void)
 {
 	printf(
@@ -21,36 +22,19 @@
 "				match destination port(s)\n");
 }
 
-static const struct option udp_opts[] = {
-	{.name = "source-port",      .has_arg = true, .val = '1'},
-	{.name = "sport",            .has_arg = true, .val = '1'}, /* synonym */
-	{.name = "destination-port", .has_arg = true, .val = '2'},
-	{.name = "dport",            .has_arg = true, .val = '2'}, /* synonym */
-	XT_GETOPT_TABLEEND,
+#define s struct xt_udp
+static const struct xt_option_entry udp_opts[] = {
+	{.name = "source-port", .id = O_SOURCE_PORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, spts)},
+	{.name = "sport", .id = O_SOURCE_PORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, spts)},
+	{.name = "destination-port", .id = O_DEST_PORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, dpts)},
+	{.name = "dport", .id = O_DEST_PORT, .type = XTTYPE_PORTRC,
+	 .flags = XTOPT_INVERT | XTOPT_PUT, XTOPT_POINTER(s, dpts)},
+	XTOPT_TABLEEND,
 };
-
-static void
-parse_udp_ports(const char *portstring, uint16_t *ports)
-{
-	char *buffer;
-	char *cp;
-
-	buffer = strdup(portstring);
-	if ((cp = strchr(buffer, ':')) == NULL)
-		ports[0] = ports[1] = xtables_parse_port(buffer, "udp");
-	else {
-		*cp = '\0';
-		cp++;
-
-		ports[0] = buffer[0] ? xtables_parse_port(buffer, "udp") : 0;
-		ports[1] = cp[0] ? xtables_parse_port(cp, "udp") : 0xFFFF;
-
-		if (ports[0] > ports[1])
-			xtables_error(PARAMETER_PROBLEM,
-				   "invalid portrange (min > max)");
-	}
-	free(buffer);
-}
+#undef s
 
 static void udp_init(struct xt_entry_match *m)
 {
@@ -59,40 +43,21 @@
 	udpinfo->spts[1] = udpinfo->dpts[1] = 0xFFFF;
 }
 
-#define UDP_SRC_PORTS 0x01
-#define UDP_DST_PORTS 0x02
-
-static int
-udp_parse(int c, char **argv, int invert, unsigned int *flags,
-          const void *entry, struct xt_entry_match **match)
+static void udp_parse(struct xt_option_call *cb)
 {
-	struct xt_udp *udpinfo = (struct xt_udp *)(*match)->data;
+	struct xt_udp *udpinfo = cb->data;
 
-	switch (c) {
-	case '1':
-		if (*flags & UDP_SRC_PORTS)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Only one `--source-port' allowed");
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
-		parse_udp_ports(optarg, udpinfo->spts);
-		if (invert)
+	xtables_option_parse(cb);
+	switch (cb->entry->id) {
+	case O_SOURCE_PORT:
+		if (cb->invert)
 			udpinfo->invflags |= XT_UDP_INV_SRCPT;
-		*flags |= UDP_SRC_PORTS;
 		break;
-
-	case '2':
-		if (*flags & UDP_DST_PORTS)
-			xtables_error(PARAMETER_PROBLEM,
-				   "Only one `--destination-port' allowed");
-		xtables_check_inverse(optarg, &invert, &optind, 0, argv);
-		parse_udp_ports(optarg, udpinfo->dpts);
-		if (invert)
+	case O_DEST_PORT:
+		if (cb->invert)
 			udpinfo->invflags |= XT_UDP_INV_DSTPT;
-		*flags |= UDP_DST_PORTS;
 		break;
 	}
-
-	return 1;
 }
 
 static char *
@@ -195,10 +160,10 @@
 	.userspacesize	= XT_ALIGN(sizeof(struct xt_udp)),
 	.help		= udp_help,
 	.init		= udp_init,
-	.parse		= udp_parse,
 	.print		= udp_print,
 	.save		= udp_save,
-	.extra_opts	= udp_opts,
+	.x6_parse	= udp_parse,
+	.x6_options	= udp_opts,
 };
 
 void
diff --git a/extensions/tos_values.c b/extensions/tos_values.c
index c5efd9d..6dc4743 100644
--- a/extensions/tos_values.c
+++ b/extensions/tos_values.c
@@ -23,64 +23,6 @@
 	{},
 };
 
-/*
- * tos_parse_numeric - parse sth. like "15/255"
- *
- * @str:	input string
- * @tvm:	(value/mask) tuple
- * @max:	maximum allowed value (must be pow(2,some_int)-1)
- */
-static bool tos_parse_numeric(const char *str, struct tos_value_mask *tvm,
-                              unsigned int max)
-{
-	unsigned int value;
-	char *end;
-
-	xtables_strtoui(str, &end, &value, 0, max);
-	tvm->value = value;
-	tvm->mask  = max;
-
-	if (*end == '/') {
-		const char *p = end + 1;
-
-		if (!xtables_strtoui(p, &end, &value, 0, max))
-			xtables_error(PARAMETER_PROBLEM, "Illegal value: \"%s\"",
-			           str);
-		tvm->mask = value;
-	}
-
-	if (*end != '\0')
-		xtables_error(PARAMETER_PROBLEM, "Illegal value: \"%s\"", str);
-	return true;
-}
-
-/**
- * @str:	input string
- * @tvm:	(value/mask) tuple
- * @def_mask:	mask to force when a symbolic name is used
- */
-static bool tos_parse_symbolic(const char *str, struct tos_value_mask *tvm,
-    unsigned int def_mask)
-{
-	static const unsigned int max = UINT8_MAX;
-	const struct tos_symbol_info *symbol;
-	char *tmp;
-
-	if (xtables_strtoui(str, &tmp, NULL, 0, max))
-		return tos_parse_numeric(str, tvm, max);
-
-	/* Do not consider ECN bits when using preset names */
-	tvm->mask = def_mask;
-	for (symbol = tos_symbol_names; symbol->name != NULL; ++symbol)
-		if (strcasecmp(str, symbol->name) == 0) {
-			tvm->value = symbol->value;
-			return true;
-		}
-
-	xtables_error(PARAMETER_PROBLEM, "Symbolic name \"%s\" is unknown", str);
-	return false;
-}
-
 static bool tos_try_print_symbolic(const char *prefix,
     uint8_t value, uint8_t mask)
 {
diff --git a/include/xtables.h.in b/include/xtables.h.in
index 1d91d4d..5563ecb 100644
--- a/include/xtables.h.in
+++ b/include/xtables.h.in
@@ -50,11 +50,14 @@
  * %XTTYPE_UINT*:	standard integer
  * %XTTYPE_UINT*RC:	colon-separated range of standard integers
  * %XTTYPE_STRING:	arbitrary string
+ * %XTTYPE_TOSMASK:	8-bit TOS value with optional mask
  * %XTTYPE_MARKMASK32:	32-bit mark with optional mask
  * %XTTYPE_SYSLOGLEVEL:	syslog level by name or number
  * %XTTYPE_ONEHOST:	one host or address (union nf_inet_addr)
  * %XTTYPE_PORT:	16-bit port name or number
  * %XTTYPE_PORT_NE:	16-bit port name or number, stored as network-endian
+ * %XTTYPE_PORTRC:	colon-separated port range (names acceptable)
+ * %XTTYPE_PORTRC_NE:	same as %XTTYPE_PORTRC, stored in network-endian
  */
 enum xt_option_type {
 	XTTYPE_NONE,
@@ -67,11 +70,14 @@
 	XTTYPE_UINT32RC,
 	XTTYPE_UINT64RC,
 	XTTYPE_STRING,
+	XTTYPE_TOSMASK,
 	XTTYPE_MARKMASK32,
 	XTTYPE_SYSLOGLEVEL,
 	XTTYPE_ONEHOST,
 	XTTYPE_PORT,
 	XTTYPE_PORT_NE,
+	XTTYPE_PORTRC,
+	XTTYPE_PORTRC_NE,
 };
 
 /**
@@ -127,11 +133,14 @@
 	uint8_t nvals;
 	union {
 		uint8_t u8, u8_range[2], syslog_level;
-		uint16_t u16, u16_range[2], port;
+		uint16_t u16, u16_range[2], port, port_range[2];
 		uint32_t u32, u32_range[2];
 		uint64_t u64, u64_range[2];
 		union nf_inet_addr inetaddr;
 		struct {
+			uint8_t tos_value, tos_mask;
+		};
+		struct {
 			uint32_t mark, mask;
 		};
 	} val;
diff --git a/xtoptions.c b/xtoptions.c
index c3cc40e..5b1df88 100644
--- a/xtoptions.c
+++ b/xtoptions.c
@@ -19,8 +19,12 @@
 #include <string.h>
 #include <syslog.h>
 #include <arpa/inet.h>
+#include <netinet/ip.h>
 #include "xtables.h"
 #include "xshared.h"
+#ifndef IPTOS_NORMALSVC
+#	define IPTOS_NORMALSVC 0
+#endif
 
 #define XTOPT_MKPTR(cb) \
 	((void *)((char *)(cb)->data + (cb)->entry->ptroff))
@@ -33,6 +37,10 @@
 	uint8_t level;
 };
 
+struct tos_value_mask {
+	uint8_t value, mask;
+};
+
 /**
  * Creates getopt options from the x6-style option map, and assigns each a
  * getopt id.
@@ -232,6 +240,78 @@
 	p[z] = '\0';
 }
 
+static const struct tos_symbol_info {
+	unsigned char value;
+	const char *name;
+} tos_symbol_names[] = {
+	{IPTOS_LOWDELAY,    "Minimize-Delay"},
+	{IPTOS_THROUGHPUT,  "Maximize-Throughput"},
+	{IPTOS_RELIABILITY, "Maximize-Reliability"},
+	{IPTOS_MINCOST,     "Minimize-Cost"},
+	{IPTOS_NORMALSVC,   "Normal-Service"},
+	{},
+};
+
+/*
+ * tos_parse_numeric - parse a string like "15/255"
+ *
+ * @str:	input string
+ * @tvm:	(value/mask) tuple
+ * @max:	maximum allowed value (must be pow(2,some_int)-1)
+ */
+static bool tos_parse_numeric(const char *str, struct xt_option_call *cb,
+                              unsigned int max)
+{
+	unsigned int value;
+	char *end;
+
+	xtables_strtoui(str, &end, &value, 0, max);
+	cb->val.tos_value = value;
+	cb->val.tos_mask  = max;
+
+	if (*end == '/') {
+		const char *p = end + 1;
+
+		if (!xtables_strtoui(p, &end, &value, 0, max))
+			xtables_error(PARAMETER_PROBLEM, "Illegal value: \"%s\"",
+			           str);
+		cb->val.tos_mask = value;
+	}
+
+	if (*end != '\0')
+		xtables_error(PARAMETER_PROBLEM, "Illegal value: \"%s\"", str);
+	return true;
+}
+
+/**
+ * @str:	input string
+ * @tvm:	(value/mask) tuple
+ * @def_mask:	mask to force when a symbolic name is used
+ */
+static void xtopt_parse_tosmask(struct xt_option_call *cb)
+{
+	const struct tos_symbol_info *symbol;
+	char *tmp;
+
+	if (xtables_strtoui(cb->arg, &tmp, NULL, 0, UINT8_MAX)) {
+		tos_parse_numeric(cb->arg, cb, UINT8_MAX);
+		return;
+	}
+	/*
+	 * This is our way we deal with different defaults
+	 * for different revisions.
+	 */
+	cb->val.tos_mask = cb->entry->max;
+	for (symbol = tos_symbol_names; symbol->name != NULL; ++symbol)
+		if (strcasecmp(cb->arg, symbol->name) == 0) {
+			cb->val.tos_value = symbol->value;
+			return;
+		}
+
+	xtables_error(PARAMETER_PROBLEM, "Symbolic name \"%s\" is unknown",
+		      cb->arg);
+}
+
 /**
  * Validate the input for being conformant to "mark[/mask]".
  */
@@ -379,6 +459,8 @@
 		}
 	}
 	freeaddrinfo(res);
+	if (ret < 0)
+		return ret;
 	return ntohs(ret);
 }
 
@@ -401,6 +483,61 @@
 		*(uint16_t *)XTOPT_MKPTR(cb) = cb->val.port;
 }
 
+static void xtopt_parse_mport(struct xt_option_call *cb)
+{
+	static const size_t esize = sizeof(uint16_t);
+	const struct xt_option_entry *entry = cb->entry;
+	char *lo_arg, *wp_arg, *arg;
+	unsigned int maxiter;
+	int value;
+
+	wp_arg = lo_arg = strdup(cb->arg);
+	if (lo_arg == NULL)
+		xt_params->exit_err(RESOURCE_PROBLEM, "strdup");
+
+	maxiter = entry->size / esize;
+	if (maxiter == 0)
+		maxiter = 2; /* ARRAY_SIZE(cb->val.port_range) */
+	if (entry->size % esize != 0)
+		xt_params->exit_err(OTHER_PROBLEM, "%s: memory block does "
+			"not have proper size\n", __func__);
+
+	cb->val.port_range[0] = 0;
+	cb->val.port_range[1] = UINT16_MAX;
+	cb->nvals = 0;
+
+	while ((arg = strsep(&wp_arg, ":")) != NULL) {
+		if (cb->nvals == maxiter)
+			xt_params->exit_err(PARAMETER_PROBLEM, "%s: Too many "
+				"components for option \"--%s\" (max: %u)\n",
+				cb->ext_name, entry->name, maxiter);
+		if (*arg == '\0') {
+			++cb->nvals;
+			continue;
+		}
+
+		value = xtables_getportbyname(arg);
+		if (value < 0)
+			xt_params->exit_err(PARAMETER_PROBLEM,
+				"Port \"%s\" does not resolve to "
+				"anything.\n", arg);
+		if (entry->type == XTTYPE_PORTRC_NE)
+			value = htons(value);
+		if (cb->nvals < ARRAY_SIZE(cb->val.port_range))
+			cb->val.port_range[cb->nvals] = value;
+		++cb->nvals;
+	}
+
+	if (cb->nvals == 1) {
+		cb->val.port_range[1] = cb->val.port_range[0];
+		++cb->nvals;
+	}
+	if (entry->flags & XTOPT_PUT)
+		memcpy(XTOPT_MKPTR(cb), cb->val.port_range, sizeof(uint16_t) *
+		       (cb->nvals <= maxiter ? cb->nvals : maxiter));
+	free(lo_arg);
+}
+
 static void (*const xtopt_subparse[])(struct xt_option_call *) = {
 	[XTTYPE_UINT8]       = xtopt_parse_int,
 	[XTTYPE_UINT16]      = xtopt_parse_int,
@@ -411,11 +548,14 @@
 	[XTTYPE_UINT32RC]    = xtopt_parse_mint,
 	[XTTYPE_UINT64RC]    = xtopt_parse_mint,
 	[XTTYPE_STRING]      = xtopt_parse_string,
+	[XTTYPE_TOSMASK]     = xtopt_parse_tosmask,
 	[XTTYPE_MARKMASK32]  = xtopt_parse_markmask,
 	[XTTYPE_SYSLOGLEVEL] = xtopt_parse_sysloglevel,
 	[XTTYPE_ONEHOST]     = xtopt_parse_onehost,
 	[XTTYPE_PORT]        = xtopt_parse_port,
 	[XTTYPE_PORT_NE]     = xtopt_parse_port,
+	[XTTYPE_PORTRC]      = xtopt_parse_mport,
+	[XTTYPE_PORTRC_NE]   = xtopt_parse_mport,
 };
 
 static const size_t xtopt_psize[] = {
@@ -432,6 +572,8 @@
 	[XTTYPE_ONEHOST]     = sizeof(union nf_inet_addr),
 	[XTTYPE_PORT]        = sizeof(uint16_t),
 	[XTTYPE_PORT_NE]     = sizeof(uint16_t),
+	[XTTYPE_PORTRC]      = sizeof(uint16_t[2]),
+	[XTTYPE_PORTRC_NE]   = sizeof(uint16_t[2]),
 };
 
 /**