]> git.ipfire.org Git - thirdparty/ipset.git/commitdiff
netfilter: ipset: Add support for new bitmask parameter
authorVishwanath Pai <vpai@akamai.com>
Thu, 10 Nov 2022 21:31:26 +0000 (16:31 -0500)
committerJozsef Kadlecsik <kadlec@netfilter.org>
Sun, 20 Nov 2022 20:56:15 +0000 (21:56 +0100)
Add a new parameter to complement the existing 'netmask' option. The
main difference between netmask and bitmask is that bitmask takes any
arbitrary ip address as input, it does not have to be a valid netmask.

The name of the new parameter is 'bitmask'. This lets us mask out
arbitrary bits in the ip address, for example:
ipset create set1 hash:ip bitmask 255.128.255.0
ipset create set2 hash:ip,port family inet6 bitmask ffff::ff80

Signed-off-by: Vishwanath Pai <vpai@akamai.com>
Signed-off-by: Joshua Hunt <johunt@akamai.com>
Signed-off-by: Jozsef Kadlecsik <kadlec@netfilter.org>
include/libipset/args.h
include/libipset/data.h
include/libipset/linux_ip_set.h
include/libipset/parse.h
lib/args.c
lib/data.c
lib/debug.c
lib/errcode.c
lib/parse.c
lib/print.c
lib/session.c

index ef861c178656bd1418f2b11241069bbcb483b061..a549e4276c0480bdc866df94ddafe600903f1223 100644 (file)
@@ -58,6 +58,7 @@ enum ipset_keywords {
        IPSET_ARG_SKBQUEUE,                     /* skbqueue */
        IPSET_ARG_BUCKETSIZE,                   /* bucketsize */
        IPSET_ARG_INITVAL,                      /* initval */
+       IPSET_ARG_BITMASK,                      /* bitmask */
        IPSET_ARG_MAX,
 };
 
index 0e33c67ca45dc704926b022881471f6e0e4474f3..afaf18c788fcb5e06644c3493fc2d9654083b2ae 100644 (file)
@@ -37,6 +37,7 @@ enum ipset_opt {
        IPSET_OPT_RESIZE,
        IPSET_OPT_SIZE,
        IPSET_OPT_FORCEADD,
+       IPSET_OPT_BITMASK,
        /* Create-specific options, filled out by the kernel */
        IPSET_OPT_ELEMENTS,
        IPSET_OPT_REFERENCES,
@@ -70,7 +71,7 @@ enum ipset_opt {
        IPSET_OPT_BUCKETSIZE,
        IPSET_OPT_INITVAL,
        /* Internal options */
-       IPSET_OPT_FLAGS = 48,   /* IPSET_FLAG_EXIST| */
+       IPSET_OPT_FLAGS = 49,   /* IPSET_FLAG_EXIST| */
        IPSET_OPT_CADT_FLAGS,   /* IPSET_FLAG_BEFORE| */
        IPSET_OPT_ELEM,
        IPSET_OPT_TYPE,
@@ -105,7 +106,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_BITMASK))
 
 #define IPSET_ADT_FLAGS                        \
        (IPSET_FLAG(IPSET_OPT_IP)       \
index 1852636f4730a60396182868ac90e987010b6e1b..4e32a509d7b1a579a3769be076c75139af78e43f 100644 (file)
@@ -89,6 +89,7 @@ enum {
        IPSET_ATTR_CADT_LINENO = IPSET_ATTR_LINENO,     /* 9 */
        IPSET_ATTR_MARK,        /* 10 */
        IPSET_ATTR_MARKMASK,    /* 11 */
+       IPSET_ATTR_BITMASK,     /* 12 */
        /* Reserve empty slots */
        IPSET_ATTR_CADT_MAX = 16,
        /* Create-only specific attributes */
@@ -157,6 +158,7 @@ enum ipset_errno {
        IPSET_ERR_COMMENT,
        IPSET_ERR_INVALID_MARKMASK,
        IPSET_ERR_SKBINFO,
+       IPSET_ERR_BITMASK_NETMASK_EXCL,
 
        /* Type specific error codes */
        IPSET_ERR_TYPE_SPECIFIC = 4352,
index 3fa912904480e7f2f8a4827d1cc3ca6cd29df708..0123d4b2c39e1b1eb67f428485e4e14c1f292b02 100644 (file)
@@ -92,6 +92,8 @@ extern int ipset_parse_uint8(struct ipset_session *session,
                             enum ipset_opt opt, const char *str);
 extern int ipset_parse_netmask(struct ipset_session *session,
                               enum ipset_opt opt, const char *str);
+extern int ipset_parse_bitmask(struct ipset_session *session,
+                              enum ipset_opt opt, const char *str);
 extern int ipset_parse_flag(struct ipset_session *session,
                            enum ipset_opt opt, const char *str);
 extern int ipset_parse_typename(struct ipset_session *session,
index bab3b13f064e17070660e4bbb775e188d4aa6bb7..e47105c92630b65006309c711e88a81623924d61 100644 (file)
@@ -300,6 +300,14 @@ static const struct ipset_arg ipset_args[] = {
                .print = ipset_print_hexnumber,
                .help = "[initval VALUE]",
        },
+       [IPSET_ARG_BITMASK] = {
+               .name = { "bitmask", NULL },
+               .has_arg = IPSET_MANDATORY_ARG,
+               .opt = IPSET_OPT_BITMASK,
+               .parse = ipset_parse_bitmask,
+               .print = ipset_print_ip,
+               .help = "[bitmask bitmask]",
+       },
 };
 
 const struct ipset_arg *
index 77201787589b0fac861fdc315e28baa060424c27..72f1330a8205d84c2a37292de34dd081702a6dbd 100644 (file)
@@ -53,6 +53,7 @@ struct ipset_data {
                        uint8_t bucketsize;
                        uint8_t resize;
                        uint8_t netmask;
+                       union nf_inet_addr bitmask;
                        uint32_t hashsize;
                        uint32_t maxelem;
                        uint32_t markmask;
@@ -301,6 +302,12 @@ ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
        case IPSET_OPT_NETMASK:
                data->create.netmask = *(const uint8_t *) value;
                break;
+       case IPSET_OPT_BITMASK:
+               if (!(data->family == NFPROTO_IPV4 ||
+                     data->family == NFPROTO_IPV6))
+                       return -1;
+               copy_addr(data->family, &data->create.bitmask, value);
+               break;
        case IPSET_OPT_BUCKETSIZE:
                data->create.bucketsize = *(const uint8_t *) value;
                break;
@@ -508,6 +515,8 @@ ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
                return &data->create.markmask;
        case IPSET_OPT_NETMASK:
                return &data->create.netmask;
+       case IPSET_OPT_BITMASK:
+               return &data->create.bitmask;
        case IPSET_OPT_BUCKETSIZE:
                return &data->create.bucketsize;
        case IPSET_OPT_RESIZE:
@@ -594,6 +603,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_BITMASK:
                return family == NFPROTO_IPV4 ? sizeof(uint32_t)
                                         : sizeof(struct in6_addr);
        case IPSET_OPT_MARK:
index bf57a415eafe586944ae2f1e9acc4e097e1b8eae..dbc5cfb34e74d7b133ac09cf617dbb0e6c0d4dc7 100644 (file)
@@ -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_BITMASK]    = { .name = "BITMASK" },
        [IPSET_ATTR_BUCKETSIZE] = { .name = "BUCKETSIZE" },
        [IPSET_ATTR_RESIZE]     = { .name = "RESIZE" },
        [IPSET_ATTR_SIZE]       = { .name = "SIZE" },
index 76bab742725d8f4f11656ab0ef23e6388bf10f75..49c97a1fa0d44853ac5d34e0d8480051c8d729ee 100644 (file)
@@ -44,6 +44,8 @@ static const struct ipset_errcode_table core_errcode_table[] = {
          "The value of the markmask parameter is invalid" },
        { IPSET_ERR_INVALID_FAMILY, 0,
          "Protocol family not supported by the set type" },
+       { IPSET_ERR_BITMASK_NETMASK_EXCL, 0,
+         "netmask and bitmask options are mutually exclusive, provide only one" },
 
        /* DESTROY specific error codes */
        { IPSET_ERR_BUSY, IPSET_CMD_DESTROY,
index 974eaf868daeecfe2a1ef6cc34ae9864293bb872..48d71bee4c0cbe1a423639ce7d180135ddf9077b 100644 (file)
@@ -1703,6 +1703,9 @@ ipset_parse_netmask(struct ipset_session *session,
        assert(str);
 
        data = ipset_session_data(session);
+       if (ipset_data_test(data, IPSET_OPT_BITMASK))
+               return syntax_err("bitmask and netmask are mutually exclusive, provide only one");
+
        family = ipset_data_family(data);
        if (family == NFPROTO_UNSPEC) {
                family = NFPROTO_IPV4;
@@ -1721,6 +1724,46 @@ ipset_parse_netmask(struct ipset_session *session,
        return ipset_data_set(data, opt, &cidr);
 }
 
+/**
+ * ipset_parse_bitmask - parse string as a bitmask
+ * @session: session structure
+ * @opt: option kind of the data
+ * @str: string to parse
+ *
+ * Parse string as a bitmask value, depending on family type.
+ * If family is not set yet, INET is assumed.
+ * The value is stored in the data blob of the session.
+ *
+ * Returns 0 on success or a negative error code.
+ */
+int
+ipset_parse_bitmask(struct ipset_session *session,
+                   enum ipset_opt opt, const char *str)
+{
+       uint8_t family;
+       struct ipset_data *data;
+
+       assert(session);
+       assert(opt == IPSET_OPT_BITMASK);
+       assert(str);
+
+       data = ipset_session_data(session);
+       if (ipset_data_test(data, IPSET_OPT_NETMASK))
+               return syntax_err("bitmask and netmask are mutually exclusive, provide only one");
+
+       family = ipset_data_family(data);
+       if (family == NFPROTO_UNSPEC) {
+               family = NFPROTO_IPV4;
+               ipset_data_set(data, IPSET_OPT_FAMILY, &family);
+       }
+
+       if (parse_ipaddr(session, opt, str, family))
+               return syntax_err("bitmask is not valid for family = %s",
+                                 family == NFPROTO_IPV4 ? "inet" : "inet6");
+
+       return 0;
+}
+
 /**
  * ipset_parse_flag - "parse" option flags
  * @session: session structure
index a7ffd810bb1cb5046d0e94687ffe67a5313859e1..50f0ad6e4aaa7a9b97e7873de5f9a322e2eee078 100644 (file)
@@ -265,7 +265,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_BITMASK);
 
        D("len: %u", len);
        family = ipset_data_family(data);
@@ -976,6 +976,7 @@ ipset_print_data(char *buf, unsigned int len,
                size = ipset_print_elem(buf, len, data, opt, env);
                break;
        case IPSET_OPT_IP:
+       case IPSET_OPT_BITMASK:
                size = ipset_print_ip(buf, len, data, opt, env);
                break;
        case IPSET_OPT_PORT:
index 1ca26ffda8c1b4134f78d619d4891693927a5a1e..cdc59e0f25c6cf6da6c766052b4154eb76616a99 100644 (file)
@@ -462,6 +462,10 @@ static const struct ipset_attr_policy create_attrs[] = {
                .type = MNL_TYPE_U32,
                .opt = IPSET_OPT_MEMSIZE,
        },
+       [IPSET_ATTR_BITMASK] = {
+               .type = MNL_TYPE_NESTED,
+               .opt = IPSET_OPT_BITMASK,
+       },
 };
 
 static const struct ipset_attr_policy adt_attrs[] = {
@@ -1721,6 +1725,10 @@ rawdata2attr(struct ipset_session *session, struct nlmsghdr *nlh,
        if (attr->type == MNL_TYPE_NESTED) {
                /* IP addresses */
                struct nlattr *nested;
+
+               if (type == IPSET_ATTR_BITMASK)
+                       family = ipset_data_family(session->data);
+
                int atype = family == NFPROTO_IPV4 ? IPSET_ATTR_IPADDR_IPV4
                                              : IPSET_ATTR_IPADDR_IPV6;