[PATCH 1/5] ipset: netmask: expand to support cidr and full mask

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Convert netmask to store cidr and netmask.

Signed-off-by: Josh Hunt <johunt@xxxxxxxxxx>
---
 include/libipset/data.h         |  5 ++-
 include/libipset/linux_ip_set.h |  5 +++
 include/libipset/print.h        |  3 ++
 lib/data.c                      | 25 +++++++++++++--
 lib/debug.c                     |  1 +
 lib/parse.c                     | 68 ++++++++++++++++++++++++++++++++++++-----
 lib/print.c                     | 39 +++++++++++++++++++++--
 lib/session.c                   |  8 +++++
 8 files changed, 141 insertions(+), 13 deletions(-)

diff --git a/include/libipset/data.h b/include/libipset/data.h
index ca21890..0314cfb 100644
--- a/include/libipset/data.h
+++ b/include/libipset/data.h
@@ -37,6 +37,7 @@ enum ipset_opt {
 	IPSET_OPT_RESIZE,
 	IPSET_OPT_SIZE,
 	IPSET_OPT_FORCEADD,
+	IPSET_OPT_NETMASK_MASK,
 	/* Create-specific options, filled out by the kernel */
 	IPSET_OPT_ELEMENTS,
 	IPSET_OPT_REFERENCES,
@@ -66,6 +67,7 @@ enum ipset_opt {
 	IPSET_OPT_SKBMARK,
 	IPSET_OPT_SKBPRIO,
 	IPSET_OPT_SKBQUEUE,
+	IPSET_OPT_NETMASK_FLAG,
 	/* Internal options */
 	IPSET_OPT_FLAGS = 48,	/* IPSET_FLAG_EXIST| */
 	IPSET_OPT_CADT_FLAGS,	/* IPSET_FLAG_BEFORE| */
@@ -101,7 +103,8 @@ enum ipset_opt {
 	| IPSET_FLAG(IPSET_OPT_COUNTERS)\
 	| IPSET_FLAG(IPSET_OPT_CREATE_COMMENT)\
 	| IPSET_FLAG(IPSET_OPT_FORCEADD)\
-	| IPSET_FLAG(IPSET_OPT_SKBINFO))
+	| IPSET_FLAG(IPSET_OPT_SKBINFO)\
+	| IPSET_FLAG(IPSET_OPT_NETMASK_MASK))
 
 #define IPSET_ADT_FLAGS			\
 	(IPSET_FLAG(IPSET_OPT_IP)	\
diff --git a/include/libipset/linux_ip_set.h b/include/libipset/linux_ip_set.h
index def91b9..4f93886 100644
--- a/include/libipset/linux_ip_set.h
+++ b/include/libipset/linux_ip_set.h
@@ -84,6 +84,7 @@ enum {
 	IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO,	/* 9 */
 	IPSET_ATTR_MARK,	/* 10 */
 	IPSET_ATTR_MARKMASK,	/* 11 */
+	IPSET_ATTR_NETMASK_MASK,/* 12 */
 	/* Reserve empty slots */
 	IPSET_ATTR_CADT_MAX = 16,
 	/* Create-only specific attributes */
@@ -199,6 +200,8 @@ enum ipset_cadt_flags {
 	IPSET_FLAG_WITH_FORCEADD = (1 << IPSET_FLAG_BIT_WITH_FORCEADD),
 	IPSET_FLAG_BIT_WITH_SKBINFO = 6,
 	IPSET_FLAG_WITH_SKBINFO = (1 << IPSET_FLAG_BIT_WITH_SKBINFO),
+	IPSET_FLAG_BIT_WITH_NETMASK = 7,
+	IPSET_FLAG_WITH_NETMASK = (1 << IPSET_FLAG_BIT_WITH_NETMASK),
 	IPSET_FLAG_CADT_MAX	= 15,
 };
 
@@ -206,6 +209,8 @@ enum ipset_cadt_flags {
 enum ipset_create_flags {
 	IPSET_CREATE_FLAG_BIT_FORCEADD = 0,
 	IPSET_CREATE_FLAG_FORCEADD = (1 << IPSET_CREATE_FLAG_BIT_FORCEADD),
+	IPSET_CREATE_FLAG_BIT_NETMASK = 1,
+	IPSET_CREATE_FLAG_NETMASK = (1 << IPSET_CREATE_FLAG_BIT_NETMASK),
 	IPSET_CREATE_FLAG_BIT_MAX = 7,
 };
 
diff --git a/include/libipset/print.h b/include/libipset/print.h
index 2103ce1..f9e77d3 100644
--- a/include/libipset/print.h
+++ b/include/libipset/print.h
@@ -74,6 +74,9 @@ extern int ipset_print_flag(char *buf, unsigned int len,
 extern int ipset_print_elem(char *buf, unsigned int len,
 			    const struct ipset_data *data,
 			    enum ipset_opt opt, uint8_t env);
+extern int ipset_print_netmask(char *buf, unsigned int len,
+			    const struct ipset_data *data,
+			    enum ipset_opt opt, uint8_t env);
 
 #define ipset_print_portnum	ipset_print_number
 
diff --git a/lib/data.c b/lib/data.c
index 8372a2f..951a124 100644
--- a/lib/data.c
+++ b/lib/data.c
@@ -24,6 +24,11 @@
  * We always store the data in host order, *except* IP addresses.
  */
 
+struct ipset_netmask {
+	uint8_t cidr;
+	union nf_inet_addr mask;
+};
+
 struct ipset_data {
 	/* Option bits: which fields are set */
 	uint64_t bits;
@@ -51,7 +56,7 @@ struct ipset_data {
 		struct {
 			uint8_t probes;
 			uint8_t resize;
-			uint8_t netmask;
+			struct ipset_netmask netmask;
 			uint32_t hashsize;
 			uint32_t maxelem;
 			uint32_t markmask;
@@ -295,7 +300,13 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
 		data->create.markmask = *(const uint32_t *) value;
 		break;
 	case IPSET_OPT_NETMASK:
-		data->create.netmask = *(const uint8_t *) value;
+		data->create.netmask.cidr = *(const uint8_t *) value;
+		break;
+	case IPSET_OPT_NETMASK_MASK:
+		if (!(data->family == NFPROTO_IPV4 ||
+		      data->family == NFPROTO_IPV6))
+			return -1;
+		copy_addr(data->family, &data->create.netmask.mask, value);
 		break;
 	case IPSET_OPT_PROBES:
 		data->create.probes = *(const uint8_t *) value;
@@ -318,6 +329,9 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
 	case IPSET_OPT_SKBINFO:
 		cadt_flag_type_attr(data, opt, IPSET_FLAG_WITH_SKBINFO);
 		break;
+	case IPSET_OPT_NETMASK_FLAG:
+		cadt_flag_type_attr(data, opt, IPSET_FLAG_WITH_NETMASK);
+		break;
 	/* Create-specific options, filled out by the kernel */
 	case IPSET_OPT_ELEMENTS:
 		data->create.elements = *(const uint32_t *) value;
@@ -495,7 +509,9 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
 	case IPSET_OPT_MARKMASK:
 		return &data->create.markmask;
 	case IPSET_OPT_NETMASK:
-		return &data->create.netmask;
+		return &data->create.netmask.cidr;
+	case IPSET_OPT_NETMASK_MASK:
+		return &data->create.netmask.mask;
 	case IPSET_OPT_PROBES:
 		return &data->create.probes;
 	case IPSET_OPT_RESIZE:
@@ -558,6 +574,7 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
 	case IPSET_OPT_CREATE_COMMENT:
 	case IPSET_OPT_FORCEADD:
 	case IPSET_OPT_SKBINFO:
+	case IPSET_OPT_NETMASK_FLAG:
 		return &data->cadt_flags;
 	default:
 		return NULL;
@@ -581,6 +598,7 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
 	case IPSET_OPT_IP_TO:
 	case IPSET_OPT_IP2:
 	case IPSET_OPT_IP2_TO:
+	case IPSET_OPT_NETMASK_MASK:
 		return family == NFPROTO_IPV4 ? sizeof(uint32_t)
 					 : sizeof(struct in6_addr);
 	case IPSET_OPT_MARK:
@@ -623,6 +641,7 @@ ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
 	case IPSET_OPT_NOMATCH:
 	case IPSET_OPT_COUNTERS:
 	case IPSET_OPT_FORCEADD:
+	case IPSET_OPT_NETMASK_FLAG:
 		return sizeof(uint32_t);
 	case IPSET_OPT_ADT_COMMENT:
 		return IPSET_MAX_COMMENT_SIZE + 1;
diff --git a/lib/debug.c b/lib/debug.c
index 6f831ec..a32f709 100644
--- a/lib/debug.c
+++ b/lib/debug.c
@@ -40,6 +40,7 @@ static const struct ipset_attrname createattr2name[] = {
 	[IPSET_ATTR_MAXELEM]	= { .name = "MAXELEM" },
 	[IPSET_ATTR_MARKMASK]	= { .name = "MARKMASK" },
 	[IPSET_ATTR_NETMASK]	= { .name = "NETMASK" },
+	[IPSET_ATTR_NETMASK_MASK] = { .name = "NETMASK_MASK" },
 	[IPSET_ATTR_PROBES]	= { .name = "PROBES" },
 	[IPSET_ATTR_RESIZE]	= { .name = "RESIZE" },
 	[IPSET_ATTR_SIZE]	= { .name = "SIZE" },
diff --git a/lib/parse.c b/lib/parse.c
index 88d2888..5c41c37 100644
--- a/lib/parse.c
+++ b/lib/parse.c
@@ -1654,7 +1654,35 @@ ipset_parse_uint8(struct ipset_session *session,
 }
 
 /**
+ * ipset_cidr_to_mask - convert cidr value to mask stored in nf_inet_addr
+ * @addr: store resulting mask here
+ * @cidr: value to convert to mask
+ * @family: INET family type
+ *
+ * Parse cidr value and convert it into a mask stored in an nf_inet_addr.
+ */
+static void
+ipset_cidr_to_mask(union nf_inet_addr *addr, uint8_t cidr, uint8_t family)
+{
+	uint8_t i;
+	uint8_t addrsize = (family == NFPROTO_IPV4) ? 1 : 4;
+
+        for (i=0; i < addrsize; i++) {
+		if (!cidr) {
+			addr->all[i] = 0;
+		} else if (cidr >= 32) {
+			addr->all[i] = 0xffffffff;
+			cidr -= 32;
+		} else {
+			addr->all[i] = htonl(~((1 << (32 - cidr)) - 1));
+			break;
+		}
+	}
+}
+
+/**
  * ipset_parse_netmask - parse string as a CIDR netmask value
+ *			 or netmask value
  * @session: session structure
  * @opt: option kind of the data
  * @str: string to parse
@@ -1669,12 +1697,13 @@ int
 ipset_parse_netmask(struct ipset_session *session,
 		    enum ipset_opt opt, const char *str)
 {
-	uint8_t family, cidr;
+	uint8_t family, cidr = 0;
 	struct ipset_data *data;
 	int err = 0;
+	union nf_inet_addr netmask = {};
 
 	assert(session);
-	assert(opt == IPSET_OPT_NETMASK);
+	assert(opt == IPSET_OPT_NETMASK_MASK || opt == IPSET_OPT_NETMASK);
 	assert(str);
 
 	data = ipset_session_data(session);
@@ -1684,16 +1713,41 @@ ipset_parse_netmask(struct ipset_session *session,
 		ipset_data_set(data, IPSET_OPT_FAMILY, &family);
 	}
 
+	/* Try to parse input as CIDR first */
 	err = string_to_cidr(session, str, 1,
 			     family == NFPROTO_IPV4 ? 32 : 128,
 			     &cidr);
 
-	if (err)
-		return syntax_err("netmask is out of the inclusive range "
-				  "of 1-%u",
-				  family == NFPROTO_IPV4 ? 32 : 128);
+	if (opt == IPSET_OPT_NETMASK) {
+		if (err)
+			return syntax_err("netmask is out of the inclusive range "
+					  "of 1-%u",
+					  family == NFPROTO_IPV4 ? 32 : 128);
+
+		return ipset_data_set(data, opt, &cidr);
+	} else {
+
+		/* If that fails, attempt to parse as an IP addr */
+		if (err) {
+			/* Store mask value as input by user */
+			err = parse_ipaddr(session, opt, str, family);
+			if (err)
+				return err;
 
-	return ipset_data_set(data, opt, &cidr);
+			/* Check if it's a CIDR and store if it is */
+			netmask = *(const union nf_inet_addr *) ipset_data_get(data, IPSET_OPT_NETMASK_MASK);
+		} else {
+			ipset_cidr_to_mask(&netmask, cidr, family);
+
+			err = ipset_data_set(data, opt, &netmask);
+			if (err)
+				return err;
+		}
+
+		ipset_data_set(data, IPSET_OPT_NETMASK_FLAG, &family);
+
+		return ipset_data_set(data, IPSET_OPT_NETMASK, &cidr);
+	}
 }
 
 /**
diff --git a/lib/print.c b/lib/print.c
index 7dd229e..cc450c6 100644
--- a/lib/print.c
+++ b/lib/print.c
@@ -241,6 +241,39 @@ SNPRINTF_IP(32, 4)
 SNPRINTF_IP(128, 6)
 
 /**
+ * ipset_print_netmask - print netmask in either CIDR or address to string
+ * @buf: printing buffer
+ * @len: length of available buffer space
+ * @data: data blob
+ * @opt: the option kind
+ * @env: environment flags
+ *
+ * If the netmask can be represented as a CIDR print that, otherwise print
+ * the mask value.
+ *
+ * Return length of printed string or error size.
+ */
+int
+ipset_print_netmask(char *buf, unsigned int len,
+		    const struct ipset_data *data, enum ipset_opt opt,
+		    uint8_t env)
+{
+	uint8_t cidr;
+
+	assert(buf);
+	assert(len > 0);
+	assert(data);
+	assert(opt == IPSET_OPT_NETMASK_MASK);
+
+	cidr = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_NETMASK);
+
+	if (cidr)
+		return snprintf(buf, len, "%u", cidr);
+	else
+		return ipset_print_ip(buf, len, data, opt, env);
+}
+
+/**
  * ipset_print_ip - print IPv4|IPv6 address to string
  * @buf: printing buffer
  * @len: length of available buffer space
@@ -265,7 +298,7 @@ ipset_print_ip(char *buf, unsigned int len,
 	assert(buf);
 	assert(len > 0);
 	assert(data);
-	assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
+	assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2 || opt == IPSET_OPT_NETMASK_MASK);
 
 	D("len: %u", len);
 	family = ipset_data_family(data);
@@ -944,7 +977,6 @@ ipset_print_data(char *buf, unsigned int len,
 	case IPSET_OPT_HASHSIZE:
 	case IPSET_OPT_MAXELEM:
 	case IPSET_OPT_MARKMASK:
-	case IPSET_OPT_NETMASK:
 	case IPSET_OPT_PROBES:
 	case IPSET_OPT_RESIZE:
 	case IPSET_OPT_TIMEOUT:
@@ -953,6 +985,9 @@ ipset_print_data(char *buf, unsigned int len,
 	case IPSET_OPT_SIZE:
 		size = ipset_print_number(buf, len, data, opt, env);
 		break;
+	case IPSET_OPT_NETMASK_MASK:
+		size = ipset_print_netmask(buf, len, data, opt, env);
+		break;
 	default:
 		return -1;
 	}
diff --git a/lib/session.c b/lib/session.c
index 1bdaaa7..55eab83 100644
--- a/lib/session.c
+++ b/lib/session.c
@@ -414,6 +414,10 @@ static const struct ipset_attr_policy create_attrs[] = {
 		.type = MNL_TYPE_U32,
 		.opt = IPSET_OPT_MEMSIZE,
 	},
+	[IPSET_ATTR_NETMASK_MASK] = {
+		.type = MNL_TYPE_NESTED,
+		.opt = IPSET_OPT_NETMASK_MASK,
+	},
 };
 
 static const struct ipset_attr_policy adt_attrs[] = {
@@ -1494,6 +1498,10 @@ rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh,
 	if (attr->type == MNL_TYPE_NESTED) {
 		/* IP addresses */
 		struct nlattr *nested;
+
+		if (type == IPSET_ATTR_NETMASK_MASK)
+			family = ipset_data_family(session->data);
+
 		int atype = family == NFPROTO_IPV4 ? IPSET_ATTR_IPADDR_IPV4
 					      : IPSET_ATTR_IPADDR_IPV6;
 
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux