1/*
2 * net/sched/simp.c	Simple example of an action
3 *
4 *		This program is free software; you can redistribute it and/or
5 *		modify it under the terms of the GNU General Public License
6 *		as published by the Free Software Foundation; either version
7 *		2 of the License, or (at your option) any later version.
8 *
9 * Authors:	Jamal Hadi Salim (2005)
10 *
11 */
12
13#include <linux/module.h>
14#include <linux/init.h>
15#include <linux/kernel.h>
16#include <linux/netdevice.h>
17#include <linux/skbuff.h>
18#include <linux/rtnetlink.h>
19#include <net/netlink.h>
20#include <net/pkt_sched.h>
21
22#define TCA_ACT_SIMP 22
23
24#include <linux/tc_act/tc_defact.h>
25#include <net/tc_act/tc_defact.h>
26
27#define SIMP_TAB_MASK     7
28static struct tcf_common *tcf_simp_ht[SIMP_TAB_MASK + 1];
29static u32 simp_idx_gen;
30static DEFINE_RWLOCK(simp_lock);
31
32static struct tcf_hashinfo simp_hash_info = {
33	.htab	=	tcf_simp_ht,
34	.hmask	=	SIMP_TAB_MASK,
35	.lock	=	&simp_lock,
36};
37
38static int tcf_simp(struct sk_buff *skb, struct tc_action *a, struct tcf_result *res)
39{
40	struct tcf_defact *d = a->priv;
41
42	spin_lock(&d->tcf_lock);
43	d->tcf_tm.lastuse = jiffies;
44	d->tcf_bstats.bytes += skb->len;
45	d->tcf_bstats.packets++;
46
47	/* print policy string followed by _ then packet count
48	 * Example if this was the 3rd packet and the string was "hello"
49	 * then it would look like "hello_3" (without quotes)
50	 **/
51	printk("simple: %s_%d\n",
52	       (char *)d->tcfd_defdata, d->tcf_bstats.packets);
53	spin_unlock(&d->tcf_lock);
54	return d->tcf_action;
55}
56
57static int tcf_simp_release(struct tcf_defact *d, int bind)
58{
59	int ret = 0;
60	if (d) {
61		if (bind)
62			d->tcf_bindcnt--;
63		d->tcf_refcnt--;
64		if (d->tcf_bindcnt <= 0 && d->tcf_refcnt <= 0) {
65			kfree(d->tcfd_defdata);
66			tcf_hash_destroy(&d->common, &simp_hash_info);
67			ret = 1;
68		}
69	}
70	return ret;
71}
72
73static int alloc_defdata(struct tcf_defact *d, u32 datalen, void *defdata)
74{
75	d->tcfd_defdata = kmemdup(defdata, datalen, GFP_KERNEL);
76	if (unlikely(!d->tcfd_defdata))
77		return -ENOMEM;
78	d->tcfd_datalen = datalen;
79	return 0;
80}
81
82static int realloc_defdata(struct tcf_defact *d, u32 datalen, void *defdata)
83{
84	kfree(d->tcfd_defdata);
85	return alloc_defdata(d, datalen, defdata);
86}
87
88static int tcf_simp_init(struct rtattr *rta, struct rtattr *est,
89			 struct tc_action *a, int ovr, int bind)
90{
91	struct rtattr *tb[TCA_DEF_MAX];
92	struct tc_defact *parm;
93	struct tcf_defact *d;
94	struct tcf_common *pc;
95	void *defdata;
96	u32 datalen = 0;
97	int ret = 0;
98
99	if (rta == NULL || rtattr_parse_nested(tb, TCA_DEF_MAX, rta) < 0)
100		return -EINVAL;
101
102	if (tb[TCA_DEF_PARMS - 1] == NULL ||
103	    RTA_PAYLOAD(tb[TCA_DEF_PARMS - 1]) < sizeof(*parm))
104		return -EINVAL;
105
106	parm = RTA_DATA(tb[TCA_DEF_PARMS - 1]);
107	defdata = RTA_DATA(tb[TCA_DEF_DATA - 1]);
108	if (defdata == NULL)
109		return -EINVAL;
110
111	datalen = RTA_PAYLOAD(tb[TCA_DEF_DATA - 1]);
112	if (datalen <= 0)
113		return -EINVAL;
114
115	pc = tcf_hash_check(parm->index, a, bind, &simp_hash_info);
116	if (!pc) {
117		pc = tcf_hash_create(parm->index, est, a, sizeof(*d), bind,
118				     &simp_idx_gen, &simp_hash_info);
119		if (unlikely(!pc))
120			return -ENOMEM;
121
122		d = to_defact(pc);
123		ret = alloc_defdata(d, datalen, defdata);
124		if (ret < 0) {
125			kfree(pc);
126			return ret;
127		}
128		ret = ACT_P_CREATED;
129	} else {
130		d = to_defact(pc);
131		if (!ovr) {
132			tcf_simp_release(d, bind);
133			return -EEXIST;
134		}
135		realloc_defdata(d, datalen, defdata);
136	}
137
138	spin_lock_bh(&d->tcf_lock);
139	d->tcf_action = parm->action;
140	spin_unlock_bh(&d->tcf_lock);
141
142	if (ret == ACT_P_CREATED)
143		tcf_hash_insert(pc, &simp_hash_info);
144	return ret;
145}
146
147static inline int tcf_simp_cleanup(struct tc_action *a, int bind)
148{
149	struct tcf_defact *d = a->priv;
150
151	if (d)
152		return tcf_simp_release(d, bind);
153	return 0;
154}
155
156static inline int tcf_simp_dump(struct sk_buff *skb, struct tc_action *a,
157				int bind, int ref)
158{
159	unsigned char *b = skb_tail_pointer(skb);
160	struct tcf_defact *d = a->priv;
161	struct tc_defact opt;
162	struct tcf_t t;
163
164	opt.index = d->tcf_index;
165	opt.refcnt = d->tcf_refcnt - ref;
166	opt.bindcnt = d->tcf_bindcnt - bind;
167	opt.action = d->tcf_action;
168	RTA_PUT(skb, TCA_DEF_PARMS, sizeof(opt), &opt);
169	RTA_PUT(skb, TCA_DEF_DATA, d->tcfd_datalen, d->tcfd_defdata);
170	t.install = jiffies_to_clock_t(jiffies - d->tcf_tm.install);
171	t.lastuse = jiffies_to_clock_t(jiffies - d->tcf_tm.lastuse);
172	t.expires = jiffies_to_clock_t(d->tcf_tm.expires);
173	RTA_PUT(skb, TCA_DEF_TM, sizeof(t), &t);
174	return skb->len;
175
176rtattr_failure:
177	nlmsg_trim(skb, b);
178	return -1;
179}
180
181static struct tc_action_ops act_simp_ops = {
182	.kind		=	"simple",
183	.hinfo		=	&simp_hash_info,
184	.type		=	TCA_ACT_SIMP,
185	.capab		=	TCA_CAP_NONE,
186	.owner		=	THIS_MODULE,
187	.act		=	tcf_simp,
188	.dump		=	tcf_simp_dump,
189	.cleanup	=	tcf_simp_cleanup,
190	.init		=	tcf_simp_init,
191	.walk		=	tcf_generic_walker,
192};
193
194MODULE_AUTHOR("Jamal Hadi Salim(2005)");
195MODULE_DESCRIPTION("Simple example action");
196MODULE_LICENSE("GPL");
197
198static int __init simp_init_module(void)
199{
200	int ret = tcf_register_action(&act_simp_ops);
201	if (!ret)
202		printk("Simple TC action Loaded\n");
203	return ret;
204}
205
206static void __exit simp_cleanup_module(void)
207{
208	tcf_unregister_action(&act_simp_ops);
209}
210
211module_init(simp_init_module);
212module_exit(simp_cleanup_module);
213