linux/net/netfilter/ipset/ip_set_hash_ipmark.c
<<
>>
Prefs
   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 false;
  67
  68nla_put_failure:
  69        return true;
  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 HOST_MASK       32
  81#include "ip_set_hash_gen.h"
  82
  83static int
  84hash_ipmark4_kadt(struct ip_set *set, const struct sk_buff *skb,
  85                  const struct xt_action_param *par,
  86                  enum ipset_adt adt, struct ip_set_adt_opt *opt)
  87{
  88        const struct hash_ipmark4 *h = set->data;
  89        ipset_adtfn adtfn = set->variant->adt[adt];
  90        struct hash_ipmark4_elem e = { };
  91        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
  92
  93        e.mark = skb->mark;
  94        e.mark &= h->markmask;
  95
  96        ip4addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip);
  97        return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
  98}
  99
 100static int
 101hash_ipmark4_uadt(struct ip_set *set, struct nlattr *tb[],
 102                  enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 103{
 104        const struct hash_ipmark4 *h = set->data;
 105        ipset_adtfn adtfn = set->variant->adt[adt];
 106        struct hash_ipmark4_elem e = { };
 107        struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
 108        u32 ip, ip_to = 0;
 109        int ret;
 110
 111        if (tb[IPSET_ATTR_LINENO])
 112                *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 113
 114        if (unlikely(!tb[IPSET_ATTR_IP] ||
 115                     !ip_set_attr_netorder(tb, IPSET_ATTR_MARK)))
 116                return -IPSET_ERR_PROTOCOL;
 117
 118        ret = ip_set_get_ipaddr4(tb[IPSET_ATTR_IP], &e.ip);
 119        if (ret)
 120                return ret;
 121
 122        ret = ip_set_get_extensions(set, tb, &ext);
 123        if (ret)
 124                return ret;
 125
 126        e.mark = ntohl(nla_get_be32(tb[IPSET_ATTR_MARK]));
 127        e.mark &= h->markmask;
 128
 129        if (adt == IPSET_TEST ||
 130            !(tb[IPSET_ATTR_IP_TO] || tb[IPSET_ATTR_CIDR])) {
 131                ret = adtfn(set, &e, &ext, &ext, flags);
 132                return ip_set_eexist(ret, flags) ? 0 : ret;
 133        }
 134
 135        ip_to = ip = ntohl(e.ip);
 136        if (tb[IPSET_ATTR_IP_TO]) {
 137                ret = ip_set_get_hostipaddr4(tb[IPSET_ATTR_IP_TO], &ip_to);
 138                if (ret)
 139                        return ret;
 140                if (ip > ip_to)
 141                        swap(ip, ip_to);
 142        } else if (tb[IPSET_ATTR_CIDR]) {
 143                u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 144
 145                if (!cidr || cidr > HOST_MASK)
 146                        return -IPSET_ERR_INVALID_CIDR;
 147                ip_set_mask_from_to(ip, ip_to, cidr);
 148        }
 149
 150        if (retried)
 151                ip = ntohl(h->next.ip);
 152        for (; !before(ip_to, ip); ip++) {
 153                e.ip = htonl(ip);
 154                ret = adtfn(set, &e, &ext, &ext, flags);
 155
 156                if (ret && !ip_set_eexist(ret, flags))
 157                        return ret;
 158
 159                ret = 0;
 160        }
 161        return ret;
 162}
 163
 164/* IPv6 variant */
 165
 166struct hash_ipmark6_elem {
 167        union nf_inet_addr ip;
 168        __u32 mark;
 169};
 170
 171/* Common functions */
 172
 173static inline bool
 174hash_ipmark6_data_equal(const struct hash_ipmark6_elem *ip1,
 175                        const struct hash_ipmark6_elem *ip2,
 176                        u32 *multi)
 177{
 178        return ipv6_addr_equal(&ip1->ip.in6, &ip2->ip.in6) &&
 179               ip1->mark == ip2->mark;
 180}
 181
 182static bool
 183hash_ipmark6_data_list(struct sk_buff *skb,
 184                       const struct hash_ipmark6_elem *data)
 185{
 186        if (nla_put_ipaddr6(skb, IPSET_ATTR_IP, &data->ip.in6) ||
 187            nla_put_net32(skb, IPSET_ATTR_MARK, htonl(data->mark)))
 188                goto nla_put_failure;
 189        return false;
 190
 191nla_put_failure:
 192        return true;
 193}
 194
 195static inline void
 196hash_ipmark6_data_next(struct hash_ipmark6_elem *next,
 197                       const struct hash_ipmark6_elem *d)
 198{
 199}
 200
 201#undef MTYPE
 202#undef HOST_MASK
 203
 204#define MTYPE           hash_ipmark6
 205#define HOST_MASK       128
 206#define IP_SET_EMIT_CREATE
 207#include "ip_set_hash_gen.h"
 208
 209static int
 210hash_ipmark6_kadt(struct ip_set *set, const struct sk_buff *skb,
 211                  const struct xt_action_param *par,
 212                  enum ipset_adt adt, struct ip_set_adt_opt *opt)
 213{
 214        const struct hash_ipmark6 *h = set->data;
 215        ipset_adtfn adtfn = set->variant->adt[adt];
 216        struct hash_ipmark6_elem e = { };
 217        struct ip_set_ext ext = IP_SET_INIT_KEXT(skb, opt, set);
 218
 219        e.mark = skb->mark;
 220        e.mark &= h->markmask;
 221
 222        ip6addrptr(skb, opt->flags & IPSET_DIM_ONE_SRC, &e.ip.in6);
 223        return adtfn(set, &e, &ext, &opt->ext, opt->cmdflags);
 224}
 225
 226static int
 227hash_ipmark6_uadt(struct ip_set *set, struct nlattr *tb[],
 228                  enum ipset_adt adt, u32 *lineno, u32 flags, bool retried)
 229{
 230        const struct hash_ipmark6 *h = set->data;
 231        ipset_adtfn adtfn = set->variant->adt[adt];
 232        struct hash_ipmark6_elem e = { };
 233        struct ip_set_ext ext = IP_SET_INIT_UEXT(set);
 234        int ret;
 235
 236        if (tb[IPSET_ATTR_LINENO])
 237                *lineno = nla_get_u32(tb[IPSET_ATTR_LINENO]);
 238
 239        if (unlikely(!tb[IPSET_ATTR_IP] ||
 240                     !ip_set_attr_netorder(tb, IPSET_ATTR_MARK)))
 241                return -IPSET_ERR_PROTOCOL;
 242        if (unlikely(tb[IPSET_ATTR_IP_TO]))
 243                return -IPSET_ERR_HASH_RANGE_UNSUPPORTED;
 244        if (unlikely(tb[IPSET_ATTR_CIDR])) {
 245                u8 cidr = nla_get_u8(tb[IPSET_ATTR_CIDR]);
 246
 247                if (cidr != HOST_MASK)
 248                        return -IPSET_ERR_INVALID_CIDR;
 249        }
 250
 251        ret = ip_set_get_ipaddr6(tb[IPSET_ATTR_IP], &e.ip);
 252        if (ret)
 253                return ret;
 254
 255        ret = ip_set_get_extensions(set, tb, &ext);
 256        if (ret)
 257                return ret;
 258
 259        e.mark = ntohl(nla_get_be32(tb[IPSET_ATTR_MARK]));
 260        e.mark &= h->markmask;
 261
 262        if (adt == IPSET_TEST) {
 263                ret = adtfn(set, &e, &ext, &ext, flags);
 264                return ip_set_eexist(ret, flags) ? 0 : ret;
 265        }
 266
 267        ret = adtfn(set, &e, &ext, &ext, flags);
 268        if (ret && !ip_set_eexist(ret, flags))
 269                return ret;
 270
 271        return 0;
 272}
 273
 274static struct ip_set_type hash_ipmark_type __read_mostly = {
 275        .name           = "hash:ip,mark",
 276        .protocol       = IPSET_PROTOCOL,
 277        .features       = IPSET_TYPE_IP | IPSET_TYPE_MARK,
 278        .dimension      = IPSET_DIM_TWO,
 279        .family         = NFPROTO_UNSPEC,
 280        .revision_min   = IPSET_TYPE_REV_MIN,
 281        .revision_max   = IPSET_TYPE_REV_MAX,
 282        .create         = hash_ipmark_create,
 283        .create_policy  = {
 284                [IPSET_ATTR_MARKMASK]   = { .type = NLA_U32 },
 285                [IPSET_ATTR_HASHSIZE]   = { .type = NLA_U32 },
 286                [IPSET_ATTR_MAXELEM]    = { .type = NLA_U32 },
 287                [IPSET_ATTR_PROBES]     = { .type = NLA_U8 },
 288                [IPSET_ATTR_RESIZE]     = { .type = NLA_U8  },
 289                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
 290                [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 },
 291        },
 292        .adt_policy     = {
 293                [IPSET_ATTR_IP]         = { .type = NLA_NESTED },
 294                [IPSET_ATTR_IP_TO]      = { .type = NLA_NESTED },
 295                [IPSET_ATTR_MARK]       = { .type = NLA_U32 },
 296                [IPSET_ATTR_CIDR]       = { .type = NLA_U8 },
 297                [IPSET_ATTR_TIMEOUT]    = { .type = NLA_U32 },
 298                [IPSET_ATTR_LINENO]     = { .type = NLA_U32 },
 299                [IPSET_ATTR_BYTES]      = { .type = NLA_U64 },
 300                [IPSET_ATTR_PACKETS]    = { .type = NLA_U64 },
 301                [IPSET_ATTR_COMMENT]    = { .type = NLA_NUL_STRING,
 302                                            .len  = IPSET_MAX_COMMENT_SIZE },
 303                [IPSET_ATTR_SKBMARK]    = { .type = NLA_U64 },
 304                [IPSET_ATTR_SKBPRIO]    = { .type = NLA_U32 },
 305                [IPSET_ATTR_SKBQUEUE]   = { .type = NLA_U16 },
 306        },
 307        .me             = THIS_MODULE,
 308};
 309
 310static int __init
 311hash_ipmark_init(void)
 312{
 313        return ip_set_type_register(&hash_ipmark_type);
 314}
 315
 316static void __exit
 317hash_ipmark_fini(void)
 318{
 319        rcu_barrier();
 320        ip_set_type_unregister(&hash_ipmark_type);
 321}
 322
 323module_init(hash_ipmark_init);
 324module_exit(hash_ipmark_fini);
 325