15d50e1d8SJozsef Kadlecsik /* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> 241d22f7bSJozsef Kadlecsik * 341d22f7bSJozsef Kadlecsik * This program is free software; you can redistribute it and/or modify 441d22f7bSJozsef Kadlecsik * it under the terms of the GNU General Public License version 2 as 541d22f7bSJozsef Kadlecsik * published by the Free Software Foundation. 641d22f7bSJozsef Kadlecsik */ 741d22f7bSJozsef Kadlecsik 841d22f7bSJozsef Kadlecsik /* Kernel module implementing an IP set type: the hash:ip,port,net type */ 941d22f7bSJozsef Kadlecsik 1041d22f7bSJozsef Kadlecsik #include <linux/jhash.h> 1141d22f7bSJozsef Kadlecsik #include <linux/module.h> 1241d22f7bSJozsef Kadlecsik #include <linux/ip.h> 1341d22f7bSJozsef Kadlecsik #include <linux/skbuff.h> 1441d22f7bSJozsef Kadlecsik #include <linux/errno.h> 1541d22f7bSJozsef Kadlecsik #include <linux/random.h> 1641d22f7bSJozsef Kadlecsik #include <net/ip.h> 1741d22f7bSJozsef Kadlecsik #include <net/ipv6.h> 1841d22f7bSJozsef Kadlecsik #include <net/netlink.h> 1941d22f7bSJozsef Kadlecsik #include <net/tcp.h> 2041d22f7bSJozsef Kadlecsik 2141d22f7bSJozsef Kadlecsik #include <linux/netfilter.h> 2241d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/pfxlen.h> 2341d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set.h> 2441d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_getport.h> 2541d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_hash.h> 2641d22f7bSJozsef Kadlecsik 2735b8dcf8SJozsef Kadlecsik #define IPSET_TYPE_REV_MIN 0 2810111a6eSJozsef Kadlecsik /* 1 SCTP and UDPLITE support added */ 2910111a6eSJozsef Kadlecsik /* 2 Range as input support for IPv4 added */ 3000d71b27SJozsef Kadlecsik /* 3 nomatch flag support added */ 31fda75c6dSOliver Smith /* 4 Counters support added */ 3207cf8f5aSJosh Hunt /* 5 Comments support added */ 33af331419SAnton Danilov /* 6 Forceadd support added */ 34af331419SAnton Danilov #define IPSET_TYPE_REV_MAX 7 /* skbinfo support added */ 3510111a6eSJozsef Kadlecsik 3641d22f7bSJozsef Kadlecsik MODULE_LICENSE("GPL"); 3741d22f7bSJozsef Kadlecsik MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); 3835b8dcf8SJozsef Kadlecsik IP_SET_MODULE_DESC("hash:ip,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); 3941d22f7bSJozsef Kadlecsik MODULE_ALIAS("ip_set_hash:ip,port,net"); 4041d22f7bSJozsef Kadlecsik 4141d22f7bSJozsef Kadlecsik /* Type specific function prefix */ 425d50e1d8SJozsef Kadlecsik #define HTYPE hash_ipportnet 4341d22f7bSJozsef Kadlecsik 442a7cef2aSJozsef Kadlecsik /* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0 452a7cef2aSJozsef Kadlecsik * However this way we have to store internally cidr - 1, 462a7cef2aSJozsef Kadlecsik * dancing back and forth. 472a7cef2aSJozsef Kadlecsik */ 482a7cef2aSJozsef Kadlecsik #define IP_SET_HASH_WITH_NETS_PACKED 495d50e1d8SJozsef Kadlecsik #define IP_SET_HASH_WITH_PROTO 505d50e1d8SJozsef Kadlecsik #define IP_SET_HASH_WITH_NETS 512a7cef2aSJozsef Kadlecsik 5203c8b234SJozsef Kadlecsik /* IPv4 variant */ 535d50e1d8SJozsef Kadlecsik 545d50e1d8SJozsef Kadlecsik /* Member elements */ 5541d22f7bSJozsef Kadlecsik struct hash_ipportnet4_elem { 5641d22f7bSJozsef Kadlecsik __be32 ip; 5741d22f7bSJozsef Kadlecsik __be32 ip2; 5841d22f7bSJozsef Kadlecsik __be16 port; 592a7cef2aSJozsef Kadlecsik u8 cidr:7; 602a7cef2aSJozsef Kadlecsik u8 nomatch:1; 6141d22f7bSJozsef Kadlecsik u8 proto; 6241d22f7bSJozsef Kadlecsik }; 6341d22f7bSJozsef Kadlecsik 645d50e1d8SJozsef Kadlecsik /* Common functions */ 655d50e1d8SJozsef Kadlecsik 6641d22f7bSJozsef Kadlecsik static inline bool 6741d22f7bSJozsef Kadlecsik hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1, 6889dc79b7SJozsef Kadlecsik const struct hash_ipportnet4_elem *ip2, 6989dc79b7SJozsef Kadlecsik u32 *multi) 7041d22f7bSJozsef Kadlecsik { 7141d22f7bSJozsef Kadlecsik return ip1->ip == ip2->ip && 7241d22f7bSJozsef Kadlecsik ip1->ip2 == ip2->ip2 && 7341d22f7bSJozsef Kadlecsik ip1->cidr == ip2->cidr && 7441d22f7bSJozsef Kadlecsik ip1->port == ip2->port && 7541d22f7bSJozsef Kadlecsik ip1->proto == ip2->proto; 7641d22f7bSJozsef Kadlecsik } 7741d22f7bSJozsef Kadlecsik 783e0304a5SJozsef Kadlecsik static inline int 795d50e1d8SJozsef Kadlecsik hash_ipportnet4_do_data_match(const struct hash_ipportnet4_elem *elem) 802a7cef2aSJozsef Kadlecsik { 813e0304a5SJozsef Kadlecsik return elem->nomatch ? -ENOTEMPTY : 1; 822a7cef2aSJozsef Kadlecsik } 832a7cef2aSJozsef Kadlecsik 842a7cef2aSJozsef Kadlecsik static inline void 855d50e1d8SJozsef Kadlecsik hash_ipportnet4_data_set_flags(struct hash_ipportnet4_elem *elem, u32 flags) 865d50e1d8SJozsef Kadlecsik { 875d50e1d8SJozsef Kadlecsik elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); 885d50e1d8SJozsef Kadlecsik } 895d50e1d8SJozsef Kadlecsik 905d50e1d8SJozsef Kadlecsik static inline void 915d50e1d8SJozsef Kadlecsik hash_ipportnet4_data_reset_flags(struct hash_ipportnet4_elem *elem, u8 *flags) 925d50e1d8SJozsef Kadlecsik { 935d50e1d8SJozsef Kadlecsik swap(*flags, elem->nomatch); 945d50e1d8SJozsef Kadlecsik } 955d50e1d8SJozsef Kadlecsik 965d50e1d8SJozsef Kadlecsik static inline void 9741d22f7bSJozsef Kadlecsik hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr) 9841d22f7bSJozsef Kadlecsik { 9941d22f7bSJozsef Kadlecsik elem->ip2 &= ip_set_netmask(cidr); 1002a7cef2aSJozsef Kadlecsik elem->cidr = cidr - 1; 10141d22f7bSJozsef Kadlecsik } 10241d22f7bSJozsef Kadlecsik 10341d22f7bSJozsef Kadlecsik static bool 10441d22f7bSJozsef Kadlecsik hash_ipportnet4_data_list(struct sk_buff *skb, 10541d22f7bSJozsef Kadlecsik const struct hash_ipportnet4_elem *data) 10641d22f7bSJozsef Kadlecsik { 1072a7cef2aSJozsef Kadlecsik u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; 1082a7cef2aSJozsef Kadlecsik 1097cf7899dSDavid S. Miller if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || 1107cf7899dSDavid S. Miller nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip2) || 1117cf7899dSDavid S. Miller nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || 1127cf7899dSDavid S. Miller nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) || 1137cf7899dSDavid S. Miller nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || 1147cf7899dSDavid S. Miller (flags && 1157cf7899dSDavid S. Miller nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) 1167cf7899dSDavid S. Miller goto nla_put_failure; 117728a7e69SSergey Popovich return false; 11841d22f7bSJozsef Kadlecsik 11941d22f7bSJozsef Kadlecsik nla_put_failure: 120728a7e69SSergey Popovich return true; 12141d22f7bSJozsef Kadlecsik } 12241d22f7bSJozsef Kadlecsik 1233d14b171SJozsef Kadlecsik static inline void 1245d50e1d8SJozsef Kadlecsik hash_ipportnet4_data_next(struct hash_ipportnet4_elem *next, 1253d14b171SJozsef Kadlecsik const struct hash_ipportnet4_elem *d) 1263d14b171SJozsef Kadlecsik { 1275d50e1d8SJozsef Kadlecsik next->ip = d->ip; 1285d50e1d8SJozsef Kadlecsik next->port = d->port; 1295d50e1d8SJozsef Kadlecsik next->ip2 = d->ip2; 1303d14b171SJozsef Kadlecsik } 1313d14b171SJozsef Kadlecsik 1325d50e1d8SJozsef Kadlecsik #define MTYPE hash_ipportnet4 1335d50e1d8SJozsef Kadlecsik #define HOST_MASK 32 1345d50e1d8SJozsef Kadlecsik #include "ip_set_hash_gen.h" 1355d50e1d8SJozsef Kadlecsik 13641d22f7bSJozsef Kadlecsik static int 13741d22f7bSJozsef Kadlecsik hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, 138b66554cfSJozsef Kadlecsik const struct xt_action_param *par, 1395d50e1d8SJozsef Kadlecsik enum ipset_adt adt, struct ip_set_adt_opt *opt) 14041d22f7bSJozsef Kadlecsik { 14121956ab2SJozsef Kadlecsik const struct hash_ipportnet4 *h = set->data; 14241d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 1435d50e1d8SJozsef Kadlecsik struct hash_ipportnet4_elem e = { 144f690cbaeSJozsef Kadlecsik .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), 1459b03a5efSJozsef Kadlecsik }; 146ca134ce8SJozsef Kadlecsik struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); 14741d22f7bSJozsef Kadlecsik 14841d22f7bSJozsef Kadlecsik if (adt == IPSET_TEST) 1495d50e1d8SJozsef Kadlecsik e.cidr = HOST_MASK - 1; 15041d22f7bSJozsef Kadlecsik 151ac8cc925SJozsef Kadlecsik if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, 1525d50e1d8SJozsef Kadlecsik &e.port, &e.proto)) 15341d22f7bSJozsef Kadlecsik return -EINVAL; 15441d22f7bSJozsef Kadlecsik 1555d50e1d8SJozsef Kadlecsik ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip); 1565d50e1d8SJozsef Kadlecsik ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2); 1575d50e1d8SJozsef Kadlecsik e.ip2 &= ip_set_netmask(e.cidr + 1); 15841d22f7bSJozsef Kadlecsik 1595d50e1d8SJozsef Kadlecsik return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); 16041d22f7bSJozsef Kadlecsik } 16141d22f7bSJozsef Kadlecsik 16241d22f7bSJozsef Kadlecsik static int 16341d22f7bSJozsef Kadlecsik hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], 1643d14b171SJozsef Kadlecsik enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) 16541d22f7bSJozsef Kadlecsik { 16621956ab2SJozsef Kadlecsik const struct hash_ipportnet4 *h = set->data; 16741d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 1685d50e1d8SJozsef Kadlecsik struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 }; 169ca134ce8SJozsef Kadlecsik struct ip_set_ext ext = IP_SET_INIT_UEXT(set); 17020b2fab4SJozsef Kadlecsik u32 ip = 0, ip_to = 0, p = 0, port, port_to; 17120b2fab4SJozsef Kadlecsik u32 ip2_from = 0, ip2_to = 0, ip2_last, ip2; 1725e0c1eb7SJozsef Kadlecsik bool with_ports = false; 1732a7cef2aSJozsef Kadlecsik u8 cidr; 17441d22f7bSJozsef Kadlecsik int ret; 17541d22f7bSJozsef Kadlecsik 176a212e08eSSergey Popovich if (tb[IPSET_ATTR_LINENO]) 177a212e08eSSergey Popovich *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); 178a212e08eSSergey Popovich 17941d22f7bSJozsef Kadlecsik if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || 18041d22f7bSJozsef Kadlecsik !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || 18141d22f7bSJozsef Kadlecsik !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || 1827dd37bc8SSergey Popovich !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) 18341d22f7bSJozsef Kadlecsik return -IPSET_ERR_PROTOCOL; 18441d22f7bSJozsef Kadlecsik 1858e55d2e5SSergey Popovich ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip); 1868e55d2e5SSergey Popovich if (ret) 1878e55d2e5SSergey Popovich return ret; 1888e55d2e5SSergey Popovich 1898e55d2e5SSergey Popovich ret = ip_set_get_extensions(set, tb, &ext); 19041d22f7bSJozsef Kadlecsik if (ret) 19141d22f7bSJozsef Kadlecsik return ret; 19241d22f7bSJozsef Kadlecsik 193d0d9e0a5SJozsef Kadlecsik ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from); 19441d22f7bSJozsef Kadlecsik if (ret) 19541d22f7bSJozsef Kadlecsik return ret; 19641d22f7bSJozsef Kadlecsik 197d0d9e0a5SJozsef Kadlecsik if (tb[IPSET_ATTR_CIDR2]) { 1982a7cef2aSJozsef Kadlecsik cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); 1992a7cef2aSJozsef Kadlecsik if (!cidr || cidr > HOST_MASK) 20041d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_CIDR; 2015d50e1d8SJozsef Kadlecsik e.cidr = cidr - 1; 202d0d9e0a5SJozsef Kadlecsik } 20341d22f7bSJozsef Kadlecsik 2045d50e1d8SJozsef Kadlecsik e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); 20541d22f7bSJozsef Kadlecsik 20641d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_PROTO]) { 2075d50e1d8SJozsef Kadlecsik e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); 2085d50e1d8SJozsef Kadlecsik with_ports = ip_set_proto_with_ports(e.proto); 20941d22f7bSJozsef Kadlecsik 2105d50e1d8SJozsef Kadlecsik if (e.proto == 0) 21141d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_PROTO; 212ca0f6a5cSJozsef Kadlecsik } else { 21341d22f7bSJozsef Kadlecsik return -IPSET_ERR_MISSING_PROTO; 214ca0f6a5cSJozsef Kadlecsik } 21541d22f7bSJozsef Kadlecsik 2165d50e1d8SJozsef Kadlecsik if (!(with_ports || e.proto == IPPROTO_ICMP)) 2175d50e1d8SJozsef Kadlecsik e.port = 0; 21841d22f7bSJozsef Kadlecsik 21943c56e59SJozsef Kadlecsik if (tb[IPSET_ATTR_CADT_FLAGS]) { 2202a7cef2aSJozsef Kadlecsik u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); 221ca0f6a5cSJozsef Kadlecsik 2222a7cef2aSJozsef Kadlecsik if (cadt_flags & IPSET_FLAG_NOMATCH) 22343c56e59SJozsef Kadlecsik flags |= (IPSET_FLAG_NOMATCH << 16); 2242a7cef2aSJozsef Kadlecsik } 2252a7cef2aSJozsef Kadlecsik 226d0d9e0a5SJozsef Kadlecsik with_ports = with_ports && tb[IPSET_ATTR_PORT_TO]; 22741d22f7bSJozsef Kadlecsik if (adt == IPSET_TEST || 228d0d9e0a5SJozsef Kadlecsik !(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports || 229d0d9e0a5SJozsef Kadlecsik tb[IPSET_ATTR_IP2_TO])) { 2305d50e1d8SJozsef Kadlecsik e.ip = htonl(ip); 2315d50e1d8SJozsef Kadlecsik e.ip2 = htonl(ip2_from & ip_set_hostmask(e.cidr + 1)); 2325d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 2330f1799baSJozsef Kadlecsik return ip_set_enomatch(ret, flags, adt, set) ? -ret : 23443c56e59SJozsef Kadlecsik ip_set_eexist(ret, flags) ? 0 : ret; 23541d22f7bSJozsef Kadlecsik } 23641d22f7bSJozsef Kadlecsik 2374fe198e6SJozsef Kadlecsik ip_to = ip; 23841d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_IP_TO]) { 23941d22f7bSJozsef Kadlecsik ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); 24041d22f7bSJozsef Kadlecsik if (ret) 24141d22f7bSJozsef Kadlecsik return ret; 24241d22f7bSJozsef Kadlecsik if (ip > ip_to) 24341d22f7bSJozsef Kadlecsik swap(ip, ip_to); 24441d22f7bSJozsef Kadlecsik } else if (tb[IPSET_ATTR_CIDR]) { 245b3aabd14SJozsef Kadlecsik cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); 24641d22f7bSJozsef Kadlecsik 247cabfd139SSergey Popovich if (!cidr || cidr > HOST_MASK) 24841d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_CIDR; 249e6146e86SJozsef Kadlecsik ip_set_mask_from_to(ip, ip_to, cidr); 250d0d9e0a5SJozsef Kadlecsik } 25141d22f7bSJozsef Kadlecsik 2525d50e1d8SJozsef Kadlecsik port_to = port = ntohs(e.port); 253d0d9e0a5SJozsef Kadlecsik if (tb[IPSET_ATTR_PORT_TO]) { 25441d22f7bSJozsef Kadlecsik port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); 25541d22f7bSJozsef Kadlecsik if (port > port_to) 25641d22f7bSJozsef Kadlecsik swap(port, port_to); 2575e0c1eb7SJozsef Kadlecsik } 2584fe198e6SJozsef Kadlecsik 2594fe198e6SJozsef Kadlecsik ip2_to = ip2_from; 260d0d9e0a5SJozsef Kadlecsik if (tb[IPSET_ATTR_IP2_TO]) { 261d0d9e0a5SJozsef Kadlecsik ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); 262d0d9e0a5SJozsef Kadlecsik if (ret) 263d0d9e0a5SJozsef Kadlecsik return ret; 264d0d9e0a5SJozsef Kadlecsik if (ip2_from > ip2_to) 265d0d9e0a5SJozsef Kadlecsik swap(ip2_from, ip2_to); 266d0d9e0a5SJozsef Kadlecsik if (ip2_from + UINT_MAX == ip2_to) 267d0d9e0a5SJozsef Kadlecsik return -IPSET_ERR_HASH_RANGE; 268ca0f6a5cSJozsef Kadlecsik } else { 2695d50e1d8SJozsef Kadlecsik ip_set_mask_from_to(ip2_from, ip2_to, e.cidr + 1); 270ca0f6a5cSJozsef Kadlecsik } 27141d22f7bSJozsef Kadlecsik 2723d14b171SJozsef Kadlecsik if (retried) 2736e27c9b4SJozsef Kadlecsik ip = ntohl(h->next.ip); 2743d14b171SJozsef Kadlecsik for (; !before(ip_to, ip); ip++) { 2755d50e1d8SJozsef Kadlecsik e.ip = htonl(ip); 2766e27c9b4SJozsef Kadlecsik p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port) 2776e27c9b4SJozsef Kadlecsik : port; 2783d14b171SJozsef Kadlecsik for (; p <= port_to; p++) { 2795d50e1d8SJozsef Kadlecsik e.port = htons(p); 280ca134ce8SJozsef Kadlecsik ip2 = retried && 281ca134ce8SJozsef Kadlecsik ip == ntohl(h->next.ip) && 282ca134ce8SJozsef Kadlecsik p == ntohs(h->next.port) 2836e27c9b4SJozsef Kadlecsik ? ntohl(h->next.ip2) : ip2_from; 284d0d9e0a5SJozsef Kadlecsik while (!after(ip2, ip2_to)) { 2855d50e1d8SJozsef Kadlecsik e.ip2 = htonl(ip2); 286d0d9e0a5SJozsef Kadlecsik ip2_last = ip_set_range_to_cidr(ip2, ip2_to, 2872a7cef2aSJozsef Kadlecsik &cidr); 2885d50e1d8SJozsef Kadlecsik e.cidr = cidr - 1; 2895d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 29041d22f7bSJozsef Kadlecsik 29141d22f7bSJozsef Kadlecsik if (ret && !ip_set_eexist(ret, flags)) 29241d22f7bSJozsef Kadlecsik return ret; 293ca0f6a5cSJozsef Kadlecsik 29441d22f7bSJozsef Kadlecsik ret = 0; 295d0d9e0a5SJozsef Kadlecsik ip2 = ip2_last + 1; 296d0d9e0a5SJozsef Kadlecsik } 29741d22f7bSJozsef Kadlecsik } 2983d14b171SJozsef Kadlecsik } 29941d22f7bSJozsef Kadlecsik return ret; 30041d22f7bSJozsef Kadlecsik } 30141d22f7bSJozsef Kadlecsik 30203c8b234SJozsef Kadlecsik /* IPv6 variant */ 30341d22f7bSJozsef Kadlecsik 30441d22f7bSJozsef Kadlecsik struct hash_ipportnet6_elem { 30541d22f7bSJozsef Kadlecsik union nf_inet_addr ip; 30641d22f7bSJozsef Kadlecsik union nf_inet_addr ip2; 30741d22f7bSJozsef Kadlecsik __be16 port; 3082a7cef2aSJozsef Kadlecsik u8 cidr:7; 3092a7cef2aSJozsef Kadlecsik u8 nomatch:1; 31041d22f7bSJozsef Kadlecsik u8 proto; 31141d22f7bSJozsef Kadlecsik }; 31241d22f7bSJozsef Kadlecsik 3135d50e1d8SJozsef Kadlecsik /* Common functions */ 3145d50e1d8SJozsef Kadlecsik 31541d22f7bSJozsef Kadlecsik static inline bool 31641d22f7bSJozsef Kadlecsik hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1, 31789dc79b7SJozsef Kadlecsik const struct hash_ipportnet6_elem *ip2, 31889dc79b7SJozsef Kadlecsik u32 *multi) 31941d22f7bSJozsef Kadlecsik { 32029e3b160SYOSHIFUJI Hideaki / 吉藤英明 return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) && 32129e3b160SYOSHIFUJI Hideaki / 吉藤英明 ipv6_addr_equal(&ip1->ip2.in6, &ip2->ip2.in6) && 32241d22f7bSJozsef Kadlecsik ip1->cidr == ip2->cidr && 32341d22f7bSJozsef Kadlecsik ip1->port == ip2->port && 32441d22f7bSJozsef Kadlecsik ip1->proto == ip2->proto; 32541d22f7bSJozsef Kadlecsik } 32641d22f7bSJozsef Kadlecsik 3273e0304a5SJozsef Kadlecsik static inline int 3285d50e1d8SJozsef Kadlecsik hash_ipportnet6_do_data_match(const struct hash_ipportnet6_elem *elem) 3292a7cef2aSJozsef Kadlecsik { 3303e0304a5SJozsef Kadlecsik return elem->nomatch ? -ENOTEMPTY : 1; 3312a7cef2aSJozsef Kadlecsik } 3322a7cef2aSJozsef Kadlecsik 3332a7cef2aSJozsef Kadlecsik static inline void 3345d50e1d8SJozsef Kadlecsik hash_ipportnet6_data_set_flags(struct hash_ipportnet6_elem *elem, u32 flags) 33541d22f7bSJozsef Kadlecsik { 3365d50e1d8SJozsef Kadlecsik elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); 3375d50e1d8SJozsef Kadlecsik } 3385d50e1d8SJozsef Kadlecsik 3395d50e1d8SJozsef Kadlecsik static inline void 3405d50e1d8SJozsef Kadlecsik hash_ipportnet6_data_reset_flags(struct hash_ipportnet6_elem *elem, u8 *flags) 3415d50e1d8SJozsef Kadlecsik { 3425d50e1d8SJozsef Kadlecsik swap(*flags, elem->nomatch); 34341d22f7bSJozsef Kadlecsik } 34441d22f7bSJozsef Kadlecsik 34541d22f7bSJozsef Kadlecsik static inline void 34641d22f7bSJozsef Kadlecsik hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr) 34741d22f7bSJozsef Kadlecsik { 34841d22f7bSJozsef Kadlecsik ip6_netmask(&elem->ip2, cidr); 3492a7cef2aSJozsef Kadlecsik elem->cidr = cidr - 1; 35041d22f7bSJozsef Kadlecsik } 35141d22f7bSJozsef Kadlecsik 35241d22f7bSJozsef Kadlecsik static bool 35341d22f7bSJozsef Kadlecsik hash_ipportnet6_data_list(struct sk_buff *skb, 35441d22f7bSJozsef Kadlecsik const struct hash_ipportnet6_elem *data) 35541d22f7bSJozsef Kadlecsik { 3562a7cef2aSJozsef Kadlecsik u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; 3572a7cef2aSJozsef Kadlecsik 3587cf7899dSDavid S. Miller if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) || 3597cf7899dSDavid S. Miller nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) || 3607cf7899dSDavid S. Miller nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || 3617cf7899dSDavid S. Miller nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) || 3627cf7899dSDavid S. Miller nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || 3637cf7899dSDavid S. Miller (flags && 3647cf7899dSDavid S. Miller nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) 3657cf7899dSDavid S. Miller goto nla_put_failure; 366728a7e69SSergey Popovich return false; 36741d22f7bSJozsef Kadlecsik 36841d22f7bSJozsef Kadlecsik nla_put_failure: 369728a7e69SSergey Popovich return true; 37041d22f7bSJozsef Kadlecsik } 37141d22f7bSJozsef Kadlecsik 3725d50e1d8SJozsef Kadlecsik static inline void 37321956ab2SJozsef Kadlecsik hash_ipportnet6_data_next(struct hash_ipportnet6_elem *next, 3745d50e1d8SJozsef Kadlecsik const struct hash_ipportnet6_elem *d) 37541d22f7bSJozsef Kadlecsik { 3765d50e1d8SJozsef Kadlecsik next->port = d->port; 37741d22f7bSJozsef Kadlecsik } 37841d22f7bSJozsef Kadlecsik 3795d50e1d8SJozsef Kadlecsik #undef MTYPE 38041d22f7bSJozsef Kadlecsik #undef HOST_MASK 38141d22f7bSJozsef Kadlecsik 3825d50e1d8SJozsef Kadlecsik #define MTYPE hash_ipportnet6 38341d22f7bSJozsef Kadlecsik #define HOST_MASK 128 3845d50e1d8SJozsef Kadlecsik #define IP_SET_EMIT_CREATE 3855d50e1d8SJozsef Kadlecsik #include "ip_set_hash_gen.h" 3863d14b171SJozsef Kadlecsik 38741d22f7bSJozsef Kadlecsik static int 38841d22f7bSJozsef Kadlecsik hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, 389b66554cfSJozsef Kadlecsik const struct xt_action_param *par, 3905d50e1d8SJozsef Kadlecsik enum ipset_adt adt, struct ip_set_adt_opt *opt) 39141d22f7bSJozsef Kadlecsik { 39221956ab2SJozsef Kadlecsik const struct hash_ipportnet6 *h = set->data; 39341d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 3945d50e1d8SJozsef Kadlecsik struct hash_ipportnet6_elem e = { 395f690cbaeSJozsef Kadlecsik .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), 3969b03a5efSJozsef Kadlecsik }; 397ca134ce8SJozsef Kadlecsik struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); 39841d22f7bSJozsef Kadlecsik 39941d22f7bSJozsef Kadlecsik if (adt == IPSET_TEST) 4005d50e1d8SJozsef Kadlecsik e.cidr = HOST_MASK - 1; 40141d22f7bSJozsef Kadlecsik 402ac8cc925SJozsef Kadlecsik if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, 4035d50e1d8SJozsef Kadlecsik &e.port, &e.proto)) 40441d22f7bSJozsef Kadlecsik return -EINVAL; 40541d22f7bSJozsef Kadlecsik 4065d50e1d8SJozsef Kadlecsik ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); 4075d50e1d8SJozsef Kadlecsik ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2.in6); 4085d50e1d8SJozsef Kadlecsik ip6_netmask(&e.ip2, e.cidr + 1); 40941d22f7bSJozsef Kadlecsik 4105d50e1d8SJozsef Kadlecsik return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); 41141d22f7bSJozsef Kadlecsik } 41241d22f7bSJozsef Kadlecsik 41341d22f7bSJozsef Kadlecsik static int 41441d22f7bSJozsef Kadlecsik hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], 4153d14b171SJozsef Kadlecsik enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) 41641d22f7bSJozsef Kadlecsik { 41721956ab2SJozsef Kadlecsik const struct hash_ipportnet6 *h = set->data; 41841d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 4195d50e1d8SJozsef Kadlecsik struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 }; 420ca134ce8SJozsef Kadlecsik struct ip_set_ext ext = IP_SET_INIT_UEXT(set); 42141d22f7bSJozsef Kadlecsik u32 port, port_to; 4225e0c1eb7SJozsef Kadlecsik bool with_ports = false; 4232a7cef2aSJozsef Kadlecsik u8 cidr; 42441d22f7bSJozsef Kadlecsik int ret; 42541d22f7bSJozsef Kadlecsik 426a212e08eSSergey Popovich if (tb[IPSET_ATTR_LINENO]) 427a212e08eSSergey Popovich *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); 428a212e08eSSergey Popovich 42941d22f7bSJozsef Kadlecsik if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || 43041d22f7bSJozsef Kadlecsik !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || 43141d22f7bSJozsef Kadlecsik !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || 4322c227f27SSergey Popovich !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) 43341d22f7bSJozsef Kadlecsik return -IPSET_ERR_PROTOCOL; 434d0d9e0a5SJozsef Kadlecsik if (unlikely(tb[IPSET_ATTR_IP_TO])) 435d0d9e0a5SJozsef Kadlecsik return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; 4362c227f27SSergey Popovich if (unlikely(tb[IPSET_ATTR_CIDR])) { 4378851e799SJozsef Kadlecsik cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); 4382c227f27SSergey Popovich 4392c227f27SSergey Popovich if (cidr != HOST_MASK) 4402c227f27SSergey Popovich return -IPSET_ERR_INVALID_CIDR; 4412c227f27SSergey Popovich } 44241d22f7bSJozsef Kadlecsik 4438e55d2e5SSergey Popovich ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip); 4448e55d2e5SSergey Popovich if (ret) 4458e55d2e5SSergey Popovich return ret; 4468e55d2e5SSergey Popovich 4478e55d2e5SSergey Popovich ret = ip_set_get_extensions(set, tb, &ext); 44841d22f7bSJozsef Kadlecsik if (ret) 44941d22f7bSJozsef Kadlecsik return ret; 45041d22f7bSJozsef Kadlecsik 4515d50e1d8SJozsef Kadlecsik ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip2); 45241d22f7bSJozsef Kadlecsik if (ret) 45341d22f7bSJozsef Kadlecsik return ret; 45441d22f7bSJozsef Kadlecsik 4552a7cef2aSJozsef Kadlecsik if (tb[IPSET_ATTR_CIDR2]) { 4562a7cef2aSJozsef Kadlecsik cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); 4572a7cef2aSJozsef Kadlecsik if (!cidr || cidr > HOST_MASK) 45841d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_CIDR; 4595d50e1d8SJozsef Kadlecsik e.cidr = cidr - 1; 4602a7cef2aSJozsef Kadlecsik } 46141d22f7bSJozsef Kadlecsik 4625d50e1d8SJozsef Kadlecsik ip6_netmask(&e.ip2, e.cidr + 1); 46341d22f7bSJozsef Kadlecsik 4645d50e1d8SJozsef Kadlecsik e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); 46541d22f7bSJozsef Kadlecsik 46641d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_PROTO]) { 4675d50e1d8SJozsef Kadlecsik e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); 4685d50e1d8SJozsef Kadlecsik with_ports = ip_set_proto_with_ports(e.proto); 46941d22f7bSJozsef Kadlecsik 4705d50e1d8SJozsef Kadlecsik if (e.proto == 0) 47141d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_PROTO; 472ca0f6a5cSJozsef Kadlecsik } else { 47341d22f7bSJozsef Kadlecsik return -IPSET_ERR_MISSING_PROTO; 474ca0f6a5cSJozsef Kadlecsik } 47541d22f7bSJozsef Kadlecsik 4765d50e1d8SJozsef Kadlecsik if (!(with_ports || e.proto == IPPROTO_ICMPV6)) 4775d50e1d8SJozsef Kadlecsik e.port = 0; 47841d22f7bSJozsef Kadlecsik 47943c56e59SJozsef Kadlecsik if (tb[IPSET_ATTR_CADT_FLAGS]) { 4802a7cef2aSJozsef Kadlecsik u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); 481ca0f6a5cSJozsef Kadlecsik 4822a7cef2aSJozsef Kadlecsik if (cadt_flags & IPSET_FLAG_NOMATCH) 48343c56e59SJozsef Kadlecsik flags |= (IPSET_FLAG_NOMATCH << 16); 4842a7cef2aSJozsef Kadlecsik } 4852a7cef2aSJozsef Kadlecsik 4865e0c1eb7SJozsef Kadlecsik if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { 4875d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 4880f1799baSJozsef Kadlecsik return ip_set_enomatch(ret, flags, adt, set) ? -ret : 48943c56e59SJozsef Kadlecsik ip_set_eexist(ret, flags) ? 0 : ret; 49041d22f7bSJozsef Kadlecsik } 49141d22f7bSJozsef Kadlecsik 4925d50e1d8SJozsef Kadlecsik port = ntohs(e.port); 49341d22f7bSJozsef Kadlecsik port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); 49441d22f7bSJozsef Kadlecsik if (port > port_to) 49541d22f7bSJozsef Kadlecsik swap(port, port_to); 49641d22f7bSJozsef Kadlecsik 4973d14b171SJozsef Kadlecsik if (retried) 4986e27c9b4SJozsef Kadlecsik port = ntohs(h->next.port); 49941d22f7bSJozsef Kadlecsik for (; port <= port_to; port++) { 5005d50e1d8SJozsef Kadlecsik e.port = htons(port); 5015d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 50241d22f7bSJozsef Kadlecsik 50341d22f7bSJozsef Kadlecsik if (ret && !ip_set_eexist(ret, flags)) 50441d22f7bSJozsef Kadlecsik return ret; 505ca0f6a5cSJozsef Kadlecsik 50641d22f7bSJozsef Kadlecsik ret = 0; 50741d22f7bSJozsef Kadlecsik } 50841d22f7bSJozsef Kadlecsik return ret; 50941d22f7bSJozsef Kadlecsik } 51041d22f7bSJozsef Kadlecsik 51141d22f7bSJozsef Kadlecsik static struct ip_set_type hash_ipportnet_type __read_mostly = { 51241d22f7bSJozsef Kadlecsik .name = "hash:ip,port,net", 51341d22f7bSJozsef Kadlecsik .protocol = IPSET_PROTOCOL, 5143e0304a5SJozsef Kadlecsik .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2 | 5153e0304a5SJozsef Kadlecsik IPSET_TYPE_NOMATCH, 51641d22f7bSJozsef Kadlecsik .dimension = IPSET_DIM_THREE, 517c15f1c83SJan Engelhardt .family = NFPROTO_UNSPEC, 51835b8dcf8SJozsef Kadlecsik .revision_min = IPSET_TYPE_REV_MIN, 51935b8dcf8SJozsef Kadlecsik .revision_max = IPSET_TYPE_REV_MAX, 52041d22f7bSJozsef Kadlecsik .create = hash_ipportnet_create, 52141d22f7bSJozsef Kadlecsik .create_policy = { 52241d22f7bSJozsef Kadlecsik [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, 52341d22f7bSJozsef Kadlecsik [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, 52441d22f7bSJozsef Kadlecsik [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, 52541d22f7bSJozsef Kadlecsik [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, 52641d22f7bSJozsef Kadlecsik [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, 5275d50e1d8SJozsef Kadlecsik [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, 52841d22f7bSJozsef Kadlecsik }, 52941d22f7bSJozsef Kadlecsik .adt_policy = { 53041d22f7bSJozsef Kadlecsik [IPSET_ATTR_IP] = { .type = NLA_NESTED }, 53141d22f7bSJozsef Kadlecsik [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, 53241d22f7bSJozsef Kadlecsik [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, 533d0d9e0a5SJozsef Kadlecsik [IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED }, 53441d22f7bSJozsef Kadlecsik [IPSET_ATTR_PORT] = { .type = NLA_U16 }, 53541d22f7bSJozsef Kadlecsik [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, 53641d22f7bSJozsef Kadlecsik [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, 53741d22f7bSJozsef Kadlecsik [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, 53841d22f7bSJozsef Kadlecsik [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, 5392a7cef2aSJozsef Kadlecsik [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, 54041d22f7bSJozsef Kadlecsik [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, 54141d22f7bSJozsef Kadlecsik [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, 54200d71b27SJozsef Kadlecsik [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, 54300d71b27SJozsef Kadlecsik [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, 54403726186SSergey Popovich [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING, 54503726186SSergey Popovich .len = IPSET_MAX_COMMENT_SIZE }, 546af331419SAnton Danilov [IPSET_ATTR_SKBMARK] = { .type = NLA_U64 }, 547af331419SAnton Danilov [IPSET_ATTR_SKBPRIO] = { .type = NLA_U32 }, 548af331419SAnton Danilov [IPSET_ATTR_SKBQUEUE] = { .type = NLA_U16 }, 54941d22f7bSJozsef Kadlecsik }, 55041d22f7bSJozsef Kadlecsik .me = THIS_MODULE, 55141d22f7bSJozsef Kadlecsik }; 55241d22f7bSJozsef Kadlecsik 55341d22f7bSJozsef Kadlecsik static int __init 55441d22f7bSJozsef Kadlecsik hash_ipportnet_init(void) 55541d22f7bSJozsef Kadlecsik { 55641d22f7bSJozsef Kadlecsik return ip_set_type_register(&hash_ipportnet_type); 55741d22f7bSJozsef Kadlecsik } 55841d22f7bSJozsef Kadlecsik 55941d22f7bSJozsef Kadlecsik static void __exit 56041d22f7bSJozsef Kadlecsik hash_ipportnet_fini(void) 56141d22f7bSJozsef Kadlecsik { 56218f84d41SJozsef Kadlecsik rcu_barrier(); 56341d22f7bSJozsef Kadlecsik ip_set_type_unregister(&hash_ipportnet_type); 56441d22f7bSJozsef Kadlecsik } 56541d22f7bSJozsef Kadlecsik 56641d22f7bSJozsef Kadlecsik module_init(hash_ipportnet_init); 56741d22f7bSJozsef Kadlecsik module_exit(hash_ipportnet_fini); 568