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