diff mbox

[2/4] netfilter: ipset: generalize netmask to support cidr and mask values

Message ID 1490120944-1770-3-git-send-email-johunt@akamai.com
State Changes Requested
Delegated to: Jozsef Kadlecsik
Headers show

Commit Message

Josh Hunt March 21, 2017, 6:29 p.m. UTC
Extends ipset netmask support to handle both cidr values and full
netmasks. As part of that it now supports wildcard masks allowing the
user to mask out any bits of an address. This commit provides the
infrastructure to specify netmasks of these types for hash sets of
both v4 and v6 addressees.

Follow on commits will add support for this type of netmasking to various
hash set types.

Signed-off-by: Josh Hunt <johunt@akamai.com>
---
 include/linux/netfilter/ipset/ip_set.h      |  3 +
 include/uapi/linux/netfilter/ipset/ip_set.h |  5 ++
 net/netfilter/ipset/ip_set_core.c           |  2 +
 net/netfilter/ipset/ip_set_hash_gen.h       | 91 +++++++++++++++++++++++++----
 4 files changed, 89 insertions(+), 12 deletions(-)

Comments

kernel test robot March 24, 2017, 10:58 a.m. UTC | #1
Hi Josh,

[auto build test ERROR on nf-next/master]
[also build test ERROR on v4.11-rc3 next-20170324]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Josh-Hunt/netfilter-ipset-Extend-netmask-support-for-kernel/20170324-170517
base:   https://git.kernel.org/pub/scm/linux/kernel/git/pablo/nf-next.git master
config: x86_64-rhel (attached as .config)
compiler: gcc-6 (Debian 6.2.0-3) 6.2.0 20160901
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

Note: the linux-review/Josh-Hunt/netfilter-ipset-Extend-netmask-support-for-kernel/20170324-170517 HEAD 1311d08039e3713214109b1d179d4aab64c4ceed builds fine.
      It only hurts bisectibility.

All errors (new ones prefixed by >>):

   net/netfilter/ipset/ip_set_hash_ip.c: In function 'hash_ip4_kadt':
>> net/netfilter/ipset/ip_set_hash_ip.c:92:23: error: incompatible type for argument 1 of 'ip_set_netmask'
     ip &= ip_set_netmask(h->netmask);
                          ^
   In file included from net/netfilter/ipset/ip_set_hash_ip.c:22:0:
   include/linux/netfilter/ipset/pfxlen.h:13:1: note: expected 'u8 {aka unsigned char}' but argument is of type 'const struct ipset_netmask'
    ip_set_netmask(u8 pfxlen)
    ^~~~~~~~~~~~~~
   net/netfilter/ipset/ip_set_hash_ip.c: In function 'hash_ip4_uadt':
>> net/netfilter/ipset/ip_set_hash_ip.c:125:24: error: incompatible type for argument 1 of 'ip_set_hostmask'
     ip &= ip_set_hostmask(h->netmask);
                           ^
   In file included from net/netfilter/ipset/ip_set_hash_ip.c:22:0:
   include/linux/netfilter/ipset/pfxlen.h:25:1: note: expected 'u8 {aka unsigned char}' but argument is of type 'const struct ipset_netmask'
    ip_set_hostmask(u8 pfxlen)
    ^~~~~~~~~~~~~~~
>> net/netfilter/ipset/ip_set_hash_ip.c:149:21: error: invalid operands to binary == (have 'const struct ipset_netmask' and 'int')
     hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
             ~~~~~~~~~~ ^~
>> net/netfilter/ipset/ip_set_hash_ip.c:149:42: error: invalid operands to binary - (have 'int' and 'const struct ipset_netmask')
     hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
                                             ^ ~~~~~~~~~~
   net/netfilter/ipset/ip_set_hash_ip.c: In function 'hash_ip6_kadt':
>> net/netfilter/ipset/ip_set_hash_ip.c:226:26: error: incompatible type for argument 2 of 'hash_ip6_netmask'
     hash_ip6_netmask(&e.ip, h->netmask);
                             ^
   net/netfilter/ipset/ip_set_hash_ip.c:185:1: note: expected 'u8 {aka unsigned char}' but argument is of type 'const struct ipset_netmask'
    hash_ip6_netmask(union nf_inet_addr *ip, u8 prefix)
    ^~~~~~~~~~~~~~~~
   net/netfilter/ipset/ip_set_hash_ip.c: In function 'hash_ip6_uadt':
   net/netfilter/ipset/ip_set_hash_ip.c:265:26: error: incompatible type for argument 2 of 'hash_ip6_netmask'
     hash_ip6_netmask(&e.ip, h->netmask);
                             ^
   net/netfilter/ipset/ip_set_hash_ip.c:185:1: note: expected 'u8 {aka unsigned char}' but argument is of type 'const struct ipset_netmask'
    hash_ip6_netmask(union nf_inet_addr *ip, u8 prefix)
    ^~~~~~~~~~~~~~~~

vim +149 net/netfilter/ipset/ip_set_hash_ip.c

6c0278896 Jozsef Kadlecsik             2011-02-01   16  #include <net/ip.h>
6c0278896 Jozsef Kadlecsik             2011-02-01   17  #include <net/ipv6.h>
6c0278896 Jozsef Kadlecsik             2011-02-01   18  #include <net/netlink.h>
6c0278896 Jozsef Kadlecsik             2011-02-01   19  #include <net/tcp.h>
6c0278896 Jozsef Kadlecsik             2011-02-01   20  
6c0278896 Jozsef Kadlecsik             2011-02-01   21  #include <linux/netfilter.h>
6c0278896 Jozsef Kadlecsik             2011-02-01  @22  #include <linux/netfilter/ipset/pfxlen.h>
6c0278896 Jozsef Kadlecsik             2011-02-01   23  #include <linux/netfilter/ipset/ip_set.h>
6c0278896 Jozsef Kadlecsik             2011-02-01   24  #include <linux/netfilter/ipset/ip_set_hash.h>
6c0278896 Jozsef Kadlecsik             2011-02-01   25  
35b8dcf8c Jozsef Kadlecsik             2013-04-30   26  #define IPSET_TYPE_REV_MIN	0
fda75c6d9 Oliver Smith                 2013-09-22   27  /*				1	   Counters support */
07cf8f5ae Josh Hunt                    2014-02-28   28  /*				2	   Comments support */
af331419d Anton Danilov                2014-08-28   29  /*				3	   Forceadd support */
af331419d Anton Danilov                2014-08-28   30  #define IPSET_TYPE_REV_MAX	4	/* skbinfo support  */
10111a6ef Jozsef Kadlecsik             2012-09-21   31  
6c0278896 Jozsef Kadlecsik             2011-02-01   32  MODULE_LICENSE("GPL");
6c0278896 Jozsef Kadlecsik             2011-02-01   33  MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>");
35b8dcf8c Jozsef Kadlecsik             2013-04-30   34  IP_SET_MODULE_DESC("hash:ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX);
6c0278896 Jozsef Kadlecsik             2011-02-01   35  MODULE_ALIAS("ip_set_hash:ip");
6c0278896 Jozsef Kadlecsik             2011-02-01   36  
6c0278896 Jozsef Kadlecsik             2011-02-01   37  /* Type specific function prefix */
5d50e1d88 Jozsef Kadlecsik             2013-04-08   38  #define HTYPE		hash_ip
5d50e1d88 Jozsef Kadlecsik             2013-04-08   39  #define IP_SET_HASH_WITH_NETMASK
6c0278896 Jozsef Kadlecsik             2011-02-01   40  
03c8b234e Jozsef Kadlecsik             2013-09-07   41  /* IPv4 variant */
6c0278896 Jozsef Kadlecsik             2011-02-01   42  
5d50e1d88 Jozsef Kadlecsik             2013-04-08   43  /* Member elements */
6c0278896 Jozsef Kadlecsik             2011-02-01   44  struct hash_ip4_elem {
5d50e1d88 Jozsef Kadlecsik             2013-04-08   45  	/* Zero valued IP addresses cannot be stored */
6c0278896 Jozsef Kadlecsik             2011-02-01   46  	__be32 ip;
6c0278896 Jozsef Kadlecsik             2011-02-01   47  };
6c0278896 Jozsef Kadlecsik             2011-02-01   48  
5d50e1d88 Jozsef Kadlecsik             2013-04-08   49  /* Common functions */
6c0278896 Jozsef Kadlecsik             2011-02-01   50  
6c0278896 Jozsef Kadlecsik             2011-02-01   51  static inline bool
5d50e1d88 Jozsef Kadlecsik             2013-04-08   52  hash_ip4_data_equal(const struct hash_ip4_elem *e1,
5d50e1d88 Jozsef Kadlecsik             2013-04-08   53  		    const struct hash_ip4_elem *e2,
5d50e1d88 Jozsef Kadlecsik             2013-04-08   54  		    u32 *multi)
6c0278896 Jozsef Kadlecsik             2011-02-01   55  {
5d50e1d88 Jozsef Kadlecsik             2013-04-08   56  	return e1->ip == e2->ip;
6c0278896 Jozsef Kadlecsik             2011-02-01   57  }
6c0278896 Jozsef Kadlecsik             2011-02-01   58  
728a7e690 Sergey Popovich              2015-05-02   59  static bool
5d50e1d88 Jozsef Kadlecsik             2013-04-08   60  hash_ip4_data_list(struct sk_buff *skb, const struct hash_ip4_elem *e)
6c0278896 Jozsef Kadlecsik             2011-02-01   61  {
5d50e1d88 Jozsef Kadlecsik             2013-04-08   62  	if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, e->ip))
7cf7899d9 David S. Miller              2012-04-01   63  		goto nla_put_failure;
728a7e690 Sergey Popovich              2015-05-02   64  	return false;
6c0278896 Jozsef Kadlecsik             2011-02-01   65  
6c0278896 Jozsef Kadlecsik             2011-02-01   66  nla_put_failure:
728a7e690 Sergey Popovich              2015-05-02   67  	return true;
6c0278896 Jozsef Kadlecsik             2011-02-01   68  }
6c0278896 Jozsef Kadlecsik             2011-02-01   69  
5d50e1d88 Jozsef Kadlecsik             2013-04-08   70  static inline void
5d50e1d88 Jozsef Kadlecsik             2013-04-08   71  hash_ip4_data_next(struct hash_ip4_elem *next, const struct hash_ip4_elem *e)
6c0278896 Jozsef Kadlecsik             2011-02-01   72  {
5d50e1d88 Jozsef Kadlecsik             2013-04-08   73  	next->ip = e->ip;
6c0278896 Jozsef Kadlecsik             2011-02-01   74  }
6c0278896 Jozsef Kadlecsik             2011-02-01   75  
5d50e1d88 Jozsef Kadlecsik             2013-04-08   76  #define MTYPE		hash_ip4
6c0278896 Jozsef Kadlecsik             2011-02-01   77  #define HOST_MASK	32
5d50e1d88 Jozsef Kadlecsik             2013-04-08   78  #include "ip_set_hash_gen.h"
3d14b171f Jozsef Kadlecsik             2011-06-16   79  
6c0278896 Jozsef Kadlecsik             2011-02-01   80  static int
6c0278896 Jozsef Kadlecsik             2011-02-01   81  hash_ip4_kadt(struct ip_set *set, const struct sk_buff *skb,
b66554cf0 Jozsef Kadlecsik             2011-06-16   82  	      const struct xt_action_param *par,
5d50e1d88 Jozsef Kadlecsik             2013-04-08   83  	      enum ipset_adt adt, struct ip_set_adt_opt *opt)
6c0278896 Jozsef Kadlecsik             2011-02-01   84  {
21956ab29 Jozsef Kadlecsik             2015-06-26   85  	const struct hash_ip4 *h = set->data;
6c0278896 Jozsef Kadlecsik             2011-02-01   86  	ipset_adtfn adtfn = set->variant->adt[adt];
94729f8a1 Mark Rustad                  2014-08-05   87  	struct hash_ip4_elem e = { 0 };
ca134ce86 Jozsef Kadlecsik             2013-09-07   88  	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
6c0278896 Jozsef Kadlecsik             2011-02-01   89  	__be32 ip;
6c0278896 Jozsef Kadlecsik             2011-02-01   90  
ac8cc925d Jozsef Kadlecsik             2011-06-16   91  	ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &ip);
6c0278896 Jozsef Kadlecsik             2011-02-01  @92  	ip &= ip_set_netmask(h->netmask);
6c0278896 Jozsef Kadlecsik             2011-02-01   93  	if (ip == 0)
6c0278896 Jozsef Kadlecsik             2011-02-01   94  		return -EINVAL;
6c0278896 Jozsef Kadlecsik             2011-02-01   95  
5d50e1d88 Jozsef Kadlecsik             2013-04-08   96  	e.ip = ip;
5d50e1d88 Jozsef Kadlecsik             2013-04-08   97  	return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
6c0278896 Jozsef Kadlecsik             2011-02-01   98  }
6c0278896 Jozsef Kadlecsik             2011-02-01   99  
6c0278896 Jozsef Kadlecsik             2011-02-01  100  static int
6c0278896 Jozsef Kadlecsik             2011-02-01  101  hash_ip4_uadt(struct ip_set *set, struct nlattr *tb[],
3d14b171f Jozsef Kadlecsik             2011-06-16  102  	      enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
6c0278896 Jozsef Kadlecsik             2011-02-01  103  {
21956ab29 Jozsef Kadlecsik             2015-06-26  104  	const struct hash_ip4 *h = set->data;
6c0278896 Jozsef Kadlecsik             2011-02-01  105  	ipset_adtfn adtfn = set->variant->adt[adt];
94729f8a1 Mark Rustad                  2014-08-05  106  	struct hash_ip4_elem e = { 0 };
ca134ce86 Jozsef Kadlecsik             2013-09-07  107  	struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
20b2fab48 Jozsef Kadlecsik             2013-05-01  108  	u32 ip = 0, ip_to = 0, hosts;
6c0278896 Jozsef Kadlecsik             2011-02-01  109  	int ret = 0;
6c0278896 Jozsef Kadlecsik             2011-02-01  110  
6c0278896 Jozsef Kadlecsik             2011-02-01  111  	if (tb[IPSET_ATTR_LINENO])
6c0278896 Jozsef Kadlecsik             2011-02-01  112  		*lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
6c0278896 Jozsef Kadlecsik             2011-02-01  113  
a212e08e8 Sergey Popovich              2015-06-12  114  	if (unlikely(!tb[IPSET_ATTR_IP]))
a212e08e8 Sergey Popovich              2015-06-12  115  		return -IPSET_ERR_PROTOCOL;
a212e08e8 Sergey Popovich              2015-06-12  116  
8e55d2e59 Sergey Popovich              2015-05-02  117  	ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip);
8e55d2e59 Sergey Popovich              2015-05-02  118  	if (ret)
8e55d2e59 Sergey Popovich              2015-05-02  119  		return ret;
8e55d2e59 Sergey Popovich              2015-05-02  120  
8e55d2e59 Sergey Popovich              2015-05-02  121  	ret = ip_set_get_extensions(set, tb, &ext);
6c0278896 Jozsef Kadlecsik             2011-02-01  122  	if (ret)
6c0278896 Jozsef Kadlecsik             2011-02-01  123  		return ret;
6c0278896 Jozsef Kadlecsik             2011-02-01  124  
6c0278896 Jozsef Kadlecsik             2011-02-01 @125  	ip &= ip_set_hostmask(h->netmask);
6c0278896 Jozsef Kadlecsik             2011-02-01  126  
6c0278896 Jozsef Kadlecsik             2011-02-01  127  	if (adt == IPSET_TEST) {
5d50e1d88 Jozsef Kadlecsik             2013-04-08  128  		e.ip = htonl(ip);
5d50e1d88 Jozsef Kadlecsik             2013-04-08  129  		if (e.ip == 0)
6c0278896 Jozsef Kadlecsik             2011-02-01  130  			return -IPSET_ERR_HASH_ELEM;
5d50e1d88 Jozsef Kadlecsik             2013-04-08  131  		return adtfn(set, &e, &ext, &ext, flags);
6c0278896 Jozsef Kadlecsik             2011-02-01  132  	}
6c0278896 Jozsef Kadlecsik             2011-02-01  133  
4fe198e6b Jozsef Kadlecsik             2012-11-19  134  	ip_to = ip;
6c0278896 Jozsef Kadlecsik             2011-02-01  135  	if (tb[IPSET_ATTR_IP_TO]) {
6c0278896 Jozsef Kadlecsik             2011-02-01  136  		ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
6c0278896 Jozsef Kadlecsik             2011-02-01  137  		if (ret)
6c0278896 Jozsef Kadlecsik             2011-02-01  138  			return ret;
6c0278896 Jozsef Kadlecsik             2011-02-01  139  		if (ip > ip_to)
6c0278896 Jozsef Kadlecsik             2011-02-01  140  			swap(ip, ip_to);
6c0278896 Jozsef Kadlecsik             2011-02-01  141  	} else if (tb[IPSET_ATTR_CIDR]) {
6c0278896 Jozsef Kadlecsik             2011-02-01  142  		u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
6c0278896 Jozsef Kadlecsik             2011-02-01  143  
cabfd139a Sergey Popovich              2015-05-02  144  		if (!cidr || cidr > HOST_MASK)
6c0278896 Jozsef Kadlecsik             2011-02-01  145  			return -IPSET_ERR_INVALID_CIDR;
e6146e868 Jozsef Kadlecsik             2011-06-16  146  		ip_set_mask_from_to(ip, ip_to, cidr);
4fe198e6b Jozsef Kadlecsik             2012-11-19  147  	}
6c0278896 Jozsef Kadlecsik             2011-02-01  148  
6c0278896 Jozsef Kadlecsik             2011-02-01 @149  	hosts = h->netmask == 32 ? 1 : 2 << (32 - h->netmask - 1);
6c0278896 Jozsef Kadlecsik             2011-02-01  150  
3d14b171f Jozsef Kadlecsik             2011-06-16  151  	if (retried)
6e27c9b4e Jozsef Kadlecsik             2012-09-21  152  		ip = ntohl(h->next.ip);
6c0278896 Jozsef Kadlecsik             2011-02-01  153  	for (; !before(ip_to, ip); ip += hosts) {
5d50e1d88 Jozsef Kadlecsik             2013-04-08  154  		e.ip = htonl(ip);
5d50e1d88 Jozsef Kadlecsik             2013-04-08  155  		if (e.ip == 0)
6c0278896 Jozsef Kadlecsik             2011-02-01  156  			return -IPSET_ERR_HASH_ELEM;
5d50e1d88 Jozsef Kadlecsik             2013-04-08  157  		ret = adtfn(set, &e, &ext, &ext, flags);
6c0278896 Jozsef Kadlecsik             2011-02-01  158  
6c0278896 Jozsef Kadlecsik             2011-02-01  159  		if (ret && !ip_set_eexist(ret, flags))
6c0278896 Jozsef Kadlecsik             2011-02-01  160  			return ret;
ca0f6a5cd Jozsef Kadlecsik             2015-06-13  161  
6c0278896 Jozsef Kadlecsik             2011-02-01  162  		ret = 0;
6c0278896 Jozsef Kadlecsik             2011-02-01  163  	}
6c0278896 Jozsef Kadlecsik             2011-02-01  164  	return ret;
6c0278896 Jozsef Kadlecsik             2011-02-01  165  }
6c0278896 Jozsef Kadlecsik             2011-02-01  166  
03c8b234e Jozsef Kadlecsik             2013-09-07  167  /* IPv6 variant */
6c0278896 Jozsef Kadlecsik             2011-02-01  168  
5d50e1d88 Jozsef Kadlecsik             2013-04-08  169  /* Member elements */
6c0278896 Jozsef Kadlecsik             2011-02-01  170  struct hash_ip6_elem {
6c0278896 Jozsef Kadlecsik             2011-02-01  171  	union nf_inet_addr ip;
6c0278896 Jozsef Kadlecsik             2011-02-01  172  };
6c0278896 Jozsef Kadlecsik             2011-02-01  173  
5d50e1d88 Jozsef Kadlecsik             2013-04-08  174  /* Common functions */
5d50e1d88 Jozsef Kadlecsik             2013-04-08  175  
6c0278896 Jozsef Kadlecsik             2011-02-01  176  static inline bool
6c0278896 Jozsef Kadlecsik             2011-02-01  177  hash_ip6_data_equal(const struct hash_ip6_elem *ip1,
89dc79b78 Jozsef Kadlecsik             2011-07-21  178  		    const struct hash_ip6_elem *ip2,
89dc79b78 Jozsef Kadlecsik             2011-07-21  179  		    u32 *multi)
6c0278896 Jozsef Kadlecsik             2011-02-01  180  {
29e3b1608 YOSHIFUJI Hideaki / 吉藤英明 2013-01-29  181  	return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6);
6c0278896 Jozsef Kadlecsik             2011-02-01  182  }
6c0278896 Jozsef Kadlecsik             2011-02-01  183  
6c0278896 Jozsef Kadlecsik             2011-02-01  184  static inline void
5d50e1d88 Jozsef Kadlecsik             2013-04-08  185  hash_ip6_netmask(union nf_inet_addr *ip, u8 prefix)
6c0278896 Jozsef Kadlecsik             2011-02-01  186  {
5d50e1d88 Jozsef Kadlecsik             2013-04-08  187  	ip6_netmask(ip, prefix);
6c0278896 Jozsef Kadlecsik             2011-02-01  188  }
6c0278896 Jozsef Kadlecsik             2011-02-01  189  
6c0278896 Jozsef Kadlecsik             2011-02-01  190  static bool
5d50e1d88 Jozsef Kadlecsik             2013-04-08  191  hash_ip6_data_list(struct sk_buff *skb, const struct hash_ip6_elem *e)
6c0278896 Jozsef Kadlecsik             2011-02-01  192  {
5d50e1d88 Jozsef Kadlecsik             2013-04-08  193  	if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &e->ip.in6))
7cf7899d9 David S. Miller              2012-04-01  194  		goto nla_put_failure;
728a7e690 Sergey Popovich              2015-05-02  195  	return false;
6c0278896 Jozsef Kadlecsik             2011-02-01  196  
6c0278896 Jozsef Kadlecsik             2011-02-01  197  nla_put_failure:
728a7e690 Sergey Popovich              2015-05-02  198  	return true;
6c0278896 Jozsef Kadlecsik             2011-02-01  199  }
6c0278896 Jozsef Kadlecsik             2011-02-01  200  
5d50e1d88 Jozsef Kadlecsik             2013-04-08  201  static inline void
21956ab29 Jozsef Kadlecsik             2015-06-26  202  hash_ip6_data_next(struct hash_ip6_elem *next, const struct hash_ip6_elem *e)
6c0278896 Jozsef Kadlecsik             2011-02-01  203  {
6c0278896 Jozsef Kadlecsik             2011-02-01  204  }
6c0278896 Jozsef Kadlecsik             2011-02-01  205  
5d50e1d88 Jozsef Kadlecsik             2013-04-08  206  #undef MTYPE
6c0278896 Jozsef Kadlecsik             2011-02-01  207  #undef HOST_MASK
6c0278896 Jozsef Kadlecsik             2011-02-01  208  
5d50e1d88 Jozsef Kadlecsik             2013-04-08  209  #define MTYPE		hash_ip6
6c0278896 Jozsef Kadlecsik             2011-02-01  210  #define HOST_MASK	128
6c0278896 Jozsef Kadlecsik             2011-02-01  211  
5d50e1d88 Jozsef Kadlecsik             2013-04-08  212  #define IP_SET_EMIT_CREATE
5d50e1d88 Jozsef Kadlecsik             2013-04-08  213  #include "ip_set_hash_gen.h"
3d14b171f Jozsef Kadlecsik             2011-06-16  214  
6c0278896 Jozsef Kadlecsik             2011-02-01  215  static int
6c0278896 Jozsef Kadlecsik             2011-02-01  216  hash_ip6_kadt(struct ip_set *set, const struct sk_buff *skb,
b66554cf0 Jozsef Kadlecsik             2011-06-16  217  	      const struct xt_action_param *par,
5d50e1d88 Jozsef Kadlecsik             2013-04-08  218  	      enum ipset_adt adt, struct ip_set_adt_opt *opt)
6c0278896 Jozsef Kadlecsik             2011-02-01  219  {
21956ab29 Jozsef Kadlecsik             2015-06-26  220  	const struct hash_ip6 *h = set->data;
6c0278896 Jozsef Kadlecsik             2011-02-01  221  	ipset_adtfn adtfn = set->variant->adt[adt];
94729f8a1 Mark Rustad                  2014-08-05  222  	struct hash_ip6_elem e = { { .all = { 0 } } };
ca134ce86 Jozsef Kadlecsik             2013-09-07  223  	struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
6c0278896 Jozsef Kadlecsik             2011-02-01  224  
5d50e1d88 Jozsef Kadlecsik             2013-04-08  225  	ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
5d50e1d88 Jozsef Kadlecsik             2013-04-08 @226  	hash_ip6_netmask(&e.ip, h->netmask);
5d50e1d88 Jozsef Kadlecsik             2013-04-08  227  	if (ipv6_addr_any(&e.ip.in6))
6c0278896 Jozsef Kadlecsik             2011-02-01  228  		return -EINVAL;
6c0278896 Jozsef Kadlecsik             2011-02-01  229  

:::::: The code at line 149 was first introduced by commit
:::::: 6c027889696a7a694b0e2f6e3cabadefec7553b6 netfilter: ipset: hash:ip set type support

:::::: TO: Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>
:::::: CC: Patrick McHardy <kaber@trash.net>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Jozsef Kadlecsik March 27, 2017, 7:21 p.m. UTC | #2
Hi Josh,

Overall, I like the feature and the patches. After reviewing I comment 
only the parts where I believe some modifications are needed.

On Tue, 21 Mar 2017, Josh Hunt wrote:

> Extends ipset netmask support to handle both cidr values and full
> netmasks. As part of that it now supports wildcard masks allowing the
> user to mask out any bits of an address. This commit provides the
> infrastructure to specify netmasks of these types for hash sets of
> both v4 and v6 addressees.
> 
> Follow on commits will add support for this type of netmasking to various
> hash set types.
> 
> Signed-off-by: Josh Hunt <johunt@akamai.com>
> ---
>  include/linux/netfilter/ipset/ip_set.h      |  3 +
>  include/uapi/linux/netfilter/ipset/ip_set.h |  5 ++
>  net/netfilter/ipset/ip_set_core.c           |  2 +
>  net/netfilter/ipset/ip_set_hash_gen.h       | 91 +++++++++++++++++++++++++----
>  4 files changed, 89 insertions(+), 12 deletions(-)
> 
> diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
> index 8e42253..0153cd3 100644
> --- a/include/linux/netfilter/ipset/ip_set.h
> +++ b/include/linux/netfilter/ipset/ip_set.h
> @@ -69,6 +69,7 @@ enum ip_set_extension {
>  #define SET_WITH_COMMENT(s)	((s)->extensions & IPSET_EXT_COMMENT)
>  #define SET_WITH_SKBINFO(s)	((s)->extensions & IPSET_EXT_SKBINFO)
>  #define SET_WITH_FORCEADD(s)	((s)->flags & IPSET_CREATE_FLAG_FORCEADD)
> +#define SET_WITH_NETMASK(s)	((s)->flags & IPSET_CREATE_FLAG_NETMASK)
>  
>  /* Extension id, in size order */
>  enum ip_set_ext_id {
> @@ -292,6 +293,8 @@ struct ip_set {
>  		cadt_flags |= IPSET_FLAG_WITH_SKBINFO;
>  	if (SET_WITH_FORCEADD(set))
>  		cadt_flags |= IPSET_FLAG_WITH_FORCEADD;
> +	if (SET_WITH_NETMASK(set))
> +		cadt_flags |= IPSET_FLAG_WITH_NETMASK;
>  
>  	if (!cadt_flags)
>  		return 0;
> diff --git a/include/uapi/linux/netfilter/ipset/ip_set.h b/include/uapi/linux/netfilter/ipset/ip_set.h
> index ebb5154..2193908 100644
> --- a/include/uapi/linux/netfilter/ipset/ip_set.h
> +++ b/include/uapi/linux/netfilter/ipset/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 */
> @@ -200,6 +201,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,
>  };
>  
> @@ -207,6 +210,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/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
> index c296f9b..e2ce7ab 100644
> --- a/net/netfilter/ipset/ip_set_core.c
> +++ b/net/netfilter/ipset/ip_set_core.c
> @@ -374,6 +374,8 @@ static inline struct ip_set_net *ip_set_pernet(struct net *net)
>  		cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
>  	if (cadt_flags & IPSET_FLAG_WITH_FORCEADD)
>  		set->flags |= IPSET_CREATE_FLAG_FORCEADD;
> +	if (cadt_flags & IPSET_FLAG_WITH_NETMASK)
> +		set->flags |= IPSET_CREATE_FLAG_NETMASK;
>  	if (!align)
>  		align = 1;
>  	for (id = 0; id < IPSET_EXT_ID_MAX; id++) {
> diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h
> index f236c0b..a407f85 100644
> --- a/net/netfilter/ipset/ip_set_hash_gen.h
> +++ b/net/netfilter/ipset/ip_set_hash_gen.h
> @@ -166,6 +166,41 @@ struct net_prefixes {
>  #define NLEN			0
>  #endif /* IP_SET_HASH_WITH_NETS */
>  
> +#ifdef IP_SET_HASH_WITH_NETMASK
> +const static union nf_inet_addr onesmask = {
> +	.all[0] = 0xffffffff,
> +	.all[1] = 0xffffffff,
> +	.all[2] = 0xffffffff,
> +	.all[3] = 0xffffffff
> +};
> +const static union nf_inet_addr zeromask;
> +
> +struct ipset_netmask {
> +	u8 cidr;
> +	union nf_inet_addr mask;
> +};
> +
> +static void
> +ip_set_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;
> +		}
> +	}
> +}
> +
> +#endif
> +
>  #endif /* _IP_SET_HASH_GEN_H */
>  
>  #ifndef MTYPE
> @@ -289,7 +324,7 @@ struct htype {
>  	u8 ahash_max;		/* max elements in an array block */
>  #endif
>  #ifdef IP_SET_HASH_WITH_NETMASK
> -	u8 netmask;		/* netmask value for subnets to store */
> +	struct ipset_netmask netmask;
>  #endif
>  	struct mtype_elem next; /* temporary storage for uadd */
>  #ifdef IP_SET_HASH_WITH_NETS
> @@ -449,7 +484,8 @@ struct htype {
>  	return x->maxelem == y->maxelem &&
>  	       a->timeout == b->timeout &&
>  #ifdef IP_SET_HASH_WITH_NETMASK
> -	       x->netmask == y->netmask &&
> +		nf_inet_addr_cmp(&x->netmask.mask, &y->netmask.mask) &&
> +		x->netmask.cidr == y->netmask.cidr &&
>  #endif
>  #ifdef IP_SET_HASH_WITH_MARKMASK
>  	       x->markmask == y->markmask &&
> @@ -1061,9 +1097,20 @@ struct htype {
>  	    nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)))
>  		goto nla_put_failure;
>  #ifdef IP_SET_HASH_WITH_NETMASK
> -	if (h->netmask != HOST_MASK &&
> -	    nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask))
> -		goto nla_put_failure;
> +	if (!nf_inet_addr_cmp(&onesmask, &h->netmask.mask)) {
> +		if (SET_WITH_NETMASK(set)) {
> +			if (set->family == NFPROTO_IPV4) {
> +				if (nla_put_ipaddr4(skb, IPSET_ATTR_NETMASK_MASK, h->netmask.mask.ip))
> +					goto nla_put_failure;
> +			} else if (set->family == NFPROTO_IPV6) {
> +				if (nla_put_ipaddr6(skb, IPSET_ATTR_NETMASK_MASK, &h->netmask.mask.in6))
> +					goto nla_put_failure;
> +			}
> +		}
> +
> +		if (nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask.cidr))
> +			goto nla_put_failure;
> +	}

Please do not pass (and do not process) both IPSET_ATTR_NETMASK and 
IPSET_ATTR_NETMASK_MASK. If it'd be allowed, which one should be used?
So instead of "if ..." it should be "else if" above.

>  #endif
>  #ifdef IP_SET_HASH_WITH_MARKMASK
>  	if (nla_put_u32(skb, IPSET_ATTR_MARKMASK, h->markmask))
> @@ -1220,7 +1267,9 @@ struct htype {
>  #endif
>  	u8 hbits;
>  #ifdef IP_SET_HASH_WITH_NETMASK
> -	u8 netmask;
> +	union nf_inet_addr netmask = onesmask;
> +	int ret = 0;
> +	u8 cidr = 0;
>  #endif
>  	size_t hsize;
>  	struct htype *h;
> @@ -1254,15 +1303,32 @@ struct htype {
>  #endif
>  
>  #ifdef IP_SET_HASH_WITH_NETMASK
> -	netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
>  	if (tb[IPSET_ATTR_NETMASK]) {
> -		netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
> +		cidr = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
> +		if ((set->family == NFPROTO_IPV4 && cidr > 32) ||
> +		    (set->family == NFPROTO_IPV6 && cidr > 128))
> +			return -IPSET_ERR_INVALID_NETMASK;
>  
> -		if ((set->family == NFPROTO_IPV4 && netmask > 32) ||
> -		    (set->family == NFPROTO_IPV6 && netmask > 128) ||
> -		    netmask == 0)
> +	}
> +	if (tb[IPSET_ATTR_NETMASK_MASK]) {
> +		if (set->family == NFPROTO_IPV4) {
> +			ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_NETMASK_MASK], &netmask.ip);
> +			if (ret || !netmask.ip)
> +				return -IPSET_ERR_INVALID_NETMASK;
> +		} else if (set->family == NFPROTO_IPV6) {
> +			ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_NETMASK_MASK], &netmask);
> +			if (ret || ipv6_addr_any(&netmask.in6))
> +				return -IPSET_ERR_INVALID_NETMASK;
> +		}
> +		if (nf_inet_addr_cmp(&netmask, &zeromask))
>  			return -IPSET_ERR_INVALID_NETMASK;
>  	}

Do not trust the netlink messages and if both IPSET_ATTR_NETMASK and 
IPSET_ATTR_NETMASK_MASK attributes are present, simply reject it.

> +
> +	/* For backwards compatibility, we'll convert the cidr to a mask if
> +	 * userspace didn't give us one.
> +	 */
> +	if (cidr && nf_inet_addr_cmp(&netmask, &onesmask))
> +		ip_set_cidr_to_mask(&netmask, cidr, set->family);
>  #endif
>  
>  	if (tb[IPSET_ATTR_HASHSIZE]) {
> @@ -1292,7 +1358,8 @@ struct htype {
>  	}
>  	h->maxelem = maxelem;
>  #ifdef IP_SET_HASH_WITH_NETMASK
> -	h->netmask = netmask;
> +	h->netmask.mask = netmask;
> +	h->netmask.cidr = cidr;
>  #endif
>  #ifdef IP_SET_HASH_WITH_MARKMASK
>  	h->markmask = markmask;
> -- 
> 1.9.1

Best regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.mta.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences
          H-1525 Budapest 114, POB. 49, Hungary
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Josh Hunt March 28, 2017, 12:35 p.m. UTC | #3
On 03/27/2017 02:21 PM, Jozsef Kadlecsik wrote:
> Hi Josh,
>
> Overall, I like the feature and the patches. After reviewing I comment
> only the parts where I believe some modifications are needed.
>

Jozsef

Thanks for the review. I'll go through this and send a v2 in the next 
few days.

Josh
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jozsef Kadlecsik March 28, 2017, 6:28 p.m. UTC | #4
Hi Josh,

On Tue, 28 Mar 2017, Josh Hunt wrote:

> > Overall, I like the feature and the patches. After reviewing I comment
> > only the parts where I believe some modifications are needed.
> 
> Thanks for the review. I'll go through this and send a v2 in the next 
> few days.

Thinking over it I understand better your approach: replace 
IPSET_ATTR_NETMASK with IPSET_ATTR_NETMASK_MASK completely and keeping the 
former for backward compatibility reasons only. What I propose is able to 
maintain syntax-compatibility in the sense that what is passed to the 
kernel as cidr/netmask can be saved from there in the same syntax.

The exemption from adding multiple entries from a range in the case of 
non-cidr value netmask must also be documented in the manpage. Maybe it'd 
be even better additionally to return an explicit error in such cases: 
thus it cannot lead to a misunderstanding from user point of view.

Best regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.mta.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences
          H-1525 Budapest 114, POB. 49, Hungary
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Josh Hunt March 28, 2017, 8:01 p.m. UTC | #5
On 03/28/2017 01:28 PM, Jozsef Kadlecsik wrote:
> Hi Josh,
>
> On Tue, 28 Mar 2017, Josh Hunt wrote:
>
>>> Overall, I like the feature and the patches. After reviewing I comment
>>> only the parts where I believe some modifications are needed.
>>
>> Thanks for the review. I'll go through this and send a v2 in the next
>> few days.
>
> Thinking over it I understand better your approach: replace
> IPSET_ATTR_NETMASK with IPSET_ATTR_NETMASK_MASK completely and keeping the
> former for backward compatibility reasons only. What I propose is able to
> maintain syntax-compatibility in the sense that what is passed to the
> kernel as cidr/netmask can be saved from there in the same syntax.

I have not gone through all of your comments yet, but my intention was 
to make sure that if cidrs are used they are presented the same way they 
are today (prior to my patches.) If a user uses the new full netmask 
support then that is what is shown. This way we stay consistent with 
whatever the user inputs at create time.

Examples of both:

# ipset create foo hash:ip netmask 24
# ipset list -t foo
Name: foo
Type: hash:ip
Revision: 5
Header: family inet hashsize 1024 maxelem 65536 netmask 24
Size in memory: 104
References: 0
Number of entries: 0

# ipset create foo hash:ip netmask 255.255.255.0
# ipset list -t foo
Name: foo
Type: hash:ip
Revision: 5
Header: family inet hashsize 1024 maxelem 65536 netmask 255.255.255.0
Size in memory: 104
References: 0
Number of entries: 0

Internally they both resolve to the same mask, but we present them 
consistently based on how they were input at create time.

This way if a user uses cidrs today nothing changes from their perspective.

>
> The exemption from adding multiple entries from a range in the case of
> non-cidr value netmask must also be documented in the manpage. Maybe it'd
> be even better additionally to return an explicit error in such cases:
> thus it cannot lead to a misunderstanding from user point of view.

I haven't gone through this yet, but will take a look and let you know 
if I have any questions.

Josh
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jozsef Kadlecsik March 28, 2017, 8:54 p.m. UTC | #6
On Tue, 28 Mar 2017, Josh Hunt wrote:

> > Thinking over it I understand better your approach: replace 
> > IPSET_ATTR_NETMASK with IPSET_ATTR_NETMASK_MASK completely and keeping 
> > the former for backward compatibility reasons only. What I propose is 
> > able to maintain syntax-compatibility in the sense that what is passed 
> > to the kernel as cidr/netmask can be saved from there in the same 
> > syntax.
> 
> I have not gone through all of your comments yet, but my intention was 
> to make sure that if cidrs are used they are presented the same way they 
> are today (prior to my patches.) If a user uses the new full netmask 
> support then that is what is shown. This way we stay consistent with 
> whatever the user inputs at create time.

Yes, you are right, some conversion confused me.

Best regards,
Jozsef
-
E-mail  : kadlec@blackhole.kfki.hu, kadlecsik.jozsef@wigner.mta.hu
PGP key : http://www.kfki.hu/~kadlec/pgp_public_key.txt
Address : Wigner Research Centre for Physics, Hungarian Academy of Sciences
          H-1525 Budapest 114, POB. 49, Hungary
--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/include/linux/netfilter/ipset/ip_set.h b/include/linux/netfilter/ipset/ip_set.h
index 8e42253..0153cd3 100644
--- a/include/linux/netfilter/ipset/ip_set.h
+++ b/include/linux/netfilter/ipset/ip_set.h
@@ -69,6 +69,7 @@  enum ip_set_extension {
 #define SET_WITH_COMMENT(s)	((s)->extensions & IPSET_EXT_COMMENT)
 #define SET_WITH_SKBINFO(s)	((s)->extensions & IPSET_EXT_SKBINFO)
 #define SET_WITH_FORCEADD(s)	((s)->flags & IPSET_CREATE_FLAG_FORCEADD)
+#define SET_WITH_NETMASK(s)	((s)->flags & IPSET_CREATE_FLAG_NETMASK)
 
 /* Extension id, in size order */
 enum ip_set_ext_id {
@@ -292,6 +293,8 @@  struct ip_set {
 		cadt_flags |= IPSET_FLAG_WITH_SKBINFO;
 	if (SET_WITH_FORCEADD(set))
 		cadt_flags |= IPSET_FLAG_WITH_FORCEADD;
+	if (SET_WITH_NETMASK(set))
+		cadt_flags |= IPSET_FLAG_WITH_NETMASK;
 
 	if (!cadt_flags)
 		return 0;
diff --git a/include/uapi/linux/netfilter/ipset/ip_set.h b/include/uapi/linux/netfilter/ipset/ip_set.h
index ebb5154..2193908 100644
--- a/include/uapi/linux/netfilter/ipset/ip_set.h
+++ b/include/uapi/linux/netfilter/ipset/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 */
@@ -200,6 +201,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,
 };
 
@@ -207,6 +210,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/net/netfilter/ipset/ip_set_core.c b/net/netfilter/ipset/ip_set_core.c
index c296f9b..e2ce7ab 100644
--- a/net/netfilter/ipset/ip_set_core.c
+++ b/net/netfilter/ipset/ip_set_core.c
@@ -374,6 +374,8 @@  static inline struct ip_set_net *ip_set_pernet(struct net *net)
 		cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]);
 	if (cadt_flags & IPSET_FLAG_WITH_FORCEADD)
 		set->flags |= IPSET_CREATE_FLAG_FORCEADD;
+	if (cadt_flags & IPSET_FLAG_WITH_NETMASK)
+		set->flags |= IPSET_CREATE_FLAG_NETMASK;
 	if (!align)
 		align = 1;
 	for (id = 0; id < IPSET_EXT_ID_MAX; id++) {
diff --git a/net/netfilter/ipset/ip_set_hash_gen.h b/net/netfilter/ipset/ip_set_hash_gen.h
index f236c0b..a407f85 100644
--- a/net/netfilter/ipset/ip_set_hash_gen.h
+++ b/net/netfilter/ipset/ip_set_hash_gen.h
@@ -166,6 +166,41 @@  struct net_prefixes {
 #define NLEN			0
 #endif /* IP_SET_HASH_WITH_NETS */
 
+#ifdef IP_SET_HASH_WITH_NETMASK
+const static union nf_inet_addr onesmask = {
+	.all[0] = 0xffffffff,
+	.all[1] = 0xffffffff,
+	.all[2] = 0xffffffff,
+	.all[3] = 0xffffffff
+};
+const static union nf_inet_addr zeromask;
+
+struct ipset_netmask {
+	u8 cidr;
+	union nf_inet_addr mask;
+};
+
+static void
+ip_set_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;
+		}
+	}
+}
+
+#endif
+
 #endif /* _IP_SET_HASH_GEN_H */
 
 #ifndef MTYPE
@@ -289,7 +324,7 @@  struct htype {
 	u8 ahash_max;		/* max elements in an array block */
 #endif
 #ifdef IP_SET_HASH_WITH_NETMASK
-	u8 netmask;		/* netmask value for subnets to store */
+	struct ipset_netmask netmask;
 #endif
 	struct mtype_elem next; /* temporary storage for uadd */
 #ifdef IP_SET_HASH_WITH_NETS
@@ -449,7 +484,8 @@  struct htype {
 	return x->maxelem == y->maxelem &&
 	       a->timeout == b->timeout &&
 #ifdef IP_SET_HASH_WITH_NETMASK
-	       x->netmask == y->netmask &&
+		nf_inet_addr_cmp(&x->netmask.mask, &y->netmask.mask) &&
+		x->netmask.cidr == y->netmask.cidr &&
 #endif
 #ifdef IP_SET_HASH_WITH_MARKMASK
 	       x->markmask == y->markmask &&
@@ -1061,9 +1097,20 @@  struct htype {
 	    nla_put_net32(skb, IPSET_ATTR_MAXELEM, htonl(h->maxelem)))
 		goto nla_put_failure;
 #ifdef IP_SET_HASH_WITH_NETMASK
-	if (h->netmask != HOST_MASK &&
-	    nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask))
-		goto nla_put_failure;
+	if (!nf_inet_addr_cmp(&onesmask, &h->netmask.mask)) {
+		if (SET_WITH_NETMASK(set)) {
+			if (set->family == NFPROTO_IPV4) {
+				if (nla_put_ipaddr4(skb, IPSET_ATTR_NETMASK_MASK, h->netmask.mask.ip))
+					goto nla_put_failure;
+			} else if (set->family == NFPROTO_IPV6) {
+				if (nla_put_ipaddr6(skb, IPSET_ATTR_NETMASK_MASK, &h->netmask.mask.in6))
+					goto nla_put_failure;
+			}
+		}
+
+		if (nla_put_u8(skb, IPSET_ATTR_NETMASK, h->netmask.cidr))
+			goto nla_put_failure;
+	}
 #endif
 #ifdef IP_SET_HASH_WITH_MARKMASK
 	if (nla_put_u32(skb, IPSET_ATTR_MARKMASK, h->markmask))
@@ -1220,7 +1267,9 @@  struct htype {
 #endif
 	u8 hbits;
 #ifdef IP_SET_HASH_WITH_NETMASK
-	u8 netmask;
+	union nf_inet_addr netmask = onesmask;
+	int ret = 0;
+	u8 cidr = 0;
 #endif
 	size_t hsize;
 	struct htype *h;
@@ -1254,15 +1303,32 @@  struct htype {
 #endif
 
 #ifdef IP_SET_HASH_WITH_NETMASK
-	netmask = set->family == NFPROTO_IPV4 ? 32 : 128;
 	if (tb[IPSET_ATTR_NETMASK]) {
-		netmask = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
+		cidr = nla_get_u8(tb[IPSET_ATTR_NETMASK]);
+		if ((set->family == NFPROTO_IPV4 && cidr > 32) ||
+		    (set->family == NFPROTO_IPV6 && cidr > 128))
+			return -IPSET_ERR_INVALID_NETMASK;
 
-		if ((set->family == NFPROTO_IPV4 && netmask > 32) ||
-		    (set->family == NFPROTO_IPV6 && netmask > 128) ||
-		    netmask == 0)
+	}
+	if (tb[IPSET_ATTR_NETMASK_MASK]) {
+		if (set->family == NFPROTO_IPV4) {
+			ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_NETMASK_MASK], &netmask.ip);
+			if (ret || !netmask.ip)
+				return -IPSET_ERR_INVALID_NETMASK;
+		} else if (set->family == NFPROTO_IPV6) {
+			ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_NETMASK_MASK], &netmask);
+			if (ret || ipv6_addr_any(&netmask.in6))
+				return -IPSET_ERR_INVALID_NETMASK;
+		}
+		if (nf_inet_addr_cmp(&netmask, &zeromask))
 			return -IPSET_ERR_INVALID_NETMASK;
 	}
+
+	/* For backwards compatibility, we'll convert the cidr to a mask if
+	 * userspace didn't give us one.
+	 */
+	if (cidr && nf_inet_addr_cmp(&netmask, &onesmask))
+		ip_set_cidr_to_mask(&netmask, cidr, set->family);
 #endif
 
 	if (tb[IPSET_ATTR_HASHSIZE]) {
@@ -1292,7 +1358,8 @@  struct htype {
 	}
 	h->maxelem = maxelem;
 #ifdef IP_SET_HASH_WITH_NETMASK
-	h->netmask = netmask;
+	h->netmask.mask = netmask;
+	h->netmask.cidr = cidr;
 #endif
 #ifdef IP_SET_HASH_WITH_MARKMASK
 	h->markmask = markmask;