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 { 1415d50e1d8SJozsef Kadlecsik const struct hash_ipportnet *h = set->data; 14241d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 1435d50e1d8SJozsef Kadlecsik struct hash_ipportnet4_elem e = { 144a04d8b6bSJozsef Kadlecsik .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1, 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 { 1665d50e1d8SJozsef Kadlecsik const struct hash_ipportnet *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; 21241d22f7bSJozsef Kadlecsik } else 21341d22f7bSJozsef Kadlecsik return -IPSET_ERR_MISSING_PROTO; 21441d22f7bSJozsef Kadlecsik 2155d50e1d8SJozsef Kadlecsik if (!(with_ports || e.proto == IPPROTO_ICMP)) 2165d50e1d8SJozsef Kadlecsik e.port = 0; 21741d22f7bSJozsef Kadlecsik 21843c56e59SJozsef Kadlecsik if (tb[IPSET_ATTR_CADT_FLAGS]) { 2192a7cef2aSJozsef Kadlecsik u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); 2202a7cef2aSJozsef Kadlecsik if (cadt_flags & IPSET_FLAG_NOMATCH) 22143c56e59SJozsef Kadlecsik flags |= (IPSET_FLAG_NOMATCH << 16); 2222a7cef2aSJozsef Kadlecsik } 2232a7cef2aSJozsef Kadlecsik 224d0d9e0a5SJozsef Kadlecsik with_ports = with_ports && tb[IPSET_ATTR_PORT_TO]; 22541d22f7bSJozsef Kadlecsik if (adt == IPSET_TEST || 226d0d9e0a5SJozsef Kadlecsik !(tb[IPSET_ATTR_CIDR] || tb[IPSET_ATTR_IP_TO] || with_ports || 227d0d9e0a5SJozsef Kadlecsik tb[IPSET_ATTR_IP2_TO])) { 2285d50e1d8SJozsef Kadlecsik e.ip = htonl(ip); 2295d50e1d8SJozsef Kadlecsik e.ip2 = htonl(ip2_from & ip_set_hostmask(e.cidr + 1)); 2305d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 2310f1799baSJozsef Kadlecsik return ip_set_enomatch(ret, flags, adt, set) ? -ret : 23243c56e59SJozsef Kadlecsik ip_set_eexist(ret, flags) ? 0 : ret; 23341d22f7bSJozsef Kadlecsik } 23441d22f7bSJozsef Kadlecsik 2354fe198e6SJozsef Kadlecsik ip_to = ip; 23641d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_IP_TO]) { 23741d22f7bSJozsef Kadlecsik ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); 23841d22f7bSJozsef Kadlecsik if (ret) 23941d22f7bSJozsef Kadlecsik return ret; 24041d22f7bSJozsef Kadlecsik if (ip > ip_to) 24141d22f7bSJozsef Kadlecsik swap(ip, ip_to); 24241d22f7bSJozsef Kadlecsik } else if (tb[IPSET_ATTR_CIDR]) { 243b3aabd14SJozsef Kadlecsik cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); 24441d22f7bSJozsef Kadlecsik 245cabfd139SSergey Popovich if (!cidr || cidr > HOST_MASK) 24641d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_CIDR; 247e6146e86SJozsef Kadlecsik ip_set_mask_from_to(ip, ip_to, cidr); 248d0d9e0a5SJozsef Kadlecsik } 24941d22f7bSJozsef Kadlecsik 2505d50e1d8SJozsef Kadlecsik port_to = port = ntohs(e.port); 251d0d9e0a5SJozsef Kadlecsik if (tb[IPSET_ATTR_PORT_TO]) { 25241d22f7bSJozsef Kadlecsik port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); 25341d22f7bSJozsef Kadlecsik if (port > port_to) 25441d22f7bSJozsef Kadlecsik swap(port, port_to); 2555e0c1eb7SJozsef Kadlecsik } 2564fe198e6SJozsef Kadlecsik 2574fe198e6SJozsef Kadlecsik ip2_to = ip2_from; 258d0d9e0a5SJozsef Kadlecsik if (tb[IPSET_ATTR_IP2_TO]) { 259d0d9e0a5SJozsef Kadlecsik ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP2_TO], &ip2_to); 260d0d9e0a5SJozsef Kadlecsik if (ret) 261d0d9e0a5SJozsef Kadlecsik return ret; 262d0d9e0a5SJozsef Kadlecsik if (ip2_from > ip2_to) 263d0d9e0a5SJozsef Kadlecsik swap(ip2_from, ip2_to); 264d0d9e0a5SJozsef Kadlecsik if (ip2_from + UINT_MAX == ip2_to) 265d0d9e0a5SJozsef Kadlecsik return -IPSET_ERR_HASH_RANGE; 2665d50e1d8SJozsef Kadlecsik } else 2675d50e1d8SJozsef Kadlecsik ip_set_mask_from_to(ip2_from, ip2_to, e.cidr + 1); 26841d22f7bSJozsef Kadlecsik 2693d14b171SJozsef Kadlecsik if (retried) 2706e27c9b4SJozsef Kadlecsik ip = ntohl(h->next.ip); 2713d14b171SJozsef Kadlecsik for (; !before(ip_to, ip); ip++) { 2725d50e1d8SJozsef Kadlecsik e.ip = htonl(ip); 2736e27c9b4SJozsef Kadlecsik p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port) 2746e27c9b4SJozsef Kadlecsik : port; 2753d14b171SJozsef Kadlecsik for (; p <= port_to; p++) { 2765d50e1d8SJozsef Kadlecsik e.port = htons(p); 277ca134ce8SJozsef Kadlecsik ip2 = retried && 278ca134ce8SJozsef Kadlecsik ip == ntohl(h->next.ip) && 279ca134ce8SJozsef Kadlecsik p == ntohs(h->next.port) 2806e27c9b4SJozsef Kadlecsik ? ntohl(h->next.ip2) : ip2_from; 281d0d9e0a5SJozsef Kadlecsik while (!after(ip2, ip2_to)) { 2825d50e1d8SJozsef Kadlecsik e.ip2 = htonl(ip2); 283d0d9e0a5SJozsef Kadlecsik ip2_last = ip_set_range_to_cidr(ip2, ip2_to, 2842a7cef2aSJozsef Kadlecsik &cidr); 2855d50e1d8SJozsef Kadlecsik e.cidr = cidr - 1; 2865d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 28741d22f7bSJozsef Kadlecsik 28841d22f7bSJozsef Kadlecsik if (ret && !ip_set_eexist(ret, flags)) 28941d22f7bSJozsef Kadlecsik return ret; 29041d22f7bSJozsef Kadlecsik else 29141d22f7bSJozsef Kadlecsik ret = 0; 292d0d9e0a5SJozsef Kadlecsik ip2 = ip2_last + 1; 293d0d9e0a5SJozsef Kadlecsik } 29441d22f7bSJozsef Kadlecsik } 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 31241d22f7bSJozsef Kadlecsik static inline 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 3243e0304a5SJozsef Kadlecsik static inline 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 3302a7cef2aSJozsef Kadlecsik static inline 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 3365d50e1d8SJozsef Kadlecsik static inline 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 34241d22f7bSJozsef Kadlecsik static inline 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 3695d50e1d8SJozsef Kadlecsik static inline void 3705d50e1d8SJozsef Kadlecsik hash_ipportnet6_data_next(struct hash_ipportnet4_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 { 3895d50e1d8SJozsef Kadlecsik const struct hash_ipportnet *h = set->data; 39041d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 3915d50e1d8SJozsef Kadlecsik struct hash_ipportnet6_elem e = { 392a04d8b6bSJozsef Kadlecsik .cidr = IP_SET_INIT_CIDR(h->nets[0].cidr[0], HOST_MASK) - 1, 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 { 4145d50e1d8SJozsef Kadlecsik const struct hash_ipportnet *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])) { 4342c227f27SSergey Popovich u8 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; 46941d22f7bSJozsef Kadlecsik } else 47041d22f7bSJozsef Kadlecsik return -IPSET_ERR_MISSING_PROTO; 47141d22f7bSJozsef Kadlecsik 4725d50e1d8SJozsef Kadlecsik if (!(with_ports || e.proto == IPPROTO_ICMPV6)) 4735d50e1d8SJozsef Kadlecsik e.port = 0; 47441d22f7bSJozsef Kadlecsik 47543c56e59SJozsef Kadlecsik if (tb[IPSET_ATTR_CADT_FLAGS]) { 4762a7cef2aSJozsef Kadlecsik u32 cadt_flags = ip_set_get_h32(tb[IPSET_ATTR_CADT_FLAGS]); 4772a7cef2aSJozsef Kadlecsik if (cadt_flags & IPSET_FLAG_NOMATCH) 47843c56e59SJozsef Kadlecsik flags |= (IPSET_FLAG_NOMATCH << 16); 4792a7cef2aSJozsef Kadlecsik } 4802a7cef2aSJozsef Kadlecsik 4815e0c1eb7SJozsef Kadlecsik if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { 4825d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 4830f1799baSJozsef Kadlecsik return ip_set_enomatch(ret, flags, adt, set) ? -ret : 48443c56e59SJozsef Kadlecsik ip_set_eexist(ret, flags) ? 0 : ret; 48541d22f7bSJozsef Kadlecsik } 48641d22f7bSJozsef Kadlecsik 4875d50e1d8SJozsef Kadlecsik port = ntohs(e.port); 48841d22f7bSJozsef Kadlecsik port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); 48941d22f7bSJozsef Kadlecsik if (port > port_to) 49041d22f7bSJozsef Kadlecsik swap(port, port_to); 49141d22f7bSJozsef Kadlecsik 4923d14b171SJozsef Kadlecsik if (retried) 4936e27c9b4SJozsef Kadlecsik port = ntohs(h->next.port); 49441d22f7bSJozsef Kadlecsik for (; port <= port_to; port++) { 4955d50e1d8SJozsef Kadlecsik e.port = htons(port); 4965d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 49741d22f7bSJozsef Kadlecsik 49841d22f7bSJozsef Kadlecsik if (ret && !ip_set_eexist(ret, flags)) 49941d22f7bSJozsef Kadlecsik return ret; 50041d22f7bSJozsef Kadlecsik else 50141d22f7bSJozsef Kadlecsik ret = 0; 50241d22f7bSJozsef Kadlecsik } 50341d22f7bSJozsef Kadlecsik return ret; 50441d22f7bSJozsef Kadlecsik } 50541d22f7bSJozsef Kadlecsik 50641d22f7bSJozsef Kadlecsik static struct ip_set_type hash_ipportnet_type __read_mostly = { 50741d22f7bSJozsef Kadlecsik .name = "hash:ip,port,net", 50841d22f7bSJozsef Kadlecsik .protocol = IPSET_PROTOCOL, 5093e0304a5SJozsef Kadlecsik .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2 | 5103e0304a5SJozsef Kadlecsik IPSET_TYPE_NOMATCH, 51141d22f7bSJozsef Kadlecsik .dimension = IPSET_DIM_THREE, 512c15f1c83SJan Engelhardt .family = NFPROTO_UNSPEC, 51335b8dcf8SJozsef Kadlecsik .revision_min = IPSET_TYPE_REV_MIN, 51435b8dcf8SJozsef Kadlecsik .revision_max = IPSET_TYPE_REV_MAX, 51541d22f7bSJozsef Kadlecsik .create = hash_ipportnet_create, 51641d22f7bSJozsef Kadlecsik .create_policy = { 51741d22f7bSJozsef Kadlecsik [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, 51841d22f7bSJozsef Kadlecsik [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, 51941d22f7bSJozsef Kadlecsik [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, 52041d22f7bSJozsef Kadlecsik [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, 52141d22f7bSJozsef Kadlecsik [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, 5225d50e1d8SJozsef Kadlecsik [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, 52341d22f7bSJozsef Kadlecsik }, 52441d22f7bSJozsef Kadlecsik .adt_policy = { 52541d22f7bSJozsef Kadlecsik [IPSET_ATTR_IP] = { .type = NLA_NESTED }, 52641d22f7bSJozsef Kadlecsik [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, 52741d22f7bSJozsef Kadlecsik [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, 528d0d9e0a5SJozsef Kadlecsik [IPSET_ATTR_IP2_TO] = { .type = NLA_NESTED }, 52941d22f7bSJozsef Kadlecsik [IPSET_ATTR_PORT] = { .type = NLA_U16 }, 53041d22f7bSJozsef Kadlecsik [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, 53141d22f7bSJozsef Kadlecsik [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, 53241d22f7bSJozsef Kadlecsik [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, 53341d22f7bSJozsef Kadlecsik [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, 5342a7cef2aSJozsef Kadlecsik [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, 53541d22f7bSJozsef Kadlecsik [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, 53641d22f7bSJozsef Kadlecsik [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, 53700d71b27SJozsef Kadlecsik [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, 53800d71b27SJozsef Kadlecsik [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, 53903726186SSergey Popovich [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING, 54003726186SSergey Popovich .len = IPSET_MAX_COMMENT_SIZE }, 541af331419SAnton Danilov [IPSET_ATTR_SKBMARK] = { .type = NLA_U64 }, 542af331419SAnton Danilov [IPSET_ATTR_SKBPRIO] = { .type = NLA_U32 }, 543af331419SAnton Danilov [IPSET_ATTR_SKBQUEUE] = { .type = NLA_U16 }, 54441d22f7bSJozsef Kadlecsik }, 54541d22f7bSJozsef Kadlecsik .me = THIS_MODULE, 54641d22f7bSJozsef Kadlecsik }; 54741d22f7bSJozsef Kadlecsik 54841d22f7bSJozsef Kadlecsik static int __init 54941d22f7bSJozsef Kadlecsik hash_ipportnet_init(void) 55041d22f7bSJozsef Kadlecsik { 55141d22f7bSJozsef Kadlecsik return ip_set_type_register(&hash_ipportnet_type); 55241d22f7bSJozsef Kadlecsik } 55341d22f7bSJozsef Kadlecsik 55441d22f7bSJozsef Kadlecsik static void __exit 55541d22f7bSJozsef Kadlecsik hash_ipportnet_fini(void) 55641d22f7bSJozsef Kadlecsik { 55741d22f7bSJozsef Kadlecsik ip_set_type_unregister(&hash_ipportnet_type); 55841d22f7bSJozsef Kadlecsik } 55941d22f7bSJozsef Kadlecsik 56041d22f7bSJozsef Kadlecsik module_init(hash_ipportnet_init); 56141d22f7bSJozsef Kadlecsik module_exit(hash_ipportnet_fini); 562