1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 21c5ba67dSPablo Neira Ayuso /* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@netfilter.org> */ 341d22f7bSJozsef Kadlecsik 441d22f7bSJozsef Kadlecsik /* Kernel module implementing an IP set type: the hash:ip,port,net type */ 541d22f7bSJozsef Kadlecsik 641d22f7bSJozsef Kadlecsik #include <linux/jhash.h> 741d22f7bSJozsef Kadlecsik #include <linux/module.h> 841d22f7bSJozsef Kadlecsik #include <linux/ip.h> 941d22f7bSJozsef Kadlecsik #include <linux/skbuff.h> 1041d22f7bSJozsef Kadlecsik #include <linux/errno.h> 1141d22f7bSJozsef Kadlecsik #include <linux/random.h> 1241d22f7bSJozsef Kadlecsik #include <net/ip.h> 1341d22f7bSJozsef Kadlecsik #include <net/ipv6.h> 1441d22f7bSJozsef Kadlecsik #include <net/netlink.h> 1541d22f7bSJozsef Kadlecsik #include <net/tcp.h> 1641d22f7bSJozsef Kadlecsik 1741d22f7bSJozsef Kadlecsik #include <linux/netfilter.h> 1841d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/pfxlen.h> 1941d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set.h> 2041d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_getport.h> 2141d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_hash.h> 2241d22f7bSJozsef Kadlecsik 2335b8dcf8SJozsef Kadlecsik #define IPSET_TYPE_REV_MIN 0 2410111a6eSJozsef Kadlecsik /* 1 SCTP and UDPLITE support added */ 2510111a6eSJozsef Kadlecsik /* 2 Range as input support for IPv4 added */ 2600d71b27SJozsef Kadlecsik /* 3 nomatch flag support added */ 27fda75c6dSOliver Smith /* 4 Counters support added */ 2807cf8f5aSJosh Hunt /* 5 Comments support added */ 29af331419SAnton Danilov /* 6 Forceadd support added */ 30*ccf0a4b7SJozsef Kadlecsik /* 7 skbinfo support added */ 31*ccf0a4b7SJozsef Kadlecsik #define IPSET_TYPE_REV_MAX 8 /* bucketsize support added */ 3210111a6eSJozsef Kadlecsik 3341d22f7bSJozsef Kadlecsik MODULE_LICENSE("GPL"); 34fe03d474SJozsef Kadlecsik MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@netfilter.org>"); 3535b8dcf8SJozsef Kadlecsik IP_SET_MODULE_DESC("hash:ip,port,net", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); 3641d22f7bSJozsef Kadlecsik MODULE_ALIAS("ip_set_hash:ip,port,net"); 3741d22f7bSJozsef Kadlecsik 3841d22f7bSJozsef Kadlecsik /* Type specific function prefix */ 395d50e1d8SJozsef Kadlecsik #define HTYPE hash_ipportnet 4041d22f7bSJozsef Kadlecsik 412a7cef2aSJozsef Kadlecsik /* We squeeze the "nomatch" flag into cidr: we don't support cidr == 0 422a7cef2aSJozsef Kadlecsik * However this way we have to store internally cidr - 1, 432a7cef2aSJozsef Kadlecsik * dancing back and forth. 442a7cef2aSJozsef Kadlecsik */ 452a7cef2aSJozsef Kadlecsik #define IP_SET_HASH_WITH_NETS_PACKED 465d50e1d8SJozsef Kadlecsik #define IP_SET_HASH_WITH_PROTO 475d50e1d8SJozsef Kadlecsik #define IP_SET_HASH_WITH_NETS 482a7cef2aSJozsef Kadlecsik 4903c8b234SJozsef Kadlecsik /* IPv4 variant */ 505d50e1d8SJozsef Kadlecsik 515d50e1d8SJozsef Kadlecsik /* Member elements */ 5241d22f7bSJozsef Kadlecsik struct hash_ipportnet4_elem { 5341d22f7bSJozsef Kadlecsik __be32 ip; 5441d22f7bSJozsef Kadlecsik __be32 ip2; 5541d22f7bSJozsef Kadlecsik __be16 port; 562a7cef2aSJozsef Kadlecsik u8 cidr:7; 572a7cef2aSJozsef Kadlecsik u8 nomatch:1; 5841d22f7bSJozsef Kadlecsik u8 proto; 5941d22f7bSJozsef Kadlecsik }; 6041d22f7bSJozsef Kadlecsik 615d50e1d8SJozsef Kadlecsik /* Common functions */ 625d50e1d8SJozsef Kadlecsik 638dea982aSJeremy Sowden static bool 6441d22f7bSJozsef Kadlecsik hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1, 6589dc79b7SJozsef Kadlecsik const struct hash_ipportnet4_elem *ip2, 6689dc79b7SJozsef Kadlecsik u32 *multi) 6741d22f7bSJozsef Kadlecsik { 6841d22f7bSJozsef Kadlecsik return ip1->ip == ip2->ip && 6941d22f7bSJozsef Kadlecsik ip1->ip2 == ip2->ip2 && 7041d22f7bSJozsef Kadlecsik ip1->cidr == ip2->cidr && 7141d22f7bSJozsef Kadlecsik ip1->port == ip2->port && 7241d22f7bSJozsef Kadlecsik ip1->proto == ip2->proto; 7341d22f7bSJozsef Kadlecsik } 7441d22f7bSJozsef Kadlecsik 758dea982aSJeremy Sowden static int 765d50e1d8SJozsef Kadlecsik hash_ipportnet4_do_data_match(const struct hash_ipportnet4_elem *elem) 772a7cef2aSJozsef Kadlecsik { 783e0304a5SJozsef Kadlecsik return elem->nomatch ? -ENOTEMPTY : 1; 792a7cef2aSJozsef Kadlecsik } 802a7cef2aSJozsef Kadlecsik 818dea982aSJeremy Sowden static void 825d50e1d8SJozsef Kadlecsik hash_ipportnet4_data_set_flags(struct hash_ipportnet4_elem *elem, u32 flags) 835d50e1d8SJozsef Kadlecsik { 845d50e1d8SJozsef Kadlecsik elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); 855d50e1d8SJozsef Kadlecsik } 865d50e1d8SJozsef Kadlecsik 878dea982aSJeremy Sowden static void 885d50e1d8SJozsef Kadlecsik hash_ipportnet4_data_reset_flags(struct hash_ipportnet4_elem *elem, u8 *flags) 895d50e1d8SJozsef Kadlecsik { 905d50e1d8SJozsef Kadlecsik swap(*flags, elem->nomatch); 915d50e1d8SJozsef Kadlecsik } 925d50e1d8SJozsef Kadlecsik 938dea982aSJeremy Sowden static void 9441d22f7bSJozsef Kadlecsik hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr) 9541d22f7bSJozsef Kadlecsik { 9641d22f7bSJozsef Kadlecsik elem->ip2 &= ip_set_netmask(cidr); 972a7cef2aSJozsef Kadlecsik elem->cidr = cidr - 1; 9841d22f7bSJozsef Kadlecsik } 9941d22f7bSJozsef Kadlecsik 10041d22f7bSJozsef Kadlecsik static bool 10141d22f7bSJozsef Kadlecsik hash_ipportnet4_data_list(struct sk_buff *skb, 10241d22f7bSJozsef Kadlecsik const struct hash_ipportnet4_elem *data) 10341d22f7bSJozsef Kadlecsik { 1042a7cef2aSJozsef Kadlecsik u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; 1052a7cef2aSJozsef Kadlecsik 1067cf7899dSDavid S. Miller if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || 1077cf7899dSDavid S. Miller nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip2) || 1087cf7899dSDavid S. Miller nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || 1097cf7899dSDavid S. Miller nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) || 1107cf7899dSDavid S. Miller nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || 1117cf7899dSDavid S. Miller (flags && 1127cf7899dSDavid S. Miller nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) 1137cf7899dSDavid S. Miller goto nla_put_failure; 114728a7e69SSergey Popovich return false; 11541d22f7bSJozsef Kadlecsik 11641d22f7bSJozsef Kadlecsik nla_put_failure: 117728a7e69SSergey Popovich return true; 11841d22f7bSJozsef Kadlecsik } 11941d22f7bSJozsef Kadlecsik 1208dea982aSJeremy Sowden static void 1215d50e1d8SJozsef Kadlecsik hash_ipportnet4_data_next(struct hash_ipportnet4_elem *next, 1223d14b171SJozsef Kadlecsik const struct hash_ipportnet4_elem *d) 1233d14b171SJozsef Kadlecsik { 1245d50e1d8SJozsef Kadlecsik next->ip = d->ip; 1255d50e1d8SJozsef Kadlecsik next->port = d->port; 1265d50e1d8SJozsef Kadlecsik next->ip2 = d->ip2; 1273d14b171SJozsef Kadlecsik } 1283d14b171SJozsef Kadlecsik 1295d50e1d8SJozsef Kadlecsik #define MTYPE hash_ipportnet4 1305d50e1d8SJozsef Kadlecsik #define HOST_MASK 32 1315d50e1d8SJozsef Kadlecsik #include "ip_set_hash_gen.h" 1325d50e1d8SJozsef Kadlecsik 13341d22f7bSJozsef Kadlecsik static int 13441d22f7bSJozsef Kadlecsik hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, 135b66554cfSJozsef Kadlecsik const struct xt_action_param *par, 1365d50e1d8SJozsef Kadlecsik enum ipset_adt adt, struct ip_set_adt_opt *opt) 13741d22f7bSJozsef Kadlecsik { 13821956ab2SJozsef Kadlecsik const struct hash_ipportnet4 *h = set->data; 13941d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 1405d50e1d8SJozsef Kadlecsik struct hash_ipportnet4_elem e = { 141f690cbaeSJozsef Kadlecsik .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), 1429b03a5efSJozsef Kadlecsik }; 143ca134ce8SJozsef Kadlecsik struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); 14441d22f7bSJozsef Kadlecsik 14541d22f7bSJozsef Kadlecsik if (adt == IPSET_TEST) 1465d50e1d8SJozsef Kadlecsik e.cidr = HOST_MASK - 1; 14741d22f7bSJozsef Kadlecsik 148ac8cc925SJozsef Kadlecsik if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, 1495d50e1d8SJozsef Kadlecsik &e.port, &e.proto)) 15041d22f7bSJozsef Kadlecsik return -EINVAL; 15141d22f7bSJozsef Kadlecsik 1525d50e1d8SJozsef Kadlecsik ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip); 1535d50e1d8SJozsef Kadlecsik ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2); 1545d50e1d8SJozsef Kadlecsik e.ip2 &= ip_set_netmask(e.cidr + 1); 15541d22f7bSJozsef Kadlecsik 1565d50e1d8SJozsef Kadlecsik return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); 15741d22f7bSJozsef Kadlecsik } 15841d22f7bSJozsef Kadlecsik 15941d22f7bSJozsef Kadlecsik static int 16041d22f7bSJozsef Kadlecsik hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], 1613d14b171SJozsef Kadlecsik enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) 16241d22f7bSJozsef Kadlecsik { 16321956ab2SJozsef Kadlecsik const struct hash_ipportnet4 *h = set->data; 16441d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 1655d50e1d8SJozsef Kadlecsik struct hash_ipportnet4_elem e = { .cidr = HOST_MASK - 1 }; 166ca134ce8SJozsef Kadlecsik struct ip_set_ext ext = IP_SET_INIT_UEXT(set); 16720b2fab4SJozsef Kadlecsik u32 ip = 0, ip_to = 0, p = 0, port, port_to; 1680b8d9073SJozsef Kadlecsik u32 ip2_from = 0, ip2_to = 0, ip2; 1695e0c1eb7SJozsef Kadlecsik bool with_ports = false; 1702a7cef2aSJozsef Kadlecsik u8 cidr; 17141d22f7bSJozsef Kadlecsik int ret; 17241d22f7bSJozsef Kadlecsik 173a212e08eSSergey Popovich if (tb[IPSET_ATTR_LINENO]) 174a212e08eSSergey Popovich *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); 175a212e08eSSergey Popovich 17641d22f7bSJozsef Kadlecsik if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || 17741d22f7bSJozsef Kadlecsik !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || 17841d22f7bSJozsef Kadlecsik !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || 1797dd37bc8SSergey Popovich !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) 18041d22f7bSJozsef Kadlecsik return -IPSET_ERR_PROTOCOL; 18141d22f7bSJozsef Kadlecsik 1828e55d2e5SSergey Popovich ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP], &ip); 1838e55d2e5SSergey Popovich if (ret) 1848e55d2e5SSergey Popovich return ret; 1858e55d2e5SSergey Popovich 1868e55d2e5SSergey Popovich ret = ip_set_get_extensions(set, tb, &ext); 18741d22f7bSJozsef Kadlecsik if (ret) 18841d22f7bSJozsef Kadlecsik return ret; 18941d22f7bSJozsef Kadlecsik 190d0d9e0a5SJozsef Kadlecsik ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2], &ip2_from); 19141d22f7bSJozsef Kadlecsik if (ret) 19241d22f7bSJozsef Kadlecsik return ret; 19341d22f7bSJozsef Kadlecsik 194d0d9e0a5SJozsef Kadlecsik if (tb[IPSET_ATTR_CIDR2]) { 1952a7cef2aSJozsef Kadlecsik cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); 1962a7cef2aSJozsef Kadlecsik if (!cidr || cidr > HOST_MASK) 19741d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_CIDR; 1985d50e1d8SJozsef Kadlecsik e.cidr = cidr - 1; 199d0d9e0a5SJozsef Kadlecsik } 20041d22f7bSJozsef Kadlecsik 2015d50e1d8SJozsef Kadlecsik e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); 20241d22f7bSJozsef Kadlecsik 20341d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_PROTO]) { 2045d50e1d8SJozsef Kadlecsik e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); 2055d50e1d8SJozsef Kadlecsik with_ports = ip_set_proto_with_ports(e.proto); 20641d22f7bSJozsef Kadlecsik 2075d50e1d8SJozsef Kadlecsik if (e.proto == 0) 20841d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_PROTO; 209ca0f6a5cSJozsef Kadlecsik } else { 21041d22f7bSJozsef Kadlecsik return -IPSET_ERR_MISSING_PROTO; 211ca0f6a5cSJozsef Kadlecsik } 21241d22f7bSJozsef Kadlecsik 2135d50e1d8SJozsef Kadlecsik if (!(with_ports || e.proto == IPPROTO_ICMP)) 2145d50e1d8SJozsef Kadlecsik e.port = 0; 21541d22f7bSJozsef Kadlecsik 21643c56e59SJozsef Kadlecsik if (tb[IPSET_ATTR_CADT_FLAGS]) { 2172a7cef2aSJozsef Kadlecsik u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); 218ca0f6a5cSJozsef Kadlecsik 2192a7cef2aSJozsef Kadlecsik if (cadt_flags & IPSET_FLAG_NOMATCH) 22043c56e59SJozsef Kadlecsik flags |= (IPSET_FLAG_NOMATCH << 16); 2212a7cef2aSJozsef Kadlecsik } 2222a7cef2aSJozsef Kadlecsik 223d0d9e0a5SJozsef Kadlecsik with_ports = with_ports && tb[IPSET_ATTR_PORT_TO]; 22441d22f7bSJozsef Kadlecsik if (adt == IPSET_TEST || 225d0d9e0a5SJozsef Kadlecsik !(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports || 226d0d9e0a5SJozsef Kadlecsik tb[IPSET_ATTR_IP2_TO])) { 2275d50e1d8SJozsef Kadlecsik e.ip = htonl(ip); 2285d50e1d8SJozsef Kadlecsik e.ip2 = htonl(ip2_from & ip_set_hostmask(e.cidr + 1)); 2295d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 2300f1799baSJozsef Kadlecsik return ip_set_enomatch(ret, flags, adt, set) ? -ret : 23143c56e59SJozsef Kadlecsik ip_set_eexist(ret, flags) ? 0 : ret; 23241d22f7bSJozsef Kadlecsik } 23341d22f7bSJozsef Kadlecsik 2344fe198e6SJozsef Kadlecsik ip_to = ip; 23541d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_IP_TO]) { 23641d22f7bSJozsef Kadlecsik ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); 23741d22f7bSJozsef Kadlecsik if (ret) 23841d22f7bSJozsef Kadlecsik return ret; 23941d22f7bSJozsef Kadlecsik if (ip > ip_to) 24041d22f7bSJozsef Kadlecsik swap(ip, ip_to); 24141d22f7bSJozsef Kadlecsik } else if (tb[IPSET_ATTR_CIDR]) { 242b3aabd14SJozsef Kadlecsik cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); 24341d22f7bSJozsef Kadlecsik 244cabfd139SSergey Popovich if (!cidr || cidr > HOST_MASK) 24541d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_CIDR; 246e6146e86SJozsef Kadlecsik ip_set_mask_from_to(ip, ip_to, cidr); 247d0d9e0a5SJozsef Kadlecsik } 24841d22f7bSJozsef Kadlecsik 2495d50e1d8SJozsef Kadlecsik port_to = port = ntohs(e.port); 250d0d9e0a5SJozsef Kadlecsik if (tb[IPSET_ATTR_PORT_TO]) { 25141d22f7bSJozsef Kadlecsik port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); 25241d22f7bSJozsef Kadlecsik if (port > port_to) 25341d22f7bSJozsef Kadlecsik swap(port, port_to); 2545e0c1eb7SJozsef Kadlecsik } 2554fe198e6SJozsef Kadlecsik 2564fe198e6SJozsef Kadlecsik ip2_to = ip2_from; 257d0d9e0a5SJozsef Kadlecsik if (tb[IPSET_ATTR_IP2_TO]) { 258d0d9e0a5SJozsef Kadlecsik ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); 259d0d9e0a5SJozsef Kadlecsik if (ret) 260d0d9e0a5SJozsef Kadlecsik return ret; 261d0d9e0a5SJozsef Kadlecsik if (ip2_from > ip2_to) 262d0d9e0a5SJozsef Kadlecsik swap(ip2_from, ip2_to); 263d0d9e0a5SJozsef Kadlecsik if (ip2_from + UINT_MAX == ip2_to) 264d0d9e0a5SJozsef Kadlecsik return -IPSET_ERR_HASH_RANGE; 265ca0f6a5cSJozsef Kadlecsik } else { 2665d50e1d8SJozsef Kadlecsik ip_set_mask_from_to(ip2_from, ip2_to, e.cidr + 1); 267ca0f6a5cSJozsef Kadlecsik } 26841d22f7bSJozsef Kadlecsik 2690b8d9073SJozsef Kadlecsik if (retried) { 2706e27c9b4SJozsef Kadlecsik ip = ntohl(h->next.ip); 2710b8d9073SJozsef Kadlecsik p = ntohs(h->next.port); 2720b8d9073SJozsef Kadlecsik ip2 = ntohl(h->next.ip2); 2730b8d9073SJozsef Kadlecsik } else { 2740b8d9073SJozsef Kadlecsik p = port; 2750b8d9073SJozsef Kadlecsik ip2 = ip2_from; 2760b8d9073SJozsef Kadlecsik } 27748596a8dSJozsef Kadlecsik for (; ip <= ip_to; ip++) { 2785d50e1d8SJozsef Kadlecsik e.ip = htonl(ip); 2793d14b171SJozsef Kadlecsik for (; p <= port_to; p++) { 2805d50e1d8SJozsef Kadlecsik e.port = htons(p); 2810b8d9073SJozsef Kadlecsik do { 2825d50e1d8SJozsef Kadlecsik e.ip2 = htonl(ip2); 2830b8d9073SJozsef Kadlecsik ip2 = ip_set_range_to_cidr(ip2, ip2_to, &cidr); 2845d50e1d8SJozsef Kadlecsik e.cidr = cidr - 1; 2855d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 28641d22f7bSJozsef Kadlecsik 28741d22f7bSJozsef Kadlecsik if (ret && !ip_set_eexist(ret, flags)) 28841d22f7bSJozsef Kadlecsik return ret; 289ca0f6a5cSJozsef Kadlecsik 29041d22f7bSJozsef Kadlecsik ret = 0; 2910b8d9073SJozsef Kadlecsik } while (ip2++ < ip2_to); 2920b8d9073SJozsef Kadlecsik ip2 = ip2_from; 293d0d9e0a5SJozsef Kadlecsik } 2940b8d9073SJozsef Kadlecsik p = port; 2953d14b171SJozsef Kadlecsik } 29641d22f7bSJozsef Kadlecsik return ret; 29741d22f7bSJozsef Kadlecsik } 29841d22f7bSJozsef Kadlecsik 29903c8b234SJozsef Kadlecsik /* IPv6 variant */ 30041d22f7bSJozsef Kadlecsik 30141d22f7bSJozsef Kadlecsik struct hash_ipportnet6_elem { 30241d22f7bSJozsef Kadlecsik union nf_inet_addr ip; 30341d22f7bSJozsef Kadlecsik union nf_inet_addr ip2; 30441d22f7bSJozsef Kadlecsik __be16 port; 3052a7cef2aSJozsef Kadlecsik u8 cidr:7; 3062a7cef2aSJozsef Kadlecsik u8 nomatch:1; 30741d22f7bSJozsef Kadlecsik u8 proto; 30841d22f7bSJozsef Kadlecsik }; 30941d22f7bSJozsef Kadlecsik 3105d50e1d8SJozsef Kadlecsik /* Common functions */ 3115d50e1d8SJozsef Kadlecsik 3128dea982aSJeremy Sowden static bool 31341d22f7bSJozsef Kadlecsik hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1, 31489dc79b7SJozsef Kadlecsik const struct hash_ipportnet6_elem *ip2, 31589dc79b7SJozsef Kadlecsik u32 *multi) 31641d22f7bSJozsef Kadlecsik { 31729e3b160SYOSHIFUJI Hideaki / 吉藤英明 return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) && 31829e3b160SYOSHIFUJI Hideaki / 吉藤英明 ipv6_addr_equal(&ip1->ip2.in6, &ip2->ip2.in6) && 31941d22f7bSJozsef Kadlecsik ip1->cidr == ip2->cidr && 32041d22f7bSJozsef Kadlecsik ip1->port == ip2->port && 32141d22f7bSJozsef Kadlecsik ip1->proto == ip2->proto; 32241d22f7bSJozsef Kadlecsik } 32341d22f7bSJozsef Kadlecsik 3248dea982aSJeremy Sowden static int 3255d50e1d8SJozsef Kadlecsik hash_ipportnet6_do_data_match(const struct hash_ipportnet6_elem *elem) 3262a7cef2aSJozsef Kadlecsik { 3273e0304a5SJozsef Kadlecsik return elem->nomatch ? -ENOTEMPTY : 1; 3282a7cef2aSJozsef Kadlecsik } 3292a7cef2aSJozsef Kadlecsik 3308dea982aSJeremy Sowden static void 3315d50e1d8SJozsef Kadlecsik hash_ipportnet6_data_set_flags(struct hash_ipportnet6_elem *elem, u32 flags) 33241d22f7bSJozsef Kadlecsik { 3335d50e1d8SJozsef Kadlecsik elem->nomatch = !!((flags >> 16) & IPSET_FLAG_NOMATCH); 3345d50e1d8SJozsef Kadlecsik } 3355d50e1d8SJozsef Kadlecsik 3368dea982aSJeremy Sowden static void 3375d50e1d8SJozsef Kadlecsik hash_ipportnet6_data_reset_flags(struct hash_ipportnet6_elem *elem, u8 *flags) 3385d50e1d8SJozsef Kadlecsik { 3395d50e1d8SJozsef Kadlecsik swap(*flags, elem->nomatch); 34041d22f7bSJozsef Kadlecsik } 34141d22f7bSJozsef Kadlecsik 3428dea982aSJeremy Sowden static void 34341d22f7bSJozsef Kadlecsik hash_ipportnet6_data_netmask(struct hash_ipportnet6_elem *elem, u8 cidr) 34441d22f7bSJozsef Kadlecsik { 34541d22f7bSJozsef Kadlecsik ip6_netmask(&elem->ip2, cidr); 3462a7cef2aSJozsef Kadlecsik elem->cidr = cidr - 1; 34741d22f7bSJozsef Kadlecsik } 34841d22f7bSJozsef Kadlecsik 34941d22f7bSJozsef Kadlecsik static bool 35041d22f7bSJozsef Kadlecsik hash_ipportnet6_data_list(struct sk_buff *skb, 35141d22f7bSJozsef Kadlecsik const struct hash_ipportnet6_elem *data) 35241d22f7bSJozsef Kadlecsik { 3532a7cef2aSJozsef Kadlecsik u32 flags = data->nomatch ? IPSET_FLAG_NOMATCH : 0; 3542a7cef2aSJozsef Kadlecsik 3557cf7899dSDavid S. Miller if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) || 3567cf7899dSDavid S. Miller nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) || 3577cf7899dSDavid S. Miller nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || 3587cf7899dSDavid S. Miller nla_put_u8(skb, IPSET_ATTR_CIDR2, data->cidr + 1) || 3597cf7899dSDavid S. Miller nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto) || 3607cf7899dSDavid S. Miller (flags && 3617cf7899dSDavid S. Miller nla_put_net32(skb, IPSET_ATTR_CADT_FLAGS, htonl(flags)))) 3627cf7899dSDavid S. Miller goto nla_put_failure; 363728a7e69SSergey Popovich return false; 36441d22f7bSJozsef Kadlecsik 36541d22f7bSJozsef Kadlecsik nla_put_failure: 366728a7e69SSergey Popovich return true; 36741d22f7bSJozsef Kadlecsik } 36841d22f7bSJozsef Kadlecsik 3698dea982aSJeremy Sowden static void 37021956ab2SJozsef Kadlecsik hash_ipportnet6_data_next(struct hash_ipportnet6_elem *next, 3715d50e1d8SJozsef Kadlecsik const struct hash_ipportnet6_elem *d) 37241d22f7bSJozsef Kadlecsik { 3735d50e1d8SJozsef Kadlecsik next->port = d->port; 37441d22f7bSJozsef Kadlecsik } 37541d22f7bSJozsef Kadlecsik 3765d50e1d8SJozsef Kadlecsik #undef MTYPE 37741d22f7bSJozsef Kadlecsik #undef HOST_MASK 37841d22f7bSJozsef Kadlecsik 3795d50e1d8SJozsef Kadlecsik #define MTYPE hash_ipportnet6 38041d22f7bSJozsef Kadlecsik #define HOST_MASK 128 3815d50e1d8SJozsef Kadlecsik #define IP_SET_EMIT_CREATE 3825d50e1d8SJozsef Kadlecsik #include "ip_set_hash_gen.h" 3833d14b171SJozsef Kadlecsik 38441d22f7bSJozsef Kadlecsik static int 38541d22f7bSJozsef Kadlecsik hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, 386b66554cfSJozsef Kadlecsik const struct xt_action_param *par, 3875d50e1d8SJozsef Kadlecsik enum ipset_adt adt, struct ip_set_adt_opt *opt) 38841d22f7bSJozsef Kadlecsik { 38921956ab2SJozsef Kadlecsik const struct hash_ipportnet6 *h = set->data; 39041d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 3915d50e1d8SJozsef Kadlecsik struct hash_ipportnet6_elem e = { 392f690cbaeSJozsef Kadlecsik .cidr = INIT_CIDR(h->nets[0].cidr[0], HOST_MASK), 3939b03a5efSJozsef Kadlecsik }; 394ca134ce8SJozsef Kadlecsik struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); 39541d22f7bSJozsef Kadlecsik 39641d22f7bSJozsef Kadlecsik if (adt == IPSET_TEST) 3975d50e1d8SJozsef Kadlecsik e.cidr = HOST_MASK - 1; 39841d22f7bSJozsef Kadlecsik 399ac8cc925SJozsef Kadlecsik if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, 4005d50e1d8SJozsef Kadlecsik &e.port, &e.proto)) 40141d22f7bSJozsef Kadlecsik return -EINVAL; 40241d22f7bSJozsef Kadlecsik 4035d50e1d8SJozsef Kadlecsik ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); 4045d50e1d8SJozsef Kadlecsik ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2.in6); 4055d50e1d8SJozsef Kadlecsik ip6_netmask(&e.ip2, e.cidr + 1); 40641d22f7bSJozsef Kadlecsik 4075d50e1d8SJozsef Kadlecsik return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); 40841d22f7bSJozsef Kadlecsik } 40941d22f7bSJozsef Kadlecsik 41041d22f7bSJozsef Kadlecsik static int 41141d22f7bSJozsef Kadlecsik hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], 4123d14b171SJozsef Kadlecsik enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) 41341d22f7bSJozsef Kadlecsik { 41421956ab2SJozsef Kadlecsik const struct hash_ipportnet6 *h = set->data; 41541d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 4165d50e1d8SJozsef Kadlecsik struct hash_ipportnet6_elem e = { .cidr = HOST_MASK - 1 }; 417ca134ce8SJozsef Kadlecsik struct ip_set_ext ext = IP_SET_INIT_UEXT(set); 41841d22f7bSJozsef Kadlecsik u32 port, port_to; 4195e0c1eb7SJozsef Kadlecsik bool with_ports = false; 4202a7cef2aSJozsef Kadlecsik u8 cidr; 42141d22f7bSJozsef Kadlecsik int ret; 42241d22f7bSJozsef Kadlecsik 423a212e08eSSergey Popovich if (tb[IPSET_ATTR_LINENO]) 424a212e08eSSergey Popovich *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); 425a212e08eSSergey Popovich 42641d22f7bSJozsef Kadlecsik if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || 42741d22f7bSJozsef Kadlecsik !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || 42841d22f7bSJozsef Kadlecsik !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || 4292c227f27SSergey Popovich !ip_set_optattr_netorder(tb, IPSET_ATTR_CADT_FLAGS))) 43041d22f7bSJozsef Kadlecsik return -IPSET_ERR_PROTOCOL; 431d0d9e0a5SJozsef Kadlecsik if (unlikely(tb[IPSET_ATTR_IP_TO])) 432d0d9e0a5SJozsef Kadlecsik return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; 4332c227f27SSergey Popovich if (unlikely(tb[IPSET_ATTR_CIDR])) { 4348851e799SJozsef Kadlecsik cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); 4352c227f27SSergey Popovich 4362c227f27SSergey Popovich if (cidr != HOST_MASK) 4372c227f27SSergey Popovich return -IPSET_ERR_INVALID_CIDR; 4382c227f27SSergey Popovich } 43941d22f7bSJozsef Kadlecsik 4408e55d2e5SSergey Popovich ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip); 4418e55d2e5SSergey Popovich if (ret) 4428e55d2e5SSergey Popovich return ret; 4438e55d2e5SSergey Popovich 4448e55d2e5SSergey Popovich ret = ip_set_get_extensions(set, tb, &ext); 44541d22f7bSJozsef Kadlecsik if (ret) 44641d22f7bSJozsef Kadlecsik return ret; 44741d22f7bSJozsef Kadlecsik 4485d50e1d8SJozsef Kadlecsik ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip2); 44941d22f7bSJozsef Kadlecsik if (ret) 45041d22f7bSJozsef Kadlecsik return ret; 45141d22f7bSJozsef Kadlecsik 4522a7cef2aSJozsef Kadlecsik if (tb[IPSET_ATTR_CIDR2]) { 4532a7cef2aSJozsef Kadlecsik cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); 4542a7cef2aSJozsef Kadlecsik if (!cidr || cidr > HOST_MASK) 45541d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_CIDR; 4565d50e1d8SJozsef Kadlecsik e.cidr = cidr - 1; 4572a7cef2aSJozsef Kadlecsik } 45841d22f7bSJozsef Kadlecsik 4595d50e1d8SJozsef Kadlecsik ip6_netmask(&e.ip2, e.cidr + 1); 46041d22f7bSJozsef Kadlecsik 4615d50e1d8SJozsef Kadlecsik e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); 46241d22f7bSJozsef Kadlecsik 46341d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_PROTO]) { 4645d50e1d8SJozsef Kadlecsik e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); 4655d50e1d8SJozsef Kadlecsik with_ports = ip_set_proto_with_ports(e.proto); 46641d22f7bSJozsef Kadlecsik 4675d50e1d8SJozsef Kadlecsik if (e.proto == 0) 46841d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_PROTO; 469ca0f6a5cSJozsef Kadlecsik } else { 47041d22f7bSJozsef Kadlecsik return -IPSET_ERR_MISSING_PROTO; 471ca0f6a5cSJozsef Kadlecsik } 47241d22f7bSJozsef Kadlecsik 4735d50e1d8SJozsef Kadlecsik if (!(with_ports || e.proto == IPPROTO_ICMPV6)) 4745d50e1d8SJozsef Kadlecsik e.port = 0; 47541d22f7bSJozsef Kadlecsik 47643c56e59SJozsef Kadlecsik if (tb[IPSET_ATTR_CADT_FLAGS]) { 4772a7cef2aSJozsef Kadlecsik u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); 478ca0f6a5cSJozsef Kadlecsik 4792a7cef2aSJozsef Kadlecsik if (cadt_flags & IPSET_FLAG_NOMATCH) 48043c56e59SJozsef Kadlecsik flags |= (IPSET_FLAG_NOMATCH << 16); 4812a7cef2aSJozsef Kadlecsik } 4822a7cef2aSJozsef Kadlecsik 4835e0c1eb7SJozsef Kadlecsik if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { 4845d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 4850f1799baSJozsef Kadlecsik return ip_set_enomatch(ret, flags, adt, set) ? -ret : 48643c56e59SJozsef Kadlecsik ip_set_eexist(ret, flags) ? 0 : ret; 48741d22f7bSJozsef Kadlecsik } 48841d22f7bSJozsef Kadlecsik 4895d50e1d8SJozsef Kadlecsik port = ntohs(e.port); 49041d22f7bSJozsef Kadlecsik port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); 49141d22f7bSJozsef Kadlecsik if (port > port_to) 49241d22f7bSJozsef Kadlecsik swap(port, port_to); 49341d22f7bSJozsef Kadlecsik 4943d14b171SJozsef Kadlecsik if (retried) 4956e27c9b4SJozsef Kadlecsik port = ntohs(h->next.port); 49641d22f7bSJozsef Kadlecsik for (; port <= port_to; port++) { 4975d50e1d8SJozsef Kadlecsik e.port = htons(port); 4985d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 49941d22f7bSJozsef Kadlecsik 50041d22f7bSJozsef Kadlecsik if (ret && !ip_set_eexist(ret, flags)) 50141d22f7bSJozsef Kadlecsik return ret; 502ca0f6a5cSJozsef Kadlecsik 50341d22f7bSJozsef Kadlecsik ret = 0; 50441d22f7bSJozsef Kadlecsik } 50541d22f7bSJozsef Kadlecsik return ret; 50641d22f7bSJozsef Kadlecsik } 50741d22f7bSJozsef Kadlecsik 50841d22f7bSJozsef Kadlecsik static struct ip_set_type hash_ipportnet_type __read_mostly = { 50941d22f7bSJozsef Kadlecsik .name = "hash:ip,port,net", 51041d22f7bSJozsef Kadlecsik .protocol = IPSET_PROTOCOL, 5113e0304a5SJozsef Kadlecsik .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2 | 5123e0304a5SJozsef Kadlecsik IPSET_TYPE_NOMATCH, 51341d22f7bSJozsef Kadlecsik .dimension = IPSET_DIM_THREE, 514c15f1c83SJan Engelhardt .family = NFPROTO_UNSPEC, 51535b8dcf8SJozsef Kadlecsik .revision_min = IPSET_TYPE_REV_MIN, 51635b8dcf8SJozsef Kadlecsik .revision_max = IPSET_TYPE_REV_MAX, 517*ccf0a4b7SJozsef Kadlecsik .create_flags[IPSET_TYPE_REV_MAX] = IPSET_CREATE_FLAG_BUCKETSIZE, 51841d22f7bSJozsef Kadlecsik .create = hash_ipportnet_create, 51941d22f7bSJozsef Kadlecsik .create_policy = { 52041d22f7bSJozsef Kadlecsik [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, 52141d22f7bSJozsef Kadlecsik [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, 522*ccf0a4b7SJozsef Kadlecsik [IPSET_ATTR_BUCKETSIZE] = { .type = NLA_U8 }, 52341d22f7bSJozsef Kadlecsik [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, 52441d22f7bSJozsef Kadlecsik [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, 5255d50e1d8SJozsef Kadlecsik [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, 52641d22f7bSJozsef Kadlecsik }, 52741d22f7bSJozsef Kadlecsik .adt_policy = { 52841d22f7bSJozsef Kadlecsik [IPSET_ATTR_IP] = { .type = NLA_NESTED }, 52941d22f7bSJozsef Kadlecsik [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, 53041d22f7bSJozsef Kadlecsik [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, 531d0d9e0a5SJozsef Kadlecsik [IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED }, 53241d22f7bSJozsef Kadlecsik [IPSET_ATTR_PORT] = { .type = NLA_U16 }, 53341d22f7bSJozsef Kadlecsik [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, 53441d22f7bSJozsef Kadlecsik [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, 53541d22f7bSJozsef Kadlecsik [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, 53641d22f7bSJozsef Kadlecsik [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, 5372a7cef2aSJozsef Kadlecsik [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, 53841d22f7bSJozsef Kadlecsik [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, 53941d22f7bSJozsef Kadlecsik [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, 54000d71b27SJozsef Kadlecsik [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, 54100d71b27SJozsef Kadlecsik [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, 54203726186SSergey Popovich [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING, 54303726186SSergey Popovich .len = IPSET_MAX_COMMENT_SIZE }, 544af331419SAnton Danilov [IPSET_ATTR_SKBMARK] = { .type = NLA_U64 }, 545af331419SAnton Danilov [IPSET_ATTR_SKBPRIO] = { .type = NLA_U32 }, 546af331419SAnton Danilov [IPSET_ATTR_SKBQUEUE] = { .type = NLA_U16 }, 54741d22f7bSJozsef Kadlecsik }, 54841d22f7bSJozsef Kadlecsik .me = THIS_MODULE, 54941d22f7bSJozsef Kadlecsik }; 55041d22f7bSJozsef Kadlecsik 55141d22f7bSJozsef Kadlecsik static int __init 55241d22f7bSJozsef Kadlecsik hash_ipportnet_init(void) 55341d22f7bSJozsef Kadlecsik { 55441d22f7bSJozsef Kadlecsik return ip_set_type_register(&hash_ipportnet_type); 55541d22f7bSJozsef Kadlecsik } 55641d22f7bSJozsef Kadlecsik 55741d22f7bSJozsef Kadlecsik static void __exit 55841d22f7bSJozsef Kadlecsik hash_ipportnet_fini(void) 55941d22f7bSJozsef Kadlecsik { 56018f84d41SJozsef Kadlecsik rcu_barrier(); 56141d22f7bSJozsef Kadlecsik ip_set_type_unregister(&hash_ipportnet_type); 56241d22f7bSJozsef Kadlecsik } 56341d22f7bSJozsef Kadlecsik 56441d22f7bSJozsef Kadlecsik module_init(hash_ipportnet_init); 56541d22f7bSJozsef Kadlecsik module_exit(hash_ipportnet_fini); 566