Blame view
net/sched/act_vlan.c
7.19 KB
c7e2b9689 sched: introduce ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/* * Copyright (c) 2014 Jiri Pirko <jiri@resnulli.us> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/skbuff.h> #include <linux/rtnetlink.h> #include <linux/if_vlan.h> #include <net/netlink.h> #include <net/pkt_sched.h> #include <linux/tc_act/tc_vlan.h> #include <net/tc_act/tc_vlan.h> #define VLAN_TAB_MASK 15 |
ddf97ccdd net_sched: add ne... |
23 |
static int vlan_net_id; |
a85a970af net_sched: move t... |
24 |
static struct tc_action_ops act_vlan_ops; |
ddf97ccdd net_sched: add ne... |
25 |
|
c7e2b9689 sched: introduce ... |
26 27 28 |
static int tcf_vlan(struct sk_buff *skb, const struct tc_action *a, struct tcf_result *res) { |
a85a970af net_sched: move t... |
29 |
struct tcf_vlan *v = to_vlan(a); |
c7e2b9689 sched: introduce ... |
30 31 |
int action; int err; |
45a497f2d net/sched: act_vl... |
32 |
u16 tci; |
c7e2b9689 sched: introduce ... |
33 34 |
spin_lock(&v->tcf_lock); |
9c4a4e488 net sched: action... |
35 |
tcf_lastuse_update(&v->tcf_tm); |
c7e2b9689 sched: introduce ... |
36 37 |
bstats_update(&v->tcf_bstats, skb); action = v->tcf_action; |
f39acc84a net/sched: act_vl... |
38 39 40 41 42 |
/* Ensure 'data' points at mac_header prior calling vlan manipulating * functions. */ if (skb_at_tc_ingress(skb)) skb_push_rcsum(skb, skb->mac_len); |
c7e2b9689 sched: introduce ... |
43 44 45 46 47 48 49 |
switch (v->tcfv_action) { case TCA_VLAN_ACT_POP: err = skb_vlan_pop(skb); if (err) goto drop; break; case TCA_VLAN_ACT_PUSH: |
956af3710 net_sched: act_vl... |
50 51 |
err = skb_vlan_push(skb, v->tcfv_push_proto, v->tcfv_push_vid | (v->tcfv_push_prio << VLAN_PRIO_SHIFT)); |
c7e2b9689 sched: introduce ... |
52 53 54 |
if (err) goto drop; break; |
45a497f2d net/sched: act_vl... |
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
case TCA_VLAN_ACT_MODIFY: /* No-op if no vlan tag (either hw-accel or in-payload) */ if (!skb_vlan_tagged(skb)) goto unlock; /* extract existing tag (and guarantee no hw-accel tag) */ if (skb_vlan_tag_present(skb)) { tci = skb_vlan_tag_get(skb); skb->vlan_tci = 0; } else { /* in-payload vlan tag, pop it */ err = __skb_vlan_pop(skb, &tci); if (err) goto drop; } /* replace the vid */ tci = (tci & ~VLAN_VID_MASK) | v->tcfv_push_vid; /* replace prio bits, if tcfv_push_prio specified */ if (v->tcfv_push_prio) { tci &= ~VLAN_PRIO_MASK; tci |= v->tcfv_push_prio << VLAN_PRIO_SHIFT; } /* put updated tci as hwaccel tag */ __vlan_hwaccel_put_tag(skb, v->tcfv_push_proto, tci); break; |
c7e2b9689 sched: introduce ... |
79 80 81 82 83 84 85 86 87 88 |
default: BUG(); } goto unlock; drop: action = TC_ACT_SHOT; v->tcf_qstats.drops++; unlock: |
f39acc84a net/sched: act_vl... |
89 90 |
if (skb_at_tc_ingress(skb)) skb_pull_rcsum(skb, skb->mac_len); |
c7e2b9689 sched: introduce ... |
91 92 93 94 95 96 97 98 |
spin_unlock(&v->tcf_lock); return action; } static const struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = { [TCA_VLAN_PARMS] = { .len = sizeof(struct tc_vlan) }, [TCA_VLAN_PUSH_VLAN_ID] = { .type = NLA_U16 }, [TCA_VLAN_PUSH_VLAN_PROTOCOL] = { .type = NLA_U16 }, |
956af3710 net_sched: act_vl... |
99 |
[TCA_VLAN_PUSH_VLAN_PRIORITY] = { .type = NLA_U8 }, |
c7e2b9689 sched: introduce ... |
100 101 102 |
}; static int tcf_vlan_init(struct net *net, struct nlattr *nla, |
a85a970af net_sched: move t... |
103 |
struct nlattr *est, struct tc_action **a, |
c7e2b9689 sched: introduce ... |
104 105 |
int ovr, int bind) { |
ddf97ccdd net_sched: add ne... |
106 |
struct tc_action_net *tn = net_generic(net, vlan_net_id); |
c7e2b9689 sched: introduce ... |
107 108 109 110 111 112 |
struct nlattr *tb[TCA_VLAN_MAX + 1]; struct tc_vlan *parm; struct tcf_vlan *v; int action; __be16 push_vid = 0; __be16 push_proto = 0; |
956af3710 net_sched: act_vl... |
113 |
u8 push_prio = 0; |
b2313077e net_sched: make t... |
114 115 |
bool exists = false; int ret = 0, err; |
c7e2b9689 sched: introduce ... |
116 117 118 119 120 121 122 123 124 125 126 |
if (!nla) return -EINVAL; err = nla_parse_nested(tb, TCA_VLAN_MAX, nla, vlan_policy); if (err < 0) return err; if (!tb[TCA_VLAN_PARMS]) return -EINVAL; parm = nla_data(tb[TCA_VLAN_PARMS]); |
5026c9b1b net sched: vlan a... |
127 128 129 |
exists = tcf_hash_check(tn, parm->index, a, bind); if (exists && bind) return 0; |
c7e2b9689 sched: introduce ... |
130 131 132 133 |
switch (parm->v_action) { case TCA_VLAN_ACT_POP: break; case TCA_VLAN_ACT_PUSH: |
45a497f2d net/sched: act_vl... |
134 |
case TCA_VLAN_ACT_MODIFY: |
5026c9b1b net sched: vlan a... |
135 136 |
if (!tb[TCA_VLAN_PUSH_VLAN_ID]) { if (exists) |
a85a970af net_sched: move t... |
137 |
tcf_hash_release(*a, bind); |
c7e2b9689 sched: introduce ... |
138 |
return -EINVAL; |
5026c9b1b net sched: vlan a... |
139 |
} |
c7e2b9689 sched: introduce ... |
140 |
push_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]); |
5026c9b1b net sched: vlan a... |
141 142 |
if (push_vid >= VLAN_VID_MASK) { if (exists) |
a85a970af net_sched: move t... |
143 |
tcf_hash_release(*a, bind); |
c7e2b9689 sched: introduce ... |
144 |
return -ERANGE; |
5026c9b1b net sched: vlan a... |
145 |
} |
c7e2b9689 sched: introduce ... |
146 147 148 149 150 151 152 153 154 155 156 157 158 |
if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) { push_proto = nla_get_be16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]); switch (push_proto) { case htons(ETH_P_8021Q): case htons(ETH_P_8021AD): break; default: return -EPROTONOSUPPORT; } } else { push_proto = htons(ETH_P_8021Q); } |
956af3710 net_sched: act_vl... |
159 160 161 |
if (tb[TCA_VLAN_PUSH_VLAN_PRIORITY]) push_prio = nla_get_u8(tb[TCA_VLAN_PUSH_VLAN_PRIORITY]); |
c7e2b9689 sched: introduce ... |
162 163 |
break; default: |
5026c9b1b net sched: vlan a... |
164 |
if (exists) |
a85a970af net_sched: move t... |
165 |
tcf_hash_release(*a, bind); |
c7e2b9689 sched: introduce ... |
166 167 168 |
return -EINVAL; } action = parm->v_action; |
5026c9b1b net sched: vlan a... |
169 |
if (!exists) { |
ddf97ccdd net_sched: add ne... |
170 |
ret = tcf_hash_create(tn, parm->index, est, a, |
a85a970af net_sched: move t... |
171 |
&act_vlan_ops, bind, false); |
c7e2b9689 sched: introduce ... |
172 173 174 175 176 |
if (ret) return ret; ret = ACT_P_CREATED; } else { |
a85a970af net_sched: move t... |
177 |
tcf_hash_release(*a, bind); |
c7e2b9689 sched: introduce ... |
178 179 180 |
if (!ovr) return -EEXIST; } |
a85a970af net_sched: move t... |
181 |
v = to_vlan(*a); |
c7e2b9689 sched: introduce ... |
182 183 184 185 186 |
spin_lock_bh(&v->tcf_lock); v->tcfv_action = action; v->tcfv_push_vid = push_vid; |
956af3710 net_sched: act_vl... |
187 |
v->tcfv_push_prio = push_prio; |
c7e2b9689 sched: introduce ... |
188 189 190 191 192 193 194 |
v->tcfv_push_proto = push_proto; v->tcf_action = parm->action; spin_unlock_bh(&v->tcf_lock); if (ret == ACT_P_CREATED) |
a85a970af net_sched: move t... |
195 |
tcf_hash_insert(tn, *a); |
c7e2b9689 sched: introduce ... |
196 197 198 199 200 201 202 |
return ret; } static int tcf_vlan_dump(struct sk_buff *skb, struct tc_action *a, int bind, int ref) { unsigned char *b = skb_tail_pointer(skb); |
a85a970af net_sched: move t... |
203 |
struct tcf_vlan *v = to_vlan(a); |
c7e2b9689 sched: introduce ... |
204 205 206 207 208 209 210 211 212 213 214 |
struct tc_vlan opt = { .index = v->tcf_index, .refcnt = v->tcf_refcnt - ref, .bindcnt = v->tcf_bindcnt - bind, .action = v->tcf_action, .v_action = v->tcfv_action, }; struct tcf_t t; if (nla_put(skb, TCA_VLAN_PARMS, sizeof(opt), &opt)) goto nla_put_failure; |
45a497f2d net/sched: act_vl... |
215 216 |
if ((v->tcfv_action == TCA_VLAN_ACT_PUSH || v->tcfv_action == TCA_VLAN_ACT_MODIFY) && |
c7e2b9689 sched: introduce ... |
217 |
(nla_put_u16(skb, TCA_VLAN_PUSH_VLAN_ID, v->tcfv_push_vid) || |
0b0f43fe2 net sched: indent... |
218 |
nla_put_be16(skb, TCA_VLAN_PUSH_VLAN_PROTOCOL, |
956af3710 net_sched: act_vl... |
219 220 221 |
v->tcfv_push_proto) || (nla_put_u8(skb, TCA_VLAN_PUSH_VLAN_PRIORITY, v->tcfv_push_prio)))) |
c7e2b9689 sched: introduce ... |
222 |
goto nla_put_failure; |
48d8ee169 net sched actions... |
223 |
tcf_tm_dump(&t, &v->tcf_tm); |
9854518ea sched: align nlat... |
224 |
if (nla_put_64bit(skb, TCA_VLAN_TM, sizeof(t), &t, TCA_VLAN_PAD)) |
c7e2b9689 sched: introduce ... |
225 226 227 228 229 230 231 |
goto nla_put_failure; return skb->len; nla_put_failure: nlmsg_trim(skb, b); return -1; } |
ddf97ccdd net_sched: add ne... |
232 233 |
static int tcf_vlan_walker(struct net *net, struct sk_buff *skb, struct netlink_callback *cb, int type, |
a85a970af net_sched: move t... |
234 |
const struct tc_action_ops *ops) |
ddf97ccdd net_sched: add ne... |
235 236 |
{ struct tc_action_net *tn = net_generic(net, vlan_net_id); |
a85a970af net_sched: move t... |
237 |
return tcf_generic_walker(tn, skb, cb, type, ops); |
ddf97ccdd net_sched: add ne... |
238 |
} |
a85a970af net_sched: move t... |
239 |
static int tcf_vlan_search(struct net *net, struct tc_action **a, u32 index) |
ddf97ccdd net_sched: add ne... |
240 241 242 243 244 |
{ struct tc_action_net *tn = net_generic(net, vlan_net_id); return tcf_hash_search(tn, a, index); } |
c7e2b9689 sched: introduce ... |
245 246 247 248 249 250 251 |
static struct tc_action_ops act_vlan_ops = { .kind = "vlan", .type = TCA_ACT_VLAN, .owner = THIS_MODULE, .act = tcf_vlan, .dump = tcf_vlan_dump, .init = tcf_vlan_init, |
ddf97ccdd net_sched: add ne... |
252 253 |
.walk = tcf_vlan_walker, .lookup = tcf_vlan_search, |
a85a970af net_sched: move t... |
254 |
.size = sizeof(struct tcf_vlan), |
ddf97ccdd net_sched: add ne... |
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
}; static __net_init int vlan_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, vlan_net_id); return tc_action_net_init(tn, &act_vlan_ops, VLAN_TAB_MASK); } static void __net_exit vlan_exit_net(struct net *net) { struct tc_action_net *tn = net_generic(net, vlan_net_id); tc_action_net_exit(tn); } static struct pernet_operations vlan_net_ops = { .init = vlan_init_net, .exit = vlan_exit_net, .id = &vlan_net_id, .size = sizeof(struct tc_action_net), |
c7e2b9689 sched: introduce ... |
276 277 278 279 |
}; static int __init vlan_init_module(void) { |
ddf97ccdd net_sched: add ne... |
280 |
return tcf_register_action(&act_vlan_ops, &vlan_net_ops); |
c7e2b9689 sched: introduce ... |
281 282 283 284 |
} static void __exit vlan_cleanup_module(void) { |
ddf97ccdd net_sched: add ne... |
285 |
tcf_unregister_action(&act_vlan_ops, &vlan_net_ops); |
c7e2b9689 sched: introduce ... |
286 287 288 289 290 291 292 293 |
} module_init(vlan_init_module); module_exit(vlan_cleanup_module); MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>"); MODULE_DESCRIPTION("vlan manipulation actions"); MODULE_LICENSE("GPL v2"); |