Commit 6d4fa852a023080101f1665ea189dd1844c87fef

Authored by Florian Westphal
Committed by David S. Miller
1 parent fa919833e3

net: sched: add ipset ematch

Can be used to match packets against netfilter ip sets created via ipset(8).
skb->sk_iif is used as 'incoming interface', skb->dev is 'outgoing interface'.

Since ipset is usually called from netfilter, the ematch
initializes a fake xt_action_param, pulls the ip header into the
linear area and also sets skb->data to the IP header (otherwise
matching Layer 4 set types doesn't work).

Tested-by: Mr Dash Four <mr.dash.four@googlemail.com>
Signed-off-by: Florian Westphal <fw@strlen.de>
Signed-off-by: David S. Miller <davem@davemloft.net>

Showing 4 changed files with 148 additions and 1 deletions Side-by-side Diff

include/linux/pkt_cls.h
... ... @@ -453,7 +453,8 @@
453 453 #define TCF_EM_TEXT 5
454 454 #define TCF_EM_VLAN 6
455 455 #define TCF_EM_CANID 7
456   -#define TCF_EM_MAX 7
  456 +#define TCF_EM_IPSET 8
  457 +#define TCF_EM_MAX 8
457 458  
458 459 enum {
459 460 TCF_EM_PROG_TC
... ... @@ -517,6 +517,16 @@
517 517 To compile this code as a module, choose M here: the
518 518 module will be called em_canid.
519 519  
  520 +config NET_EMATCH_IPSET
  521 + tristate "IPset"
  522 + depends on NET_EMATCH && IP_SET
  523 + ---help---
  524 + Say Y here if you want to be able to classify packets based on
  525 + ipset membership.
  526 +
  527 + To compile this code as a module, choose M here: the
  528 + module will be called em_ipset.
  529 +
520 530 config NET_CLS_ACT
521 531 bool "Actions"
522 532 ---help---
... ... @@ -56,4 +56,5 @@
56 56 obj-$(CONFIG_NET_EMATCH_META) += em_meta.o
57 57 obj-$(CONFIG_NET_EMATCH_TEXT) += em_text.o
58 58 obj-$(CONFIG_NET_EMATCH_CANID) += em_canid.o
  59 +obj-$(CONFIG_NET_EMATCH_IPSET) += em_ipset.o
net/sched/em_ipset.c
  1 +/*
  2 + * net/sched/em_ipset.c ipset ematch
  3 + *
  4 + * Copyright (c) 2012 Florian Westphal <fw@strlen.de>
  5 + *
  6 + * This program is free software; you can redistribute it and/or
  7 + * modify it under the terms of the GNU General Public License
  8 + * version 2 as published by the Free Software Foundation.
  9 + */
  10 +
  11 +#include <linux/gfp.h>
  12 +#include <linux/module.h>
  13 +#include <linux/types.h>
  14 +#include <linux/kernel.h>
  15 +#include <linux/string.h>
  16 +#include <linux/skbuff.h>
  17 +#include <linux/netfilter/xt_set.h>
  18 +#include <linux/ipv6.h>
  19 +#include <net/ip.h>
  20 +#include <net/pkt_cls.h>
  21 +
  22 +static int em_ipset_change(struct tcf_proto *tp, void *data, int data_len,
  23 + struct tcf_ematch *em)
  24 +{
  25 + struct xt_set_info *set = data;
  26 + ip_set_id_t index;
  27 +
  28 + if (data_len != sizeof(*set))
  29 + return -EINVAL;
  30 +
  31 + index = ip_set_nfnl_get_byindex(set->index);
  32 + if (index == IPSET_INVALID_ID)
  33 + return -ENOENT;
  34 +
  35 + em->datalen = sizeof(*set);
  36 + em->data = (unsigned long)kmemdup(data, em->datalen, GFP_KERNEL);
  37 + if (em->data)
  38 + return 0;
  39 +
  40 + ip_set_nfnl_put(index);
  41 + return -ENOMEM;
  42 +}
  43 +
  44 +static void em_ipset_destroy(struct tcf_proto *p, struct tcf_ematch *em)
  45 +{
  46 + const struct xt_set_info *set = (const void *) em->data;
  47 + if (set) {
  48 + ip_set_nfnl_put(set->index);
  49 + kfree((void *) em->data);
  50 + }
  51 +}
  52 +
  53 +static int em_ipset_match(struct sk_buff *skb, struct tcf_ematch *em,
  54 + struct tcf_pkt_info *info)
  55 +{
  56 + struct ip_set_adt_opt opt;
  57 + struct xt_action_param acpar;
  58 + const struct xt_set_info *set = (const void *) em->data;
  59 + struct net_device *dev, *indev = NULL;
  60 + int ret, network_offset;
  61 +
  62 + switch (skb->protocol) {
  63 + case htons(ETH_P_IP):
  64 + acpar.family = NFPROTO_IPV4;
  65 + if (!pskb_network_may_pull(skb, sizeof(struct iphdr)))
  66 + return 0;
  67 + acpar.thoff = ip_hdrlen(skb);
  68 + break;
  69 + case htons(ETH_P_IPV6):
  70 + acpar.family = NFPROTO_IPV6;
  71 + if (!pskb_network_may_pull(skb, sizeof(struct ipv6hdr)))
  72 + return 0;
  73 + /* doesn't call ipv6_find_hdr() because ipset doesn't use thoff, yet */
  74 + acpar.thoff = sizeof(struct ipv6hdr);
  75 + break;
  76 + default:
  77 + return 0;
  78 + }
  79 +
  80 + acpar.hooknum = 0;
  81 +
  82 + opt.family = acpar.family;
  83 + opt.dim = set->dim;
  84 + opt.flags = set->flags;
  85 + opt.cmdflags = 0;
  86 + opt.timeout = ~0u;
  87 +
  88 + network_offset = skb_network_offset(skb);
  89 + skb_pull(skb, network_offset);
  90 +
  91 + dev = skb->dev;
  92 +
  93 + rcu_read_lock();
  94 +
  95 + if (dev && skb->skb_iif)
  96 + indev = dev_get_by_index_rcu(dev_net(dev), skb->skb_iif);
  97 +
  98 + acpar.in = indev ? indev : dev;
  99 + acpar.out = dev;
  100 +
  101 + ret = ip_set_test(set->index, skb, &acpar, &opt);
  102 +
  103 + rcu_read_unlock();
  104 +
  105 + skb_push(skb, network_offset);
  106 + return ret;
  107 +}
  108 +
  109 +static struct tcf_ematch_ops em_ipset_ops = {
  110 + .kind = TCF_EM_IPSET,
  111 + .change = em_ipset_change,
  112 + .destroy = em_ipset_destroy,
  113 + .match = em_ipset_match,
  114 + .owner = THIS_MODULE,
  115 + .link = LIST_HEAD_INIT(em_ipset_ops.link)
  116 +};
  117 +
  118 +static int __init init_em_ipset(void)
  119 +{
  120 + return tcf_em_register(&em_ipset_ops);
  121 +}
  122 +
  123 +static void __exit exit_em_ipset(void)
  124 +{
  125 + tcf_em_unregister(&em_ipset_ops);
  126 +}
  127 +
  128 +MODULE_LICENSE("GPL");
  129 +MODULE_AUTHOR("Florian Westphal <fw@strlen.de>");
  130 +MODULE_DESCRIPTION("TC extended match for IP sets");
  131 +
  132 +module_init(init_em_ipset);
  133 +module_exit(exit_em_ipset);
  134 +
  135 +MODULE_ALIAS_TCF_EMATCH(TCF_EM_IPSET);