| xj | b04a402 | 2021-11-25 15:01:52 +0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * net/sched/act_police.c	Input police filter | 
|  | 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:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> | 
|  | 10 | * 		J Hadi Salim (action changes) | 
|  | 11 | */ | 
|  | 12 |  | 
|  | 13 | #include <linux/module.h> | 
|  | 14 | #include <linux/types.h> | 
|  | 15 | #include <linux/kernel.h> | 
|  | 16 | #include <linux/string.h> | 
|  | 17 | #include <linux/errno.h> | 
|  | 18 | #include <linux/skbuff.h> | 
|  | 19 | #include <linux/rtnetlink.h> | 
|  | 20 | #include <linux/init.h> | 
|  | 21 | #include <linux/slab.h> | 
|  | 22 | #include <net/act_api.h> | 
|  | 23 | #include <net/netlink.h> | 
|  | 24 |  | 
|  | 25 | struct tcf_police { | 
|  | 26 | struct tc_action	common; | 
|  | 27 | int			tcfp_result; | 
|  | 28 | u32			tcfp_ewma_rate; | 
|  | 29 | s64			tcfp_burst; | 
|  | 30 | u32			tcfp_mtu; | 
|  | 31 | s64			tcfp_toks; | 
|  | 32 | s64			tcfp_ptoks; | 
|  | 33 | s64			tcfp_mtu_ptoks; | 
|  | 34 | s64			tcfp_t_c; | 
|  | 35 | struct psched_ratecfg	rate; | 
|  | 36 | bool			rate_present; | 
|  | 37 | struct psched_ratecfg	peak; | 
|  | 38 | bool			peak_present; | 
|  | 39 | }; | 
|  | 40 |  | 
|  | 41 | #define to_police(pc) ((struct tcf_police *)pc) | 
|  | 42 |  | 
|  | 43 | /* old policer structure from before tc actions */ | 
|  | 44 | struct tc_police_compat { | 
|  | 45 | u32			index; | 
|  | 46 | int			action; | 
|  | 47 | u32			limit; | 
|  | 48 | u32			burst; | 
|  | 49 | u32			mtu; | 
|  | 50 | struct tc_ratespec	rate; | 
|  | 51 | struct tc_ratespec	peakrate; | 
|  | 52 | }; | 
|  | 53 |  | 
|  | 54 | /* Each policer is serialized by its individual spinlock */ | 
|  | 55 |  | 
|  | 56 | static unsigned int police_net_id; | 
|  | 57 | static struct tc_action_ops act_police_ops; | 
|  | 58 |  | 
|  | 59 | static int tcf_police_walker(struct net *net, struct sk_buff *skb, | 
|  | 60 | struct netlink_callback *cb, int type, | 
|  | 61 | const struct tc_action_ops *ops, | 
|  | 62 | struct netlink_ext_ack *extack) | 
|  | 63 | { | 
|  | 64 | struct tc_action_net *tn = net_generic(net, police_net_id); | 
|  | 65 |  | 
|  | 66 | return tcf_generic_walker(tn, skb, cb, type, ops, extack); | 
|  | 67 | } | 
|  | 68 |  | 
|  | 69 | static const struct nla_policy police_policy[TCA_POLICE_MAX + 1] = { | 
|  | 70 | [TCA_POLICE_RATE]	= { .len = TC_RTAB_SIZE }, | 
|  | 71 | [TCA_POLICE_PEAKRATE]	= { .len = TC_RTAB_SIZE }, | 
|  | 72 | [TCA_POLICE_AVRATE]	= { .type = NLA_U32 }, | 
|  | 73 | [TCA_POLICE_RESULT]	= { .type = NLA_U32 }, | 
|  | 74 | }; | 
|  | 75 |  | 
|  | 76 | static int tcf_police_init(struct net *net, struct nlattr *nla, | 
|  | 77 | struct nlattr *est, struct tc_action **a, | 
|  | 78 | int ovr, int bind, bool rtnl_held, | 
|  | 79 | struct netlink_ext_ack *extack) | 
|  | 80 | { | 
|  | 81 | int ret = 0, err; | 
|  | 82 | struct nlattr *tb[TCA_POLICE_MAX + 1]; | 
|  | 83 | struct tc_police *parm; | 
|  | 84 | struct tcf_police *police; | 
|  | 85 | struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL; | 
|  | 86 | struct tc_action_net *tn = net_generic(net, police_net_id); | 
|  | 87 | bool exists = false; | 
|  | 88 | u32 index; | 
|  | 89 | int size; | 
|  | 90 |  | 
|  | 91 | if (nla == NULL) | 
|  | 92 | return -EINVAL; | 
|  | 93 |  | 
|  | 94 | err = nla_parse_nested(tb, TCA_POLICE_MAX, nla, police_policy, NULL); | 
|  | 95 | if (err < 0) | 
|  | 96 | return err; | 
|  | 97 |  | 
|  | 98 | if (tb[TCA_POLICE_TBF] == NULL) | 
|  | 99 | return -EINVAL; | 
|  | 100 | size = nla_len(tb[TCA_POLICE_TBF]); | 
|  | 101 | if (size != sizeof(*parm) && size != sizeof(struct tc_police_compat)) | 
|  | 102 | return -EINVAL; | 
|  | 103 |  | 
|  | 104 | parm = nla_data(tb[TCA_POLICE_TBF]); | 
|  | 105 | index = parm->index; | 
|  | 106 | err = tcf_idr_check_alloc(tn, &index, a, bind); | 
|  | 107 | if (err < 0) | 
|  | 108 | return err; | 
|  | 109 | exists = err; | 
|  | 110 | if (exists && bind) | 
|  | 111 | return 0; | 
|  | 112 |  | 
|  | 113 | if (!exists) { | 
|  | 114 | ret = tcf_idr_create(tn, index, NULL, a, | 
|  | 115 | &act_police_ops, bind, false); | 
|  | 116 | if (ret) { | 
|  | 117 | tcf_idr_cleanup(tn, index); | 
|  | 118 | return ret; | 
|  | 119 | } | 
|  | 120 | ret = ACT_P_CREATED; | 
|  | 121 | } else if (!ovr) { | 
|  | 122 | tcf_idr_release(*a, bind); | 
|  | 123 | return -EEXIST; | 
|  | 124 | } | 
|  | 125 |  | 
|  | 126 | police = to_police(*a); | 
|  | 127 | if (parm->rate.rate) { | 
|  | 128 | err = -ENOMEM; | 
|  | 129 | R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE], NULL); | 
|  | 130 | if (R_tab == NULL) | 
|  | 131 | goto failure; | 
|  | 132 |  | 
|  | 133 | if (parm->peakrate.rate) { | 
|  | 134 | P_tab = qdisc_get_rtab(&parm->peakrate, | 
|  | 135 | tb[TCA_POLICE_PEAKRATE], NULL); | 
|  | 136 | if (P_tab == NULL) | 
|  | 137 | goto failure; | 
|  | 138 | } | 
|  | 139 | } | 
|  | 140 |  | 
|  | 141 | if (est) { | 
|  | 142 | err = gen_replace_estimator(&police->tcf_bstats, NULL, | 
|  | 143 | &police->tcf_rate_est, | 
|  | 144 | &police->tcf_lock, | 
|  | 145 | NULL, est); | 
|  | 146 | if (err) | 
|  | 147 | goto failure; | 
|  | 148 | } else if (tb[TCA_POLICE_AVRATE] && | 
|  | 149 | (ret == ACT_P_CREATED || | 
|  | 150 | !gen_estimator_active(&police->tcf_rate_est))) { | 
|  | 151 | err = -EINVAL; | 
|  | 152 | goto failure; | 
|  | 153 | } | 
|  | 154 |  | 
|  | 155 | spin_lock_bh(&police->tcf_lock); | 
|  | 156 | /* No failure allowed after this point */ | 
|  | 157 | police->tcfp_mtu = parm->mtu; | 
|  | 158 | if (police->tcfp_mtu == 0) { | 
|  | 159 | police->tcfp_mtu = ~0; | 
|  | 160 | if (R_tab) | 
|  | 161 | police->tcfp_mtu = 255 << R_tab->rate.cell_log; | 
|  | 162 | } | 
|  | 163 | if (R_tab) { | 
|  | 164 | police->rate_present = true; | 
|  | 165 | psched_ratecfg_precompute(&police->rate, &R_tab->rate, 0); | 
|  | 166 | qdisc_put_rtab(R_tab); | 
|  | 167 | } else { | 
|  | 168 | police->rate_present = false; | 
|  | 169 | } | 
|  | 170 | if (P_tab) { | 
|  | 171 | police->peak_present = true; | 
|  | 172 | psched_ratecfg_precompute(&police->peak, &P_tab->rate, 0); | 
|  | 173 | qdisc_put_rtab(P_tab); | 
|  | 174 | } else { | 
|  | 175 | police->peak_present = false; | 
|  | 176 | } | 
|  | 177 |  | 
|  | 178 | if (tb[TCA_POLICE_RESULT]) | 
|  | 179 | police->tcfp_result = nla_get_u32(tb[TCA_POLICE_RESULT]); | 
|  | 180 | police->tcfp_burst = PSCHED_TICKS2NS(parm->burst); | 
|  | 181 | police->tcfp_toks = police->tcfp_burst; | 
|  | 182 | if (police->peak_present) { | 
|  | 183 | police->tcfp_mtu_ptoks = (s64) psched_l2t_ns(&police->peak, | 
|  | 184 | police->tcfp_mtu); | 
|  | 185 | police->tcfp_ptoks = police->tcfp_mtu_ptoks; | 
|  | 186 | } | 
|  | 187 | police->tcf_action = parm->action; | 
|  | 188 |  | 
|  | 189 | if (tb[TCA_POLICE_AVRATE]) | 
|  | 190 | police->tcfp_ewma_rate = nla_get_u32(tb[TCA_POLICE_AVRATE]); | 
|  | 191 |  | 
|  | 192 | spin_unlock_bh(&police->tcf_lock); | 
|  | 193 | if (ret != ACT_P_CREATED) | 
|  | 194 | return ret; | 
|  | 195 |  | 
|  | 196 | police->tcfp_t_c = ktime_get_ns(); | 
|  | 197 | tcf_idr_insert(tn, *a); | 
|  | 198 |  | 
|  | 199 | return ret; | 
|  | 200 |  | 
|  | 201 | failure: | 
|  | 202 | qdisc_put_rtab(P_tab); | 
|  | 203 | qdisc_put_rtab(R_tab); | 
|  | 204 | tcf_idr_release(*a, bind); | 
|  | 205 | return err; | 
|  | 206 | } | 
|  | 207 |  | 
|  | 208 | static int tcf_police_act(struct sk_buff *skb, const struct tc_action *a, | 
|  | 209 | struct tcf_result *res) | 
|  | 210 | { | 
|  | 211 | struct tcf_police *police = to_police(a); | 
|  | 212 | s64 now; | 
|  | 213 | s64 toks; | 
|  | 214 | s64 ptoks = 0; | 
|  | 215 |  | 
|  | 216 | spin_lock(&police->tcf_lock); | 
|  | 217 |  | 
|  | 218 | bstats_update(&police->tcf_bstats, skb); | 
|  | 219 | tcf_lastuse_update(&police->tcf_tm); | 
|  | 220 |  | 
|  | 221 | if (police->tcfp_ewma_rate) { | 
|  | 222 | struct gnet_stats_rate_est64 sample; | 
|  | 223 |  | 
|  | 224 | if (!gen_estimator_read(&police->tcf_rate_est, &sample) || | 
|  | 225 | sample.bps >= police->tcfp_ewma_rate) { | 
|  | 226 | police->tcf_qstats.overlimits++; | 
|  | 227 | if (police->tcf_action == TC_ACT_SHOT) | 
|  | 228 | police->tcf_qstats.drops++; | 
|  | 229 | spin_unlock(&police->tcf_lock); | 
|  | 230 | return police->tcf_action; | 
|  | 231 | } | 
|  | 232 | } | 
|  | 233 |  | 
|  | 234 | if (qdisc_pkt_len(skb) <= police->tcfp_mtu) { | 
|  | 235 | if (!police->rate_present) { | 
|  | 236 | spin_unlock(&police->tcf_lock); | 
|  | 237 | return police->tcfp_result; | 
|  | 238 | } | 
|  | 239 |  | 
|  | 240 | now = ktime_get_ns(); | 
|  | 241 | toks = min_t(s64, now - police->tcfp_t_c, | 
|  | 242 | police->tcfp_burst); | 
|  | 243 | if (police->peak_present) { | 
|  | 244 | ptoks = toks + police->tcfp_ptoks; | 
|  | 245 | if (ptoks > police->tcfp_mtu_ptoks) | 
|  | 246 | ptoks = police->tcfp_mtu_ptoks; | 
|  | 247 | ptoks -= (s64) psched_l2t_ns(&police->peak, | 
|  | 248 | qdisc_pkt_len(skb)); | 
|  | 249 | } | 
|  | 250 | toks += police->tcfp_toks; | 
|  | 251 | if (toks > police->tcfp_burst) | 
|  | 252 | toks = police->tcfp_burst; | 
|  | 253 | toks -= (s64) psched_l2t_ns(&police->rate, qdisc_pkt_len(skb)); | 
|  | 254 | if ((toks|ptoks) >= 0) { | 
|  | 255 | police->tcfp_t_c = now; | 
|  | 256 | police->tcfp_toks = toks; | 
|  | 257 | police->tcfp_ptoks = ptoks; | 
|  | 258 | if (police->tcfp_result == TC_ACT_SHOT) | 
|  | 259 | police->tcf_qstats.drops++; | 
|  | 260 | spin_unlock(&police->tcf_lock); | 
|  | 261 | return police->tcfp_result; | 
|  | 262 | } | 
|  | 263 | } | 
|  | 264 |  | 
|  | 265 | police->tcf_qstats.overlimits++; | 
|  | 266 | if (police->tcf_action == TC_ACT_SHOT) | 
|  | 267 | police->tcf_qstats.drops++; | 
|  | 268 | spin_unlock(&police->tcf_lock); | 
|  | 269 | return police->tcf_action; | 
|  | 270 | } | 
|  | 271 |  | 
|  | 272 | static int tcf_police_dump(struct sk_buff *skb, struct tc_action *a, | 
|  | 273 | int bind, int ref) | 
|  | 274 | { | 
|  | 275 | unsigned char *b = skb_tail_pointer(skb); | 
|  | 276 | struct tcf_police *police = to_police(a); | 
|  | 277 | struct tc_police opt = { | 
|  | 278 | .index = police->tcf_index, | 
|  | 279 | .refcnt = refcount_read(&police->tcf_refcnt) - ref, | 
|  | 280 | .bindcnt = atomic_read(&police->tcf_bindcnt) - bind, | 
|  | 281 | }; | 
|  | 282 | struct tcf_t t; | 
|  | 283 |  | 
|  | 284 | spin_lock_bh(&police->tcf_lock); | 
|  | 285 | opt.action = police->tcf_action; | 
|  | 286 | opt.mtu = police->tcfp_mtu; | 
|  | 287 | opt.burst = PSCHED_NS2TICKS(police->tcfp_burst); | 
|  | 288 | if (police->rate_present) | 
|  | 289 | psched_ratecfg_getrate(&opt.rate, &police->rate); | 
|  | 290 | if (police->peak_present) | 
|  | 291 | psched_ratecfg_getrate(&opt.peakrate, &police->peak); | 
|  | 292 | if (nla_put(skb, TCA_POLICE_TBF, sizeof(opt), &opt)) | 
|  | 293 | goto nla_put_failure; | 
|  | 294 | if (police->tcfp_result && | 
|  | 295 | nla_put_u32(skb, TCA_POLICE_RESULT, police->tcfp_result)) | 
|  | 296 | goto nla_put_failure; | 
|  | 297 | if (police->tcfp_ewma_rate && | 
|  | 298 | nla_put_u32(skb, TCA_POLICE_AVRATE, police->tcfp_ewma_rate)) | 
|  | 299 | goto nla_put_failure; | 
|  | 300 |  | 
|  | 301 | t.install = jiffies_to_clock_t(jiffies - police->tcf_tm.install); | 
|  | 302 | t.lastuse = jiffies_to_clock_t(jiffies - police->tcf_tm.lastuse); | 
|  | 303 | t.firstuse = jiffies_to_clock_t(jiffies - police->tcf_tm.firstuse); | 
|  | 304 | t.expires = jiffies_to_clock_t(police->tcf_tm.expires); | 
|  | 305 | if (nla_put_64bit(skb, TCA_POLICE_TM, sizeof(t), &t, TCA_POLICE_PAD)) | 
|  | 306 | goto nla_put_failure; | 
|  | 307 | spin_unlock_bh(&police->tcf_lock); | 
|  | 308 |  | 
|  | 309 | return skb->len; | 
|  | 310 |  | 
|  | 311 | nla_put_failure: | 
|  | 312 | spin_unlock_bh(&police->tcf_lock); | 
|  | 313 | nlmsg_trim(skb, b); | 
|  | 314 | return -1; | 
|  | 315 | } | 
|  | 316 |  | 
|  | 317 | static int tcf_police_search(struct net *net, struct tc_action **a, u32 index, | 
|  | 318 | struct netlink_ext_ack *extack) | 
|  | 319 | { | 
|  | 320 | struct tc_action_net *tn = net_generic(net, police_net_id); | 
|  | 321 |  | 
|  | 322 | return tcf_idr_search(tn, a, index); | 
|  | 323 | } | 
|  | 324 |  | 
|  | 325 | MODULE_AUTHOR("Alexey Kuznetsov"); | 
|  | 326 | MODULE_DESCRIPTION("Policing actions"); | 
|  | 327 | MODULE_LICENSE("GPL"); | 
|  | 328 |  | 
|  | 329 | static struct tc_action_ops act_police_ops = { | 
|  | 330 | .kind		=	"police", | 
|  | 331 | .type		=	TCA_ID_POLICE, | 
|  | 332 | .owner		=	THIS_MODULE, | 
|  | 333 | .act		=	tcf_police_act, | 
|  | 334 | .dump		=	tcf_police_dump, | 
|  | 335 | .init		=	tcf_police_init, | 
|  | 336 | .walk		=	tcf_police_walker, | 
|  | 337 | .lookup		=	tcf_police_search, | 
|  | 338 | .size		=	sizeof(struct tcf_police), | 
|  | 339 | }; | 
|  | 340 |  | 
|  | 341 | static __net_init int police_init_net(struct net *net) | 
|  | 342 | { | 
|  | 343 | struct tc_action_net *tn = net_generic(net, police_net_id); | 
|  | 344 |  | 
|  | 345 | return tc_action_net_init(net, tn, &act_police_ops); | 
|  | 346 | } | 
|  | 347 |  | 
|  | 348 | static void __net_exit police_exit_net(struct list_head *net_list) | 
|  | 349 | { | 
|  | 350 | tc_action_net_exit(net_list, police_net_id); | 
|  | 351 | } | 
|  | 352 |  | 
|  | 353 | static struct pernet_operations police_net_ops = { | 
|  | 354 | .init = police_init_net, | 
|  | 355 | .exit_batch = police_exit_net, | 
|  | 356 | .id   = &police_net_id, | 
|  | 357 | .size = sizeof(struct tc_action_net), | 
|  | 358 | }; | 
|  | 359 |  | 
|  | 360 | static int __init police_init_module(void) | 
|  | 361 | { | 
|  | 362 | return tcf_register_action(&act_police_ops, &police_net_ops); | 
|  | 363 | } | 
|  | 364 |  | 
|  | 365 | static void __exit police_cleanup_module(void) | 
|  | 366 | { | 
|  | 367 | tcf_unregister_action(&act_police_ops, &police_net_ops); | 
|  | 368 | } | 
|  | 369 |  | 
|  | 370 | module_init(police_init_module); | 
|  | 371 | module_exit(police_cleanup_module); |