diff --git a/include/libipset/data.h b/include/libipset/data.h
index 744b010..55cf42f 100644
--- a/include/libipset/data.h
+++ b/include/libipset/data.h
@@ -66,6 +66,10 @@ enum ipset_opt {
 	IPSET_OPT_SKBMARK,
 	IPSET_OPT_SKBPRIO,
 	IPSET_OPT_SKBQUEUE,
+	/* New second port ADT options */
+	IPSET_OPT_PORT2,
+	IPSET_OPT_PORT2_FROM = IPSET_OPT_PORT2,
+	IPSET_OPT_PORT2_TO,
 	/* Internal options */
 	IPSET_OPT_FLAGS = 48,	/* IPSET_FLAG_EXIST| */
 	IPSET_OPT_CADT_FLAGS,	/* IPSET_FLAG_BEFORE| */
@@ -111,6 +115,8 @@ enum ipset_opt {
 	| IPSET_FLAG(IPSET_OPT_MARK)	\
 	| IPSET_FLAG(IPSET_OPT_PORT)	\
 	| IPSET_FLAG(IPSET_OPT_PORT_TO)	\
+	| IPSET_FLAG(IPSET_OPT_PORT2)   \
+	| IPSET_FLAG(IPSET_OPT_PORT2_TO)\
 	| IPSET_FLAG(IPSET_OPT_TIMEOUT)	\
 	| IPSET_FLAG(IPSET_OPT_ETHER)	\
 	| IPSET_FLAG(IPSET_OPT_NAME)	\
diff --git a/include/libipset/linux_ip_set.h b/include/libipset/linux_ip_set.h
index 68a2087..4ea0c6d 100644
--- a/include/libipset/linux_ip_set.h
+++ b/include/libipset/linux_ip_set.h
@@ -122,6 +122,9 @@ enum {
 	IPSET_ATTR_SKBMARK,
 	IPSET_ATTR_SKBPRIO,
 	IPSET_ATTR_SKBQUEUE,
+	IPSET_ATTR_PORT2,
+	IPSET_ATTR_PORT2_FROM = IPSET_ATTR_PORT2,
+	IPSET_ATTR_PORT2_TO,
 	IPSET_ATTR_PAD,
 	__IPSET_ATTR_ADT_MAX,
 };
@@ -237,6 +240,7 @@ enum ip_set_dim {
 	IPSET_DIM_ONE,
 	IPSET_DIM_TWO,
 	IPSET_DIM_THREE,
+	IPSET_DIM_FOUR,
 	/* Max dimension in elements.
 	 * If changed, new revision of iptables match/target is required.
 	 */
@@ -251,6 +255,7 @@ enum ip_set_kopt {
 	IPSET_DIM_ONE_SRC = (1 << IPSET_DIM_ONE),
 	IPSET_DIM_TWO_SRC = (1 << IPSET_DIM_TWO),
 	IPSET_DIM_THREE_SRC = (1 << IPSET_DIM_THREE),
+	IPSET_DIM_FOUR_SRC = (1 << IPSET_DIM_FOUR),
 	IPSET_RETURN_NOMATCH = (1 << IPSET_BIT_RETURN_NOMATCH),
 };
 
diff --git a/include/libipset/types.h b/include/libipset/types.h
index bb71d88..a0b0d48 100644
--- a/include/libipset/types.h
+++ b/include/libipset/types.h
@@ -29,7 +29,7 @@ enum {
 };
 
 /* The maximal type dimension userspace supports */
-#define IPSET_DIM_UMAX		3
+#define IPSET_DIM_UMAX		4
 
 /* Parser options */
 enum {
diff --git a/kernel/include/linux/netfilter/ipset/ip_set.h b/kernel/include/linux/netfilter/ipset/ip_set.h
index f2b946f..62ec898 100644
--- a/kernel/include/linux/netfilter/ipset/ip_set.h
+++ b/kernel/include/linux/netfilter/ipset/ip_set.h
@@ -44,9 +44,11 @@ enum ip_set_feature {
 	IPSET_TYPE_MARK = (1 << IPSET_TYPE_MARK_FLAG),
 	IPSET_TYPE_NOMATCH_FLAG = 7,
 	IPSET_TYPE_NOMATCH = (1 << IPSET_TYPE_NOMATCH_FLAG),
+	IPSET_TYPE_PORT2_FLAG = 8,
+	IPSET_TYPE_PORT2 = (1 << IPSET_TYPE_PORT2_FLAG),
 	/* Strictly speaking not a feature, but a flag for dumping:
 	 * this settype must be dumped last */
-	IPSET_DUMP_LAST_FLAG = 8,
+	IPSET_DUMP_LAST_FLAG = 9,
 	IPSET_DUMP_LAST = (1 << IPSET_DUMP_LAST_FLAG),
 };
 
diff --git a/kernel/include/uapi/linux/netfilter/ipset/ip_set.h b/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
index c83497f..7fce287 100644
--- a/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
+++ b/kernel/include/uapi/linux/netfilter/ipset/ip_set.h
@@ -122,6 +122,9 @@ enum {
 	IPSET_ATTR_SKBMARK,
 	IPSET_ATTR_SKBPRIO,
 	IPSET_ATTR_SKBQUEUE,
+	IPSET_ATTR_PORT2,
+	IPSET_ATTR_PORT2_FROM = IPSET_ATTR_PORT2,
+	IPSET_ATTR_PORT2_TO,
 	IPSET_ATTR_PAD,
 	__IPSET_ATTR_ADT_MAX,
 };
@@ -237,6 +240,7 @@ enum ip_set_dim {
 	IPSET_DIM_ONE,
 	IPSET_DIM_TWO,
 	IPSET_DIM_THREE,
+	IPSET_DIM_FOUR,
 	/* Max dimension in elements.
 	 * If changed, new revision of iptables match/target is required.
 	 */
@@ -251,6 +255,7 @@ enum ip_set_kopt {
 	IPSET_DIM_ONE_SRC = (1 << IPSET_DIM_ONE),
 	IPSET_DIM_TWO_SRC = (1 << IPSET_DIM_TWO),
 	IPSET_DIM_THREE_SRC = (1 << IPSET_DIM_THREE),
+	IPSET_DIM_FOUR_SRC = (1 << IPSET_DIM_FOUR),
 	IPSET_RETURN_NOMATCH = (1 << IPSET_BIT_RETURN_NOMATCH),
 };
 
diff --git a/kernel/net/netfilter/ipset/Kconfig b/kernel/net/netfilter/ipset/Kconfig
index 861659f..4083a80 100644
--- a/kernel/net/netfilter/ipset/Kconfig
+++ b/kernel/net/netfilter/ipset/Kconfig
@@ -21,7 +21,7 @@ config IP_SET_MAX
 	  You can define here default value of the maximum number 
 	  of IP sets for the kernel.
 
-	  The value can be overriden by the 'max_sets' module
+	  The value can be overridden by the 'max_sets' module
 	  parameter of the 'ip_set' module.
 
 config IP_SET_BITMAP_IP
diff --git a/lib/data.c b/lib/data.c
index 9a7c861..94080f7 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -44,6 +44,8 @@ struct ipset_data {
 	uint32_t mark;
 	uint16_t port;
 	uint16_t port_to;
+	uint16_t port2;
+	uint16_t port2_to;
 	uint16_t index;
 	union {
 		/* RENAME/SWAP */
@@ -365,6 +367,12 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
 			return -1;
 		copy_addr(data->family, &data->adt.ip2_to, value);
 		break;
+	case IPSET_OPT_PORT2:
+		data->port2 = *(const uint16_t *) value;
+		break;
+	case IPSET_OPT_PORT2_TO:
+		data->port2_to = *(const uint16_t *) value;
+		break;
 	case IPSET_OPT_CIDR2:
 		data->adt.cidr2 = *(const uint8_t *) value;
 		break;
@@ -531,6 +539,10 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
 		return &data->adt.ip2;
 	case IPSET_OPT_IP2_TO:
 		return &data->adt.ip2_to;
+	case IPSET_OPT_PORT2:
+		return &data->port2;
+	case IPSET_OPT_PORT2_TO:
+		return &data->port2_to;
 	case IPSET_OPT_CIDR2:
 		return &data->adt.cidr2;
 	case IPSET_OPT_PROTO:
@@ -593,6 +605,8 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
 		return sizeof(uint32_t);
 	case IPSET_OPT_PORT:
 	case IPSET_OPT_PORT_TO:
+	case IPSET_OPT_PORT2:
+	case IPSET_OPT_PORT2_TO:
 	case IPSET_OPT_SKBQUEUE:
 	case IPSET_OPT_INDEX:
 		return sizeof(uint16_t);
diff --git a/lib/debug.c b/lib/debug.c
index 44d0f04..eed1731 100644
--- a/lib/debug.c
+++ b/lib/debug.c
@@ -55,6 +55,8 @@ static const struct ipset_attrname adtattr2name[] = {
 	[IPSET_ATTR_MARK]	= { .name = "MARK" },
 	[IPSET_ATTR_PORT]	= { .name = "PORT" },
 	[IPSET_ATTR_PORT_TO]	= { .name = "PORT_TO" },
+	[IPSET_ATTR_PORT2]	= { .name = "PORT2" },
+	[IPSET_ATTR_PORT2_TO]	= { .name = "PORT2_TO" },
 	[IPSET_ATTR_TIMEOUT]	= { .name = "TIMEOUT" },
 	[IPSET_ATTR_PROTO]	= { .name = "PROTO" },
 	[IPSET_ATTR_CADT_FLAGS]	= { .name = "CADT_FLAGS" },
diff --git a/lib/parse.c b/lib/parse.c
index 5943f05..b10432f 100644
--- a/lib/parse.c
+++ b/lib/parse.c
@@ -316,7 +316,8 @@ ipset_parse_port(struct ipset_session *session,
 	uint16_t port;
 
 	assert(session);
-	assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO);
+	assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO ||
+	       opt == IPSET_OPT_PORT2 || opt == IPSET_OPT_PORT2_TO);
 	assert(str);
 
 	if (parse_portname(session, str, &port, proto) == 0) {
@@ -383,7 +384,7 @@ ipset_parse_tcpudp_port(struct ipset_session *session,
 	int err = 0;
 
 	assert(session);
-	assert(opt == IPSET_OPT_PORT);
+	assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT2);
 	assert(str);
 
 	saved = tmp = ipset_strdup(session, str);
@@ -399,7 +400,10 @@ ipset_parse_tcpudp_port(struct ipset_session *session,
 	if (a != NULL) {
 		/* port-port */
 		*a++ = '\0';
-		err = ipset_parse_port(session, IPSET_OPT_PORT_TO, a, proto);
+		err = ipset_parse_port(session,
+			opt == IPSET_OPT_PORT ? IPSET_OPT_PORT_TO
+					      : IPSET_OPT_PORT2_TO,
+			a, proto);
 		if (err)
 			goto error;
 	}
@@ -446,7 +450,8 @@ ipset_parse_single_tcp_port(struct ipset_session *session,
 			    enum ipset_opt opt, const char *str)
 {
 	assert(session);
-	assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO);
+	assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT_TO ||
+	       opt == IPSET_OPT_PORT2 || opt == IPSET_OPT_PORT2_TO);
 	assert(str);
 
 	return ipset_parse_port(session, opt, str, "tcp");
@@ -603,7 +608,7 @@ ipset_parse_proto_port(struct ipset_session *session,
 	int err = 0;
 
 	assert(session);
-	assert(opt == IPSET_OPT_PORT);
+	assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT2);
 	assert(str);
 
 	data = ipset_session_data(session);
@@ -1962,7 +1967,7 @@ ipset_parse_elem(struct ipset_session *session,
 		 bool optional, const char *str)
 {
 	const struct ipset_type *type;
-	char *a = NULL, *b = NULL, *tmp, *saved;
+	char *a = NULL, *b = NULL, *c = NULL, *tmp, *saved;
 	int ret;
 
 	assert(session);
@@ -2010,9 +2015,22 @@ ipset_parse_elem(struct ipset_session *session,
 		elem_syntax_err("Two elem separators in %s, "
 				"but settype %s supports one.",
 				str, type->name);
-	if (b != NULL && elem_separator(b))
-		elem_syntax_err("Three elem separators in %s, "
-				"but settype %s supports two.",
+	if (b)
+		c = elem_separator(b);
+	if (type->dimension > IPSET_DIM_THREE) {
+		if (c != NULL) {
+			/* elem,elem,elem,elem */
+			*c++ = '\0';
+		} else if (!optional)
+			elem_syntax_err("Fourth element is missing from %s.",
+					str);
+	} else if (c != NULL)
+		elem_syntax_err("Four elem separators in %s, "
+				"but settype %s supports three.",
+				str, type->name);
+	if (c != NULL && elem_separator(c))
+		elem_syntax_err("Five elem separators in %s, "
+				"but settype %s supports four.",
 				str, type->name);
 
 	D("parse elem part one: %s", tmp);
@@ -2026,6 +2044,10 @@ ipset_parse_elem(struct ipset_session *session,
 		D("parse elem part three: %s", b);
 		parse_elem(session, type, IPSET_DIM_THREE, b);
 	}
+	if (type->dimension > IPSET_DIM_THREE && c != NULL) {
+		D("parse elem part four: %s", c);
+		parse_elem(session, type, IPSET_DIM_FOUR, c);
+	}
 
 	goto out;
 
diff --git a/lib/print.c b/lib/print.c
index 02ffe41..70b4396 100644
--- a/lib/print.c
+++ b/lib/print.c
@@ -470,12 +470,12 @@ ipset_print_port(char *buf, unsigned int len,
 	assert(buf);
 	assert(len > 0);
 	assert(data);
-	assert(opt == IPSET_OPT_PORT);
+	assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT2);
 
 	if (len < 2*strlen("65535") + 2)
 		return -1;
 
-	port = ipset_data_get(data, IPSET_OPT_PORT);
+	port = ipset_data_get(data, opt);
 	assert(port);
 	size = snprintf(buf, len, "%u", *port);
 	SNPRINTF_FAILURE(size, len, offset);
@@ -486,6 +486,12 @@ ipset_print_port(char *buf, unsigned int len,
 				"%s%u",
 				IPSET_RANGE_SEPARATOR, *port);
 		SNPRINTF_FAILURE(size, len, offset);
+	} else if (ipset_data_test(data, IPSET_OPT_PORT2_TO)) {
+		port = ipset_data_get(data, IPSET_OPT_PORT2_TO);
+		size = snprintf(buf + offset, len,
+				"%s%u",
+				IPSET_RANGE_SEPARATOR, *port);
+		SNPRINTF_FAILURE(size, len, offset);
 	}
 
 	return offset;
@@ -775,7 +781,7 @@ ipset_print_proto_port(char *buf, unsigned int len,
 	assert(buf);
 	assert(len > 0);
 	assert(data);
-	assert(opt == IPSET_OPT_PORT);
+	assert(opt == IPSET_OPT_PORT || opt == IPSET_OPT_PORT2);
 
 	if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_PROTO))) {
 		uint8_t proto = *(const uint8_t *) ipset_data_get(data,
@@ -795,17 +801,17 @@ ipset_print_proto_port(char *buf, unsigned int len,
 			break;
 		case IPPROTO_ICMP:
 			size = ipset_print_icmp(buf + offset, len, data,
-						IPSET_OPT_PORT, env);
+						opt, env);
 			goto out;
 		case IPPROTO_ICMPV6:
 			size = ipset_print_icmpv6(buf + offset, len, data,
-						  IPSET_OPT_PORT, env);
+						  opt, env);
 			goto out;
 		default:
 			break;
 		}
 	}
-	size = ipset_print_port(buf + offset, len, data, IPSET_OPT_PORT, env);
+	size = ipset_print_port(buf + offset, len, data, opt, env);
 out:
 	SNPRINTF_FAILURE(size, len, offset);
 	return offset;
@@ -871,6 +877,16 @@ ipset_print_elem(char *buf, unsigned int len,
 	size = type->elem[IPSET_DIM_THREE - 1].print(buf + offset, len, data,
 			type->elem[IPSET_DIM_THREE - 1].opt, env);
 	SNPRINTF_FAILURE(size, len, offset);
+	if (type->dimension == IPSET_DIM_THREE ||
+	    (type->last_elem_optional &&
+	     !ipset_data_test(data, type->elem[IPSET_DIM_FOUR - 1].opt)))
+		return offset;
+
+	size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR);
+	SNPRINTF_FAILURE(size, len, offset);
+	size = type->elem[IPSET_DIM_FOUR - 1].print(buf + offset, len, data,
+			type->elem[IPSET_DIM_FOUR - 1].opt, env);
+	SNPRINTF_FAILURE(size, len, offset);
 
 	return offset;
 }
diff --git a/lib/session.c b/lib/session.c
index 9e1d7dd..ed82ca9 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -488,6 +488,14 @@ static const struct ipset_attr_policy adt_attrs[] = {
 		.type = MNL_TYPE_U16,
 		.opt = IPSET_OPT_PORT_TO,
 	},
+	[IPSET_ATTR_PORT2] = {
+		.type = MNL_TYPE_U16,
+		.opt = IPSET_OPT_PORT2,
+	},
+	[IPSET_ATTR_PORT2_TO] = {
+		.type = MNL_TYPE_U16,
+		.opt = IPSET_OPT_PORT2_TO,
+	},
 	[IPSET_ATTR_PROTO] = {
 		.type = MNL_TYPE_U8,
 		.opt = IPSET_OPT_PROTO,
