linux/net/sched/act_connmark.c
<<
>>
Prefs
   1/*
   2 * net/sched/act_connmark.c  netfilter connmark retriever action
   3 * skb mark is over-written
   4 *
   5 * Copyright (c) 2011 Felix Fietkau <nbd@openwrt.org>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * (at your option) any later version.
  11*/
  12
  13#include <linux/module.h>
  14#include <linux/init.h>
  15#include <linux/kernel.h>
  16#include <linux/skbuff.h>
  17#include <linux/rtnetlink.h>
  18#include <linux/pkt_cls.h>
  19#include <linux/ip.h>
  20#include <linux/ipv6.h>
  21#include <net/netlink.h>
  22#include <net/pkt_sched.h>
  23#include <net/act_api.h>
  24#include <uapi/linux/tc_act/tc_connmark.h>
  25#include <net/tc_act/tc_connmark.h>
  26
  27#include <net/netfilter/nf_conntrack.h>
  28#include <net/netfilter/nf_conntrack_core.h>
  29#include <net/netfilter/nf_conntrack_zones.h>
  30
  31#define CONNMARK_TAB_MASK     3
  32
  33static int tcf_connmark(struct sk_buff *skb, const struct tc_action *a,
  34                        struct tcf_result *res)
  35{
  36        const struct nf_conntrack_tuple_hash *thash;
  37        struct nf_conntrack_tuple tuple;
  38        enum ip_conntrack_info ctinfo;
  39        struct tcf_connmark_info *ca = a->priv;
  40        struct nf_conntrack_zone zone;
  41        struct nf_conn *c;
  42        int proto;
  43
  44        spin_lock(&ca->tcf_lock);
  45        ca->tcf_tm.lastuse = jiffies;
  46        bstats_update(&ca->tcf_bstats, skb);
  47
  48        if (skb->protocol == htons(ETH_P_IP)) {
  49                if (skb->len < sizeof(struct iphdr))
  50                        goto out;
  51
  52                proto = NFPROTO_IPV4;
  53        } else if (skb->protocol == htons(ETH_P_IPV6)) {
  54                if (skb->len < sizeof(struct ipv6hdr))
  55                        goto out;
  56
  57                proto = NFPROTO_IPV6;
  58        } else {
  59                goto out;
  60        }
  61
  62        c = nf_ct_get(skb, &ctinfo);
  63        if (c) {
  64                skb->mark = c->mark;
  65                /* using overlimits stats to count how many packets marked */
  66                ca->tcf_qstats.overlimits++;
  67                goto out;
  68        }
  69
  70        if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
  71                               proto, ca->net, &tuple))
  72                goto out;
  73
  74        zone.id = ca->zone;
  75        zone.dir = NF_CT_DEFAULT_ZONE_DIR;
  76
  77        thash = nf_conntrack_find_get(ca->net, &zone, &tuple);
  78        if (!thash)
  79                goto out;
  80
  81        c = nf_ct_tuplehash_to_ctrack(thash);
  82        /* using overlimits stats to count how many packets marked */
  83        ca->tcf_qstats.overlimits++;
  84        skb->mark = c->mark;
  85        nf_ct_put(c);
  86
  87out:
  88        spin_unlock(&ca->tcf_lock);
  89        return ca->tcf_action;
  90}
  91
  92static const struct nla_policy connmark_policy[TCA_CONNMARK_MAX + 1] = {
  93        [TCA_CONNMARK_PARMS] = { .len = sizeof(struct tc_connmark) },
  94};
  95
  96static int tcf_connmark_init(struct net *net, struct nlattr *nla,
  97                             struct nlattr *est, struct tc_action *a,
  98                             int ovr, int bind)
  99{
 100        struct nlattr *tb[TCA_CONNMARK_MAX + 1];
 101        struct tcf_connmark_info *ci;
 102        struct tc_connmark *parm;
 103        int ret = 0;
 104
 105        if (!nla)
 106                return -EINVAL;
 107
 108        ret = nla_parse_nested(tb, TCA_CONNMARK_MAX, nla, connmark_policy);
 109        if (ret < 0)
 110                return ret;
 111
 112        parm = nla_data(tb[TCA_CONNMARK_PARMS]);
 113
 114        if (!tcf_hash_check(parm->index, a, bind)) {
 115                ret = tcf_hash_create(parm->index, est, a, sizeof(*ci),
 116                                      bind, false);
 117                if (ret)
 118                        return ret;
 119
 120                ci = to_connmark(a);
 121                ci->tcf_action = parm->action;
 122                ci->net = net;
 123                ci->zone = parm->zone;
 124
 125                tcf_hash_insert(a);
 126                ret = ACT_P_CREATED;
 127        } else {
 128                ci = to_connmark(a);
 129                if (bind)
 130                        return 0;
 131                tcf_hash_release(a, bind);
 132                if (!ovr)
 133                        return -EEXIST;
 134                /* replacing action and zone */
 135                ci->tcf_action = parm->action;
 136                ci->zone = parm->zone;
 137        }
 138
 139        return ret;
 140}
 141
 142static inline int tcf_connmark_dump(struct sk_buff *skb, struct tc_action *a,
 143                                    int bind, int ref)
 144{
 145        unsigned char *b = skb_tail_pointer(skb);
 146        struct tcf_connmark_info *ci = a->priv;
 147
 148        struct tc_connmark opt = {
 149                .index   = ci->tcf_index,
 150                .refcnt  = ci->tcf_refcnt - ref,
 151                .bindcnt = ci->tcf_bindcnt - bind,
 152                .action  = ci->tcf_action,
 153                .zone   = ci->zone,
 154        };
 155        struct tcf_t t;
 156
 157        if (nla_put(skb, TCA_CONNMARK_PARMS, sizeof(opt), &opt))
 158                goto nla_put_failure;
 159
 160        t.install = jiffies_to_clock_t(jiffies - ci->tcf_tm.install);
 161        t.lastuse = jiffies_to_clock_t(jiffies - ci->tcf_tm.lastuse);
 162        t.expires = jiffies_to_clock_t(ci->tcf_tm.expires);
 163        if (nla_put(skb, TCA_CONNMARK_TM, sizeof(t), &t))
 164                goto nla_put_failure;
 165
 166        return skb->len;
 167nla_put_failure:
 168        nlmsg_trim(skb, b);
 169        return -1;
 170}
 171
 172static struct tc_action_ops act_connmark_ops = {
 173        .kind           =       "connmark",
 174        .type           =       TCA_ACT_CONNMARK,
 175        .owner          =       THIS_MODULE,
 176        .act            =       tcf_connmark,
 177        .dump           =       tcf_connmark_dump,
 178        .init           =       tcf_connmark_init,
 179};
 180
 181static int __init connmark_init_module(void)
 182{
 183        return tcf_register_action(&act_connmark_ops, CONNMARK_TAB_MASK);
 184}
 185
 186static void __exit connmark_cleanup_module(void)
 187{
 188        tcf_unregister_action(&act_connmark_ops);
 189}
 190
 191module_init(connmark_init_module);
 192module_exit(connmark_cleanup_module);
 193MODULE_AUTHOR("Felix Fietkau <nbd@openwrt.org>");
 194MODULE_DESCRIPTION("Connection tracking mark restoring");
 195MODULE_LICENSE("GPL");
 196
 197