14bba3925SPatrick McHardy /* 24bba3925SPatrick McHardy * net/sched/police.c Input police filter. 34bba3925SPatrick McHardy * 44bba3925SPatrick McHardy * This program is free software; you can redistribute it and/or 54bba3925SPatrick McHardy * modify it under the terms of the GNU General Public License 64bba3925SPatrick McHardy * as published by the Free Software Foundation; either version 74bba3925SPatrick McHardy * 2 of the License, or (at your option) any later version. 84bba3925SPatrick McHardy * 94bba3925SPatrick McHardy * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> 104bba3925SPatrick McHardy * J Hadi Salim (action changes) 114bba3925SPatrick McHardy */ 124bba3925SPatrick McHardy 134bba3925SPatrick McHardy #include <asm/uaccess.h> 144bba3925SPatrick McHardy #include <asm/system.h> 154bba3925SPatrick McHardy #include <linux/bitops.h> 164bba3925SPatrick McHardy #include <linux/module.h> 174bba3925SPatrick McHardy #include <linux/types.h> 184bba3925SPatrick McHardy #include <linux/kernel.h> 194bba3925SPatrick McHardy #include <linux/sched.h> 204bba3925SPatrick McHardy #include <linux/string.h> 214bba3925SPatrick McHardy #include <linux/mm.h> 224bba3925SPatrick McHardy #include <linux/socket.h> 234bba3925SPatrick McHardy #include <linux/sockios.h> 244bba3925SPatrick McHardy #include <linux/in.h> 254bba3925SPatrick McHardy #include <linux/errno.h> 264bba3925SPatrick McHardy #include <linux/interrupt.h> 274bba3925SPatrick McHardy #include <linux/netdevice.h> 284bba3925SPatrick McHardy #include <linux/skbuff.h> 294bba3925SPatrick McHardy #include <linux/module.h> 304bba3925SPatrick McHardy #include <linux/rtnetlink.h> 314bba3925SPatrick McHardy #include <linux/init.h> 324bba3925SPatrick McHardy #include <net/sock.h> 334bba3925SPatrick McHardy #include <net/act_api.h> 344bba3925SPatrick McHardy 35*e9ce1cd3SDavid S. Miller #define L2T(p,L) ((p)->tcfp_R_tab->data[(L)>>(p)->tcfp_R_tab->rate.cell_log]) 36*e9ce1cd3SDavid S. Miller #define L2T_P(p,L) ((p)->tcfp_P_tab->data[(L)>>(p)->tcfp_P_tab->rate.cell_log]) 374bba3925SPatrick McHardy 38*e9ce1cd3SDavid S. Miller #define POL_TAB_MASK 15 39*e9ce1cd3SDavid S. Miller static struct tcf_common *tcf_police_ht[POL_TAB_MASK + 1]; 40*e9ce1cd3SDavid S. Miller static u32 police_idx_gen; 414bba3925SPatrick McHardy static DEFINE_RWLOCK(police_lock); 424bba3925SPatrick McHardy 43*e9ce1cd3SDavid S. Miller static struct tcf_hashinfo police_hash_info = { 44*e9ce1cd3SDavid S. Miller .htab = tcf_police_ht, 45*e9ce1cd3SDavid S. Miller .hmask = POL_TAB_MASK, 46*e9ce1cd3SDavid S. Miller .lock = &police_lock, 47*e9ce1cd3SDavid S. Miller }; 48*e9ce1cd3SDavid S. Miller 494bba3925SPatrick McHardy /* Each policer is serialized by its individual spinlock */ 504bba3925SPatrick McHardy 514bba3925SPatrick McHardy #ifdef CONFIG_NET_CLS_ACT 5283b950c8SJamal Hadi Salim static int tcf_act_police_walker(struct sk_buff *skb, struct netlink_callback *cb, 534bba3925SPatrick McHardy int type, struct tc_action *a) 544bba3925SPatrick McHardy { 55*e9ce1cd3SDavid S. Miller struct tcf_common *p; 564bba3925SPatrick McHardy int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; 574bba3925SPatrick McHardy struct rtattr *r; 584bba3925SPatrick McHardy 594bba3925SPatrick McHardy read_lock(&police_lock); 604bba3925SPatrick McHardy 614bba3925SPatrick McHardy s_i = cb->args[0]; 624bba3925SPatrick McHardy 63*e9ce1cd3SDavid S. Miller for (i = 0; i < (POL_TAB_MASK + 1); i++) { 64*e9ce1cd3SDavid S. Miller p = tcf_police_ht[tcf_hash(i, POL_TAB_MASK)]; 654bba3925SPatrick McHardy 66*e9ce1cd3SDavid S. Miller for (; p; p = p->tcfc_next) { 674bba3925SPatrick McHardy index++; 684bba3925SPatrick McHardy if (index < s_i) 694bba3925SPatrick McHardy continue; 704bba3925SPatrick McHardy a->priv = p; 714bba3925SPatrick McHardy a->order = index; 724bba3925SPatrick McHardy r = (struct rtattr*) skb->tail; 734bba3925SPatrick McHardy RTA_PUT(skb, a->order, 0, NULL); 744bba3925SPatrick McHardy if (type == RTM_DELACTION) 754bba3925SPatrick McHardy err = tcf_action_dump_1(skb, a, 0, 1); 764bba3925SPatrick McHardy else 774bba3925SPatrick McHardy err = tcf_action_dump_1(skb, a, 0, 0); 784bba3925SPatrick McHardy if (err < 0) { 794bba3925SPatrick McHardy index--; 804bba3925SPatrick McHardy skb_trim(skb, (u8*)r - skb->data); 814bba3925SPatrick McHardy goto done; 824bba3925SPatrick McHardy } 834bba3925SPatrick McHardy r->rta_len = skb->tail - (u8*)r; 844bba3925SPatrick McHardy n_i++; 854bba3925SPatrick McHardy } 864bba3925SPatrick McHardy } 874bba3925SPatrick McHardy done: 884bba3925SPatrick McHardy read_unlock(&police_lock); 894bba3925SPatrick McHardy if (n_i) 904bba3925SPatrick McHardy cb->args[0] += n_i; 914bba3925SPatrick McHardy return n_i; 924bba3925SPatrick McHardy 934bba3925SPatrick McHardy rtattr_failure: 944bba3925SPatrick McHardy skb_trim(skb, (u8*)r - skb->data); 954bba3925SPatrick McHardy goto done; 964bba3925SPatrick McHardy } 974bba3925SPatrick McHardy #endif 984bba3925SPatrick McHardy 994bba3925SPatrick McHardy void tcf_police_destroy(struct tcf_police *p) 1004bba3925SPatrick McHardy { 101*e9ce1cd3SDavid S. Miller unsigned int h = tcf_hash(p->tcf_index, POL_TAB_MASK); 102*e9ce1cd3SDavid S. Miller struct tcf_common **p1p; 1034bba3925SPatrick McHardy 104*e9ce1cd3SDavid S. Miller for (p1p = &tcf_police_ht[h]; *p1p; p1p = &(*p1p)->tcfc_next) { 105*e9ce1cd3SDavid S. Miller if (*p1p == &p->common) { 1064bba3925SPatrick McHardy write_lock_bh(&police_lock); 107*e9ce1cd3SDavid S. Miller *p1p = p->tcf_next; 1084bba3925SPatrick McHardy write_unlock_bh(&police_lock); 1094bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 110*e9ce1cd3SDavid S. Miller gen_kill_estimator(&p->tcf_bstats, 111*e9ce1cd3SDavid S. Miller &p->tcf_rate_est); 1124bba3925SPatrick McHardy #endif 113*e9ce1cd3SDavid S. Miller if (p->tcfp_R_tab) 114*e9ce1cd3SDavid S. Miller qdisc_put_rtab(p->tcfp_R_tab); 115*e9ce1cd3SDavid S. Miller if (p->tcfp_P_tab) 116*e9ce1cd3SDavid S. Miller qdisc_put_rtab(p->tcfp_P_tab); 1174bba3925SPatrick McHardy kfree(p); 1184bba3925SPatrick McHardy return; 1194bba3925SPatrick McHardy } 1204bba3925SPatrick McHardy } 1214bba3925SPatrick McHardy BUG_TRAP(0); 1224bba3925SPatrick McHardy } 1234bba3925SPatrick McHardy 1244bba3925SPatrick McHardy #ifdef CONFIG_NET_CLS_ACT 1254bba3925SPatrick McHardy static int tcf_act_police_locate(struct rtattr *rta, struct rtattr *est, 1264bba3925SPatrick McHardy struct tc_action *a, int ovr, int bind) 1274bba3925SPatrick McHardy { 1284bba3925SPatrick McHardy unsigned h; 1294bba3925SPatrick McHardy int ret = 0, err; 1304bba3925SPatrick McHardy struct rtattr *tb[TCA_POLICE_MAX]; 1314bba3925SPatrick McHardy struct tc_police *parm; 132*e9ce1cd3SDavid S. Miller struct tcf_police *police; 1334bba3925SPatrick McHardy struct qdisc_rate_table *R_tab = NULL, *P_tab = NULL; 1344bba3925SPatrick McHardy 1354bba3925SPatrick McHardy if (rta == NULL || rtattr_parse_nested(tb, TCA_POLICE_MAX, rta) < 0) 1364bba3925SPatrick McHardy return -EINVAL; 1374bba3925SPatrick McHardy 1384bba3925SPatrick McHardy if (tb[TCA_POLICE_TBF-1] == NULL || 1394bba3925SPatrick McHardy RTA_PAYLOAD(tb[TCA_POLICE_TBF-1]) != sizeof(*parm)) 1404bba3925SPatrick McHardy return -EINVAL; 1414bba3925SPatrick McHardy parm = RTA_DATA(tb[TCA_POLICE_TBF-1]); 1424bba3925SPatrick McHardy 1434bba3925SPatrick McHardy if (tb[TCA_POLICE_RESULT-1] != NULL && 1444bba3925SPatrick McHardy RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32)) 1454bba3925SPatrick McHardy return -EINVAL; 1464bba3925SPatrick McHardy if (tb[TCA_POLICE_RESULT-1] != NULL && 1474bba3925SPatrick McHardy RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32)) 1484bba3925SPatrick McHardy return -EINVAL; 1494bba3925SPatrick McHardy 150*e9ce1cd3SDavid S. Miller if (parm->index) { 151*e9ce1cd3SDavid S. Miller struct tcf_common *pc; 152*e9ce1cd3SDavid S. Miller 153*e9ce1cd3SDavid S. Miller pc = tcf_hash_lookup(parm->index, &police_hash_info); 154*e9ce1cd3SDavid S. Miller if (pc != NULL) { 155*e9ce1cd3SDavid S. Miller a->priv = pc; 156*e9ce1cd3SDavid S. Miller police = to_police(pc); 1574bba3925SPatrick McHardy if (bind) { 158*e9ce1cd3SDavid S. Miller police->tcf_bindcnt += 1; 159*e9ce1cd3SDavid S. Miller police->tcf_refcnt += 1; 1604bba3925SPatrick McHardy } 1614bba3925SPatrick McHardy if (ovr) 1624bba3925SPatrick McHardy goto override; 1634bba3925SPatrick McHardy return ret; 1644bba3925SPatrick McHardy } 165*e9ce1cd3SDavid S. Miller } 1664bba3925SPatrick McHardy 167*e9ce1cd3SDavid S. Miller police = kzalloc(sizeof(*police), GFP_KERNEL); 168*e9ce1cd3SDavid S. Miller if (police == NULL) 1694bba3925SPatrick McHardy return -ENOMEM; 1704bba3925SPatrick McHardy ret = ACT_P_CREATED; 171*e9ce1cd3SDavid S. Miller police->tcf_refcnt = 1; 172*e9ce1cd3SDavid S. Miller spin_lock_init(&police->tcf_lock); 173*e9ce1cd3SDavid S. Miller police->tcf_stats_lock = &police->tcf_lock; 1744bba3925SPatrick McHardy if (bind) 175*e9ce1cd3SDavid S. Miller police->tcf_bindcnt = 1; 1764bba3925SPatrick McHardy override: 1774bba3925SPatrick McHardy if (parm->rate.rate) { 1784bba3925SPatrick McHardy err = -ENOMEM; 1794bba3925SPatrick McHardy R_tab = qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1]); 1804bba3925SPatrick McHardy if (R_tab == NULL) 1814bba3925SPatrick McHardy goto failure; 1824bba3925SPatrick McHardy if (parm->peakrate.rate) { 1834bba3925SPatrick McHardy P_tab = qdisc_get_rtab(&parm->peakrate, 1844bba3925SPatrick McHardy tb[TCA_POLICE_PEAKRATE-1]); 185*e9ce1cd3SDavid S. Miller if (P_tab == NULL) { 1864bba3925SPatrick McHardy qdisc_put_rtab(R_tab); 1874bba3925SPatrick McHardy goto failure; 1884bba3925SPatrick McHardy } 1894bba3925SPatrick McHardy } 1904bba3925SPatrick McHardy } 1914bba3925SPatrick McHardy /* No failure allowed after this point */ 192*e9ce1cd3SDavid S. Miller spin_lock_bh(&police->tcf_lock); 1934bba3925SPatrick McHardy if (R_tab != NULL) { 194*e9ce1cd3SDavid S. Miller qdisc_put_rtab(police->tcfp_R_tab); 195*e9ce1cd3SDavid S. Miller police->tcfp_R_tab = R_tab; 1964bba3925SPatrick McHardy } 1974bba3925SPatrick McHardy if (P_tab != NULL) { 198*e9ce1cd3SDavid S. Miller qdisc_put_rtab(police->tcfp_P_tab); 199*e9ce1cd3SDavid S. Miller police->tcfp_P_tab = P_tab; 2004bba3925SPatrick McHardy } 2014bba3925SPatrick McHardy 2024bba3925SPatrick McHardy if (tb[TCA_POLICE_RESULT-1]) 203*e9ce1cd3SDavid S. Miller police->tcfp_result = *(u32*)RTA_DATA(tb[TCA_POLICE_RESULT-1]); 204*e9ce1cd3SDavid S. Miller police->tcfp_toks = police->tcfp_burst = parm->burst; 205*e9ce1cd3SDavid S. Miller police->tcfp_mtu = parm->mtu; 206*e9ce1cd3SDavid S. Miller if (police->tcfp_mtu == 0) { 207*e9ce1cd3SDavid S. Miller police->tcfp_mtu = ~0; 208*e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab) 209*e9ce1cd3SDavid S. Miller police->tcfp_mtu = 255<<police->tcfp_R_tab->rate.cell_log; 2104bba3925SPatrick McHardy } 211*e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab) 212*e9ce1cd3SDavid S. Miller police->tcfp_ptoks = L2T_P(police, police->tcfp_mtu); 213*e9ce1cd3SDavid S. Miller police->tcf_action = parm->action; 2144bba3925SPatrick McHardy 2154bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 2164bba3925SPatrick McHardy if (tb[TCA_POLICE_AVRATE-1]) 217*e9ce1cd3SDavid S. Miller police->tcfp_ewma_rate = 218*e9ce1cd3SDavid S. Miller *(u32*)RTA_DATA(tb[TCA_POLICE_AVRATE-1]); 2194bba3925SPatrick McHardy if (est) 220*e9ce1cd3SDavid S. Miller gen_replace_estimator(&police->tcf_bstats, 221*e9ce1cd3SDavid S. Miller &police->tcf_rate_est, 222*e9ce1cd3SDavid S. Miller police->tcf_stats_lock, est); 2234bba3925SPatrick McHardy #endif 2244bba3925SPatrick McHardy 225*e9ce1cd3SDavid S. Miller spin_unlock_bh(&police->tcf_lock); 2264bba3925SPatrick McHardy if (ret != ACT_P_CREATED) 2274bba3925SPatrick McHardy return ret; 2284bba3925SPatrick McHardy 229*e9ce1cd3SDavid S. Miller PSCHED_GET_TIME(police->tcfp_t_c); 230*e9ce1cd3SDavid S. Miller police->tcf_index = parm->index ? parm->index : 231*e9ce1cd3SDavid S. Miller tcf_hash_new_index(&police_idx_gen, &police_hash_info); 232*e9ce1cd3SDavid S. Miller h = tcf_hash(police->tcf_index, POL_TAB_MASK); 2334bba3925SPatrick McHardy write_lock_bh(&police_lock); 234*e9ce1cd3SDavid S. Miller police->tcf_next = tcf_police_ht[h]; 235*e9ce1cd3SDavid S. Miller tcf_police_ht[h] = &police->common; 2364bba3925SPatrick McHardy write_unlock_bh(&police_lock); 2374bba3925SPatrick McHardy 238*e9ce1cd3SDavid S. Miller a->priv = police; 2394bba3925SPatrick McHardy return ret; 2404bba3925SPatrick McHardy 2414bba3925SPatrick McHardy failure: 2424bba3925SPatrick McHardy if (ret == ACT_P_CREATED) 243*e9ce1cd3SDavid S. Miller kfree(police); 2444bba3925SPatrick McHardy return err; 2454bba3925SPatrick McHardy } 2464bba3925SPatrick McHardy 2474bba3925SPatrick McHardy static int tcf_act_police_cleanup(struct tc_action *a, int bind) 2484bba3925SPatrick McHardy { 249*e9ce1cd3SDavid S. Miller struct tcf_police *p = a->priv; 2504bba3925SPatrick McHardy 2514bba3925SPatrick McHardy if (p != NULL) 2524bba3925SPatrick McHardy return tcf_police_release(p, bind); 2534bba3925SPatrick McHardy return 0; 2544bba3925SPatrick McHardy } 2554bba3925SPatrick McHardy 2564bba3925SPatrick McHardy static int tcf_act_police(struct sk_buff *skb, struct tc_action *a, 2574bba3925SPatrick McHardy struct tcf_result *res) 2584bba3925SPatrick McHardy { 259*e9ce1cd3SDavid S. Miller struct tcf_police *police = a->priv; 2604bba3925SPatrick McHardy psched_time_t now; 2614bba3925SPatrick McHardy long toks; 2624bba3925SPatrick McHardy long ptoks = 0; 2634bba3925SPatrick McHardy 264*e9ce1cd3SDavid S. Miller spin_lock(&police->tcf_lock); 2654bba3925SPatrick McHardy 266*e9ce1cd3SDavid S. Miller police->tcf_bstats.bytes += skb->len; 267*e9ce1cd3SDavid S. Miller police->tcf_bstats.packets++; 2684bba3925SPatrick McHardy 2694bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 270*e9ce1cd3SDavid S. Miller if (police->tcfp_ewma_rate && 271*e9ce1cd3SDavid S. Miller police->tcf_rate_est.bps >= police->tcfp_ewma_rate) { 272*e9ce1cd3SDavid S. Miller police->tcf_qstats.overlimits++; 273*e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 274*e9ce1cd3SDavid S. Miller return police->tcf_action; 2754bba3925SPatrick McHardy } 2764bba3925SPatrick McHardy #endif 2774bba3925SPatrick McHardy 278*e9ce1cd3SDavid S. Miller if (skb->len <= police->tcfp_mtu) { 279*e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab == NULL) { 280*e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 281*e9ce1cd3SDavid S. Miller return police->tcfp_result; 2824bba3925SPatrick McHardy } 2834bba3925SPatrick McHardy 2844bba3925SPatrick McHardy PSCHED_GET_TIME(now); 2854bba3925SPatrick McHardy 286*e9ce1cd3SDavid S. Miller toks = PSCHED_TDIFF_SAFE(now, police->tcfp_t_c, 287*e9ce1cd3SDavid S. Miller police->tcfp_burst); 288*e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab) { 289*e9ce1cd3SDavid S. Miller ptoks = toks + police->tcfp_ptoks; 290*e9ce1cd3SDavid S. Miller if (ptoks > (long)L2T_P(police, police->tcfp_mtu)) 291*e9ce1cd3SDavid S. Miller ptoks = (long)L2T_P(police, police->tcfp_mtu); 292*e9ce1cd3SDavid S. Miller ptoks -= L2T_P(police, skb->len); 2934bba3925SPatrick McHardy } 294*e9ce1cd3SDavid S. Miller toks += police->tcfp_toks; 295*e9ce1cd3SDavid S. Miller if (toks > (long)police->tcfp_burst) 296*e9ce1cd3SDavid S. Miller toks = police->tcfp_burst; 297*e9ce1cd3SDavid S. Miller toks -= L2T(police, skb->len); 2984bba3925SPatrick McHardy if ((toks|ptoks) >= 0) { 299*e9ce1cd3SDavid S. Miller police->tcfp_t_c = now; 300*e9ce1cd3SDavid S. Miller police->tcfp_toks = toks; 301*e9ce1cd3SDavid S. Miller police->tcfp_ptoks = ptoks; 302*e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 303*e9ce1cd3SDavid S. Miller return police->tcfp_result; 3044bba3925SPatrick McHardy } 3054bba3925SPatrick McHardy } 3064bba3925SPatrick McHardy 307*e9ce1cd3SDavid S. Miller police->tcf_qstats.overlimits++; 308*e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 309*e9ce1cd3SDavid S. Miller return police->tcf_action; 3104bba3925SPatrick McHardy } 3114bba3925SPatrick McHardy 3124bba3925SPatrick McHardy static int 3134bba3925SPatrick McHardy tcf_act_police_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) 3144bba3925SPatrick McHardy { 3154bba3925SPatrick McHardy unsigned char *b = skb->tail; 316*e9ce1cd3SDavid S. Miller struct tcf_police *police = a->priv; 3174bba3925SPatrick McHardy struct tc_police opt; 3184bba3925SPatrick McHardy 319*e9ce1cd3SDavid S. Miller opt.index = police->tcf_index; 320*e9ce1cd3SDavid S. Miller opt.action = police->tcf_action; 321*e9ce1cd3SDavid S. Miller opt.mtu = police->tcfp_mtu; 322*e9ce1cd3SDavid S. Miller opt.burst = police->tcfp_burst; 323*e9ce1cd3SDavid S. Miller opt.refcnt = police->tcf_refcnt - ref; 324*e9ce1cd3SDavid S. Miller opt.bindcnt = police->tcf_bindcnt - bind; 325*e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab) 326*e9ce1cd3SDavid S. Miller opt.rate = police->tcfp_R_tab->rate; 3274bba3925SPatrick McHardy else 3284bba3925SPatrick McHardy memset(&opt.rate, 0, sizeof(opt.rate)); 329*e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab) 330*e9ce1cd3SDavid S. Miller opt.peakrate = police->tcfp_P_tab->rate; 3314bba3925SPatrick McHardy else 3324bba3925SPatrick McHardy memset(&opt.peakrate, 0, sizeof(opt.peakrate)); 3334bba3925SPatrick McHardy RTA_PUT(skb, TCA_POLICE_TBF, sizeof(opt), &opt); 334*e9ce1cd3SDavid S. Miller if (police->tcfp_result) 335*e9ce1cd3SDavid S. Miller RTA_PUT(skb, TCA_POLICE_RESULT, sizeof(int), 336*e9ce1cd3SDavid S. Miller &police->tcfp_result); 3374bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 338*e9ce1cd3SDavid S. Miller if (police->tcfp_ewma_rate) 339*e9ce1cd3SDavid S. Miller RTA_PUT(skb, TCA_POLICE_AVRATE, 4, &police->tcfp_ewma_rate); 3404bba3925SPatrick McHardy #endif 3414bba3925SPatrick McHardy return skb->len; 3424bba3925SPatrick McHardy 3434bba3925SPatrick McHardy rtattr_failure: 3444bba3925SPatrick McHardy skb_trim(skb, b - skb->data); 3454bba3925SPatrick McHardy return -1; 3464bba3925SPatrick McHardy } 3474bba3925SPatrick McHardy 3484bba3925SPatrick McHardy MODULE_AUTHOR("Alexey Kuznetsov"); 3494bba3925SPatrick McHardy MODULE_DESCRIPTION("Policing actions"); 3504bba3925SPatrick McHardy MODULE_LICENSE("GPL"); 3514bba3925SPatrick McHardy 3524bba3925SPatrick McHardy static struct tc_action_ops act_police_ops = { 3534bba3925SPatrick McHardy .kind = "police", 354*e9ce1cd3SDavid S. Miller .hinfo = &police_hash_info, 3554bba3925SPatrick McHardy .type = TCA_ID_POLICE, 3564bba3925SPatrick McHardy .capab = TCA_CAP_NONE, 3574bba3925SPatrick McHardy .owner = THIS_MODULE, 3584bba3925SPatrick McHardy .act = tcf_act_police, 3594bba3925SPatrick McHardy .dump = tcf_act_police_dump, 3604bba3925SPatrick McHardy .cleanup = tcf_act_police_cleanup, 361*e9ce1cd3SDavid S. Miller .lookup = tcf_hash_search, 3624bba3925SPatrick McHardy .init = tcf_act_police_locate, 36383b950c8SJamal Hadi Salim .walk = tcf_act_police_walker 3644bba3925SPatrick McHardy }; 3654bba3925SPatrick McHardy 3664bba3925SPatrick McHardy static int __init 3674bba3925SPatrick McHardy police_init_module(void) 3684bba3925SPatrick McHardy { 3694bba3925SPatrick McHardy return tcf_register_action(&act_police_ops); 3704bba3925SPatrick McHardy } 3714bba3925SPatrick McHardy 3724bba3925SPatrick McHardy static void __exit 3734bba3925SPatrick McHardy police_cleanup_module(void) 3744bba3925SPatrick McHardy { 3754bba3925SPatrick McHardy tcf_unregister_action(&act_police_ops); 3764bba3925SPatrick McHardy } 3774bba3925SPatrick McHardy 3784bba3925SPatrick McHardy module_init(police_init_module); 3794bba3925SPatrick McHardy module_exit(police_cleanup_module); 3804bba3925SPatrick McHardy 3814bba3925SPatrick McHardy #else /* CONFIG_NET_CLS_ACT */ 3824bba3925SPatrick McHardy 383*e9ce1cd3SDavid S. Miller static struct tcf_common *tcf_police_lookup(u32 index) 384*e9ce1cd3SDavid S. Miller { 385*e9ce1cd3SDavid S. Miller struct tcf_hashinfo *hinfo = &police_hash_info; 386*e9ce1cd3SDavid S. Miller struct tcf_common *p; 387*e9ce1cd3SDavid S. Miller 388*e9ce1cd3SDavid S. Miller read_lock(hinfo->lock); 389*e9ce1cd3SDavid S. Miller for (p = hinfo->htab[tcf_hash(index, hinfo->hmask)]; p; 390*e9ce1cd3SDavid S. Miller p = p->tcfc_next) { 391*e9ce1cd3SDavid S. Miller if (p->tcfc_index == index) 392*e9ce1cd3SDavid S. Miller break; 393*e9ce1cd3SDavid S. Miller } 394*e9ce1cd3SDavid S. Miller read_unlock(hinfo->lock); 395*e9ce1cd3SDavid S. Miller 396*e9ce1cd3SDavid S. Miller return p; 397*e9ce1cd3SDavid S. Miller } 398*e9ce1cd3SDavid S. Miller 399*e9ce1cd3SDavid S. Miller static u32 tcf_police_new_index(void) 400*e9ce1cd3SDavid S. Miller { 401*e9ce1cd3SDavid S. Miller u32 *idx_gen = &police_idx_gen; 402*e9ce1cd3SDavid S. Miller u32 val = *idx_gen; 403*e9ce1cd3SDavid S. Miller 404*e9ce1cd3SDavid S. Miller do { 405*e9ce1cd3SDavid S. Miller if (++val == 0) 406*e9ce1cd3SDavid S. Miller val = 1; 407*e9ce1cd3SDavid S. Miller } while (tcf_police_lookup(val)); 408*e9ce1cd3SDavid S. Miller 409*e9ce1cd3SDavid S. Miller return (*idx_gen = val); 410*e9ce1cd3SDavid S. Miller } 411*e9ce1cd3SDavid S. Miller 4124bba3925SPatrick McHardy struct tcf_police *tcf_police_locate(struct rtattr *rta, struct rtattr *est) 4134bba3925SPatrick McHardy { 414*e9ce1cd3SDavid S. Miller unsigned int h; 415*e9ce1cd3SDavid S. Miller struct tcf_police *police; 4164bba3925SPatrick McHardy struct rtattr *tb[TCA_POLICE_MAX]; 4174bba3925SPatrick McHardy struct tc_police *parm; 4184bba3925SPatrick McHardy 4194bba3925SPatrick McHardy if (rtattr_parse_nested(tb, TCA_POLICE_MAX, rta) < 0) 4204bba3925SPatrick McHardy return NULL; 4214bba3925SPatrick McHardy 4224bba3925SPatrick McHardy if (tb[TCA_POLICE_TBF-1] == NULL || 4234bba3925SPatrick McHardy RTA_PAYLOAD(tb[TCA_POLICE_TBF-1]) != sizeof(*parm)) 4244bba3925SPatrick McHardy return NULL; 4254bba3925SPatrick McHardy 4264bba3925SPatrick McHardy parm = RTA_DATA(tb[TCA_POLICE_TBF-1]); 4274bba3925SPatrick McHardy 428*e9ce1cd3SDavid S. Miller if (parm->index) { 429*e9ce1cd3SDavid S. Miller struct tcf_common *pc; 4304bba3925SPatrick McHardy 431*e9ce1cd3SDavid S. Miller pc = tcf_police_lookup(parm->index); 432*e9ce1cd3SDavid S. Miller if (pc) { 433*e9ce1cd3SDavid S. Miller police = to_police(pc); 434*e9ce1cd3SDavid S. Miller police->tcf_refcnt++; 435*e9ce1cd3SDavid S. Miller return police; 436*e9ce1cd3SDavid S. Miller } 437*e9ce1cd3SDavid S. Miller } 438*e9ce1cd3SDavid S. Miller police = kzalloc(sizeof(*police), GFP_KERNEL); 439*e9ce1cd3SDavid S. Miller if (unlikely(!police)) 4404bba3925SPatrick McHardy return NULL; 4414bba3925SPatrick McHardy 442*e9ce1cd3SDavid S. Miller police->tcf_refcnt = 1; 443*e9ce1cd3SDavid S. Miller spin_lock_init(&police->tcf_lock); 444*e9ce1cd3SDavid S. Miller police->tcf_stats_lock = &police->tcf_lock; 4454bba3925SPatrick McHardy if (parm->rate.rate) { 446*e9ce1cd3SDavid S. Miller police->tcfp_R_tab = 447*e9ce1cd3SDavid S. Miller qdisc_get_rtab(&parm->rate, tb[TCA_POLICE_RATE-1]); 448*e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab == NULL) 4494bba3925SPatrick McHardy goto failure; 4504bba3925SPatrick McHardy if (parm->peakrate.rate) { 451*e9ce1cd3SDavid S. Miller police->tcfp_P_tab = 452*e9ce1cd3SDavid S. Miller qdisc_get_rtab(&parm->peakrate, 4534bba3925SPatrick McHardy tb[TCA_POLICE_PEAKRATE-1]); 454*e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab == NULL) 4554bba3925SPatrick McHardy goto failure; 4564bba3925SPatrick McHardy } 4574bba3925SPatrick McHardy } 4584bba3925SPatrick McHardy if (tb[TCA_POLICE_RESULT-1]) { 4594bba3925SPatrick McHardy if (RTA_PAYLOAD(tb[TCA_POLICE_RESULT-1]) != sizeof(u32)) 4604bba3925SPatrick McHardy goto failure; 461*e9ce1cd3SDavid S. Miller police->tcfp_result = *(u32*)RTA_DATA(tb[TCA_POLICE_RESULT-1]); 4624bba3925SPatrick McHardy } 4634bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 4644bba3925SPatrick McHardy if (tb[TCA_POLICE_AVRATE-1]) { 4654bba3925SPatrick McHardy if (RTA_PAYLOAD(tb[TCA_POLICE_AVRATE-1]) != sizeof(u32)) 4664bba3925SPatrick McHardy goto failure; 467*e9ce1cd3SDavid S. Miller police->tcfp_ewma_rate = 468*e9ce1cd3SDavid S. Miller *(u32*)RTA_DATA(tb[TCA_POLICE_AVRATE-1]); 4694bba3925SPatrick McHardy } 4704bba3925SPatrick McHardy #endif 471*e9ce1cd3SDavid S. Miller police->tcfp_toks = police->tcfp_burst = parm->burst; 472*e9ce1cd3SDavid S. Miller police->tcfp_mtu = parm->mtu; 473*e9ce1cd3SDavid S. Miller if (police->tcfp_mtu == 0) { 474*e9ce1cd3SDavid S. Miller police->tcfp_mtu = ~0; 475*e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab) 476*e9ce1cd3SDavid S. Miller police->tcfp_mtu = 255<<police->tcfp_R_tab->rate.cell_log; 4774bba3925SPatrick McHardy } 478*e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab) 479*e9ce1cd3SDavid S. Miller police->tcfp_ptoks = L2T_P(police, police->tcfp_mtu); 480*e9ce1cd3SDavid S. Miller PSCHED_GET_TIME(police->tcfp_t_c); 481*e9ce1cd3SDavid S. Miller police->tcf_index = parm->index ? parm->index : 482*e9ce1cd3SDavid S. Miller tcf_police_new_index(); 483*e9ce1cd3SDavid S. Miller police->tcf_action = parm->action; 4844bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 4854bba3925SPatrick McHardy if (est) 486*e9ce1cd3SDavid S. Miller gen_new_estimator(&police->tcf_bstats, &police->tcf_rate_est, 487*e9ce1cd3SDavid S. Miller police->tcf_stats_lock, est); 4884bba3925SPatrick McHardy #endif 489*e9ce1cd3SDavid S. Miller h = tcf_hash(police->tcf_index, POL_TAB_MASK); 4904bba3925SPatrick McHardy write_lock_bh(&police_lock); 491*e9ce1cd3SDavid S. Miller police->tcf_next = tcf_police_ht[h]; 492*e9ce1cd3SDavid S. Miller tcf_police_ht[h] = &police->common; 4934bba3925SPatrick McHardy write_unlock_bh(&police_lock); 494*e9ce1cd3SDavid S. Miller return police; 4954bba3925SPatrick McHardy 4964bba3925SPatrick McHardy failure: 497*e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab) 498*e9ce1cd3SDavid S. Miller qdisc_put_rtab(police->tcfp_R_tab); 499*e9ce1cd3SDavid S. Miller kfree(police); 5004bba3925SPatrick McHardy return NULL; 5014bba3925SPatrick McHardy } 5024bba3925SPatrick McHardy 503*e9ce1cd3SDavid S. Miller int tcf_police(struct sk_buff *skb, struct tcf_police *police) 5044bba3925SPatrick McHardy { 5054bba3925SPatrick McHardy psched_time_t now; 5064bba3925SPatrick McHardy long toks; 5074bba3925SPatrick McHardy long ptoks = 0; 5084bba3925SPatrick McHardy 509*e9ce1cd3SDavid S. Miller spin_lock(&police->tcf_lock); 5104bba3925SPatrick McHardy 511*e9ce1cd3SDavid S. Miller police->tcf_bstats.bytes += skb->len; 512*e9ce1cd3SDavid S. Miller police->tcf_bstats.packets++; 5134bba3925SPatrick McHardy 5144bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 515*e9ce1cd3SDavid S. Miller if (police->tcfp_ewma_rate && 516*e9ce1cd3SDavid S. Miller police->tcf_rate_est.bps >= police->tcfp_ewma_rate) { 517*e9ce1cd3SDavid S. Miller police->tcf_qstats.overlimits++; 518*e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 519*e9ce1cd3SDavid S. Miller return police->tcf_action; 5204bba3925SPatrick McHardy } 5214bba3925SPatrick McHardy #endif 522*e9ce1cd3SDavid S. Miller if (skb->len <= police->tcfp_mtu) { 523*e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab == NULL) { 524*e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 525*e9ce1cd3SDavid S. Miller return police->tcfp_result; 5264bba3925SPatrick McHardy } 5274bba3925SPatrick McHardy 5284bba3925SPatrick McHardy PSCHED_GET_TIME(now); 529*e9ce1cd3SDavid S. Miller toks = PSCHED_TDIFF_SAFE(now, police->tcfp_t_c, 530*e9ce1cd3SDavid S. Miller police->tcfp_burst); 531*e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab) { 532*e9ce1cd3SDavid S. Miller ptoks = toks + police->tcfp_ptoks; 533*e9ce1cd3SDavid S. Miller if (ptoks > (long)L2T_P(police, police->tcfp_mtu)) 534*e9ce1cd3SDavid S. Miller ptoks = (long)L2T_P(police, police->tcfp_mtu); 535*e9ce1cd3SDavid S. Miller ptoks -= L2T_P(police, skb->len); 5364bba3925SPatrick McHardy } 537*e9ce1cd3SDavid S. Miller toks += police->tcfp_toks; 538*e9ce1cd3SDavid S. Miller if (toks > (long)police->tcfp_burst) 539*e9ce1cd3SDavid S. Miller toks = police->tcfp_burst; 540*e9ce1cd3SDavid S. Miller toks -= L2T(police, skb->len); 5414bba3925SPatrick McHardy if ((toks|ptoks) >= 0) { 542*e9ce1cd3SDavid S. Miller police->tcfp_t_c = now; 543*e9ce1cd3SDavid S. Miller police->tcfp_toks = toks; 544*e9ce1cd3SDavid S. Miller police->tcfp_ptoks = ptoks; 545*e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 546*e9ce1cd3SDavid S. Miller return police->tcfp_result; 5474bba3925SPatrick McHardy } 5484bba3925SPatrick McHardy } 5494bba3925SPatrick McHardy 550*e9ce1cd3SDavid S. Miller police->tcf_qstats.overlimits++; 551*e9ce1cd3SDavid S. Miller spin_unlock(&police->tcf_lock); 552*e9ce1cd3SDavid S. Miller return police->tcf_action; 5534bba3925SPatrick McHardy } 5544bba3925SPatrick McHardy EXPORT_SYMBOL(tcf_police); 5554bba3925SPatrick McHardy 556*e9ce1cd3SDavid S. Miller int tcf_police_dump(struct sk_buff *skb, struct tcf_police *police) 5574bba3925SPatrick McHardy { 5584bba3925SPatrick McHardy unsigned char *b = skb->tail; 5594bba3925SPatrick McHardy struct tc_police opt; 5604bba3925SPatrick McHardy 561*e9ce1cd3SDavid S. Miller opt.index = police->tcf_index; 562*e9ce1cd3SDavid S. Miller opt.action = police->tcf_action; 563*e9ce1cd3SDavid S. Miller opt.mtu = police->tcfp_mtu; 564*e9ce1cd3SDavid S. Miller opt.burst = police->tcfp_burst; 565*e9ce1cd3SDavid S. Miller if (police->tcfp_R_tab) 566*e9ce1cd3SDavid S. Miller opt.rate = police->tcfp_R_tab->rate; 5674bba3925SPatrick McHardy else 5684bba3925SPatrick McHardy memset(&opt.rate, 0, sizeof(opt.rate)); 569*e9ce1cd3SDavid S. Miller if (police->tcfp_P_tab) 570*e9ce1cd3SDavid S. Miller opt.peakrate = police->tcfp_P_tab->rate; 5714bba3925SPatrick McHardy else 5724bba3925SPatrick McHardy memset(&opt.peakrate, 0, sizeof(opt.peakrate)); 5734bba3925SPatrick McHardy RTA_PUT(skb, TCA_POLICE_TBF, sizeof(opt), &opt); 574*e9ce1cd3SDavid S. Miller if (police->tcfp_result) 575*e9ce1cd3SDavid S. Miller RTA_PUT(skb, TCA_POLICE_RESULT, sizeof(int), 576*e9ce1cd3SDavid S. Miller &police->tcfp_result); 5774bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 578*e9ce1cd3SDavid S. Miller if (police->tcfp_ewma_rate) 579*e9ce1cd3SDavid S. Miller RTA_PUT(skb, TCA_POLICE_AVRATE, 4, &police->tcfp_ewma_rate); 5804bba3925SPatrick McHardy #endif 5814bba3925SPatrick McHardy return skb->len; 5824bba3925SPatrick McHardy 5834bba3925SPatrick McHardy rtattr_failure: 5844bba3925SPatrick McHardy skb_trim(skb, b - skb->data); 5854bba3925SPatrick McHardy return -1; 5864bba3925SPatrick McHardy } 5874bba3925SPatrick McHardy 588*e9ce1cd3SDavid S. Miller int tcf_police_dump_stats(struct sk_buff *skb, struct tcf_police *police) 5894bba3925SPatrick McHardy { 5904bba3925SPatrick McHardy struct gnet_dump d; 5914bba3925SPatrick McHardy 5924bba3925SPatrick McHardy if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, 593*e9ce1cd3SDavid S. Miller TCA_XSTATS, police->tcf_stats_lock, 594*e9ce1cd3SDavid S. Miller &d) < 0) 5954bba3925SPatrick McHardy goto errout; 5964bba3925SPatrick McHardy 597*e9ce1cd3SDavid S. Miller if (gnet_stats_copy_basic(&d, &police->tcf_bstats) < 0 || 5984bba3925SPatrick McHardy #ifdef CONFIG_NET_ESTIMATOR 599*e9ce1cd3SDavid S. Miller gnet_stats_copy_rate_est(&d, &police->tcf_rate_est) < 0 || 6004bba3925SPatrick McHardy #endif 601*e9ce1cd3SDavid S. Miller gnet_stats_copy_queue(&d, &police->tcf_qstats) < 0) 6024bba3925SPatrick McHardy goto errout; 6034bba3925SPatrick McHardy 6044bba3925SPatrick McHardy if (gnet_stats_finish_copy(&d) < 0) 6054bba3925SPatrick McHardy goto errout; 6064bba3925SPatrick McHardy 6074bba3925SPatrick McHardy return 0; 6084bba3925SPatrick McHardy 6094bba3925SPatrick McHardy errout: 6104bba3925SPatrick McHardy return -1; 6114bba3925SPatrick McHardy } 6124bba3925SPatrick McHardy 6134bba3925SPatrick McHardy #endif /* CONFIG_NET_CLS_ACT */ 614