1/* Copyright (C) 2003-2013 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> 2 * Copyright (C) 2013 Smoothwall Ltd. <vytas.dauksa@smoothwall.net> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9/* Kernel module implementing an IP set type: the hash:ip,mark type */ 10 11#include <linux/jhash.h> 12#include <linux/module.h> 13#include <linux/ip.h> 14#include <linux/skbuff.h> 15#include <linux/errno.h> 16#include <linux/random.h> 17#include <net/ip.h> 18#include <net/ipv6.h> 19#include <net/netlink.h> 20#include <net/tcp.h> 21 22#include <linux/netfilter.h> 23#include <linux/netfilter/ipset/pfxlen.h> 24#include <linux/netfilter/ipset/ip_set.h> 25#include <linux/netfilter/ipset/ip_set_hash.h> 26 27#define IPSET_TYPE_REV_MIN 0 28/* 1 Forceadd support */ 29#define IPSET_TYPE_REV_MAX 2 /* skbinfo support */ 30 31MODULE_LICENSE("GPL"); 32MODULE_AUTHOR("Vytas Dauksa <vytas.dauksa@smoothwall.net>"); 33IP_SET_MODULE_DESC("hash:ip,mark", IPSET_TYPE_REV_MIN, IPSET_TYPE_REV_MAX); 34MODULE_ALIAS("ip_set_hash:ip,mark"); 35 36/* Type specific function prefix */ 37#define HTYPE hash_ipmark 38#define IP_SET_HASH_WITH_MARKMASK 39 40/* IPv4 variant */ 41 42/* Member elements */ 43struct hash_ipmark4_elem { 44 __be32 ip; 45 __u32 mark; 46}; 47 48/* Common functions */ 49 50static inline bool 51hash_ipmark4_data_equal(const struct hash_ipmark4_elem *ip1, 52 const struct hash_ipmark4_elem *ip2, 53 u32 *multi) 54{ 55 return ip1->ip == ip2->ip && 56 ip1->mark == ip2->mark; 57} 58 59static bool 60hash_ipmark4_data_list(struct sk_buff *skb, 61 const struct hash_ipmark4_elem *data) 62{ 63 if (nla_put_ipaddr4(skb, IPSET_ATTR_IP, data->ip) || 64 nla_put_net32(skb, IPSET_ATTR_MARK, htonl(data->mark))) 65 goto nla_put_failure; 66 return 0; 67 68nla_put_failure: 69 return 1; 70} 71 72static inline void 73hash_ipmark4_data_next(struct hash_ipmark4_elem *next, 74 const struct hash_ipmark4_elem *d) 75{ 76 next->ip = d->ip; 77} 78 79#define MTYPE hash_ipmark4 80#define PF 4 81#define HOST_MASK 32 82#define HKEY_DATALEN sizeof(struct hash_ipmark4_elem) 83#include "ip_set_hash_gen.h" 84 85static int 86hash_ipmark4_kadt(struct ip_set *set, const struct sk_buff *skb, 87 const struct xt_action_param *par, 88 enum ipset_adt adt, struct ip_set_adt_opt *opt) 89{ 90 const struct hash_ipmark *h = set->data; 91 ipset_adtfn adtfn = set->variant->adt[adt]; 92 struct hash_ipmark4_elem e = { }; 93 struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); 94 95 e.mark = skb->mark; 96 e.mark &= h->markmask; 97 98 ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip); 99 return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); 100} 101 102static int 103hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[], 104 enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) 105{ 106 const struct hash_ipmark *h = set->data; 107 ipset_adtfn adtfn = set->variant->adt[adt]; 108 struct hash_ipmark4_elem e = { }; 109 struct ip_set_ext ext = IP_SET_INIT_UEXT(set); 110 u32 ip, ip_to = 0; 111 int ret; 112 113 if (unlikely(!tb[IPSET_ATTR_IP] || 114 !ip_set_attr_netorder(tb, IPSET_ATTR_MARK) || 115 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || 116 !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || 117 !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) || 118 !ip_set_optattr_netorder(tb, IPSET_ATTR_SKBMARK) || 119 !ip_set_optattr_netorder(tb, IPSET_ATTR_SKBPRIO) || 120 !ip_set_optattr_netorder(tb, IPSET_ATTR_SKBQUEUE))) 121 return -IPSET_ERR_PROTOCOL; 122 123 if (tb[IPSET_ATTR_LINENO]) 124 *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); 125 126 ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &e.ip) || 127 ip_set_get_extensions(set, tb, &ext); 128 if (ret) 129 return ret; 130 131 e.mark = ntohl(nla_get_u32(tb[IPSET_ATTR_MARK])); 132 e.mark &= h->markmask; 133 134 if (adt == IPSET_TEST || 135 !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR])) { 136 ret = adtfn(set, &e, &ext, &ext, flags); 137 return ip_set_eexist(ret, flags) ? 0 : ret; 138 } 139 140 ip_to = ip = ntohl(e.ip); 141 if (tb[IPSET_ATTR_IP_TO]) { 142 ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to); 143 if (ret) 144 return ret; 145 if (ip > ip_to) 146 swap(ip, ip_to); 147 } else if (tb[IPSET_ATTR_CIDR]) { 148 u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]); 149 150 if (!cidr || cidr > 32) 151 return -IPSET_ERR_INVALID_CIDR; 152 ip_set_mask_from_to(ip, ip_to, cidr); 153 } 154 155 if (retried) 156 ip = ntohl(h->next.ip); 157 for (; !before(ip_to, ip); ip++) { 158 e.ip = htonl(ip); 159 ret = adtfn(set, &e, &ext, &ext, flags); 160 161 if (ret && !ip_set_eexist(ret, flags)) 162 return ret; 163 else 164 ret = 0; 165 } 166 return ret; 167} 168 169/* IPv6 variant */ 170 171struct hash_ipmark6_elem { 172 union nf_inet_addr ip; 173 __u32 mark; 174}; 175 176/* Common functions */ 177 178static inline bool 179hash_ipmark6_data_equal(const struct hash_ipmark6_elem *ip1, 180 const struct hash_ipmark6_elem *ip2, 181 u32 *multi) 182{ 183 return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) && 184 ip1->mark == ip2->mark; 185} 186 187static bool 188hash_ipmark6_data_list(struct sk_buff *skb, 189 const struct hash_ipmark6_elem *data) 190{ 191 if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) || 192 nla_put_net32(skb, IPSET_ATTR_MARK, htonl(data->mark))) 193 goto nla_put_failure; 194 return 0; 195 196nla_put_failure: 197 return 1; 198} 199 200static inline void 201hash_ipmark6_data_next(struct hash_ipmark4_elem *next, 202 const struct hash_ipmark6_elem *d) 203{ 204} 205 206#undef MTYPE 207#undef PF 208#undef HOST_MASK 209#undef HKEY_DATALEN 210 211#define MTYPE hash_ipmark6 212#define PF 6 213#define HOST_MASK 128 214#define HKEY_DATALEN sizeof(struct hash_ipmark6_elem) 215#define IP_SET_EMIT_CREATE 216#include "ip_set_hash_gen.h" 217 218 219static int 220hash_ipmark6_kadt(struct ip_set *set, const struct sk_buff *skb, 221 const struct xt_action_param *par, 222 enum ipset_adt adt, struct ip_set_adt_opt *opt) 223{ 224 const struct hash_ipmark *h = set->data; 225 ipset_adtfn adtfn = set->variant->adt[adt]; 226 struct hash_ipmark6_elem e = { }; 227 struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set); 228 229 e.mark = skb->mark; 230 e.mark &= h->markmask; 231 232 ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6); 233 return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags); 234} 235 236static int 237hash_ipmark6_uadt(struct ip_set *set, struct nlattr *tb[], 238 enum ipset_adt adt, u32 *lineno, u32 flags, bool retried) 239{ 240 const struct hash_ipmark *h = set->data; 241 ipset_adtfn adtfn = set->variant->adt[adt]; 242 struct hash_ipmark6_elem e = { }; 243 struct ip_set_ext ext = IP_SET_INIT_UEXT(set); 244 int ret; 245 246 if (unlikely(!tb[IPSET_ATTR_IP] || 247 !ip_set_attr_netorder(tb, IPSET_ATTR_MARK) || 248 !ip_set_optattr_netorder(tb, IPSET_ATTR_TIMEOUT) || 249 !ip_set_optattr_netorder(tb, IPSET_ATTR_PACKETS) || 250 !ip_set_optattr_netorder(tb, IPSET_ATTR_BYTES) || 251 !ip_set_optattr_netorder(tb, IPSET_ATTR_SKBMARK) || 252 !ip_set_optattr_netorder(tb, IPSET_ATTR_SKBPRIO) || 253 !ip_set_optattr_netorder(tb, IPSET_ATTR_SKBQUEUE) || 254 tb[IPSET_ATTR_IP_TO] || 255 tb[IPSET_ATTR_CIDR])) 256 return -IPSET_ERR_PROTOCOL; 257 258 if (tb[IPSET_ATTR_LINENO]) 259 *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]); 260 261 ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip) || 262 ip_set_get_extensions(set, tb, &ext); 263 if (ret) 264 return ret; 265 266 e.mark = ntohl(nla_get_u32(tb[IPSET_ATTR_MARK])); 267 e.mark &= h->markmask; 268 269 if (adt == IPSET_TEST) { 270 ret = adtfn(set, &e, &ext, &ext, flags); 271 return ip_set_eexist(ret, flags) ? 0 : ret; 272 } 273 274 ret = adtfn(set, &e, &ext, &ext, flags); 275 if (ret && !ip_set_eexist(ret, flags)) 276 return ret; 277 else 278 ret = 0; 279 280 return ret; 281} 282 283static struct ip_set_type hash_ipmark_type __read_mostly = { 284 .name = "hash:ip,mark", 285 .protocol = IPSET_PROTOCOL, 286 .features = IPSET_TYPE_IP | IPSET_TYPE_MARK, 287 .dimension = IPSET_DIM_TWO, 288 .family = NFPROTO_UNSPEC, 289 .revision_min = IPSET_TYPE_REV_MIN, 290 .revision_max = IPSET_TYPE_REV_MAX, 291 .create = hash_ipmark_create, 292 .create_policy = { 293 [IPSET_ATTR_MARKMASK] = { .type = NLA_U32 }, 294 [IPSET_ATTR_HASHSIZE] = { .type = NLA_U32 }, 295 [IPSET_ATTR_MAXELEM] = { .type = NLA_U32 }, 296 [IPSET_ATTR_PROBES] = { .type = NLA_U8 }, 297 [IPSET_ATTR_RESIZE] = { .type = NLA_U8 }, 298 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, 299 [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, 300 }, 301 .adt_policy = { 302 [IPSET_ATTR_IP] = { .type = NLA_NESTED }, 303 [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, 304 [IPSET_ATTR_MARK] = { .type = NLA_U32 }, 305 [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, 306 [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, 307 [IPSET_ATTR_LINENO] = { .type = NLA_U32 }, 308 [IPSET_ATTR_BYTES] = { .type = NLA_U64 }, 309 [IPSET_ATTR_PACKETS] = { .type = NLA_U64 }, 310 [IPSET_ATTR_COMMENT] = { .type = NLA_NUL_STRING }, 311 [IPSET_ATTR_SKBMARK] = { .type = NLA_U64 }, 312 [IPSET_ATTR_SKBPRIO] = { .type = NLA_U32 }, 313 [IPSET_ATTR_SKBQUEUE] = { .type = NLA_U16 }, 314 }, 315 .me = THIS_MODULE, 316}; 317 318static int __init 319hash_ipmark_init(void) 320{ 321 return ip_set_type_register(&hash_ipmark_type); 322} 323 324static void __exit 325hash_ipmark_fini(void) 326{ 327 ip_set_type_unregister(&hash_ipmark_type); 328} 329 330module_init(hash_ipmark_init); 331module_exit(hash_ipmark_fini); 332