1 /* 2 * net/sched/pedit.c Generic packet editor 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 (2002-4) 10 */ 11 12 #include <linux/types.h> 13 #include <linux/kernel.h> 14 #include <linux/string.h> 15 #include <linux/errno.h> 16 #include <linux/skbuff.h> 17 #include <linux/rtnetlink.h> 18 #include <linux/module.h> 19 #include <linux/init.h> 20 #include <net/netlink.h> 21 #include <net/pkt_sched.h> 22 #include <linux/tc_act/tc_pedit.h> 23 #include <net/tc_act/tc_pedit.h> 24 25 #define PEDIT_TAB_MASK 15 26 static struct tcf_common *tcf_pedit_ht[PEDIT_TAB_MASK + 1]; 27 static u32 pedit_idx_gen; 28 static DEFINE_RWLOCK(pedit_lock); 29 30 static struct tcf_hashinfo pedit_hash_info = { 31 .htab = tcf_pedit_ht, 32 .hmask = PEDIT_TAB_MASK, 33 .lock = &pedit_lock, 34 }; 35 36 static int tcf_pedit_init(struct rtattr *rta, struct rtattr *est, 37 struct tc_action *a, int ovr, int bind) 38 { 39 struct rtattr *tb[TCA_PEDIT_MAX]; 40 struct tc_pedit *parm; 41 int ret = 0; 42 struct tcf_pedit *p; 43 struct tcf_common *pc; 44 struct tc_pedit_key *keys = NULL; 45 int ksize; 46 47 if (rta == NULL || rtattr_parse_nested(tb, TCA_PEDIT_MAX, rta) < 0) 48 return -EINVAL; 49 50 if (tb[TCA_PEDIT_PARMS - 1] == NULL || 51 RTA_PAYLOAD(tb[TCA_PEDIT_PARMS-1]) < sizeof(*parm)) 52 return -EINVAL; 53 parm = RTA_DATA(tb[TCA_PEDIT_PARMS-1]); 54 ksize = parm->nkeys * sizeof(struct tc_pedit_key); 55 if (RTA_PAYLOAD(tb[TCA_PEDIT_PARMS-1]) < sizeof(*parm) + ksize) 56 return -EINVAL; 57 58 pc = tcf_hash_check(parm->index, a, bind, &pedit_hash_info); 59 if (!pc) { 60 if (!parm->nkeys) 61 return -EINVAL; 62 pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind, 63 &pedit_idx_gen, &pedit_hash_info); 64 if (unlikely(!pc)) 65 return -ENOMEM; 66 p = to_pedit(pc); 67 keys = kmalloc(ksize, GFP_KERNEL); 68 if (keys == NULL) { 69 kfree(pc); 70 return -ENOMEM; 71 } 72 ret = ACT_P_CREATED; 73 } else { 74 p = to_pedit(pc); 75 if (!ovr) { 76 tcf_hash_release(pc, bind, &pedit_hash_info); 77 return -EEXIST; 78 } 79 if (p->tcfp_nkeys && p->tcfp_nkeys != parm->nkeys) { 80 keys = kmalloc(ksize, GFP_KERNEL); 81 if (keys == NULL) 82 return -ENOMEM; 83 } 84 } 85 86 spin_lock_bh(&p->tcf_lock); 87 p->tcfp_flags = parm->flags; 88 p->tcf_action = parm->action; 89 if (keys) { 90 kfree(p->tcfp_keys); 91 p->tcfp_keys = keys; 92 p->tcfp_nkeys = parm->nkeys; 93 } 94 memcpy(p->tcfp_keys, parm->keys, ksize); 95 spin_unlock_bh(&p->tcf_lock); 96 if (ret == ACT_P_CREATED) 97 tcf_hash_insert(pc, &pedit_hash_info); 98 return ret; 99 } 100 101 static int tcf_pedit_cleanup(struct tc_action *a, int bind) 102 { 103 struct tcf_pedit *p = a->priv; 104 105 if (p) { 106 struct tc_pedit_key *keys = p->tcfp_keys; 107 if (tcf_hash_release(&p->common, bind, &pedit_hash_info)) { 108 kfree(keys); 109 return 1; 110 } 111 } 112 return 0; 113 } 114 115 static int tcf_pedit(struct sk_buff *skb, struct tc_action *a, 116 struct tcf_result *res) 117 { 118 struct tcf_pedit *p = a->priv; 119 int i, munged = 0; 120 u8 *pptr; 121 122 if (!(skb->tc_verd & TC_OK2MUNGE)) { 123 /* should we set skb->cloned? */ 124 if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { 125 return p->tcf_action; 126 } 127 } 128 129 pptr = skb_network_header(skb); 130 131 spin_lock(&p->tcf_lock); 132 133 p->tcf_tm.lastuse = jiffies; 134 135 if (p->tcfp_nkeys > 0) { 136 struct tc_pedit_key *tkey = p->tcfp_keys; 137 138 for (i = p->tcfp_nkeys; i > 0; i--, tkey++) { 139 u32 *ptr; 140 int offset = tkey->off; 141 142 if (tkey->offmask) { 143 if (skb->len > tkey->at) { 144 char *j = pptr + tkey->at; 145 offset += ((*j & tkey->offmask) >> 146 tkey->shift); 147 } else { 148 goto bad; 149 } 150 } 151 152 if (offset % 4) { 153 printk("offset must be on 32 bit boundaries\n"); 154 goto bad; 155 } 156 if (offset > 0 && offset > skb->len) { 157 printk("offset %d cant exceed pkt length %d\n", 158 offset, skb->len); 159 goto bad; 160 } 161 162 ptr = (u32 *)(pptr+offset); 163 /* just do it, baby */ 164 *ptr = ((*ptr & tkey->mask) ^ tkey->val); 165 munged++; 166 } 167 168 if (munged) 169 skb->tc_verd = SET_TC_MUNGED(skb->tc_verd); 170 goto done; 171 } else { 172 printk("pedit BUG: index %d\n", p->tcf_index); 173 } 174 175 bad: 176 p->tcf_qstats.overlimits++; 177 done: 178 p->tcf_bstats.bytes += skb->len; 179 p->tcf_bstats.packets++; 180 spin_unlock(&p->tcf_lock); 181 return p->tcf_action; 182 } 183 184 static int tcf_pedit_dump(struct sk_buff *skb, struct tc_action *a, 185 int bind, int ref) 186 { 187 unsigned char *b = skb_tail_pointer(skb); 188 struct tcf_pedit *p = a->priv; 189 struct tc_pedit *opt; 190 struct tcf_t t; 191 int s; 192 193 s = sizeof(*opt) + p->tcfp_nkeys * sizeof(struct tc_pedit_key); 194 195 /* netlink spinlocks held above us - must use ATOMIC */ 196 opt = kzalloc(s, GFP_ATOMIC); 197 if (unlikely(!opt)) 198 return -ENOBUFS; 199 200 memcpy(opt->keys, p->tcfp_keys, 201 p->tcfp_nkeys * sizeof(struct tc_pedit_key)); 202 opt->index = p->tcf_index; 203 opt->nkeys = p->tcfp_nkeys; 204 opt->flags = p->tcfp_flags; 205 opt->action = p->tcf_action; 206 opt->refcnt = p->tcf_refcnt - ref; 207 opt->bindcnt = p->tcf_bindcnt - bind; 208 209 RTA_PUT(skb, TCA_PEDIT_PARMS, s, opt); 210 t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install); 211 t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse); 212 t.expires = jiffies_to_clock_t(p->tcf_tm.expires); 213 RTA_PUT(skb, TCA_PEDIT_TM, sizeof(t), &t); 214 kfree(opt); 215 return skb->len; 216 217 rtattr_failure: 218 nlmsg_trim(skb, b); 219 kfree(opt); 220 return -1; 221 } 222 223 static struct tc_action_ops act_pedit_ops = { 224 .kind = "pedit", 225 .hinfo = &pedit_hash_info, 226 .type = TCA_ACT_PEDIT, 227 .capab = TCA_CAP_NONE, 228 .owner = THIS_MODULE, 229 .act = tcf_pedit, 230 .dump = tcf_pedit_dump, 231 .cleanup = tcf_pedit_cleanup, 232 .lookup = tcf_hash_search, 233 .init = tcf_pedit_init, 234 .walk = tcf_generic_walker 235 }; 236 237 MODULE_AUTHOR("Jamal Hadi Salim(2002-4)"); 238 MODULE_DESCRIPTION("Generic Packet Editor actions"); 239 MODULE_LICENSE("GPL"); 240 241 static int __init pedit_init_module(void) 242 { 243 return tcf_register_action(&act_pedit_ops); 244 } 245 246 static void __exit pedit_cleanup_module(void) 247 { 248 tcf_unregister_action(&act_pedit_ops); 249 } 250 251 module_init(pedit_init_module); 252 module_exit(pedit_cleanup_module); 253 254