1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 25d50e1d8SJozsef Kadlecsik /* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> 35663bc30SJozsef Kadlecsik */ 45663bc30SJozsef Kadlecsik 55663bc30SJozsef Kadlecsik /* Kernel module implementing an IP set type: the hash:ip,port,ip type */ 65663bc30SJozsef Kadlecsik 75663bc30SJozsef Kadlecsik #include <linux/jhash.h> 85663bc30SJozsef Kadlecsik #include <linux/module.h> 95663bc30SJozsef Kadlecsik #include <linux/ip.h> 105663bc30SJozsef Kadlecsik #include <linux/skbuff.h> 115663bc30SJozsef Kadlecsik #include <linux/errno.h> 125663bc30SJozsef Kadlecsik #include <linux/random.h> 135663bc30SJozsef Kadlecsik #include <net/ip.h> 145663bc30SJozsef Kadlecsik #include <net/ipv6.h> 155663bc30SJozsef Kadlecsik #include <net/netlink.h> 165663bc30SJozsef Kadlecsik #include <net/tcp.h> 175663bc30SJozsef Kadlecsik 185663bc30SJozsef Kadlecsik #include <linux/netfilter.h> 195663bc30SJozsef Kadlecsik #include <linux/netfilter/ipset/pfxlen.h> 205663bc30SJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set.h> 215663bc30SJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_getport.h> 225663bc30SJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_hash.h> 235663bc30SJozsef Kadlecsik 2435b8dcf8SJozsef Kadlecsik #define IPSET_TYPE_REV_MIN 0 2500d71b27SJozsef Kadlecsik /* 1 SCTP and UDPLITE support added */ 26fda75c6dSOliver Smith /* 2 Counters support added */ 2707cf8f5aSJosh Hunt /* 3 Comments support added */ 28af331419SAnton Danilov /* 4 Forceadd support added */ 29af331419SAnton Danilov #define IPSET_TYPE_REV_MAX 5 /* skbinfo support added */ 3010111a6eSJozsef Kadlecsik 315663bc30SJozsef Kadlecsik MODULE_LICENSE("GPL"); 325663bc30SJozsef Kadlecsik MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); 3335b8dcf8SJozsef Kadlecsik IP_SET_MODULE_DESC("hash:ip,port,ip", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); 345663bc30SJozsef Kadlecsik MODULE_ALIAS("ip_set_hash:ip,port,ip"); 355663bc30SJozsef Kadlecsik 365663bc30SJozsef Kadlecsik /* Type specific function prefix */ 375d50e1d8SJozsef Kadlecsik #define HTYPE hash_ipportip 385663bc30SJozsef Kadlecsik 3903c8b234SJozsef Kadlecsik /* IPv4 variant */ 405663bc30SJozsef Kadlecsik 415d50e1d8SJozsef Kadlecsik /* Member elements */ 425663bc30SJozsef Kadlecsik struct hash_ipportip4_elem { 435663bc30SJozsef Kadlecsik __be32 ip; 445663bc30SJozsef Kadlecsik __be32 ip2; 455663bc30SJozsef Kadlecsik __be16 port; 465663bc30SJozsef Kadlecsik u8 proto; 475663bc30SJozsef Kadlecsik u8 padding; 485663bc30SJozsef Kadlecsik }; 495663bc30SJozsef Kadlecsik 505663bc30SJozsef Kadlecsik static inline bool 515663bc30SJozsef Kadlecsik hash_ipportip4_data_equal(const struct hash_ipportip4_elem *ip1, 5289dc79b7SJozsef Kadlecsik const struct hash_ipportip4_elem *ip2, 5389dc79b7SJozsef Kadlecsik u32 *multi) 545663bc30SJozsef Kadlecsik { 555663bc30SJozsef Kadlecsik return ip1->ip == ip2->ip && 565663bc30SJozsef Kadlecsik ip1->ip2 == ip2->ip2 && 575663bc30SJozsef Kadlecsik ip1->port == ip2->port && 585663bc30SJozsef Kadlecsik ip1->proto == ip2->proto; 595663bc30SJozsef Kadlecsik } 605663bc30SJozsef Kadlecsik 615663bc30SJozsef Kadlecsik static bool 625663bc30SJozsef Kadlecsik hash_ipportip4_data_list(struct sk_buff *skb, 635663bc30SJozsef Kadlecsik const struct hash_ipportip4_elem *data) 645663bc30SJozsef Kadlecsik { 657cf7899dSDavid S. Miller if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || 667cf7899dSDavid S. Miller nla_put_ipaddr4(skb, IPSET_ATTR_IP2, data->ip2) || 677cf7899dSDavid S. Miller nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || 687cf7899dSDavid S. Miller nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto)) 697cf7899dSDavid S. Miller goto nla_put_failure; 70728a7e69SSergey Popovich return false; 715663bc30SJozsef Kadlecsik 725663bc30SJozsef Kadlecsik nla_put_failure: 73728a7e69SSergey Popovich return true; 745663bc30SJozsef Kadlecsik } 755663bc30SJozsef Kadlecsik 763d14b171SJozsef Kadlecsik static inline void 775d50e1d8SJozsef Kadlecsik hash_ipportip4_data_next(struct hash_ipportip4_elem *next, 783d14b171SJozsef Kadlecsik const struct hash_ipportip4_elem *d) 793d14b171SJozsef Kadlecsik { 805d50e1d8SJozsef Kadlecsik next->ip = d->ip; 815d50e1d8SJozsef Kadlecsik next->port = d->port; 823d14b171SJozsef Kadlecsik } 833d14b171SJozsef Kadlecsik 845d50e1d8SJozsef Kadlecsik /* Common functions */ 855d50e1d8SJozsef Kadlecsik #define MTYPE hash_ipportip4 865d50e1d8SJozsef Kadlecsik #define HOST_MASK 32 875d50e1d8SJozsef Kadlecsik #include "ip_set_hash_gen.h" 885d50e1d8SJozsef Kadlecsik 895663bc30SJozsef Kadlecsik static int 905663bc30SJozsef Kadlecsik hash_ipportip4_kadt(struct ip_set *set, const struct sk_buff *skb, 91b66554cfSJozsef Kadlecsik const struct xt_action_param *par, 925d50e1d8SJozsef Kadlecsik enum ipset_adt adt, struct ip_set_adt_opt *opt) 935663bc30SJozsef Kadlecsik { 945663bc30SJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 9594729f8aSMark Rustad struct hash_ipportip4_elem e = { .ip = 0 }; 96ca134ce8SJozsef Kadlecsik struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); 975663bc30SJozsef Kadlecsik 98ac8cc925SJozsef Kadlecsik if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, 995d50e1d8SJozsef Kadlecsik &e.port, &e.proto)) 1005663bc30SJozsef Kadlecsik return -EINVAL; 1015663bc30SJozsef Kadlecsik 1025d50e1d8SJozsef Kadlecsik ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip); 1035d50e1d8SJozsef Kadlecsik ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2); 1045d50e1d8SJozsef Kadlecsik return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); 1055663bc30SJozsef Kadlecsik } 1065663bc30SJozsef Kadlecsik 1075663bc30SJozsef Kadlecsik static int 1085663bc30SJozsef Kadlecsik hash_ipportip4_uadt(struct ip_set *set, struct nlattr *tb[], 1093d14b171SJozsef Kadlecsik enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) 1105663bc30SJozsef Kadlecsik { 11121956ab2SJozsef Kadlecsik const struct hash_ipportip4 *h = set->data; 1125663bc30SJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 11394729f8aSMark Rustad struct hash_ipportip4_elem e = { .ip = 0 }; 114ca134ce8SJozsef Kadlecsik struct ip_set_ext ext = IP_SET_INIT_UEXT(set); 11520b2fab4SJozsef Kadlecsik u32 ip, ip_to = 0, p = 0, port, port_to; 1165e0c1eb7SJozsef Kadlecsik bool with_ports = false; 1175663bc30SJozsef Kadlecsik int ret; 1185663bc30SJozsef Kadlecsik 119a212e08eSSergey Popovich if (tb[IPSET_ATTR_LINENO]) 120a212e08eSSergey Popovich *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); 121a212e08eSSergey Popovich 1225663bc30SJozsef Kadlecsik if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || 1235663bc30SJozsef Kadlecsik !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || 1247dd37bc8SSergey Popovich !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO))) 1255663bc30SJozsef Kadlecsik return -IPSET_ERR_PROTOCOL; 1265663bc30SJozsef Kadlecsik 1278e55d2e5SSergey Popovich ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &e.ip); 1288e55d2e5SSergey Popovich if (ret) 1298e55d2e5SSergey Popovich return ret; 1308e55d2e5SSergey Popovich 1318e55d2e5SSergey Popovich ret = ip_set_get_extensions(set, tb, &ext); 1325663bc30SJozsef Kadlecsik if (ret) 1335663bc30SJozsef Kadlecsik return ret; 1345663bc30SJozsef Kadlecsik 1355d50e1d8SJozsef Kadlecsik ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP2], &e.ip2); 1365663bc30SJozsef Kadlecsik if (ret) 1375663bc30SJozsef Kadlecsik return ret; 1385663bc30SJozsef Kadlecsik 1395d50e1d8SJozsef Kadlecsik e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); 1405663bc30SJozsef Kadlecsik 1415663bc30SJozsef Kadlecsik if (tb[IPSET_ATTR_PROTO]) { 1425d50e1d8SJozsef Kadlecsik e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); 1435d50e1d8SJozsef Kadlecsik with_ports = ip_set_proto_with_ports(e.proto); 1445663bc30SJozsef Kadlecsik 1455d50e1d8SJozsef Kadlecsik if (e.proto == 0) 1465663bc30SJozsef Kadlecsik return -IPSET_ERR_INVALID_PROTO; 147ca0f6a5cSJozsef Kadlecsik } else { 1485663bc30SJozsef Kadlecsik return -IPSET_ERR_MISSING_PROTO; 149ca0f6a5cSJozsef Kadlecsik } 1505663bc30SJozsef Kadlecsik 1515d50e1d8SJozsef Kadlecsik if (!(with_ports || e.proto == IPPROTO_ICMP)) 1525d50e1d8SJozsef Kadlecsik e.port = 0; 1535663bc30SJozsef Kadlecsik 1545663bc30SJozsef Kadlecsik if (adt == IPSET_TEST || 1555663bc30SJozsef Kadlecsik !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || 1565663bc30SJozsef Kadlecsik tb[IPSET_ATTR_PORT_TO])) { 1575d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 1585663bc30SJozsef Kadlecsik return ip_set_eexist(ret, flags) ? 0 : ret; 1595663bc30SJozsef Kadlecsik } 1605663bc30SJozsef Kadlecsik 1615d50e1d8SJozsef Kadlecsik ip_to = ip = ntohl(e.ip); 1625663bc30SJozsef Kadlecsik if (tb[IPSET_ATTR_IP_TO]) { 1635663bc30SJozsef Kadlecsik ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); 1645663bc30SJozsef Kadlecsik if (ret) 1655663bc30SJozsef Kadlecsik return ret; 1665663bc30SJozsef Kadlecsik if (ip > ip_to) 1675663bc30SJozsef Kadlecsik swap(ip, ip_to); 1685663bc30SJozsef Kadlecsik } else if (tb[IPSET_ATTR_CIDR]) { 1695663bc30SJozsef Kadlecsik u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); 1705663bc30SJozsef Kadlecsik 171cabfd139SSergey Popovich if (!cidr || cidr > HOST_MASK) 1725663bc30SJozsef Kadlecsik return -IPSET_ERR_INVALID_CIDR; 173e6146e86SJozsef Kadlecsik ip_set_mask_from_to(ip, ip_to, cidr); 1744fe198e6SJozsef Kadlecsik } 1755663bc30SJozsef Kadlecsik 1765d50e1d8SJozsef Kadlecsik port_to = port = ntohs(e.port); 1775e0c1eb7SJozsef Kadlecsik if (with_ports && tb[IPSET_ATTR_PORT_TO]) { 1785663bc30SJozsef Kadlecsik port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); 1795663bc30SJozsef Kadlecsik if (port > port_to) 1805663bc30SJozsef Kadlecsik swap(port, port_to); 1815e0c1eb7SJozsef Kadlecsik } 1825663bc30SJozsef Kadlecsik 1833d14b171SJozsef Kadlecsik if (retried) 1846e27c9b4SJozsef Kadlecsik ip = ntohl(h->next.ip); 18548596a8dSJozsef Kadlecsik for (; ip <= ip_to; ip++) { 1866e27c9b4SJozsef Kadlecsik p = retried && ip == ntohl(h->next.ip) ? ntohs(h->next.port) 1876e27c9b4SJozsef Kadlecsik : port; 1883d14b171SJozsef Kadlecsik for (; p <= port_to; p++) { 1895d50e1d8SJozsef Kadlecsik e.ip = htonl(ip); 1905d50e1d8SJozsef Kadlecsik e.port = htons(p); 1915d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 1925663bc30SJozsef Kadlecsik 1935663bc30SJozsef Kadlecsik if (ret && !ip_set_eexist(ret, flags)) 1945663bc30SJozsef Kadlecsik return ret; 195ca0f6a5cSJozsef Kadlecsik 1965663bc30SJozsef Kadlecsik ret = 0; 1975663bc30SJozsef Kadlecsik } 1983d14b171SJozsef Kadlecsik } 1995663bc30SJozsef Kadlecsik return ret; 2005663bc30SJozsef Kadlecsik } 2015663bc30SJozsef Kadlecsik 20203c8b234SJozsef Kadlecsik /* IPv6 variant */ 2035663bc30SJozsef Kadlecsik 2045663bc30SJozsef Kadlecsik struct hash_ipportip6_elem { 2055663bc30SJozsef Kadlecsik union nf_inet_addr ip; 2065663bc30SJozsef Kadlecsik union nf_inet_addr ip2; 2075663bc30SJozsef Kadlecsik __be16 port; 2085663bc30SJozsef Kadlecsik u8 proto; 2095663bc30SJozsef Kadlecsik u8 padding; 2105663bc30SJozsef Kadlecsik }; 2115663bc30SJozsef Kadlecsik 2125d50e1d8SJozsef Kadlecsik /* Common functions */ 2135d50e1d8SJozsef Kadlecsik 2145663bc30SJozsef Kadlecsik static inline bool 2155663bc30SJozsef Kadlecsik hash_ipportip6_data_equal(const struct hash_ipportip6_elem *ip1, 21689dc79b7SJozsef Kadlecsik const struct hash_ipportip6_elem *ip2, 21789dc79b7SJozsef Kadlecsik u32 *multi) 2185663bc30SJozsef Kadlecsik { 21929e3b160SYOSHIFUJI Hideaki / 吉藤英明 return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) && 22029e3b160SYOSHIFUJI Hideaki / 吉藤英明 ipv6_addr_equal(&ip1->ip2.in6, &ip2->ip2.in6) && 2215663bc30SJozsef Kadlecsik ip1->port == ip2->port && 2225663bc30SJozsef Kadlecsik ip1->proto == ip2->proto; 2235663bc30SJozsef Kadlecsik } 2245663bc30SJozsef Kadlecsik 2255663bc30SJozsef Kadlecsik static bool 2265663bc30SJozsef Kadlecsik hash_ipportip6_data_list(struct sk_buff *skb, 2275663bc30SJozsef Kadlecsik const struct hash_ipportip6_elem *data) 2285663bc30SJozsef Kadlecsik { 2297cf7899dSDavid S. Miller if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) || 2307cf7899dSDavid S. Miller nla_put_ipaddr6(skb, IPSET_ATTR_IP2, &data->ip2.in6) || 2317cf7899dSDavid S. Miller nla_put_net16(skb, IPSET_ATTR_PORT, data->port) || 2327cf7899dSDavid S. Miller nla_put_u8(skb, IPSET_ATTR_PROTO, data->proto)) 2337cf7899dSDavid S. Miller goto nla_put_failure; 234728a7e69SSergey Popovich return false; 2355663bc30SJozsef Kadlecsik 2365663bc30SJozsef Kadlecsik nla_put_failure: 237728a7e69SSergey Popovich return true; 2385663bc30SJozsef Kadlecsik } 2395663bc30SJozsef Kadlecsik 2405d50e1d8SJozsef Kadlecsik static inline void 24121956ab2SJozsef Kadlecsik hash_ipportip6_data_next(struct hash_ipportip6_elem *next, 2425d50e1d8SJozsef Kadlecsik const struct hash_ipportip6_elem *d) 2435663bc30SJozsef Kadlecsik { 2445d50e1d8SJozsef Kadlecsik next->port = d->port; 2455663bc30SJozsef Kadlecsik } 2465663bc30SJozsef Kadlecsik 2475d50e1d8SJozsef Kadlecsik #undef MTYPE 2485663bc30SJozsef Kadlecsik #undef HOST_MASK 2495663bc30SJozsef Kadlecsik 2505d50e1d8SJozsef Kadlecsik #define MTYPE hash_ipportip6 2515663bc30SJozsef Kadlecsik #define HOST_MASK 128 2525d50e1d8SJozsef Kadlecsik #define IP_SET_EMIT_CREATE 2535d50e1d8SJozsef Kadlecsik #include "ip_set_hash_gen.h" 2543d14b171SJozsef Kadlecsik 2555663bc30SJozsef Kadlecsik static int 2565663bc30SJozsef Kadlecsik hash_ipportip6_kadt(struct ip_set *set, const struct sk_buff *skb, 257b66554cfSJozsef Kadlecsik const struct xt_action_param *par, 2585d50e1d8SJozsef Kadlecsik enum ipset_adt adt, struct ip_set_adt_opt *opt) 2595663bc30SJozsef Kadlecsik { 2605663bc30SJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 26194729f8aSMark Rustad struct hash_ipportip6_elem e = { .ip = { .all = { 0 } } }; 262ca134ce8SJozsef Kadlecsik struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); 2635663bc30SJozsef Kadlecsik 264ac8cc925SJozsef Kadlecsik if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, 2655d50e1d8SJozsef Kadlecsik &e.port, &e.proto)) 2665663bc30SJozsef Kadlecsik return -EINVAL; 2675663bc30SJozsef Kadlecsik 2685d50e1d8SJozsef Kadlecsik ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); 2695d50e1d8SJozsef Kadlecsik ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &e.ip2.in6); 2705d50e1d8SJozsef Kadlecsik return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); 2715663bc30SJozsef Kadlecsik } 2725663bc30SJozsef Kadlecsik 2735663bc30SJozsef Kadlecsik static int 2745663bc30SJozsef Kadlecsik hash_ipportip6_uadt(struct ip_set *set, struct nlattr *tb[], 2753d14b171SJozsef Kadlecsik enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) 2765663bc30SJozsef Kadlecsik { 27721956ab2SJozsef Kadlecsik const struct hash_ipportip6 *h = set->data; 2785663bc30SJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 27994729f8aSMark Rustad struct hash_ipportip6_elem e = { .ip = { .all = { 0 } } }; 280ca134ce8SJozsef Kadlecsik struct ip_set_ext ext = IP_SET_INIT_UEXT(set); 2815663bc30SJozsef Kadlecsik u32 port, port_to; 2825e0c1eb7SJozsef Kadlecsik bool with_ports = false; 2835663bc30SJozsef Kadlecsik int ret; 2845663bc30SJozsef Kadlecsik 285a212e08eSSergey Popovich if (tb[IPSET_ATTR_LINENO]) 286a212e08eSSergey Popovich *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); 287a212e08eSSergey Popovich 2885663bc30SJozsef Kadlecsik if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || 2895663bc30SJozsef Kadlecsik !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || 2902c227f27SSergey Popovich !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO))) 2915663bc30SJozsef Kadlecsik return -IPSET_ERR_PROTOCOL; 2922c227f27SSergey Popovich if (unlikely(tb[IPSET_ATTR_IP_TO])) 2932c227f27SSergey Popovich return -IPSET_ERR_HASH_RANGE_UNSUPPORTED; 2942c227f27SSergey Popovich if (unlikely(tb[IPSET_ATTR_CIDR])) { 2952c227f27SSergey Popovich u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); 2962c227f27SSergey Popovich 2972c227f27SSergey Popovich if (cidr != HOST_MASK) 2982c227f27SSergey Popovich return -IPSET_ERR_INVALID_CIDR; 2992c227f27SSergey Popovich } 3005663bc30SJozsef Kadlecsik 3018e55d2e5SSergey Popovich ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip); 3028e55d2e5SSergey Popovich if (ret) 3038e55d2e5SSergey Popovich return ret; 3048e55d2e5SSergey Popovich 3058e55d2e5SSergey Popovich ret = ip_set_get_extensions(set, tb, &ext); 3065663bc30SJozsef Kadlecsik if (ret) 3075663bc30SJozsef Kadlecsik return ret; 3085663bc30SJozsef Kadlecsik 3095d50e1d8SJozsef Kadlecsik ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &e.ip2); 3105663bc30SJozsef Kadlecsik if (ret) 3115663bc30SJozsef Kadlecsik return ret; 3125663bc30SJozsef Kadlecsik 3135d50e1d8SJozsef Kadlecsik e.port = nla_get_be16(tb[IPSET_ATTR_PORT]); 3145663bc30SJozsef Kadlecsik 3155663bc30SJozsef Kadlecsik if (tb[IPSET_ATTR_PROTO]) { 3165d50e1d8SJozsef Kadlecsik e.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); 3175d50e1d8SJozsef Kadlecsik with_ports = ip_set_proto_with_ports(e.proto); 3185663bc30SJozsef Kadlecsik 3195d50e1d8SJozsef Kadlecsik if (e.proto == 0) 3205663bc30SJozsef Kadlecsik return -IPSET_ERR_INVALID_PROTO; 321ca0f6a5cSJozsef Kadlecsik } else { 3225663bc30SJozsef Kadlecsik return -IPSET_ERR_MISSING_PROTO; 323ca0f6a5cSJozsef Kadlecsik } 3245663bc30SJozsef Kadlecsik 3255d50e1d8SJozsef Kadlecsik if (!(with_ports || e.proto == IPPROTO_ICMPV6)) 3265d50e1d8SJozsef Kadlecsik e.port = 0; 3275663bc30SJozsef Kadlecsik 3285e0c1eb7SJozsef Kadlecsik if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { 3295d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 3305663bc30SJozsef Kadlecsik return ip_set_eexist(ret, flags) ? 0 : ret; 3315663bc30SJozsef Kadlecsik } 3325663bc30SJozsef Kadlecsik 3335d50e1d8SJozsef Kadlecsik port = ntohs(e.port); 3345663bc30SJozsef Kadlecsik port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); 3355663bc30SJozsef Kadlecsik if (port > port_to) 3365663bc30SJozsef Kadlecsik swap(port, port_to); 3375663bc30SJozsef Kadlecsik 3383d14b171SJozsef Kadlecsik if (retried) 3396e27c9b4SJozsef Kadlecsik port = ntohs(h->next.port); 3405663bc30SJozsef Kadlecsik for (; port <= port_to; port++) { 3415d50e1d8SJozsef Kadlecsik e.port = htons(port); 3425d50e1d8SJozsef Kadlecsik ret = adtfn(set, &e, &ext, &ext, flags); 3435663bc30SJozsef Kadlecsik 3445663bc30SJozsef Kadlecsik if (ret && !ip_set_eexist(ret, flags)) 3455663bc30SJozsef Kadlecsik return ret; 346ca0f6a5cSJozsef Kadlecsik 3475663bc30SJozsef Kadlecsik ret = 0; 3485663bc30SJozsef Kadlecsik } 3495663bc30SJozsef Kadlecsik return ret; 3505663bc30SJozsef Kadlecsik } 3515663bc30SJozsef Kadlecsik 3525663bc30SJozsef Kadlecsik static struct ip_set_type hash_ipportip_type __read_mostly = { 3535663bc30SJozsef Kadlecsik .name = "hash:ip,port,ip", 3545663bc30SJozsef Kadlecsik .protocol = IPSET_PROTOCOL, 3555663bc30SJozsef Kadlecsik .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, 3565663bc30SJozsef Kadlecsik .dimension = IPSET_DIM_THREE, 357c15f1c83SJan Engelhardt .family = NFPROTO_UNSPEC, 35835b8dcf8SJozsef Kadlecsik .revision_min = IPSET_TYPE_REV_MIN, 35935b8dcf8SJozsef Kadlecsik .revision_max = IPSET_TYPE_REV_MAX, 3605663bc30SJozsef Kadlecsik .create = hash_ipportip_create, 3615663bc30SJozsef Kadlecsik .create_policy = { 3625663bc30SJozsef Kadlecsik [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, 3635663bc30SJozsef Kadlecsik [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, 3645663bc30SJozsef Kadlecsik [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, 3655663bc30SJozsef Kadlecsik [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, 3665663bc30SJozsef Kadlecsik [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, 36700d71b27SJozsef Kadlecsik [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, 3685663bc30SJozsef Kadlecsik }, 3695663bc30SJozsef Kadlecsik .adt_policy = { 3705663bc30SJozsef Kadlecsik [IPSET_ATTR_IP] = { .type = NLA_NESTED }, 3715663bc30SJozsef Kadlecsik [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, 3725663bc30SJozsef Kadlecsik [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, 3735663bc30SJozsef Kadlecsik [IPSET_ATTR_PORT] = { .type = NLA_U16 }, 3745663bc30SJozsef Kadlecsik [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, 3755663bc30SJozsef Kadlecsik [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, 3765663bc30SJozsef Kadlecsik [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, 3775663bc30SJozsef Kadlecsik [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, 3785663bc30SJozsef Kadlecsik [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, 37900d71b27SJozsef Kadlecsik [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, 38000d71b27SJozsef Kadlecsik [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, 38103726186SSergey Popovich [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING, 38203726186SSergey Popovich .len = IPSET_MAX_COMMENT_SIZE }, 383af331419SAnton Danilov [IPSET_ATTR_SKBMARK] = { .type = NLA_U64 }, 384af331419SAnton Danilov [IPSET_ATTR_SKBPRIO] = { .type = NLA_U32 }, 385af331419SAnton Danilov [IPSET_ATTR_SKBQUEUE] = { .type = NLA_U16 }, 3865663bc30SJozsef Kadlecsik }, 3875663bc30SJozsef Kadlecsik .me = THIS_MODULE, 3885663bc30SJozsef Kadlecsik }; 3895663bc30SJozsef Kadlecsik 3905663bc30SJozsef Kadlecsik static int __init 3915663bc30SJozsef Kadlecsik hash_ipportip_init(void) 3925663bc30SJozsef Kadlecsik { 3935663bc30SJozsef Kadlecsik return ip_set_type_register(&hash_ipportip_type); 3945663bc30SJozsef Kadlecsik } 3955663bc30SJozsef Kadlecsik 3965663bc30SJozsef Kadlecsik static void __exit 3975663bc30SJozsef Kadlecsik hash_ipportip_fini(void) 3985663bc30SJozsef Kadlecsik { 39918f84d41SJozsef Kadlecsik rcu_barrier(); 4005663bc30SJozsef Kadlecsik ip_set_type_unregister(&hash_ipportip_type); 4015663bc30SJozsef Kadlecsik } 4025663bc30SJozsef Kadlecsik 4035663bc30SJozsef Kadlecsik module_init(hash_ipportip_init); 4045663bc30SJozsef Kadlecsik module_exit(hash_ipportip_fini); 405