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