141d22f7bSJozsef Kadlecsik /* Copyright (C) 2003-2011 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_timeout.h> 2541d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_getport.h> 2641d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_hash.h> 2741d22f7bSJozsef Kadlecsik 2841d22f7bSJozsef Kadlecsik MODULE_LICENSE("GPL"); 2941d22f7bSJozsef Kadlecsik MODULE_AUTHOR("Jozsef Kadlecsik <kadlec@blackhole.kfki.hu>"); 3041d22f7bSJozsef Kadlecsik MODULE_DESCRIPTION("hash:ip,port,net type of IP sets"); 3141d22f7bSJozsef Kadlecsik MODULE_ALIAS("ip_set_hash:ip,port,net"); 3241d22f7bSJozsef Kadlecsik 3341d22f7bSJozsef Kadlecsik /* Type specific function prefix */ 3441d22f7bSJozsef Kadlecsik #define TYPE hash_ipportnet 3541d22f7bSJozsef Kadlecsik 3641d22f7bSJozsef Kadlecsik static bool 3741d22f7bSJozsef Kadlecsik hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b); 3841d22f7bSJozsef Kadlecsik 3941d22f7bSJozsef Kadlecsik #define hash_ipportnet4_same_set hash_ipportnet_same_set 4041d22f7bSJozsef Kadlecsik #define hash_ipportnet6_same_set hash_ipportnet_same_set 4141d22f7bSJozsef Kadlecsik 4241d22f7bSJozsef Kadlecsik /* The type variant functions: IPv4 */ 4341d22f7bSJozsef Kadlecsik 4441d22f7bSJozsef Kadlecsik /* Member elements without timeout */ 4541d22f7bSJozsef Kadlecsik struct hash_ipportnet4_elem { 4641d22f7bSJozsef Kadlecsik __be32 ip; 4741d22f7bSJozsef Kadlecsik __be32 ip2; 4841d22f7bSJozsef Kadlecsik __be16 port; 4941d22f7bSJozsef Kadlecsik u8 cidr; 5041d22f7bSJozsef Kadlecsik u8 proto; 5141d22f7bSJozsef Kadlecsik }; 5241d22f7bSJozsef Kadlecsik 5341d22f7bSJozsef Kadlecsik /* Member elements with timeout support */ 5441d22f7bSJozsef Kadlecsik struct hash_ipportnet4_telem { 5541d22f7bSJozsef Kadlecsik __be32 ip; 5641d22f7bSJozsef Kadlecsik __be32 ip2; 5741d22f7bSJozsef Kadlecsik __be16 port; 5841d22f7bSJozsef Kadlecsik u8 cidr; 5941d22f7bSJozsef Kadlecsik u8 proto; 6041d22f7bSJozsef Kadlecsik unsigned long timeout; 6141d22f7bSJozsef Kadlecsik }; 6241d22f7bSJozsef Kadlecsik 6341d22f7bSJozsef Kadlecsik static inline bool 6441d22f7bSJozsef Kadlecsik hash_ipportnet4_data_equal(const struct hash_ipportnet4_elem *ip1, 6541d22f7bSJozsef Kadlecsik const struct hash_ipportnet4_elem *ip2) 6641d22f7bSJozsef Kadlecsik { 6741d22f7bSJozsef Kadlecsik return ip1->ip == ip2->ip && 6841d22f7bSJozsef Kadlecsik ip1->ip2 == ip2->ip2 && 6941d22f7bSJozsef Kadlecsik ip1->cidr == ip2->cidr && 7041d22f7bSJozsef Kadlecsik ip1->port == ip2->port && 7141d22f7bSJozsef Kadlecsik ip1->proto == ip2->proto; 7241d22f7bSJozsef Kadlecsik } 7341d22f7bSJozsef Kadlecsik 7441d22f7bSJozsef Kadlecsik static inline bool 7541d22f7bSJozsef Kadlecsik hash_ipportnet4_data_isnull(const struct hash_ipportnet4_elem *elem) 7641d22f7bSJozsef Kadlecsik { 7741d22f7bSJozsef Kadlecsik return elem->proto == 0; 7841d22f7bSJozsef Kadlecsik } 7941d22f7bSJozsef Kadlecsik 8041d22f7bSJozsef Kadlecsik static inline void 8141d22f7bSJozsef Kadlecsik hash_ipportnet4_data_copy(struct hash_ipportnet4_elem *dst, 8241d22f7bSJozsef Kadlecsik const struct hash_ipportnet4_elem *src) 8341d22f7bSJozsef Kadlecsik { 8441d22f7bSJozsef Kadlecsik memcpy(dst, src, sizeof(*dst)); 8541d22f7bSJozsef Kadlecsik } 8641d22f7bSJozsef Kadlecsik 8741d22f7bSJozsef Kadlecsik static inline void 8841d22f7bSJozsef Kadlecsik hash_ipportnet4_data_netmask(struct hash_ipportnet4_elem *elem, u8 cidr) 8941d22f7bSJozsef Kadlecsik { 9041d22f7bSJozsef Kadlecsik elem->ip2 &= ip_set_netmask(cidr); 9141d22f7bSJozsef Kadlecsik elem->cidr = cidr; 9241d22f7bSJozsef Kadlecsik } 9341d22f7bSJozsef Kadlecsik 9441d22f7bSJozsef Kadlecsik static inline void 9541d22f7bSJozsef Kadlecsik hash_ipportnet4_data_zero_out(struct hash_ipportnet4_elem *elem) 9641d22f7bSJozsef Kadlecsik { 9741d22f7bSJozsef Kadlecsik elem->proto = 0; 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 { 10441d22f7bSJozsef Kadlecsik NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, data->ip); 10541d22f7bSJozsef Kadlecsik NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, data->ip2); 10641d22f7bSJozsef Kadlecsik NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); 10741d22f7bSJozsef Kadlecsik NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); 10841d22f7bSJozsef Kadlecsik NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); 10941d22f7bSJozsef Kadlecsik return 0; 11041d22f7bSJozsef Kadlecsik 11141d22f7bSJozsef Kadlecsik nla_put_failure: 11241d22f7bSJozsef Kadlecsik return 1; 11341d22f7bSJozsef Kadlecsik } 11441d22f7bSJozsef Kadlecsik 11541d22f7bSJozsef Kadlecsik static bool 11641d22f7bSJozsef Kadlecsik hash_ipportnet4_data_tlist(struct sk_buff *skb, 11741d22f7bSJozsef Kadlecsik const struct hash_ipportnet4_elem *data) 11841d22f7bSJozsef Kadlecsik { 11941d22f7bSJozsef Kadlecsik const struct hash_ipportnet4_telem *tdata = 12041d22f7bSJozsef Kadlecsik (const struct hash_ipportnet4_telem *)data; 12141d22f7bSJozsef Kadlecsik 12241d22f7bSJozsef Kadlecsik NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP, tdata->ip); 12341d22f7bSJozsef Kadlecsik NLA_PUT_IPADDR4(skb, IPSET_ATTR_IP2, tdata->ip2); 12441d22f7bSJozsef Kadlecsik NLA_PUT_NET16(skb, IPSET_ATTR_PORT, tdata->port); 12541d22f7bSJozsef Kadlecsik NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); 12641d22f7bSJozsef Kadlecsik NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); 12741d22f7bSJozsef Kadlecsik NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, 12841d22f7bSJozsef Kadlecsik htonl(ip_set_timeout_get(tdata->timeout))); 12941d22f7bSJozsef Kadlecsik 13041d22f7bSJozsef Kadlecsik return 0; 13141d22f7bSJozsef Kadlecsik 13241d22f7bSJozsef Kadlecsik nla_put_failure: 13341d22f7bSJozsef Kadlecsik return 1; 13441d22f7bSJozsef Kadlecsik } 13541d22f7bSJozsef Kadlecsik 13641d22f7bSJozsef Kadlecsik #define IP_SET_HASH_WITH_PROTO 13741d22f7bSJozsef Kadlecsik #define IP_SET_HASH_WITH_NETS 13841d22f7bSJozsef Kadlecsik 13941d22f7bSJozsef Kadlecsik #define PF 4 14041d22f7bSJozsef Kadlecsik #define HOST_MASK 32 14141d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_ahash.h> 14241d22f7bSJozsef Kadlecsik 14341d22f7bSJozsef Kadlecsik static int 14441d22f7bSJozsef Kadlecsik hash_ipportnet4_kadt(struct ip_set *set, const struct sk_buff *skb, 145ac8cc925SJozsef Kadlecsik enum ipset_adt adt, const struct ip_set_adt_opt *opt) 14641d22f7bSJozsef Kadlecsik { 14741d22f7bSJozsef Kadlecsik const struct ip_set_hash *h = set->data; 14841d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 14941d22f7bSJozsef Kadlecsik struct hash_ipportnet4_elem data = 15041d22f7bSJozsef Kadlecsik { .cidr = h->nets[0].cidr || HOST_MASK }; 15141d22f7bSJozsef Kadlecsik 15241d22f7bSJozsef Kadlecsik if (data.cidr == 0) 15341d22f7bSJozsef Kadlecsik return -EINVAL; 15441d22f7bSJozsef Kadlecsik if (adt == IPSET_TEST) 15541d22f7bSJozsef Kadlecsik data.cidr = HOST_MASK; 15641d22f7bSJozsef Kadlecsik 157ac8cc925SJozsef Kadlecsik if (!ip_set_get_ip4_port(skb, opt->flags & IPSET_DIM_TWO_SRC, 15841d22f7bSJozsef Kadlecsik &data.port, &data.proto)) 15941d22f7bSJozsef Kadlecsik return -EINVAL; 16041d22f7bSJozsef Kadlecsik 161ac8cc925SJozsef Kadlecsik ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip); 162ac8cc925SJozsef Kadlecsik ip4addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2); 16341d22f7bSJozsef Kadlecsik data.ip2 &= ip_set_netmask(data.cidr); 16441d22f7bSJozsef Kadlecsik 165ac8cc925SJozsef Kadlecsik return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); 16641d22f7bSJozsef Kadlecsik } 16741d22f7bSJozsef Kadlecsik 16841d22f7bSJozsef Kadlecsik static int 16941d22f7bSJozsef Kadlecsik hash_ipportnet4_uadt(struct ip_set *set, struct nlattr *tb[], 17041d22f7bSJozsef Kadlecsik enum ipset_adt adt, u32 *lineno, u32 flags) 17141d22f7bSJozsef Kadlecsik { 17241d22f7bSJozsef Kadlecsik const struct ip_set_hash *h = set->data; 17341d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 17441d22f7bSJozsef Kadlecsik struct hash_ipportnet4_elem data = { .cidr = HOST_MASK }; 17541d22f7bSJozsef Kadlecsik u32 ip, ip_to, p, port, port_to; 17641d22f7bSJozsef Kadlecsik u32 timeout = h->timeout; 1775e0c1eb7SJozsef Kadlecsik bool with_ports = false; 17841d22f7bSJozsef Kadlecsik int ret; 17941d22f7bSJozsef Kadlecsik 18041d22f7bSJozsef Kadlecsik if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || 18141d22f7bSJozsef Kadlecsik !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || 18241d22f7bSJozsef Kadlecsik !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || 18341d22f7bSJozsef Kadlecsik !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) 18441d22f7bSJozsef Kadlecsik return -IPSET_ERR_PROTOCOL; 18541d22f7bSJozsef Kadlecsik 18641d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_LINENO]) 18741d22f7bSJozsef Kadlecsik *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); 18841d22f7bSJozsef Kadlecsik 18941d22f7bSJozsef Kadlecsik ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &data.ip); 19041d22f7bSJozsef Kadlecsik if (ret) 19141d22f7bSJozsef Kadlecsik return ret; 19241d22f7bSJozsef Kadlecsik 19341d22f7bSJozsef Kadlecsik ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP2], &data.ip2); 19441d22f7bSJozsef Kadlecsik if (ret) 19541d22f7bSJozsef Kadlecsik return ret; 19641d22f7bSJozsef Kadlecsik 19741d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_CIDR2]) 19841d22f7bSJozsef Kadlecsik data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); 19941d22f7bSJozsef Kadlecsik 20041d22f7bSJozsef Kadlecsik if (!data.cidr) 20141d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_CIDR; 20241d22f7bSJozsef Kadlecsik 20341d22f7bSJozsef Kadlecsik data.ip2 &= ip_set_netmask(data.cidr); 20441d22f7bSJozsef Kadlecsik 20541d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_PORT]) 20641d22f7bSJozsef Kadlecsik data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); 20741d22f7bSJozsef Kadlecsik else 20841d22f7bSJozsef Kadlecsik return -IPSET_ERR_PROTOCOL; 20941d22f7bSJozsef Kadlecsik 21041d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_PROTO]) { 21141d22f7bSJozsef Kadlecsik data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); 2125e0c1eb7SJozsef Kadlecsik with_ports = ip_set_proto_with_ports(data.proto); 21341d22f7bSJozsef Kadlecsik 21441d22f7bSJozsef Kadlecsik if (data.proto == 0) 21541d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_PROTO; 21641d22f7bSJozsef Kadlecsik } else 21741d22f7bSJozsef Kadlecsik return -IPSET_ERR_MISSING_PROTO; 21841d22f7bSJozsef Kadlecsik 2195e0c1eb7SJozsef Kadlecsik if (!(with_ports || data.proto == IPPROTO_ICMP)) 22041d22f7bSJozsef Kadlecsik data.port = 0; 22141d22f7bSJozsef Kadlecsik 22241d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_TIMEOUT]) { 22341d22f7bSJozsef Kadlecsik if (!with_timeout(h->timeout)) 22441d22f7bSJozsef Kadlecsik return -IPSET_ERR_TIMEOUT; 22541d22f7bSJozsef Kadlecsik timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); 22641d22f7bSJozsef Kadlecsik } 22741d22f7bSJozsef Kadlecsik 22841d22f7bSJozsef Kadlecsik if (adt == IPSET_TEST || 22941d22f7bSJozsef Kadlecsik !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR] || 23041d22f7bSJozsef Kadlecsik tb[IPSET_ATTR_PORT_TO])) { 2315416219eSJozsef Kadlecsik ret = adtfn(set, &data, timeout, flags); 23241d22f7bSJozsef Kadlecsik return ip_set_eexist(ret, flags) ? 0 : ret; 23341d22f7bSJozsef Kadlecsik } 23441d22f7bSJozsef Kadlecsik 23541d22f7bSJozsef Kadlecsik ip = ntohl(data.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]) { 24341d22f7bSJozsef Kadlecsik u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); 24441d22f7bSJozsef Kadlecsik 24541d22f7bSJozsef Kadlecsik if (cidr > 32) 24641d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_CIDR; 24741d22f7bSJozsef Kadlecsik ip &= ip_set_hostmask(cidr); 24841d22f7bSJozsef Kadlecsik ip_to = ip | ~ip_set_hostmask(cidr); 24941d22f7bSJozsef Kadlecsik } else 25041d22f7bSJozsef Kadlecsik ip_to = ip; 25141d22f7bSJozsef Kadlecsik 2525e0c1eb7SJozsef Kadlecsik port_to = port = ntohs(data.port); 2535e0c1eb7SJozsef Kadlecsik if (with_ports && 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 } 25841d22f7bSJozsef Kadlecsik 25941d22f7bSJozsef Kadlecsik for (; !before(ip_to, ip); ip++) 26041d22f7bSJozsef Kadlecsik for (p = port; p <= port_to; p++) { 26141d22f7bSJozsef Kadlecsik data.ip = htonl(ip); 26241d22f7bSJozsef Kadlecsik data.port = htons(p); 2635416219eSJozsef Kadlecsik ret = adtfn(set, &data, timeout, flags); 26441d22f7bSJozsef Kadlecsik 26541d22f7bSJozsef Kadlecsik if (ret && !ip_set_eexist(ret, flags)) 26641d22f7bSJozsef Kadlecsik return ret; 26741d22f7bSJozsef Kadlecsik else 26841d22f7bSJozsef Kadlecsik ret = 0; 26941d22f7bSJozsef Kadlecsik } 27041d22f7bSJozsef Kadlecsik return ret; 27141d22f7bSJozsef Kadlecsik } 27241d22f7bSJozsef Kadlecsik 27341d22f7bSJozsef Kadlecsik static bool 27441d22f7bSJozsef Kadlecsik hash_ipportnet_same_set(const struct ip_set *a, const struct ip_set *b) 27541d22f7bSJozsef Kadlecsik { 27641d22f7bSJozsef Kadlecsik const struct ip_set_hash *x = a->data; 27741d22f7bSJozsef Kadlecsik const struct ip_set_hash *y = b->data; 27841d22f7bSJozsef Kadlecsik 27941d22f7bSJozsef Kadlecsik /* Resizing changes htable_bits, so we ignore it */ 28041d22f7bSJozsef Kadlecsik return x->maxelem == y->maxelem && 28141d22f7bSJozsef Kadlecsik x->timeout == y->timeout; 28241d22f7bSJozsef Kadlecsik } 28341d22f7bSJozsef Kadlecsik 28441d22f7bSJozsef Kadlecsik /* The type variant functions: IPv6 */ 28541d22f7bSJozsef Kadlecsik 28641d22f7bSJozsef Kadlecsik struct hash_ipportnet6_elem { 28741d22f7bSJozsef Kadlecsik union nf_inet_addr ip; 28841d22f7bSJozsef Kadlecsik union nf_inet_addr ip2; 28941d22f7bSJozsef Kadlecsik __be16 port; 29041d22f7bSJozsef Kadlecsik u8 cidr; 29141d22f7bSJozsef Kadlecsik u8 proto; 29241d22f7bSJozsef Kadlecsik }; 29341d22f7bSJozsef Kadlecsik 29441d22f7bSJozsef Kadlecsik struct hash_ipportnet6_telem { 29541d22f7bSJozsef Kadlecsik union nf_inet_addr ip; 29641d22f7bSJozsef Kadlecsik union nf_inet_addr ip2; 29741d22f7bSJozsef Kadlecsik __be16 port; 29841d22f7bSJozsef Kadlecsik u8 cidr; 29941d22f7bSJozsef Kadlecsik u8 proto; 30041d22f7bSJozsef Kadlecsik unsigned long timeout; 30141d22f7bSJozsef Kadlecsik }; 30241d22f7bSJozsef Kadlecsik 30341d22f7bSJozsef Kadlecsik static inline bool 30441d22f7bSJozsef Kadlecsik hash_ipportnet6_data_equal(const struct hash_ipportnet6_elem *ip1, 30541d22f7bSJozsef Kadlecsik const struct hash_ipportnet6_elem *ip2) 30641d22f7bSJozsef Kadlecsik { 30741d22f7bSJozsef Kadlecsik return ipv6_addr_cmp(&ip1->ip.in6, &ip2->ip.in6) == 0 && 30841d22f7bSJozsef Kadlecsik ipv6_addr_cmp(&ip1->ip2.in6, &ip2->ip2.in6) == 0 && 30941d22f7bSJozsef Kadlecsik ip1->cidr == ip2->cidr && 31041d22f7bSJozsef Kadlecsik ip1->port == ip2->port && 31141d22f7bSJozsef Kadlecsik ip1->proto == ip2->proto; 31241d22f7bSJozsef Kadlecsik } 31341d22f7bSJozsef Kadlecsik 31441d22f7bSJozsef Kadlecsik static inline bool 31541d22f7bSJozsef Kadlecsik hash_ipportnet6_data_isnull(const struct hash_ipportnet6_elem *elem) 31641d22f7bSJozsef Kadlecsik { 31741d22f7bSJozsef Kadlecsik return elem->proto == 0; 31841d22f7bSJozsef Kadlecsik } 31941d22f7bSJozsef Kadlecsik 32041d22f7bSJozsef Kadlecsik static inline void 32141d22f7bSJozsef Kadlecsik hash_ipportnet6_data_copy(struct hash_ipportnet6_elem *dst, 32241d22f7bSJozsef Kadlecsik const struct hash_ipportnet6_elem *src) 32341d22f7bSJozsef Kadlecsik { 32441d22f7bSJozsef Kadlecsik memcpy(dst, src, sizeof(*dst)); 32541d22f7bSJozsef Kadlecsik } 32641d22f7bSJozsef Kadlecsik 32741d22f7bSJozsef Kadlecsik static inline void 32841d22f7bSJozsef Kadlecsik hash_ipportnet6_data_zero_out(struct hash_ipportnet6_elem *elem) 32941d22f7bSJozsef Kadlecsik { 33041d22f7bSJozsef Kadlecsik elem->proto = 0; 33141d22f7bSJozsef Kadlecsik } 33241d22f7bSJozsef Kadlecsik 33341d22f7bSJozsef Kadlecsik static inline void 33441d22f7bSJozsef Kadlecsik ip6_netmask(union nf_inet_addr *ip, u8 prefix) 33541d22f7bSJozsef Kadlecsik { 33641d22f7bSJozsef Kadlecsik ip->ip6[0] &= ip_set_netmask6(prefix)[0]; 33741d22f7bSJozsef Kadlecsik ip->ip6[1] &= ip_set_netmask6(prefix)[1]; 33841d22f7bSJozsef Kadlecsik ip->ip6[2] &= ip_set_netmask6(prefix)[2]; 33941d22f7bSJozsef Kadlecsik ip->ip6[3] &= ip_set_netmask6(prefix)[3]; 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); 34641d22f7bSJozsef Kadlecsik elem->cidr = cidr; 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 { 35341d22f7bSJozsef Kadlecsik NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &data->ip); 35441d22f7bSJozsef Kadlecsik NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); 35541d22f7bSJozsef Kadlecsik NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); 35641d22f7bSJozsef Kadlecsik NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); 35741d22f7bSJozsef Kadlecsik NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); 35841d22f7bSJozsef Kadlecsik return 0; 35941d22f7bSJozsef Kadlecsik 36041d22f7bSJozsef Kadlecsik nla_put_failure: 36141d22f7bSJozsef Kadlecsik return 1; 36241d22f7bSJozsef Kadlecsik } 36341d22f7bSJozsef Kadlecsik 36441d22f7bSJozsef Kadlecsik static bool 36541d22f7bSJozsef Kadlecsik hash_ipportnet6_data_tlist(struct sk_buff *skb, 36641d22f7bSJozsef Kadlecsik const struct hash_ipportnet6_elem *data) 36741d22f7bSJozsef Kadlecsik { 36841d22f7bSJozsef Kadlecsik const struct hash_ipportnet6_telem *e = 36941d22f7bSJozsef Kadlecsik (const struct hash_ipportnet6_telem *)data; 37041d22f7bSJozsef Kadlecsik 37141d22f7bSJozsef Kadlecsik NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP, &e->ip); 37241d22f7bSJozsef Kadlecsik NLA_PUT_IPADDR6(skb, IPSET_ATTR_IP2, &data->ip2); 37341d22f7bSJozsef Kadlecsik NLA_PUT_NET16(skb, IPSET_ATTR_PORT, data->port); 37441d22f7bSJozsef Kadlecsik NLA_PUT_U8(skb, IPSET_ATTR_CIDR2, data->cidr); 37541d22f7bSJozsef Kadlecsik NLA_PUT_U8(skb, IPSET_ATTR_PROTO, data->proto); 37641d22f7bSJozsef Kadlecsik NLA_PUT_NET32(skb, IPSET_ATTR_TIMEOUT, 37741d22f7bSJozsef Kadlecsik htonl(ip_set_timeout_get(e->timeout))); 37841d22f7bSJozsef Kadlecsik return 0; 37941d22f7bSJozsef Kadlecsik 38041d22f7bSJozsef Kadlecsik nla_put_failure: 38141d22f7bSJozsef Kadlecsik return 1; 38241d22f7bSJozsef Kadlecsik } 38341d22f7bSJozsef Kadlecsik 38441d22f7bSJozsef Kadlecsik #undef PF 38541d22f7bSJozsef Kadlecsik #undef HOST_MASK 38641d22f7bSJozsef Kadlecsik 38741d22f7bSJozsef Kadlecsik #define PF 6 38841d22f7bSJozsef Kadlecsik #define HOST_MASK 128 38941d22f7bSJozsef Kadlecsik #include <linux/netfilter/ipset/ip_set_ahash.h> 39041d22f7bSJozsef Kadlecsik 39141d22f7bSJozsef Kadlecsik static int 39241d22f7bSJozsef Kadlecsik hash_ipportnet6_kadt(struct ip_set *set, const struct sk_buff *skb, 393ac8cc925SJozsef Kadlecsik enum ipset_adt adt, const struct ip_set_adt_opt *opt) 39441d22f7bSJozsef Kadlecsik { 39541d22f7bSJozsef Kadlecsik const struct ip_set_hash *h = set->data; 39641d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 39741d22f7bSJozsef Kadlecsik struct hash_ipportnet6_elem data = 39841d22f7bSJozsef Kadlecsik { .cidr = h->nets[0].cidr || HOST_MASK }; 39941d22f7bSJozsef Kadlecsik 40041d22f7bSJozsef Kadlecsik if (data.cidr == 0) 40141d22f7bSJozsef Kadlecsik return -EINVAL; 40241d22f7bSJozsef Kadlecsik if (adt == IPSET_TEST) 40341d22f7bSJozsef Kadlecsik data.cidr = HOST_MASK; 40441d22f7bSJozsef Kadlecsik 405ac8cc925SJozsef Kadlecsik if (!ip_set_get_ip6_port(skb, opt->flags & IPSET_DIM_TWO_SRC, 40641d22f7bSJozsef Kadlecsik &data.port, &data.proto)) 40741d22f7bSJozsef Kadlecsik return -EINVAL; 40841d22f7bSJozsef Kadlecsik 409ac8cc925SJozsef Kadlecsik ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &data.ip.in6); 410ac8cc925SJozsef Kadlecsik ip6addrptr(skb, opt->flags & IPSET_DIM_THREE_SRC, &data.ip2.in6); 41141d22f7bSJozsef Kadlecsik ip6_netmask(&data.ip2, data.cidr); 41241d22f7bSJozsef Kadlecsik 413ac8cc925SJozsef Kadlecsik return adtfn(set, &data, opt_timeout(opt, h), opt->cmdflags); 41441d22f7bSJozsef Kadlecsik } 41541d22f7bSJozsef Kadlecsik 41641d22f7bSJozsef Kadlecsik static int 41741d22f7bSJozsef Kadlecsik hash_ipportnet6_uadt(struct ip_set *set, struct nlattr *tb[], 41841d22f7bSJozsef Kadlecsik enum ipset_adt adt, u32 *lineno, u32 flags) 41941d22f7bSJozsef Kadlecsik { 42041d22f7bSJozsef Kadlecsik const struct ip_set_hash *h = set->data; 42141d22f7bSJozsef Kadlecsik ipset_adtfn adtfn = set->variant->adt[adt]; 42241d22f7bSJozsef Kadlecsik struct hash_ipportnet6_elem data = { .cidr = HOST_MASK }; 42341d22f7bSJozsef Kadlecsik u32 port, port_to; 42441d22f7bSJozsef Kadlecsik u32 timeout = h->timeout; 4255e0c1eb7SJozsef Kadlecsik bool with_ports = false; 42641d22f7bSJozsef Kadlecsik int ret; 42741d22f7bSJozsef Kadlecsik 42841d22f7bSJozsef Kadlecsik if (unlikely(!tb[IPSET_ATTR_IP] || !tb[IPSET_ATTR_IP2] || 42941d22f7bSJozsef Kadlecsik !ip_set_attr_netorder(tb, IPSET_ATTR_PORT) || 43041d22f7bSJozsef Kadlecsik !ip_set_optattr_netorder(tb, IPSET_ATTR_PORT_TO) || 43141d22f7bSJozsef Kadlecsik !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || 43241d22f7bSJozsef Kadlecsik tb[IPSET_ATTR_IP_TO] || 43341d22f7bSJozsef Kadlecsik tb[IPSET_ATTR_CIDR])) 43441d22f7bSJozsef Kadlecsik return -IPSET_ERR_PROTOCOL; 43541d22f7bSJozsef Kadlecsik 43641d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_LINENO]) 43741d22f7bSJozsef Kadlecsik *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); 43841d22f7bSJozsef Kadlecsik 43941d22f7bSJozsef Kadlecsik ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &data.ip); 44041d22f7bSJozsef Kadlecsik if (ret) 44141d22f7bSJozsef Kadlecsik return ret; 44241d22f7bSJozsef Kadlecsik 44341d22f7bSJozsef Kadlecsik ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP2], &data.ip2); 44441d22f7bSJozsef Kadlecsik if (ret) 44541d22f7bSJozsef Kadlecsik return ret; 44641d22f7bSJozsef Kadlecsik 44741d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_CIDR2]) 44841d22f7bSJozsef Kadlecsik data.cidr = nla_get_u8(tb[IPSET_ATTR_CIDR2]); 44941d22f7bSJozsef Kadlecsik 45041d22f7bSJozsef Kadlecsik if (!data.cidr) 45141d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_CIDR; 45241d22f7bSJozsef Kadlecsik 45341d22f7bSJozsef Kadlecsik ip6_netmask(&data.ip2, data.cidr); 45441d22f7bSJozsef Kadlecsik 45541d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_PORT]) 45641d22f7bSJozsef Kadlecsik data.port = nla_get_be16(tb[IPSET_ATTR_PORT]); 45741d22f7bSJozsef Kadlecsik else 45841d22f7bSJozsef Kadlecsik return -IPSET_ERR_PROTOCOL; 45941d22f7bSJozsef Kadlecsik 46041d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_PROTO]) { 46141d22f7bSJozsef Kadlecsik data.proto = nla_get_u8(tb[IPSET_ATTR_PROTO]); 4625e0c1eb7SJozsef Kadlecsik with_ports = ip_set_proto_with_ports(data.proto); 46341d22f7bSJozsef Kadlecsik 46441d22f7bSJozsef Kadlecsik if (data.proto == 0) 46541d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_PROTO; 46641d22f7bSJozsef Kadlecsik } else 46741d22f7bSJozsef Kadlecsik return -IPSET_ERR_MISSING_PROTO; 46841d22f7bSJozsef Kadlecsik 4695e0c1eb7SJozsef Kadlecsik if (!(with_ports || data.proto == IPPROTO_ICMPV6)) 47041d22f7bSJozsef Kadlecsik data.port = 0; 47141d22f7bSJozsef Kadlecsik 47241d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_TIMEOUT]) { 47341d22f7bSJozsef Kadlecsik if (!with_timeout(h->timeout)) 47441d22f7bSJozsef Kadlecsik return -IPSET_ERR_TIMEOUT; 47541d22f7bSJozsef Kadlecsik timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); 47641d22f7bSJozsef Kadlecsik } 47741d22f7bSJozsef Kadlecsik 4785e0c1eb7SJozsef Kadlecsik if (adt == IPSET_TEST || !with_ports || !tb[IPSET_ATTR_PORT_TO]) { 4795416219eSJozsef Kadlecsik ret = adtfn(set, &data, timeout, flags); 48041d22f7bSJozsef Kadlecsik return ip_set_eexist(ret, flags) ? 0 : ret; 48141d22f7bSJozsef Kadlecsik } 48241d22f7bSJozsef Kadlecsik 48341d22f7bSJozsef Kadlecsik port = ntohs(data.port); 48441d22f7bSJozsef Kadlecsik port_to = ip_set_get_h16(tb[IPSET_ATTR_PORT_TO]); 48541d22f7bSJozsef Kadlecsik if (port > port_to) 48641d22f7bSJozsef Kadlecsik swap(port, port_to); 48741d22f7bSJozsef Kadlecsik 48841d22f7bSJozsef Kadlecsik for (; port <= port_to; port++) { 48941d22f7bSJozsef Kadlecsik data.port = htons(port); 4905416219eSJozsef Kadlecsik ret = adtfn(set, &data, timeout, flags); 49141d22f7bSJozsef Kadlecsik 49241d22f7bSJozsef Kadlecsik if (ret && !ip_set_eexist(ret, flags)) 49341d22f7bSJozsef Kadlecsik return ret; 49441d22f7bSJozsef Kadlecsik else 49541d22f7bSJozsef Kadlecsik ret = 0; 49641d22f7bSJozsef Kadlecsik } 49741d22f7bSJozsef Kadlecsik return ret; 49841d22f7bSJozsef Kadlecsik } 49941d22f7bSJozsef Kadlecsik 50041d22f7bSJozsef Kadlecsik /* Create hash:ip type of sets */ 50141d22f7bSJozsef Kadlecsik 50241d22f7bSJozsef Kadlecsik static int 50341d22f7bSJozsef Kadlecsik hash_ipportnet_create(struct ip_set *set, struct nlattr *tb[], u32 flags) 50441d22f7bSJozsef Kadlecsik { 50541d22f7bSJozsef Kadlecsik struct ip_set_hash *h; 50641d22f7bSJozsef Kadlecsik u32 hashsize = IPSET_DEFAULT_HASHSIZE, maxelem = IPSET_DEFAULT_MAXELEM; 50741d22f7bSJozsef Kadlecsik u8 hbits; 50841d22f7bSJozsef Kadlecsik 50941d22f7bSJozsef Kadlecsik if (!(set->family == AF_INET || set->family == AF_INET6)) 51041d22f7bSJozsef Kadlecsik return -IPSET_ERR_INVALID_FAMILY; 51141d22f7bSJozsef Kadlecsik 51241d22f7bSJozsef Kadlecsik if (unlikely(!ip_set_optattr_netorder(tb, IPSET_ATTR_HASHSIZE) || 51341d22f7bSJozsef Kadlecsik !ip_set_optattr_netorder(tb, IPSET_ATTR_MAXELEM) || 51441d22f7bSJozsef Kadlecsik !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT))) 51541d22f7bSJozsef Kadlecsik return -IPSET_ERR_PROTOCOL; 51641d22f7bSJozsef Kadlecsik 51741d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_HASHSIZE]) { 51841d22f7bSJozsef Kadlecsik hashsize = ip_set_get_h32(tb[IPSET_ATTR_HASHSIZE]); 51941d22f7bSJozsef Kadlecsik if (hashsize < IPSET_MIMINAL_HASHSIZE) 52041d22f7bSJozsef Kadlecsik hashsize = IPSET_MIMINAL_HASHSIZE; 52141d22f7bSJozsef Kadlecsik } 52241d22f7bSJozsef Kadlecsik 52341d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_MAXELEM]) 52441d22f7bSJozsef Kadlecsik maxelem = ip_set_get_h32(tb[IPSET_ATTR_MAXELEM]); 52541d22f7bSJozsef Kadlecsik 52641d22f7bSJozsef Kadlecsik h = kzalloc(sizeof(*h) 52741d22f7bSJozsef Kadlecsik + sizeof(struct ip_set_hash_nets) 52841d22f7bSJozsef Kadlecsik * (set->family == AF_INET ? 32 : 128), GFP_KERNEL); 52941d22f7bSJozsef Kadlecsik if (!h) 53041d22f7bSJozsef Kadlecsik return -ENOMEM; 53141d22f7bSJozsef Kadlecsik 53241d22f7bSJozsef Kadlecsik h->maxelem = maxelem; 53341d22f7bSJozsef Kadlecsik get_random_bytes(&h->initval, sizeof(h->initval)); 53441d22f7bSJozsef Kadlecsik h->timeout = IPSET_NO_TIMEOUT; 53541d22f7bSJozsef Kadlecsik 53641d22f7bSJozsef Kadlecsik hbits = htable_bits(hashsize); 53741d22f7bSJozsef Kadlecsik h->table = ip_set_alloc( 53841d22f7bSJozsef Kadlecsik sizeof(struct htable) 53941d22f7bSJozsef Kadlecsik + jhash_size(hbits) * sizeof(struct hbucket)); 54041d22f7bSJozsef Kadlecsik if (!h->table) { 54141d22f7bSJozsef Kadlecsik kfree(h); 54241d22f7bSJozsef Kadlecsik return -ENOMEM; 54341d22f7bSJozsef Kadlecsik } 54441d22f7bSJozsef Kadlecsik h->table->htable_bits = hbits; 54541d22f7bSJozsef Kadlecsik 54641d22f7bSJozsef Kadlecsik set->data = h; 54741d22f7bSJozsef Kadlecsik 54841d22f7bSJozsef Kadlecsik if (tb[IPSET_ATTR_TIMEOUT]) { 54941d22f7bSJozsef Kadlecsik h->timeout = ip_set_timeout_uget(tb[IPSET_ATTR_TIMEOUT]); 55041d22f7bSJozsef Kadlecsik 55141d22f7bSJozsef Kadlecsik set->variant = set->family == AF_INET 55241d22f7bSJozsef Kadlecsik ? &hash_ipportnet4_tvariant 55341d22f7bSJozsef Kadlecsik : &hash_ipportnet6_tvariant; 55441d22f7bSJozsef Kadlecsik 55541d22f7bSJozsef Kadlecsik if (set->family == AF_INET) 55641d22f7bSJozsef Kadlecsik hash_ipportnet4_gc_init(set); 55741d22f7bSJozsef Kadlecsik else 55841d22f7bSJozsef Kadlecsik hash_ipportnet6_gc_init(set); 55941d22f7bSJozsef Kadlecsik } else { 56041d22f7bSJozsef Kadlecsik set->variant = set->family == AF_INET 56141d22f7bSJozsef Kadlecsik ? &hash_ipportnet4_variant : &hash_ipportnet6_variant; 56241d22f7bSJozsef Kadlecsik } 56341d22f7bSJozsef Kadlecsik 56441d22f7bSJozsef Kadlecsik pr_debug("create %s hashsize %u (%u) maxelem %u: %p(%p)\n", 56541d22f7bSJozsef Kadlecsik set->name, jhash_size(h->table->htable_bits), 56641d22f7bSJozsef Kadlecsik h->table->htable_bits, h->maxelem, set->data, h->table); 56741d22f7bSJozsef Kadlecsik 56841d22f7bSJozsef Kadlecsik return 0; 56941d22f7bSJozsef Kadlecsik } 57041d22f7bSJozsef Kadlecsik 57141d22f7bSJozsef Kadlecsik static struct ip_set_type hash_ipportnet_type __read_mostly = { 57241d22f7bSJozsef Kadlecsik .name = "hash:ip,port,net", 57341d22f7bSJozsef Kadlecsik .protocol = IPSET_PROTOCOL, 57441d22f7bSJozsef Kadlecsik .features = IPSET_TYPE_IP | IPSET_TYPE_PORT | IPSET_TYPE_IP2, 57541d22f7bSJozsef Kadlecsik .dimension = IPSET_DIM_THREE, 57641d22f7bSJozsef Kadlecsik .family = AF_UNSPEC, 57791eb7c08SJozsef Kadlecsik .revision = 1, 57841d22f7bSJozsef Kadlecsik .create = hash_ipportnet_create, 57941d22f7bSJozsef Kadlecsik .create_policy = { 58041d22f7bSJozsef Kadlecsik [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, 58141d22f7bSJozsef Kadlecsik [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, 58241d22f7bSJozsef Kadlecsik [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, 58341d22f7bSJozsef Kadlecsik [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, 58441d22f7bSJozsef Kadlecsik [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, 58541d22f7bSJozsef Kadlecsik }, 58641d22f7bSJozsef Kadlecsik .adt_policy = { 58741d22f7bSJozsef Kadlecsik [IPSET_ATTR_IP] = { .type = NLA_NESTED }, 58841d22f7bSJozsef Kadlecsik [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, 58941d22f7bSJozsef Kadlecsik [IPSET_ATTR_IP2] = { .type = NLA_NESTED }, 59041d22f7bSJozsef Kadlecsik [IPSET_ATTR_PORT] = { .type = NLA_U16 }, 59141d22f7bSJozsef Kadlecsik [IPSET_ATTR_PORT_TO] = { .type = NLA_U16 }, 59241d22f7bSJozsef Kadlecsik [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, 59341d22f7bSJozsef Kadlecsik [IPSET_ATTR_CIDR2] = { .type = NLA_U8 }, 59441d22f7bSJozsef Kadlecsik [IPSET_ATTR_PROTO] = { .type = NLA_U8 }, 59541d22f7bSJozsef Kadlecsik [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, 59641d22f7bSJozsef Kadlecsik [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, 59741d22f7bSJozsef Kadlecsik }, 59841d22f7bSJozsef Kadlecsik .me = THIS_MODULE, 59941d22f7bSJozsef Kadlecsik }; 60041d22f7bSJozsef Kadlecsik 60141d22f7bSJozsef Kadlecsik static int __init 60241d22f7bSJozsef Kadlecsik hash_ipportnet_init(void) 60341d22f7bSJozsef Kadlecsik { 60441d22f7bSJozsef Kadlecsik return ip_set_type_register(&hash_ipportnet_type); 60541d22f7bSJozsef Kadlecsik } 60641d22f7bSJozsef Kadlecsik 60741d22f7bSJozsef Kadlecsik static void __exit 60841d22f7bSJozsef Kadlecsik hash_ipportnet_fini(void) 60941d22f7bSJozsef Kadlecsik { 61041d22f7bSJozsef Kadlecsik ip_set_type_unregister(&hash_ipportnet_type); 61141d22f7bSJozsef Kadlecsik } 61241d22f7bSJozsef Kadlecsik 61341d22f7bSJozsef Kadlecsik module_init(hash_ipportnet_init); 61441d22f7bSJozsef Kadlecsik module_exit(hash_ipportnet_fini); 615