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 v->tcf_tm.lastuse = jiffies; 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; 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 switch (parm->v_action) { 94 case TCA_VLAN_ACT_POP: 95 break; 96 case TCA_VLAN_ACT_PUSH: 97 if (!tb[TCA_VLAN_PUSH_VLAN_ID]) 98 return -EINVAL; 99 push_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]); 100 if (push_vid >= VLAN_VID_MASK) 101 return -ERANGE; 102 103 if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) { 104 push_proto = nla_get_be16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]); 105 switch (push_proto) { 106 case htons(ETH_P_8021Q): 107 case htons(ETH_P_8021AD): 108 break; 109 default: 110 return -EPROTONOSUPPORT; 111 } 112 } else { 113 push_proto = htons(ETH_P_8021Q); 114 } 115 break; 116 default: 117 return -EINVAL; 118 } 119 action = parm->v_action; 120 121 if (!tcf_hash_check(tn, parm->index, a, bind)) { 122 ret = tcf_hash_create(tn, parm->index, est, a, 123 sizeof(*v), bind, false); 124 if (ret) 125 return ret; 126 127 ret = ACT_P_CREATED; 128 } else { 129 if (bind) 130 return 0; 131 tcf_hash_release(a, bind); 132 if (!ovr) 133 return -EEXIST; 134 } 135 136 v = to_vlan(a); 137 138 spin_lock_bh(&v->tcf_lock); 139 140 v->tcfv_action = action; 141 v->tcfv_push_vid = push_vid; 142 v->tcfv_push_proto = push_proto; 143 144 v->tcf_action = parm->action; 145 146 spin_unlock_bh(&v->tcf_lock); 147 148 if (ret == ACT_P_CREATED) 149 tcf_hash_insert(tn, a); 150 return ret; 151 } 152 153 static int tcf_vlan_dump(struct sk_buff *skb, struct tc_action *a, 154 int bind, int ref) 155 { 156 unsigned char *b = skb_tail_pointer(skb); 157 struct tcf_vlan *v = a->priv; 158 struct tc_vlan opt = { 159 .index = v->tcf_index, 160 .refcnt = v->tcf_refcnt - ref, 161 .bindcnt = v->tcf_bindcnt - bind, 162 .action = v->tcf_action, 163 .v_action = v->tcfv_action, 164 }; 165 struct tcf_t t; 166 167 if (nla_put(skb, TCA_VLAN_PARMS, sizeof(opt), &opt)) 168 goto nla_put_failure; 169 170 if (v->tcfv_action == TCA_VLAN_ACT_PUSH && 171 (nla_put_u16(skb, TCA_VLAN_PUSH_VLAN_ID, v->tcfv_push_vid) || 172 nla_put_be16(skb, TCA_VLAN_PUSH_VLAN_PROTOCOL, v->tcfv_push_proto))) 173 goto nla_put_failure; 174 175 t.install = jiffies_to_clock_t(jiffies - v->tcf_tm.install); 176 t.lastuse = jiffies_to_clock_t(jiffies - v->tcf_tm.lastuse); 177 t.expires = jiffies_to_clock_t(v->tcf_tm.expires); 178 if (nla_put(skb, TCA_VLAN_TM, sizeof(t), &t)) 179 goto nla_put_failure; 180 return skb->len; 181 182 nla_put_failure: 183 nlmsg_trim(skb, b); 184 return -1; 185 } 186 187 static int tcf_vlan_walker(struct net *net, struct sk_buff *skb, 188 struct netlink_callback *cb, int type, 189 struct tc_action *a) 190 { 191 struct tc_action_net *tn = net_generic(net, vlan_net_id); 192 193 return tcf_generic_walker(tn, skb, cb, type, a); 194 } 195 196 static int tcf_vlan_search(struct net *net, struct tc_action *a, u32 index) 197 { 198 struct tc_action_net *tn = net_generic(net, vlan_net_id); 199 200 return tcf_hash_search(tn, a, index); 201 } 202 203 static struct tc_action_ops act_vlan_ops = { 204 .kind = "vlan", 205 .type = TCA_ACT_VLAN, 206 .owner = THIS_MODULE, 207 .act = tcf_vlan, 208 .dump = tcf_vlan_dump, 209 .init = tcf_vlan_init, 210 .walk = tcf_vlan_walker, 211 .lookup = tcf_vlan_search, 212 }; 213 214 static __net_init int vlan_init_net(struct net *net) 215 { 216 struct tc_action_net *tn = net_generic(net, vlan_net_id); 217 218 return tc_action_net_init(tn, &act_vlan_ops, VLAN_TAB_MASK); 219 } 220 221 static void __net_exit vlan_exit_net(struct net *net) 222 { 223 struct tc_action_net *tn = net_generic(net, vlan_net_id); 224 225 tc_action_net_exit(tn); 226 } 227 228 static struct pernet_operations vlan_net_ops = { 229 .init = vlan_init_net, 230 .exit = vlan_exit_net, 231 .id = &vlan_net_id, 232 .size = sizeof(struct tc_action_net), 233 }; 234 235 static int __init vlan_init_module(void) 236 { 237 return tcf_register_action(&act_vlan_ops, &vlan_net_ops); 238 } 239 240 static void __exit vlan_cleanup_module(void) 241 { 242 tcf_unregister_action(&act_vlan_ops, &vlan_net_ops); 243 } 244 245 module_init(vlan_init_module); 246 module_exit(vlan_cleanup_module); 247 248 MODULE_AUTHOR("Jiri Pirko <jiri@resnulli.us>"); 249 MODULE_DESCRIPTION("vlan manipulation actions"); 250 MODULE_LICENSE("GPL v2"); 251